From c86b2a0056002de4654e664bd0126ae0478964ec Mon Sep 17 00:00:00 2001 From: claude-dev Date: Sat, 9 May 2026 04:35:08 +0000 Subject: [PATCH] Phase 15: language + bias als Spalten, Filter, Edit-Form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bisher waren die DB-Felder sources.language und sources.bias zwar gepflegt (254/275 Quellen mit bias, 254 mit language), aber in der Verwaltung nicht sichtbar. Der Admin konnte nicht filtern oder editieren. Backend (routers/sources.py) - GlobalSourceCreate + GlobalSourceUpdate Pydantic-Modelle: language + bias als Optional[str] erweitert (max 100 / 500 Zeichen). - SOURCE_UPDATE_COLUMNS: language + bias hinzu. - INSERT in create_global_source: schreibt language + bias mit. - Neuer Endpoint GET /api/sources/global/languages: distinct language-Werte fuer Frontend-Filter-Dropdown. Frontend HTML (dashboard.html) - Grundquellen-Filter-Bar: Sprachen-Dropdown ergaenzt. - Grundquellen-Tabellenkopf: 2 neue Spalten Sprache (sortable) + Bias. - modalSource: 2 neue Felder language (mit datalist Vorschlaegen) + bias. - Kundenquellen-Filter-Bar: Sprachen-Dropdown. - Kundenquellen-Tabellenkopf: Sprache (sortable) + Bias. Frontend JS (sources.js) - loadGlobalSources lädt /languages parallel zu /global + /global/stats, populiert beide Sprache-Dropdowns + datalist im Edit-Modal. - renderGlobalSources: cols 11 -> 13, language+bias-Zellen (Bias mit Tooltip fuer Lang-Texte). - filterGlobalSources: Sprache-Filter, Bias in Suche. - editGlobalSource: language + bias laden. - Form-Submit: language + bias mitgesendet. - renderTenantSources: cols 8 -> 10, language+bias-Zellen. - tenantFilters um language erweitert, applyTenantFilterAndSort prueft. Cache-Buster ?v=20260509 (heute) bleibt - Tag wechselt erst morgen. --- src/routers/sources.py | 31 ++++++++++++++++++++++++---- src/static/dashboard.html | 31 +++++++++++++++++++++++----- src/static/js/sources.js | 43 ++++++++++++++++++++++++++++++++++----- 3 files changed, 91 insertions(+), 14 deletions(-) diff --git a/src/routers/sources.py b/src/routers/sources.py index 3974de4..276e953 100644 --- a/src/routers/sources.py +++ b/src/routers/sources.py @@ -24,7 +24,7 @@ logger = logging.getLogger("verwaltung.sources") router = APIRouter(prefix="/api/sources", tags=["sources"]) -SOURCE_UPDATE_COLUMNS = {"name", "url", "domain", "source_type", "category", "status", "notes"} +SOURCE_UPDATE_COLUMNS = {"name", "url", "domain", "source_type", "category", "status", "notes", "language", "bias"} @router.get("/meta") @@ -46,6 +46,8 @@ class GlobalSourceCreate(BaseModel): category: str = Field(default="sonstige") status: str = Field(default="active", pattern="^(active|inactive)$") notes: Optional[str] = None + language: Optional[str] = Field(default=None, max_length=100) + bias: Optional[str] = Field(default=None, max_length=500) class GlobalSourceUpdate(BaseModel): @@ -56,6 +58,8 @@ class GlobalSourceUpdate(BaseModel): category: Optional[str] = None status: Optional[str] = Field(default=None, pattern="^(active|inactive)$") notes: Optional[str] = None + language: Optional[str] = Field(default=None, max_length=100) + bias: Optional[str] = Field(default=None, max_length=500) @router.get("/global") @@ -139,9 +143,10 @@ async def create_global_source( ) cursor = await db.execute( - """INSERT INTO sources (name, url, domain, source_type, category, status, notes, added_by, tenant_id) - VALUES (?, ?, ?, ?, ?, ?, ?, 'system', NULL)""", - (data.name, data.url, data.domain, data.source_type, data.category, data.status, data.notes), + """INSERT INTO sources (name, url, domain, source_type, category, status, notes, language, bias, added_by, tenant_id) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'system', NULL)""", + (data.name, data.url, data.domain, data.source_type, data.category, data.status, data.notes, + data.language, data.bias), ) src_id = cursor.lastrowid await db.commit() @@ -223,6 +228,24 @@ async def delete_global_source( + + + +@router.get("/global/languages") +async def get_global_languages( + admin: dict = Depends(get_current_admin), + db: aiosqlite.Connection = Depends(db_dependency), +): + """Distinct language-Werte aus Grundquellen - für Frontend-Filter-Dropdown.""" + cur = await db.execute(""" + SELECT DISTINCT language + FROM sources + WHERE tenant_id IS NULL AND language IS NOT NULL AND language != '' + ORDER BY language + """) + return [r["language"] for r in await cur.fetchall()] + + @router.get("/global/stats") async def get_global_stats( admin: dict = Depends(get_current_admin), diff --git a/src/static/dashboard.html b/src/static/dashboard.html index cdbd73a..08893ca 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -313,6 +313,9 @@ + @@ -330,6 +333,8 @@ Artikel Aktivität Sperren + Sprache + Bias Letzter Treffer Health Status @@ -356,6 +361,9 @@ +