diff --git a/src/routers/public_api.py b/src/routers/public_api.py index d8c4427..a30b2b6 100644 --- a/src/routers/public_api.py +++ b/src/routers/public_api.py @@ -160,6 +160,102 @@ async def get_lagebild(db=Depends(db_dependency)): } + + +@router.get("/globe-feed", dependencies=[Depends(verify_api_key)]) +async def get_globe_feed( + incident_id: int = None, + db=Depends(db_dependency), +): + """Globe-Feed: Locations + Artikel + Summary fuer beliebige Lage(n) als GeoJSON.""" + import json as _json + + # Wenn keine ID: alle oeffentlichen Lagen + if incident_id: + cursor = await db.execute( + "SELECT id, title, description, summary, updated_at, type, status, category_labels " + "FROM incidents WHERE id = ? AND status != 'deleted'", (incident_id,) + ) + else: + cursor = await db.execute( + "SELECT id, title, description, summary, updated_at, type, status, category_labels " + "FROM incidents WHERE visibility = 'public' AND status IN ('active','archived') " + "ORDER BY updated_at DESC LIMIT 20" + ) + incidents = [dict(r) for r in await cursor.fetchall()] + if not incidents: + return {"type": "FeatureCollection", "features": [], "incidents": []} + + inc_ids = [i["id"] for i in incidents] + ids_sql = ",".join(str(i) for i in inc_ids) + + # Locations mit Artikel-Headlines + cursor = await db.execute( + f"""SELECT + al.location_name_normalized as name, + ROUND(al.latitude, 4) as lat, + ROUND(al.longitude, 4) as lon, + al.country_code, + al.category, + al.incident_id, + COUNT(*) as article_count, + MAX(al.confidence) as confidence, + GROUP_CONCAT(DISTINCT a.headline_de) as headlines + FROM article_locations al + LEFT JOIN articles a ON al.article_id = a.id + WHERE al.incident_id IN ({ids_sql}) + GROUP BY al.location_name_normalized, al.incident_id + ORDER BY article_count DESC + LIMIT 500""" + ) + locations = [dict(r) for r in await cursor.fetchall()] + + # Als GeoJSON + features = [] + for loc in locations: + inc = next((i for i in incidents if i["id"] == loc["incident_id"]), None) + headlines = (loc.get("headlines") or "").split(",")[:5] + features.append({ + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [loc["lon"], loc["lat"]]}, + "properties": { + "name": loc["name"], + "country": loc["country_code"], + "category": loc["category"], + "article_count": loc["article_count"], + "confidence": loc["confidence"], + "incident_id": loc["incident_id"], + "incident_title": inc["title"] if inc else "", + "headlines": headlines, + }, + }) + + # Incident-Summaries + inc_summaries = [] + for i in incidents: + cat_labels = None + if i.get("category_labels"): + try: + cat_labels = _json.loads(i["category_labels"]) + except Exception: + pass + inc_summaries.append({ + "id": i["id"], + "title": i["title"], + "type": i["type"], + "status": i["status"], + "summary": (i.get("summary") or "")[:1000], + "updated_at": i["updated_at"], + "category_labels": cat_labels, + }) + + return { + "type": "FeatureCollection", + "features": features, + "incidents": inc_summaries, + "generated_at": datetime.now(TIMEZONE).isoformat(), + } + @router.get("/lagebild/snapshot/{snapshot_id}", dependencies=[Depends(verify_api_key)]) async def get_snapshot(snapshot_id: int, db=Depends(db_dependency)): """Liefert einen historischen Snapshot."""