2026-04-07 14:17:54 +02:00
|
|
|
"""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.
|
|
|
|
|
BUNDESLAENDER: dict[str, Bundesland] = {
|
|
|
|
|
"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",
|
Activate Baden-Württemberg via PARLISAdapter (#29, Phase 1)
PARLIS auf parlis.landtag-bw.de läuft technisch auf demselben
eUI-Backend wie LSA-PADOKA und BE-PARDOK, hat aber drei wichtige
Unterschiede, die eine eigene Klasse statt einer PortalaAdapter-
Subklasse rechtfertigen:
1. Body-Schema: minimales lines mit l1/l2/l3/l4 (statt LSA/BE
2/3/4/10/11/20.x/90.x), serverrecordname=vorgang,
format=suchergebnis-vorgang-full, sort=SORT01/D SORT02/D SORT03,
keine parsed/json-Felder. Quelle: dokukratie/scrapers/portala.query.bw.json
plus HAR-Verifikation gegen die Live-Instanz.
2. Async polling: die initiale SearchAndDisplay-Antwort liefert nur
search_id mit status=running, KEINE report_id. Erst eine zweite
SearchAndDisplay-Anfrage mit id=<search_id> (ohne search-Component)
bekommt nach 1-3 Sekunden die report_id zurück. Reverse-engineered
aus esearch-ui.main.js requestReportOK() Z. ~1268.
3. Hit-Format: report.tt.html liefert Records als JSON-in-HTML-Comments
<!--{"WMV33":[...],"EWBV22":[...],...}-->. Komplett anderes Format
als LSA Perl-Dump oder BE HTML-Cards. Felder:
- EWBV22: "Drucksache 17/10323"
- EWBD05: direkter PDF-URL
- WMV33: Schlagworte (joined by ;)
- WMV30: Urheber-Kurzform
- EWBV23: "Antrag <Urheber> <DD.MM.YYYY>"
Smoke-Test (lokal):
BW q='': 8 hits in 17s, jüngste WP17-Anträge mit Datum + Fraktion
BW q='Schule': 8 hits, alle wirklich Schul-bezogen (Hochschule, Grundschule,
Schwimmunterricht, Lehrerbedarf etc.)
BW q='Klima': 8 hits, Klimaschutz/CO2/Energieberatung
get_document(17/10323): roundtrip funktioniert
bundeslaender.py: aktiv=True für BW; Anmerkung erweitert mit
PARLISAdapter-Verweis und drei-Unterschiede-Hinweis für künftige
Wartung. Test test_four_active_bundeslaender umbenannt zu
test_active_bundeslaender_include_phase_1_set, prüft jetzt nur
Subset-Bedingung statt exakter Count, damit Phase-1/2-Erweiterungen
keine Test-Updates brauchen.
Phase 1 (1/3) aus Roadmap-Issue #49.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:38:04 +02:00
|
|
|
aktiv=True,
|
2026-04-07 14:17:54 +02:00
|
|
|
anmerkung=(
|
Activate Baden-Württemberg via PARLISAdapter (#29, Phase 1)
PARLIS auf parlis.landtag-bw.de läuft technisch auf demselben
eUI-Backend wie LSA-PADOKA und BE-PARDOK, hat aber drei wichtige
Unterschiede, die eine eigene Klasse statt einer PortalaAdapter-
Subklasse rechtfertigen:
1. Body-Schema: minimales lines mit l1/l2/l3/l4 (statt LSA/BE
2/3/4/10/11/20.x/90.x), serverrecordname=vorgang,
format=suchergebnis-vorgang-full, sort=SORT01/D SORT02/D SORT03,
keine parsed/json-Felder. Quelle: dokukratie/scrapers/portala.query.bw.json
plus HAR-Verifikation gegen die Live-Instanz.
2. Async polling: die initiale SearchAndDisplay-Antwort liefert nur
search_id mit status=running, KEINE report_id. Erst eine zweite
SearchAndDisplay-Anfrage mit id=<search_id> (ohne search-Component)
bekommt nach 1-3 Sekunden die report_id zurück. Reverse-engineered
aus esearch-ui.main.js requestReportOK() Z. ~1268.
3. Hit-Format: report.tt.html liefert Records als JSON-in-HTML-Comments
<!--{"WMV33":[...],"EWBV22":[...],...}-->. Komplett anderes Format
als LSA Perl-Dump oder BE HTML-Cards. Felder:
- EWBV22: "Drucksache 17/10323"
- EWBD05: direkter PDF-URL
- WMV33: Schlagworte (joined by ;)
- WMV30: Urheber-Kurzform
- EWBV23: "Antrag <Urheber> <DD.MM.YYYY>"
Smoke-Test (lokal):
BW q='': 8 hits in 17s, jüngste WP17-Anträge mit Datum + Fraktion
BW q='Schule': 8 hits, alle wirklich Schul-bezogen (Hochschule, Grundschule,
Schwimmunterricht, Lehrerbedarf etc.)
BW q='Klima': 8 hits, Klimaschutz/CO2/Energieberatung
get_document(17/10323): roundtrip funktioniert
bundeslaender.py: aktiv=True für BW; Anmerkung erweitert mit
PARLISAdapter-Verweis und drei-Unterschiede-Hinweis für künftige
Wartung. Test test_four_active_bundeslaender umbenannt zu
test_active_bundeslaender_include_phase_1_set, prüft jetzt nur
Subset-Bedingung statt exakter Count, damit Phase-1/2-Erweiterungen
keine Test-Updates brauchen.
Phase 1 (1/3) aus Roadmap-Issue #49.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:38:04 +02:00
|
|
|
"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."
|
2026-04-07 14:17:54 +02:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
"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",
|
Activate Berlin (PARDOK) — search-only MVP (#3)
PortalaAdapter is now parameterizable and serves both LSA and Berlin
from a single class. Berlin is activated as the third live bundesland
(after NRW + LSA), with the deliberate caveat that the LTW 2023
Wahlprogramme are not yet indexed.
PortalaAdapter refactor
- Class attributes (bundesland, name, base_url, db_id, wahlperiode)
moved into the constructor. New optional parameters:
- portala_path: "/portal" for LSA, "/portala" for Berlin
- document_type: "Antrag" for LSA, None for Berlin (BE's ETYPF
index uses different value strings; the document_type subtree
is dropped from the action.search.json tree)
- pdf_url_prefix: "/files/" by default; absolute URLs in the hit
list are passed through unchanged (Berlin embeds full
starweb/adis/citat/... links)
- date_window_days: 730 for LSA, 180 for BE (BE has ~10x more
documents per WP, narrower window keeps payloads bounded)
- _build_search_body builds the JSON tree dynamically: when
document_type is None, the entire ETYPF/DTYPF/DART subtree is
omitted, mirrored in the parsed/sref display strings as well.
- _parse_hit_list_html now auto-detects between two formats:
1. LSA-style: <pre>$VAR1 = …</pre> Perl Data::Dumper records
(existing parser, untouched).
2. Berlin-style: production HTML cards with efxRecordRepeater
divs, h3 titles, h6 metadata lines containing the document
type, drucksachen-id and date, plus a direct <a href="…pdf">
to the PDF on the same host.
- Berlin extracts originator parties from the h6 line ("Antrag CDU,
SPD" → ["CDU","SPD"], typ "Antrag") via the new word-boundary
_normalize_fraktion regex.
- _normalize_fraktion rewritten with regex word boundaries, fixing a
long-standing bug where comma-separated fraction lists like
"CDU, SPD" failed to match CDU. Also picks up BSW for the
Brombeer/SPD-BSW landtage and "Senat von Berlin" as Landesregierung.
bundeslaender.py
- BE flipped to aktiv=True. anmerkung documents the Wahlprogramm-
Lücke and the auto-detected hit-list format.
Live verified against pardok.parlament-berlin.de:
- WP 19 with 180-day date window returns 2962 hits, page 1 contains
5 records all with title, drucksache, date, PDF URL.
- 19/3107 ("Kleingewässerprogramm") correctly extracted as Antrag of
CDU+SPD; 19/3104-3106 as Vorlagen zur Beschlussfassung; 19/3108 as
Vorlage zur Kenntnisnahme.
- LSA still returns the same 5 current Anträge of März 2026 — no
regression from the refactor.
Known limitation (will be tracked as a follow-up issue)
- Berlin Wahlprogramme zur LTW 2023 are not yet indexed in the
embeddings DB. The 2023 PDFs are no longer linked from the live
party websites (which currently feature 2026 draft programmes), and
Wayback has no snapshots. The analyzer therefore falls back to
bundesländer-übergreifende Grundsatzprogramme for BE Anträge until
the 2023 PDFs are sourced manually.
Refs #3.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 23:33:16 +02:00
|
|
|
aktiv=True,
|
2026-04-07 14:17:54 +02:00
|
|
|
anmerkung=(
|
Activate Berlin (PARDOK) — search-only MVP (#3)
PortalaAdapter is now parameterizable and serves both LSA and Berlin
from a single class. Berlin is activated as the third live bundesland
(after NRW + LSA), with the deliberate caveat that the LTW 2023
Wahlprogramme are not yet indexed.
PortalaAdapter refactor
- Class attributes (bundesland, name, base_url, db_id, wahlperiode)
moved into the constructor. New optional parameters:
- portala_path: "/portal" for LSA, "/portala" for Berlin
- document_type: "Antrag" for LSA, None for Berlin (BE's ETYPF
index uses different value strings; the document_type subtree
is dropped from the action.search.json tree)
- pdf_url_prefix: "/files/" by default; absolute URLs in the hit
list are passed through unchanged (Berlin embeds full
starweb/adis/citat/... links)
- date_window_days: 730 for LSA, 180 for BE (BE has ~10x more
documents per WP, narrower window keeps payloads bounded)
- _build_search_body builds the JSON tree dynamically: when
document_type is None, the entire ETYPF/DTYPF/DART subtree is
omitted, mirrored in the parsed/sref display strings as well.
- _parse_hit_list_html now auto-detects between two formats:
1. LSA-style: <pre>$VAR1 = …</pre> Perl Data::Dumper records
(existing parser, untouched).
2. Berlin-style: production HTML cards with efxRecordRepeater
divs, h3 titles, h6 metadata lines containing the document
type, drucksachen-id and date, plus a direct <a href="…pdf">
to the PDF on the same host.
- Berlin extracts originator parties from the h6 line ("Antrag CDU,
SPD" → ["CDU","SPD"], typ "Antrag") via the new word-boundary
_normalize_fraktion regex.
- _normalize_fraktion rewritten with regex word boundaries, fixing a
long-standing bug where comma-separated fraction lists like
"CDU, SPD" failed to match CDU. Also picks up BSW for the
Brombeer/SPD-BSW landtage and "Senat von Berlin" as Landesregierung.
bundeslaender.py
- BE flipped to aktiv=True. anmerkung documents the Wahlprogramm-
Lücke and the auto-detected hit-list format.
Live verified against pardok.parlament-berlin.de:
- WP 19 with 180-day date window returns 2962 hits, page 1 contains
5 records all with title, drucksache, date, PDF URL.
- 19/3107 ("Kleingewässerprogramm") correctly extracted as Antrag of
CDU+SPD; 19/3104-3106 as Vorlagen zur Beschlussfassung; 19/3108 as
Vorlage zur Kenntnisnahme.
- LSA still returns the same 5 current Anträge of März 2026 — no
regression from the refactor.
Known limitation (will be tracked as a follow-up issue)
- Berlin Wahlprogramme zur LTW 2023 are not yet indexed in the
embeddings DB. The 2023 PDFs are no longer linked from the live
party websites (which currently feature 2026 draft programmes), and
Wayback has no snapshots. The analyzer therefore falls back to
bundesländer-übergreifende Grundsatzprogramme for BE Anträge until
the 2023 PDFs are sourced manually.
Refs #3.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 23:33:16 +02:00
|
|
|
"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."
|
2026-04-07 14:17:54 +02:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
"BB": Bundesland(
|
|
|
|
|
code="BB",
|
|
|
|
|
name="Brandenburg",
|
|
|
|
|
parlament_name="Landtag Brandenburg",
|
|
|
|
|
wahlperiode=8,
|
|
|
|
|
wahlperiode_start="2024-10-23",
|
|
|
|
|
naechste_wahl="2029-09-23",
|
|
|
|
|
regierungsfraktionen=["SPD", "BSW"],
|
|
|
|
|
landtagsfraktionen=["SPD", "AfD", "CDU", "BSW"],
|
|
|
|
|
doku_system="StarWeb",
|
|
|
|
|
doku_base_url="https://www.parlamentsdokumentation.brandenburg.de",
|
|
|
|
|
drucksache_format="8/1234",
|
|
|
|
|
dokukratie_scraper="bb",
|
|
|
|
|
anmerkung="Kabinett Woidke IV (SPD-BSW) seit Dezember 2024. Knappe Mehrheit (zwei Sitze).",
|
|
|
|
|
),
|
|
|
|
|
"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"],
|
2026-04-08 23:19:41 +02:00
|
|
|
doku_system="StarWeb",
|
2026-04-07 14:17:54 +02:00
|
|
|
doku_base_url="https://paris.bremische-buergerschaft.de",
|
|
|
|
|
drucksache_format="21/1234",
|
|
|
|
|
dokukratie_scraper="hb",
|
|
|
|
|
anmerkung=(
|
2026-04-08 23:19:41 +02:00
|
|
|
"PARiS ist eine StarWeb-Skin auf bremischer Hardware — kein "
|
|
|
|
|
"eigenständiges System. Endpoint folgt dem Standard "
|
|
|
|
|
"/starweb/paris/servlet.starweb?path=paris/LISSH.web (siehe "
|
|
|
|
|
"dokukratie/hb.yml). Wiederverwendbar mit dem generischen "
|
|
|
|
|
"StarWebAdapter aus Issue #27. AfD durch Listenstreichung 2023 "
|
|
|
|
|
"nicht im Landtag, stattdessen Bürger in Wut (BiW). Wahltag 2027 "
|
|
|
|
|
"noch nicht festgesetzt."
|
2026-04-07 14:17:54 +02:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
"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",
|
|
|
|
|
anmerkung="Wahl am 02.03.2025; Senat Tschentscher III seit 07.05.2025 vereidigt.",
|
|
|
|
|
),
|
|
|
|
|
"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="StarWeb",
|
|
|
|
|
doku_base_url="https://starweb.hessen.de/starweb/LIS",
|
|
|
|
|
drucksache_format="21/1234",
|
|
|
|
|
dokukratie_scraper="he",
|
|
|
|
|
anmerkung="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",
|
2026-04-08 08:19:48 +02:00
|
|
|
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."
|
|
|
|
|
),
|
2026-04-07 14:17:54 +02:00
|
|
|
),
|
|
|
|
|
"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="StarWeb",
|
|
|
|
|
doku_base_url="https://opal.rlp.de",
|
|
|
|
|
drucksache_format="18/12345",
|
|
|
|
|
dokukratie_scraper="rp",
|
|
|
|
|
anmerkung=(
|
|
|
|
|
"OPAL in RLP basiert auf StarWeb. 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"],
|
2026-04-08 23:19:41 +02:00
|
|
|
doku_system="Eigensystem",
|
2026-04-07 14:17:54 +02:00
|
|
|
doku_base_url="https://edas.landtag.sachsen.de",
|
|
|
|
|
drucksache_format="8/1234",
|
|
|
|
|
dokukratie_scraper="sn",
|
|
|
|
|
anmerkung=(
|
2026-04-08 23:19:41 +02:00
|
|
|
"Minderheitsregierung CDU+SPD (Kabinett Kretschmer III seit "
|
|
|
|
|
"18.12.2024). EDAS auf edas.landtag.sachsen.de ist eine "
|
|
|
|
|
"ASP.NET-Webforms-Anwendung mit __VIEWSTATE/__CALLBACKID-"
|
|
|
|
|
"Postbacks (siehe dokukratie/sn.yml) — NICHT ParlDok-kompatibel "
|
|
|
|
|
"mit MV/HH trotz älterer Wikipedia-Klassifikation. Eigener "
|
|
|
|
|
"Adapter notwendig (Issue #26)."
|
2026-04-07 14:17:54 +02:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
"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"],
|
Add PortalaAdapter for PADOKA / Sachsen-Anhalt (#2)
Adds a clean-room PortalaAdapter that talks to the eUI/portala framework
behind PADOKA (Landtag Sachsen-Anhalt). Same engine powers Berlin's
PARDOK; the same adapter will serve issue #3 once activated for BE.
Reverse-engineering notes
- The "PADOKA = StarWeb" assumption from issue #1 / dokukratie's st.yml
is outdated. The Sachsen-Anhalt portal was migrated to the same
eUI/portala SPA framework Berlin uses. The legacy starweb URL returns
503; the new entry point is /portal/browse.tt.html.
- Search workflow is two-stage:
1. POST /portal/browse.tt.json with a JSON action body containing an
Elasticsearch-style query tree under search.json. Returns a
report_id plus hit count.
2. POST /portal/report.tt.html with {report_id, start, chunksize}
returns the HTML hit list. Each record carries a Perl Data::Dumper
block in a <pre> tag with the canonical metadata.
- The query schema (sources, search.lines, search.json tree, report
block) is taken from dokukratie/scrapers/portala.query.json (GPL-3.0)
— only structure/selectors are reused, no Python code is ported.
- DB id is "lsa.lissh"; the server validates this and rejects unknown
interfaces with an explicit errormsg.
- PDFs live under /files/drs/wp{N}/drs/d{nr}{xxx}.pdf and are served
directly without any session cookie.
What the adapter does
- search() builds a date-window query (last ~24 months) for "Antrag"
document type and returns the most recent hits. The user's free-text
query is applied as a client-side title/Urheber filter (no fulltext
search server-side yet — see "Limitations" below).
- Hits are parsed from the Perl record dumps in the report HTML:
- WEV06.main → title (Perl \x{xx} hex escapes decoded)
- WEV32.5 → relative PDF path
- WEV32.main → "Antrag <Urheber> <DD.MM.YYYY> Drucksache <b>X/YYYY</b>"
- Fraktion strings are normalised to canonical codes (CDU, SPD, GRÜNE,
FDP, AfD, LINKE, Landesregierung).
- get_document() looks up a single Drucksache by re-running the search.
- download_text() fetches the PDF and extracts text via PyMuPDF.
- bundeslaender.py: LSA's doku_system corrected from "StarWeb" to
"PARDOK", anmerkung updated with the migration story.
Limitations (deliberate, MVP)
- No server-side full-text search. The portala framework's sf index
names for LSA full-text content are not yet known; tree mutations
with sf=alAB return 0 hits. Client-side filter is "good enough" for
the next ~24 months of Anträge (≈few hundred per WP).
- LSA is still aktiv=False in bundeslaender.py — the adapter is dormant
in production until issue #2's wahlprogramm ingest and frontend
activation land.
Verified live against padoka.landtag.sachsen-anhalt.de:
- search(query="", limit=5) returned 5 current Anträge from März 2026
(LINKE + GRÜNE) with correct dates, fractions, titles and PDF URLs.
- download_text("8/6790") returned 5051 chars of real Antragstext
("ICE-Halt für Salzwedel dauerhaft erhalten").
Refs #2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 21:50:23 +02:00
|
|
|
doku_system="PARDOK",
|
2026-04-07 14:17:54 +02:00
|
|
|
doku_base_url="https://padoka.landtag.sachsen-anhalt.de",
|
|
|
|
|
drucksache_format="8/1234",
|
|
|
|
|
dokukratie_scraper="st",
|
Activate LSA: Wahlprogramme + ingest + frontend (#2)
Brings Sachsen-Anhalt online as the second supported Bundesland after
NRW. Closes the gap that issue #2 left open: with the PortalaAdapter
already in place from c7242f8, this commit adds the reference data and
flips the activation switch.
Wahlprogramme (LTW Sachsen-Anhalt 06.06.2021)
- Six PDFs added under app/static/referenzen/{cdu,spd,gruene,fdp,afd,
linke}-lsa-2021.pdf, plus paged plain-text extractions under
app/kontext/*.txt for the keyword fallback search.
- Sources verified by hand:
- CDU "Unsere Heimat. Unsere Verantwortung." (cdulsa.de, 82 pages)
- SPD "Zusammenhalt und neue Chancen" (FES library, 77 pages)
- GRÜNE "Verlässlich für Sachsen-Anhalt" (gruene-lsa.de, 164 pages)
- FDP "Wahlprogramm zur Landtagswahl 2021" (Naumann-Stiftung, 76 pages)
- AfD "Alles für unsere Heimat!" (klimawahlen.de mirror, 64 pages)
- LINKE "Wahlprogramm zur Landtagswahl 2021" (dielinke-sachsen-anhalt.de,
88 pages)
- The CDU PDF was the trickiest: KAS blocks bot downloads via
Cloudflare; the cdulsa.de copy was located by an autonomous web
search and verified to be byte-identical with the official document.
Embeddings indexed (in production container, OpenAI-compatible
DashScope embeddings via the existing index_programm pipeline):
- CDU 134, SPD 145, GRÜNE 183, FDP 100, AfD 64, LINKE 143 chunks
- Total LSA: 769 new chunks alongside the existing 775 NRW chunks
and 335 federal Grundsatzprogramm chunks.
wahlprogramme.py
- WAHLPROGRAMME["LSA"] populated with all six parties (canonical fraction
codes, original titles, page counts).
embeddings.py
- PROGRAMME extended with the six new "<partei>-lsa-2021" entries that
the indexer pipeline expects.
bundeslaender.py
- LSA flipped to aktiv=True. The frontend dropdown will now offer
Sachsen-Anhalt as a selectable bundesland and analyzer.get_bundesland_
context() will produce a real LSA prompt block (CDU/SPD/FDP as
governing fractions, all six landtagsfraktionen).
End-to-end smoke test (live in production container before commit)
- Adapter: PortalaAdapter.search() returned current Anträge of März 2026
(LINKE + GRÜNE) with correct titles and PDF URLs.
- Semantic search for an LSA "ÖPNV in der Altmark" sample antrag
matched LINKE S.53, SPD S.68, FDP S.52 — all three with similarity
> 0.6 and topical hits (Regionalisierungsmittel, ÖPNV-Förderprogramm,
Wasserstoffnetz).
Resolves issue #2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:12:32 +02:00
|
|
|
aktiv=True,
|
2026-04-07 14:17:54 +02:00
|
|
|
anmerkung=(
|
|
|
|
|
"ISO-Code wäre ST; LSA ist im politischen Sprachgebrauch dominant. "
|
|
|
|
|
"Sven Schulze (CDU) seit 28.01.2026 MP nach Rücktritt Haseloff. "
|
Add PortalaAdapter for PADOKA / Sachsen-Anhalt (#2)
Adds a clean-room PortalaAdapter that talks to the eUI/portala framework
behind PADOKA (Landtag Sachsen-Anhalt). Same engine powers Berlin's
PARDOK; the same adapter will serve issue #3 once activated for BE.
Reverse-engineering notes
- The "PADOKA = StarWeb" assumption from issue #1 / dokukratie's st.yml
is outdated. The Sachsen-Anhalt portal was migrated to the same
eUI/portala SPA framework Berlin uses. The legacy starweb URL returns
503; the new entry point is /portal/browse.tt.html.
- Search workflow is two-stage:
1. POST /portal/browse.tt.json with a JSON action body containing an
Elasticsearch-style query tree under search.json. Returns a
report_id plus hit count.
2. POST /portal/report.tt.html with {report_id, start, chunksize}
returns the HTML hit list. Each record carries a Perl Data::Dumper
block in a <pre> tag with the canonical metadata.
- The query schema (sources, search.lines, search.json tree, report
block) is taken from dokukratie/scrapers/portala.query.json (GPL-3.0)
— only structure/selectors are reused, no Python code is ported.
- DB id is "lsa.lissh"; the server validates this and rejects unknown
interfaces with an explicit errormsg.
- PDFs live under /files/drs/wp{N}/drs/d{nr}{xxx}.pdf and are served
directly without any session cookie.
What the adapter does
- search() builds a date-window query (last ~24 months) for "Antrag"
document type and returns the most recent hits. The user's free-text
query is applied as a client-side title/Urheber filter (no fulltext
search server-side yet — see "Limitations" below).
- Hits are parsed from the Perl record dumps in the report HTML:
- WEV06.main → title (Perl \x{xx} hex escapes decoded)
- WEV32.5 → relative PDF path
- WEV32.main → "Antrag <Urheber> <DD.MM.YYYY> Drucksache <b>X/YYYY</b>"
- Fraktion strings are normalised to canonical codes (CDU, SPD, GRÜNE,
FDP, AfD, LINKE, Landesregierung).
- get_document() looks up a single Drucksache by re-running the search.
- download_text() fetches the PDF and extracts text via PyMuPDF.
- bundeslaender.py: LSA's doku_system corrected from "StarWeb" to
"PARDOK", anmerkung updated with the migration story.
Limitations (deliberate, MVP)
- No server-side full-text search. The portala framework's sf index
names for LSA full-text content are not yet known; tree mutations
with sf=alAB return 0 hits. Client-side filter is "good enough" for
the next ~24 months of Anträge (≈few hundred per WP).
- LSA is still aktiv=False in bundeslaender.py — the adapter is dormant
in production until issue #2's wahlprogramm ingest and frontend
activation land.
Verified live against padoka.landtag.sachsen-anhalt.de:
- search(query="", limit=5) returned 5 current Anträge from März 2026
(LINKE + GRÜNE) with correct dates, fractions, titles and PDF URLs.
- download_text("8/6790") returned 5051 chars of real Antragstext
("ICE-Halt für Salzwedel dauerhaft erhalten").
Refs #2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 21:50:23 +02:00
|
|
|
"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."
|
2026-04-07 14:17:54 +02:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
"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="https://www.landtag.ltsh.de",
|
|
|
|
|
drucksache_format="20/1234",
|
|
|
|
|
dokukratie_scraper="sh",
|
|
|
|
|
anmerkung="SSW ist von der 5%-Hürde befreit.",
|
|
|
|
|
),
|
|
|
|
|
"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"],
|
2026-04-08 23:19:41 +02:00
|
|
|
doku_system="ParlDok",
|
2026-04-07 14:17:54 +02:00
|
|
|
doku_base_url="https://parldok.thueringen.de",
|
|
|
|
|
drucksache_format="8/1234",
|
|
|
|
|
dokukratie_scraper="th",
|
|
|
|
|
anmerkung=(
|
2026-04-08 23:19:41 +02:00
|
|
|
"Erste Brombeer-Koalition Deutschlands (CDU+BSW+SPD) als "
|
|
|
|
|
"Minderheitsregierung mit 44 von 88 Sitzen. Mario Voigt (CDU) "
|
|
|
|
|
"seit Dezember 2024 MP. Doku-System ist ParlDok (siehe "
|
|
|
|
|
"dokukratie/th.yml mit DokumententypId/LegislaturperiodenNummer-"
|
|
|
|
|
"Form-Feldern), nicht StarWeb wie ursprünglich klassifiziert. "
|
|
|
|
|
"Vor Implementierung mit `curl parldok.thueringen.de/parldok/` "
|
|
|
|
|
"verifizieren ob das Live-System ParlDok 8.x SPA wie MV ist — "
|
|
|
|
|
"dann ist der ParLDokAdapter direkt wiederverwendbar."
|
2026-04-07 14:17:54 +02:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|