Sechs der zehn aktiven Bundesländer hatten bisher keine Wahlprogramme
indexiert (alle sechs heute neu aktiviert: BW/HH/TH in Phase 1, SH/BB/RP
in Phase 2). Antrag-Analysen für diese BL fielen damit auf föderale
Grundsatzprogramme als Fallback zurück.
Beschafft via abgeordnetenwatch.de für die jeweils laufende WP:
- TH WP8 (LTW 01.09.2024): CDU, AfD, LINKE, BSW, SPD — 5 PDFs
- BB WP8 (LTW 22.09.2024): SPD, AfD, CDU, BSW — 4 PDFs
- HH WP23 (Bürgerschaftswahl 02.03.2025): SPD, CDU, GRÜNE, LINKE, AfD — 5 PDFs
- SH WP20 (LTW 08.05.2022): CDU, SPD, GRÜNE, FDP, SSW — 5 PDFs
- BW WP17 (LTW 14.03.2021): GRÜNE, CDU, AfD, SPD, FDP — 5 PDFs
- RP WP18 (LTW 14.03.2021): SPD, CDU, AfD, GRÜNE, FREIE WÄHLER, FDP — 6 PDFs
Insgesamt 30 PDFs in app/static/referenzen/, plus 30 Einträge in
WAHLPROGRAMME[bl][partei] und embeddings.PROGRAMME.
Naming-Schema wie etabliert: <partei>-<bl>-<jahr>.pdf, also
spd-th-2024.pdf, fw-rp-2021.pdf etc.
Wichtig zu Memory feedback_legislaturprogramme: alle BL nutzen das
Programm der LAUFENDEN Wahlperiode, NICHT Programme aus späteren
Wahlen. BW und RP wählen am 08.03.2026 / 22.03.2026 neu — der
18./19. Landtag konstituiert sich erst, daher sind die 17./18. WP
mit den 2021er Programmen weiterhin laufend bis zur Konstituierung.
Indexierung im prod-Container ist NICHT Teil dieses Commits — muss
separat ausgeführt werden:
ssh vserver 'docker exec gwoe-antragspruefer python -c "
from app.embeddings import index_programm
from pathlib import Path
d = Path(\"/app/app/static/referenzen\")
for pid in [
\"cdu-th-2024\",\"afd-th-2024\",\"linke-th-2024\",\"bsw-th-2024\",\"spd-th-2024\",
\"spd-bb-2024\",\"afd-bb-2024\",\"cdu-bb-2024\",\"bsw-bb-2024\",
\"spd-hh-2025\",\"cdu-hh-2025\",\"gruene-hh-2025\",\"linke-hh-2025\",\"afd-hh-2025\",
\"cdu-sh-2022\",\"spd-sh-2022\",\"gruene-sh-2022\",\"fdp-sh-2022\",\"ssw-sh-2022\",
\"gruene-bw-2021\",\"cdu-bw-2021\",\"afd-bw-2021\",\"spd-bw-2021\",\"fdp-bw-2021\",
\"spd-rp-2021\",\"cdu-rp-2021\",\"afd-rp-2021\",\"gruene-rp-2021\",\"fw-rp-2021\",\"fdp-rp-2021\",
]:
index_programm(pid, d)
"'
77 pytest tests passing — der File-Existenz-Check in test_wahlprogramme.py
hätte einen Tippfehler im PDF-Namen sofort gefangen.
Erledigt UI-Aktivierungs-Issues #37 (TH), #39 (BB), #40 (HH), #32 (SH),
#41 (BW), #42 (RP).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
448 lines
19 KiB
Python
448 lines
19 KiB
Python
"""Wahlprogramm-Referenzsystem mit Zitaten und Seitenreferenzen.
|
||
|
||
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.
|
||
|
||
Verantwortlich für die schlüsselwortbasierte Fallback-Suche in den
|
||
paged-Textversionen der Wahlprogramme. Die semantische Suche lebt in
|
||
``embeddings.py``.
|
||
"""
|
||
|
||
import re
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
|
||
from .bundeslaender import BUNDESLAENDER
|
||
|
||
|
||
# WAHLPROGRAMME[bundesland][partei] -> Metadaten
|
||
# Beim Hinzufügen eines neuen Bundeslands: Eintrag hier UND parallel
|
||
# in WAHLPROGRAMM_KONTEXT_FILES.
|
||
WAHLPROGRAMME: dict[str, dict[str, dict]] = {
|
||
"NRW": {
|
||
"CDU": {
|
||
"file": "cdu-nrw-2022.pdf",
|
||
"titel": "Machen, worauf es ankommt",
|
||
"partei": "CDU NRW",
|
||
"jahr": 2022,
|
||
"seiten": 109,
|
||
},
|
||
"SPD": {
|
||
"file": "spd-nrw-2022.pdf",
|
||
"titel": "Unser Land von morgen",
|
||
"partei": "SPD NRW",
|
||
"jahr": 2022,
|
||
"seiten": 116,
|
||
},
|
||
"GRÜNE": {
|
||
"file": "gruene-nrw-2022.pdf",
|
||
"titel": "Von hier an Zukunft",
|
||
"partei": "BÜNDNIS 90/DIE GRÜNEN NRW",
|
||
"jahr": 2022,
|
||
"seiten": 100,
|
||
},
|
||
"FDP": {
|
||
"file": "fdp-nrw-2022.pdf",
|
||
"titel": "Nie gab es mehr zu tun",
|
||
"partei": "FDP NRW",
|
||
"jahr": 2022,
|
||
"seiten": 96,
|
||
},
|
||
"AfD": {
|
||
"file": "afd-nrw-2022.pdf",
|
||
"titel": "Wer sonst.",
|
||
"partei": "AfD NRW",
|
||
"jahr": 2022,
|
||
"seiten": 68,
|
||
},
|
||
},
|
||
# Sachsen-Anhalt — Wahlprogramme zur LTW 06.06.2021. Die aktuelle 8. WP
|
||
# (seit 07/2021) wird mit diesen Programmen analysiert.
|
||
"LSA": {
|
||
"CDU": {
|
||
"file": "cdu-lsa-2021.pdf",
|
||
"titel": "Unsere Heimat. Unsere Verantwortung.",
|
||
"partei": "CDU Sachsen-Anhalt",
|
||
"jahr": 2021,
|
||
"seiten": 82,
|
||
},
|
||
"SPD": {
|
||
"file": "spd-lsa-2021.pdf",
|
||
"titel": "Zusammenhalt und neue Chancen. Politik fürs ganze Land",
|
||
"partei": "SPD Sachsen-Anhalt",
|
||
"jahr": 2021,
|
||
"seiten": 77,
|
||
},
|
||
"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,
|
||
},
|
||
"FDP": {
|
||
"file": "fdp-lsa-2021.pdf",
|
||
"titel": "Wahlprogramm der FDP Sachsen-Anhalt zur Landtagswahl 2021",
|
||
"partei": "FDP Sachsen-Anhalt",
|
||
"jahr": 2021,
|
||
"seiten": 76,
|
||
},
|
||
"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,
|
||
},
|
||
"LINKE": {
|
||
"file": "linke-lsa-2021.pdf",
|
||
"titel": "Wahlprogramm zur Landtagswahl 2021",
|
||
"partei": "DIE LINKE Sachsen-Anhalt",
|
||
"jahr": 2021,
|
||
"seiten": 88,
|
||
},
|
||
},
|
||
# Mecklenburg-Vorpommern — Wahlprogramme zur LTW 26.09.2021. Die
|
||
# aktuelle 8. WP (seit 26.10.2021) wird mit diesen Programmen
|
||
# analysiert. Issue #4.
|
||
"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,
|
||
},
|
||
"SPD": {
|
||
"file": "spd-mv-2021.pdf",
|
||
"titel": "Verantwortung für heute und morgen — Regierungsprogramm 2021–2026",
|
||
"partei": "SPD Mecklenburg-Vorpommern",
|
||
"jahr": 2021,
|
||
"seiten": 95,
|
||
},
|
||
"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,
|
||
},
|
||
"FDP": {
|
||
"file": "fdp-mv-2021.pdf",
|
||
"titel": "Wahlprogramm der Freien Demokraten Mecklenburg-Vorpommern zur Landtagswahl 2021",
|
||
"partei": "FDP Mecklenburg-Vorpommern",
|
||
"jahr": 2021,
|
||
"seiten": 120,
|
||
},
|
||
"AfD": {
|
||
"file": "afd-mv-2021.pdf",
|
||
"titel": "Landeswahlprogramm der AfD Mecklenburg-Vorpommern 2021",
|
||
"partei": "AfD Mecklenburg-Vorpommern",
|
||
"jahr": 2021,
|
||
"seiten": 84,
|
||
},
|
||
"LINKE": {
|
||
"file": "linke-mv-2021.pdf",
|
||
"titel": "Das ist links! — Zukunftsprogramm für Mecklenburg-Vorpommern",
|
||
"partei": "DIE LINKE Mecklenburg-Vorpommern",
|
||
"jahr": 2021,
|
||
"seiten": 82,
|
||
},
|
||
},
|
||
# Berlin — Wahlprogramme zur Abgeordnetenhauswahl 2021 (am 26.09.2021,
|
||
# wiederholt am 12.02.2023). Die laufende 19. WP (seit 27.04.2023) wird
|
||
# mit den 2021er Programmen analysiert, weil die Parteien zur
|
||
# Wiederholungswahl mit denselben Programmen angetreten sind. Issue #10.
|
||
"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,
|
||
},
|
||
"SPD": {
|
||
"file": "spd-be-2023.pdf",
|
||
"titel": "Ganz sicher Berlin — Wahlprogramm der SPD Berlin zur Abgeordnetenhauswahl 2021",
|
||
"partei": "SPD Berlin",
|
||
"jahr": 2021,
|
||
"seiten": 86,
|
||
},
|
||
"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,
|
||
},
|
||
"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,
|
||
},
|
||
"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,
|
||
},
|
||
},
|
||
# Thüringen — Wahlprogramme zur LTW 01.09.2024 (WP 8 seit 01.10.2024).
|
||
# Brombeer-Koalition CDU+BSW+SPD als Minderheit. Issue #37.
|
||
"TH": {
|
||
"CDU": {"file": "cdu-th-2024.pdf", "titel": "Wahlprogramm der CDU Thüringen 2024", "partei": "CDU Thüringen", "jahr": 2024, "seiten": 83},
|
||
"AfD": {"file": "afd-th-2024.pdf", "titel": "AfD Thüringen Landtagswahlprogramm 2024", "partei": "AfD Thüringen", "jahr": 2024, "seiten": 100},
|
||
"LINKE": {"file": "linke-th-2024.pdf", "titel": "DIE LINKE Thüringen Wahlprogramm 2024", "partei": "DIE LINKE Thüringen", "jahr": 2024, "seiten": 100},
|
||
"BSW": {"file": "bsw-th-2024.pdf", "titel": "BSW Thüringen Wahlprogramm 2024", "partei": "BSW Thüringen", "jahr": 2024, "seiten": 50},
|
||
"SPD": {"file": "spd-th-2024.pdf", "titel": "SPD Thüringen Wahlprogramm 2024", "partei": "SPD Thüringen", "jahr": 2024, "seiten": 100},
|
||
},
|
||
# Brandenburg — Wahlprogramme zur LTW 22.09.2024 (WP 8 seit 23.10.2024).
|
||
# Kabinett Woidke IV (SPD-BSW). Issue #39.
|
||
"BB": {
|
||
"SPD": {"file": "spd-bb-2024.pdf", "titel": "SPD Brandenburg Wahlprogramm 2024", "partei": "SPD Brandenburg", "jahr": 2024, "seiten": 100},
|
||
"AfD": {"file": "afd-bb-2024.pdf", "titel": "AfD Brandenburg Wahlprogramm 2024", "partei": "AfD Brandenburg", "jahr": 2024, "seiten": 100},
|
||
"CDU": {"file": "cdu-bb-2024.pdf", "titel": "CDU Brandenburg Wahlprogramm 2024", "partei": "CDU Brandenburg", "jahr": 2024, "seiten": 100},
|
||
"BSW": {"file": "bsw-bb-2024.pdf", "titel": "BSW Brandenburg Wahlprogramm 2024", "partei": "BSW Brandenburg", "jahr": 2024, "seiten": 50},
|
||
},
|
||
# Hamburg — Wahlprogramme zur Bürgerschaftswahl 02.03.2025 (WP 23 seit 26.03.2025).
|
||
# Senat Tschentscher III (SPD-GRÜNE). Issue #40.
|
||
"HH": {
|
||
"SPD": {"file": "spd-hh-2025.pdf", "titel": "SPD Hamburg Wahlprogramm Bürgerschaftswahl 2025", "partei": "SPD Hamburg", "jahr": 2025, "seiten": 100},
|
||
"CDU": {"file": "cdu-hh-2025.pdf", "titel": "CDU Hamburg Wahlprogramm Bürgerschaftswahl 2025", "partei": "CDU Hamburg", "jahr": 2025, "seiten": 100},
|
||
"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},
|
||
"LINKE": {"file": "linke-hh-2025.pdf", "titel": "DIE LINKE Hamburg Wahlprogramm Bürgerschaftswahl 2025", "partei": "DIE LINKE Hamburg", "jahr": 2025, "seiten": 100},
|
||
"AfD": {"file": "afd-hh-2025.pdf", "titel": "AfD Hamburg Wahlprogramm Bürgerschaftswahl 2025", "partei": "AfD Hamburg", "jahr": 2025, "seiten": 100},
|
||
},
|
||
# Schleswig-Holstein — Wahlprogramme zur LTW 08.05.2022 (WP 20 seit 07.06.2022).
|
||
# Kabinett Günther II (CDU-GRÜNE). Issue #32.
|
||
"SH": {
|
||
"CDU": {"file": "cdu-sh-2022.pdf", "titel": "CDU Schleswig-Holstein Wahlprogramm 2022", "partei": "CDU Schleswig-Holstein", "jahr": 2022, "seiten": 100},
|
||
"SPD": {"file": "spd-sh-2022.pdf", "titel": "SPD Schleswig-Holstein Wahlprogramm 2022", "partei": "SPD Schleswig-Holstein", "jahr": 2022, "seiten": 100},
|
||
"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},
|
||
"FDP": {"file": "fdp-sh-2022.pdf", "titel": "FDP Schleswig-Holstein Wahlprogramm 2022", "partei": "FDP Schleswig-Holstein", "jahr": 2022, "seiten": 100},
|
||
"SSW": {"file": "ssw-sh-2022.pdf", "titel": "SSW Schleswig-Holstein Wahlprogramm 2022", "partei": "SSW", "jahr": 2022, "seiten": 80},
|
||
},
|
||
# Baden-Württemberg — Wahlprogramme zur LTW 14.03.2021 (WP 17, läuft bis Konstituierung
|
||
# des 18. Landtags nach 08.03.2026). Kabinett Kretschmann III (GRÜNE-CDU) noch
|
||
# geschäftsführend. Issue #41.
|
||
"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},
|
||
"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},
|
||
"AfD": {"file": "afd-bw-2021.pdf", "titel": "AfD Baden-Württemberg Landtagswahlprogramm 2021", "partei": "AfD Baden-Württemberg", "jahr": 2021, "seiten": 100},
|
||
"SPD": {"file": "spd-bw-2021.pdf", "titel": "SPD Baden-Württemberg Wahlprogramm zur Landtagswahl 2021", "partei": "SPD Baden-Württemberg", "jahr": 2021, "seiten": 100},
|
||
"FDP": {"file": "fdp-bw-2021.pdf", "titel": "FDP Baden-Württemberg Landtagswahlprogramm 2021", "partei": "FDP Baden-Württemberg", "jahr": 2021, "seiten": 100},
|
||
},
|
||
# Rheinland-Pfalz — Wahlprogramme zur LTW 14.03.2021 (WP 18, läuft bis Konstituierung
|
||
# des 19. Landtags nach 22.03.2026). Kabinett Schweitzer I (SPD-GRÜNE-FDP) noch
|
||
# geschäftsführend. Issue #42.
|
||
"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},
|
||
"CDU": {"file": "cdu-rp-2021.pdf", "titel": "Regierungsprogramm der CDU RLP 2021–26", "partei": "CDU Rheinland-Pfalz", "jahr": 2021, "seiten": 100},
|
||
"AfD": {"file": "afd-rp-2021.pdf", "titel": "AfD Rheinland-Pfalz Wahlprogramm 2021", "partei": "AfD Rheinland-Pfalz", "jahr": 2021, "seiten": 100},
|
||
"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},
|
||
"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},
|
||
"FDP": {"file": "fdp-rp-2021.pdf", "titel": "FDP Rheinland-Pfalz Landtagswahlprogramm 2021", "partei": "FDP Rheinland-Pfalz", "jahr": 2021, "seiten": 100},
|
||
},
|
||
}
|
||
|
||
# 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] = {
|
||
"NRW": "wahlprogramme-nrw-2022.md",
|
||
}
|
||
|
||
REFERENZEN_PATH = Path(__file__).parent / "static" / "referenzen"
|
||
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)
|
||
|
||
|
||
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 load_wahlprogramm_text(bundesland: str, partei: str) -> dict[int, str]:
|
||
"""Lädt Wahlprogramm-Text mit Seitenzuordnung.
|
||
|
||
Returns:
|
||
Dict mit Seitennummer -> Text. Leer, wenn kein Wahlprogramm hinterlegt
|
||
oder die paged-Textdatei fehlt.
|
||
"""
|
||
info = get_wahlprogramm(bundesland, partei)
|
||
if not info:
|
||
return {}
|
||
|
||
# Versuche paged-Textdatei zu laden
|
||
paged_file = KONTEXT_PATH / info['file'].replace('.pdf', '-paged.txt')
|
||
if not paged_file.exists():
|
||
# Fallback: Normale Textdatei
|
||
txt_file = KONTEXT_PATH / info['file'].replace('.pdf', '.txt')
|
||
if txt_file.exists():
|
||
return {1: txt_file.read_text()}
|
||
return {}
|
||
|
||
text = paged_file.read_text()
|
||
pages = {}
|
||
current_page = 1
|
||
current_text = []
|
||
|
||
for line in text.split('\n'):
|
||
if line.startswith('--- PAGE '):
|
||
if current_text:
|
||
pages[current_page] = '\n'.join(current_text)
|
||
match = re.search(r'PAGE (\d+)', line)
|
||
if match:
|
||
current_page = int(match.group(1))
|
||
current_text = []
|
||
else:
|
||
current_text.append(line)
|
||
|
||
if current_text:
|
||
pages[current_page] = '\n'.join(current_text)
|
||
|
||
return pages
|
||
|
||
|
||
def search_wahlprogramm(
|
||
bundesland: str,
|
||
partei: str,
|
||
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
|
||
|
||
Returns:
|
||
Liste von {bundesland, partei, seite, text, score, url, quelle}
|
||
"""
|
||
info = get_wahlprogramm(bundesland, partei)
|
||
if not info:
|
||
return []
|
||
|
||
pages = load_wahlprogramm_text(bundesland, partei)
|
||
if not pages:
|
||
return []
|
||
|
||
results = []
|
||
keywords_lower = [k.lower() for k in keywords]
|
||
|
||
for page_num, text in pages.items():
|
||
text_lower = text.lower()
|
||
score = sum(1 for kw in keywords_lower if kw in text_lower)
|
||
|
||
if score > 0:
|
||
paragraphs = text.split('\n\n')
|
||
relevant_paragraphs = []
|
||
|
||
for para in paragraphs:
|
||
para_clean = para.strip()
|
||
if len(para_clean) < 50:
|
||
continue
|
||
para_lower = para_clean.lower()
|
||
if any(kw in para_lower for kw in keywords_lower):
|
||
relevant_paragraphs.append(para_clean)
|
||
|
||
if relevant_paragraphs:
|
||
best_para = max(
|
||
relevant_paragraphs,
|
||
key=lambda p: sum(1 for kw in keywords_lower if kw in p.lower()),
|
||
)
|
||
if len(best_para) > 300:
|
||
best_para = best_para[:297] + "..."
|
||
|
||
results.append({
|
||
"partei": partei,
|
||
"bundesland": bundesland,
|
||
"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}",
|
||
})
|
||
|
||
results.sort(key=lambda x: x['score'], reverse=True)
|
||
return results[:max_results]
|
||
|
||
|
||
def find_relevant_quotes(
|
||
antrag_text: str,
|
||
fraktionen: list[str],
|
||
bundesland: str,
|
||
) -> dict[str, list[dict]]:
|
||
"""Findet relevante Zitate aus Wahlprogrammen für einen Antrag.
|
||
|
||
Args:
|
||
antrag_text: Volltext des Antrags
|
||
fraktionen: Liste der einreichenden Fraktionen
|
||
bundesland: Bundesland-Code (Pflichtparameter; bestimmt, welche
|
||
Wahlprogramme durchsucht werden und welche Regierungsfraktionen
|
||
zusätzlich einbezogen werden).
|
||
|
||
Returns:
|
||
Dict mit Partei -> Liste von Zitaten
|
||
"""
|
||
if bundesland not in BUNDESLAENDER:
|
||
raise ValueError(f"Unbekanntes Bundesland: {bundesland}")
|
||
|
||
# Extrahiere Keywords aus Antrag (einfache Heuristik)
|
||
stopwords = {
|
||
'der', 'die', 'das', 'und', 'oder', 'für', 'mit', 'von', 'zu', 'auf',
|
||
'ist', 'sind', 'wird', 'werden', 'hat', 'haben', 'ein', 'eine', 'einer',
|
||
'den', 'dem', 'des', 'im', 'in', 'an', 'bei', 'nach', 'über', 'unter',
|
||
'durch', 'als', 'auch', 'nur', 'noch', 'aber', 'wenn', 'dass', 'sich',
|
||
'nicht', 'wie', 'so', 'aus', 'zum', 'zur', 'vom', 'beim', 'seit', 'bis',
|
||
}
|
||
|
||
words = re.findall(r'\b[A-Za-zäöüÄÖÜß]{4,}\b', antrag_text)
|
||
keywords = [w for w in words if w.lower() not in stopwords]
|
||
|
||
word_freq: dict[str, int] = {}
|
||
for w in keywords:
|
||
w_lower = w.lower()
|
||
word_freq[w_lower] = word_freq.get(w_lower, 0) + 1
|
||
|
||
top_keywords = sorted(word_freq.keys(), key=lambda x: word_freq[x], reverse=True)[:15]
|
||
|
||
# Antragsteller + Regierungsfraktionen des Bundeslands
|
||
regierungsfraktionen = BUNDESLAENDER[bundesland].regierungsfraktionen
|
||
parteien_to_search = set(fraktionen) | set(regierungsfraktionen)
|
||
|
||
quotes: dict[str, list[dict]] = {}
|
||
for partei in parteien_to_search:
|
||
if get_wahlprogramm(bundesland, partei):
|
||
found = search_wahlprogramm(bundesland, partei, top_keywords, max_results=2)
|
||
if found:
|
||
quotes[partei] = found
|
||
|
||
return quotes
|
||
|
||
|
||
def format_quote_for_prompt(quotes: dict[str, list[dict]]) -> str:
|
||
"""Formatiert Zitate für den LLM-Prompt."""
|
||
if not quotes:
|
||
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("")
|
||
|
||
return "\n".join(lines)
|