diff --git a/app/embeddings.py b/app/embeddings.py index 5d52ccc..7ea17c9 100644 --- a/app/embeddings.py +++ b/app/embeddings.py @@ -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", diff --git a/app/programme.py b/app/programme.py index 5411cf9..d4f2409 100644 --- a/app/programme.py +++ b/app/programme.py @@ -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. diff --git a/app/static/referenzen/afd-nrw-2017.pdf b/app/static/referenzen/afd-nrw-2017.pdf new file mode 100644 index 0000000..4aaf451 Binary files /dev/null and b/app/static/referenzen/afd-nrw-2017.pdf differ diff --git a/app/static/referenzen/cdu-nrw-2017.pdf b/app/static/referenzen/cdu-nrw-2017.pdf new file mode 100644 index 0000000..9ed7b6c Binary files /dev/null and b/app/static/referenzen/cdu-nrw-2017.pdf differ diff --git a/app/static/referenzen/fdp-nrw-2017.pdf b/app/static/referenzen/fdp-nrw-2017.pdf new file mode 100644 index 0000000..933d00d Binary files /dev/null and b/app/static/referenzen/fdp-nrw-2017.pdf differ diff --git a/app/static/referenzen/gruene-nrw-2017.pdf b/app/static/referenzen/gruene-nrw-2017.pdf new file mode 100644 index 0000000..e2b0ef2 Binary files /dev/null and b/app/static/referenzen/gruene-nrw-2017.pdf differ diff --git a/app/static/referenzen/spd-nrw-2017.pdf b/app/static/referenzen/spd-nrw-2017.pdf new file mode 100644 index 0000000..98b918b Binary files /dev/null and b/app/static/referenzen/spd-nrw-2017.pdf differ diff --git a/app/wahlprogramm-links.yaml b/app/wahlprogramm-links.yaml index dd0be09..6ef258e 100644 --- a/app/wahlprogramm-links.yaml +++ b/app/wahlprogramm-links.yaml @@ -19,6 +19,35 @@ NRW: jahr: 2022 sha256: "" +# Landtagswahl NRW 14.05.2017 — Wahlperiode 17 (2017–2022). +# Recherche 2026-05-08. Liegen unter `app/static/referenzen/.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 2017–2022 (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 2017–2022 #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 diff --git a/app/wahlprogramm-shas.lock.json b/app/wahlprogramm-shas.lock.json index dccfa84..3fcd34c 100644 --- a/app/wahlprogramm-shas.lock.json +++ b/app/wahlprogramm-shas.lock.json @@ -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", diff --git a/tests/test_programme.py b/tests/test_programme.py index a1eb0d5..9cec6b6 100644 --- a/tests/test_programme.py +++ b/tests/test_programme.py @@ -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") == []