69 lines
2.6 KiB
Python
69 lines
2.6 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""RSS proxy for ATC Lakehouse — normalized JSON for homepage widgets."""
|
||
|
|
from flask import Flask, jsonify
|
||
|
|
import feedparser
|
||
|
|
|
||
|
|
app = Flask(__name__)
|
||
|
|
|
||
|
|
FEEDS = {
|
||
|
|
# Hacker News & community
|
||
|
|
"hn-front": "https://hnrss.org/frontpage",
|
||
|
|
"hn-de": "https://hnrss.org/newest?q=data+engineering",
|
||
|
|
"hn-kafka": "https://hnrss.org/newest?q=kafka",
|
||
|
|
"hn-spark": "https://hnrss.org/newest?q=apache+spark",
|
||
|
|
"hn-lakehouse": "https://hnrss.org/newest?q=lakehouse",
|
||
|
|
"lobsters": "https://lobste.rs/rss",
|
||
|
|
# Data architecture & FDE reading
|
||
|
|
"de-weekly": "https://www.dataengineeringweekly.com/feed",
|
||
|
|
"de-central": "https://dataengineeringcentral.substack.com/feed",
|
||
|
|
"pragmatic": "https://blog.pragmaticengineer.com/rss/",
|
||
|
|
"martinfowler": "https://martinfowler.com/feed.atom",
|
||
|
|
"infoq": "https://www.infoq.com/feed/",
|
||
|
|
"oreilly-radar": "https://feeds.feedburner.com/oreilly/radar/atom",
|
||
|
|
"bytebytego": "https://blog.bytebytego.com/feed",
|
||
|
|
"seattle-de": "https://www.seattledataguy.com/feed/",
|
||
|
|
# Platforms & lakehouse stack
|
||
|
|
"confluent": "https://www.confluent.io/rss.xml",
|
||
|
|
"databricks": "https://www.databricks.com/blog/feed.xml",
|
||
|
|
"airflow": "https://airflow.apache.org/blog/index.xml",
|
||
|
|
"debezium": "https://debezium.io/blog.atom",
|
||
|
|
"elastic": "https://www.elastic.co/blog/feed",
|
||
|
|
"minio": "https://blog.min.io/rss.xml",
|
||
|
|
"aws-bigdata": "https://aws.amazon.com/blogs/big-data/feed/",
|
||
|
|
"google-cloud": "https://cloudblog.withgoogle.com/rss/",
|
||
|
|
"cloudflare": "https://blog.cloudflare.com/rss/",
|
||
|
|
"fb-engineering": "https://engineering.fb.com/feed/",
|
||
|
|
# Newsletters
|
||
|
|
"tldr-de": "https://tldr.tech/rss/dataengineering",
|
||
|
|
"thenewstack": "https://thenewstack.io/feed/",
|
||
|
|
"kdnuggets": "https://www.kdnuggets.com/feed",
|
||
|
|
"towardsds": "https://medium.com/feed/towards-data-science",
|
||
|
|
"redmonk": "https://redmonk.com/feed/",
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/health")
|
||
|
|
def health():
|
||
|
|
return jsonify({"status": "ok", "count": len(FEEDS), "feeds": sorted(FEEDS.keys())})
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/feed/<name>")
|
||
|
|
def feed(name):
|
||
|
|
url = FEEDS.get(name)
|
||
|
|
if not url:
|
||
|
|
return jsonify({"error": f"unknown feed: {name}", "feeds": sorted(FEEDS.keys())}), 404
|
||
|
|
parsed = feedparser.parse(url)
|
||
|
|
items = [
|
||
|
|
{
|
||
|
|
"title": e.get("title", "Untitled"),
|
||
|
|
"link": e.get("link", ""),
|
||
|
|
"published": e.get("published", e.get("updated", "")),
|
||
|
|
}
|
||
|
|
for e in parsed.entries[:10]
|
||
|
|
]
|
||
|
|
return jsonify({"title": parsed.feed.get("title", name), "items": items})
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
app.run(host="0.0.0.0", port=8090)
|