feat(sources): primary_language Spalte + ISO-Backfill + org-relativer Feed-Bucket

- Neue Spalte sources.primary_language (ISO-2-Code) mit Backfill aus dem
  Freitext-Feld language (Erste Sprache vor /-Trennung). Edge-Cases wie
  Iran Military Magazine (English) [Farsi/Arabisch] landen als fa und
  koennen ueber das Verwaltungsportal manuell justiert werden.
- get_source_rules(tenant_id) bestimmt die Org-Sprache und bucketed Feeds
  nach primary (=Org-Sprache) / international (=alle anderen) / behoerden
  (Kategorie behoerde). Bei tenant_id=None oder Helper-Fehler default de.
- rss_parser.search_feeds unveraendert in Logik (international=False
  laesst weiterhin alle ausser dem international-Bucket durch), Kommentare
  generischer formuliert.

Phase 3 von 8 (eng_demo / Org-Sprache).
Dieser Commit ist enthalten in:
Claude Code
2026-05-13 20:57:51 +00:00
Ursprung f68d25dbce
Commit 9754dcb4ef
3 geänderte Dateien mit 61 neuen und 9 gelöschten Zeilen

Datei anzeigen

@@ -692,12 +692,24 @@ async def get_source_rules(tenant_id: int = None) -> dict:
Returns:
dict mit:
- excluded_domains: Liste ausgeschlossener Domains
- rss_feeds: Dict mit Kategorien deutsch/international/behoerden
- rss_feeds: Dict mit Kategorien primary/international/behoerden, wobei
'primary' diejenigen Feeds enthaelt, deren primary_language der
Ausgabesprache der Org entspricht. Andere Sprachen wandern in
'international'. Bei tenant_id=None wird die Org-Sprache 'de' angenommen.
"""
from database import get_db
from services.org_settings import get_org_language
db = await get_db()
try:
# Ausgabesprache der Org bestimmen (Default 'de')
org_lang_iso = "de"
if tenant_id:
try:
org_lang_iso = await get_org_language(db, tenant_id)
except Exception as e:
logger.warning("Konnte Org-Sprache nicht laden, default 'de': %s", e)
if tenant_id:
cursor = await db.execute(
"SELECT * FROM sources WHERE status = 'active' AND (tenant_id IS NULL OR tenant_id = ?)",
@@ -710,7 +722,7 @@ async def get_source_rules(tenant_id: int = None) -> dict:
sources = [dict(row) for row in await cursor.fetchall()]
excluded_domains = []
rss_feeds = {"deutsch": [], "international": [], "behoerden": []}
rss_feeds = {"primary": [], "international": [], "behoerden": []}
for source in sources:
if source["source_type"] == "excluded":
@@ -718,13 +730,16 @@ async def get_source_rules(tenant_id: int = None) -> dict:
elif source["source_type"] == "rss_feed" and source["url"]:
feed_entry = {"name": source["name"], "url": source["url"]}
cat = source["category"]
src_lang = source.get("primary_language") or "de"
if cat == "behoerde":
rss_feeds["behoerden"].append(feed_entry)
elif cat == "international":
rss_feeds["international"].append(feed_entry)
elif src_lang == org_lang_iso:
# Feed-Sprache entspricht Org-Sprache -> primary
rss_feeds["primary"].append(feed_entry)
else:
# Alle anderen Kategorien → deutsch
rss_feeds["deutsch"].append(feed_entry)
# Andere Sprache -> international (wird nur bei
# 'international'-Lagen verwendet)
rss_feeds["international"].append(feed_entry)
return {
"excluded_domains": excluded_domains,