Neuer API-Endpoint: /api/public/globe-feed fuer Globe-Integration

Liefert Locations + Artikel-Headlines + Summaries als GeoJSON.
Flexible Lage-Auswahl per incident_id oder alle oeffentlichen.
Farbkodiert nach Kategorie (primary/secondary/tertiary/mentioned).
Dieser Commit ist enthalten in:
Claude Dev
2026-03-24 13:11:16 +01:00
Ursprung e64447ab7f
Commit 094f2463bb

Datei anzeigen

@@ -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)]) @router.get("/lagebild/snapshot/{snapshot_id}", dependencies=[Depends(verify_api_key)])
async def get_snapshot(snapshot_id: int, db=Depends(db_dependency)): async def get_snapshot(snapshot_id: int, db=Depends(db_dependency)):
"""Liefert einen historischen Snapshot.""" """Liefert einen historischen Snapshot."""