feat(public-mood): Stimmungs-Kachel aus Foren-Quellen

Eigene Pipeline-Stufe nach factcheck, vor summary, die Foren-Artikel
(media_type='forum') zu einer Themen-Zusammenfassung verarbeitet. Wird als
separate Dashboard-Kachel "Öffentliche Stimmung" angezeigt — getrennt von
Lagebild und Faktencheck, damit anonyme Forenposts nicht mit belegter
Faktenlage verwechselt werden.

- DB-Migration: incidents.public_mood (TEXT) + public_mood_updated_at (TS).
- pipeline_tracker: neuer Pipeline-Step "public_mood" (DE/EN-Labels).
- analyzer.generate_public_mood: Haiku-Call der Foren-Beitraege pro Quelle
  gruppiert und 3-6 thematische Bullets erzeugt, mit expliziter Quellen-
  Herkunft pro Bullet. Bei zu duennem Material gibt's keinen Output.
- orchestrator: neuer Schritt zwischen Factcheck und Summary. Laedt alle
  Foren-Artikel der Lage (via JOIN auf sources), uebergibt sie an den
  Stimmungs-Agent, speichert den Markdown-Text in incidents.public_mood.
- Topic-Filter (analyzer.filter_relevant_articles) markiert Foren-Quellen
  mit [FORUM]-Tag und bekommt im Prompt die Regel, Foren-Artikel weicher
  zu bewerten (Lage-Keyword im Titel reicht). Sie sollen in der Stimmungs-
  Kachel landen, nicht voreilig verworfen werden.
- IncidentResponse-Modell: public_mood/public_mood_updated_at ergaenzt.
- Frontend: neuer Tab "Öffentliche Stimmung" (nur sichtbar wenn Inhalt da),
  eigene Kachel mit Warn-Hinweis "keine Faktenlage". UI.renderPublicMood
  als einfacher Bullet-Renderer.
- dashboard.html Cache-Buster fuer components.js + app.js gebumpt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
2026-05-22 00:20:17 +02:00
Ursprung 379d14518c
Commit d65f0180d9
8 geänderte Dateien mit 243 neuen und 3 gelöschten Zeilen

Datei anzeigen

@@ -429,6 +429,16 @@ async def init_db():
await db.commit()
logger.info("Migration: latest_developments zu incidents hinzugefuegt")
if "public_mood" not in columns:
await db.execute("ALTER TABLE incidents ADD COLUMN public_mood TEXT")
await db.commit()
logger.info("Migration: public_mood zu incidents hinzugefuegt")
if "public_mood_updated_at" not in columns:
await db.execute("ALTER TABLE incidents ADD COLUMN public_mood_updated_at TIMESTAMP")
await db.commit()
logger.info("Migration: public_mood_updated_at zu incidents hinzugefuegt")
# Migration: Tabelle podcast_transcripts (URL-Cache fuer Transkripte)
cursor = await db.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name='podcast_transcripts'"