- 16 aktive BL-Adapter + BUND (parlamente.py 3397 LOC) - drucksache_typen.py: BL-spezifische Typ-Normalisierung (#127) - mail.py: SMTP + Daily-Digest (#124) - clustering.py: Embedding-Naehe-Graph + Bubble-Chart (#105) - redline_utils.py: §INS§/§DEL§-Parser + PDF-Cite-URL-Builder - embeddings v3->v4 Migration (#123, ADR 0006) - chart.js + d3.v7 als statische Assets fuer Auswertungen-Cluster Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
89 lines
2.8 KiB
Python
89 lines
2.8 KiB
Python
"""Drucksache-Typ-Normalisierung (#127).
|
|
|
|
Jeder Landtag hat eigene Bezeichnungen für Dokumenttypen. Dieses Modul
|
|
normalisiert sie auf einheitliche Kategorien und bestimmt ob eine
|
|
Drucksache abstimmbar ist (= GWÖ-Bewertung sinnvoll).
|
|
"""
|
|
|
|
# Normierte Kategorien
|
|
ANTRAG = "antrag"
|
|
GESETZENTWURF = "gesetzentwurf"
|
|
AENDERUNGSANTRAG = "aenderungsantrag"
|
|
DRINGLICHKEITSANTRAG = "dringlichkeitsantrag"
|
|
ENTSCHLIESSUNGSANTRAG = "entschliessungsantrag"
|
|
BESCHLUSSEMPFEHLUNG = "beschlussempfehlung"
|
|
KLEINE_ANFRAGE = "kleine_anfrage"
|
|
GROSSE_ANFRAGE = "grosse_anfrage"
|
|
UNTERRICHTUNG = "unterrichtung"
|
|
PETITION = "petition"
|
|
WAHLVORSCHLAG = "wahlvorschlag"
|
|
BERICHT = "bericht"
|
|
SONSTIGE = "sonstige"
|
|
|
|
ABSTIMMBARE_TYPEN = {
|
|
ANTRAG,
|
|
GESETZENTWURF,
|
|
AENDERUNGSANTRAG,
|
|
DRINGLICHKEITSANTRAG,
|
|
ENTSCHLIESSUNGSANTRAG,
|
|
}
|
|
|
|
# Übersetzungstabelle: Original-Typ (lowercase) → normierter Typ.
|
|
# Keys werden case-insensitive + substring-matched.
|
|
# Reihenfolge: spezifischere zuerst (z.B. "kleine anfrage" vor "anfrage").
|
|
_TYP_MAP = [
|
|
# Abstimmbar
|
|
("gesetzentwurf", GESETZENTWURF),
|
|
("änderungsantrag", AENDERUNGSANTRAG),
|
|
("aenderungsantrag", AENDERUNGSANTRAG),
|
|
("dringlichkeitsantrag", DRINGLICHKEITSANTRAG),
|
|
("entschließungsantrag", ENTSCHLIESSUNGSANTRAG),
|
|
("entschliessungsantrag", ENTSCHLIESSUNGSANTRAG),
|
|
("antrag gemäß", ANTRAG),
|
|
("antrag", ANTRAG),
|
|
# Nicht abstimmbar
|
|
("kleine anfrage", KLEINE_ANFRAGE),
|
|
("große anfrage", GROSSE_ANFRAGE),
|
|
("grosse anfrage", GROSSE_ANFRAGE),
|
|
("anfrage", KLEINE_ANFRAGE),
|
|
("beschlussempfehlung", BESCHLUSSEMPFEHLUNG),
|
|
("unterrichtung", UNTERRICHTUNG),
|
|
("bericht", BERICHT),
|
|
("mitteilung", UNTERRICHTUNG),
|
|
("vorlage", UNTERRICHTUNG),
|
|
("petition", PETITION),
|
|
("wahlvorschlag", WAHLVORSCHLAG),
|
|
("stellungnahme", SONSTIGE),
|
|
("drucksache", SONSTIGE),
|
|
]
|
|
|
|
|
|
def normalize_typ(original: str) -> str:
|
|
"""Normalisiert einen BL-spezifischen Typ-String auf eine Kategorie.
|
|
|
|
Case-insensitiv, Substring-Match, spezifischere Patterns zuerst.
|
|
"""
|
|
if not original:
|
|
return SONSTIGE
|
|
low = original.lower().strip()
|
|
for pattern, norm in _TYP_MAP:
|
|
if pattern in low:
|
|
return norm
|
|
return SONSTIGE
|
|
|
|
|
|
def ist_abstimmbar(typ_normiert: str) -> bool:
|
|
"""Prüft ob ein normierter Typ zur Abstimmung steht.
|
|
|
|
``sonstige`` wird durchgelassen (benefit of the doubt) — wenn der
|
|
Adapter den Typ nicht bestimmen kann (z.B. NRW liefert nur
|
|
"Drucksache"), wird der echte Check erst beim Analysieren gemacht
|
|
(aus dem Dokument-Text).
|
|
"""
|
|
return typ_normiert in ABSTIMMBARE_TYPEN or typ_normiert == SONSTIGE
|
|
|
|
|
|
def ist_abstimmbar_original(original: str) -> bool:
|
|
"""Convenience: prüft direkt am Original-Typ-String."""
|
|
return ist_abstimmbar(normalize_typ(original))
|