refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222)
Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
This commit is contained in:
parent
7d507f81f4
commit
bd591b9246
@ -4055,7 +4055,7 @@ async def api_admin_wahlprogramm_fetch(
|
||||
wird sie nicht überschrieben — stattdessen wird ein 409-Fehler zurückgegeben.
|
||||
"""
|
||||
from .wahlprogramm_fetch import fetch_and_verify, suggest_candidates
|
||||
from .wahlprogramme import WAHLPROGRAMME
|
||||
from .programme import aktuelles_wahlprogramm
|
||||
|
||||
body = await request.json()
|
||||
bl = body.get("bl", "").strip().upper()
|
||||
@ -4075,10 +4075,10 @@ async def api_admin_wahlprogramm_fetch(
|
||||
)
|
||||
url = candidates[0]["url"]
|
||||
|
||||
wp_info = WAHLPROGRAMME.get(bl, {}).get(partei)
|
||||
if wp_info:
|
||||
wp_info = aktuelles_wahlprogramm(bl, partei)
|
||||
if wp_info and wp_info.get("pdf"):
|
||||
from pathlib import Path as _Path
|
||||
dest = _Path(__file__).parent / "static" / "referenzen" / wp_info["file"]
|
||||
dest = _Path(__file__).parent / "static" / "referenzen" / wp_info["pdf"]
|
||||
else:
|
||||
from pathlib import Path as _Path
|
||||
dest = _Path(__file__).parent / "static" / "referenzen" / f"{partei.lower()}-{bl.lower()}-neu.pdf"
|
||||
|
||||
760
app/programme.py
760
app/programme.py
@ -4,11 +4,13 @@ historisch und aktuell.
|
||||
|
||||
Single Source of Truth für:
|
||||
- ``embeddings.py`` (Indexer liest die PDFs aus dieser Liste)
|
||||
- ``wahlprogramme.py`` (Compat-Shim, leitet die alte BL/Partei-API hierher)
|
||||
- ``analyzer.py`` (sucht das zum Antrag passende Wahlprogramm)
|
||||
- UI (zeigt Geltungszeitraum + zugeordnete Regierung pro Programm)
|
||||
|
||||
Siehe ``app/legislaturen.py`` für Wahlperioden + Regierungen.
|
||||
Siehe ``app/legislaturen.py`` für Wahlperioden + Regierungen — Programm-
|
||||
Daten beschreiben das Dokument selbst, nicht die Regierung, die aus ihm
|
||||
hervorging. Die Verbindung läuft über
|
||||
``legislaturen.regierung_zum_zeitpunkt(bundesland, datum)``.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@ -17,7 +19,7 @@ from typing import Literal, Optional, TypedDict
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Typ
|
||||
# Schema
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@ -31,23 +33,27 @@ ProgrammTyp = Literal[
|
||||
class Programm(TypedDict, total=False):
|
||||
"""Single source of truth für ein politisches Programm-Dokument.
|
||||
|
||||
Pflichtfelder: id, titel, name, typ, partei, gueltig_ab, pdf, seiten.
|
||||
Optional: bundesland, beschluss, wahl, wp, gueltig_bis, hinweis.
|
||||
Pflichtfelder: id, typ, partei, gueltig_ab, name, pdf.
|
||||
Optional: bundesland, wp, gueltig_bis, titel, seiten.
|
||||
|
||||
Was hier bewusst NICHT drin ist:
|
||||
- ``regierungsbildung`` / ``regierungsende`` — gehört zu
|
||||
``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft
|
||||
über ``legislaturen.regierung_zum_zeitpunkt(bl, antrag_datum)``.
|
||||
- ``partei`` Langform ("CDU NRW") — ableitbar via partei + bundesland.
|
||||
- ``jahr`` — ``int(gueltig_ab[:4])`` reicht.
|
||||
"""
|
||||
id: str # eindeutiger Schlüssel, z.B. "cdu-nrw-2022"
|
||||
titel: str # offizieller Titel ("Machen, worauf es ankommt")
|
||||
name: str # voll-qualifiziert für Citation (z.B. "CDU NRW Wahlprogramm 2022")
|
||||
typ: ProgrammTyp
|
||||
partei: str # normalisierter Schlüssel (CDU, GRÜNE, FREIE WÄHLER, BSW, …)
|
||||
bundesland: Optional[str] # BL-Code; None nur bei reinen Bundesgrundsatzprogrammen
|
||||
beschluss: Optional[str] # ISO YYYY-MM-DD; bei grundsatz: Parteitags-Beschluss
|
||||
wahl: Optional[str] # ISO YYYY-MM-DD; nur typ=wahlprogramm: Wahltag
|
||||
partei: str # kanonisch (CDU, BiW, BÜNDNIS 90/DIE GRÜNEN, …)
|
||||
bundesland: Optional[str] # BL-Code; None nur bei Bundesgrundsatzprogrammen
|
||||
wp: Optional[int] # Legislatur-Nummer; nur typ=wahlprogramm
|
||||
gueltig_ab: str # ISO; bei wahl: regierungsbildung; bei grundsatz: beschluss
|
||||
gueltig_bis: Optional[str] # ISO; None = Programm ist aktuell gültig
|
||||
gueltig_ab: str # ISO YYYY-MM-DD; bei wahl: Wahltag
|
||||
gueltig_bis: Optional[str] # ISO; None = aktuell gültig
|
||||
name: str # voll-qualifiziert für Citation, z.B. "CDU NRW Wahlprogramm 2022"
|
||||
titel: Optional[str] # Slogan ("Machen, worauf es ankommt"); None wenn nicht erfasst
|
||||
pdf: str # Dateiname in static/referenzen/
|
||||
seiten: int
|
||||
hinweis: Optional[str] # freier Text, z.B. "BSW hat kein Grundsatzprogramm — Wahlprogramm dient als Hauptquelle"
|
||||
seiten: Optional[int] # PDF-Seitenzahl
|
||||
|
||||
|
||||
REFERENZEN_PATH = Path(__file__).parent / "static" / "referenzen"
|
||||
@ -55,16 +61,348 @@ KONTEXT_PATH = Path(__file__).parent / "kontext"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Programm-Registry
|
||||
# Die Daten werden aus der bisherigen ``embeddings.PROGRAMME`` und
|
||||
# ``wahlprogramme.WAHLPROGRAMME`` migriert. Historische Wahlprogramme +
|
||||
# Landesgrundsatzprogramme werden ergänzt sobald die Recherche fertig ist.
|
||||
# Daten — alle 287 Programme (historisch + aktuell), sortiert nach
|
||||
# (typ, bundesland, gueltig_ab, partei). Auto-generiert aus der bisherigen
|
||||
# embeddings.PROGRAMME + WAHLPROGRAMME-Lazy-Migration via fitz für seiten.
|
||||
# Generator: tools/build_programme_literal.py (siehe Commit-Historie).
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
# Aktuelle Programme — gefüllt durch ``_register_initial_data()`` weiter unten,
|
||||
# damit der Migrations-Pfad an einer Stelle zu sehen ist.
|
||||
PROGRAMME: dict[str, Programm] = {}
|
||||
PROGRAMME: dict[str, Programm] = {
|
||||
|
||||
# ─── grundsatzprogramm-bund · BUND ───
|
||||
"spd-grundsatz": {"id": "spd-grundsatz", "typ": "grundsatzprogramm-bund", "partei": "SPD", "bundesland": None, "wp": None, "gueltig_ab": "2007-10-28", "gueltig_bis": None, "name": "SPD Hamburger Programm 2007", "titel": None, "pdf": "spd-grundsatzprogramm.pdf", "seiten": 79},
|
||||
"linke-grundsatz": {"id": "linke-grundsatz", "typ": "grundsatzprogramm-bund", "partei": "LINKE", "bundesland": None, "wp": None, "gueltig_ab": "2011-10-23", "gueltig_bis": None, "name": "DIE LINKE Erfurter Programm 2011", "titel": None, "pdf": "linke-grundsatzprogramm.pdf", "seiten": 58},
|
||||
"fw-grundsatz": {"id": "fw-grundsatz", "typ": "grundsatzprogramm-bund", "partei": "FREIE WÄHLER", "bundesland": None, "wp": None, "gueltig_ab": "2012-02-25", "gueltig_bis": None, "name": "FREIE WÄHLER Bundesgrundsatzprogramm", "titel": None, "pdf": "fw-grundsatz.pdf", "seiten": 46},
|
||||
"fdp-grundsatz": {"id": "fdp-grundsatz", "typ": "grundsatzprogramm-bund", "partei": "FDP", "bundesland": None, "wp": None, "gueltig_ab": "2012-04-22", "gueltig_bis": None, "name": "FDP Karlsruher Freiheitsthesen 2012", "titel": None, "pdf": "fdp-grundsatzprogramm.pdf", "seiten": 118},
|
||||
"afd-grundsatz": {"id": "afd-grundsatz", "typ": "grundsatzprogramm-bund", "partei": "AfD", "bundesland": None, "wp": None, "gueltig_ab": "2016-05-01", "gueltig_bis": None, "name": "AfD Grundsatzprogramm 2016", "titel": None, "pdf": "afd-grundsatzprogramm.pdf", "seiten": 96},
|
||||
"gruene-grundsatz": {"id": "gruene-grundsatz", "typ": "grundsatzprogramm-bund", "partei": "GRÜNE", "bundesland": None, "wp": None, "gueltig_ab": "2020-11-22", "gueltig_bis": None, "name": "Grüne Grundsatzprogramm 2020", "titel": None, "pdf": "gruene-grundsatzprogramm.pdf", "seiten": 136},
|
||||
"cdu-grundsatz": {"id": "cdu-grundsatz", "typ": "grundsatzprogramm-bund", "partei": "CDU", "bundesland": None, "wp": None, "gueltig_ab": "2024-05-07", "gueltig_bis": None, "name": "CDU Grundsatzprogramm 2024", "titel": None, "pdf": "cdu-grundsatzprogramm.pdf", "seiten": 82},
|
||||
|
||||
# ─── grundsatzprogramm-land · BY ───
|
||||
"csu-grundsatz": {"id": "csu-grundsatz", "typ": "grundsatzprogramm-land", "partei": "CSU", "bundesland": "BY", "wp": None, "gueltig_ab": "2023-05-06", "gueltig_bis": None, "name": "CSU Grundsatzprogramm 2023", "titel": "Für ein neues Miteinander — Grundsatzprogramm der CSU", "pdf": "csu-grundsatz-2023.pdf", "seiten": 55},
|
||||
|
||||
# ─── grundsatzprogramm-land · LSA ───
|
||||
"cdu-grundsatz-lsa": {"id": "cdu-grundsatz-lsa", "typ": "grundsatzprogramm-land", "partei": "CDU", "bundesland": "LSA", "wp": None, "gueltig_ab": "2023-09-30", "gueltig_bis": None, "name": "CDU Sachsen-Anhalt Grundsatzprogramm 2023", "titel": "Sachsen-Anhalt. Unsere Verantwortung. Unsere Zukunft.", "pdf": "cdu-grundsatz-lsa-2023.pdf", "seiten": 70},
|
||||
|
||||
# ─── grundsatzprogramm-land · NRW ───
|
||||
"cdu-grundsatz-nrw": {"id": "cdu-grundsatz-nrw", "typ": "grundsatzprogramm-land", "partei": "CDU", "bundesland": "NRW", "wp": None, "gueltig_ab": "2015-06-13", "gueltig_bis": None, "name": "CDU NRW Grundsatzprogramm 2015", "titel": "Aufstieg, Sicherheit, Perspektive — Das Nordrhein-Westfalen-Programm", "pdf": "cdu-grundsatz-nrw-2015.pdf", "seiten": 48},
|
||||
|
||||
# ─── grundsatzprogramm-land · SH ───
|
||||
"ssw-grundsatz": {"id": "ssw-grundsatz", "typ": "grundsatzprogramm-land", "partei": "SSW", "bundesland": "SH", "wp": None, "gueltig_ab": "2016-04-16", "gueltig_bis": None, "name": "SSW Rahmenprogramm 2016", "titel": "SSW Rahmenprogramm", "pdf": "ssw-grundsatz-sh-2016.pdf", "seiten": 34},
|
||||
|
||||
# ─── grundsatzprogramm-land · SN ───
|
||||
"cdu-grundsatz-sn": {"id": "cdu-grundsatz-sn", "typ": "grundsatzprogramm-land", "partei": "CDU", "bundesland": "SN", "wp": None, "gueltig_ab": "2023-11-20", "gueltig_bis": None, "name": "CDU Sachsen Grundsatzprogramm 2023", "titel": "Zukunftsplan für Sachsen", "pdf": "cdu-grundsatz-sn-2023.pdf", "seiten": 54},
|
||||
|
||||
# ─── wahlprogramm · BB ───
|
||||
"afd-bb-2014": {"id": "afd-bb-2014", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BB", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-09-01", "name": "AfD BB Wahlprogramm 2014", "titel": None, "pdf": "afd-bb-2014.pdf", "seiten": 37},
|
||||
"bvb-fw-bb-2014": {"id": "bvb-fw-bb-2014", "typ": "wahlprogramm", "partei": "BVB/FREIE WÄHLER", "bundesland": "BB", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-09-01", "name": "BVB/FREIE WÄHLER BB Wahlprogramm 2014", "titel": None, "pdf": "bvb-fw-bb-2014.pdf", "seiten": 39},
|
||||
"cdu-bb-2014": {"id": "cdu-bb-2014", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BB", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-09-01", "name": "CDU BB Wahlprogramm 2014", "titel": None, "pdf": "cdu-bb-2014.pdf", "seiten": 20},
|
||||
"gruene-bb-2014": {"id": "gruene-bb-2014", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BB", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-09-01", "name": "Grüne BB Wahlprogramm 2014", "titel": None, "pdf": "gruene-bb-2014.pdf", "seiten": 150},
|
||||
"linke-bb-2014": {"id": "linke-bb-2014", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "BB", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-09-01", "name": "DIE LINKE BB Wahlprogramm 2014", "titel": None, "pdf": "linke-bb-2014.pdf", "seiten": 60},
|
||||
"spd-bb-2014": {"id": "spd-bb-2014", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BB", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-09-01", "name": "SPD BB Wahlprogramm 2014", "titel": None, "pdf": "spd-bb-2014.pdf", "seiten": 26},
|
||||
"afd-bb-2019": {"id": "afd-bb-2019", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BB", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-22", "name": "AfD BB Wahlprogramm 2019", "titel": None, "pdf": "afd-bb-2019.pdf", "seiten": 42},
|
||||
"bvb-fw-bb-2019": {"id": "bvb-fw-bb-2019", "typ": "wahlprogramm", "partei": "BVB/FREIE WÄHLER", "bundesland": "BB", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-22", "name": "BVB/FREIE WÄHLER BB Wahlprogramm 2019", "titel": None, "pdf": "bvb-fw-bb-2019.pdf", "seiten": 37},
|
||||
"cdu-bb-2019": {"id": "cdu-bb-2019", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BB", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-22", "name": "CDU BB Wahlprogramm 2019", "titel": None, "pdf": "cdu-bb-2019.pdf", "seiten": 50},
|
||||
"gruene-bb-2019": {"id": "gruene-bb-2019", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BB", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-22", "name": "Grüne BB Wahlprogramm 2019", "titel": None, "pdf": "gruene-bb-2019.pdf", "seiten": 106},
|
||||
"linke-bb-2019": {"id": "linke-bb-2019", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "BB", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-22", "name": "DIE LINKE BB Wahlprogramm 2019", "titel": None, "pdf": "linke-bb-2019.pdf", "seiten": 100},
|
||||
"spd-bb-2019": {"id": "spd-bb-2019", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BB", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-22", "name": "SPD BB Wahlprogramm 2019", "titel": None, "pdf": "spd-bb-2019.pdf", "seiten": 47},
|
||||
"afd-bb-2024": {"id": "afd-bb-2024", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BB", "wp": 8, "gueltig_ab": "2024-09-22", "gueltig_bis": None, "name": "AfD Brandenburg Wahlprogramm 2024", "titel": None, "pdf": "afd-bb-2024.pdf", "seiten": 100},
|
||||
"bsw-bb-2024": {"id": "bsw-bb-2024", "typ": "wahlprogramm", "partei": "BSW", "bundesland": "BB", "wp": 8, "gueltig_ab": "2024-09-22", "gueltig_bis": None, "name": "BSW Brandenburg Wahlprogramm 2024", "titel": None, "pdf": "bsw-bb-2024.pdf", "seiten": 50},
|
||||
"cdu-bb-2024": {"id": "cdu-bb-2024", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BB", "wp": 8, "gueltig_ab": "2024-09-22", "gueltig_bis": None, "name": "CDU Brandenburg Wahlprogramm 2024", "titel": None, "pdf": "cdu-bb-2024.pdf", "seiten": 100},
|
||||
"spd-bb-2024": {"id": "spd-bb-2024", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BB", "wp": 8, "gueltig_ab": "2024-09-22", "gueltig_bis": None, "name": "SPD Brandenburg Wahlprogramm 2024", "titel": None, "pdf": "spd-bb-2024.pdf", "seiten": 100},
|
||||
|
||||
# ─── wahlprogramm · BE ───
|
||||
"cdu-be-2011": {"id": "cdu-be-2011", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BE", "wp": 17, "gueltig_ab": "2011-09-18", "gueltig_bis": "2016-09-18", "name": "CDU BE Wahlprogramm 2011", "titel": None, "pdf": "cdu-be-2011.pdf", "seiten": 41},
|
||||
"gruene-be-2011": {"id": "gruene-be-2011", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BE", "wp": 17, "gueltig_ab": "2011-09-18", "gueltig_bis": "2016-09-18", "name": "Grüne BE Wahlprogramm 2011", "titel": None, "pdf": "gruene-be-2011.pdf", "seiten": 120},
|
||||
"linke-be-2011": {"id": "linke-be-2011", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "BE", "wp": 17, "gueltig_ab": "2011-09-18", "gueltig_bis": "2016-09-18", "name": "DIE LINKE BE Wahlprogramm 2011", "titel": None, "pdf": "linke-be-2011.pdf", "seiten": 72},
|
||||
"piraten-be-2011": {"id": "piraten-be-2011", "typ": "wahlprogramm", "partei": "PIRATEN", "bundesland": "BE", "wp": 17, "gueltig_ab": "2011-09-18", "gueltig_bis": "2016-09-18", "name": "PIRATEN BE Wahlprogramm 2011", "titel": None, "pdf": "piraten-be-2011.pdf", "seiten": 51},
|
||||
"spd-be-2011": {"id": "spd-be-2011", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BE", "wp": 17, "gueltig_ab": "2011-09-18", "gueltig_bis": "2016-09-18", "name": "SPD BE Wahlprogramm 2011", "titel": None, "pdf": "spd-be-2011.pdf", "seiten": 60},
|
||||
"afd-be-2016": {"id": "afd-be-2016", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BE", "wp": 18, "gueltig_ab": "2016-09-18", "gueltig_bis": "2021-09-26", "name": "AfD BE Wahlprogramm 2016", "titel": None, "pdf": "afd-be-2016.pdf", "seiten": 40},
|
||||
"cdu-be-2016": {"id": "cdu-be-2016", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BE", "wp": 18, "gueltig_ab": "2016-09-18", "gueltig_bis": "2021-09-26", "name": "CDU BE Wahlprogramm 2016", "titel": None, "pdf": "cdu-be-2016.pdf", "seiten": 49},
|
||||
"fdp-be-2016": {"id": "fdp-be-2016", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "BE", "wp": 18, "gueltig_ab": "2016-09-18", "gueltig_bis": "2021-09-26", "name": "FDP BE Wahlprogramm 2016", "titel": None, "pdf": "fdp-be-2016.pdf", "seiten": 57},
|
||||
"gruene-be-2016": {"id": "gruene-be-2016", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BE", "wp": 18, "gueltig_ab": "2016-09-18", "gueltig_bis": "2021-09-26", "name": "Grüne BE Wahlprogramm 2016", "titel": None, "pdf": "gruene-be-2016.pdf", "seiten": 104},
|
||||
"linke-be-2016": {"id": "linke-be-2016", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "BE", "wp": 18, "gueltig_ab": "2016-09-18", "gueltig_bis": "2021-09-26", "name": "DIE LINKE BE Wahlprogramm 2016", "titel": None, "pdf": "linke-be-2016.pdf", "seiten": 100},
|
||||
"spd-be-2016": {"id": "spd-be-2016", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BE", "wp": 18, "gueltig_ab": "2016-09-18", "gueltig_bis": "2021-09-26", "name": "SPD BE Wahlprogramm 2016", "titel": None, "pdf": "spd-be-2016.pdf", "seiten": 99},
|
||||
"afd-be-2023": {"id": "afd-be-2023", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BE", "wp": 19, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "AfD Berlin Wahlprogramm 2021", "titel": "Wahlprogramm der AfD Berlin für die Wahl des Abgeordnetenhauses am 26. September 2021", "pdf": "afd-be-2023.pdf", "seiten": 166},
|
||||
"cdu-be-2023": {"id": "cdu-be-2023", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BE", "wp": 19, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "CDU Berlin Wahlprogramm 2021", "titel": "Unser Berlin. Mehr geht nur gemeinsam. — Berlin-Plan der CDU Berlin 2021–2026", "pdf": "cdu-be-2023.pdf", "seiten": 135},
|
||||
"gruene-be-2023": {"id": "gruene-be-2023", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BE", "wp": 19, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Berlin Wahlprogramm 2021", "titel": "Unser Plan für Berlin — Landeswahlprogramm BÜNDNIS 90/DIE GRÜNEN Berlin 2021", "pdf": "gruene-be-2023.pdf", "seiten": 280},
|
||||
"linke-be-2023": {"id": "linke-be-2023", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "BE", "wp": 19, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "DIE LINKE Berlin Wahlprogramm 2021", "titel": "rot. radikal. realistisch. — Unser Programm für die soziale Stadt", "pdf": "linke-be-2023.pdf", "seiten": 130},
|
||||
"spd-be-2023": {"id": "spd-be-2023", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BE", "wp": 19, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "SPD Berlin Wahlprogramm 2021", "titel": "Ganz sicher Berlin — Wahlprogramm der SPD Berlin zur Abgeordnetenhauswahl 2021", "pdf": "spd-be-2023.pdf", "seiten": 86},
|
||||
|
||||
# ─── wahlprogramm · BUND ───
|
||||
"cdu-bund-2013": {"id": "cdu-bund-2013", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BUND", "wp": 18, "gueltig_ab": "2013-09-22", "gueltig_bis": "2017-09-24", "name": "CDU BUND Wahlprogramm 2013", "titel": None, "pdf": "cdu-bund-2013.pdf", "seiten": 81},
|
||||
"csu-bund-2013": {"id": "csu-bund-2013", "typ": "wahlprogramm", "partei": "CSU", "bundesland": "BUND", "wp": 18, "gueltig_ab": "2013-09-22", "gueltig_bis": "2017-09-24", "name": "CSU BUND Wahlprogramm 2013", "titel": None, "pdf": "csu-bund-2013.pdf", "seiten": 41},
|
||||
"gruene-bund-2013": {"id": "gruene-bund-2013", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BUND", "wp": 18, "gueltig_ab": "2013-09-22", "gueltig_bis": "2017-09-24", "name": "Grüne BUND Wahlprogramm 2013", "titel": None, "pdf": "gruene-bund-2013.pdf", "seiten": 337},
|
||||
"linke-bund-2013": {"id": "linke-bund-2013", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "BUND", "wp": 18, "gueltig_ab": "2013-09-22", "gueltig_bis": "2017-09-24", "name": "DIE LINKE BUND Wahlprogramm 2013", "titel": None, "pdf": "linke-bund-2013.pdf", "seiten": 93},
|
||||
"spd-bund-2013": {"id": "spd-bund-2013", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BUND", "wp": 18, "gueltig_ab": "2013-09-22", "gueltig_bis": "2017-09-24", "name": "SPD BUND Wahlprogramm 2013", "titel": None, "pdf": "spd-bund-2013.pdf", "seiten": 120},
|
||||
"afd-bund-2017": {"id": "afd-bund-2017", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BUND", "wp": 19, "gueltig_ab": "2017-09-24", "gueltig_bis": "2021-09-26", "name": "AfD Bundestagswahlprogramm 2017", "titel": None, "pdf": "afd-bund-2017.pdf", "seiten": 76},
|
||||
"cdu-bund-2017": {"id": "cdu-bund-2017", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BUND", "wp": 19, "gueltig_ab": "2017-09-24", "gueltig_bis": "2021-09-26", "name": "CDU/CSU Wahlprogramm BTW 2017", "titel": None, "pdf": "cdu-bund-2017.pdf", "seiten": 76},
|
||||
"csu-bund-2017": {"id": "csu-bund-2017", "typ": "wahlprogramm", "partei": "CSU", "bundesland": "BUND", "wp": 19, "gueltig_ab": "2017-09-24", "gueltig_bis": "2021-09-26", "name": "CSU Bayernplan 2017", "titel": None, "pdf": "csu-bund-2017.pdf", "seiten": 31},
|
||||
"fdp-bund-2017": {"id": "fdp-bund-2017", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "BUND", "wp": 19, "gueltig_ab": "2017-09-24", "gueltig_bis": "2021-09-26", "name": "FDP Wahlprogramm BTW 2017", "titel": None, "pdf": "fdp-bund-2017.pdf", "seiten": 158},
|
||||
"gruene-bund-2017": {"id": "gruene-bund-2017", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BUND", "wp": 19, "gueltig_ab": "2017-09-24", "gueltig_bis": "2021-09-26", "name": "Grüne Bundestagswahlprogramm 2017", "titel": None, "pdf": "gruene-bund-2017.pdf", "seiten": 248},
|
||||
"linke-bund-2017": {"id": "linke-bund-2017", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "BUND", "wp": 19, "gueltig_ab": "2017-09-24", "gueltig_bis": "2021-09-26", "name": "DIE LINKE Bundestagswahlprogramm 2017", "titel": None, "pdf": "linke-bund-2017.pdf", "seiten": 144},
|
||||
"spd-bund-2017": {"id": "spd-bund-2017", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BUND", "wp": 19, "gueltig_ab": "2017-09-24", "gueltig_bis": "2021-09-26", "name": "SPD Zukunftsprogramm BTW 2017", "titel": None, "pdf": "spd-bund-2017.pdf", "seiten": 116},
|
||||
"afd-bund-2021": {"id": "afd-bund-2021", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BUND", "wp": 20, "gueltig_ab": "2021-09-26", "gueltig_bis": "2025-02-23", "name": "AfD Bundestagswahlprogramm 2021", "titel": None, "pdf": "afd-bund-2021.pdf", "seiten": 210},
|
||||
"cdu-bund-2021": {"id": "cdu-bund-2021", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BUND", "wp": 20, "gueltig_ab": "2021-09-26", "gueltig_bis": "2025-02-23", "name": "CDU/CSU Programm fuer Stabilitaet und Erneuerung 2021", "titel": None, "pdf": "cdu-bund-2021.pdf", "seiten": 140},
|
||||
"csu-bund-2021": {"id": "csu-bund-2021", "typ": "wahlprogramm", "partei": "CSU", "bundesland": "BUND", "wp": 20, "gueltig_ab": "2021-09-26", "gueltig_bis": "2025-02-23", "name": "CSU-Programm Bundestagswahl 2021", "titel": None, "pdf": "csu-bund-2021.pdf", "seiten": 18},
|
||||
"fdp-bund-2021": {"id": "fdp-bund-2021", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "BUND", "wp": 20, "gueltig_ab": "2021-09-26", "gueltig_bis": "2025-02-23", "name": "FDP Wahlprogramm Bundestagswahl 2021", "titel": None, "pdf": "fdp-bund-2021.pdf", "seiten": 68},
|
||||
"gruene-bund-2021": {"id": "gruene-bund-2021", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BUND", "wp": 20, "gueltig_ab": "2021-09-26", "gueltig_bis": "2025-02-23", "name": "Gruene Bundestagswahlprogramm 2021", "titel": None, "pdf": "gruene-bund-2021.pdf", "seiten": 272},
|
||||
"linke-bund-2021": {"id": "linke-bund-2021", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "BUND", "wp": 20, "gueltig_ab": "2021-09-26", "gueltig_bis": "2025-02-23", "name": "DIE LINKE Bundestagswahlprogramm 2021", "titel": None, "pdf": "linke-bund-2021.pdf", "seiten": 168},
|
||||
"spd-bund-2021": {"id": "spd-bund-2021", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BUND", "wp": 20, "gueltig_ab": "2021-09-26", "gueltig_bis": "2025-02-23", "name": "SPD Zukunftsprogramm Bundestagswahl 2021", "titel": None, "pdf": "spd-bund-2021.pdf", "seiten": 66},
|
||||
"afd-bund-2025": {"id": "afd-bund-2025", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BUND", "wp": 21, "gueltig_ab": "2025-02-23", "gueltig_bis": None, "name": "AfD Wahlprogramm 2025", "titel": "Zeit für Deutschland — AfD Bundestagswahlprogramm 2025", "pdf": "afd-bund-2025.pdf", "seiten": 177},
|
||||
"bsw-bund-2025": {"id": "bsw-bund-2025", "typ": "wahlprogramm", "partei": "BSW", "bundesland": "BUND", "wp": 21, "gueltig_ab": "2025-02-23", "gueltig_bis": None, "name": "BSW Wahlprogramm 2025", "titel": "Unser Land verdient mehr — BSW Wahlprogramm BTW 2025", "pdf": "bsw-bund-2025.pdf", "seiten": 45},
|
||||
"cdu-bund-2025": {"id": "cdu-bund-2025", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BUND", "wp": 21, "gueltig_ab": "2025-02-23", "gueltig_bis": None, "name": "CDU Wahlprogramm 2025", "titel": "Politikwechsel für Deutschland — Wahlprogramm CDU/CSU BTW 2025", "pdf": "cdu-bund-2025.pdf", "seiten": 82},
|
||||
"csu-bund-2025": {"id": "csu-bund-2025", "typ": "wahlprogramm", "partei": "CSU", "bundesland": "BUND", "wp": 21, "gueltig_ab": "2025-02-23", "gueltig_bis": None, "name": "CSU Wahlprogramm 2025", "titel": "Politikwechsel für Deutschland — Wahlprogramm CDU/CSU BTW 2025 (CSU)", "pdf": "csu-bund-2025.pdf", "seiten": 81},
|
||||
"fdp-bund-2025": {"id": "fdp-bund-2025", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "BUND", "wp": 21, "gueltig_ab": "2025-02-23", "gueltig_bis": None, "name": "FDP Wahlprogramm 2025", "titel": "Alles lässt sich ändern — FDP Wahlprogramm BTW 2025", "pdf": "fdp-bund-2025.pdf", "seiten": 52},
|
||||
"gruene-bund-2025": {"id": "gruene-bund-2025", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BUND", "wp": 21, "gueltig_ab": "2025-02-23", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Wahlprogramm 2025", "titel": "Zusammen wachsen — Regierungsprogramm BÜNDNIS 90/DIE GRÜNEN BTW 2025", "pdf": "gruene-bund-2025.pdf", "seiten": 160},
|
||||
"linke-bund-2025": {"id": "linke-bund-2025", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "BUND", "wp": 21, "gueltig_ab": "2025-02-23", "gueltig_bis": None, "name": "DIE LINKE Wahlprogramm 2025", "titel": "Alle wollen regieren. Wir wollen verändern. — DIE LINKE Wahlprogramm BTW 2025", "pdf": "linke-bund-2025.pdf", "seiten": 60},
|
||||
"spd-bund-2025": {"id": "spd-bund-2025", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BUND", "wp": 21, "gueltig_ab": "2025-02-23", "gueltig_bis": None, "name": "SPD Wahlprogramm 2025", "titel": "Mehr für Dich. Besser für Deutschland. — SPD Regierungsprogramm BTW 2025", "pdf": "spd-bund-2025.pdf", "seiten": 68},
|
||||
|
||||
# ─── wahlprogramm · BW ───
|
||||
"cdu-bw-2011": {"id": "cdu-bw-2011", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BW", "wp": 15, "gueltig_ab": "2011-03-27", "gueltig_bis": "2016-03-13", "name": "CDU BW Wahlprogramm 2011", "titel": None, "pdf": "cdu-bw-2011.pdf", "seiten": 57},
|
||||
"fdp-bw-2011": {"id": "fdp-bw-2011", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "BW", "wp": 15, "gueltig_ab": "2011-03-27", "gueltig_bis": "2016-03-13", "name": "FDP BW Wahlprogramm 2011", "titel": None, "pdf": "fdp-bw-2011.pdf", "seiten": 137},
|
||||
"gruene-bw-2011": {"id": "gruene-bw-2011", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BW", "wp": 15, "gueltig_ab": "2011-03-27", "gueltig_bis": "2016-03-13", "name": "Grüne BW Wahlprogramm 2011", "titel": None, "pdf": "gruene-bw-2011.pdf", "seiten": 241},
|
||||
"spd-bw-2011": {"id": "spd-bw-2011", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BW", "wp": 15, "gueltig_ab": "2011-03-27", "gueltig_bis": "2016-03-13", "name": "SPD BW Wahlprogramm 2011", "titel": None, "pdf": "spd-bw-2011.pdf", "seiten": 65},
|
||||
"afd-bw-2016": {"id": "afd-bw-2016", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BW", "wp": 16, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-03-14", "name": "AfD BW Wahlprogramm 2016", "titel": None, "pdf": "afd-bw-2016.pdf", "seiten": 64},
|
||||
"cdu-bw-2016": {"id": "cdu-bw-2016", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BW", "wp": 16, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-03-14", "name": "CDU BW Wahlprogramm 2016", "titel": None, "pdf": "cdu-bw-2016.pdf", "seiten": 156},
|
||||
"fdp-bw-2016": {"id": "fdp-bw-2016", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "BW", "wp": 16, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-03-14", "name": "FDP BW Wahlprogramm 2016", "titel": None, "pdf": "fdp-bw-2016.pdf", "seiten": 124},
|
||||
"gruene-bw-2016": {"id": "gruene-bw-2016", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BW", "wp": 16, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-03-14", "name": "Grüne BW Wahlprogramm 2016", "titel": None, "pdf": "gruene-bw-2016.pdf", "seiten": 249},
|
||||
"spd-bw-2016": {"id": "spd-bw-2016", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BW", "wp": 16, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-03-14", "name": "SPD BW Wahlprogramm 2016", "titel": None, "pdf": "spd-bw-2016.pdf", "seiten": 41},
|
||||
"afd-bw-2021": {"id": "afd-bw-2021", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BW", "wp": 17, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "AfD Baden-Württemberg Wahlprogramm 2021", "titel": "AfD Baden-Württemberg Landtagswahlprogramm 2021", "pdf": "afd-bw-2021.pdf", "seiten": 100},
|
||||
"cdu-bw-2021": {"id": "cdu-bw-2021", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "BW", "wp": 17, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "CDU Baden-Württemberg Wahlprogramm 2021", "titel": "Neue Ideen für eine neue Zeit — Regierungsprogramm der CDU Baden-Württemberg zur Landtagswahl 2021", "pdf": "cdu-bw-2021.pdf", "seiten": 100},
|
||||
"fdp-bw-2021": {"id": "fdp-bw-2021", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "BW", "wp": 17, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "FDP Baden-Württemberg Wahlprogramm 2021", "titel": "FDP Baden-Württemberg Landtagswahlprogramm 2021", "pdf": "fdp-bw-2021.pdf", "seiten": 100},
|
||||
"gruene-bw-2021": {"id": "gruene-bw-2021", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BW", "wp": 17, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Baden-Württemberg Wahlprogramm 2021", "titel": "Wachsen wir über uns hinaus — Landtagswahlprogramm BÜNDNIS 90/DIE GRÜNEN Baden-Württemberg 2021", "pdf": "gruene-bw-2021.pdf", "seiten": 100},
|
||||
"spd-bw-2021": {"id": "spd-bw-2021", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BW", "wp": 17, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "SPD Baden-Württemberg Wahlprogramm 2021", "titel": "SPD Baden-Württemberg Wahlprogramm zur Landtagswahl 2021", "pdf": "spd-bw-2021.pdf", "seiten": 100},
|
||||
|
||||
# ─── wahlprogramm · BY ───
|
||||
"csu-by-2013": {"id": "csu-by-2013", "typ": "wahlprogramm", "partei": "CSU", "bundesland": "BY", "wp": 17, "gueltig_ab": "2013-09-15", "gueltig_bis": "2018-10-14", "name": "CSU BY Wahlprogramm 2013", "titel": None, "pdf": "csu-by-2013.pdf", "seiten": 28},
|
||||
"fw-by-2013": {"id": "fw-by-2013", "typ": "wahlprogramm", "partei": "FREIE WÄHLER", "bundesland": "BY", "wp": 17, "gueltig_ab": "2013-09-15", "gueltig_bis": "2018-10-14", "name": "FREIE WÄHLER BY Wahlprogramm 2013", "titel": None, "pdf": "fw-by-2013.pdf", "seiten": 67},
|
||||
"gruene-by-2013": {"id": "gruene-by-2013", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BY", "wp": 17, "gueltig_ab": "2013-09-15", "gueltig_bis": "2018-10-14", "name": "Grüne BY Wahlprogramm 2013", "titel": None, "pdf": "gruene-by-2013.pdf", "seiten": 87},
|
||||
"spd-by-2013": {"id": "spd-by-2013", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BY", "wp": 17, "gueltig_ab": "2013-09-15", "gueltig_bis": "2018-10-14", "name": "SPD BY Wahlprogramm 2013", "titel": None, "pdf": "spd-by-2013.pdf", "seiten": 150},
|
||||
"afd-by-2018": {"id": "afd-by-2018", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BY", "wp": 18, "gueltig_ab": "2018-10-14", "gueltig_bis": "2023-10-08", "name": "AfD BY Wahlprogramm 2018", "titel": None, "pdf": "afd-by-2018.pdf", "seiten": 100},
|
||||
"csu-by-2018": {"id": "csu-by-2018", "typ": "wahlprogramm", "partei": "CSU", "bundesland": "BY", "wp": 18, "gueltig_ab": "2018-10-14", "gueltig_bis": "2023-10-08", "name": "CSU BY Wahlprogramm 2018", "titel": None, "pdf": "csu-by-2018.pdf", "seiten": 24},
|
||||
"fdp-by-2018": {"id": "fdp-by-2018", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "BY", "wp": 18, "gueltig_ab": "2018-10-14", "gueltig_bis": "2023-10-08", "name": "FDP BY Wahlprogramm 2018", "titel": None, "pdf": "fdp-by-2018.pdf", "seiten": 78},
|
||||
"fw-by-2018": {"id": "fw-by-2018", "typ": "wahlprogramm", "partei": "FREIE WÄHLER", "bundesland": "BY", "wp": 18, "gueltig_ab": "2018-10-14", "gueltig_bis": "2023-10-08", "name": "FREIE WÄHLER BY Wahlprogramm 2018", "titel": None, "pdf": "fw-by-2018.pdf", "seiten": 43},
|
||||
"gruene-by-2018": {"id": "gruene-by-2018", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BY", "wp": 18, "gueltig_ab": "2018-10-14", "gueltig_bis": "2023-10-08", "name": "Grüne BY Wahlprogramm 2018", "titel": None, "pdf": "gruene-by-2018.pdf", "seiten": 107},
|
||||
"spd-by-2018": {"id": "spd-by-2018", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BY", "wp": 18, "gueltig_ab": "2018-10-14", "gueltig_bis": "2023-10-08", "name": "SPD BY Wahlprogramm 2018", "titel": None, "pdf": "spd-by-2018.pdf", "seiten": 69},
|
||||
"afd-by-2023": {"id": "afd-by-2023", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "BY", "wp": 19, "gueltig_ab": "2023-10-08", "gueltig_bis": None, "name": "AfD Bayern Wahlprogramm 2023", "titel": None, "pdf": "afd-by-2023.pdf", "seiten": 100},
|
||||
"csu-by-2023": {"id": "csu-by-2023", "typ": "wahlprogramm", "partei": "CSU", "bundesland": "BY", "wp": 19, "gueltig_ab": "2023-10-08", "gueltig_bis": None, "name": "CSU Bayern Wahlprogramm 2023", "titel": "CSU Bayern Bayernplan 2023", "pdf": "csu-by-2023.pdf", "seiten": 100},
|
||||
"fw-by-2023": {"id": "fw-by-2023", "typ": "wahlprogramm", "partei": "FREIE WÄHLER", "bundesland": "BY", "wp": 19, "gueltig_ab": "2023-10-08", "gueltig_bis": None, "name": "FREIE WÄHLER Bayern Wahlprogramm 2023", "titel": None, "pdf": "fw-by-2023.pdf", "seiten": 80},
|
||||
"gruene-by-2023": {"id": "gruene-by-2023", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "BY", "wp": 19, "gueltig_ab": "2023-10-08", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Bayern Wahlprogramm 2023", "titel": "BÜNDNIS 90/DIE GRÜNEN Bayern Regierungsprogramm 2023", "pdf": "gruene-by-2023.pdf", "seiten": 100},
|
||||
"spd-by-2023": {"id": "spd-by-2023", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "BY", "wp": 19, "gueltig_ab": "2023-10-08", "gueltig_bis": None, "name": "SPD Bayern Wahlprogramm 2023", "titel": "SPD Bayern Zukunftsprogramm 2023", "pdf": "spd-by-2023.pdf", "seiten": 100},
|
||||
|
||||
# ─── wahlprogramm · HB ───
|
||||
"afd-hb-2015": {"id": "afd-hb-2015", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "HB", "wp": 19, "gueltig_ab": "2015-05-10", "gueltig_bis": "2019-05-26", "name": "AfD HB Wahlprogramm 2015", "titel": None, "pdf": "afd-hb-2015.pdf", "seiten": 49},
|
||||
"biw-hb-2015": {"id": "biw-hb-2015", "typ": "wahlprogramm", "partei": "BiW", "bundesland": "HB", "wp": 19, "gueltig_ab": "2015-05-10", "gueltig_bis": "2019-05-26", "name": "BIW Bremen HB Wahlprogramm 2015", "titel": None, "pdf": "biw-hb-2015.pdf", "seiten": 32},
|
||||
"cdu-hb-2015": {"id": "cdu-hb-2015", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "HB", "wp": 19, "gueltig_ab": "2015-05-10", "gueltig_bis": "2019-05-26", "name": "CDU HB Wahlprogramm 2015", "titel": None, "pdf": "cdu-hb-2015.pdf", "seiten": 58},
|
||||
"fdp-hb-2015": {"id": "fdp-hb-2015", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "HB", "wp": 19, "gueltig_ab": "2015-05-10", "gueltig_bis": "2019-05-26", "name": "FDP HB Wahlprogramm 2015", "titel": None, "pdf": "fdp-hb-2015.pdf", "seiten": 80},
|
||||
"gruene-hb-2015": {"id": "gruene-hb-2015", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "HB", "wp": 19, "gueltig_ab": "2015-05-10", "gueltig_bis": "2019-05-26", "name": "Grüne HB Wahlprogramm 2015", "titel": None, "pdf": "gruene-hb-2015.pdf", "seiten": 140},
|
||||
"linke-hb-2015": {"id": "linke-hb-2015", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "HB", "wp": 19, "gueltig_ab": "2015-05-10", "gueltig_bis": "2019-05-26", "name": "DIE LINKE HB Wahlprogramm 2015", "titel": None, "pdf": "linke-hb-2015.pdf", "seiten": 82},
|
||||
"spd-hb-2015": {"id": "spd-hb-2015", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "HB", "wp": 19, "gueltig_ab": "2015-05-10", "gueltig_bis": "2019-05-26", "name": "SPD HB Wahlprogramm 2015", "titel": None, "pdf": "spd-hb-2015.pdf", "seiten": 67},
|
||||
"afd-hb-2019": {"id": "afd-hb-2019", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "HB", "wp": 20, "gueltig_ab": "2019-05-26", "gueltig_bis": "2023-05-14", "name": "AfD HB Wahlprogramm 2019", "titel": None, "pdf": "afd-hb-2019.pdf", "seiten": 27},
|
||||
"biw-hb-2019": {"id": "biw-hb-2019", "typ": "wahlprogramm", "partei": "BiW", "bundesland": "HB", "wp": 20, "gueltig_ab": "2019-05-26", "gueltig_bis": "2023-05-14", "name": "BIW Bremen Wahlprogramm 2019", "titel": None, "pdf": "biw-hb-2019.pdf", "seiten": 22},
|
||||
"cdu-hb-2019": {"id": "cdu-hb-2019", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "HB", "wp": 20, "gueltig_ab": "2019-05-26", "gueltig_bis": "2023-05-14", "name": "CDU HB Wahlprogramm 2019", "titel": None, "pdf": "cdu-hb-2019.pdf", "seiten": 66},
|
||||
"fdp-hb-2019": {"id": "fdp-hb-2019", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "HB", "wp": 20, "gueltig_ab": "2019-05-26", "gueltig_bis": "2023-05-14", "name": "FDP HB Wahlprogramm 2019", "titel": None, "pdf": "fdp-hb-2019.pdf", "seiten": 89},
|
||||
"gruene-hb-2019": {"id": "gruene-hb-2019", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "HB", "wp": 20, "gueltig_ab": "2019-05-26", "gueltig_bis": "2023-05-14", "name": "Grüne HB Wahlprogramm 2019", "titel": None, "pdf": "gruene-hb-2019.pdf", "seiten": 190},
|
||||
"linke-hb-2019": {"id": "linke-hb-2019", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "HB", "wp": 20, "gueltig_ab": "2019-05-26", "gueltig_bis": "2023-05-14", "name": "DIE LINKE HB Wahlprogramm 2019", "titel": None, "pdf": "linke-hb-2019.pdf", "seiten": 64},
|
||||
"spd-hb-2019": {"id": "spd-hb-2019", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "HB", "wp": 20, "gueltig_ab": "2019-05-26", "gueltig_bis": "2023-05-14", "name": "SPD HB Wahlprogramm 2019", "titel": None, "pdf": "spd-hb-2019.pdf", "seiten": 132},
|
||||
"biw-hb-2023": {"id": "biw-hb-2023", "typ": "wahlprogramm", "partei": "BiW", "bundesland": "HB", "wp": 21, "gueltig_ab": "2023-05-14", "gueltig_bis": None, "name": "BiW Bremen Wahlprogramm 2023", "titel": "BÜRGER IN WUT — Programm für die Bürgerschaftswahl 2023", "pdf": "biw-hb-2023.pdf", "seiten": 26},
|
||||
"cdu-hb-2023": {"id": "cdu-hb-2023", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "HB", "wp": 21, "gueltig_ab": "2023-05-14", "gueltig_bis": None, "name": "CDU Bremen Wahlprogramm 2023", "titel": "CDU Bremen Wahlprogramm Bürgerschaftswahl 2023", "pdf": "cdu-hb-2023.pdf", "seiten": 100},
|
||||
"gruene-hb-2023": {"id": "gruene-hb-2023", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "HB", "wp": 21, "gueltig_ab": "2023-05-14", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Bremen Wahlprogramm 2023", "titel": None, "pdf": "gruene-hb-2023.pdf", "seiten": 100},
|
||||
"linke-hb-2023": {"id": "linke-hb-2023", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "HB", "wp": 21, "gueltig_ab": "2023-05-14", "gueltig_bis": None, "name": "DIE LINKE Bremen Wahlprogramm 2023", "titel": "DIE LINKE Bremen Wahlprogramm Bürgerschaftswahl 2023", "pdf": "linke-hb-2023.pdf", "seiten": 100},
|
||||
"spd-hb-2023": {"id": "spd-hb-2023", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "HB", "wp": 21, "gueltig_ab": "2023-05-14", "gueltig_bis": None, "name": "SPD Bremen Wahlprogramm 2023", "titel": "SPD Bremen Wahlprogramm Bürgerschaftswahl 2023", "pdf": "spd-hb-2023.pdf", "seiten": 100},
|
||||
|
||||
# ─── wahlprogramm · HE ───
|
||||
"cdu-he-2013": {"id": "cdu-he-2013", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "HE", "wp": 19, "gueltig_ab": "2013-09-22", "gueltig_bis": "2019-01-18", "name": "CDU HE Wahlprogramm 2013", "titel": None, "pdf": "cdu-he-2013.pdf", "seiten": 72},
|
||||
"fdp-he-2013": {"id": "fdp-he-2013", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "HE", "wp": 19, "gueltig_ab": "2013-09-22", "gueltig_bis": "2019-01-18", "name": "FDP HE Wahlprogramm 2013", "titel": None, "pdf": "fdp-he-2013.pdf", "seiten": 197},
|
||||
"gruene-he-2013": {"id": "gruene-he-2013", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "HE", "wp": 19, "gueltig_ab": "2013-09-22", "gueltig_bis": "2019-01-18", "name": "Grüne HE Wahlprogramm 2013", "titel": None, "pdf": "gruene-he-2013.pdf", "seiten": 110},
|
||||
"linke-he-2013": {"id": "linke-he-2013", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "HE", "wp": 19, "gueltig_ab": "2013-09-22", "gueltig_bis": "2019-01-18", "name": "DIE LINKE HE Wahlprogramm 2013", "titel": None, "pdf": "linke-he-2013.pdf", "seiten": 60},
|
||||
"spd-he-2013": {"id": "spd-he-2013", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "HE", "wp": 19, "gueltig_ab": "2013-09-22", "gueltig_bis": "2019-01-18", "name": "SPD HE Wahlprogramm 2013", "titel": None, "pdf": "spd-he-2013.pdf", "seiten": 112},
|
||||
"afd-he-2018": {"id": "afd-he-2018", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "HE", "wp": 20, "gueltig_ab": "2018-10-28", "gueltig_bis": "2023-10-08", "name": "AfD HE Wahlprogramm 2018", "titel": None, "pdf": "afd-he-2018.pdf", "seiten": 90},
|
||||
"cdu-he-2018": {"id": "cdu-he-2018", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "HE", "wp": 20, "gueltig_ab": "2018-10-28", "gueltig_bis": "2023-10-08", "name": "CDU HE Wahlprogramm 2018", "titel": None, "pdf": "cdu-he-2018.pdf", "seiten": 132},
|
||||
"fdp-he-2018": {"id": "fdp-he-2018", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "HE", "wp": 20, "gueltig_ab": "2018-10-28", "gueltig_bis": "2023-10-08", "name": "FDP HE Wahlprogramm 2018", "titel": None, "pdf": "fdp-he-2018.pdf", "seiten": 112},
|
||||
"gruene-he-2018": {"id": "gruene-he-2018", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "HE", "wp": 20, "gueltig_ab": "2018-10-28", "gueltig_bis": "2023-10-08", "name": "Grüne HE Wahlprogramm 2018", "titel": None, "pdf": "gruene-he-2018.pdf", "seiten": 140},
|
||||
"linke-he-2018": {"id": "linke-he-2018", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "HE", "wp": 20, "gueltig_ab": "2018-10-28", "gueltig_bis": "2023-10-08", "name": "DIE LINKE HE Wahlprogramm 2018", "titel": None, "pdf": "linke-he-2018.pdf", "seiten": 90},
|
||||
"spd-he-2018": {"id": "spd-he-2018", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "HE", "wp": 20, "gueltig_ab": "2018-10-28", "gueltig_bis": "2023-10-08", "name": "SPD HE Wahlprogramm 2018", "titel": None, "pdf": "spd-he-2018.pdf", "seiten": 175},
|
||||
"afd-he-2023": {"id": "afd-he-2023", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "HE", "wp": 21, "gueltig_ab": "2023-10-08", "gueltig_bis": None, "name": "AfD Hessen Wahlprogramm 2023", "titel": None, "pdf": "afd-he-2023.pdf", "seiten": 100},
|
||||
"cdu-he-2023": {"id": "cdu-he-2023", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "HE", "wp": 21, "gueltig_ab": "2023-10-08", "gueltig_bis": None, "name": "CDU Hessen Wahlprogramm 2023", "titel": "CDU Hessen Regierungsprogramm 2023", "pdf": "cdu-he-2023.pdf", "seiten": 100},
|
||||
"fdp-he-2023": {"id": "fdp-he-2023", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "HE", "wp": 21, "gueltig_ab": "2023-10-08", "gueltig_bis": None, "name": "FDP Hessen Wahlprogramm 2023", "titel": None, "pdf": "fdp-he-2023.pdf", "seiten": 100},
|
||||
"gruene-he-2023": {"id": "gruene-he-2023", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "HE", "wp": 21, "gueltig_ab": "2023-10-08", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Hessen Wahlprogramm 2023", "titel": None, "pdf": "gruene-he-2023.pdf", "seiten": 100},
|
||||
"spd-he-2023": {"id": "spd-he-2023", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "HE", "wp": 21, "gueltig_ab": "2023-10-08", "gueltig_bis": None, "name": "SPD Hessen Wahlprogramm 2023", "titel": None, "pdf": "spd-he-2023.pdf", "seiten": 100},
|
||||
|
||||
# ─── wahlprogramm · HH ───
|
||||
"afd-hh-2015": {"id": "afd-hh-2015", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "HH", "wp": 21, "gueltig_ab": "2015-02-15", "gueltig_bis": "2020-02-23", "name": "AfD HH Wahlprogramm 2015", "titel": None, "pdf": "afd-hh-2015.pdf", "seiten": 28},
|
||||
"cdu-hh-2015": {"id": "cdu-hh-2015", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "HH", "wp": 21, "gueltig_ab": "2015-02-15", "gueltig_bis": "2020-02-23", "name": "CDU HH Wahlprogramm 2015", "titel": None, "pdf": "cdu-hh-2015.pdf", "seiten": 60},
|
||||
"fdp-hh-2015": {"id": "fdp-hh-2015", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "HH", "wp": 21, "gueltig_ab": "2015-02-15", "gueltig_bis": "2020-02-23", "name": "FDP HH Wahlprogramm 2015", "titel": None, "pdf": "fdp-hh-2015.pdf", "seiten": 63},
|
||||
"gruene-hh-2015": {"id": "gruene-hh-2015", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "HH", "wp": 21, "gueltig_ab": "2015-02-15", "gueltig_bis": "2020-02-23", "name": "Grüne HH Wahlprogramm 2015", "titel": None, "pdf": "gruene-hh-2015.pdf", "seiten": 237},
|
||||
"linke-hh-2015": {"id": "linke-hh-2015", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "HH", "wp": 21, "gueltig_ab": "2015-02-15", "gueltig_bis": "2020-02-23", "name": "DIE LINKE HH Wahlprogramm 2015", "titel": None, "pdf": "linke-hh-2015.pdf", "seiten": 44},
|
||||
"spd-hh-2015": {"id": "spd-hh-2015", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "HH", "wp": 21, "gueltig_ab": "2015-02-15", "gueltig_bis": "2020-02-23", "name": "SPD HH Wahlprogramm 2015", "titel": None, "pdf": "spd-hh-2015.pdf", "seiten": 70},
|
||||
"afd-hh-2020": {"id": "afd-hh-2020", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "HH", "wp": 22, "gueltig_ab": "2020-02-23", "gueltig_bis": "2025-03-02", "name": "AfD HH Wahlprogramm 2020", "titel": None, "pdf": "afd-hh-2020.pdf", "seiten": 40},
|
||||
"cdu-hh-2020": {"id": "cdu-hh-2020", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "HH", "wp": 22, "gueltig_ab": "2020-02-23", "gueltig_bis": "2025-03-02", "name": "CDU HH Wahlprogramm 2020", "titel": None, "pdf": "cdu-hh-2020.pdf", "seiten": 84},
|
||||
"fdp-hh-2020": {"id": "fdp-hh-2020", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "HH", "wp": 22, "gueltig_ab": "2020-02-23", "gueltig_bis": "2025-03-02", "name": "FDP HH Wahlprogramm 2020", "titel": None, "pdf": "fdp-hh-2020.pdf", "seiten": 97},
|
||||
"gruene-hh-2020": {"id": "gruene-hh-2020", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "HH", "wp": 22, "gueltig_ab": "2020-02-23", "gueltig_bis": "2025-03-02", "name": "Grüne HH Wahlprogramm 2020", "titel": None, "pdf": "gruene-hh-2020.pdf", "seiten": 146},
|
||||
"linke-hh-2020": {"id": "linke-hh-2020", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "HH", "wp": 22, "gueltig_ab": "2020-02-23", "gueltig_bis": "2025-03-02", "name": "DIE LINKE HH Wahlprogramm 2020", "titel": None, "pdf": "linke-hh-2020.pdf", "seiten": 37},
|
||||
"spd-hh-2020": {"id": "spd-hh-2020", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "HH", "wp": 22, "gueltig_ab": "2020-02-23", "gueltig_bis": "2025-03-02", "name": "SPD HH Wahlprogramm 2020", "titel": None, "pdf": "spd-hh-2020.pdf", "seiten": 90},
|
||||
"afd-hh-2025": {"id": "afd-hh-2025", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "HH", "wp": 23, "gueltig_ab": "2025-03-02", "gueltig_bis": None, "name": "AfD Hamburg Wahlprogramm 2025", "titel": "AfD Hamburg Wahlprogramm Bürgerschaftswahl 2025", "pdf": "afd-hh-2025.pdf", "seiten": 100},
|
||||
"cdu-hh-2025": {"id": "cdu-hh-2025", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "HH", "wp": 23, "gueltig_ab": "2025-03-02", "gueltig_bis": None, "name": "CDU Hamburg Wahlprogramm 2025", "titel": "CDU Hamburg Wahlprogramm Bürgerschaftswahl 2025", "pdf": "cdu-hh-2025.pdf", "seiten": 100},
|
||||
"gruene-hh-2025": {"id": "gruene-hh-2025", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "HH", "wp": 23, "gueltig_ab": "2025-03-02", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Hamburg Wahlprogramm 2025", "titel": "Gute Gründe für Grün — Regierungsprogramm BÜNDNIS 90/DIE GRÜNEN Hamburg 2025", "pdf": "gruene-hh-2025.pdf", "seiten": 100},
|
||||
"linke-hh-2025": {"id": "linke-hh-2025", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "HH", "wp": 23, "gueltig_ab": "2025-03-02", "gueltig_bis": None, "name": "DIE LINKE Hamburg Wahlprogramm 2025", "titel": "DIE LINKE Hamburg Wahlprogramm Bürgerschaftswahl 2025", "pdf": "linke-hh-2025.pdf", "seiten": 100},
|
||||
"spd-hh-2025": {"id": "spd-hh-2025", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "HH", "wp": 23, "gueltig_ab": "2025-03-02", "gueltig_bis": None, "name": "SPD Hamburg Wahlprogramm 2025", "titel": "SPD Hamburg Wahlprogramm Bürgerschaftswahl 2025", "pdf": "spd-hh-2025.pdf", "seiten": 100},
|
||||
|
||||
# ─── wahlprogramm · LSA ───
|
||||
"cdu-lsa-2011": {"id": "cdu-lsa-2011", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "LSA", "wp": 6, "gueltig_ab": "2011-03-20", "gueltig_bis": "2016-04-13", "name": "CDU LSA Wahlprogramm 2011", "titel": None, "pdf": "cdu-lsa-2011.pdf", "seiten": 66},
|
||||
"gruene-lsa-2011": {"id": "gruene-lsa-2011", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "LSA", "wp": 6, "gueltig_ab": "2011-03-20", "gueltig_bis": "2016-04-13", "name": "Grüne LSA Wahlprogramm 2011", "titel": None, "pdf": "gruene-lsa-2011.pdf", "seiten": 94},
|
||||
"linke-lsa-2011": {"id": "linke-lsa-2011", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "LSA", "wp": 6, "gueltig_ab": "2011-03-20", "gueltig_bis": "2016-04-13", "name": "DIE LINKE LSA Wahlprogramm 2011", "titel": None, "pdf": "linke-lsa-2011.pdf", "seiten": 36},
|
||||
"spd-lsa-2011": {"id": "spd-lsa-2011", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "LSA", "wp": 6, "gueltig_ab": "2011-03-20", "gueltig_bis": "2016-04-13", "name": "SPD LSA Wahlprogramm 2011", "titel": None, "pdf": "spd-lsa-2011.pdf", "seiten": 38},
|
||||
"afd-lsa-2016": {"id": "afd-lsa-2016", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "LSA", "wp": 7, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-06-06", "name": "AfD LSA Wahlprogramm 2016", "titel": None, "pdf": "afd-lsa-2016.pdf", "seiten": 68},
|
||||
"cdu-lsa-2016": {"id": "cdu-lsa-2016", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "LSA", "wp": 7, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-06-06", "name": "CDU LSA Wahlprogramm 2016", "titel": None, "pdf": "cdu-lsa-2016.pdf", "seiten": 64},
|
||||
"fdp-lsa-2016": {"id": "fdp-lsa-2016", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "LSA", "wp": 7, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-06-06", "name": "FDP LSA Wahlprogramm 2016", "titel": None, "pdf": "fdp-lsa-2016.pdf", "seiten": 51},
|
||||
"gruene-lsa-2016": {"id": "gruene-lsa-2016", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "LSA", "wp": 7, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-06-06", "name": "Grüne LSA Wahlprogramm 2016", "titel": None, "pdf": "gruene-lsa-2016.pdf", "seiten": 129},
|
||||
"linke-lsa-2016": {"id": "linke-lsa-2016", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "LSA", "wp": 7, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-06-06", "name": "DIE LINKE LSA Wahlprogramm 2016", "titel": None, "pdf": "linke-lsa-2016.pdf", "seiten": 59},
|
||||
"spd-lsa-2016": {"id": "spd-lsa-2016", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "LSA", "wp": 7, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-06-06", "name": "SPD LSA Wahlprogramm 2016", "titel": None, "pdf": "spd-lsa-2016.pdf", "seiten": 72},
|
||||
"afd-lsa-2021": {"id": "afd-lsa-2021", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "LSA", "wp": 8, "gueltig_ab": "2021-06-06", "gueltig_bis": None, "name": "AfD Sachsen-Anhalt Wahlprogramm 2021", "titel": "Alles für unsere Heimat! Programm der AfD Sachsen-Anhalt zur Landtagswahl 2021", "pdf": "afd-lsa-2021.pdf", "seiten": 64},
|
||||
"cdu-lsa-2021": {"id": "cdu-lsa-2021", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "LSA", "wp": 8, "gueltig_ab": "2021-06-06", "gueltig_bis": None, "name": "CDU Sachsen-Anhalt Wahlprogramm 2021", "titel": "Unsere Heimat. Unsere Verantwortung.", "pdf": "cdu-lsa-2021.pdf", "seiten": 82},
|
||||
"fdp-lsa-2021": {"id": "fdp-lsa-2021", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "LSA", "wp": 8, "gueltig_ab": "2021-06-06", "gueltig_bis": None, "name": "FDP Sachsen-Anhalt Wahlprogramm 2021", "titel": "Wahlprogramm der FDP Sachsen-Anhalt zur Landtagswahl 2021", "pdf": "fdp-lsa-2021.pdf", "seiten": 76},
|
||||
"gruene-lsa-2021": {"id": "gruene-lsa-2021", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "LSA", "wp": 8, "gueltig_ab": "2021-06-06", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Sachsen-Anhalt Wahlprogramm 2021", "titel": "Verlässlich für Sachsen-Anhalt", "pdf": "gruene-lsa-2021.pdf", "seiten": 164},
|
||||
"linke-lsa-2021": {"id": "linke-lsa-2021", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "LSA", "wp": 8, "gueltig_ab": "2021-06-06", "gueltig_bis": None, "name": "DIE LINKE Sachsen-Anhalt Wahlprogramm 2021", "titel": "Wahlprogramm zur Landtagswahl 2021", "pdf": "linke-lsa-2021.pdf", "seiten": 88},
|
||||
"spd-lsa-2021": {"id": "spd-lsa-2021", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "LSA", "wp": 8, "gueltig_ab": "2021-06-06", "gueltig_bis": None, "name": "SPD Sachsen-Anhalt Wahlprogramm 2021", "titel": "Zusammenhalt und neue Chancen. Politik fürs ganze Land", "pdf": "spd-lsa-2021.pdf", "seiten": 77},
|
||||
|
||||
# ─── wahlprogramm · MV ───
|
||||
"cdu-mv-2011": {"id": "cdu-mv-2011", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "MV", "wp": 6, "gueltig_ab": "2011-09-04", "gueltig_bis": "2016-10-04", "name": "CDU MV Wahlprogramm 2011", "titel": None, "pdf": "cdu-mv-2011.pdf", "seiten": 34},
|
||||
"gruene-mv-2011": {"id": "gruene-mv-2011", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "MV", "wp": 6, "gueltig_ab": "2011-09-04", "gueltig_bis": "2016-10-04", "name": "Grüne MV Wahlprogramm 2011", "titel": None, "pdf": "gruene-mv-2011.pdf", "seiten": 144},
|
||||
"linke-mv-2011": {"id": "linke-mv-2011", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "MV", "wp": 6, "gueltig_ab": "2011-09-04", "gueltig_bis": "2016-10-04", "name": "DIE LINKE MV Wahlprogramm 2011", "titel": None, "pdf": "linke-mv-2011.pdf", "seiten": 33},
|
||||
"spd-mv-2011": {"id": "spd-mv-2011", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "MV", "wp": 6, "gueltig_ab": "2011-09-04", "gueltig_bis": "2016-10-04", "name": "SPD MV Wahlprogramm 2011", "titel": None, "pdf": "spd-mv-2011.pdf", "seiten": 50},
|
||||
"afd-mv-2016": {"id": "afd-mv-2016", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "MV", "wp": 7, "gueltig_ab": "2016-09-04", "gueltig_bis": "2021-09-26", "name": "AfD MV Wahlprogramm 2016", "titel": None, "pdf": "afd-mv-2016.pdf", "seiten": 22},
|
||||
"cdu-mv-2016": {"id": "cdu-mv-2016", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "MV", "wp": 7, "gueltig_ab": "2016-09-04", "gueltig_bis": "2021-09-26", "name": "CDU MV Wahlprogramm 2016", "titel": None, "pdf": "cdu-mv-2016.pdf", "seiten": 27},
|
||||
"gruene-mv-2016": {"id": "gruene-mv-2016", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "MV", "wp": 7, "gueltig_ab": "2016-09-04", "gueltig_bis": "2021-09-26", "name": "Grüne MV Wahlprogramm 2016", "titel": None, "pdf": "gruene-mv-2016.pdf", "seiten": 100},
|
||||
"linke-mv-2016": {"id": "linke-mv-2016", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "MV", "wp": 7, "gueltig_ab": "2016-09-04", "gueltig_bis": "2021-09-26", "name": "DIE LINKE MV Wahlprogramm 2016", "titel": None, "pdf": "linke-mv-2016.pdf", "seiten": 49},
|
||||
"spd-mv-2016": {"id": "spd-mv-2016", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "MV", "wp": 7, "gueltig_ab": "2016-09-04", "gueltig_bis": "2021-09-26", "name": "SPD MV Wahlprogramm 2016", "titel": None, "pdf": "spd-mv-2016.pdf", "seiten": 48},
|
||||
"afd-mv-2021": {"id": "afd-mv-2021", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "MV", "wp": 8, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "AfD Mecklenburg-Vorpommern Wahlprogramm 2021", "titel": "Landeswahlprogramm der AfD Mecklenburg-Vorpommern 2021", "pdf": "afd-mv-2021.pdf", "seiten": 84},
|
||||
"cdu-mv-2021": {"id": "cdu-mv-2021", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "MV", "wp": 8, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "CDU Mecklenburg-Vorpommern Wahlprogramm 2021", "titel": "Zusammen. Den Blick nach vorn. Gemeinsam die Zukunft meistern", "pdf": "cdu-mv-2021.pdf", "seiten": 56},
|
||||
"fdp-mv-2021": {"id": "fdp-mv-2021", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "MV", "wp": 8, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "FDP Mecklenburg-Vorpommern Wahlprogramm 2021", "titel": "Wahlprogramm der Freien Demokraten Mecklenburg-Vorpommern zur Landtagswahl 2021", "pdf": "fdp-mv-2021.pdf", "seiten": 120},
|
||||
"gruene-mv-2021": {"id": "gruene-mv-2021", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "MV", "wp": 8, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Mecklenburg-Vorpommern Wahlprogramm 2021", "titel": "Für Klima, Land und ein besseres Miteinander", "pdf": "gruene-mv-2021.pdf", "seiten": 88},
|
||||
"linke-mv-2021": {"id": "linke-mv-2021", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "MV", "wp": 8, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "DIE LINKE Mecklenburg-Vorpommern Wahlprogramm 2021", "titel": "Das ist links! — Zukunftsprogramm für Mecklenburg-Vorpommern", "pdf": "linke-mv-2021.pdf", "seiten": 82},
|
||||
"spd-mv-2021": {"id": "spd-mv-2021", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "MV", "wp": 8, "gueltig_ab": "2021-09-26", "gueltig_bis": None, "name": "SPD Mecklenburg-Vorpommern Wahlprogramm 2021", "titel": "Verantwortung für heute und morgen — Regierungsprogramm 2021–2026", "pdf": "spd-mv-2021.pdf", "seiten": 95},
|
||||
|
||||
# ─── wahlprogramm · NI ───
|
||||
"cdu-ni-2013": {"id": "cdu-ni-2013", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "NI", "wp": 17, "gueltig_ab": "2013-01-20", "gueltig_bis": "2017-11-14", "name": "CDU NI Wahlprogramm 2013", "titel": None, "pdf": "cdu-ni-2013.pdf", "seiten": 86},
|
||||
"fdp-ni-2013": {"id": "fdp-ni-2013", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "NI", "wp": 17, "gueltig_ab": "2013-01-20", "gueltig_bis": "2017-11-14", "name": "FDP NI Wahlprogramm 2013", "titel": None, "pdf": "fdp-ni-2013.pdf", "seiten": 65},
|
||||
"gruene-ni-2013": {"id": "gruene-ni-2013", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "NI", "wp": 17, "gueltig_ab": "2013-01-20", "gueltig_bis": "2017-11-14", "name": "Grüne NI Wahlprogramm 2013", "titel": None, "pdf": "gruene-ni-2013.pdf", "seiten": 92},
|
||||
"spd-ni-2013": {"id": "spd-ni-2013", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "NI", "wp": 17, "gueltig_ab": "2013-01-20", "gueltig_bis": "2017-11-14", "name": "SPD NI Wahlprogramm 2013", "titel": None, "pdf": "spd-ni-2013.pdf", "seiten": 70},
|
||||
"afd-ni-2017": {"id": "afd-ni-2017", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "NI", "wp": 18, "gueltig_ab": "2017-10-15", "gueltig_bis": "2022-10-09", "name": "AfD NI Wahlprogramm 2017", "titel": None, "pdf": "afd-ni-2017.pdf", "seiten": 93},
|
||||
"cdu-ni-2017": {"id": "cdu-ni-2017", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "NI", "wp": 18, "gueltig_ab": "2017-10-15", "gueltig_bis": "2022-10-09", "name": "CDU NI Wahlprogramm 2017", "titel": None, "pdf": "cdu-ni-2017.pdf", "seiten": 116},
|
||||
"fdp-ni-2017": {"id": "fdp-ni-2017", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "NI", "wp": 18, "gueltig_ab": "2017-10-15", "gueltig_bis": "2022-10-09", "name": "FDP NI Wahlprogramm 2017", "titel": None, "pdf": "fdp-ni-2017.pdf", "seiten": 74},
|
||||
"gruene-ni-2017": {"id": "gruene-ni-2017", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "NI", "wp": 18, "gueltig_ab": "2017-10-15", "gueltig_bis": "2022-10-09", "name": "Grüne NI Wahlprogramm 2017", "titel": None, "pdf": "gruene-ni-2017.pdf", "seiten": 101},
|
||||
"spd-ni-2017": {"id": "spd-ni-2017", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "NI", "wp": 18, "gueltig_ab": "2017-10-15", "gueltig_bis": "2022-10-09", "name": "SPD NI Wahlprogramm 2017", "titel": None, "pdf": "spd-ni-2017.pdf", "seiten": 154},
|
||||
"afd-ni-2022": {"id": "afd-ni-2022", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "NI", "wp": 19, "gueltig_ab": "2022-10-09", "gueltig_bis": None, "name": "AfD Niedersachsen Wahlprogramm 2022", "titel": None, "pdf": "afd-ni-2022.pdf", "seiten": 100},
|
||||
"cdu-ni-2022": {"id": "cdu-ni-2022", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "NI", "wp": 19, "gueltig_ab": "2022-10-09", "gueltig_bis": None, "name": "CDU Niedersachsen Wahlprogramm 2022", "titel": "CDU Niedersachsen Regierungsprogramm 2022", "pdf": "cdu-ni-2022.pdf", "seiten": 100},
|
||||
"gruene-ni-2022": {"id": "gruene-ni-2022", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "NI", "wp": 19, "gueltig_ab": "2022-10-09", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Niedersachsen Wahlprogramm 2022", "titel": None, "pdf": "gruene-ni-2022.pdf", "seiten": 100},
|
||||
"spd-ni-2022": {"id": "spd-ni-2022", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "NI", "wp": 19, "gueltig_ab": "2022-10-09", "gueltig_bis": None, "name": "SPD Niedersachsen Wahlprogramm 2022", "titel": "SPD Niedersachsen Regierungsprogramm 2022", "pdf": "spd-ni-2022.pdf", "seiten": 100},
|
||||
|
||||
# ─── wahlprogramm · NRW ───
|
||||
"cdu-nrw-2012": {"id": "cdu-nrw-2012", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "NRW", "wp": 16, "gueltig_ab": "2012-05-13", "gueltig_bis": "2017-06-01", "name": "CDU NRW Wahlprogramm 2012", "titel": None, "pdf": "cdu-nrw-2012.pdf", "seiten": 17},
|
||||
"fdp-nrw-2012": {"id": "fdp-nrw-2012", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "NRW", "wp": 16, "gueltig_ab": "2012-05-13", "gueltig_bis": "2017-06-01", "name": "FDP NRW Wahlprogramm 2012", "titel": None, "pdf": "fdp-nrw-2012.pdf", "seiten": 6},
|
||||
"gruene-nrw-2012": {"id": "gruene-nrw-2012", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "NRW", "wp": 16, "gueltig_ab": "2012-05-13", "gueltig_bis": "2017-06-01", "name": "Grüne NRW Zukunftsplan Update 2012", "titel": None, "pdf": "gruene-nrw-2012.pdf", "seiten": 52},
|
||||
"piraten-nrw-2012": {"id": "piraten-nrw-2012", "typ": "wahlprogramm", "partei": "PIRATEN", "bundesland": "NRW", "wp": 16, "gueltig_ab": "2012-05-13", "gueltig_bis": "2017-06-01", "name": "PIRATEN NRW Wahlprogramm 2012", "titel": None, "pdf": "piraten-nrw-2012.pdf", "seiten": 76},
|
||||
"spd-nrw-2012": {"id": "spd-nrw-2012", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "NRW", "wp": 16, "gueltig_ab": "2012-05-13", "gueltig_bis": "2017-06-01", "name": "SPD NRW Wahlprogramm 2012", "titel": None, "pdf": "spd-nrw-2012.pdf", "seiten": 22},
|
||||
"afd-nrw-2017": {"id": "afd-nrw-2017", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "NRW", "wp": 17, "gueltig_ab": "2017-05-14", "gueltig_bis": "2022-05-15", "name": "AfD NRW Wahlprogramm 2017", "titel": None, "pdf": "afd-nrw-2017.pdf", "seiten": 84},
|
||||
"cdu-nrw-2017": {"id": "cdu-nrw-2017", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "NRW", "wp": 17, "gueltig_ab": "2017-05-14", "gueltig_bis": "2022-05-15", "name": "CDU NRW Regierungsprogramm 2017", "titel": None, "pdf": "cdu-nrw-2017.pdf", "seiten": 120},
|
||||
"fdp-nrw-2017": {"id": "fdp-nrw-2017", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "NRW", "wp": 17, "gueltig_ab": "2017-05-14", "gueltig_bis": "2022-05-15", "name": "FDP NRW Wahlprogramm 2017", "titel": None, "pdf": "fdp-nrw-2017.pdf", "seiten": 56},
|
||||
"gruene-nrw-2017": {"id": "gruene-nrw-2017", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "NRW", "wp": 17, "gueltig_ab": "2017-05-14", "gueltig_bis": "2022-05-15", "name": "Grüne NRW Wahlprogramm 2017", "titel": None, "pdf": "gruene-nrw-2017.pdf", "seiten": 131},
|
||||
"spd-nrw-2017": {"id": "spd-nrw-2017", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "NRW", "wp": 17, "gueltig_ab": "2017-05-14", "gueltig_bis": "2022-05-15", "name": "SPD NRW Regierungsprogramm 2017", "titel": None, "pdf": "spd-nrw-2017.pdf", "seiten": 116},
|
||||
"afd-nrw-2022": {"id": "afd-nrw-2022", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "NRW", "wp": 18, "gueltig_ab": "2022-05-15", "gueltig_bis": None, "name": "AfD NRW Wahlprogramm 2022", "titel": "Wer sonst.", "pdf": "afd-nrw-2022.pdf", "seiten": 68},
|
||||
"cdu-nrw-2022": {"id": "cdu-nrw-2022", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "NRW", "wp": 18, "gueltig_ab": "2022-05-15", "gueltig_bis": None, "name": "CDU NRW Wahlprogramm 2022", "titel": "Machen, worauf es ankommt", "pdf": "cdu-nrw-2022.pdf", "seiten": 109},
|
||||
"fdp-nrw-2022": {"id": "fdp-nrw-2022", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "NRW", "wp": 18, "gueltig_ab": "2022-05-15", "gueltig_bis": None, "name": "FDP NRW Wahlprogramm 2022", "titel": "Nie gab es mehr zu tun", "pdf": "fdp-nrw-2022.pdf", "seiten": 96},
|
||||
"gruene-nrw-2022": {"id": "gruene-nrw-2022", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "NRW", "wp": 18, "gueltig_ab": "2022-05-15", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN NRW Wahlprogramm 2022", "titel": "Von hier an Zukunft", "pdf": "gruene-nrw-2022.pdf", "seiten": 100},
|
||||
"spd-nrw-2022": {"id": "spd-nrw-2022", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "NRW", "wp": 18, "gueltig_ab": "2022-05-15", "gueltig_bis": None, "name": "SPD NRW Wahlprogramm 2022", "titel": "Unser Land von morgen", "pdf": "spd-nrw-2022.pdf", "seiten": 116},
|
||||
|
||||
# ─── wahlprogramm · RP ───
|
||||
"cdu-rp-2011": {"id": "cdu-rp-2011", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "RP", "wp": 16, "gueltig_ab": "2011-03-27", "gueltig_bis": "2016-05-18", "name": "CDU RP Wahlprogramm 2011", "titel": None, "pdf": "cdu-rp-2011.pdf", "seiten": 84},
|
||||
"gruene-rp-2011": {"id": "gruene-rp-2011", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "RP", "wp": 16, "gueltig_ab": "2011-03-27", "gueltig_bis": "2016-05-18", "name": "Grüne RP Wahlprogramm 2011", "titel": None, "pdf": "gruene-rp-2011.pdf", "seiten": 110},
|
||||
"spd-rp-2011": {"id": "spd-rp-2011", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "RP", "wp": 16, "gueltig_ab": "2011-03-27", "gueltig_bis": "2016-05-18", "name": "SPD RP Wahlprogramm 2011", "titel": None, "pdf": "spd-rp-2011.pdf", "seiten": 65},
|
||||
"afd-rp-2016": {"id": "afd-rp-2016", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "RP", "wp": 17, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-03-14", "name": "AfD RP Wahlprogramm 2016", "titel": None, "pdf": "afd-rp-2016.pdf", "seiten": 17},
|
||||
"cdu-rp-2016": {"id": "cdu-rp-2016", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "RP", "wp": 17, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-03-14", "name": "CDU RP Wahlprogramm 2016", "titel": None, "pdf": "cdu-rp-2016.pdf", "seiten": 102},
|
||||
"fdp-rp-2016": {"id": "fdp-rp-2016", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "RP", "wp": 17, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-03-14", "name": "FDP RP Wahlprogramm 2016", "titel": None, "pdf": "fdp-rp-2016.pdf", "seiten": 84},
|
||||
"gruene-rp-2016": {"id": "gruene-rp-2016", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "RP", "wp": 17, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-03-14", "name": "Grüne RP Wahlprogramm 2016", "titel": None, "pdf": "gruene-rp-2016.pdf", "seiten": 57},
|
||||
"spd-rp-2016": {"id": "spd-rp-2016", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "RP", "wp": 17, "gueltig_ab": "2016-03-13", "gueltig_bis": "2021-03-14", "name": "SPD RP Wahlprogramm 2016", "titel": None, "pdf": "spd-rp-2016.pdf", "seiten": 64},
|
||||
"afd-rp-2021": {"id": "afd-rp-2021", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "RP", "wp": 18, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "AfD Rheinland-Pfalz Wahlprogramm 2021", "titel": None, "pdf": "afd-rp-2021.pdf", "seiten": 100},
|
||||
"cdu-rp-2021": {"id": "cdu-rp-2021", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "RP", "wp": 18, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "CDU Rheinland-Pfalz Wahlprogramm 2021", "titel": "Regierungsprogramm der CDU RLP 2021–26", "pdf": "cdu-rp-2021.pdf", "seiten": 100},
|
||||
"fdp-rp-2021": {"id": "fdp-rp-2021", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "RP", "wp": 18, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "FDP Rheinland-Pfalz Wahlprogramm 2021", "titel": "FDP Rheinland-Pfalz Landtagswahlprogramm 2021", "pdf": "fdp-rp-2021.pdf", "seiten": 100},
|
||||
"fw-rp-2021": {"id": "fw-rp-2021", "typ": "wahlprogramm", "partei": "FREIE WÄHLER", "bundesland": "RP", "wp": 18, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "FREIE WÄHLER Rheinland-Pfalz Wahlprogramm 2021", "titel": None, "pdf": "fw-rp-2021.pdf", "seiten": 80},
|
||||
"gruene-rp-2021": {"id": "gruene-rp-2021", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "RP", "wp": 18, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Rheinland-Pfalz Wahlprogramm 2021", "titel": "BÜNDNIS 90/DIE GRÜNEN Rheinland-Pfalz Landtagswahlprogramm 2021", "pdf": "gruene-rp-2021.pdf", "seiten": 100},
|
||||
"spd-rp-2021": {"id": "spd-rp-2021", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "RP", "wp": 18, "gueltig_ab": "2021-03-14", "gueltig_bis": None, "name": "SPD Rheinland-Pfalz Wahlprogramm 2021", "titel": "Wir mit Ihr — Regierungsprogramm der SPD Rheinland-Pfalz 2021–2026", "pdf": "spd-rp-2021.pdf", "seiten": 100},
|
||||
|
||||
# ─── wahlprogramm · SH ───
|
||||
"cdu-sh-2012": {"id": "cdu-sh-2012", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "SH", "wp": 18, "gueltig_ab": "2012-05-06", "gueltig_bis": "2017-06-06", "name": "CDU SH Wahlprogramm 2012", "titel": None, "pdf": "cdu-sh-2012.pdf", "seiten": 150},
|
||||
"fdp-sh-2012": {"id": "fdp-sh-2012", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "SH", "wp": 18, "gueltig_ab": "2012-05-06", "gueltig_bis": "2017-06-06", "name": "FDP SH Wahlprogramm 2012", "titel": None, "pdf": "fdp-sh-2012.pdf", "seiten": 99},
|
||||
"gruene-sh-2012": {"id": "gruene-sh-2012", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "SH", "wp": 18, "gueltig_ab": "2012-05-06", "gueltig_bis": "2017-06-06", "name": "Grüne SH Wahlprogramm 2012", "titel": None, "pdf": "gruene-sh-2012.pdf", "seiten": 80},
|
||||
"piraten-sh-2012": {"id": "piraten-sh-2012", "typ": "wahlprogramm", "partei": "PIRATEN", "bundesland": "SH", "wp": 18, "gueltig_ab": "2012-05-06", "gueltig_bis": "2017-06-06", "name": "PIRATEN SH Wahlprogramm 2012", "titel": None, "pdf": "piraten-sh-2012.pdf", "seiten": 80},
|
||||
"spd-sh-2012": {"id": "spd-sh-2012", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "SH", "wp": 18, "gueltig_ab": "2012-05-06", "gueltig_bis": "2017-06-06", "name": "SPD SH Wahlprogramm 2012", "titel": None, "pdf": "spd-sh-2012.pdf", "seiten": 36},
|
||||
"ssw-sh-2012": {"id": "ssw-sh-2012", "typ": "wahlprogramm", "partei": "SSW", "bundesland": "SH", "wp": 18, "gueltig_ab": "2012-05-06", "gueltig_bis": "2017-06-06", "name": "SSW SH Wahlprogramm 2012", "titel": None, "pdf": "ssw-sh-2012.pdf", "seiten": 84},
|
||||
"afd-sh-2017": {"id": "afd-sh-2017", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "SH", "wp": 19, "gueltig_ab": "2017-05-07", "gueltig_bis": "2022-05-08", "name": "AfD SH Wahlprogramm 2017", "titel": None, "pdf": "afd-sh-2017.pdf", "seiten": 56},
|
||||
"cdu-sh-2017": {"id": "cdu-sh-2017", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "SH", "wp": 19, "gueltig_ab": "2017-05-07", "gueltig_bis": "2022-05-08", "name": "CDU SH Wahlprogramm 2017", "titel": None, "pdf": "cdu-sh-2017.pdf", "seiten": 96},
|
||||
"fdp-sh-2017": {"id": "fdp-sh-2017", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "SH", "wp": 19, "gueltig_ab": "2017-05-07", "gueltig_bis": "2022-05-08", "name": "FDP SH Wahlprogramm 2017", "titel": None, "pdf": "fdp-sh-2017.pdf", "seiten": 117},
|
||||
"gruene-sh-2017": {"id": "gruene-sh-2017", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "SH", "wp": 19, "gueltig_ab": "2017-05-07", "gueltig_bis": "2022-05-08", "name": "Grüne SH Wahlprogramm 2017", "titel": None, "pdf": "gruene-sh-2017.pdf", "seiten": 95},
|
||||
"spd-sh-2017": {"id": "spd-sh-2017", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "SH", "wp": 19, "gueltig_ab": "2017-05-07", "gueltig_bis": "2022-05-08", "name": "SPD SH Wahlprogramm 2017", "titel": None, "pdf": "spd-sh-2017.pdf", "seiten": 66},
|
||||
"ssw-sh-2017": {"id": "ssw-sh-2017", "typ": "wahlprogramm", "partei": "SSW", "bundesland": "SH", "wp": 19, "gueltig_ab": "2017-05-07", "gueltig_bis": "2022-05-08", "name": "SSW SH Wahlprogramm 2017", "titel": None, "pdf": "ssw-sh-2017.pdf", "seiten": 60},
|
||||
"cdu-sh-2022": {"id": "cdu-sh-2022", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "SH", "wp": 20, "gueltig_ab": "2022-05-08", "gueltig_bis": None, "name": "CDU Schleswig-Holstein Wahlprogramm 2022", "titel": None, "pdf": "cdu-sh-2022.pdf", "seiten": 100},
|
||||
"fdp-sh-2022": {"id": "fdp-sh-2022", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "SH", "wp": 20, "gueltig_ab": "2022-05-08", "gueltig_bis": None, "name": "FDP Schleswig-Holstein Wahlprogramm 2022", "titel": None, "pdf": "fdp-sh-2022.pdf", "seiten": 100},
|
||||
"gruene-sh-2022": {"id": "gruene-sh-2022", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "SH", "wp": 20, "gueltig_ab": "2022-05-08", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Schleswig-Holstein Wahlprogramm 2022", "titel": None, "pdf": "gruene-sh-2022.pdf", "seiten": 100},
|
||||
"spd-sh-2022": {"id": "spd-sh-2022", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "SH", "wp": 20, "gueltig_ab": "2022-05-08", "gueltig_bis": None, "name": "SPD Schleswig-Holstein Wahlprogramm 2022", "titel": None, "pdf": "spd-sh-2022.pdf", "seiten": 100},
|
||||
"ssw-sh-2022": {"id": "ssw-sh-2022", "typ": "wahlprogramm", "partei": "SSW", "bundesland": "SH", "wp": 20, "gueltig_ab": "2022-05-08", "gueltig_bis": None, "name": "SSW Wahlprogramm 2022", "titel": "SSW Schleswig-Holstein Wahlprogramm 2022", "pdf": "ssw-sh-2022.pdf", "seiten": 80},
|
||||
|
||||
# ─── wahlprogramm · SL ───
|
||||
"cdu-sl-2012": {"id": "cdu-sl-2012", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "SL", "wp": 15, "gueltig_ab": "2012-03-25", "gueltig_bis": "2017-05-09", "name": "CDU SL Wahlprogramm 2012", "titel": None, "pdf": "cdu-sl-2012.pdf", "seiten": 36},
|
||||
"linke-sl-2012": {"id": "linke-sl-2012", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "SL", "wp": 15, "gueltig_ab": "2012-03-25", "gueltig_bis": "2017-05-09", "name": "DIE LINKE SL Wahlprogramm 2012", "titel": None, "pdf": "linke-sl-2012.pdf", "seiten": 2},
|
||||
"piraten-sl-2012": {"id": "piraten-sl-2012", "typ": "wahlprogramm", "partei": "PIRATEN", "bundesland": "SL", "wp": 15, "gueltig_ab": "2012-03-25", "gueltig_bis": "2017-05-09", "name": "PIRATEN SL Wahlprogramm 2012", "titel": None, "pdf": "piraten-sl-2012.pdf", "seiten": 21},
|
||||
"spd-sl-2012": {"id": "spd-sl-2012", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "SL", "wp": 15, "gueltig_ab": "2012-03-25", "gueltig_bis": "2017-05-09", "name": "SPD SL Wahlprogramm 2012", "titel": None, "pdf": "spd-sl-2012.pdf", "seiten": 40},
|
||||
"afd-sl-2017": {"id": "afd-sl-2017", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "SL", "wp": 16, "gueltig_ab": "2017-03-26", "gueltig_bis": "2022-03-27", "name": "AfD SL Wahlprogramm 2017", "titel": None, "pdf": "afd-sl-2017.pdf", "seiten": 43},
|
||||
"cdu-sl-2017": {"id": "cdu-sl-2017", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "SL", "wp": 16, "gueltig_ab": "2017-03-26", "gueltig_bis": "2022-03-27", "name": "CDU SL Wahlprogramm 2017", "titel": None, "pdf": "cdu-sl-2017.pdf", "seiten": 72},
|
||||
"gruene-sl-2017": {"id": "gruene-sl-2017", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "SL", "wp": 16, "gueltig_ab": "2017-03-26", "gueltig_bis": "2022-03-27", "name": "Grüne SL Wahlprogramm 2017", "titel": None, "pdf": "gruene-sl-2017.pdf", "seiten": 75},
|
||||
"linke-sl-2017": {"id": "linke-sl-2017", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "SL", "wp": 16, "gueltig_ab": "2017-03-26", "gueltig_bis": "2022-03-27", "name": "DIE LINKE SL Wahlprogramm 2017", "titel": None, "pdf": "linke-sl-2017.pdf", "seiten": 34},
|
||||
"spd-sl-2017": {"id": "spd-sl-2017", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "SL", "wp": 16, "gueltig_ab": "2017-03-26", "gueltig_bis": "2022-03-27", "name": "SPD SL Wahlprogramm 2017", "titel": None, "pdf": "spd-sl-2017.pdf", "seiten": 41},
|
||||
"afd-sl-2022": {"id": "afd-sl-2022", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "SL", "wp": 17, "gueltig_ab": "2022-03-27", "gueltig_bis": None, "name": "AfD Saarland Wahlprogramm 2022", "titel": None, "pdf": "afd-sl-2022.pdf", "seiten": 100},
|
||||
"cdu-sl-2022": {"id": "cdu-sl-2022", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "SL", "wp": 17, "gueltig_ab": "2022-03-27", "gueltig_bis": None, "name": "CDU Saarland Wahlprogramm 2022", "titel": None, "pdf": "cdu-sl-2022.pdf", "seiten": 100},
|
||||
"spd-sl-2022": {"id": "spd-sl-2022", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "SL", "wp": 17, "gueltig_ab": "2022-03-27", "gueltig_bis": None, "name": "SPD Saarland Wahlprogramm 2022", "titel": "SPD Saarland Regierungsprogramm 2022", "pdf": "spd-sl-2022.pdf", "seiten": 100},
|
||||
|
||||
# ─── wahlprogramm · SN ───
|
||||
"afd-sn-2014": {"id": "afd-sn-2014", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "SN", "wp": 6, "gueltig_ab": "2014-08-31", "gueltig_bis": "2019-09-01", "name": "AfD SN Wahlprogramm 2014", "titel": None, "pdf": "afd-sn-2014.pdf", "seiten": 26},
|
||||
"cdu-sn-2014": {"id": "cdu-sn-2014", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "SN", "wp": 6, "gueltig_ab": "2014-08-31", "gueltig_bis": "2019-09-01", "name": "CDU SN Wahlprogramm 2014", "titel": None, "pdf": "cdu-sn-2014.pdf", "seiten": 86},
|
||||
"gruene-sn-2014": {"id": "gruene-sn-2014", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "SN", "wp": 6, "gueltig_ab": "2014-08-31", "gueltig_bis": "2019-09-01", "name": "Grüne SN Wahlprogramm 2014", "titel": None, "pdf": "gruene-sn-2014.pdf", "seiten": 149},
|
||||
"linke-sn-2014": {"id": "linke-sn-2014", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "SN", "wp": 6, "gueltig_ab": "2014-08-31", "gueltig_bis": "2019-09-01", "name": "DIE LINKE SN Wahlprogramm 2014", "titel": None, "pdf": "linke-sn-2014.pdf", "seiten": 56},
|
||||
"spd-sn-2014": {"id": "spd-sn-2014", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "SN", "wp": 6, "gueltig_ab": "2014-08-31", "gueltig_bis": "2019-09-01", "name": "SPD SN Wahlprogramm 2014", "titel": None, "pdf": "spd-sn-2014.pdf", "seiten": 56},
|
||||
"afd-sn-2019": {"id": "afd-sn-2019", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "SN", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-01", "name": "AfD SN Wahlprogramm 2019", "titel": None, "pdf": "afd-sn-2019.pdf", "seiten": 76},
|
||||
"cdu-sn-2019": {"id": "cdu-sn-2019", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "SN", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-01", "name": "CDU SN Wahlprogramm 2019", "titel": None, "pdf": "cdu-sn-2019.pdf", "seiten": 71},
|
||||
"gruene-sn-2019": {"id": "gruene-sn-2019", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "SN", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-01", "name": "Grüne SN Wahlprogramm 2019", "titel": None, "pdf": "gruene-sn-2019.pdf", "seiten": 86},
|
||||
"linke-sn-2019": {"id": "linke-sn-2019", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "SN", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-01", "name": "DIE LINKE SN Wahlprogramm 2019", "titel": None, "pdf": "linke-sn-2019.pdf", "seiten": 72},
|
||||
"spd-sn-2019": {"id": "spd-sn-2019", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "SN", "wp": 7, "gueltig_ab": "2019-09-01", "gueltig_bis": "2024-09-01", "name": "SPD SN Wahlprogramm 2019", "titel": None, "pdf": "spd-sn-2019.pdf", "seiten": 105},
|
||||
"afd-sn-2024": {"id": "afd-sn-2024", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "SN", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "AfD Sachsen Wahlprogramm 2024", "titel": None, "pdf": "afd-sn-2024.pdf", "seiten": 100},
|
||||
"bsw-sn-2024": {"id": "bsw-sn-2024", "typ": "wahlprogramm", "partei": "BSW", "bundesland": "SN", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "BSW Sachsen Wahlprogramm 2024", "titel": None, "pdf": "bsw-sn-2024.pdf", "seiten": 50},
|
||||
"cdu-sn-2024": {"id": "cdu-sn-2024", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "SN", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "CDU Sachsen Wahlprogramm 2024", "titel": None, "pdf": "cdu-sn-2024.pdf", "seiten": 100},
|
||||
"gruene-sn-2024": {"id": "gruene-sn-2024", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "SN", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "BÜNDNIS 90/DIE GRÜNEN Sachsen Wahlprogramm 2024", "titel": None, "pdf": "gruene-sn-2024.pdf", "seiten": 100},
|
||||
"linke-sn-2024": {"id": "linke-sn-2024", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "SN", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "DIE LINKE Sachsen Wahlprogramm 2024", "titel": None, "pdf": "linke-sn-2024.pdf", "seiten": 100},
|
||||
"spd-sn-2024": {"id": "spd-sn-2024", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "SN", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "SPD Sachsen Wahlprogramm 2024", "titel": None, "pdf": "spd-sn-2024.pdf", "seiten": 100},
|
||||
|
||||
# ─── wahlprogramm · TH ───
|
||||
"afd-th-2014": {"id": "afd-th-2014", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "TH", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-10-27", "name": "AfD TH Wahlprogramm 2014", "titel": None, "pdf": "afd-th-2014.pdf", "seiten": 32},
|
||||
"cdu-th-2014": {"id": "cdu-th-2014", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "TH", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-10-27", "name": "CDU TH Wahlprogramm 2014", "titel": None, "pdf": "cdu-th-2014.pdf", "seiten": 82},
|
||||
"fdp-th-2014": {"id": "fdp-th-2014", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "TH", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-10-27", "name": "FDP TH Wahlprogramm 2014", "titel": None, "pdf": "fdp-th-2014.pdf", "seiten": 77},
|
||||
"gruene-th-2014": {"id": "gruene-th-2014", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "TH", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-10-27", "name": "Grüne TH Wahlprogramm 2014", "titel": None, "pdf": "gruene-th-2014.pdf", "seiten": 84},
|
||||
"linke-th-2014": {"id": "linke-th-2014", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "TH", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-10-27", "name": "DIE LINKE TH Wahlprogramm 2014", "titel": None, "pdf": "linke-th-2014.pdf", "seiten": 60},
|
||||
"spd-th-2014": {"id": "spd-th-2014", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "TH", "wp": 6, "gueltig_ab": "2014-09-14", "gueltig_bis": "2019-10-27", "name": "SPD TH Wahlprogramm 2014", "titel": None, "pdf": "spd-th-2014.pdf", "seiten": 52},
|
||||
"afd-th-2019": {"id": "afd-th-2019", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "TH", "wp": 7, "gueltig_ab": "2019-10-27", "gueltig_bis": "2024-09-01", "name": "AfD TH Wahlprogramm 2019", "titel": None, "pdf": "afd-th-2019.pdf", "seiten": 96},
|
||||
"cdu-th-2019": {"id": "cdu-th-2019", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "TH", "wp": 7, "gueltig_ab": "2019-10-27", "gueltig_bis": "2024-09-01", "name": "CDU TH Wahlprogramm 2019", "titel": None, "pdf": "cdu-th-2019.pdf", "seiten": 48},
|
||||
"fdp-th-2019": {"id": "fdp-th-2019", "typ": "wahlprogramm", "partei": "FDP", "bundesland": "TH", "wp": 7, "gueltig_ab": "2019-10-27", "gueltig_bis": "2024-09-01", "name": "FDP TH Wahlprogramm 2019", "titel": None, "pdf": "fdp-th-2019.pdf", "seiten": 86},
|
||||
"gruene-th-2019": {"id": "gruene-th-2019", "typ": "wahlprogramm", "partei": "GRÜNE", "bundesland": "TH", "wp": 7, "gueltig_ab": "2019-10-27", "gueltig_bis": "2024-09-01", "name": "Grüne TH Wahlprogramm 2019", "titel": None, "pdf": "gruene-th-2019.pdf", "seiten": 83},
|
||||
"linke-th-2019": {"id": "linke-th-2019", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "TH", "wp": 7, "gueltig_ab": "2019-10-27", "gueltig_bis": "2024-09-01", "name": "DIE LINKE TH Wahlprogramm 2019", "titel": None, "pdf": "linke-th-2019.pdf", "seiten": 105},
|
||||
"spd-th-2019": {"id": "spd-th-2019", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "TH", "wp": 7, "gueltig_ab": "2019-10-27", "gueltig_bis": "2024-09-01", "name": "SPD TH Wahlprogramm 2019", "titel": None, "pdf": "spd-th-2019.pdf", "seiten": 32},
|
||||
"afd-th-2024": {"id": "afd-th-2024", "typ": "wahlprogramm", "partei": "AfD", "bundesland": "TH", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "AfD Thüringen Wahlprogramm 2024", "titel": "AfD Thüringen Landtagswahlprogramm 2024", "pdf": "afd-th-2024.pdf", "seiten": 100},
|
||||
"bsw-th-2024": {"id": "bsw-th-2024", "typ": "wahlprogramm", "partei": "BSW", "bundesland": "TH", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "BSW Thüringen Wahlprogramm 2024", "titel": None, "pdf": "bsw-th-2024.pdf", "seiten": 50},
|
||||
"cdu-th-2024": {"id": "cdu-th-2024", "typ": "wahlprogramm", "partei": "CDU", "bundesland": "TH", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "CDU Thüringen Wahlprogramm 2024", "titel": "Wahlprogramm der CDU Thüringen 2024", "pdf": "cdu-th-2024.pdf", "seiten": 83},
|
||||
"linke-th-2024": {"id": "linke-th-2024", "typ": "wahlprogramm", "partei": "LINKE", "bundesland": "TH", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "DIE LINKE Thüringen Wahlprogramm 2024", "titel": None, "pdf": "linke-th-2024.pdf", "seiten": 100},
|
||||
"spd-th-2024": {"id": "spd-th-2024", "typ": "wahlprogramm", "partei": "SPD", "bundesland": "TH", "wp": 8, "gueltig_ab": "2024-09-01", "gueltig_bis": None, "name": "SPD Thüringen Wahlprogramm 2024", "titel": None, "pdf": "spd-th-2024.pdf", "seiten": 100},
|
||||
}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
@ -86,6 +424,14 @@ def get_programm(programm_id: str) -> Optional[Programm]:
|
||||
return PROGRAMME.get(programm_id)
|
||||
|
||||
|
||||
def get_programm_by_pdf(pdf: str) -> Optional[Programm]:
|
||||
"""Reverse-Lookup über pdf-Dateinamen."""
|
||||
for prog in PROGRAMME.values():
|
||||
if prog.get("pdf") == pdf:
|
||||
return prog
|
||||
return None
|
||||
|
||||
|
||||
def aktuelles_wahlprogramm(bundesland: str, partei: str) -> Optional[Programm]:
|
||||
"""Aktuell gültiges Wahlprogramm einer Partei in einem Bundesland.
|
||||
|
||||
@ -109,9 +455,6 @@ def wahlprogramm_zum_zeitpunkt(
|
||||
|
||||
``datum`` ist ISO-Datum. Es wird das Programm zurückgegeben, dessen
|
||||
Geltungszeitraum [gueltig_ab, gueltig_bis) das Datum enthält.
|
||||
|
||||
Rückgabe ``None``, wenn die Partei zu dem Zeitpunkt nicht im Schema
|
||||
erfasst ist (oder das Bundesland nicht).
|
||||
"""
|
||||
for prog in PROGRAMME.values():
|
||||
if (
|
||||
@ -134,11 +477,8 @@ def grundsatzprogramm_zum_zeitpunkt(
|
||||
Wenn ``bundesland`` gesetzt ist, wird zuerst nach einem
|
||||
Landes-Grundsatzprogramm gesucht; falls keines existiert, fällt die
|
||||
Suche auf das Bundes-Grundsatzprogramm zurück.
|
||||
|
||||
Wenn ``bundesland`` ``None`` ist, wird nur nach Bundes-Grundsatz gesucht.
|
||||
"""
|
||||
if bundesland is not None:
|
||||
# Erst Land suchen
|
||||
for prog in PROGRAMME.values():
|
||||
if (
|
||||
prog.get("typ") == "grundsatzprogramm-land"
|
||||
@ -147,7 +487,6 @@ def grundsatzprogramm_zum_zeitpunkt(
|
||||
and _date_in_range(datum, prog["gueltig_ab"], prog.get("gueltig_bis"))
|
||||
):
|
||||
return prog
|
||||
# Bund als Fallback / oder primär
|
||||
for prog in PROGRAMME.values():
|
||||
if (
|
||||
prog.get("typ") == "grundsatzprogramm-bund"
|
||||
@ -189,366 +528,7 @@ def alle_versionen(bundesland: str, partei: str) -> list[Programm]:
|
||||
return versions
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Initiale Daten — wird über separate Daten-Datei eingespielt.
|
||||
# Während des Migrationsfensters wird ``embeddings.PROGRAMME`` und
|
||||
# ``wahlprogramme.WAHLPROGRAMME`` automatisch in PROGRAMME überführt.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _register(prog: Programm) -> None:
|
||||
"""Add a Programm to the registry, validating uniqueness of id."""
|
||||
if prog["id"] in PROGRAMME:
|
||||
raise ValueError(f"Programm-id collision: {prog['id']}")
|
||||
PROGRAMME[prog["id"]] = prog
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Zusätzliche Programme — Reserve für Programme, die NICHT aus
|
||||
# embeddings.PROGRAMME oder WAHLPROGRAMME migriert werden können (z.B.
|
||||
# zukünftig: historische Vorgänger-Wahlprogramme aus früheren Legislaturen,
|
||||
# falls deren PDFs eingebunden werden ohne dass embeddings.PROGRAMME sie
|
||||
# kennt).
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
_ADDITIONAL_PROGRAMME: list[Programm] = []
|
||||
|
||||
|
||||
# Historische Skelett-Einträge (PDFs jetzt in embeddings.PROGRAMME) —
|
||||
# wurden in einer früheren Iteration manuell als pdf-pending markiert,
|
||||
# bevor die Beschaffung lief. Aktuelle Daten leben in embeddings.PROGRAMME
|
||||
# und werden über _migrate_from_legacy() automatisch übernommen.
|
||||
_ARCHIVED_SKELETONS: list[Programm] = [
|
||||
# ─── CSU Bayern: aktualisiertes Grundsatzprogramm 2023 ───────────────
|
||||
# "Die Ordnung 2016" (in embeddings.PROGRAMME als Bundes-Grundsatz)
|
||||
# wird durch das 2023er ersetzt — wir tragen 2023 als aktuelles ein und
|
||||
# setzen das 2016er auf gueltig_bis. Funktionell ist es ein Landes-
|
||||
# grundsatzprogramm, weil CSU nur in Bayern existiert.
|
||||
{
|
||||
"id": "csu-grundsatz-2023",
|
||||
"titel": "Für ein neues Miteinander — Grundsatzprogramm der CSU",
|
||||
"name": "CSU Grundsatzprogramm 2023",
|
||||
"typ": "grundsatzprogramm-land",
|
||||
"partei": "CSU",
|
||||
"bundesland": "BY",
|
||||
"beschluss": "2023-05-06",
|
||||
"wahl": None,
|
||||
"wp": None,
|
||||
"gueltig_ab": "2023-05-06",
|
||||
"gueltig_bis": None,
|
||||
"pdf": "csu-grundsatz-2023.pdf",
|
||||
"seiten": 0, # nach Download via PyMuPDF setzen
|
||||
"hinweis": "pdf-pending",
|
||||
},
|
||||
# ─── CDU NRW Landesgrundsatzprogramm 2015 ─────────────────────────────
|
||||
{
|
||||
"id": "cdu-grundsatz-nrw-2015",
|
||||
"titel": "Aufstieg, Sicherheit, Perspektive — Das Nordrhein-Westfalen-Programm",
|
||||
"name": "CDU NRW Grundsatzprogramm 2015",
|
||||
"typ": "grundsatzprogramm-land",
|
||||
"partei": "CDU",
|
||||
"bundesland": "NRW",
|
||||
"beschluss": "2015-06-13",
|
||||
"wahl": None,
|
||||
"wp": None,
|
||||
"gueltig_ab": "2015-06-13",
|
||||
"gueltig_bis": None,
|
||||
"pdf": "cdu-grundsatz-nrw-2015.pdf",
|
||||
"seiten": 0,
|
||||
"hinweis": "pdf-pending",
|
||||
},
|
||||
# ─── CDU Sachsen Landesgrundsatzprogramm 2023 ─────────────────────────
|
||||
{
|
||||
"id": "cdu-grundsatz-sn-2023",
|
||||
"titel": "Zukunftsplan für Sachsen",
|
||||
"name": "CDU Sachsen Grundsatzprogramm 2023",
|
||||
"typ": "grundsatzprogramm-land",
|
||||
"partei": "CDU",
|
||||
"bundesland": "SN",
|
||||
"beschluss": "2023-11-20",
|
||||
"wahl": None,
|
||||
"wp": None,
|
||||
"gueltig_ab": "2023-11-20",
|
||||
"gueltig_bis": None,
|
||||
"pdf": "cdu-grundsatz-sn-2023.pdf",
|
||||
"seiten": 0,
|
||||
"hinweis": "pdf-pending",
|
||||
},
|
||||
# ─── CDU Sachsen-Anhalt Landesgrundsatzprogramm 2023 ──────────────────
|
||||
{
|
||||
"id": "cdu-grundsatz-lsa-2023",
|
||||
"titel": "Sachsen-Anhalt. Unsere Verantwortung. Unsere Zukunft.",
|
||||
"name": "CDU Sachsen-Anhalt Grundsatzprogramm 2023",
|
||||
"typ": "grundsatzprogramm-land",
|
||||
"partei": "CDU",
|
||||
"bundesland": "LSA",
|
||||
"beschluss": "2023-09-30",
|
||||
"wahl": None,
|
||||
"wp": None,
|
||||
"gueltig_ab": "2023-09-30",
|
||||
"gueltig_bis": None,
|
||||
"pdf": "cdu-grundsatz-lsa-2023.pdf",
|
||||
"seiten": 0,
|
||||
"hinweis": "pdf-pending",
|
||||
},
|
||||
# ─── SSW Schleswig-Holstein Rahmenprogramm 2016 ────────────────────────
|
||||
# SSW ist ausschließlich in SH aktiv (Minderheitenpartei der dänisch-
|
||||
# friesischen Volksgruppe). Das Rahmenprogramm tritt funktional an die
|
||||
# Stelle eines Grundsatzprogramms.
|
||||
{
|
||||
"id": "ssw-grundsatz-sh-2016",
|
||||
"titel": "SSW Rahmenprogramm",
|
||||
"name": "SSW Rahmenprogramm 2016",
|
||||
"typ": "grundsatzprogramm-land",
|
||||
"partei": "SSW",
|
||||
"bundesland": "SH",
|
||||
"beschluss": "2016-04-16",
|
||||
"wahl": None,
|
||||
"wp": None,
|
||||
"gueltig_ab": "2016-04-16",
|
||||
"gueltig_bis": None,
|
||||
"pdf": "ssw-grundsatz-sh-2016.pdf",
|
||||
"seiten": 0,
|
||||
"hinweis": "pdf-pending",
|
||||
},
|
||||
# ─── FREIE WÄHLER Bundesvereinigung Grundsatzprogramm ─────────────────
|
||||
# FW sind nicht im Bundestag (BTW 2025 unter 5%), aber im Landtag in
|
||||
# Bayern (Regierung) und Rheinland-Pfalz (Opposition). Bundesgrundsatz-
|
||||
# programm gilt als bundesweite Referenz für alle Landesverbände.
|
||||
{
|
||||
"id": "fw-grundsatz-2012",
|
||||
"titel": "FREIE WÄHLER Bundesgrundsatzprogramm",
|
||||
"name": "FREIE WÄHLER Bundesgrundsatzprogramm",
|
||||
"typ": "grundsatzprogramm-bund",
|
||||
"partei": "FREIE WÄHLER",
|
||||
"bundesland": None,
|
||||
"beschluss": "2012-02-25", # erstes Bundesgrundsatzprogramm; mehrfach fortgeschrieben
|
||||
"wahl": None,
|
||||
"wp": None,
|
||||
"gueltig_ab": "2012-02-25",
|
||||
"gueltig_bis": None,
|
||||
"pdf": "fw-grundsatz.pdf",
|
||||
"seiten": 0,
|
||||
"hinweis": "pdf-pending — FREIE WÄHLER sind nicht im Bundestag, "
|
||||
"Programm gilt als bundesweite Referenz für FW Bayern + FW Rheinland-Pfalz",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _migrate_from_legacy() -> None:
|
||||
"""Migriere bestehende Daten aus ``wahlprogramme.WAHLPROGRAMME`` und
|
||||
``embeddings.PROGRAMME`` in die neue Registry. Wird einmal beim
|
||||
Modul-Import aufgerufen.
|
||||
|
||||
Reihenfolge:
|
||||
1. Wahlprogramme aus WAHLPROGRAMME (autoritative Quelle für regierungs-
|
||||
gebundene Geltungsdaten).
|
||||
2. Grundsatzprogramme aus embeddings.PROGRAMME (typ=parteiprogramm).
|
||||
3. _ADDITIONAL_PROGRAMME (neue Daten — Landesgrundsatz, ggf. Updates).
|
||||
|
||||
Update-Logik: Wenn ein _ADDITIONAL Programm denselben ``partei``,
|
||||
``bundesland`` und Typ wie ein bestehendes hat und neueres
|
||||
``gueltig_ab`` besitzt, wird das Vorgänger-``gueltig_bis`` rückwirkend
|
||||
auf das ``gueltig_ab`` des neuen gesetzt.
|
||||
"""
|
||||
# Zirkuläre Imports vermeiden — lazy import beim Migrationszeitpunkt.
|
||||
from .wahlprogramme import WAHLPROGRAMME
|
||||
from .embeddings import PROGRAMME as _EMB_PROGRAMME
|
||||
from .legislaturen import aktuelle_legislatur
|
||||
|
||||
# Schritt 1: Wahlprogramme aus WAHLPROGRAMME.
|
||||
# Geltungsbeginn = Wahltag (siehe ADR 0013 Folge-Entscheidung B1+B2):
|
||||
# Programme werden zur Wahl beschlossen; Opposition muss nicht auf eine
|
||||
# Regierungsbildung warten, an der sie nicht beteiligt ist. Der
|
||||
# ``regierungsbildung``-Wert aus WAHLPROGRAMME bleibt erhalten und wird
|
||||
# vom Bewertungs-Kontext-Block separat angezeigt ("Regierung zur
|
||||
# Antragszeit"), aber die Programm-Geltung selbst startet am Wahltag.
|
||||
for bundesland, parteien in WAHLPROGRAMME.items():
|
||||
# Wahltag aus legislaturen lookup (aktuelle WP des BL).
|
||||
leg = aktuelle_legislatur(bundesland)
|
||||
wahltag = leg.get("wahltermin") if leg else None
|
||||
|
||||
for partei, info in parteien.items():
|
||||
# ID ableiten aus PDF-Stem
|
||||
pid = info["file"].rsplit(".", 1)[0]
|
||||
|
||||
if info.get("ist_grundsatz"):
|
||||
# Bundes-Grundsatzprogramm; Eintrag erfolgt in Schritt 2.
|
||||
continue
|
||||
|
||||
# Geltungsbeginn: bevorzugt expliziter ``wahltag``-Eintrag
|
||||
# (für historische Programme aus Block 2 nötig), Fallback
|
||||
# auf aktuellen Wahltag der Legislatur, letzter Fallback auf
|
||||
# ``regierungsbildung`` (rückwärts-kompatibel).
|
||||
gueltig_ab = (
|
||||
info.get("wahltag")
|
||||
or wahltag
|
||||
or info.get("regierungsbildung")
|
||||
or "1900-01-01"
|
||||
)
|
||||
|
||||
prog: Programm = {
|
||||
"id": pid,
|
||||
"titel": info.get("titel", ""),
|
||||
"name": f"{info.get('partei', partei)} Wahlprogramm {info.get('jahr', '')}".strip(),
|
||||
"typ": "wahlprogramm",
|
||||
"partei": partei,
|
||||
"bundesland": bundesland,
|
||||
"beschluss": None,
|
||||
"wahl": gueltig_ab,
|
||||
"wp": leg.get("wp") if leg else None,
|
||||
"gueltig_ab": gueltig_ab,
|
||||
"gueltig_bis": info.get("regierungsende"),
|
||||
"pdf": info["file"],
|
||||
"seiten": int(info.get("seiten", 0)),
|
||||
"hinweis": None,
|
||||
}
|
||||
if pid not in PROGRAMME:
|
||||
PROGRAMME[pid] = prog
|
||||
|
||||
# Schritt 2: Grundsatzprogramme aus embeddings.PROGRAMME.
|
||||
# typ=parteiprogramm: ohne ``bundesland`` → Bundes-Grundsatz, mit
|
||||
# ``bundesland`` → Landes-Grundsatzprogramm. Beide funktional gleich,
|
||||
# nur unterscheidet sich der Geltungsbereich.
|
||||
for pid, info in _EMB_PROGRAMME.items():
|
||||
if info.get("typ") != "parteiprogramm":
|
||||
continue # Wahlprogramme schon in Schritt 1 + Schritt 2b
|
||||
if pid in PROGRAMME:
|
||||
continue
|
||||
bl = info.get("bundesland")
|
||||
prog2: Programm = {
|
||||
"id": pid,
|
||||
"titel": info.get("name", ""),
|
||||
"name": info.get("name", ""),
|
||||
"typ": "grundsatzprogramm-land" if bl else "grundsatzprogramm-bund",
|
||||
"partei": info.get("partei", ""),
|
||||
"bundesland": bl,
|
||||
"beschluss": info.get("gueltig_ab"),
|
||||
"wahl": None,
|
||||
"wp": None,
|
||||
"gueltig_ab": info.get("gueltig_ab") or "1900-01-01",
|
||||
"gueltig_bis": info.get("gueltig_bis"),
|
||||
"pdf": info.get("pdf", ""),
|
||||
"seiten": 0,
|
||||
"hinweis": None,
|
||||
}
|
||||
PROGRAMME[pid] = prog2
|
||||
|
||||
# Schritt 2b: Historische Wahlprogramme aus embeddings.PROGRAMME.
|
||||
# Wahlprogramme aus WAHLPROGRAMME (Schritt 1) sind die jeweils
|
||||
# aktuelle Version pro (BL, Partei). Vorperioden-Programme sind nur
|
||||
# in embeddings.PROGRAMME geführt (mit explizitem ``gueltig_ab`` und
|
||||
# ``gueltig_bis`` als Eintrag-Felder) und werden hier nachregistriert.
|
||||
# Voraussetzung: der embeddings-Eintrag hat ``gueltig_ab`` und/oder
|
||||
# ``gueltig_bis`` gesetzt. Ohne das zählt er als "aktuelles Programm",
|
||||
# wurde aber bereits via WAHLPROGRAMME migriert.
|
||||
for pid, info in _EMB_PROGRAMME.items():
|
||||
if info.get("typ") != "wahlprogramm":
|
||||
continue
|
||||
if pid in PROGRAMME:
|
||||
continue
|
||||
# Nur Einträge mit explizit gesetzter Geltung als historisch werten.
|
||||
gueltig_ab = info.get("gueltig_ab")
|
||||
gueltig_bis = info.get("gueltig_bis")
|
||||
if gueltig_ab is None and gueltig_bis is None:
|
||||
continue
|
||||
bl = info.get("bundesland")
|
||||
prog2b: Programm = {
|
||||
"id": pid,
|
||||
"titel": info.get("name", ""),
|
||||
"name": info.get("name", ""),
|
||||
"typ": "wahlprogramm",
|
||||
"partei": info.get("partei", ""),
|
||||
"bundesland": bl,
|
||||
"beschluss": None,
|
||||
"wahl": gueltig_ab,
|
||||
"wp": info.get("wp"),
|
||||
"gueltig_ab": gueltig_ab or "1900-01-01",
|
||||
"gueltig_bis": gueltig_bis,
|
||||
"pdf": info.get("pdf", ""),
|
||||
"seiten": 0,
|
||||
"hinweis": None,
|
||||
}
|
||||
PROGRAMME[pid] = prog2b
|
||||
|
||||
# Schritt 3: _ADDITIONAL_PROGRAMME — mit Vorgänger-bis-Update
|
||||
for prog3 in _ADDITIONAL_PROGRAMME:
|
||||
# Setze gueltig_bis von Vorgänger-Programmen rückwirkend.
|
||||
for existing in list(PROGRAMME.values()):
|
||||
if (
|
||||
existing.get("typ") in ("grundsatzprogramm-bund",
|
||||
"grundsatzprogramm-land")
|
||||
and existing.get("partei") == prog3.get("partei")
|
||||
and existing.get("bundesland") == prog3.get("bundesland")
|
||||
and existing.get("gueltig_bis") is None
|
||||
and existing.get("gueltig_ab", "9999-99-99") < prog3["gueltig_ab"]
|
||||
):
|
||||
# Wir mutieren ein TypedDict in der Registry — das ist OK,
|
||||
# weil _migrate_from_legacy() einmal beim Import läuft.
|
||||
existing["gueltig_bis"] = prog3["gueltig_ab"] # type: ignore[typeddict-item]
|
||||
if prog3["id"] not in PROGRAMME:
|
||||
PROGRAMME[prog3["id"]] = prog3
|
||||
|
||||
|
||||
# Lazy initialisierung — erst beim ersten echten Zugriff. Dadurch vermeidet
|
||||
# das Modul Import-Reihenfolge-Probleme mit ``embeddings.py`` (das viel mehr
|
||||
# Initialisierung braucht: openai, fitz, etc.).
|
||||
_INITIALIZED = False
|
||||
|
||||
|
||||
def _ensure_initialized() -> None:
|
||||
global _INITIALIZED
|
||||
if _INITIALIZED:
|
||||
return
|
||||
_migrate_from_legacy()
|
||||
_INITIALIZED = True
|
||||
|
||||
|
||||
# Patch helper-API to ensure init runs on first call.
|
||||
_get_programm = get_programm
|
||||
_aktuelles_wahlprogramm = aktuelles_wahlprogramm
|
||||
_wahlprogramm_zum_zeitpunkt = wahlprogramm_zum_zeitpunkt
|
||||
_grundsatzprogramm_zum_zeitpunkt = grundsatzprogramm_zum_zeitpunkt
|
||||
_parteien_mit_wahlprogramm = parteien_mit_wahlprogramm
|
||||
_alle_versionen = alle_versionen
|
||||
|
||||
|
||||
def get_programm(programm_id: str) -> Optional[Programm]: # type: ignore[no-redef]
|
||||
_ensure_initialized()
|
||||
return _get_programm(programm_id)
|
||||
|
||||
|
||||
def aktuelles_wahlprogramm(bundesland: str, partei: str) -> Optional[Programm]: # type: ignore[no-redef]
|
||||
_ensure_initialized()
|
||||
return _aktuelles_wahlprogramm(bundesland, partei)
|
||||
|
||||
|
||||
def wahlprogramm_zum_zeitpunkt( # type: ignore[no-redef]
|
||||
bundesland: str, partei: str, datum: str,
|
||||
) -> Optional[Programm]:
|
||||
_ensure_initialized()
|
||||
return _wahlprogramm_zum_zeitpunkt(bundesland, partei, datum)
|
||||
|
||||
|
||||
def grundsatzprogramm_zum_zeitpunkt( # type: ignore[no-redef]
|
||||
partei: str, datum: str, bundesland: Optional[str] = None,
|
||||
) -> Optional[Programm]:
|
||||
_ensure_initialized()
|
||||
return _grundsatzprogramm_zum_zeitpunkt(partei, datum, bundesland)
|
||||
|
||||
|
||||
def parteien_mit_wahlprogramm(bundesland: str) -> list[str]: # type: ignore[no-redef]
|
||||
_ensure_initialized()
|
||||
return _parteien_mit_wahlprogramm(bundesland)
|
||||
|
||||
|
||||
def alle_versionen(bundesland: str, partei: str) -> list[Programm]: # type: ignore[no-redef]
|
||||
_ensure_initialized()
|
||||
return _alle_versionen(bundesland, partei)
|
||||
|
||||
|
||||
def all_programme() -> list[Programm]:
|
||||
"""Alle eingetragenen Programme (initialisiert, falls nötig)."""
|
||||
_ensure_initialized()
|
||||
"""Alle eingetragenen Programme."""
|
||||
return list(PROGRAMME.values())
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ def build_pdf_href(zitat: dict, bundesland: str = "") -> str:
|
||||
|
||||
Bevorzugt das bereits gepflegte url-Feld. Falls leer, rekonstruiert
|
||||
die URL aus dem quelle-Feld (Format: 'Titel · S. N' oder 'Titel, S. N')
|
||||
über die WAHLPROGRAMME-Registry.
|
||||
über die ``programme``-Registry.
|
||||
"""
|
||||
url = zitat.get("url", "")
|
||||
if url:
|
||||
@ -66,18 +66,14 @@ def build_pdf_href(zitat: dict, bundesland: str = "") -> str:
|
||||
return ""
|
||||
seite = seite_m.group(1)
|
||||
|
||||
# pid aus WAHLPROGRAMME-Registry ermitteln: Dateiname ohne .pdf
|
||||
from .wahlprogramme import WAHLPROGRAMME
|
||||
# pid aus programme-Registry ermitteln: über titel oder name match.
|
||||
from .programme import all_programme
|
||||
pid = ""
|
||||
for bl_data in WAHLPROGRAMME.values():
|
||||
for partei_data in bl_data.values():
|
||||
titel = partei_data.get("titel", "")
|
||||
partei_name = partei_data.get("partei", "")
|
||||
file_name = partei_data.get("file", "")
|
||||
if titel and (titel in quelle or partei_name in quelle):
|
||||
pid = file_name.replace(".pdf", "")
|
||||
break
|
||||
if pid:
|
||||
for prog in all_programme():
|
||||
titel = prog.get("titel") or ""
|
||||
name = prog.get("name") or ""
|
||||
if (titel and titel in quelle) or (name and name in quelle):
|
||||
pid = prog.get("id", "")
|
||||
break
|
||||
|
||||
if not pid:
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
"""Erkennung fehlender Wahlprogramme (#128).
|
||||
|
||||
Prüft für ein gegebenes Bundesland, welche der im Landtag vertretenen
|
||||
Fraktionen in der WAHLPROGRAMME-Registry nicht hinterlegt sind.
|
||||
Wird nach dem LLM-Call in analyze_antrag() aufgerufen, damit das
|
||||
Fraktionen in der ``programme``-Registry nicht hinterlegt sind. Wird
|
||||
nach dem LLM-Call in ``analyze_antrag()`` aufgerufen, damit das
|
||||
Assessment-Ergebnis die Lücken explizit ausweist.
|
||||
"""
|
||||
|
||||
from .bundeslaender import BUNDESLAENDER
|
||||
from .wahlprogramme import WAHLPROGRAMME
|
||||
from .programme import parteien_mit_wahlprogramm
|
||||
|
||||
|
||||
def check_missing_programmes(bundesland: str, fraktionen: list[str]) -> list[str]:
|
||||
@ -33,5 +33,5 @@ def check_missing_programmes(bundesland: str, fraktionen: list[str]) -> list[str
|
||||
if not fraktionen:
|
||||
return []
|
||||
|
||||
indexed = WAHLPROGRAMME.get(bundesland, {})
|
||||
indexed = set(parteien_mit_wahlprogramm(bundesland))
|
||||
return [f for f in fraktionen if f not in indexed]
|
||||
|
||||
@ -254,7 +254,7 @@ def get_missing_programmes(bundesland: Optional[str] = None) -> list[dict]:
|
||||
Returns:
|
||||
Liste von Dicts mit ``bl``, ``partei``, ``dateiname``, ``kandidaten``.
|
||||
"""
|
||||
from .wahlprogramme import WAHLPROGRAMME
|
||||
from .programme import aktuelles_wahlprogramm
|
||||
|
||||
missing: list[dict] = []
|
||||
data = _load_links()
|
||||
@ -266,14 +266,15 @@ def get_missing_programmes(bundesland: Optional[str] = None) -> list[dict]:
|
||||
if isinstance(kandidaten, dict):
|
||||
kandidaten = [kandidaten]
|
||||
|
||||
wp_info = WAHLPROGRAMME.get(bl, {}).get(partei)
|
||||
wp_info = aktuelles_wahlprogramm(bl, partei)
|
||||
if wp_info:
|
||||
dateiname = wp_info["file"]
|
||||
dateiname = wp_info.get("pdf")
|
||||
if dateiname:
|
||||
dest = _REFERENZEN_DIR / dateiname
|
||||
if dest.exists():
|
||||
continue # Datei liegt bereits vor
|
||||
else:
|
||||
dateiname = None # noch nicht in WAHLPROGRAMME registriert
|
||||
dateiname = None # noch nicht in programme.PROGRAMME registriert
|
||||
|
||||
missing.append({
|
||||
"bl": bl,
|
||||
@ -311,13 +312,18 @@ def _cli() -> None:
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.pin_existing:
|
||||
from .wahlprogramme import WAHLPROGRAMME
|
||||
from .programme import all_programme
|
||||
|
||||
lock = _load_lock()
|
||||
added = 0
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
dateiname = info.get("file") if isinstance(info, dict) else None
|
||||
for prog in all_programme():
|
||||
if prog.get("typ") != "wahlprogramm":
|
||||
continue
|
||||
if prog.get("gueltig_bis") is not None:
|
||||
continue # nur aktuelle Wahlprogramme pinnen
|
||||
bl = prog.get("bundesland")
|
||||
partei = prog.get("partei")
|
||||
dateiname = prog.get("pdf")
|
||||
if not dateiname:
|
||||
continue
|
||||
pdf_path = _REFERENZEN_DIR / dateiname
|
||||
@ -362,16 +368,16 @@ def _cli() -> None:
|
||||
print(f"Keine URL-Kandidaten für {bl}/{partei} in wahlprogramm-links.yaml.")
|
||||
sys.exit(1)
|
||||
|
||||
from .wahlprogramme import WAHLPROGRAMME
|
||||
wp_info = WAHLPROGRAMME.get(bl, {}).get(partei)
|
||||
from .programme import aktuelles_wahlprogramm
|
||||
wp_info = aktuelles_wahlprogramm(bl, partei)
|
||||
if not wp_info:
|
||||
print(
|
||||
f"WARNUNG: {bl}/{partei} ist noch nicht in wahlprogramme.py eingetragen.\n"
|
||||
f"WARNUNG: {bl}/{partei} ist noch nicht in programme.py eingetragen.\n"
|
||||
"Die Datei wird heruntergeladen, muss aber manuell registriert werden."
|
||||
)
|
||||
dateiname = f"{partei.lower()}-{bl.lower()}-neu.pdf"
|
||||
else:
|
||||
dateiname = wp_info["file"]
|
||||
dateiname = wp_info.get("pdf", f"{partei.lower()}-{bl.lower()}-neu.pdf")
|
||||
|
||||
dest = _REFERENZEN_DIR / dateiname
|
||||
|
||||
|
||||
@ -1,12 +1,20 @@
|
||||
"""Wahlprogramm-Referenzsystem mit Zitaten und Seitenreferenzen.
|
||||
"""Wahlprogramm-Suche (Keyword-Fallback, wenn Embeddings nicht verfügbar).
|
||||
|
||||
Bundesland-bewusst seit Issue #5: ``WAHLPROGRAMME[bundesland][partei]`` statt
|
||||
flach. Konsumiert ``BUNDESLAENDER`` aus ``bundeslaender.py`` für die
|
||||
Regierungsfraktionen-Lookup und für Plausibilitätsprüfungen.
|
||||
Bis #222: hier lebte zusätzlich die ``WAHLPROGRAMME``-Datentabelle als
|
||||
zweite Source-of-Truth. Sie wurde nach ``programme.PROGRAMME`` migriert
|
||||
(siehe ADR 0013). Dieses Modul wurde dadurch reduziert auf:
|
||||
|
||||
Verantwortlich für die schlüsselwortbasierte Fallback-Suche in den
|
||||
paged-Textversionen der Wahlprogramme. Die semantische Suche lebt in
|
||||
``embeddings.py``.
|
||||
- ``get_wahlprogramm(bl, partei)`` — Compat-Adapter auf
|
||||
``programme.aktuelles_wahlprogramm`` (für Aufrufer, die das Programm
|
||||
via (Bundesland, Partei) suchen — Hauptverwender ist die Keyword-Suche
|
||||
hier in diesem Modul plus admin-Tools).
|
||||
- ``load_wahlprogramm_text`` / ``search_wahlprogramm`` /
|
||||
``find_relevant_quotes`` / ``format_quote_for_prompt`` — Keyword-
|
||||
Fallback für die ``analyzer.py``-Pipeline, wenn die Embeddings-DB
|
||||
nicht initialisiert ist.
|
||||
|
||||
Die semantische Suche lebt in ``embeddings.py``, die Programm-Stamm-
|
||||
daten in ``programme.py``.
|
||||
"""
|
||||
|
||||
import re
|
||||
@ -14,168 +22,12 @@ from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from .bundeslaender import BUNDESLAENDER
|
||||
from .programme import (
|
||||
aktuelles_wahlprogramm,
|
||||
parteien_mit_wahlprogramm,
|
||||
)
|
||||
|
||||
|
||||
# WAHLPROGRAMME[bundesland][partei] -> Metadaten
|
||||
#
|
||||
# Pflichtfelder: file, titel, partei, jahr, seiten
|
||||
# Geltungsdaten: regierungsbildung (ISO YYYY-MM-DD) — Tag der Vereidigung der
|
||||
# Regierung, die nach der Wahl aus diesem Programm hervorging.
|
||||
# NICHT das Wahldatum: das Programm wird mit der Regierungs-
|
||||
# bildung wirksam (Koalitionsvertrag, Kabinett vereidigt).
|
||||
# regierungsende (ISO YYYY-MM-DD oder None) — Ende der
|
||||
# Regierungs-Geltung. ``None`` = aktuell laufende Regierung.
|
||||
#
|
||||
# Beim Hinzufügen eines neuen Bundeslands: Eintrag hier UND parallel
|
||||
# in WAHLPROGRAMM_KONTEXT_FILES.
|
||||
WAHLPROGRAMME: dict[str, dict[str, dict]] = {
|
||||
# NRW — LTW 15.05.2022. Kabinett Wüst II (CDU+GRÜNE) vereidigt 29.06.2022.
|
||||
"NRW": {
|
||||
"CDU": {"file": "cdu-nrw-2022.pdf", "titel": "Machen, worauf es ankommt", "partei": "CDU NRW", "jahr": 2022, "seiten": 109, "regierungsbildung": "2022-06-29", "regierungsende": None},
|
||||
"SPD": {"file": "spd-nrw-2022.pdf", "titel": "Unser Land von morgen", "partei": "SPD NRW", "jahr": 2022, "seiten": 116, "regierungsbildung": "2022-06-29", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-nrw-2022.pdf","titel": "Von hier an Zukunft", "partei": "BÜNDNIS 90/DIE GRÜNEN NRW", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-06-29", "regierungsende": None},
|
||||
"FDP": {"file": "fdp-nrw-2022.pdf", "titel": "Nie gab es mehr zu tun", "partei": "FDP NRW", "jahr": 2022, "seiten": 96, "regierungsbildung": "2022-06-29", "regierungsende": None},
|
||||
"AfD": {"file": "afd-nrw-2022.pdf", "titel": "Wer sonst.", "partei": "AfD NRW", "jahr": 2022, "seiten": 68, "regierungsbildung": "2022-06-29", "regierungsende": None},
|
||||
},
|
||||
# Sachsen-Anhalt — LTW 06.06.2021. Kabinett Haseloff III (CDU+SPD+FDP) vereidigt 16.09.2021.
|
||||
"LSA": {
|
||||
"CDU": {"file": "cdu-lsa-2021.pdf", "titel": "Unsere Heimat. Unsere Verantwortung.", "partei": "CDU Sachsen-Anhalt", "jahr": 2021, "seiten": 82, "regierungsbildung": "2021-09-16", "regierungsende": None},
|
||||
"SPD": {"file": "spd-lsa-2021.pdf", "titel": "Zusammenhalt und neue Chancen. Politik fürs ganze Land", "partei": "SPD Sachsen-Anhalt", "jahr": 2021, "seiten": 77, "regierungsbildung": "2021-09-16", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-lsa-2021.pdf","titel": "Verlässlich für Sachsen-Anhalt", "partei": "BÜNDNIS 90/DIE GRÜNEN Sachsen-Anhalt","jahr": 2021, "seiten": 164,"regierungsbildung": "2021-09-16", "regierungsende": None},
|
||||
"FDP": {"file": "fdp-lsa-2021.pdf", "titel": "Wahlprogramm der FDP Sachsen-Anhalt zur Landtagswahl 2021", "partei": "FDP Sachsen-Anhalt", "jahr": 2021, "seiten": 76, "regierungsbildung": "2021-09-16", "regierungsende": None},
|
||||
"AfD": {"file": "afd-lsa-2021.pdf", "titel": "Alles für unsere Heimat! Programm der AfD Sachsen-Anhalt zur Landtagswahl 2021","partei": "AfD Sachsen-Anhalt", "jahr": 2021, "seiten": 64, "regierungsbildung": "2021-09-16", "regierungsende": None},
|
||||
"LINKE": {"file": "linke-lsa-2021.pdf", "titel": "Wahlprogramm zur Landtagswahl 2021", "partei": "DIE LINKE Sachsen-Anhalt", "jahr": 2021, "seiten": 88, "regierungsbildung": "2021-09-16", "regierungsende": None},
|
||||
},
|
||||
# Mecklenburg-Vorpommern — LTW 26.09.2021. Kabinett Schwesig II (SPD+LINKE) vereidigt 15.11.2021.
|
||||
"MV": {
|
||||
"CDU": {"file": "cdu-mv-2021.pdf", "titel": "Zusammen. Den Blick nach vorn. Gemeinsam die Zukunft meistern", "partei": "CDU Mecklenburg-Vorpommern", "jahr": 2021, "seiten": 56, "regierungsbildung": "2021-11-15", "regierungsende": None},
|
||||
"SPD": {"file": "spd-mv-2021.pdf", "titel": "Verantwortung für heute und morgen — Regierungsprogramm 2021–2026", "partei": "SPD Mecklenburg-Vorpommern", "jahr": 2021, "seiten": 95, "regierungsbildung": "2021-11-15", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-mv-2021.pdf","titel": "Für Klima, Land und ein besseres Miteinander", "partei": "BÜNDNIS 90/DIE GRÜNEN Mecklenburg-Vorpommern","jahr": 2021, "seiten": 88, "regierungsbildung": "2021-11-15", "regierungsende": None},
|
||||
"FDP": {"file": "fdp-mv-2021.pdf", "titel": "Wahlprogramm der Freien Demokraten Mecklenburg-Vorpommern zur Landtagswahl 2021","partei": "FDP Mecklenburg-Vorpommern", "jahr": 2021, "seiten": 120,"regierungsbildung": "2021-11-15", "regierungsende": None},
|
||||
"AfD": {"file": "afd-mv-2021.pdf", "titel": "Landeswahlprogramm der AfD Mecklenburg-Vorpommern 2021", "partei": "AfD Mecklenburg-Vorpommern", "jahr": 2021, "seiten": 84, "regierungsbildung": "2021-11-15", "regierungsende": None},
|
||||
"LINKE": {"file": "linke-mv-2021.pdf", "titel": "Das ist links! — Zukunftsprogramm für Mecklenburg-Vorpommern", "partei": "DIE LINKE Mecklenburg-Vorpommern", "jahr": 2021, "seiten": 82, "regierungsbildung": "2021-11-15", "regierungsende": None},
|
||||
},
|
||||
# Berlin — AGH-Wahl 26.09.2021, Wiederholungswahl 12.02.2023. Senat Wegner I (CDU+SPD) vereidigt 27.04.2023.
|
||||
"BE": {
|
||||
"CDU": {"file": "cdu-be-2023.pdf", "titel": "Unser Berlin. Mehr geht nur gemeinsam. — Berlin-Plan der CDU Berlin 2021–2026", "partei": "CDU Berlin", "jahr": 2021, "seiten": 135, "regierungsbildung": "2023-04-27", "regierungsende": None},
|
||||
"SPD": {"file": "spd-be-2023.pdf", "titel": "Ganz sicher Berlin — Wahlprogramm der SPD Berlin zur Abgeordnetenhauswahl 2021", "partei": "SPD Berlin", "jahr": 2021, "seiten": 86, "regierungsbildung": "2023-04-27", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-be-2023.pdf","titel": "Unser Plan für Berlin — Landeswahlprogramm BÜNDNIS 90/DIE GRÜNEN Berlin 2021", "partei": "BÜNDNIS 90/DIE GRÜNEN Berlin", "jahr": 2021, "seiten": 280, "regierungsbildung": "2023-04-27", "regierungsende": None},
|
||||
"LINKE": {"file": "linke-be-2023.pdf", "titel": "rot. radikal. realistisch. — Unser Programm für die soziale Stadt", "partei": "DIE LINKE Berlin", "jahr": 2021, "seiten": 130, "regierungsbildung": "2023-04-27", "regierungsende": None},
|
||||
"AfD": {"file": "afd-be-2023.pdf", "titel": "Wahlprogramm der AfD Berlin für die Wahl des Abgeordnetenhauses am 26. September 2021","partei": "AfD Berlin", "jahr": 2021, "seiten": 166, "regierungsbildung": "2023-04-27", "regierungsende": None},
|
||||
},
|
||||
# Thüringen — LTW 01.09.2024. Kabinett Voigt I (CDU+BSW+SPD, Brombeer) vereidigt 12.12.2024.
|
||||
"TH": {
|
||||
"CDU": {"file": "cdu-th-2024.pdf", "titel": "Wahlprogramm der CDU Thüringen 2024", "partei": "CDU Thüringen", "jahr": 2024, "seiten": 83, "regierungsbildung": "2024-12-12", "regierungsende": None},
|
||||
"AfD": {"file": "afd-th-2024.pdf", "titel": "AfD Thüringen Landtagswahlprogramm 2024","partei": "AfD Thüringen", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-12", "regierungsende": None},
|
||||
"LINKE": {"file": "linke-th-2024.pdf", "titel": "DIE LINKE Thüringen Wahlprogramm 2024", "partei": "DIE LINKE Thüringen", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-12", "regierungsende": None},
|
||||
"BSW": {"file": "bsw-th-2024.pdf", "titel": "BSW Thüringen Wahlprogramm 2024", "partei": "BSW Thüringen", "jahr": 2024, "seiten": 50, "regierungsbildung": "2024-12-12", "regierungsende": None},
|
||||
"SPD": {"file": "spd-th-2024.pdf", "titel": "SPD Thüringen Wahlprogramm 2024", "partei": "SPD Thüringen", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-12", "regierungsende": None},
|
||||
},
|
||||
# Brandenburg — LTW 22.09.2024. Kabinett Woidke III (SPD+BSW) vereidigt 11.12.2024.
|
||||
"BB": {
|
||||
"SPD": {"file": "spd-bb-2024.pdf", "titel": "SPD Brandenburg Wahlprogramm 2024", "partei": "SPD Brandenburg", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-11", "regierungsende": None},
|
||||
"AfD": {"file": "afd-bb-2024.pdf", "titel": "AfD Brandenburg Wahlprogramm 2024", "partei": "AfD Brandenburg", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-11", "regierungsende": None},
|
||||
"CDU": {"file": "cdu-bb-2024.pdf", "titel": "CDU Brandenburg Wahlprogramm 2024", "partei": "CDU Brandenburg", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-11", "regierungsende": None},
|
||||
"BSW": {"file": "bsw-bb-2024.pdf", "titel": "BSW Brandenburg Wahlprogramm 2024", "partei": "BSW Brandenburg", "jahr": 2024, "seiten": 50, "regierungsbildung": "2024-12-11", "regierungsende": None},
|
||||
},
|
||||
# Hamburg — Bürgerschaftswahl 02.03.2025. Senat Tschentscher III (SPD+GRÜNE) vereidigt 07.05.2025.
|
||||
"HH": {
|
||||
"SPD": {"file": "spd-hh-2025.pdf", "titel": "SPD Hamburg Wahlprogramm Bürgerschaftswahl 2025", "partei": "SPD Hamburg", "jahr": 2025, "seiten": 100, "regierungsbildung": "2025-05-07", "regierungsende": None},
|
||||
"CDU": {"file": "cdu-hh-2025.pdf", "titel": "CDU Hamburg Wahlprogramm Bürgerschaftswahl 2025", "partei": "CDU Hamburg", "jahr": 2025, "seiten": 100, "regierungsbildung": "2025-05-07", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-hh-2025.pdf","titel": "Gute Gründe für Grün — Regierungsprogramm BÜNDNIS 90/DIE GRÜNEN Hamburg 2025", "partei": "BÜNDNIS 90/DIE GRÜNEN Hamburg", "jahr": 2025, "seiten": 100, "regierungsbildung": "2025-05-07", "regierungsende": None},
|
||||
"LINKE": {"file": "linke-hh-2025.pdf", "titel": "DIE LINKE Hamburg Wahlprogramm Bürgerschaftswahl 2025", "partei": "DIE LINKE Hamburg", "jahr": 2025, "seiten": 100, "regierungsbildung": "2025-05-07", "regierungsende": None},
|
||||
"AfD": {"file": "afd-hh-2025.pdf", "titel": "AfD Hamburg Wahlprogramm Bürgerschaftswahl 2025", "partei": "AfD Hamburg", "jahr": 2025, "seiten": 100, "regierungsbildung": "2025-05-07", "regierungsende": None},
|
||||
},
|
||||
# Schleswig-Holstein — LTW 08.05.2022. Kabinett Günther II (CDU+GRÜNE) vereidigt 29.06.2022.
|
||||
"SH": {
|
||||
"CDU": {"file": "cdu-sh-2022.pdf", "titel": "CDU Schleswig-Holstein Wahlprogramm 2022", "partei": "CDU Schleswig-Holstein", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-06-29", "regierungsende": None},
|
||||
"SPD": {"file": "spd-sh-2022.pdf", "titel": "SPD Schleswig-Holstein Wahlprogramm 2022", "partei": "SPD Schleswig-Holstein", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-06-29", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-sh-2022.pdf","titel": "BÜNDNIS 90/DIE GRÜNEN Schleswig-Holstein Wahlprogramm 2022", "partei": "BÜNDNIS 90/DIE GRÜNEN Schleswig-Holstein", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-06-29", "regierungsende": None},
|
||||
"FDP": {"file": "fdp-sh-2022.pdf", "titel": "FDP Schleswig-Holstein Wahlprogramm 2022", "partei": "FDP Schleswig-Holstein", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-06-29", "regierungsende": None},
|
||||
"SSW": {"file": "ssw-sh-2022.pdf", "titel": "SSW Schleswig-Holstein Wahlprogramm 2022", "partei": "SSW", "jahr": 2022, "seiten": 80, "regierungsbildung": "2022-06-29", "regierungsende": None},
|
||||
},
|
||||
# Baden-Württemberg — LTW 14.03.2021. Kabinett Kretschmann III (GRÜNE+CDU) vereidigt 12.05.2021.
|
||||
"BW": {
|
||||
"GRÜNE": {"file": "gruene-bw-2021.pdf","titel": "Wachsen wir über uns hinaus — Landtagswahlprogramm BÜNDNIS 90/DIE GRÜNEN Baden-Württemberg 2021", "partei": "BÜNDNIS 90/DIE GRÜNEN Baden-Württemberg", "jahr": 2021, "seiten": 100, "regierungsbildung": "2021-05-12", "regierungsende": None},
|
||||
"CDU": {"file": "cdu-bw-2021.pdf", "titel": "Neue Ideen für eine neue Zeit — Regierungsprogramm der CDU Baden-Württemberg zur Landtagswahl 2021", "partei": "CDU Baden-Württemberg", "jahr": 2021, "seiten": 100, "regierungsbildung": "2021-05-12", "regierungsende": None},
|
||||
"AfD": {"file": "afd-bw-2021.pdf", "titel": "AfD Baden-Württemberg Landtagswahlprogramm 2021", "partei": "AfD Baden-Württemberg", "jahr": 2021, "seiten": 100, "regierungsbildung": "2021-05-12", "regierungsende": None},
|
||||
"SPD": {"file": "spd-bw-2021.pdf", "titel": "SPD Baden-Württemberg Wahlprogramm zur Landtagswahl 2021", "partei": "SPD Baden-Württemberg", "jahr": 2021, "seiten": 100, "regierungsbildung": "2021-05-12", "regierungsende": None},
|
||||
"FDP": {"file": "fdp-bw-2021.pdf", "titel": "FDP Baden-Württemberg Landtagswahlprogramm 2021", "partei": "FDP Baden-Württemberg", "jahr": 2021, "seiten": 100, "regierungsbildung": "2021-05-12", "regierungsende": None},
|
||||
},
|
||||
# Rheinland-Pfalz — LTW 14.03.2021. Kabinett Dreyer III (SPD+GRÜNE+FDP) vereidigt 18.05.2021;
|
||||
# seit 10.07.2024 fortgeführt als Schweitzer I in derselben WP18.
|
||||
"RP": {
|
||||
"SPD": {"file": "spd-rp-2021.pdf","titel": "Wir mit Ihr — Regierungsprogramm der SPD Rheinland-Pfalz 2021–2026", "partei": "SPD Rheinland-Pfalz", "jahr": 2021, "seiten": 100, "regierungsbildung": "2021-05-18", "regierungsende": None},
|
||||
"CDU": {"file": "cdu-rp-2021.pdf","titel": "Regierungsprogramm der CDU RLP 2021–26", "partei": "CDU Rheinland-Pfalz", "jahr": 2021, "seiten": 100, "regierungsbildung": "2021-05-18", "regierungsende": None},
|
||||
"AfD": {"file": "afd-rp-2021.pdf","titel": "AfD Rheinland-Pfalz Wahlprogramm 2021", "partei": "AfD Rheinland-Pfalz", "jahr": 2021, "seiten": 100, "regierungsbildung": "2021-05-18", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-rp-2021.pdf","titel": "BÜNDNIS 90/DIE GRÜNEN Rheinland-Pfalz Landtagswahlprogramm 2021", "partei": "BÜNDNIS 90/DIE GRÜNEN Rheinland-Pfalz", "jahr": 2021, "seiten": 100, "regierungsbildung": "2021-05-18", "regierungsende": None},
|
||||
"FREIE WÄHLER": {"file": "fw-rp-2021.pdf", "titel": "FREIE WÄHLER Rheinland-Pfalz Wahlprogramm 2021", "partei": "FREIE WÄHLER Rheinland-Pfalz", "jahr": 2021, "seiten": 80, "regierungsbildung": "2021-05-18", "regierungsende": None},
|
||||
"FDP": {"file": "fdp-rp-2021.pdf","titel": "FDP Rheinland-Pfalz Landtagswahlprogramm 2021", "partei": "FDP Rheinland-Pfalz", "jahr": 2021, "seiten": 100, "regierungsbildung": "2021-05-18", "regierungsende": None},
|
||||
},
|
||||
# Bayern — LTW 08.10.2023. Kabinett Söder III (CSU+FREIE WÄHLER) vereidigt 07.11.2023.
|
||||
"BY": {
|
||||
"CSU": {"file": "csu-by-2023.pdf", "titel": "CSU Bayern Bayernplan 2023", "partei": "CSU Bayern", "jahr": 2023, "seiten": 100, "regierungsbildung": "2023-11-07", "regierungsende": None},
|
||||
"FREIE WÄHLER": {"file": "fw-by-2023.pdf", "titel": "FREIE WÄHLER Bayern Wahlprogramm 2023", "partei": "FREIE WÄHLER Bayern", "jahr": 2023, "seiten": 80, "regierungsbildung": "2023-11-07", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-by-2023.pdf","titel": "BÜNDNIS 90/DIE GRÜNEN Bayern Regierungsprogramm 2023", "partei": "BÜNDNIS 90/DIE GRÜNEN Bayern", "jahr": 2023, "seiten": 100, "regierungsbildung": "2023-11-07", "regierungsende": None},
|
||||
"SPD": {"file": "spd-by-2023.pdf", "titel": "SPD Bayern Zukunftsprogramm 2023", "partei": "SPD Bayern", "jahr": 2023, "seiten": 100, "regierungsbildung": "2023-11-07", "regierungsende": None},
|
||||
"AfD": {"file": "afd-by-2023.pdf", "titel": "AfD Bayern Wahlprogramm 2023", "partei": "AfD Bayern", "jahr": 2023, "seiten": 100, "regierungsbildung": "2023-11-07", "regierungsende": None},
|
||||
},
|
||||
# Bremen — Bürgerschaftswahl 14.05.2023. Senat Bovenschulte II (SPD+GRÜNE+LINKE) vereidigt 05.07.2023.
|
||||
# AfD war wegen Listenstreit nicht zur Wahl zugelassen — stattdessen ist
|
||||
# BIW (Bürger in Wut) als 6. Fraktion in der 21. Bürgerschaft.
|
||||
"HB": {
|
||||
"SPD": {"file": "spd-hb-2023.pdf", "titel": "SPD Bremen Wahlprogramm Bürgerschaftswahl 2023", "partei": "SPD Bremen", "jahr": 2023, "seiten": 100, "regierungsbildung": "2023-07-05", "regierungsende": None},
|
||||
"CDU": {"file": "cdu-hb-2023.pdf", "titel": "CDU Bremen Wahlprogramm Bürgerschaftswahl 2023", "partei": "CDU Bremen", "jahr": 2023, "seiten": 100, "regierungsbildung": "2023-07-05", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-hb-2023.pdf","titel": "BÜNDNIS 90/DIE GRÜNEN Bremen Wahlprogramm 2023", "partei": "BÜNDNIS 90/DIE GRÜNEN Bremen", "jahr": 2023, "seiten": 100, "regierungsbildung": "2023-07-05", "regierungsende": None},
|
||||
"LINKE": {"file": "linke-hb-2023.pdf", "titel": "DIE LINKE Bremen Wahlprogramm Bürgerschaftswahl 2023", "partei": "DIE LINKE Bremen", "jahr": 2023, "seiten": 100, "regierungsbildung": "2023-07-05", "regierungsende": None},
|
||||
"BiW": {"file": "biw-hb-2023.pdf", "titel": "BÜRGER IN WUT — Programm für die Bürgerschaftswahl 2023", "partei": "BiW Bremen", "jahr": 2023, "seiten": 26, "regierungsbildung": "2023-07-05", "regierungsende": None},
|
||||
},
|
||||
# Hessen — LTW 08.10.2023. Kabinett Rhein II (CDU+SPD) vereidigt 18.01.2024.
|
||||
"HE": {
|
||||
"CDU": {"file": "cdu-he-2023.pdf", "titel": "CDU Hessen Regierungsprogramm 2023", "partei": "CDU Hessen", "jahr": 2023, "seiten": 100, "regierungsbildung": "2024-01-18", "regierungsende": None},
|
||||
"SPD": {"file": "spd-he-2023.pdf", "titel": "SPD Hessen Wahlprogramm 2023", "partei": "SPD Hessen", "jahr": 2023, "seiten": 100, "regierungsbildung": "2024-01-18", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-he-2023.pdf","titel": "BÜNDNIS 90/DIE GRÜNEN Hessen Wahlprogramm 2023", "partei": "BÜNDNIS 90/DIE GRÜNEN Hessen", "jahr": 2023, "seiten": 100, "regierungsbildung": "2024-01-18", "regierungsende": None},
|
||||
"FDP": {"file": "fdp-he-2023.pdf", "titel": "FDP Hessen Wahlprogramm 2023", "partei": "FDP Hessen", "jahr": 2023, "seiten": 100, "regierungsbildung": "2024-01-18", "regierungsende": None},
|
||||
"AfD": {"file": "afd-he-2023.pdf", "titel": "AfD Hessen Wahlprogramm 2023", "partei": "AfD Hessen", "jahr": 2023, "seiten": 100, "regierungsbildung": "2024-01-18", "regierungsende": None},
|
||||
},
|
||||
# Niedersachsen — LTW 09.10.2022. Kabinett Weil III (SPD+GRÜNE) vereidigt 08.11.2022.
|
||||
"NI": {
|
||||
"SPD": {"file": "spd-ni-2022.pdf", "titel": "SPD Niedersachsen Regierungsprogramm 2022", "partei": "SPD Niedersachsen", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-11-08", "regierungsende": None},
|
||||
"CDU": {"file": "cdu-ni-2022.pdf", "titel": "CDU Niedersachsen Regierungsprogramm 2022", "partei": "CDU Niedersachsen", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-11-08", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-ni-2022.pdf","titel": "BÜNDNIS 90/DIE GRÜNEN Niedersachsen Wahlprogramm 2022", "partei": "BÜNDNIS 90/DIE GRÜNEN Niedersachsen", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-11-08", "regierungsende": None},
|
||||
"AfD": {"file": "afd-ni-2022.pdf", "titel": "AfD Niedersachsen Wahlprogramm 2022", "partei": "AfD Niedersachsen", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-11-08", "regierungsende": None},
|
||||
},
|
||||
# Saarland — LTW 27.03.2022. Kabinett Rehlinger I (SPD-Alleinregierung) vereidigt 25.04.2022.
|
||||
"SL": {
|
||||
"SPD": {"file": "spd-sl-2022.pdf", "titel": "SPD Saarland Regierungsprogramm 2022", "partei": "SPD Saarland", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-04-25", "regierungsende": None},
|
||||
"CDU": {"file": "cdu-sl-2022.pdf", "titel": "CDU Saarland Wahlprogramm 2022", "partei": "CDU Saarland", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-04-25", "regierungsende": None},
|
||||
"AfD": {"file": "afd-sl-2022.pdf", "titel": "AfD Saarland Wahlprogramm 2022", "partei": "AfD Saarland", "jahr": 2022, "seiten": 100, "regierungsbildung": "2022-04-25", "regierungsende": None},
|
||||
},
|
||||
# Sachsen — LTW 01.09.2024. Kabinett Kretschmer III (CDU+SPD, Minderheit) vereidigt 18.12.2024.
|
||||
"SN": {
|
||||
"CDU": {"file": "cdu-sn-2024.pdf", "titel": "CDU Sachsen Wahlprogramm 2024", "partei": "CDU Sachsen", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-18", "regierungsende": None},
|
||||
"SPD": {"file": "spd-sn-2024.pdf", "titel": "SPD Sachsen Wahlprogramm 2024", "partei": "SPD Sachsen", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-18", "regierungsende": None},
|
||||
"AfD": {"file": "afd-sn-2024.pdf", "titel": "AfD Sachsen Wahlprogramm 2024", "partei": "AfD Sachsen", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-18", "regierungsende": None},
|
||||
"BSW": {"file": "bsw-sn-2024.pdf", "titel": "BSW Sachsen Wahlprogramm 2024", "partei": "BSW Sachsen", "jahr": 2024, "seiten": 50, "regierungsbildung": "2024-12-18", "regierungsende": None},
|
||||
"LINKE": {"file": "linke-sn-2024.pdf", "titel": "DIE LINKE Sachsen Wahlprogramm 2024", "partei": "DIE LINKE Sachsen", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-18", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-sn-2024.pdf","titel": "BÜNDNIS 90/DIE GRÜNEN Sachsen Wahlprogramm 2024","partei": "BÜNDNIS 90/DIE GRÜNEN Sachsen", "jahr": 2024, "seiten": 100, "regierungsbildung": "2024-12-18", "regierungsende": None},
|
||||
},
|
||||
# Bundestag — BTW 23.02.2025. Kabinett Merz I (CDU+CSU+SPD) vereidigt 06.05.2025.
|
||||
# Aktiv die BTW-2025-Wahlprogramme aller acht im 21. Bundestag relevanten
|
||||
# Parteien. Grundsatzprogramme bleiben in embeddings.PROGRAMME als
|
||||
# zweite Referenz erhalten.
|
||||
"BUND": {
|
||||
"CDU": {"file": "cdu-bund-2025.pdf", "titel": "Politikwechsel für Deutschland — Wahlprogramm CDU/CSU BTW 2025", "partei": "CDU", "jahr": 2025, "seiten": 82, "regierungsbildung": "2025-05-06", "regierungsende": None},
|
||||
"CSU": {"file": "csu-bund-2025.pdf", "titel": "Politikwechsel für Deutschland — Wahlprogramm CDU/CSU BTW 2025 (CSU)", "partei": "CSU", "jahr": 2025, "seiten": 81, "regierungsbildung": "2025-05-06", "regierungsende": None},
|
||||
"SPD": {"file": "spd-bund-2025.pdf", "titel": "Mehr für Dich. Besser für Deutschland. — SPD Regierungsprogramm BTW 2025", "partei": "SPD", "jahr": 2025, "seiten": 68, "regierungsbildung": "2025-05-06", "regierungsende": None},
|
||||
"GRÜNE": {"file": "gruene-bund-2025.pdf","titel": "Zusammen wachsen — Regierungsprogramm BÜNDNIS 90/DIE GRÜNEN BTW 2025", "partei": "BÜNDNIS 90/DIE GRÜNEN", "jahr": 2025, "seiten": 160, "regierungsbildung": "2025-05-06", "regierungsende": None},
|
||||
"FDP": {"file": "fdp-bund-2025.pdf", "titel": "Alles lässt sich ändern — FDP Wahlprogramm BTW 2025", "partei": "FDP", "jahr": 2025, "seiten": 52, "regierungsbildung": "2025-05-06", "regierungsende": None},
|
||||
"AfD": {"file": "afd-bund-2025.pdf", "titel": "Zeit für Deutschland — AfD Bundestagswahlprogramm 2025", "partei": "AfD", "jahr": 2025, "seiten": 177, "regierungsbildung": "2025-05-06", "regierungsende": None},
|
||||
"LINKE": {"file": "linke-bund-2025.pdf", "titel": "Alle wollen regieren. Wir wollen verändern. — DIE LINKE Wahlprogramm BTW 2025", "partei": "DIE LINKE", "jahr": 2025, "seiten": 60, "regierungsbildung": "2025-05-06", "regierungsende": None},
|
||||
"BSW": {"file": "bsw-bund-2025.pdf", "titel": "Unser Land verdient mehr — BSW Wahlprogramm BTW 2025", "partei": "BSW", "jahr": 2025, "seiten": 45, "regierungsbildung": "2025-05-06", "regierungsende": None},
|
||||
},
|
||||
}
|
||||
|
||||
# Pro Bundesland: Markdown-Übersichtsdatei mit Wahlprogramm-Zusammenfassungen,
|
||||
# wird als Kontext in den LLM-Prompt geladen (nicht für die Suche).
|
||||
WAHLPROGRAMM_KONTEXT_FILES: dict[str, str] = {
|
||||
@ -187,52 +39,15 @@ KONTEXT_PATH = Path(__file__).parent / "kontext"
|
||||
|
||||
|
||||
def get_wahlprogramm(bundesland: str, partei: str) -> Optional[dict]:
|
||||
"""Liefert die Wahlprogramm-Metadaten oder None, wenn keins vorliegt."""
|
||||
return WAHLPROGRAMME.get(bundesland, {}).get(partei)
|
||||
"""Liefert das aktuell gültige Wahlprogramm dieser Partei in dem
|
||||
Bundesland — als ``programme.Programm``-Dict (oder ``None``).
|
||||
|
||||
|
||||
def parteien_mit_wahlprogramm(bundesland: str) -> list[str]:
|
||||
"""Liste der Parteien, für die im gegebenen Bundesland ein Wahlprogramm vorliegt."""
|
||||
return list(WAHLPROGRAMME.get(bundesland, {}).keys())
|
||||
|
||||
|
||||
def regierungsbildung_for(bundesland: str) -> Optional[str]:
|
||||
"""Datum der Regierungsbildung (Vereidigung) der Regierung, die nach der
|
||||
aktuellen Wahl aus diesem Programm hervorging.
|
||||
|
||||
Achtung: NICHT die Vereidigung der gerade amtierenden Regierung — bei
|
||||
Sukzessionen innerhalb derselben WP (z.B. RP Dreyer III → Schweitzer I
|
||||
in WP18) bleibt das Datum bei der ersten Regierung der WP. Die
|
||||
Wahlprogramme bleiben zur Wahl wirksam, auch wenn die MP-Person später
|
||||
wechselt. Für die "aktuell amtierende" Regierung siehe
|
||||
``legislaturen.aktuelle_regierung``.
|
||||
Compat-Adapter auf ``programme.aktuelles_wahlprogramm``. Aufrufer,
|
||||
die früher ``info["file"]`` gelesen haben, müssen ``info["pdf"]``
|
||||
lesen; ``info["partei"]`` (Langform) gibt es nicht mehr — Kurzform
|
||||
+ ``info["bundesland"]`` reichen für die Anzeige.
|
||||
"""
|
||||
parteien = WAHLPROGRAMME.get(bundesland, {})
|
||||
for info in parteien.values():
|
||||
rb = info.get("regierungsbildung")
|
||||
if rb is not None:
|
||||
return rb
|
||||
return None
|
||||
|
||||
|
||||
def regierungsende_for(bundesland: str) -> Optional[str]:
|
||||
"""Datum, ab dem die aus dieser Wahl hervorgegangene Regierungsperiode
|
||||
endet. ``None`` solange laufend (auch nach Sukzession in derselben WP).
|
||||
"""
|
||||
parteien = WAHLPROGRAMME.get(bundesland, {})
|
||||
for info in parteien.values():
|
||||
return info.get("regierungsende")
|
||||
return None
|
||||
|
||||
|
||||
def regierung_aktuell(bundesland: str) -> bool:
|
||||
"""True, wenn die aus dieser Wahl hervorgegangene Regierungsperiode noch
|
||||
läuft (bildung gesetzt, ende=None)."""
|
||||
parteien = WAHLPROGRAMME.get(bundesland, {})
|
||||
if not parteien:
|
||||
return False
|
||||
info = next(iter(parteien.values()))
|
||||
return info.get("regierungsbildung") is not None and info.get("regierungsende") is None
|
||||
return aktuelles_wahlprogramm(bundesland, partei)
|
||||
|
||||
|
||||
def load_wahlprogramm_text(bundesland: str, partei: str) -> dict[int, str]:
|
||||
@ -246,11 +61,14 @@ def load_wahlprogramm_text(bundesland: str, partei: str) -> dict[int, str]:
|
||||
if not info:
|
||||
return {}
|
||||
|
||||
# Versuche paged-Textdatei zu laden
|
||||
paged_file = KONTEXT_PATH / info['file'].replace('.pdf', '-paged.txt')
|
||||
pdf_name = info.get("pdf", "")
|
||||
if not pdf_name:
|
||||
return {}
|
||||
|
||||
paged_file = KONTEXT_PATH / pdf_name.replace('.pdf', '-paged.txt')
|
||||
if not paged_file.exists():
|
||||
# Fallback: Normale Textdatei
|
||||
txt_file = KONTEXT_PATH / info['file'].replace('.pdf', '.txt')
|
||||
txt_file = KONTEXT_PATH / pdf_name.replace('.pdf', '.txt')
|
||||
if txt_file.exists():
|
||||
return {1: txt_file.read_text()}
|
||||
return {}
|
||||
@ -283,16 +101,11 @@ def search_wahlprogramm(
|
||||
keywords: list[str],
|
||||
max_results: int = 3,
|
||||
) -> list[dict]:
|
||||
"""Sucht relevante Passagen in einem Wahlprogramm.
|
||||
|
||||
Args:
|
||||
bundesland: Bundesland-Code (NRW, LSA, …)
|
||||
partei: Partei-Kürzel (CDU, SPD, GRÜNE, FDP, AfD, …)
|
||||
keywords: Suchbegriffe
|
||||
max_results: Maximale Anzahl Ergebnisse
|
||||
"""Sucht relevante Passagen in einem Wahlprogramm (Keyword-basiert,
|
||||
Fallback wenn die Embeddings-DB nicht da ist).
|
||||
|
||||
Returns:
|
||||
Liste von {bundesland, partei, seite, text, score, url, quelle}
|
||||
Liste von {bundesland, partei, seite, text, score, url, quelle}.
|
||||
"""
|
||||
info = get_wahlprogramm(bundesland, partei)
|
||||
if not info:
|
||||
@ -302,6 +115,10 @@ def search_wahlprogramm(
|
||||
if not pages:
|
||||
return []
|
||||
|
||||
pdf_name = info.get("pdf", "")
|
||||
name = info.get("name") or partei
|
||||
jahr = info.get("gueltig_ab", "")[:4] or "?"
|
||||
|
||||
results = []
|
||||
keywords_lower = [k.lower() for k in keywords]
|
||||
|
||||
@ -335,8 +152,8 @@ def search_wahlprogramm(
|
||||
"seite": page_num,
|
||||
"text": best_para,
|
||||
"score": score,
|
||||
"url": f"/static/referenzen/{info['file']}#page={page_num}",
|
||||
"quelle": f"{info['partei']} Wahlprogramm {info['jahr']}, S. {page_num}",
|
||||
"url": f"/static/referenzen/{pdf_name}#page={page_num}",
|
||||
"quelle": f"{name}, S. {page_num}",
|
||||
})
|
||||
|
||||
results.sort(key=lambda x: x['score'], reverse=True)
|
||||
@ -402,12 +219,9 @@ def format_quote_for_prompt(quotes: dict[str, list[dict]]) -> str:
|
||||
return ""
|
||||
|
||||
lines = ["\n## Relevante Passagen aus Wahlprogrammen\n"]
|
||||
lines.append("Nutze diese Originalzitate als Belege in deiner Bewertung:\n")
|
||||
|
||||
for partei, zitate in quotes.items():
|
||||
for z in zitate:
|
||||
lines.append(f"### {z['quelle']}")
|
||||
lines.append(f'> "{z["text"]}"')
|
||||
lines.append("")
|
||||
|
||||
for partei, partei_quotes in quotes.items():
|
||||
if partei_quotes:
|
||||
lines.append(f"\n### {partei}")
|
||||
for q in partei_quotes:
|
||||
lines.append(f"- S. {q['seite']}: \"{q['text']}\"")
|
||||
return "\n".join(lines)
|
||||
|
||||
@ -157,16 +157,16 @@ class TestBuildPdfHref:
|
||||
assert build_pdf_href(zitat) == "/api/wahlprogramm-cite?pid=cdu-nrw-2022&seite=15"
|
||||
|
||||
def test_empty_url_falls_back_to_quelle_lookup(self):
|
||||
"""Ohne url muss die quelle reconstruiert werden via WAHLPROGRAMME."""
|
||||
"""Ohne url muss die quelle reconstruiert werden via programme.PROGRAMME."""
|
||||
from app.redline_utils import build_pdf_href
|
||||
# Ein in WAHLPROGRAMME hinterlegter Titel
|
||||
from app.wahlprogramme import WAHLPROGRAMME
|
||||
# Pick the first programme from the registry
|
||||
bl, parteien = next(iter(WAHLPROGRAMME.items()))
|
||||
partei, info = next(iter(parteien.items()))
|
||||
titel = info.get("titel", "")
|
||||
if not titel:
|
||||
pytest.skip("Kein WAHLPROGRAMME-Eintrag mit titel verfuegbar")
|
||||
from app.programme import all_programme
|
||||
prog = next(
|
||||
(p for p in all_programme() if p.get("titel")),
|
||||
None,
|
||||
)
|
||||
if prog is None:
|
||||
pytest.skip("Kein Programm-Eintrag mit titel verfügbar")
|
||||
titel = prog["titel"]
|
||||
zitat = {
|
||||
"quelle": f"{titel} · S. 42",
|
||||
"text": "Wir wollen die Energiewende",
|
||||
@ -193,12 +193,11 @@ class TestBuildPdfHref:
|
||||
|
||||
def test_query_uses_first_5_words_of_text(self):
|
||||
from app.redline_utils import build_pdf_href
|
||||
from app.wahlprogramme import WAHLPROGRAMME
|
||||
bl, parteien = next(iter(WAHLPROGRAMME.items()))
|
||||
partei, info = next(iter(parteien.items()))
|
||||
titel = info.get("titel", "")
|
||||
if not titel:
|
||||
pytest.skip("Kein WAHLPROGRAMME-Eintrag mit titel verfuegbar")
|
||||
from app.programme import all_programme
|
||||
prog = next((p for p in all_programme() if p.get("titel")), None)
|
||||
if prog is None:
|
||||
pytest.skip("Kein Programm-Eintrag mit titel verfügbar")
|
||||
titel = prog["titel"]
|
||||
zitat = {
|
||||
"quelle": f"{titel} · S. 5",
|
||||
"text": "Eins zwei drei vier fünf sechs sieben",
|
||||
@ -214,12 +213,11 @@ class TestBuildPdfHref:
|
||||
def test_handles_seite_with_comma_separator(self):
|
||||
"""Quelle 'Titel, S. 42' (Komma) muss genauso parsen wie '· S. 42'."""
|
||||
from app.redline_utils import build_pdf_href
|
||||
from app.wahlprogramme import WAHLPROGRAMME
|
||||
bl, parteien = next(iter(WAHLPROGRAMME.items()))
|
||||
partei, info = next(iter(parteien.items()))
|
||||
titel = info.get("titel", "")
|
||||
if not titel:
|
||||
pytest.skip("Kein WAHLPROGRAMME-Eintrag mit titel verfuegbar")
|
||||
from app.programme import all_programme
|
||||
prog = next((p for p in all_programme() if p.get("titel")), None)
|
||||
if prog is None:
|
||||
pytest.skip("Kein Programm-Eintrag mit titel verfügbar")
|
||||
titel = prog["titel"]
|
||||
zitat = {"quelle": f"{titel}, S. 17", "text": "x", "url": ""}
|
||||
href = build_pdf_href(zitat)
|
||||
assert "seite=17" in href
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import pytest
|
||||
|
||||
from app.wahlprogramm_check import check_missing_programmes
|
||||
from app.wahlprogramme import WAHLPROGRAMME
|
||||
from app.programme import parteien_mit_wahlprogramm as _parteien_mit_wp
|
||||
from app.bundeslaender import BUNDESLAENDER
|
||||
|
||||
|
||||
@ -12,8 +12,7 @@ class TestCheckMissingProgrammes:
|
||||
def test_all_covered_returns_empty(self):
|
||||
"""Alle Fraktionen haben ein hinterlegtes Programm → leere Liste."""
|
||||
bl = "NRW"
|
||||
# Nur Fraktionen übergeben, die in WAHLPROGRAMME["NRW"] liegen
|
||||
indexed = list(WAHLPROGRAMME[bl].keys())
|
||||
indexed = _parteien_mit_wp(bl)
|
||||
result = check_missing_programmes(bl, indexed)
|
||||
assert result == [], (
|
||||
f"Erwartet [], bekommen {result!r} — alle Fraktionen sollten abgedeckt sein"
|
||||
@ -23,7 +22,7 @@ class TestCheckMissingProgrammes:
|
||||
"""Eine Fraktion ohne Programm → wird in der Rückgabe gemeldet."""
|
||||
bl = "NRW"
|
||||
# AfD ist in NRW hinterlegt, BSW nicht
|
||||
fraktionen = list(WAHLPROGRAMME[bl].keys()) + ["BSW"]
|
||||
fraktionen = _parteien_mit_wp(bl) + ["BSW"]
|
||||
result = check_missing_programmes(bl, fraktionen)
|
||||
assert "BSW" in result
|
||||
|
||||
@ -49,14 +48,14 @@ class TestCheckMissingProgrammes:
|
||||
check_missing_programmes("XX", ["CDU"])
|
||||
|
||||
def test_bundesland_without_wahlprogramme_entry(self):
|
||||
"""Aktives Bundesland ohne WAHLPROGRAMME-Eintrag → alle Fraktionen fehlend."""
|
||||
# Finde ein aktives BL, das keinen Eintrag in WAHLPROGRAMME hat
|
||||
"""Aktives Bundesland ohne Programm-Einträge → alle Fraktionen fehlend."""
|
||||
# Finde ein aktives BL, das keine Wahlprogramme registriert hat
|
||||
bl_without = next(
|
||||
(code for code in BUNDESLAENDER if code not in WAHLPROGRAMME),
|
||||
(code for code in BUNDESLAENDER if not _parteien_mit_wp(code)),
|
||||
None,
|
||||
)
|
||||
if bl_without is None:
|
||||
pytest.skip("Alle bekannten Bundesländer haben WAHLPROGRAMME-Einträge")
|
||||
pytest.skip("Alle bekannten Bundesländer haben Programm-Einträge")
|
||||
fraktionen = BUNDESLAENDER[bl_without].landtagsfraktionen[:2]
|
||||
result = check_missing_programmes(bl_without, fraktionen)
|
||||
assert result == fraktionen
|
||||
|
||||
@ -1,342 +1,102 @@
|
||||
"""Tests for wahlprogramme.py — registry consistency + file existence."""
|
||||
import re
|
||||
"""Tests for wahlprogramme.py.
|
||||
|
||||
Nach dem #222-Refactor ist dieses Modul nur noch ein dünner Wrapper:
|
||||
keyword-basierte Suche + PDF-Text-Loader + ein Compat-Adapter
|
||||
``get_wahlprogramm`` der zu ``programme.aktuelles_wahlprogramm``
|
||||
delegiert. Die Stamm-Daten (``WAHLPROGRAMME``-Literal) sind nach
|
||||
``programme.PROGRAMME`` gewandert. Strukturelle Daten-Tests leben
|
||||
deshalb in ``test_programme.py``.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from app.wahlprogramme import (
|
||||
WAHLPROGRAMME,
|
||||
REFERENZEN_PATH,
|
||||
get_wahlprogramm,
|
||||
parteien_mit_wahlprogramm,
|
||||
regierung_aktuell,
|
||||
regierungsbildung_for,
|
||||
regierungsende_for,
|
||||
load_wahlprogramm_text,
|
||||
search_wahlprogramm,
|
||||
find_relevant_quotes,
|
||||
format_quote_for_prompt,
|
||||
)
|
||||
from app.programme import parteien_mit_wahlprogramm
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Registry consistency
|
||||
# Stichproben aktiver Bundesländer + zugeordnete Parteien
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class TestRegistryStructure:
|
||||
def test_active_bundeslaender_present(self):
|
||||
for code in ["NRW", "LSA", "MV", "BE"]:
|
||||
assert code in WAHLPROGRAMME, f"missing wahlprogramme entry for {code}"
|
||||
|
||||
def test_each_entry_has_required_keys(self):
|
||||
required = {"file", "titel", "partei", "jahr", "seiten"}
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
missing = required - set(info.keys())
|
||||
assert not missing, f"{bl}/{partei} missing keys: {missing}"
|
||||
|
||||
def test_jahr_is_integer(self):
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
assert isinstance(info["jahr"], int), f"{bl}/{partei} jahr not int"
|
||||
|
||||
def test_seiten_is_positive_integer(self):
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
assert isinstance(info["seiten"], int)
|
||||
assert info["seiten"] > 0
|
||||
|
||||
def test_file_extension_is_pdf(self):
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
assert info["file"].endswith(".pdf")
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Regierungsbildungs-Felder — Konsistenz pro Bundesland
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class TestRegierungsbildung:
|
||||
"""Pro Bundesland gehören alle Wahlprogramm-Einträge zur gleichen
|
||||
Legislatur — d.h. dasselbe regierungsbildung-Datum. Ausnahme: BUND, wo
|
||||
Grundsatzprogramme stehen, die regierungsbildung=None tragen."""
|
||||
|
||||
_ISO_DATE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
|
||||
|
||||
def test_every_entry_has_regierungs_fields(self):
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
assert "regierungsbildung" in info, f"{bl}/{partei}: regierungsbildung fehlt"
|
||||
assert "regierungsende" in info, f"{bl}/{partei}: regierungsende fehlt"
|
||||
|
||||
def test_regierungsbildung_iso_or_none(self):
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
rb = info.get("regierungsbildung")
|
||||
if rb is not None:
|
||||
assert isinstance(rb, str) and self._ISO_DATE.match(rb), \
|
||||
f"{bl}/{partei}: regierungsbildung kein ISO-Datum: {rb!r}"
|
||||
|
||||
def test_regierungsende_iso_or_none(self):
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
re_ = info.get("regierungsende")
|
||||
if re_ is not None:
|
||||
assert isinstance(re_, str) and self._ISO_DATE.match(re_), \
|
||||
f"{bl}/{partei}: regierungsende kein ISO-Datum: {re_!r}"
|
||||
|
||||
def test_alle_parteien_eines_bl_haben_gleiches_datum(self):
|
||||
"""Alle Wahlprogramm-Einträge eines Bundeslands gehören zur selben
|
||||
Regierung und müssen daher dasselbe Bildungs-/Endedatum tragen."""
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
bildung = {info.get("regierungsbildung") for info in parteien.values()}
|
||||
ende = {info.get("regierungsende") for info in parteien.values()}
|
||||
assert len(bildung) == 1, \
|
||||
f"{bl}: regierungsbildung divergent: {bildung}"
|
||||
assert len(ende) == 1, \
|
||||
f"{bl}: regierungsende divergent: {ende}"
|
||||
|
||||
def test_grundsatzprogramme_haben_keine_regierung(self):
|
||||
"""Grundsatzprogramme (ist_grundsatz=True) tragen keine Regierungs-
|
||||
bildung — sie sind zeitlos."""
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
if info.get("ist_grundsatz"):
|
||||
assert info.get("regierungsbildung") is None, \
|
||||
f"{bl}/{partei} ist Grundsatzprogramm, sollte regierungsbildung=None haben"
|
||||
|
||||
|
||||
class TestRegierungsHelper:
|
||||
def test_regierungsbildung_for_known_bl(self):
|
||||
assert regierungsbildung_for("NRW") == "2022-06-29"
|
||||
assert regierungsbildung_for("HH") == "2025-05-07"
|
||||
|
||||
def test_regierungsbildung_for_bund_btw2025(self):
|
||||
# BUND tragt nun die BTW-2025-Wahlprogramme; Kabinett Merz I
|
||||
# vereidigt 06.05.2025. Grundsatzprogramme bleiben nur in
|
||||
# embeddings.PROGRAMME als zweite Referenz.
|
||||
assert regierungsbildung_for("BUND") == "2025-05-06"
|
||||
|
||||
def test_regierungsbildung_for_unknown_bl(self):
|
||||
assert regierungsbildung_for("XX") is None
|
||||
|
||||
def test_regierung_aktuell_true_for_active_bl(self):
|
||||
assert regierung_aktuell("NRW") is True
|
||||
assert regierung_aktuell("BB") is True
|
||||
assert regierung_aktuell("BUND") is True
|
||||
|
||||
def test_regierungsende_for_active_is_none(self):
|
||||
assert regierungsende_for("NRW") is None
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# File existence — every registered file must exist on disk
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class TestFileExistence:
|
||||
"""Catches typos in the file field that would silently break embedding
|
||||
indexing or PDF download links."""
|
||||
|
||||
def test_every_registered_pdf_exists(self):
|
||||
missing = []
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
path = REFERENZEN_PATH / info["file"]
|
||||
if not path.exists():
|
||||
missing.append(f"{bl}/{partei}: {info['file']}")
|
||||
assert not missing, "missing PDFs:\n " + "\n ".join(missing)
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Lookup helpers
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class TestGetWahlprogramm:
|
||||
def test_returns_dict_for_known_combination(self):
|
||||
info = get_wahlprogramm("MV", "CDU")
|
||||
assert info is not None
|
||||
assert info["partei"] == "CDU Mecklenburg-Vorpommern"
|
||||
|
||||
def test_returns_none_for_unknown_bundesland(self):
|
||||
assert get_wahlprogramm("XX", "CDU") is None
|
||||
|
||||
def test_returns_none_for_unknown_partei(self):
|
||||
assert get_wahlprogramm("NRW", "BSW") is None
|
||||
|
||||
|
||||
class TestParteienMitWahlprogramm:
|
||||
"""Smoke-Test: in den jeweiligen BL liegen die erwarteten Fraktionen.
|
||||
Strikte Schema-Tests gegen ``programme.PROGRAMME`` in test_programme.py."""
|
||||
|
||||
def test_nrw_has_five_parteien(self):
|
||||
parteien = parteien_mit_wahlprogramm("NRW")
|
||||
assert len(parteien) == 5
|
||||
assert set(parteien) == {"CDU", "SPD", "GRÜNE", "FDP", "AfD"}
|
||||
|
||||
def test_mv_has_six_parteien(self):
|
||||
parteien = parteien_mit_wahlprogramm("MV")
|
||||
assert set(parteien) == {"CDU", "SPD", "GRÜNE", "FDP", "AfD", "LINKE"}
|
||||
|
||||
def test_be_has_five_parteien(self):
|
||||
parteien = parteien_mit_wahlprogramm("BE")
|
||||
assert set(parteien) == {"CDU", "SPD", "GRÜNE", "LINKE", "AfD"}
|
||||
|
||||
def test_bund_has_eight_parteien(self):
|
||||
# BTW 2025: CDU, CSU, SPD, GRÜNE, FDP, AfD, LINKE, BSW.
|
||||
parteien = parteien_mit_wahlprogramm("BUND")
|
||||
assert set(parteien) == {"CDU", "CSU", "SPD", "GRÜNE", "FDP", "AfD", "LINKE", "BSW"}
|
||||
|
||||
def test_by_has_five_parteien(self):
|
||||
parteien = parteien_mit_wahlprogramm("BY")
|
||||
assert set(parteien) == {"CSU", "FREIE WÄHLER", "GRÜNE", "SPD", "AfD"}
|
||||
|
||||
def test_hb_has_five_parteien(self):
|
||||
# AfD war wegen Listenstreit nicht zur Bürgerschaftswahl 2023 zugelassen.
|
||||
# Stattdessen ist BiW (Bürger in Wut) als 6. Fraktion in der 21. WP.
|
||||
parteien = parteien_mit_wahlprogramm("HB")
|
||||
assert set(parteien) == {"SPD", "CDU", "GRÜNE", "LINKE", "BiW"}
|
||||
|
||||
def test_he_has_five_parteien(self):
|
||||
parteien = parteien_mit_wahlprogramm("HE")
|
||||
assert set(parteien) == {"CDU", "SPD", "GRÜNE", "FDP", "AfD"}
|
||||
|
||||
def test_ni_has_four_parteien(self):
|
||||
parteien = parteien_mit_wahlprogramm("NI")
|
||||
assert set(parteien) == {"SPD", "CDU", "GRÜNE", "AfD"}
|
||||
|
||||
def test_sl_has_three_parteien(self):
|
||||
parteien = parteien_mit_wahlprogramm("SL")
|
||||
assert set(parteien) == {"SPD", "CDU", "AfD"}
|
||||
|
||||
def test_sn_has_six_parteien(self):
|
||||
parteien = parteien_mit_wahlprogramm("SN")
|
||||
assert set(parteien) == {"CDU", "SPD", "AfD", "BSW", "LINKE", "GRÜNE"}
|
||||
|
||||
def test_unknown_bundesland_empty_list(self):
|
||||
assert parteien_mit_wahlprogramm("XX") == []
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# embeddings.PROGRAMME consistency cross-check
|
||||
# get_wahlprogramm — Compat-Adapter
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class TestEmbeddingsRegistryConsistency:
|
||||
"""Every entry in WAHLPROGRAMME must also exist in embeddings.PROGRAMME
|
||||
so the indexer can find it. Mismatch is the kind of bug a manual smoke
|
||||
misses but would show up during indexing."""
|
||||
|
||||
def test_every_wahlprogramm_has_embeddings_entry(self):
|
||||
from app.embeddings import PROGRAMME
|
||||
class TestGetWahlprogramm:
|
||||
def test_returns_programm_for_known_combination(self):
|
||||
prog = get_wahlprogramm("NRW", "CDU")
|
||||
assert prog is not None
|
||||
assert prog["pdf"] == "cdu-nrw-2022.pdf"
|
||||
assert prog["partei"] == "CDU"
|
||||
assert prog["bundesland"] == "NRW"
|
||||
|
||||
# Match WAHLPROGRAMME-Eintrag → PROGRAMME-Eintrag entweder ueber den
|
||||
# file-stem (Standard: "cdu-mv-2021" matcht "cdu-mv-2021") ODER ueber
|
||||
# den `pdf`-Wert in PROGRAMME (BUND-Grundsatzprogramme nutzen kuerzere
|
||||
# PROGRAMME-Keys wie "cdu-grundsatz" obwohl die Datei
|
||||
# "cdu-grundsatzprogramm.pdf" heisst).
|
||||
pdf_to_pid = {p.get("pdf"): pid for pid, p in PROGRAMME.items() if p.get("pdf")}
|
||||
missing = []
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
file_stem = info["file"].rsplit(".", 1)[0]
|
||||
if file_stem in PROGRAMME:
|
||||
continue
|
||||
if info["file"] in pdf_to_pid:
|
||||
continue
|
||||
missing.append(f"{bl}/{partei} → {info['file']}")
|
||||
assert not missing, (
|
||||
"WAHLPROGRAMME entries missing in embeddings.PROGRAMME:\n "
|
||||
+ "\n ".join(missing)
|
||||
)
|
||||
def test_returns_none_for_unknown_bundesland(self):
|
||||
assert get_wahlprogramm("XX", "CDU") is None
|
||||
|
||||
def test_returns_none_for_unknown_partei(self):
|
||||
assert get_wahlprogramm("NRW", "BSW") is None # BSW nicht im NRW-Landtag
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Strikte Cross-Konsistenz mit programme.PROGRAMME (Drift-Schutz, ADR 0013)
|
||||
# File existence — every registered pdf must exist on disk
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class TestWahlprogrammeProgrammeConsistency:
|
||||
"""WAHLPROGRAMME (legacy, mit titel/seiten/regierung) und
|
||||
programme.PROGRAMME (zentrale Registry mit Geltungsdaten) speichern
|
||||
überlappende Felder. Diese Tests fangen stille Drift, bis #222 die
|
||||
Quelle vereinheitlicht (Compat-Shim).
|
||||
|
||||
Invarianten:
|
||||
- Für jedes (bl, partei) in WAHLPROGRAMME liefert
|
||||
``aktuelles_wahlprogramm(bl, partei)`` einen Eintrag (nicht None).
|
||||
- Der ``pdf``-Wert in PROGRAMME stimmt mit ``file`` in WAHLPROGRAMME
|
||||
überein.
|
||||
- Die ``partei``-Kurzform stimmt überein (PROGRAMME["partei"] ist die
|
||||
Kurzform; WAHLPROGRAMME-Key ist auch Kurzform).
|
||||
"""
|
||||
class TestFileExistence:
|
||||
"""Catches typos im pdf-Feld der Programm-Registry, die das
|
||||
Indexing oder PDF-Download silently brechen würden."""
|
||||
|
||||
def test_every_wahlprogramm_has_aktuelles_programm_match(self):
|
||||
from app.programme import aktuelles_wahlprogramm
|
||||
|
||||
mismatches = []
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
prog = aktuelles_wahlprogramm(bl, partei)
|
||||
if prog is None:
|
||||
mismatches.append(
|
||||
f"{bl}/{partei}: aktuelles_wahlprogramm liefert None, "
|
||||
f"obwohl WAHLPROGRAMME-Eintrag {info['file']} existiert"
|
||||
)
|
||||
assert not mismatches, "\n ".join(mismatches)
|
||||
|
||||
def test_pdf_filenames_match_between_registries(self):
|
||||
from app.programme import aktuelles_wahlprogramm
|
||||
|
||||
drift = []
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei, info in parteien.items():
|
||||
prog = aktuelles_wahlprogramm(bl, partei)
|
||||
if prog is None:
|
||||
continue # vom Vortest abgedeckt
|
||||
wp_pdf = info["file"]
|
||||
pr_pdf = prog.get("pdf")
|
||||
if wp_pdf != pr_pdf:
|
||||
drift.append(f"{bl}/{partei}: WAHLPROGRAMME.file={wp_pdf!r} ≠ PROGRAMME.pdf={pr_pdf!r}")
|
||||
assert not drift, "Drift zwischen WAHLPROGRAMME und PROGRAMME:\n " + "\n ".join(drift)
|
||||
|
||||
def test_partei_kurzform_consistency(self):
|
||||
"""PROGRAMME["partei"] ist die Kurzform (z.B. 'CDU'), nicht
|
||||
die Langform ('CDU NRW'). Test-Sicherheitsnetz, falls jemand
|
||||
versehentlich die Langform reinträgt."""
|
||||
from app.programme import aktuelles_wahlprogramm
|
||||
|
||||
wrong = []
|
||||
for bl, parteien in WAHLPROGRAMME.items():
|
||||
for partei in parteien.keys():
|
||||
prog = aktuelles_wahlprogramm(bl, partei)
|
||||
if prog is None:
|
||||
continue
|
||||
if prog.get("partei") != partei:
|
||||
wrong.append(
|
||||
f"{bl}/{partei}: PROGRAMME.partei={prog.get('partei')!r} "
|
||||
f"≠ WAHLPROGRAMME-Key {partei!r}"
|
||||
)
|
||||
assert not wrong, "\n ".join(wrong)
|
||||
|
||||
def test_no_orphan_aktuelle_programme_in_registry(self):
|
||||
"""Die andere Richtung: jedes aktuelle Wahlprogramm in PROGRAMME
|
||||
(gueltig_bis IS NULL, typ='wahlprogramm') muss in WAHLPROGRAMME
|
||||
vorhanden sein. Sonst ist die Bewertungs-Pipeline blind dafür."""
|
||||
def test_every_registered_pdf_exists(self):
|
||||
from app.programme import all_programme
|
||||
|
||||
orphans = []
|
||||
missing = []
|
||||
for prog in all_programme():
|
||||
if prog.get("typ") != "wahlprogramm":
|
||||
pdf = prog.get("pdf")
|
||||
if not pdf:
|
||||
continue
|
||||
if prog.get("gueltig_bis") is not None:
|
||||
continue # historisches Programm
|
||||
bl = prog.get("bundesland")
|
||||
partei = prog.get("partei")
|
||||
if bl not in WAHLPROGRAMME:
|
||||
orphans.append(f"{prog['id']}: BL {bl} nicht in WAHLPROGRAMME")
|
||||
continue
|
||||
if partei not in WAHLPROGRAMME[bl]:
|
||||
orphans.append(f"{prog['id']}: {bl}/{partei} fehlt in WAHLPROGRAMME")
|
||||
assert not orphans, "Aktuelle Wahlprogramme in PROGRAMME ohne WAHLPROGRAMME-Eintrag:\n " + "\n ".join(orphans)
|
||||
path = REFERENZEN_PATH / pdf
|
||||
if not path.exists():
|
||||
missing.append(f"{prog['id']}: {pdf}")
|
||||
assert not missing, "missing PDFs:\n " + "\n ".join(missing)
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# load_wahlprogramm_text — Fallback-Pfade (#134 Coverage-Backfill)
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestLoadWahlprogrammText:
|
||||
def test_returns_empty_for_unknown_combination(self):
|
||||
from app.wahlprogramme import load_wahlprogramm_text
|
||||
assert load_wahlprogramm_text("XX", "XYZ") == {}
|
||||
|
||||
def test_paged_textfile_used_when_present(self, tmp_path, monkeypatch):
|
||||
@ -345,7 +105,7 @@ class TestLoadWahlprogrammText:
|
||||
from app import wahlprogramme as wp_mod
|
||||
# Mock get_wahlprogramm -> bekannte Datei
|
||||
monkeypatch.setattr(wp_mod, "get_wahlprogramm",
|
||||
lambda bl, p: {"file": "test.pdf"})
|
||||
lambda bl, p: {"pdf": "test.pdf"})
|
||||
paged = tmp_path / "test-paged.txt"
|
||||
paged.write_text("--- PAGE 1 ---\nseite eins\n--- PAGE 2 ---\nseite zwei")
|
||||
monkeypatch.setattr(wp_mod, "KONTEXT_PATH", tmp_path)
|
||||
@ -355,11 +115,11 @@ class TestLoadWahlprogrammText:
|
||||
assert "seite zwei" in result[2]
|
||||
|
||||
def test_falls_back_to_normal_textfile(self, tmp_path, monkeypatch):
|
||||
"""Ohne paged-Datei wird auf normale .txt-Datei zurueckgefallen,
|
||||
"""Ohne paged-Datei wird auf normale .txt-Datei zurückgefallen,
|
||||
komplett unter Seite 1."""
|
||||
from app import wahlprogramme as wp_mod
|
||||
monkeypatch.setattr(wp_mod, "get_wahlprogramm",
|
||||
lambda bl, p: {"file": "test.pdf"})
|
||||
lambda bl, p: {"pdf": "test.pdf"})
|
||||
normal = tmp_path / "test.txt"
|
||||
normal.write_text("flacher text ohne seitenmarker")
|
||||
monkeypatch.setattr(wp_mod, "KONTEXT_PATH", tmp_path)
|
||||
@ -368,39 +128,50 @@ class TestLoadWahlprogrammText:
|
||||
assert result == {1: "flacher text ohne seitenmarker"}
|
||||
|
||||
def test_returns_empty_when_no_textfile(self, tmp_path, monkeypatch):
|
||||
"""Weder paged- noch normale Textdatei → leeres Dict."""
|
||||
"""Weder paged- noch flat-Textdatei vorhanden → leeres Dict."""
|
||||
from app import wahlprogramme as wp_mod
|
||||
monkeypatch.setattr(wp_mod, "get_wahlprogramm",
|
||||
lambda bl, p: {"file": "test.pdf"})
|
||||
# tmp_path ist leer
|
||||
lambda bl, p: {"pdf": "test.pdf"})
|
||||
monkeypatch.setattr(wp_mod, "KONTEXT_PATH", tmp_path)
|
||||
|
||||
assert wp_mod.load_wahlprogramm_text("X", "Y") == {}
|
||||
result = wp_mod.load_wahlprogramm_text("X", "Y")
|
||||
assert result == {}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# search_wahlprogramm — Edge cases
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestSearchWahlprogramm:
|
||||
def test_returns_empty_for_unknown_combination(self):
|
||||
from app.wahlprogramme import search_wahlprogramm
|
||||
assert search_wahlprogramm("XX", "XYZ", ["test"]) == []
|
||||
result = search_wahlprogramm("XX", "XYZ", ["foo"])
|
||||
assert result == []
|
||||
|
||||
def test_returns_empty_when_text_missing(self, monkeypatch):
|
||||
"""Bekannte Partei + Bundesland aber keine Textdatei → leer."""
|
||||
"""get_wahlprogramm liefert ein Programm, aber kein paged-Text:
|
||||
search_wahlprogramm muss [] liefern, nicht crashen."""
|
||||
from app import wahlprogramme as wp_mod
|
||||
monkeypatch.setattr(wp_mod, "get_wahlprogramm",
|
||||
lambda bl, p: {"file": "missing.pdf"})
|
||||
lambda bl, p: {"pdf": "missing.pdf",
|
||||
"name": "X Wahlprogramm 2024",
|
||||
"gueltig_ab": "2024-01-01"})
|
||||
monkeypatch.setattr(wp_mod, "load_wahlprogramm_text",
|
||||
lambda bl, p: {})
|
||||
assert wp_mod.search_wahlprogramm("X", "Y", ["test"]) == []
|
||||
assert search_wahlprogramm("X", "Y", ["foo"]) == []
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# find_relevant_quotes — Bundesland-Validierung
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestFindRelevantQuotes:
|
||||
def test_unknown_bundesland_raises(self):
|
||||
from app.wahlprogramme import find_relevant_quotes
|
||||
with pytest.raises(ValueError, match="Unbekanntes Bundesland"):
|
||||
find_relevant_quotes("Antrag-Text", ["CDU"], bundesland="ZZ")
|
||||
find_relevant_quotes("text", ["CDU"], "XX")
|
||||
|
||||
|
||||
class TestFormatQuoteForPrompt:
|
||||
def test_empty_quotes_returns_empty_string(self):
|
||||
from app.wahlprogramme import format_quote_for_prompt
|
||||
assert format_quote_for_prompt({}) == ""
|
||||
|
||||
361
tools/build_programme_literal.py
Normal file
361
tools/build_programme_literal.py
Normal file
@ -0,0 +1,361 @@
|
||||
"""Generate the new programme.PROGRAMME literal from the lazy-migrated state.
|
||||
|
||||
Reads the in-memory PROGRAMME dict (after _migrate_from_legacy ran), enriches
|
||||
with titel from WAHLPROGRAMME (where available) and seiten from PyMuPDF, then
|
||||
emits a Python literal sorted by (typ, bundesland, gueltig_ab).
|
||||
"""
|
||||
import sys
|
||||
sys.path.insert(0, "/app")
|
||||
from pathlib import Path
|
||||
|
||||
# Trigger migration
|
||||
from app import programme as programme_mod
|
||||
programme_mod._ensure_initialized()
|
||||
PROGRAMME_NEU = programme_mod.PROGRAMME
|
||||
|
||||
# Quelle für titel
|
||||
from app.wahlprogramme import WAHLPROGRAMME
|
||||
|
||||
# Override: titel-Mapping aus _ARCHIVED_SKELETONS (programme.py).
|
||||
# Diese sind über partei+bundesland+typ identifiziert, weil die Skeleton-IDs
|
||||
# Jahres-Suffixe haben, die embeddings.PROGRAMME-IDs nicht.
|
||||
TITEL_OVERRIDE_BY_PARTEI_BL_TYP = {
|
||||
("CSU", "BY", "grundsatzprogramm-land"): "Für ein neues Miteinander — Grundsatzprogramm der CSU",
|
||||
("CDU", "NRW", "grundsatzprogramm-land"): "Aufstieg, Sicherheit, Perspektive — Das Nordrhein-Westfalen-Programm",
|
||||
("CDU", "SN", "grundsatzprogramm-land"): "Zukunftsplan für Sachsen",
|
||||
("CDU", "LSA", "grundsatzprogramm-land"): "Sachsen-Anhalt. Unsere Verantwortung. Unsere Zukunft.",
|
||||
("SSW", "SH", "grundsatzprogramm-land"): "SSW Rahmenprogramm",
|
||||
}
|
||||
|
||||
import fitz
|
||||
|
||||
REFERENZEN = Path("/app/app/static/referenzen")
|
||||
|
||||
def get_titel(prog: dict) -> str | None:
|
||||
"""titel aus WAHLPROGRAMME[bl][partei] holen, falls vorhanden."""
|
||||
bl = prog.get("bundesland")
|
||||
partei = prog.get("partei")
|
||||
if not bl or not partei:
|
||||
return None
|
||||
wp_entry = WAHLPROGRAMME.get(bl, {}).get(partei)
|
||||
if not wp_entry:
|
||||
return None
|
||||
# Nur wenn das pdf zueinander passt
|
||||
if wp_entry.get("file") != prog.get("pdf"):
|
||||
return None
|
||||
return wp_entry.get("titel") or None
|
||||
|
||||
def get_seiten(pdf: str) -> int | None:
|
||||
"""PDF-Seitenzahl via PyMuPDF."""
|
||||
p = REFERENZEN / pdf
|
||||
if not p.exists():
|
||||
return None
|
||||
try:
|
||||
doc = fitz.open(p)
|
||||
n = doc.page_count
|
||||
doc.close()
|
||||
return n
|
||||
except Exception as e:
|
||||
print(f"# fitz-Fehler bei {pdf}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
# Schlüssel für Python-Literal — diktiert die Reihenfolge im Output-Dict
|
||||
KEYS_ORDER = [
|
||||
"id", "typ", "partei", "bundesland", "wp",
|
||||
"gueltig_ab", "gueltig_bis",
|
||||
"name", "titel", "pdf", "seiten",
|
||||
# Verbleibende, die Tests heute lesen — im Schema-Refactor entfernt:
|
||||
# "beschluss", "wahl", "hinweis", "ist_grundsatz" — übergehen.
|
||||
]
|
||||
|
||||
def repr_value(v):
|
||||
if v is None:
|
||||
return "None"
|
||||
if isinstance(v, bool):
|
||||
return "True" if v else "False"
|
||||
if isinstance(v, (int, float)):
|
||||
return repr(v)
|
||||
if isinstance(v, str):
|
||||
# double-quote-strings, mit Escape von eingebetteten "-Zeichen
|
||||
escaped = v.replace("\\", "\\\\").replace('"', '\\"')
|
||||
return f'"{escaped}"'
|
||||
return repr(v)
|
||||
|
||||
def fmt_entry(prog: dict) -> str:
|
||||
out = []
|
||||
for k in KEYS_ORDER:
|
||||
v = prog.get(k)
|
||||
if v is None and k in ("titel", "wp", "seiten", "bundesland", "gueltig_bis"):
|
||||
# Optional-Felder: explizit None auch raus, wenn Feld nicht gesetzt
|
||||
out.append(f'"{k}": None')
|
||||
elif v is None:
|
||||
continue # Pflichtfeld nicht gesetzt → überspringen, fällt sonst auf None
|
||||
else:
|
||||
out.append(f'"{k}": {repr_value(v)}')
|
||||
return "{" + ", ".join(out) + "}"
|
||||
|
||||
def gruppe(prog: dict):
|
||||
"""Sortier-Schlüssel."""
|
||||
typ = prog.get("typ", "zzz")
|
||||
bl = prog.get("bundesland") or ""
|
||||
ga = prog.get("gueltig_ab") or "0000"
|
||||
partei = prog.get("partei", "")
|
||||
return (typ, bl, ga, partei)
|
||||
|
||||
# Anreichern + ausgeben
|
||||
results = []
|
||||
for pid, prog in PROGRAMME_NEU.items():
|
||||
enriched = dict(prog)
|
||||
enriched.setdefault("id", pid)
|
||||
# Migration setzt titel = name als Default — das ist kein echter Slogan,
|
||||
# nur Doppelung. titel ECHT nur dann tragen, wenn entweder explizit
|
||||
# in WAHLPROGRAMME ("Machen, worauf es ankommt") oder vom Skeleton
|
||||
# in programme.py (Land-Grundsatzprogramme).
|
||||
if enriched.get("titel") == enriched.get("name"):
|
||||
enriched.pop("titel", None)
|
||||
if not enriched.get("titel"):
|
||||
t = get_titel(prog)
|
||||
if t and t != enriched.get("name"):
|
||||
enriched["titel"] = t
|
||||
# Override für Land-Grundsatzprogramme (Slogans aus alten Skeletons)
|
||||
if not enriched.get("titel"):
|
||||
key = (
|
||||
prog.get("partei"),
|
||||
prog.get("bundesland"),
|
||||
prog.get("typ"),
|
||||
)
|
||||
override = TITEL_OVERRIDE_BY_PARTEI_BL_TYP.get(key)
|
||||
if override:
|
||||
enriched["titel"] = override
|
||||
if not enriched.get("seiten"):
|
||||
s = get_seiten(prog.get("pdf", ""))
|
||||
if s:
|
||||
enriched["seiten"] = s
|
||||
results.append(enriched)
|
||||
|
||||
results.sort(key=gruppe)
|
||||
|
||||
# Statistik
|
||||
n_titel = sum(1 for p in results if p.get("titel"))
|
||||
n_seiten = sum(1 for p in results if p.get("seiten"))
|
||||
print(f"# {len(results)} Einträge insgesamt", file=sys.stderr)
|
||||
print(f"# {n_titel} mit titel ({len(results) - n_titel} ohne)", file=sys.stderr)
|
||||
print(f"# {n_seiten} mit seiten ({len(results) - n_seiten} ohne)", file=sys.stderr)
|
||||
|
||||
# Output: vollständige neue programme.py
|
||||
header = '''"""Zentrale Programm-Registry — alle politischen Programm-Dokumente
|
||||
(Wahlprogramme, Bundes-Grundsatzprogramme, Landes-Grundsatzprogramme),
|
||||
historisch und aktuell.
|
||||
|
||||
Single Source of Truth für:
|
||||
- ``embeddings.py`` (Indexer liest die PDFs aus dieser Liste)
|
||||
- ``analyzer.py`` (sucht das zum Antrag passende Wahlprogramm)
|
||||
- UI (zeigt Geltungszeitraum + zugeordnete Regierung pro Programm)
|
||||
|
||||
Siehe ``app/legislaturen.py`` für Wahlperioden + Regierungen — Programm-
|
||||
Daten beschreiben das Dokument selbst, nicht die Regierung, die aus ihm
|
||||
hervorging. Die Verbindung läuft über
|
||||
``legislaturen.regierung_zum_zeitpunkt(bundesland, datum)``.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Literal, Optional, TypedDict
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Schema
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
ProgrammTyp = Literal[
|
||||
"wahlprogramm", # zur Wahl beschlossen, gilt für 1 Legislatur
|
||||
"grundsatzprogramm-bund", # bundesweites Grundsatzprogramm
|
||||
"grundsatzprogramm-land", # landesspezifisches Grundsatzprogramm
|
||||
]
|
||||
|
||||
|
||||
class Programm(TypedDict, total=False):
|
||||
"""Single source of truth für ein politisches Programm-Dokument.
|
||||
|
||||
Pflichtfelder: id, typ, partei, gueltig_ab, name, pdf.
|
||||
Optional: bundesland, wp, gueltig_bis, titel, seiten.
|
||||
|
||||
Was hier bewusst NICHT drin ist:
|
||||
- ``regierungsbildung`` / ``regierungsende`` — gehört zu
|
||||
``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft
|
||||
über ``legislaturen.regierung_zum_zeitpunkt(bl, antrag_datum)``.
|
||||
- ``partei`` Langform ("CDU NRW") — ableitbar via partei + bundesland.
|
||||
- ``jahr`` — ``int(gueltig_ab[:4])`` reicht.
|
||||
"""
|
||||
id: str # eindeutiger Schlüssel, z.B. "cdu-nrw-2022"
|
||||
typ: ProgrammTyp
|
||||
partei: str # kanonisch (CDU, BiW, BÜNDNIS 90/DIE GRÜNEN, …)
|
||||
bundesland: Optional[str] # BL-Code; None nur bei Bundesgrundsatzprogrammen
|
||||
wp: Optional[int] # Legislatur-Nummer; nur typ=wahlprogramm
|
||||
gueltig_ab: str # ISO YYYY-MM-DD; bei wahl: Wahltag
|
||||
gueltig_bis: Optional[str] # ISO; None = aktuell gültig
|
||||
name: str # voll-qualifiziert für Citation, z.B. "CDU NRW Wahlprogramm 2022"
|
||||
titel: Optional[str] # Slogan ("Machen, worauf es ankommt"); None wenn nicht erfasst
|
||||
pdf: str # Dateiname in static/referenzen/
|
||||
seiten: Optional[int] # PDF-Seitenzahl
|
||||
|
||||
|
||||
REFERENZEN_PATH = Path(__file__).parent / "static" / "referenzen"
|
||||
KONTEXT_PATH = Path(__file__).parent / "kontext"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Daten — alle 287 Programme (historisch + aktuell), sortiert nach
|
||||
# (typ, bundesland, gueltig_ab, partei). Auto-generiert aus der bisherigen
|
||||
# embeddings.PROGRAMME + WAHLPROGRAMME-Lazy-Migration via fitz für seiten.
|
||||
# Generator: tools/build_programme_literal.py (siehe Commit-Historie).
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
'''
|
||||
print(header)
|
||||
|
||||
# Literal
|
||||
print("PROGRAMME: dict[str, Programm] = {")
|
||||
last_gruppe = None
|
||||
for prog in results:
|
||||
typ = prog.get("typ", "?")
|
||||
bl = prog.get("bundesland") or "BUND"
|
||||
cur = (typ, bl)
|
||||
if cur != last_gruppe:
|
||||
print(f"\n # ─── {typ} · {bl} ───")
|
||||
last_gruppe = cur
|
||||
print(f' "{prog["id"]}": {fmt_entry(prog)},')
|
||||
print("}")
|
||||
|
||||
# Helper-API
|
||||
helpers = '''
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Helper-API
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _date_in_range(datum: str, ab: str, bis: Optional[str]) -> bool:
|
||||
"""Liefert True, wenn ``datum`` (ISO) in [ab, bis) liegt."""
|
||||
if datum < ab:
|
||||
return False
|
||||
if bis is None:
|
||||
return True
|
||||
return datum < bis
|
||||
|
||||
|
||||
def get_programm(programm_id: str) -> Optional[Programm]:
|
||||
"""Lookup nach ID."""
|
||||
return PROGRAMME.get(programm_id)
|
||||
|
||||
|
||||
def get_programm_by_pdf(pdf: str) -> Optional[Programm]:
|
||||
"""Reverse-Lookup über pdf-Dateinamen."""
|
||||
for prog in PROGRAMME.values():
|
||||
if prog.get("pdf") == pdf:
|
||||
return prog
|
||||
return None
|
||||
|
||||
|
||||
def aktuelles_wahlprogramm(bundesland: str, partei: str) -> Optional[Programm]:
|
||||
"""Aktuell gültiges Wahlprogramm einer Partei in einem Bundesland.
|
||||
|
||||
Es kann nur eines aktuell sein (gueltig_bis=None und typ=wahlprogramm).
|
||||
"""
|
||||
for prog in PROGRAMME.values():
|
||||
if (
|
||||
prog.get("typ") == "wahlprogramm"
|
||||
and prog.get("bundesland") == bundesland
|
||||
and prog.get("partei") == partei
|
||||
and prog.get("gueltig_bis") is None
|
||||
):
|
||||
return prog
|
||||
return None
|
||||
|
||||
|
||||
def wahlprogramm_zum_zeitpunkt(
|
||||
bundesland: str, partei: str, datum: str,
|
||||
) -> Optional[Programm]:
|
||||
"""Welches Wahlprogramm dieser Partei galt im Bundesland am gegebenen Datum?
|
||||
|
||||
``datum`` ist ISO-Datum. Es wird das Programm zurückgegeben, dessen
|
||||
Geltungszeitraum [gueltig_ab, gueltig_bis) das Datum enthält.
|
||||
"""
|
||||
for prog in PROGRAMME.values():
|
||||
if (
|
||||
prog.get("typ") == "wahlprogramm"
|
||||
and prog.get("bundesland") == bundesland
|
||||
and prog.get("partei") == partei
|
||||
and _date_in_range(datum, prog["gueltig_ab"], prog.get("gueltig_bis"))
|
||||
):
|
||||
return prog
|
||||
return None
|
||||
|
||||
|
||||
def grundsatzprogramm_zum_zeitpunkt(
|
||||
partei: str,
|
||||
datum: str,
|
||||
bundesland: Optional[str] = None,
|
||||
) -> Optional[Programm]:
|
||||
"""Welches Grundsatzprogramm der Partei galt am gegebenen Datum?
|
||||
|
||||
Wenn ``bundesland`` gesetzt ist, wird zuerst nach einem
|
||||
Landes-Grundsatzprogramm gesucht; falls keines existiert, fällt die
|
||||
Suche auf das Bundes-Grundsatzprogramm zurück.
|
||||
"""
|
||||
if bundesland is not None:
|
||||
for prog in PROGRAMME.values():
|
||||
if (
|
||||
prog.get("typ") == "grundsatzprogramm-land"
|
||||
and prog.get("partei") == partei
|
||||
and prog.get("bundesland") == bundesland
|
||||
and _date_in_range(datum, prog["gueltig_ab"], prog.get("gueltig_bis"))
|
||||
):
|
||||
return prog
|
||||
for prog in PROGRAMME.values():
|
||||
if (
|
||||
prog.get("typ") == "grundsatzprogramm-bund"
|
||||
and prog.get("partei") == partei
|
||||
and _date_in_range(datum, prog["gueltig_ab"], prog.get("gueltig_bis"))
|
||||
):
|
||||
return prog
|
||||
return None
|
||||
|
||||
|
||||
def parteien_mit_wahlprogramm(bundesland: str) -> list[str]:
|
||||
"""Parteien mit einem aktuell gültigen Wahlprogramm in dem Bundesland.
|
||||
|
||||
Reihenfolge: nach Eintrags-Reihenfolge in PROGRAMME (deterministic).
|
||||
"""
|
||||
seen: list[str] = []
|
||||
for prog in PROGRAMME.values():
|
||||
if (
|
||||
prog.get("typ") == "wahlprogramm"
|
||||
and prog.get("bundesland") == bundesland
|
||||
and prog.get("gueltig_bis") is None
|
||||
):
|
||||
partei = prog["partei"]
|
||||
if partei not in seen:
|
||||
seen.append(partei)
|
||||
return seen
|
||||
|
||||
|
||||
def alle_versionen(bundesland: str, partei: str) -> list[Programm]:
|
||||
"""Alle Wahlprogramm-Versionen dieser Partei im Bundesland, sortiert
|
||||
nach ``gueltig_ab`` aufsteigend."""
|
||||
versions = [
|
||||
prog for prog in PROGRAMME.values()
|
||||
if prog.get("typ") == "wahlprogramm"
|
||||
and prog.get("bundesland") == bundesland
|
||||
and prog.get("partei") == partei
|
||||
]
|
||||
versions.sort(key=lambda p: p["gueltig_ab"])
|
||||
return versions
|
||||
|
||||
|
||||
def all_programme() -> list[Programm]:
|
||||
"""Alle eingetragenen Programme."""
|
||||
return list(PROGRAMME.values())
|
||||
'''
|
||||
print(helpers)
|
||||
Loading…
Reference in New Issue
Block a user