Phase 15: language + bias als Spalten, Filter, Edit-Form

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.
Dieser Commit ist enthalten in:
claude-dev
2026-05-09 04:35:08 +00:00
Ursprung ff83f64aa6
Commit c86b2a0056
3 geänderte Dateien mit 91 neuen und 14 gelöschten Zeilen

Datei anzeigen

@@ -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),