feat: Block 2.1 — NRW WP17 historische Wahlprogramme indiziert (Pilot)

5 Programme zur LTW NRW 14.05.2017 als historische Wahlprogramme im
Embeddings-Index — erster Datensatz für die zeitpunktige Bewertung
historischer Antraege:

- cdu-nrw-2017 (Laschet, 120 S., 172 chunks)
- spd-nrw-2017 (Kraft, 116 S., 169 chunks)
- gruene-nrw-2017 (131 S., 322 chunks)
- fdp-nrw-2017 (Lindner, 56 S., 92 chunks)
- afd-nrw-2017 (84 S., 78 chunks)

Geltungszeitraum 2017-05-14 (Wahltag WP17) bis 2022-05-15 (Wahltag
WP18, exklusiv). Eintraege liegen NUR in embeddings.PROGRAMME — die
WAHLPROGRAMME[NRW]-Struktur bleibt single-current (cdu-nrw-2022).

programme._migrate_from_legacy hat einen neuen Schritt 2b, der
typ=wahlprogramm-Eintraege aus embeddings.PROGRAMME mit explizitem
gueltig_ab/_bis als historische Wahlprogramme registriert. Damit
liefert wahlprogramm_zum_zeitpunkt() jetzt fuer NRW-Antraege aus dem
Zeitraum 2017-2022 das passende Programm.

Live-Verifikation auf gwoe-antragspruefer-dev:
- 2018-09-01 -> cdu-nrw-2017 (WP17)
- 2024-01-01 -> cdu-nrw-2022 (WP18)
- Grenze: 14.05.2022 -> WP17, 15.05.2022 -> WP18

Tests: 116 gruen, plus neue test_grenze_zwischen_wp17_und_wp18 und
angepasstes test_datum_vor_aktueller_wp_nrw_wp17.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dotty Dotter 2026-05-08 09:44:26 +02:00
parent b003cc1d6d
commit 445fcc90ca
10 changed files with 148 additions and 10 deletions

View File

@ -66,6 +66,59 @@ PROGRAMME = {
"bundesland": "NRW",
"pdf": "afd-nrw-2022.pdf",
},
# NRW WP17 — historisch (LTW 14.05.2017, Geltung bis Wahl WP18 am
# 15.05.2022). Programme deckten Kabinette Laschet I (CDU+FDP) +
# Wüst I (CDU+FDP, ab 2021-10-27).
"cdu-nrw-2017": {
"name": "CDU NRW Regierungsprogramm 2017",
"typ": "wahlprogramm",
"partei": "CDU",
"bundesland": "NRW",
"pdf": "cdu-nrw-2017.pdf",
"gueltig_ab": "2017-05-14",
"gueltig_bis": "2022-05-15",
"wp": 17,
},
"spd-nrw-2017": {
"name": "SPD NRW Regierungsprogramm 2017",
"typ": "wahlprogramm",
"partei": "SPD",
"bundesland": "NRW",
"pdf": "spd-nrw-2017.pdf",
"gueltig_ab": "2017-05-14",
"gueltig_bis": "2022-05-15",
"wp": 17,
},
"gruene-nrw-2017": {
"name": "Grüne NRW Wahlprogramm 2017",
"typ": "wahlprogramm",
"partei": "GRÜNE",
"bundesland": "NRW",
"pdf": "gruene-nrw-2017.pdf",
"gueltig_ab": "2017-05-14",
"gueltig_bis": "2022-05-15",
"wp": 17,
},
"fdp-nrw-2017": {
"name": "FDP NRW Wahlprogramm 2017",
"typ": "wahlprogramm",
"partei": "FDP",
"bundesland": "NRW",
"pdf": "fdp-nrw-2017.pdf",
"gueltig_ab": "2017-05-14",
"gueltig_bis": "2022-05-15",
"wp": 17,
},
"afd-nrw-2017": {
"name": "AfD NRW Wahlprogramm 2017",
"typ": "wahlprogramm",
"partei": "AfD",
"bundesland": "NRW",
"pdf": "afd-nrw-2017.pdf",
"gueltig_ab": "2017-05-14",
"gueltig_bis": "2022-05-15",
"wp": 17,
},
# Sachsen-Anhalt (LTW 2021)
"cdu-lsa-2021": {
"name": "CDU Sachsen-Anhalt Regierungsprogramm 2021",

View File

@ -413,7 +413,7 @@ def _migrate_from_legacy() -> None:
# nur unterscheidet sich der Geltungsbereich.
for pid, info in _EMB_PROGRAMME.items():
if info.get("typ") != "parteiprogramm":
continue # Wahlprogramme schon in Schritt 1
continue # Wahlprogramme schon in Schritt 1 + Schritt 2b
if pid in PROGRAMME:
continue
bl = info.get("bundesland")
@ -435,6 +435,43 @@ def _migrate_from_legacy() -> 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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -19,6 +19,35 @@ NRW:
jahr: 2022
sha256: ""
# Landtagswahl NRW 14.05.2017 — Wahlperiode 17 (20172022).
# Recherche 2026-05-08. Liegen unter `app/static/referenzen/<datei>.pdf`.
NRW_WP17:
CDU:
- url: https://www.cdu-nrw.de/sites/www.neu.cdu-nrw.de/files/downloads/2017-04-01_regierungsprogramm_cdu_fuer_nrw_2017-2022.pdf
titel: "Zuhören. Entscheiden. Handeln. — Regierungsprogramm der CDU für Nordrhein-Westfalen 20172022 (Beschluss 39. Landesparteitag, 1. April 2017, Münster)"
jahr: 2017
sha256: "3aabd097244938cff7d79359b32dc3faa08c351a77fb51beb8534e02df85c7ed"
SPD:
- url: https://library.fes.de/pdf-files/bibliothek/ltw-programme/regierungsprogramm_der_nrwspd_7822683.pdf
titel: "Der NRW-Plan — Unser Programm für 20172022 #NRWir (Regierungsprogramm der NRWSPD; Mirror Friedrich-Ebert-Stiftung)"
jahr: 2017
sha256: "487e80ac60ce33176b892fe22abddd34a3f50a67f07e9c205ac0182cd8e42e15"
GRÜNE:
- url: https://web.archive.org/web/20190527233611if_/https://www.gruene-nrw.de/dateien/wahlprogramm2017.pdf
titel: "Wahl. Programm. — BÜNDNIS 90/DIE GRÜNEN NRW zur Landtagswahl am 14. Mai 2017 (Wayback-Mirror, Original gruene-nrw.de offline)"
jahr: 2017
sha256: "4b3de49336707a39f94749c301e7e500489e3855eeea1999a86cc261e8a3355a"
FDP:
- url: https://www.fdp.nrw/sites/default/files/2022-03/Landtagswahlprogramm2017_2.pdf
titel: "Landtagswahlprogramm 2017 der Freien Demokraten Nordrhein-Westfalen (Spitzenkandidat Christian Lindner)"
jahr: 2017
sha256: "b15aba0ce2c4942d48c9c5eb808d276ca2f589b458677f92ec557412d65e64d5"
AfD:
- url: https://www.wahlprogramme-vergleichen.de/media/programme/afd_landtagswahl_nrw_2017.pdf
titel: "Wahlprogramm der AfD für die Bürger Nordrhein-Westfalens — Landtagswahl 2017 (Mirror wahlprogramme-vergleichen.de)"
jahr: 2017
sha256: "74c613a4683f1ddca8e0966d491696c9b9d839e3833582f574e6bc7614f2f21b"
TH:
FDP:
- url: https://www.fdp-thueringen.de/files/fdp-th-wahlprogramm-2024.pdf

View File

@ -7,6 +7,7 @@
"afd-hh-2025.pdf": "6aae3ad00cd07824bcd99473e130d1b894e2174a89fcafece51865c51fdcd4c8",
"afd-lsa-2021.pdf": "dd2651af2a9423039b1c5a39760be2332025d569a878453f09e0302e252edc23",
"afd-mv-2021.pdf": "953c39941a1f997233daaf0cec01bc82b1e86ba895b43e8d34b015cc72799648",
"afd-nrw-2017.pdf": "74c613a4683f1ddca8e0966d491696c9b9d839e3833582f574e6bc7614f2f21b",
"afd-nrw-2022.pdf": "36c4bc55c3239e3f7e69568e19d7f074ce2f1cf018653d493767ec09df637282",
"afd-rp-2021.pdf": "3ec39eb08a073244813a51f260e18fe52aab791bea26bf8079546b6e189ec2b3",
"afd-th-2024.pdf": "26e61fdc3456e7ce18f7a3d2ea1eada303f93cad0b9698797f83a671574eaf51",
@ -24,6 +25,7 @@
"cdu-hh-2025.pdf": "8d29e514b8bce5c2f3f497dc5b97f6f8ab95a7bdbf619abf258e9582d57f2dbd",
"cdu-lsa-2021.pdf": "63b6cf42ce97834d5d105fb7b8cc7fb7a2aa96928d4153bd3a5858c196ee0797",
"cdu-mv-2021.pdf": "605a2211bef8666c2103771ebffd97a088e7cdb1545401087ef125155e7e4db2",
"cdu-nrw-2017.pdf": "3aabd097244938cff7d79359b32dc3faa08c351a77fb51beb8534e02df85c7ed",
"cdu-nrw-2022.pdf": "49d97a6f30fbacad3a0b770c182ed0527bc5d347dc4cacd65f85e7e4e9644566",
"cdu-rp-2021.pdf": "54c50d88bdf5c5f7dee5abcc981ffb4d1cfd5c86fbf2a29f4f2f4a8a3dd4797a",
"cdu-sh-2022.pdf": "39b79a22e904b300cf1bbc25752b618195683c90c31e6b10c3bc0e8408aa6a1a",
@ -35,6 +37,7 @@
"fdp-grundsatzprogramm.pdf": "9abb6570c3505271b1f43db2a8340c05b970a7934d0c1b2542403025affe2b13",
"fdp-lsa-2021.pdf": "3d4275e36e29c0b191dcc4a29061a1072920f868cc52bee954bf81491ad15224",
"fdp-mv-2021.pdf": "8dc341dd017f1d82c51608a26e1fd6c3d8acd1281dc37409e375389999b37b55",
"fdp-nrw-2017.pdf": "b15aba0ce2c4942d48c9c5eb808d276ca2f589b458677f92ec557412d65e64d5",
"fdp-nrw-2022.pdf": "576b42a26c29ca5d8b7469d417ae709c8d0699aed5195d4ca16dd696dcff8bea",
"fdp-rp-2021.pdf": "fba792d8d43842f33ae8f0aa94b0d4e50838908c217402b4c5cb4707f958e1ae",
"fdp-sh-2022.pdf": "4c49da411bb3c8e008f4b57dd20dc005104515b56056ff746cf5403529728d09",
@ -47,6 +50,7 @@
"gruene-hh-2025.pdf": "4428d1cdc16b4e74588f0bd51145ab7371f9e0871a2fc9d25a1f94e4f5aeb662",
"gruene-lsa-2021.pdf": "7b5cea92cd600283d7edf18dc0d358c0b7d78d7269589d9ef05de7d5f8b35998",
"gruene-mv-2021.pdf": "40f0070743ef9ae7808cab319234b4c83faa53a8a098ba8a82f28023bee4d9f6",
"gruene-nrw-2017.pdf": "4b3de49336707a39f94749c301e7e500489e3855eeea1999a86cc261e8a3355a",
"gruene-nrw-2022.pdf": "2d7eaf2f4b73e0b7cdccf8641208b86d306b654ead5706d72c446965f82e5769",
"gruene-rp-2021.pdf": "4fd68629d1560c28d61b2b913fd20ce6ad9a76b22823fd8496e51bfaf70dc19c",
"gruene-sh-2022.pdf": "62870c948c9e05663125b051d3a6401d63952ea6a64e4140dcece7bd1b1aea52",
@ -65,6 +69,7 @@
"spd-hh-2025.pdf": "5e8c57969cb3b159b9299c173831f7863ab81bd206c2a87ae232ba96f23156ee",
"spd-lsa-2021.pdf": "59140aa1921ab0ee85142d74e1d72b1af7254da3f7870a30460abd605d280333",
"spd-mv-2021.pdf": "c8c671c2e60f1a4f8048bd74e379eb8edc69ab2daeb09581fe83f25f6c87d529",
"spd-nrw-2017.pdf": "487e80ac60ce33176b892fe22abddd34a3f50a67f07e9c205ac0182cd8e42e15",
"spd-nrw-2022.pdf": "6f1375add74a532cb084dee10c3e5a6215e7d4118ddd26ef0d27bf39765d19a6",
"spd-rp-2021.pdf": "13966815b8870b30e3480673437634fb90882bf5410c652694a6579492e32707",
"spd-sh-2022.pdf": "3acd3ed6c42a0e0a8f49abd76610b536c7d5fdf13fcc4499e391bc9b1a3d0f0f",

View File

@ -133,12 +133,24 @@ class TestWahlprogrammZumZeitpunkt:
p = wahlprogramm_zum_zeitpunkt("BUND", "SPD", "2024-01-01")
assert p is None
def test_datum_vor_aktueller_wp(self):
# Antrag aus 2018 in NRW: aktuelle WP18-Programm gilt erst ab
# Wahltag 2022-05-15.
def test_datum_vor_aktueller_wp_nrw_wp17(self):
# Antrag aus 2018 in NRW: WP17-Programm (cdu-nrw-2017) gilt
# ab 2017-05-14 bis 2022-05-15.
p = wahlprogramm_zum_zeitpunkt("NRW", "CDU", "2018-09-01")
# Heute keine WP17-Programme indiziert → erwarten None.
assert p is None
assert p is not None
assert p["id"] == "cdu-nrw-2017"
assert p["wp"] == 17
assert p["gueltig_ab"] == "2017-05-14"
assert p["gueltig_bis"] == "2022-05-15"
def test_grenze_zwischen_wp17_und_wp18(self):
# Genau am Wahltag der nächsten WP (2022-05-15) gilt das neue
# Programm. range = [gueltig_ab, gueltig_bis), also gueltig_bis
# selbst ist exklusiv.
p_alt = wahlprogramm_zum_zeitpunkt("NRW", "CDU", "2022-05-14")
assert p_alt["id"] == "cdu-nrw-2017"
p_neu = wahlprogramm_zum_zeitpunkt("NRW", "CDU", "2022-05-15")
assert p_neu["id"] == "cdu-nrw-2022"
# ─────────────────────────────────────────────────────────────────────────────
@ -207,11 +219,13 @@ class TestParteienMitWahlprogramm:
# ─────────────────────────────────────────────────────────────────────────────
class TestAlleVersionen:
def test_nrw_cdu_eine_version(self):
# Heute nur cdu-nrw-2022 indiziert.
def test_nrw_cdu_zwei_versionen(self):
# WP17 (2017) + WP18 (2022) sind indiziert.
versions = alle_versionen("NRW", "CDU")
assert len(versions) == 1
assert versions[0]["id"] == "cdu-nrw-2022"
assert len(versions) == 2
# sortiert nach gueltig_ab aufsteigend
assert versions[0]["id"] == "cdu-nrw-2017"
assert versions[1]["id"] == "cdu-nrw-2022"
def test_unknown_bl_leer(self):
assert alle_versionen("XX", "CDU") == []