feat(sources): externer Reputations-Layer (IFCN + EUvsDisinfo)
Externe Datenquellen (kostenlos, Open Data) ergaenzen die LLM-geschaetzte Reliability-Achse mit objektiven Signalen: - IFCN-Signatories (raw.githubusercontent.com/IFCN/verified-signatories): Plain-Text-Liste anerkannter Faktencheck-Organisationen. - EUvsDisinfo (Zenodo CSV): Pro-Kreml-Desinformations-Datenbank. Schema-Erweiterung: - ifcn_signatory, eu_disinfo_listed, eu_disinfo_case_count, eu_disinfo_last_seen, external_data_synced_at. Service src/services/external_reputation.py: - sync_ifcn_signatories(), sync_eu_disinfo(), apply_reputation_overrides(), sync_all() mit Domain-Normalisierung (lowercase, ohne www., ohne Schema). Reliability-Override-Regeln (laufen nach Approve und manuellem Sync): - ifcn_signatory=1 -> reliability=sehr_hoch - eu_disinfo_case_count >= 5 -> reliability=sehr_niedrig - eu_disinfo_case_count >= 1 -> Reliability eine Stufe runter (max niedrig) API: POST /api/sources/external-reputation/sync (Admin, BackgroundTask). Filter: ?ifcn_signatory=true, ?eu_disinfo_listed=true. UI: - Filter-Dropdown "Externe Reputation" im Quellen-Modal. - Badges: gruenes "IFCN" und rotes "EU-Desinfo (n)". - Tooltip macht Reliability-Quelle transparent: "(IFCN-Faktenchecker)", "(EU-Desinfo, n Faelle)" oder "(LLM-Schaetzung)". - "Externe Daten syncen"-Button im Review-Toolbar (Admin-only). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -176,7 +176,12 @@ CREATE TABLE IF NOT EXISTS sources (
|
||||
proposed_alignments_json TEXT,
|
||||
proposed_confidence REAL,
|
||||
proposed_reasoning TEXT,
|
||||
proposed_at TIMESTAMP
|
||||
proposed_at TIMESTAMP,
|
||||
eu_disinfo_listed INTEGER DEFAULT 0,
|
||||
eu_disinfo_case_count INTEGER DEFAULT 0,
|
||||
eu_disinfo_last_seen TIMESTAMP,
|
||||
ifcn_signatory INTEGER DEFAULT 0,
|
||||
external_data_synced_at TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS source_alignments (
|
||||
@@ -668,6 +673,20 @@ async def init_db():
|
||||
if any(c not in src_columns for c in ("political_orientation", "media_type", "reliability")):
|
||||
logger.info("Migration: Klassifikations-Spalten zu sources hinzugefuegt")
|
||||
|
||||
# Migration: externe Reputations-Daten (EUvsDisinfo + IFCN)
|
||||
for col, ddl in [
|
||||
("eu_disinfo_listed", "ALTER TABLE sources ADD COLUMN eu_disinfo_listed INTEGER DEFAULT 0"),
|
||||
("eu_disinfo_case_count", "ALTER TABLE sources ADD COLUMN eu_disinfo_case_count INTEGER DEFAULT 0"),
|
||||
("eu_disinfo_last_seen", "ALTER TABLE sources ADD COLUMN eu_disinfo_last_seen TIMESTAMP"),
|
||||
("ifcn_signatory", "ALTER TABLE sources ADD COLUMN ifcn_signatory INTEGER DEFAULT 0"),
|
||||
("external_data_synced_at", "ALTER TABLE sources ADD COLUMN external_data_synced_at TIMESTAMP"),
|
||||
]:
|
||||
if col not in src_columns:
|
||||
await db.execute(ddl)
|
||||
await db.commit()
|
||||
if any(c not in src_columns for c in ("eu_disinfo_listed", "ifcn_signatory")):
|
||||
logger.info("Migration: externe Reputations-Spalten zu sources hinzugefuegt")
|
||||
|
||||
# Migration: source_alignments-Tabelle (Mehrfach-Tags fuer geopolitische Naehe)
|
||||
cursor = await db.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='source_alignments'"
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren