# 0009 — Plenarprotokoll-Parser-Registry pro Bundesland | | | |---|---| | **Status** | accepted | | **Datum** | 2026-04-28 | | **Refs** | #106, #126, ADR 0002 (Adapter-Pattern) | ## Kontext Der NRW-Plenarprotokoll-Parser (#106) ist deterministisch, anchor-basiert und erreicht 19/19 auf der MMP18-119-Fixture. Damit war die Architektur-Frage gelöst — aber nur fuer NRW. Andere Bundeslaender publizieren ihre Plenarprotokolle in fundamental anderen Formaten: - Hessen: HTML mit semantischen Tags pro Beschluss - Brandenburg: PDF mit Tabellen-Layout fuer Vote-Counts - Mecklenburg-Vorpommern: ParLDok-XML-Export - Berlin: PDF mit eigenem Formularkasten-Schema - ... Ein einziger Parser fuer alle BL ist nicht baubar. Die Reverse-Engineering- Arbeit pro Landtag ist substantiell und passiert phasenweise: zuerst NRW wegen der hohen Antragsdichte, danach BL fuer BL nach Bedarf. Das Adapter-Pattern aus ADR 0002 (`ParlamentAdapter`) hat dieses Problem fuer die Antrags-Suche bereits geloest. Plenarprotokoll-Parser ist die naechste Familie mit derselben Form: pro BL eine eigene Implementierung, ein gemeinsamer Aufruf-Vertrag, ein Registry-Lookup. ## Optionen ### Option A — Eine grosse Datei mit If-Else-Dispatch Eine einzige `app/protokoll_parser.py`-Datei mit einem `parse_protocol(bl, pdf)`, das je nach BL andere Funktionen ruft. **Vorteile:** flach, einfach. **Nachteile:** waechst zur 2000-LOC-Datei, BL-spezifische Reverse-Engineering- Notizen und Helper-Functions vermischen sich, schlechte Test-Isolation. ### Option B — OOP-Hierarchie mit `ProtokollParserBase` als ABC Abstrakte Basisklasse mit `parse(pdf_path) -> list[VoteResult]`, konkrete Subklassen pro BL. **Vorteile:** typisierter Vertrag. **Nachteile:** Boilerplate fuer Klassen-Definitionen ohne Mehrwert, weil der NRW-Parser keine Instanz-State hat (alles `def`-Funktionen, keine `self.x`). ### Option C — Sub-Package mit Funktions-Registry (gewaehlt) `app/protokoll_parsers/` als Sub-Package, pro BL eine eigene Datei (`nrw.py`, `mv.py`, `he.py`, ...) die mindestens `parse_protocol(pdf_path: str) -> list[dict]` exportiert. Eine `PROTOKOLL_PARSERS`-Dict in `__init__.py` mappt BL-Code → Funktion. Das BL-uebergreifende `parse_protocol(bl, pdf_path)` macht den Lookup. **Vorteile:** - Konsistent mit dem `ADAPTERS`-Dict in `parlamente.py` (ADR 0002) - BL-Code lebt in eigener Datei mit eigenen Helpern und Notizen - Neue BL = neue Datei + ein Eintrag in `__init__.py`, kein Refactoring - Tests pro BL in eigener Test-Datei (`tests/test_protokoll_parsers_.py`) - Parser-Funktionen bleiben simpel, kein OOP-Overhead **Nachteile:** - Vertrag ist nur per Convention dokumentiert (nicht via Type-System erzwingbar) — dafuer ein Schema-Test in `test_protokoll_parsers.py` als Sicherheitsnetz. ## Entscheidung **Option C.** Konkret: ``` app/protokoll_parsers/ ├── __init__.py # Registry + parse_protocol(bl, pdf) + supported_bundeslaender() ├── nrw.py # NRW v5 (vorher app/protokoll_parser_nrw.py) └── .py # je BL eine Datei, sobald implementiert ``` **Vertrag fuer jeden Parser** (verbindlich): ```python def parse_protocol(pdf_path: str) -> list[dict]: """Returns: [ { "drucksache": str | None, "ergebnis": str, # angenommen/abgelehnt/ueberwiesen/... "einstimmig": bool, "kind": str, # parser-intern, fuer Debug "votes": { "ja": list[str], # Fraktions-Codes (CDU, SPD, GRUENE, ...) "nein": list[str], "enthaltung": list[str], }, }, ... ]""" ``` **Naming:** Datei-Stem = lowercase BL-Code (`nrw.py`, `mv.py`, ...). Registry-Key = uppercase BL-Code (`"NRW"`, `"MV"`). **Konsumenten** rufen `parse_protocol(bundesland, pdf_path)` aus dem Sub-Package, nicht direkt eine BL-Datei. ## Konsequenzen ### Positiv - Folge-BL-Implementierungen ohne Refactoring der Bestands-Logik. - Reverse-Engineering-Notizen leben pro BL in einer Datei statt verteilt ueber eine Mega-Datei. - Der `supported_bundeslaender()`-Helper macht in CLI und UI sofort sichtbar, wo Daten verfuegbar sind und wo nicht. - Neue Adapter-Test-Files folgen demselben Schema (`test_protokoll_parsers_.py`). ### Negativ - Schema-Vertrag nur per Convention (kein TypedDict). Dafuer ein Smoke-Test in `tests/test_protokoll_parsers.py`, der pro registriertem Parser die Result-Keys pruefen wird, sobald >1 Implementation existiert. ### Folgen fuer andere ADRs - ADR 0002 (Adapter-Pattern) bleibt gueltig; dieses ADR ueberbruckt es nicht, sondern wendet das gleiche Muster auf eine zweite Adapter-Familie an. - Folge-Issues (HE/BB/MV/BE/...) sind reine Implementation-Tickets ohne Architektur-Diskussion — der Vertrag ist hier festgelegt.