gwoe-antragspruefer/app/wahlprogramme.py

258 lines
8.3 KiB
Python
Raw Normal View History

Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
"""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
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
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,
},
},
}
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
# 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"
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
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.
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
Returns:
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
Dict mit Seitennummer -> Text. Leer, wenn kein Wahlprogramm hinterlegt
oder die paged-Textdatei fehlt.
"""
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
info = get_wahlprogramm(bundesland, partei)
if not info:
return {}
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
# Versuche paged-Textdatei zu laden
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
paged_file = KONTEXT_PATH / info['file'].replace('.pdf', '-paged.txt')
if not paged_file.exists():
# Fallback: Normale Textdatei
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
txt_file = KONTEXT_PATH / info['file'].replace('.pdf', '.txt')
if txt_file.exists():
return {1: txt_file.read_text()}
return {}
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
text = paged_file.read_text()
pages = {}
current_page = 1
current_text = []
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
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)
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
if current_text:
pages[current_page] = '\n'.join(current_text)
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
return pages
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
def search_wahlprogramm(
bundesland: str,
partei: str,
keywords: list[str],
max_results: int = 3,
) -> list[dict]:
"""Sucht relevante Passagen in einem Wahlprogramm.
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
Args:
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
bundesland: Bundesland-Code (NRW, LSA, )
partei: Partei-Kürzel (CDU, SPD, GRÜNE, FDP, AfD, )
keywords: Suchbegriffe
max_results: Maximale Anzahl Ergebnisse
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
Returns:
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
Liste von {bundesland, partei, seite, text, score, url, quelle}
"""
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
info = get_wahlprogramm(bundesland, partei)
if not info:
return []
pages = load_wahlprogramm_text(bundesland, partei)
if not pages:
return []
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
results = []
keywords_lower = [k.lower() for k in keywords]
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
for page_num, text in pages.items():
text_lower = text.lower()
score = sum(1 for kw in keywords_lower if kw in text_lower)
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
if score > 0:
paragraphs = text.split('\n\n')
relevant_paragraphs = []
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
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)
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
if relevant_paragraphs:
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
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] + "..."
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
results.append({
"partei": partei,
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
"bundesland": bundesland,
"seite": page_num,
"text": best_para,
"score": score,
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
"url": f"/static/referenzen/{info['file']}#page={page_num}",
"quelle": f"{info['partei']} Wahlprogramm {info['jahr']}, S. {page_num}",
})
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
results.sort(key=lambda x: x['score'], reverse=True)
return results[:max_results]
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
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.
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
Args:
antrag_text: Volltext des Antrags
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
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
"""
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
if bundesland not in BUNDESLAENDER:
raise ValueError(f"Unbekanntes Bundesland: {bundesland}")
# Extrahiere Keywords aus Antrag (einfache Heuristik)
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
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]
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
word_freq: dict[str, int] = {}
for w in keywords:
w_lower = w.lower()
word_freq[w_lower] = word_freq.get(w_lower, 0) + 1
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
top_keywords = sorted(word_freq.keys(), key=lambda x: word_freq[x], reverse=True)[:15]
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
# 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:
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
if get_wahlprogramm(bundesland, partei):
found = search_wahlprogramm(bundesland, partei, top_keywords, max_results=2)
if found:
quotes[partei] = found
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
return quotes
def format_quote_for_prompt(quotes: dict[str, list[dict]]) -> str:
"""Formatiert Zitate für den LLM-Prompt."""
if not quotes:
return ""
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
lines = ["\n## Relevante Passagen aus Wahlprogrammen\n"]
lines.append("Nutze diese Originalzitate als Belege in deiner Bewertung:\n")
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
for partei, zitate in quotes.items():
for z in zitate:
lines.append(f"### {z['quelle']}")
lines.append(f'> "{z["text"]}"')
lines.append("")
Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) Atomic refactor of the three modules that previously hardcoded NRW behaviour. After this commit, every analysis path consults the central BUNDESLAENDER registry for governing fractions, parliament name, and state metadata. wahlprogramme.py - WAHLPROGRAMME is now nested {bundesland: {partei: meta}}; NRW data hoisted unchanged under the "NRW" key. - New WAHLPROGRAMM_KONTEXT_FILES dict maps a state to its overview markdown file (currently only NRW). - find_relevant_quotes(text, fraktionen, bundesland) — bundesland is now a required positional. Governing fractions for the requested state are merged with the submitting fractions before lookup. - Helpers get_wahlprogramm() and parteien_mit_wahlprogramm() expose the new shape to other modules. - ValueError on unknown bundesland (no silent fallback). embeddings.py - Schema migration in init_embeddings_db: adds a `bundesland` column to the chunks table when missing, plus an index, and backfills existing rows from the PROGRAMME registry. Grundsatzprogramme (federal level) keep bundesland NULL by design. - find_relevant_chunks accepts a bundesland filter that matches state rows OR NULL — so federal Grundsatzprogramme remain visible to every analysis. - get_relevant_quotes_for_antrag(text, fraktionen, bundesland, …) — bundesland required, governing fractions read from BUNDESLAENDER instead of hardcoded ["CDU","GRÜNE"]. Order-preserving dedup replaces the previous set-based merge. - index_programm now writes the bundesland column on insert. - Dropped the hardcoded "Wahlprogramm NRW 2022" label in format_quotes_for_prompt — bundesland context is implicit in the surrounding prompt block. analyzer.py - get_bundesland_context reads parlament_name, regierungsfraktionen, landtagsfraktionen and the optional WAHLPROGRAMM_KONTEXT_FILES entry from the central registry. Throws ValueError on unknown OR inactive bundesland — kills the silent NRW fallback that previously masked configuration gaps. - The Antragsteller-detection heuristic now iterates BUNDESLAENDER[bundesland].landtagsfraktionen instead of WAHLPROGRAMME.keys(), so we recognise parties for which we don't yet have a Wahlprogramm PDF. - Both quote lookups (semantic + keyword fallback) now receive the bundesland. Resolves issue #5. Foundation for #2 (LSA), #3 (Berlin), #4 (MV). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:48:11 +02:00
return "\n".join(lines)