feat: Antrag-Detail um Bewertungs-Kontext erweitert

Vor der Programm-Treue-Sektion eine kompakte Info-Box, die transparent
macht, was zur Antragszeit fuer das jeweilige Parlament galt:

- **Wahlperiode** (Nummer + Konstituierung-bis-Ende-Spanne) ueber
  legislatur_zum_zeitpunkt(bl, antrag_datum)
- **Regierung zur Antragszeit** (Name + Koalitionsparteien + Vereidigung,
  ggf. Endedatum bei Sukzessionen wie Dreyer III -> Schweitzer I) ueber
  regierung_zum_zeitpunkt(bl, antrag_datum)
- **Bewertet gegen die folgenden Wahlprogramme** (pro Antragsteller-
  Fraktion mit gueltig-seit-Datum) ueber wahlprogramm_zum_zeitpunkt
  pro Partei

Daten kommen aus den neuen Modulen app/legislaturen.py + app/programme.py.
Helper laufen historisch korrekt — z.B. ein Antrag aus 2020-02-15 in TH
wuerde "Kemmerich I (FDP)" zurueckliefern.

Aktuell zeigen alle Antraege die jeweils "aktuelle" Regierung & das
aktuelle Programm, weil keine historischen Wahlprogramme im Embeddings-
Index sind. Die Architektur ist aber fuer den Tag vorbereitet, wo
historische Programme indiziert werden.

Implementation:
- main.py: _render_antrag_detail laedt geltung_kontext und gibt es ans
  Template weiter. ISO-Datum aus row["datum"] (nicht aus dem display-
  formatierten antrag["datum"]).
- v3/screens/antrag_detail.html: neue Sektion v3-geltung vor Programm-
  Treue-Block.
- static/v3/v3.css: neue v3-geltung-Klassen mit dezentem Doku-Look.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dotty Dotter 2026-05-08 00:45:44 +02:00
parent a80ac17218
commit c7861cfb58
3 changed files with 96 additions and 0 deletions

View File

@ -329,6 +329,36 @@ async def _render_antrag_detail(
except Exception:
logger.exception("Fehler beim Laden plenum_vote_results für %s", drucksache)
antrag["plenum_votes"] = []
# Geltungs-Kontext: Regierung + Legislatur zum Antragsdatum, plus pro
# Fraktion das jeweilige Wahlprogramm. Nutzt die neuen Helper aus
# ``app/legislaturen.py`` und ``app/programme.py``.
geltung_kontext: dict = {"regierung": None, "legislatur": None, "programme": []}
try:
from .legislaturen import (
regierung_zum_zeitpunkt,
legislatur_zum_zeitpunkt,
)
from .programme import wahlprogramm_zum_zeitpunkt
bl = antrag.get("bundesland")
# antrag["datum"] ist die deutsche Anzeige-Form ("10.09.2025"); für
# die zeitpunktigen Helper brauchen wir das ISO-Datum aus row.
antrag_datum = (row.get("datum") or "")[:10] # type: ignore[union-attr]
if bl and antrag_datum:
geltung_kontext["regierung"] = regierung_zum_zeitpunkt(bl, antrag_datum)
geltung_kontext["legislatur"] = legislatur_zum_zeitpunkt(bl, antrag_datum)
seen_pids: set = set()
# _row_to_detail mappt fraktionen → "parteien"-Key fuers Template;
# die Antragsteller-Fraktionen sind dort.
for partei in (antrag.get("parteien") or []):
prog = wahlprogramm_zum_zeitpunkt(bl, partei, antrag_datum)
if prog and prog["id"] not in seen_pids:
seen_pids.add(prog["id"])
geltung_kontext["programme"].append(prog)
except Exception:
logger.exception("Fehler beim Laden des Geltungs-Kontexts für %s", drucksache)
from .models import MATRIX_LABELS
return templates.TemplateResponse(template_name, {
"request": request,
@ -337,6 +367,7 @@ async def _render_antrag_detail(
"assessment_count": None,
"matrix_explanations": _MATRIX_EXPLANATIONS,
"matrix_labels": MATRIX_LABELS,
"geltung_kontext": geltung_kontext,
**_v2_template_context(current_user),
})

View File

@ -830,3 +830,30 @@ body.v2 .v3-page p { max-width: none; }
margin: 4px 0 0;
border-radius: 0 2px 2px 0;
}
/* Geltungs-Kontext kompakte Doku-Sektion direkt vor Programm-Treue.
Erklärt transparent, gegen welche Programme die LLM-Bewertung gemessen
wurde, plus Regierung/Wahlperiode zur Antragszeit. */
.v3-geltung-meta {
font-size: 0.95em;
color: var(--ink-muted, #555);
display: grid;
gap: 8px;
}
.v3-geltung-label {
font-weight: 600;
color: var(--ink, #222);
margin-right: 6px;
}
.v3-geltung-list {
margin: 6px 0 0;
padding-left: 18px;
list-style-type: "· ";
}
.v3-geltung-list li {
margin: 2px 0;
}
.v3-geltung-gueltig {
color: var(--ink-muted, #777);
font-size: 0.92em;
}

View File

@ -180,6 +180,44 @@
</section>
{% endif %}
{# Geltungs-Kontext — Regierung + Programmgeltung zur Antragszeit.
Macht transparent, gegen welche Programme die LLM-Bewertung gemessen
wurde. Daten kommen aus app/legislaturen.py + app/programme.py. #}
{% if geltung_kontext and (geltung_kontext.regierung or geltung_kontext.legislatur or geltung_kontext.programme) %}
<section class="v3-section v3-geltung">
<h3 class="v3-h3">Bewertungs-Kontext</h3>
<div class="v3-geltung-meta">
{% if geltung_kontext.legislatur %}
<div>
<span class="v3-geltung-label">Wahlperiode:</span>
{{ geltung_kontext.legislatur.wp }}.&nbsp;Wahlperiode
({{ geltung_kontext.legislatur.konstituierung }}{% if geltung_kontext.legislatur.ende %} bis {{ geltung_kontext.legislatur.ende }}{% else %} laufend{% endif %})
</div>
{% endif %}
{% if geltung_kontext.regierung %}
<div>
<span class="v3-geltung-label">Regierung zur Antragszeit:</span>
{{ geltung_kontext.regierung.name }} ({{ geltung_kontext.regierung.parteien | join("+") }}),
vereidigt {{ geltung_kontext.regierung.von }}{% if geltung_kontext.regierung.bis %}, abgelöst {{ geltung_kontext.regierung.bis }}{% endif %}
</div>
{% endif %}
{% if geltung_kontext.programme %}
<div>
<span class="v3-geltung-label">Bewertet gegen die folgenden Wahlprogramme:</span>
<ul class="v3-geltung-list">
{% for p in geltung_kontext.programme %}
<li>
<strong>{{ p.partei }}:</strong> {{ p.titel }}
<span class="v3-geltung-gueltig">— gültig seit {{ p.gueltig_ab }}{% if p.gueltig_bis %} bis {{ p.gueltig_bis }}{% endif %}</span>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
</section>
{% endif %}
{# 6 ── Programm-Treue — wird komplett ausgeblendet wenn ALLE
Fraktionen ohne Programm sind (typisch bei Bundestags-Antraegen,
wo aktuell keine Programme indiziert sind), sonst werden nur