Ersetzt den rohen JOIN ueber article_locations x articles (bei Iran
21.814 Zeilen, 11 MB Payload) durch drei kleine aggregierte Queries:
1. Orte per GROUP BY (name, lat, lon) — direkt die Ergebnismenge.
2. Kategorien pro Ort per GROUP BY fuer die dominante Kategorie.
3. Sample-Artikel (max. 10 pro Ort) via ROW_NUMBER() OVER PARTITION BY.
Response-Shape unveraendert ({category_labels, locations: [...]}), keine
Frontend-Aenderung noetig. Priorisierung primary > secondary > tertiary >
mentioned bleibt erhalten.
Erwarteter Effekt: Iran-Locations 11 MB -> <500 KB; Query-Zeit sinkt
zusaetzlich, da kein 21k-Zeilen-JOIN mehr materialisiert werden muss.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend:
- GET /{id}/articles paginiert jetzt per limit/offset (Default 500,
Max 1000) und unterstuetzt optionalen search-Parameter (LIKE ueber
headline/source/content). Response-Shape: {total, articles}.
- Neuer Endpunkt GET /{id}/articles/sources-summary liefert pro Quelle
{source, article_count, languages} sowie language_counts gesamt —
serverseitige Aggregation, unabhaengig von Artikel-Paginierung.
- Neuer Endpunkt GET /{id}/articles/timeline-buckets?granularity=hour|day|week|month
aggregiert Artikel + Snapshot-Counts pro Zeitbucket (fuer spaetere
Timeline-Zaehler ueber die volle Historie).
- database.py: Index idx_articles_incident_collected auf
(incident_id, collected_at DESC) fuer schnelleres ORDER BY + Pagination.
Frontend:
- api.js: getArticles({limit, offset, search}),
getArticlesSourcesSummary(), getArticlesTimelineBuckets().
- app.js: loadIncidentDetail laedt erste Seite (500 Artikel), startet
_loadSourcesSummary parallel und zieht restliche Artikel
batchweise (500er Bloecke) im Hintergrund nach, bis _currentArticlesTotal
erreicht ist. rerenderTimeline nach jedem Batch.
- components.js: renderSourceOverviewFromSummary(data) rendert aus
Aggregat-Daten (ersetzt clientseitige Zaehlung ueber geladene Artikel).
Hintergrund: /articles lieferte bei der Iran-Lage 22 MB (17.286 Artikel
mit SELECT *). Die Erstantwort sinkt auf ~650 KB (500 Artikel), weitere
werden progressiv im Hintergrund nachgeladen. Quellenuebersicht zeigt
dank Aggregat-Endpunkt sofort alle Quellen + Sprachen komplett.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend:
- GET /{id}/snapshots liefert nur noch schlanke Shape (Metadaten +
SUBSTR(summary,1,300) AS summary_preview), kein Volltext, kein sources_json.
- Neuer Endpunkt GET /{id}/snapshots/{snapshot_id} fuer Volltext-Lazy-Load.
- Neuer Endpunkt GET /{id}/snapshots/search?q=... fuer serverseitige
Volltextsuche ueber alle Snapshots einer Lage.
Frontend:
- api.js: getSnapshot() und searchSnapshots() ergaenzt.
- app.js: _snapshotFullCache, Volltext wird beim Aufklappen eines
Snapshot-Eintrags per lazyLoadSnapshotDetail() nachgeladen und gecacht.
- Suche ueber Snapshots filtert weiterhin clientseitig ueber summary_preview.
Hintergrund: Bei grossen Lagen (Iran-Lage: 347 Snapshots) fiel die
Snapshots-Listenantwort mit Volltext-Summaries auf ~54 MB. Die Liste
faellt damit auf ~150 KB; Volltexte werden nur on-demand geladen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Alle sichtbaren "Executive Summary"-Bezeichnungen durch "Zusammenfassung" ersetzt
(PDF/DOCX-Überschrift, Dateiname, Fallback-Texte)
- Deckblatt-Farben von #888/#aaa auf Navy #0a1832 geändert für
bessere Lesbarkeit beim Druck (PDF-Template + DOCX)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Beide Prompts (Research + Adhoc) definieren jetzt explizit:
- Modell ist Recherche-Planer, nicht Faktenbehaupter
- Thema muss nicht bekannt oder verifiziert werden
- Briefing IMMER erstellen, keine Rueckfragen/Disclaimer
- Recherche-Schwerpunkte praxisnaeher formuliert
Behebt sporadische Verweigerungen bei unbekannten Faellen.
API /incidents/refreshing gibt jetzt auch queued IDs mit Position
und den aktuell laufenden Task zurueck.
Frontend nutzt started_at aus der API fuer Timer-Wiederherstellung.
Queued Lagen werden mit korrekter Position wiederhergestellt.
Aktiv laufender Task wird als researching angezeigt statt queued.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Neuer raw_text Parameter in call_claude() umgeht den JSON-System-Prompt.
Haiku gibt direkt lesbaren Text zurück statt JSON-Objekte.
Gesamtes JSON-Parsing (_json_to_text, Markdown-Strip) entfernt.
Claude CLI gibt bei tools=None oft Antworten in Markdown-Code-Blocks
zurueck (dreifache Backticks json...Backticks). Diese werden jetzt vor
dem JSON-Parse per Regex entfernt.
Haiku gibt oft tief verschachtelte JSON-Objekte zurück statt reinem
Text. Neue _json_to_text() Funktion konvertiert beliebige JSON-Strukturen
rekursiv in lesbaren Fliesstext mit Aufzaehlungen.
call_claude erzwingt bei tools=None JSON-Output per System-Prompt.
Haiku wrapped den generierten Text dann in ein JSON-Objekt.
Fix: JSON parsen und erstes String-Feld extrahieren.
KI-gestütztes Prompt Enhancement: Button generiert per Haiku aus dem
Titel eine strukturierte Beschreibung. Unterscheidet zwischen
Live-Monitoring (kompakte Vorfallsbeschreibung) und Recherche
(strukturiertes Briefing mit Schwerpunkten und Suchbegriffen).
- Neuer Endpoint POST /api/incidents/enhance-description
- Button erscheint für beide Lage-Typen, aktiv ab 3 Zeichen Titel
- Info-Hinweis wechselt je nach Typ mit Beispiel
- Spinner-Animation während der Generierung
4 feste Farbstufen (primary/secondary/tertiary/mentioned) mit
variablen Labels pro Lage, die von Haiku generiert werden.
- DB: category_labels Spalte in incidents, alte Kategorien migriert
(target->primary, response/retaliation->secondary, actor->tertiary)
- Geoparsing: generate_category_labels() + neuer Prompt mit neuen Keys
- QC: Kategorieprüfung auf neue Keys umgestellt
- Orchestrator: Tuple-Rückgabe + Labels in DB speichern
- API: category_labels im Locations- und Lagebild-Response
- Frontend: Dynamische Legende aus API-Labels mit Fallback-Defaults
- Migrationsskript für bestehende Lagen
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- PRAGMA busy_timeout=5000 in get_db() hinzugefügt (SQLite wartet bis zu 5s auf Lock-Freigabe)
- Try/except im Delete-Endpoint: gibt HTTP 409 statt 500 bei Lock-Konflikt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Neue Tabelle user_excluded_domains für benutzerspezifische Ausschlüsse
- Domain-Ausschlüsse wirken nur für den jeweiligen User, nicht org-weit
- user_id wird durch die gesamte Pipeline geschleust (Orchestrator → Researcher → RSS-Parser)
- Grundquellen (is_global) können nicht mehr bearbeitet/gelöscht werden im Frontend
- Grundquelle-Badge bei globalen Quellen statt Edit/Delete-Buttons
- Filter Von mir ausgeschlossen im Quellen-Modal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- geoparsing.py: Komplett-Rewrite (spaCy NER + Nominatim -> Haiku + geonamescache)
- orchestrator.py: incident_context an geoparse_articles, category in INSERT
- incidents.py: incident_context aus DB laden und an Geoparsing uebergeben
- public_api.py: Locations aggregiert im Lagebild-Endpoint
- components.js: response-Kategorie neben retaliation (beide akzeptiert)
- requirements.txt: spaCy und geopy entfernt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ALLE Timestamps einheitlich Europe/Berlin (kein UTC mehr)
- DB-Migration: 1704 bestehende Timestamps von UTC nach Berlin konvertiert
- Auto-Refresh Timer Fix: ORDER BY id DESC statt completed_at DESC
(verhindert falsche Sortierung bei gemischten Timestamp-Formaten)
- started_at statt completed_at fuer Timer-Vergleich (konsistenter)
- Manuelle Refreshes werden bei Intervall-Pruefung beruecksichtigt
- Debug-Logging fuer Auto-Refresh Entscheidungen
- astimezone() fuer Timestamps mit Offset-Info
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Korrektur: Alle DB-Timestamps (refresh_log, created_at, updated_at,
auth, notifications) bleiben UTC fuer korrekte Timer-Vergleiche.
Europe/Berlin nur fuer angezeigte Werte (Exporte, Prompts, API).
Verhindert zu fruehes Ausloesen des Auto-Refresh-Timers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Inkonsistenz behoben: Manche Timestamps wurden in UTC, andere in
Berlin-Zeit gespeichert. Das fuehrte zu Fehlern beim Auto-Refresh
und Faktencheck, da Zeitvergleiche falsche Ergebnisse lieferten.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Endpunkt startet async Background-Task statt synchron zu warten
- Batch-Verarbeitung (50 Artikel pro Batch)
- Neuer Status-Endpunkt GET /incidents/{id}/geoparse-status
- Frontend pollt alle 3s und zeigt Fortschritt im Button (z.B. "150/427 Artikel...")
- Kein Timeout-Problem mehr bei grossen Lagen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Neues Geoparsing-Modul (spaCy NER + geonamescache/Nominatim)
- article_locations-Tabelle mit Migration
- Pipeline-Integration nach Artikel-Speicherung
- API-Endpunkt GET /incidents/{id}/locations
- Leaflet.js + MarkerCluster im Dashboard-Grid
- Theme-aware Kartenkacheln (CartoDB dark / OSM light)
- Gold-Akzent MarkerCluster, Popup mit Artikelliste
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>