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:
@@ -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."""
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren