Kartenfeature: Geoparsing + Leaflet-Karte im Dashboard

- 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>
Dieser Commit ist enthalten in:
claude-dev
2026-03-04 22:04:07 +01:00
Ursprung 23ac6d6fd7
Commit 4bfc626067
11 geänderte Dateien mit 746 neuen und 10 gelöschten Zeilen

Datei anzeigen

@@ -167,6 +167,22 @@ CREATE TABLE IF NOT EXISTS incident_subscriptions (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, incident_id)
);
CREATE TABLE IF NOT EXISTS article_locations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
article_id INTEGER REFERENCES articles(id) ON DELETE CASCADE,
incident_id INTEGER REFERENCES incidents(id) ON DELETE CASCADE,
location_name TEXT NOT NULL,
location_name_normalized TEXT,
country_code TEXT,
latitude REAL NOT NULL,
longitude REAL NOT NULL,
confidence REAL DEFAULT 0.0,
source_text TEXT,
tenant_id INTEGER REFERENCES organizations(id)
);
CREATE INDEX IF NOT EXISTS idx_article_locations_incident ON article_locations(incident_id);
CREATE INDEX IF NOT EXISTS idx_article_locations_article ON article_locations(article_id);
"""
@@ -366,6 +382,29 @@ async def init_db():
except Exception:
pass
# Migration: article_locations-Tabelle (fuer bestehende DBs)
cursor = await db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='article_locations'")
if not await cursor.fetchone():
await db.executescript("""
CREATE TABLE IF NOT EXISTS article_locations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
article_id INTEGER REFERENCES articles(id) ON DELETE CASCADE,
incident_id INTEGER REFERENCES incidents(id) ON DELETE CASCADE,
location_name TEXT NOT NULL,
location_name_normalized TEXT,
country_code TEXT,
latitude REAL NOT NULL,
longitude REAL NOT NULL,
confidence REAL DEFAULT 0.0,
source_text TEXT,
tenant_id INTEGER REFERENCES organizations(id)
);
CREATE INDEX IF NOT EXISTS idx_article_locations_incident ON article_locations(incident_id);
CREATE INDEX IF NOT EXISTS idx_article_locations_article ON article_locations(article_id);
""")
await db.commit()
logger.info("Migration: article_locations-Tabelle erstellt")
# Verwaiste running-Eintraege beim Start als error markieren (aelter als 15 Min)
await db.execute(
"""UPDATE refresh_log SET status = 'error', error_message = 'Verwaist beim Neustart',