"""Zentrale Konfiguration aller 16 deutschen Bundesländer. Dieses Modul ist die Single Source of Truth für alle bundeslandspezifischen Daten: Parlamente, Regierungen, Wahlperioden, Doku-Systeme, etc. Andere Module (main.py, parlamente.py, wahlprogramme.py, analyzer.py) lesen ausschließlich von hier. Stand: April 2026. Nach jeder Landtagswahl bzw. Regierungsbildung müssen die betroffenen Einträge aktualisiert werden. Datenquellen: Wikipedia, offizielle Landtagsseiten, parlamentsspiegel.de, https://github.com/okfde/dokukratie (für Doku-System-Zuordnung). """ from dataclasses import dataclass, field from typing import Optional @dataclass class Bundesland: """Konfiguration eines deutschen Bundeslands. Attributes: code: Übliches Kürzel im politischen Sprachgebrauch (NRW, BY, LSA…). Bei Mehrdeutigkeit ISO-3166-2-DE-Suffix; Sachsen-Anhalt nutzt jedoch das politisch dominante "LSA" statt ISO "ST". name: Vollständiger Landesname. parlament_name: Offizieller Name des Parlaments. wahlperiode: Aktuelle Wahlperiode als Zahl. wahlperiode_start: Beginn der aktuellen WP (ISO-Datum). naechste_wahl: Nächste reguläre Landtagswahl (ISO-Datum), oder None wenn noch nicht festgesetzt. regierungsfraktionen: Parteien der aktuellen Landesregierung in Reihenfolge der Größe. landtagsfraktionen: Alle aktuell im Landtag vertretenen Fraktionen. doku_system: Verwendetes Parlamentsdokumentationssystem. Werte: "OPAL", "StarWeb", "ParlDok", "PARDOK", "PARLIS", "PARiS", "Eigensystem". doku_base_url: Basis-URL der Parlamentsdokumentation. drucksache_format: Beispielhaftes Format einer Drucksachen-ID, z.B. "18/12345" für NRW WP18. dokukratie_scraper: Code-Name des Dokukratie-Scrapers (falls vorhanden), nützlich für künftige Adapter-Implementierung. aktiv: Ob das Bundesland im Frontend auswählbar und im Analyzer unterstützt ist. Inaktive Bundesländer werden im UI als "(bald)" angezeigt und sind disabled. anmerkung: Optionale Hinweise zu Sondersituationen (z.B. Koalitionsverhandlungen, jüngste Wahl, geschätzte Termine). """ code: str name: str parlament_name: str wahlperiode: int wahlperiode_start: str naechste_wahl: Optional[str] regierungsfraktionen: list[str] landtagsfraktionen: list[str] doku_system: str doku_base_url: str drucksache_format: str dokukratie_scraper: Optional[str] aktiv: bool = False anmerkung: str = "" # Hauptregister: code -> Bundesland-Instanz. # Reihenfolge alphabetisch nach offiziellem Namen für stabile UI-Sortierung. # Sonder-Eintrag "BUND" für den Deutschen Bundestag (technisch kein BL, # aber teilt die gesamte Adapter/Analyzer-Pipeline mit den 16 BL). BUNDESLAENDER: dict[str, Bundesland] = { "BUND": Bundesland( code="BUND", name="Deutscher Bundestag", parlament_name="Deutscher Bundestag", wahlperiode=21, wahlperiode_start="2025-03-25", # Konstituierung 21. WP nach BTW 2025 naechste_wahl="2029-09-30", # geschätzt regierungsfraktionen=["CDU", "CSU", "SPD"], # Kabinett Merz, schwarz-rot landtagsfraktionen=["CDU", "CSU", "AfD", "SPD", "GRÜNE", "LINKE", "BSW", "FDP"], doku_system="DIP", doku_base_url="https://search.dip.bundestag.de", drucksache_format="21/12345", dokukratie_scraper=None, aktiv=True, anmerkung=( "DIP-API auf search.dip.bundestag.de mit öffentlichem " "API-Key aus dip-config.js und Origin-Header-Locking auf " "https://dip.bundestag.de. ~600 Anträge pro Wahlperiode. " "Kabinett Merz seit Mai 2025 (CDU/CSU+SPD nach BSW-Aus). " "BundestagAdapter implementiert in #56." ), ), "BW": Bundesland( code="BW", name="Baden-Württemberg", parlament_name="Landtag von Baden-Württemberg", wahlperiode=17, wahlperiode_start="2021-05-01", naechste_wahl="2031-03-08", regierungsfraktionen=["GRÜNE", "CDU"], landtagsfraktionen=["GRÜNE", "CDU", "AfD", "SPD", "FDP"], doku_system="PARLIS", doku_base_url="https://parlis.landtag-bw.de", drucksache_format="17/12345", dokukratie_scraper="bw", aktiv=True, anmerkung=( "PARLIS auf parlis.landtag-bw.de läuft auf demselben " "eUI-Backend wie LSA-PADOKA und BE-PARDOK, aber mit drei " "Unterschieden: minimales lines-Schema (l1/l2/l3/l4), " "asynchrones Polling (initial → search_id → poll → " "report_id) und Hit-Records als JSON-in-HTML-Comments. " "Eigene Adapter-Klasse PARLISAdapter (#29). Wahl zum 18. " "Landtag fand am 08.03.2026 statt; Koalitionsverhandlungen " "GRÜNE+CDU laufen, Kabinett Kretschmann III geschäftsführend. " "Nach Konstituierung des 18. LT ca. Mai 2026 müssen WP und " "Wahltermin aktualisiert werden." ), ), "BY": Bundesland( code="BY", name="Bayern", parlament_name="Bayerischer Landtag", wahlperiode=19, wahlperiode_start="2023-10-30", naechste_wahl="2028-10-08", regierungsfraktionen=["CSU", "FW"], landtagsfraktionen=["CSU", "GRÜNE", "FW", "AfD", "SPD"], doku_system="Eigensystem", doku_base_url="https://www.bayern.landtag.de", drucksache_format="19/1234", dokukratie_scraper="by", anmerkung="Wahltermin 2028 noch nicht offiziell festgesetzt; Schätzung Herbst 2028.", ), "BE": Bundesland( code="BE", name="Berlin", parlament_name="Abgeordnetenhaus von Berlin", wahlperiode=19, wahlperiode_start="2023-04-27", naechste_wahl="2026-09-20", regierungsfraktionen=["CDU", "SPD"], landtagsfraktionen=["CDU", "SPD", "GRÜNE", "LINKE", "AfD"], doku_system="PARDOK", doku_base_url="https://pardok.parlament-berlin.de", drucksache_format="19/1234", dokukratie_scraper="be", aktiv=True, anmerkung=( "PARDOK = portala/eUI-Framework (gleiche Engine wie LSA-PADOKA, " "unter /portala/ statt /portal/). Hit list arrives as production " "HTML cards instead of LSA-style Perl Data::Dumper blocks — " "PortalaAdapter auto-detects both formats. document_type=None " "for BE because Berlin's ETYPF index uses different value strings " "than LSA. Wahlprogramme zur LTW 2023 sind noch nicht indexiert " "(Folge-Issue) — Analyse läuft daher mit Grundsatzprogramm-" "Zitaten als Fallback. Open-Data-XML unter " "parlament-berlin.de/dokumente/open-data ist eine alternative " "Datenquelle, derzeit nicht verwendet." ), ), "BB": Bundesland( code="BB", name="Brandenburg", parlament_name="Landtag Brandenburg", wahlperiode=8, # Wahltag (statt Konstituierende Sitzung am 2024-10-23), damit # die Geschäftsordnungs-Drucksachen der konstituierenden Sitzung # in den Plausibilitäts-Check fallen (siehe #61 Bug 4). wahlperiode_start="2024-09-22", naechste_wahl="2029-09-23", regierungsfraktionen=["SPD", "BSW"], landtagsfraktionen=["SPD", "AfD", "CDU", "BSW"], doku_system="portala", doku_base_url="https://www.parlamentsdokumentation.brandenburg.de", drucksache_format="8/1234", dokukratie_scraper="bb", aktiv=True, anmerkung=( "Kabinett Woidke IV (SPD-BSW) seit Dezember 2024. Knappe " "Mehrheit (zwei Sitze). Doku-System ist NICHT StarWeb wie " "ursprünglich klassifiziert (das alte /starweb/LBB/ELVIS/-" "Frontend ist nur Legacy), sondern das moderne portala/eUI-" "Backend auf /portal/browse.tt.json mit db_id=lbb.lissh. " "Wiederverwendet PortalaAdapter aus #2/#3 (#27)." ), ), "HB": Bundesland( code="HB", name="Bremen", parlament_name="Bremische Bürgerschaft", wahlperiode=21, wahlperiode_start="2023-07-05", naechste_wahl="2027-05-09", regierungsfraktionen=["SPD", "GRÜNE", "LINKE"], landtagsfraktionen=["SPD", "CDU", "GRÜNE", "LINKE", "AfD", "BiW"], doku_system="PARiS", doku_base_url="https://paris.bremische-buergerschaft.de/starweb/paris", drucksache_format="21/1234S", dokukratie_scraper="hb", aktiv=True, anmerkung=( "PARiS ist eine alte Java-Servlet-Variante von StarWeb. " "Single-POST-Search gegen /starweb/paris/servlet.starweb mit " "form-urlencoded Body, Hits in
. " "Drucksachen tragen einen S/L-Suffix für Stadtbürgerschaft " "vs. Landtag (z.B. 21/730S). Eigener PARiSHBAdapter (#21/#33). " "AfD durch Listenstreichung 2023 nicht im Landtag, stattdessen " "BiW. Wahltag 2027 noch nicht festgesetzt." ), ), "HH": Bundesland( code="HH", name="Hamburg", parlament_name="Hamburgische Bürgerschaft", wahlperiode=23, wahlperiode_start="2025-03-26", naechste_wahl="2030-03-03", regierungsfraktionen=["SPD", "GRÜNE"], landtagsfraktionen=["SPD", "CDU", "GRÜNE", "LINKE", "AfD"], doku_system="ParlDok", doku_base_url="https://www.buergerschaft-hh.de/parldok", drucksache_format="23/1234", dokukratie_scraper="hh", aktiv=True, anmerkung=( "Wahl am 02.03.2025; Senat Tschentscher III seit 07.05.2025 " "vereidigt. ParlDok 8.3.1 (J3S GmbH) — kompatibel mit der MV-" "Variante (8.3.5), gleiches /parldok/Fulltext/Search-Schema. " "Aktiv via ParLDokAdapter-Registry-Eintrag in #28." ), ), "HE": Bundesland( code="HE", name="Hessen", parlament_name="Hessischer Landtag", wahlperiode=21, wahlperiode_start="2024-01-18", naechste_wahl="2028-10-22", regierungsfraktionen=["CDU", "SPD"], landtagsfraktionen=["CDU", "AfD", "SPD", "GRÜNE", "FDP"], doku_system="portala", doku_base_url="https://starweb.hessen.de/portal", drucksache_format="21/1234", dokukratie_scraper="he", aktiv=True, anmerkung=( "starweb.hessen.de läuft auf demselben portala/eUI-Backend " "wie LSA/BE/BB/RP, aber mit HE-spezifischem Hit-Format: " "Cards (efxRecordRepeater) mit Daten in HTML-Kommentar-" "Perl-Dumps (WEV01-WEV12). PortalaAdapter mit eigenem " "Parser-Modus _parse_hit_list_he_comment_dump (#24/#30). " "Wahltermin 2028 ist Schätzung." ), ), "MV": Bundesland( code="MV", name="Mecklenburg-Vorpommern", parlament_name="Landtag Mecklenburg-Vorpommern", wahlperiode=8, wahlperiode_start="2021-10-26", naechste_wahl="2026-09-20", regierungsfraktionen=["SPD", "LINKE"], landtagsfraktionen=["SPD", "AfD", "CDU", "LINKE", "GRÜNE", "FDP"], doku_system="ParlDok", doku_base_url="https://www.dokumentation.landtag-mv.de", drucksache_format="8/1234", dokukratie_scraper="mv", aktiv=True, anmerkung=( "ParlDok 8.3.5 (J3S GmbH) — moderne SPA, JSON-API unter " "/parldok/Fulltext/Search. ParLDokAdapter (eigene Implementierung, " "nicht portala-kompatibel). Die in dokukratie/mv.yml beschriebene " "Legacy-HTML-Form (parldok/formalkriterien) ist mit dem 8.x-Upgrade " "deprecated. Suche filtert via facet_lp=10/id=8 server-seitig auf " "WP8, type=Antrag wird client-seitig gefiltert. Wahlprogramme zur " "LTW 26.09.2021 sind noch nicht indexiert (Folge-Issue) — Analyse " "läuft daher mit Grundsatzprogramm-Zitaten als Fallback. Wahltag " "offiziell auf 20.09.2026 festgelegt." ), ), "NI": Bundesland( code="NI", name="Niedersachsen", parlament_name="Niedersächsischer Landtag", wahlperiode=19, wahlperiode_start="2022-11-08", naechste_wahl="2027-10-10", regierungsfraktionen=["SPD", "GRÜNE"], landtagsfraktionen=["SPD", "CDU", "GRÜNE", "AfD"], doku_system="StarWeb", doku_base_url="https://www.landtag-niedersachsen.de", drucksache_format="19/12345", dokukratie_scraper="ni", anmerkung=( "Wahltermin Herbst 2027 (zwischen 11.07. und 03.10.2027) noch nicht festgesetzt; " "geschätzt. Olaf Lies (SPD) seit 20.05.2025 Ministerpräsident." ), ), "NRW": Bundesland( code="NRW", name="Nordrhein-Westfalen", parlament_name="Landtag Nordrhein-Westfalen", wahlperiode=18, wahlperiode_start="2022-06-01", naechste_wahl="2027-05-15", regierungsfraktionen=["CDU", "GRÜNE"], landtagsfraktionen=["CDU", "SPD", "GRÜNE", "FDP", "AfD"], doku_system="OPAL", doku_base_url="https://opal.landtag.nrw.de", drucksache_format="18/12345", dokukratie_scraper="nw", aktiv=True, anmerkung=( "OPAL in NRW ist eine eigene Implementierung, nicht identisch mit dem " "StarWeb-basierten OPAL in RLP. Wahltermin 2027 ist Schätzung." ), ), "RP": Bundesland( code="RP", name="Rheinland-Pfalz", parlament_name="Landtag Rheinland-Pfalz", wahlperiode=18, wahlperiode_start="2021-05-18", naechste_wahl="2031-03-22", regierungsfraktionen=["SPD", "GRÜNE", "FDP"], landtagsfraktionen=["SPD", "CDU", "AfD", "GRÜNE", "FREIE WÄHLER", "FDP"], doku_system="portala", doku_base_url="https://opal.rlp.de", drucksache_format="18/12345", dokukratie_scraper="rp", aktiv=True, anmerkung=( "OPAL in RLP läuft tatsächlich auf dem portala/eUI-Backend " "(NICHT StarWeb wie ursprünglich klassifiziert), erreichbar " "unter /portal/browse.tt.json mit db_id=rlp.lissh. " "Wiederverwendet PortalaAdapter aus #2/#3 (#30). NICHT " "verwechseln mit dem NRW OPAL — anderer Markenname, " "andere Engine. Wahl zum 19. Landtag fand am 22.03.2026 " "statt; Koalitionsverhandlungen CDU+SPD laufen, Kabinett " "Schweitzer I geschäftsführend. Nach Konstituierung müssen " "WP und Wahltermin aktualisiert werden." ), ), "SL": Bundesland( code="SL", name="Saarland", parlament_name="Landtag des Saarlandes", wahlperiode=17, wahlperiode_start="2022-04-25", naechste_wahl="2027-04-18", regierungsfraktionen=["SPD"], landtagsfraktionen=["SPD", "CDU", "AfD"], doku_system="Eigensystem", doku_base_url="https://www.landtag-saar.de", drucksache_format="17/1234", dokukratie_scraper="sl", anmerkung=( "Einzige SPD-Alleinregierung in Deutschland. AfD-Status im 17. LT vor " "produktiver Nutzung verifizieren." ), ), "SN": Bundesland( code="SN", name="Sachsen", parlament_name="Sächsischer Landtag", wahlperiode=8, wahlperiode_start="2024-10-01", naechste_wahl="2029-09-02", regierungsfraktionen=["CDU", "SPD"], landtagsfraktionen=["CDU", "AfD", "BSW", "SPD", "LINKE", "GRÜNE"], doku_system="EDAS-XML-Export", doku_base_url="https://edas.landtag.sachsen.de", drucksache_format="8/1234", dokukratie_scraper="sn", aktiv=True, anmerkung=( "Minderheitsregierung CDU+SPD (Kabinett Kretschmer III seit " "18.12.2024). EDAS ist ASP.NET-Webforms mit DevExpress-" "Postbacks UND robots.txt: Disallow: / — direktes Scraping " "blockiert. Stattdessen liest SNEdasXmlAdapter die wöchentlich " "manuell aus der EDAS-Suchmaske exportierte XML-Datei aus " "data/sn-edas-export.xml. PDF-URLs werden lazy beim " "download_text() aus dem viewer_navigation.aspx-Frame " "extrahiert (single GET, kein Postback). Schließt #26." ), ), "LSA": Bundesland( code="LSA", name="Sachsen-Anhalt", parlament_name="Landtag von Sachsen-Anhalt", wahlperiode=8, wahlperiode_start="2021-07-06", naechste_wahl="2026-09-06", regierungsfraktionen=["CDU", "SPD", "FDP"], landtagsfraktionen=["CDU", "AfD", "LINKE", "SPD", "GRÜNE", "FDP"], doku_system="PARDOK", doku_base_url="https://padoka.landtag.sachsen-anhalt.de", drucksache_format="8/1234", dokukratie_scraper="st", aktiv=True, anmerkung=( "ISO-Code wäre ST; LSA ist im politischen Sprachgebrauch dominant. " "Sven Schulze (CDU) seit 28.01.2026 MP nach Rücktritt Haseloff. " "PADOKA wurde von StarWeb auf das portala/eUI-Framework migriert " "(gleiche Engine wie Berlin/PARDOK). dokukratie's st.yml ist veraltet. " "Suche läuft via POST /portal/browse.tt.json + report.tt.html." ), ), "SH": Bundesland( code="SH", name="Schleswig-Holstein", parlament_name="Schleswig-Holsteinischer Landtag", wahlperiode=20, wahlperiode_start="2022-06-07", naechste_wahl="2027-04-18", regierungsfraktionen=["CDU", "GRÜNE"], landtagsfraktionen=["CDU", "GRÜNE", "SPD", "FDP", "SSW"], doku_system="StarWeb", doku_base_url="http://lissh.lvn.parlanet.de", drucksache_format="20/1234", dokukratie_scraper="sh", aktiv=True, anmerkung=( "SSW ist von der 5%-Hürde befreit. Doku-System ist die " "alte Starfinder-CGI auf lissh.lvn.parlanet.de — URL-" "basiert via " "/cgi-bin/starfinder/0?path=lisshfl.txt&search=WP=20+AND+dtyp=antrag, " "Latin-1-encoding. NICHT die moderne StarWeb-Servlet-" "Variante (BB/HE/NI/RP/HB) — eigene Klasse " "StarFinderCGIAdapter." ), ), "TH": Bundesland( code="TH", name="Thüringen", parlament_name="Thüringer Landtag", wahlperiode=8, wahlperiode_start="2024-10-01", naechste_wahl="2029-09-01", regierungsfraktionen=["CDU", "BSW", "SPD"], landtagsfraktionen=["AfD", "CDU", "LINKE", "BSW", "SPD"], doku_system="ParlDok", doku_base_url="https://parldok.thueringer-landtag.de", drucksache_format="8/1234", dokukratie_scraper="th", aktiv=True, anmerkung=( "Erste Brombeer-Koalition Deutschlands (CDU+BSW+SPD) als " "Minderheitsregierung mit 44 von 88 Sitzen. Mario Voigt " "(CDU) seit Dezember 2024 MP. ParlDok 8.3.5 (J3S GmbH) — " "EXAKT dieselbe Version wie MV. ParLDokAdapter direkt " "wiederverwendbar als Registry-Eintrag (#25). Achtung: " "alter Hostname parldok.thueringen.de redirected per 303 " "auf parldok.thueringer-landtag.de — neuer Hostname ist " "der korrekte." ), ), } def get(code: str) -> Optional[Bundesland]: """Bundesland-Konfig per Code abrufen, oder None.""" return BUNDESLAENDER.get(code) def aktive_bundeslaender() -> list[Bundesland]: """Alle aktuell aktiven (im Analyzer unterstützten) Bundesländer.""" return [bl for bl in BUNDESLAENDER.values() if bl.aktiv] def alle_bundeslaender() -> list[Bundesland]: """Alle 16 Bundesländer (aktive zuerst, dann alphabetisch nach Name).""" aktiv = sorted([bl for bl in BUNDESLAENDER.values() if bl.aktiv], key=lambda b: b.name) inaktiv = sorted([bl for bl in BUNDESLAENDER.values() if not bl.aktiv], key=lambda b: b.name) return aktiv + inaktiv