Commit Graph

153 Commits

Author SHA1 Message Date
Dotty Dotter
7ebdc78331 feat(#160): SH-Parser produktiv — Schleswig-Holsteiner Plenarprotokolle
Verifiziert auf WP20 Sitzungen 115 + 116. Format ist TH-aehnlich:

Result-Anchor: "Damit ist [Subjekt] (mehrheitlich|einstimmig)? (angenommen|abgelehnt|überwiesen|so beschlossen)"
Vote-Block (Q+A im Reden-Stil):
  - JA: "Wer dem zustimmen will ... Das sind die Fraktionen von X"
  - NEIN: "Wer stimmt dagegen? ... Das sind die Fraktionen von Y"
  - ENTH: "Wer enthaelt sich? ... Z"
Drucksachen-Lookup: rueckwaerts vom Anchor

Besonderheiten:
- SSW (5%-Huerden-befreit) als feste Fraktion
- "Damit ist die Ausschussueberweisung einstimmig so beschlossen" → ergebnis="ueberwiesen"
- "Das sind alle anderen Fraktionen" → NEIN als Komplement von JA inferiert
- Soft-Hyphen-Reparatur (PDF-Zeilenumbruch "zustim- men" → "zustimmen")
- _last_match-Helper, weil 1500-char-Window mehrere Vote-Bloecke enthalten kann
  (TH-Limitierung gefixed)

URL-Pattern (verifiziert):
https://www.landtag.ltsh.de/export/sites/ltsh/infothek/wahl20/plenum/plenprot/{YYYY}/20-{n:03}_{MM-YY}.pdf

Datum-Anteile (YYYY-Pfad + MM-YY-Suffix) machen URL-Vorhersage unmoeglich
→ Auto-Ingest-Cron via Index-Scrape (analog HH/HE):
https://www.landtag.ltsh.de/infothek/wahl20/plenum/plenprot_seite/

Tests: 23 SH-Tests + Stub-Registry-Test angepasst.
Stand: 7 produktive Parser (NRW, BUND, BE, HH, TH, HE, SH).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 01:29:06 +02:00
Dotty Dotter
8125dbb731 feat(#154): HE-Parser produktiv — Hessen Beschlussprotokoll (Status-Only)
Hessen publiziert nur Beschlussprotokolle (Tagesordnung + Status), KEINE
Wortprotokolle mit Vote-Block. Daher minimaler Parser:
- Drucksache + Status (angenommen/abgelehnt/ueberwiesen)
- Vote-Listen bleiben leer (HE hat keine Fraktions-Detail)

URL-Pattern (verifiziert WP21 Sitzungen 61-63):
http://starweb.hessen.de/cache/hessen/landtag/Plenum/{wp}/Beschlussprotokoll_PL_{n}_{datum}.pdf

Datum-Teil DD-MM-YYYY → URL-Vorhersage unmoeglich, Auto-Ingest braucht
Index-Scrape via starweb.hessen.de/starweb/LIS/Pd_Eingang.htm (analog HH).

Status-Mapping:
- "angenommen" → ergebnis="angenommen"
- "Abgelehnt" → ergebnis="abgelehnt"
- "Nach (Aussprache|Lesung) an [Ausschuss]" → ergebnis="ueberwiesen"
- "Entgegengenommen", "Abgehalten", "Zur Kenntnis genommen" → uebersprungen

Tests: PROTOKOLL_PARSERS-Set jetzt {NRW, BUND, BE, HH, TH, HE}. STUB_BL_CODES
auf 11 BL reduziert (BB, BW, BY, HB, LSA, MV, NI, RP, SH, SL, SN bleiben).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 01:19:02 +02:00
Dotty Dotter
399dbc2639 feat(#163): TH-Parser produktiv — Thueringer Plenarprotokolle
Fuenfter produktiver Parser nach NRW + BUND + BE + HH.

URL-Pattern verifiziert (WP8 Sitzungen 1, 10, 20, 30, 40, 42):
  https://www.thueringer-landtag.de/uploads/tx_tltcalendar/protocols/Arbeitsfassung{n}.pdf

Anchor-Sprache (BE-aehnlich):
  Wer dem zustimmt, ... Das sind die Stimmen aus den Fraktionen der
  CDU, BSW, SPD und Die Linke. Wer stimmt gegen ...? Das sind die
  Stimmen aus der Fraktion der AfD. Damit ist [...] mehrheitlich
  angenommen.

Pattern:
- Result-Anchor: Damit ist [Subjekt] (mehrheitlich|einstimmig)?
  (angenommen|abgelehnt)
- Vote-Block: Wer dem zustimmt / Wer stimmt gegen / Wer enthaelt sich
- Drucksachen-Lookup: 'Drucksache 8/N' rueckwaerts

Fraktions-Mapping WP8 (ab Mai 2024): CDU, AfD, BSW, Linke, SPD
(WP7-Faktionen GRUENE/FDP fuer Backfill ebenfalls im Mapping).

Cron-PROTO_TARGETS um TH-WP8 erweitert. Stub-Test angepasst.
2026-04-29 01:11:58 +02:00
Dotty Dotter
5f97ae9fc3 feat(#155): HH-Parser produktiv — Hamburg Beschlussprotokolle
Vierter produktiver Plenarprotokoll-Parser nach NRW + BUND + BE.

Hamburg publiziert kompakte Beschlussprotokolle (Tabellen-Form mit
Vote-Block pro Beschluss):
  ... mehrheitlich mit den Stimmen der SPD und GRUENEN gegen die
  Stimmen der CDU und AfD bei Enthaltung der Linken angenommen

Pattern:
- einstimmig (angenommen|abgelehnt) — alle Fraktionen
- mehrheitlich mit den Stimmen X gegen die Stimmen Y bei Enthaltung Z
  (angenommen|abgelehnt)

Fraktions-Mapping WP23: SPD, GRUENE, CDU, AfD, Linke

URL-Discovery laeuft ueber die Protokoll-Liste der Buergerschaft
(Blob-IDs via Index-Page-Scrape). Cron-Eintrag erst sobald
URL-Discovery-Skript hier integriert ist.

Stub-Test angepasst (HH raus aus STUB_BL_CODES).
2026-04-29 00:57:58 +02:00
Dotty Dotter
c7d6ac7f5f feat(#150): BE-Parser produktiv — Berliner Abgeordnetenhaus-Plenarprotokolle
Dritter vollwertiger Plenarprotokoll-Parser nach NRW + BUND.

URL-Pattern verifiziert (WP19 Sitzungen 1, 10, 50, 80, 100):
  https://www.parlament-berlin.de/ados/{wp}/IIIPlen/protokoll/plen{wp}-{n:03}-pp.pdf

Anchor-Sprache (NRW-aehnlich, mit Berliner-Eigenheit 'pro forma'):
  Wer den Antrag auf Drucksache 19/X annehmen moechte, ... – Das sind
    die Fraktionen Buendnis 90/Die Gruenen und Die Linke.
  Wer stimmt dagegen? – Das sind die Fraktionen der CDU, SPD und AfD.
  Wer enthaelt sich, pro forma? – Das ist niemand.
  Damit ist der Antrag abgelehnt.

Pattern:
- Result-Anchor: Damit ist [Antrag/Aenderungsantrag/Gesetzentwurf/...]
  (angenommen|abgelehnt)
- Vote-Block: 3 Q+A-Paare im Reden-Stil (annehmen moechte / dagegen /
  enthaelt sich)
- Drucksachen-Lookup: 'Drucksache 19/N(-suffix)' rueckwaerts (1500-char Fenster)

Fraktions-Mapping WP19:
- Buendnis 90/Die Gruenen → GRÜNE
- Die Linke → LINKE
- CDU, SPD, AfD, FDP

21 Tests in test_protokoll_parsers_be.py.
Cron-PROTO_TARGETS erweitert um BE WP19 (~80 Sitzungen).
Stub-Test angepasst.

905 Tests gruen (889 → 905, +16 fuer BE).
2026-04-29 00:37:47 +02:00
Dotty Dotter
a83c770b93 docs(#151): BW-Datenmodell-Inkompatibilitaet vermerkt
Vertiefte Probe (WP17 Sitzung 50): BW stimmt 'pro Artikel'
('Damit ist Artikel 1 einstimmig zugestimmt'), nicht pro Drucksache.

Das ist andere Datenmodellierung als NRW (Drucksache→Vote) und BUND
(Beschlussempfehlung→Vote). Ein BW-Parser braucht entweder:
- Aggregations-Heuristik: alle Artikel angenommen → DS angenommen
- Schema-Erweiterung um 'artikel'-Spalte fuer per-Artikel-Records

Implementer muss vor Start mit Maintainer abstimmen, welcher Weg
gegangen wird. BW bleibt Stub bis Designwahl getroffen ist.
2026-04-28 23:29:31 +02:00
Dotty Dotter
22a2b63c35 feat(#148): BUND-Parser produktiv — Bundestags-XML-Plenarprotokolle
Erster vollwertiger Plenarprotokoll-Parser nach NRW. Quelle:
https://dserver.bundestag.de/btp/{wp}/{wp}{n:03}.xml

Anchor-Sprache (verifiziert WP20 Sitzungen 30 + 100):
  'Die Beschlussempfehlung ist mit den Stimmen der Koalitionsfraktionen
   gegen die Stimmen der CDU/CSU-Fraktion bei Enthaltung der AfD-Fraktion
   angenommen.'

Pattern:
- Subjekt: Beschlussempfehlung | Ueberweisungsvorschlag | Antrag | Gesetzentwurf
- Vote-Block: 'mit den Stimmen X / gegen die Stimmen Y / bei Enthaltung Z'
- Ergebnis: 'angenommen' oder 'abgelehnt'
- Drucksache rueckwaerts vom Anchor (1500 chars Window)
- Kind 'ueberweisung' invertiert ergebnis zu 'ueberwiesen'

Fraktions-Mapping (WP20 = Ampel):
- 'Koalitionsfraktionen' → SPD + GRÜNE + FDP
- 'Oppositionsfraktionen' → CDU/CSU + AfD + LINKE
- 'CDU/CSU-Fraktion', 'Fraktion Bündnis 90/Die Grünen', etc.

WP21 (ab 2025) braucht eigenes Mapping-Update.

26 Tests in test_protokoll_parsers_bund.py (Vote-Block-Parsing, Anchor-
Erkennung, Drucksachen-Lookup, End-to-End mit Mock-XML).

Cron + Ingest-CLI:
- PROTO_TARGETS-Format erweitert um PROTOKOLL_ID_PREFIX und {n3}-
  Placeholder fuer 3-stellig zero-gepaddetes BT-Schema (BTP20-N)
- ingest_votes URL-Suffix dynamisch (PDF vs XML) statt hardcoded .pdf
- Eintrag in PROTOKOLL_PARSERS (NRW + BUND)
- Stub-Test angepasst: BUND raus aus STUB_BL_CODES

889 Tests gruen (787 → 889, +102 fuer Phase-2 Stubs+Tests+BUND).
2026-04-28 23:21:39 +02:00
Dotty Dotter
7cfbd9f210 docs(#148): BUND-Stub um echte Anchor-Sprache aus WP20-Sitzung 30
Erste Probe (Sitzung 184) war Aussprache, daher 0 Beschluss-Anchors.
Sitzung 30 (572k chars, 5 angenommen-Anchors) zeigt die echte
BT-Vote-Sprache:

  'Die Beschlussempfehlung ist mit den Stimmen der Koalitions-
   fraktionen und der Fraktion Die Linke gegen die Stimmen der
   CDU/CSU-Fraktion bei Enthaltung der AfD-Fraktion angenommen.'

Pattern-Erkennung:
- Anchor-Verb 'angenommen' oder 'abgelehnt' am Satzende
- Vote-Block: 'mit den Stimmen [...] gegen die Stimmen [...]
  bei Enthaltung [...]'
- Fraktions-Phrasen: 'Fraktion X', 'X-Fraktion', 'Koalitionsfraktionen'
- Drucksachen rueckwaerts vom Anchor (oft 100+ Zeichen vorher)

Wichtig: BT-Anchor-Sprache ist viel laenger als NRW — Regex-Begrenzung
muss 200+ Zeichen tolerieren.

Sample-Sitzungen mit Beschluessen: WP20 30, 100, 150.
2026-04-28 23:15:36 +02:00
Dotty Dotter
171a05ed4d docs(#151): BW-Stub um konkrete Recherche-Befunde erweitert
Heutige Probe von WP17 Sitzung 50 (618 KB PDF) ergab:

URL-Pattern bestaetigt:
  https://www.landtag-bw.de/.../WP{wp}/Plp/{wp}_{n:04}.pdf
  4-stellige Sitzungs-Nr mit Padding (anders als NRW unkpaddet)

Anchor-Phrasen-Stichprobe:
  'einstimmig zugestimmt' x5 — Haupt-Anchor (NRW: 'angenommen')
  'Damit ist [...] einstimmig' x2 — NRW-aehnliche Struktur
  'angenommen' x1 — nur in einer Rede, KEIN Beschluss-Anchor!
  'Drucksache 17/N' x35 — DS-Pattern wie NRW
  'zugestimmt' x19 — dominierende Vote-Phrase

Fraktions-Auflistung pro Vote in BW deutlich weniger detailliert als
NRW — Parser wird oft nur 'einstimmig' / 'mit Mehrheit' extrahieren
koennen, kein ja/nein/enthaltung-Breakdown pro Fraktion.

Fuer den naechsten Implementer (BW-Session) wertvolle Vorarbeit.
2026-04-28 23:14:17 +02:00
Dotty Dotter
30d51da5f7 feat(#106 Folge): Datenquellen-Tabelle auf /methodik um Plenum-Vote-Spalte erweitert
Pro BL zeigt die Tabelle nun:
- Doku-System (wie bisher)
- Drucksachen: alle aktiv (Adapter laufen)
- Plenum-Votes: 'aktiv' wenn Parser registriert (NRW), sonst 'Stub'

Plus Erklär-Hinweis: 'Plenum-Votes = fraktions-aggregierte
Abstimmungs­ergebnisse aus den Plenarprotokollen (#106). Stubs sind
Tracking-Stellen fuer kuenftige Implementierungen (Issues #148-#163).'

main.py reicht supported_bundeslaender() aus protokoll_parsers an die
Template-Context durch (plenum_vote_parsers-Set).
2026-04-28 23:12:58 +02:00
Dotty Dotter
c0692b3078 feat(#106 Folge): 16 Stub-Parser + Tracking-Issues fuer alle Bundeslaender
Pro BL (BUND + 15 Laender) ein Modul app/protokoll_parsers/<bl>.py mit:
- Recherche-Findings im Docstring (Doku-System, Base-URL, Format,
  URL-Discovery-Status, Familie, Aufwand-Schaetzung)
- parse_protocol() raised NotImplementedError mit Hinweis auf Issue-Tracker
- *Nicht* in PROTOKOLL_PARSERS-Registry → Auto-Ingest-Cron uebersieht sie

Tracking-Issues #148-#163 auf Gitea, jeweils mit den Recherche-Findings
und einer Checkliste fuer die Implementer-Session.

Roadmap-Doc (docs/protokoll-parser-roadmap.md) aktualisiert mit
Stub→Issue-Mapping-Tabelle.

Wenn der Implementer pro BL fertig ist:
1. NotImplementedError durch echten Parser ersetzen
2. Eintrag in app/protokoll_parsers/__init__.py::PROTOKOLL_PARSERS
3. PROTO_TARGETS in scripts/auto-ingest-protocols.sh ergaenzen

787 Tests gruen, NRW unveraendert.
2026-04-28 23:09:07 +02:00
Dotty Dotter
145ad1e8d4 docs(methodik): klarstellen wie System- und User-Prompt zusammenwirken
User-Frage zur Transparenz-Seite: 'Welcher Prompt wird ausgefuehrt?
Der System-Prompt ist deutlich umfangreicher.' Antwort: keiner allein —
beide werden in einem API-Call zusammen gesendet und gemeinsam
ausgewertet.

Auf /methodik#prompts neu vor den details-Bloecken:
- Erklaerung 'in einem einzigen API-Call', beide ins Kontextfenster
- 2-Spalten-Tabelle 'System (Wer/wie)' vs. 'User (Was)'
- Begruendung der Trennung (Caching, Compliance, Wartbarkeit)
- Code-Referenz zu qwen_bewerter.py:83-85 mit messages-Aufbau

Reine UI-Aenderung, keine Code-Logik betroffen.
2026-04-28 09:14:22 +02:00
Dotty Dotter
eb0669d6ac feat(#147): Hover-Tooltips fuer Abkuerzungen auf Antrag-Detail
User-Feedback: '(A)' hinter Partei, 'WP', 'PP' brauchen Erklaerung
fuer Erstleser:innen. Loesung: ausfuehrliche title-Tooltips plus
visuelle Affordanz (cursor:help).

Geaendert:
- v2-badge-antragsteller / -regierung: cursor:help
- v2-score-chip[title]: cursor:help
- (A) → 'A — Antragstellende Fraktion: hat den Antrag eingereicht.'
- (R) → 'R — Regierungsfraktion: traegt die aktuelle Mehrheit im Landtag.'
- WP-Chip: 'WP — Wahlprogramm-Treue (0–10): wie gut passt der Antrag
  zum aktuellen Wahlprogramm? + Begruendung'
- PP-Chip: analog fuer Parteiprogramm-Treue
- Score-Hero: Tooltip mit GWÖ-Score-Definition + Methodik-Verweis
- 'Enth.:' im Abstimmungs-Block: dotted underline + Tooltip 'Enth. —
  Enthaltung: weder Zustimmung noch Ablehnung'

Closes #147
2026-04-28 08:46:27 +02:00
Dotty Dotter
7de4df1fef feat(#126): protokoll_parsers/-Sub-Package + Registry-Pattern + ADR 0009
Architektur-Refactor zur Vorbereitung BL-uebergreifender Parser:

- app/protokoll_parser_nrw.py → app/protokoll_parsers/nrw.py
- app/ingest_votes_nrw.py → app/ingest_votes.py (BL-uebergreifend)
- Neue app/protokoll_parsers/__init__.py mit:
  - PROTOKOLL_PARSERS-Dict (BL-Code → Parser-Funktion, derzeit nur NRW)
  - parse_protocol(bundesland, pdf_path) als BL-uebergreifender Einstieg
  - supported_bundeslaender()-Helper
  - NotImplementedError mit hilfreicher Message bei unbekanntem BL

CLI bekommt --supported-Flag fuer BL-Discovery:
  python -m app.ingest_votes --supported  → 'NRW'

ADR 0009 dokumentiert das Muster (Sub-Package + Funktions-Registry,
analog zu ADR 0002 fuer ParlamentAdapter). Folge-BL bekommen je
eine eigene Datei und einen Eintrag in PROTOKOLL_PARSERS — kein
Refactoring der Bestands-Logik.

Tests:
- 7 neue Tests in test_protokoll_parsers.py fuer Registry und Dispatch
- Bestehende NRW-Tests umbenannt zu test_protokoll_parsers_nrw.py,
  Imports angepasst — keine Verhaltens-Aenderung
- Bestehende Ingest-Tests umbenannt zu test_ingest_votes.py

642 Tests gruen, kein Verhaltens-Drift.
2026-04-28 08:37:31 +02:00
Dotty Dotter
7e0f0117e6 feat(#106): UI-Block 'Abstimmungsergebnis' auf Antrag-Detail
Antrag-Detail-Endpoint liest plenum_votes via get_plenum_votes() und
reicht sie an antrag_detail.html durch.

Block rendert pro Plenum-Abstimmung eine Karte:
- Ergebnis (angenommen/abgelehnt/...) farb-kodiert
- 'einstimmig'-Annotation falls gesetzt
- Quelle (Protokoll-ID, mit URL als Tooltip)
- Fraktions-Chips fuer Ja/Nein/Enthaltung

Mehrfach-Abstimmungen einer Drucksache (Ueberweisung + finale
Beschlussfassung) erzeugen mehrere Karten — chronologisch via
parsed_at DESC im Repository sortiert.

Block erscheint nur, wenn Eintraege existieren (kein leerer Header).
2026-04-28 08:04:32 +02:00
Dotty Dotter
e26607854f feat(#106): Ingest-CLI fuer NRW-Plenarprotokolle
app/ingest_votes_nrw.py: Pipeline PDF → protokoll_parser_nrw → DB.

CLI:
  python -m app.ingest_votes_nrw --pdf /pfad/MMP18-119.pdf
  python -m app.ingest_votes_nrw --url https://landtag.nrw.de/.../MMP18-119.pdf
  python -m app.ingest_votes_nrw --pdf x.pdf --protokoll-id MMP18-119 --bundesland NRW

Protokoll-ID wird default aus Datei-Stem abgeleitet (MMP18-119.pdf →
MMP18-119), URL-Mode parst sie aus dem letzten Pfadsegment.

ingest_pdf() ist die programmatische API (auch fuer Folge-Cron, falls
spaeter automatisch Plenarprotokoll-Sammelinges nachgeruestet wird).
Statistik-Dict: parsed/written/skipped_no_drucksache/errors.

6 Tests: Roundtrip, skip-bei-fehlender-Drucksache, default + override
fuer Protokoll-ID, BL-Override (fuer #126-Folge), idempotenter Re-Ingest.
2026-04-28 08:03:18 +02:00
Dotty Dotter
ae3f48be41 feat(#106): plenum_vote_results-Tabelle + Repository
DB-Schema fuer fraktions-aggregierte Plenum-Abstimmungsergebnisse:
- bundesland, drucksache, quelle_protokoll als Compound-PK
  (eine Drucksache kann mehrfach abgestimmt werden — Ausschuss-Empfehlung
  und finale Beschlussfassung leben nebeneinander)
- ergebnis (angenommen/abgelehnt/ueberwiesen/...), einstimmig-Flag
- fraktionen_ja/_nein/_enthaltung als JSON-Arrays
- quelle_protokoll (z.B. 'MMP18-119') + optional quelle_url
- Index auf (bundesland, drucksache) fuer Lookup-Path

Repository-API:
- upsert_plenum_vote(...) idempotent ueber Compound-PK
- get_plenum_votes(bl, drucksache) → Liste, neueste zuerst

7 Tests fuer Roundtrip, einstimmig-Flag, Idempotenz, Multi-Protokoll-Erhalt,
leere Queries, Unicode-Handling von 'GRÜNE'.

Refs #106 — naechster Schritt: Ingest-CLI gegen NRW-PDFs.
2026-04-28 08:01:26 +02:00
Dotty Dotter
d640734641 feat(#106,#134): NRW-Protokoll-Parser v5 ins Repo migriert
Vorher als parser_v5_iteration15.py nur auf Prod-Server, nicht
versionskontrolliert. Jetzt unter app/protokoll_parser_nrw.py
mit klarem Naming-Schema (BL-Suffix, damit Folge-Adapter analog
heissen koennen, vgl. ADR 0002).

Aenderungen am Code:
- from __future__ import annotations (Py3.9-kompatibel fuer 'str | None')
- fitz-Import optional (try/except), damit pure-string-Funktionen
  auch im Stub-conftest funktionieren

30 Tests in test_protokoll_parser_nrw.py (#134 Phase 2):
- normalize_fraktionen: F.D.P., GRÜNE-Aliase, Landesregierung
- _is_empty_phrase: Niemand/Keine/nicht-Mustern
- _parse_vote_block: ja/nein-Extraktion plus Negationen
- find_results: angenommen/abgelehnt, einstimmig (nur ueber-Kind!),
  (neu)-Suffix in Drucksachen-Nrn, Sortierung, Dedup
- resolve_drucksache_for_ueber: Backward-Search mit closest-match

Refs #106 (Abstimmungsverhalten verknuepfen — Vorbereitung fuer DB-Schema)
Refs #126 (BL-uebergreifender Parser — NRW als Referenz-Implementierung)
Refs #134 (Test-Suite Audit — Phase 2)
2026-04-28 02:08:03 +02:00
Dotty Dotter
5559f42c92 feat(#138): SHA-Lock-File schuetzt vor stillem PDF-Tausch
Hintergrund: abgeordnetenwatch hatte das CDU-BE-2023-PDF unter dem alten
Slug-Namen gegen das CDU-BE-2026-Wahlprogramm ersetzt — ohne den
Datei-Namen zu aendern. Die Embedding-Indexierung haette das anachronistische
Programm uebernommen, ohne dass es jemand bemerkt.

Loesung: app/wahlprogramm-shas.lock.json pinnt nach erstem erfolgreichen
Download den SHA-256 jedes Programmes. Spaetere Aufrufe von
fetch_and_verify() vergleichen den Server-Inhalt gegen den Lock; bei
Abweichung wird abgebrochen mit klarer Fehlermeldung. Nur mit explizitem
Maintainer-Override (--accept-new-sha) wird der Lock aktualisiert.

CLI:
  python -m app.wahlprogramm_fetch --pin-existing
    seedet den Lock einmalig aus den vorhandenen PDFs (52 Eintraege).
  python -m app.wahlprogramm_fetch --fetch BL PARTEI [--accept-new-sha]
    laedt mit Lock-Pruefung; --accept-new-sha bei bewusstem Update.

6 neue Tests in test_wahlprogramm_fetch.py decken den Pferdetausch-
Block, das initiale Pinnen, das Migration-Szenario (PDF da, Lock leer)
und den --accept-new-sha-Override ab.

Closes #138
2026-04-28 01:58:42 +02:00
Dotty Dotter
d0d941444d feat(#144): Matrix-Ueberschriften ausschreiben + Hover-Tooltips
Statt Abkuerzungen (Wuerde, Solid., Liefer., Verwalt., Gesell.) jetzt
voll ausgeschrieben: Menschenwuerde, Solidaritaet, Lieferant:innen,
Verwaltung, Gesellschaft & Natur, etc.

Hover-Tooltip pro Spalte/Zeile mit Erklaerung + Staatsprinzip
(Rechtsstaatsprinzip, Gemeinnutz, Umwelt-Verantwortung, ...).
Matrix-Felder bekommen Tooltip mit Feldname als Vorschau, der
volle Erklaerungstext bleibt im Click-Modal (showField).

Layout: rhdr-Spalte 130/150px, line-height 1.25, min-height 36px,
damit lange Begriffe sauber umbrechen koennen.

Closes #144
2026-04-28 01:53:38 +02:00
Dotty Dotter
0d26cad549 feat(#145): LLM-Prompts auf /methodik als Transparenz-Block
System- und User-Prompt-Template stehen jetzt collapsed unter dem
neuen Abschnitt 'LLM-Prompts'. Der User-Prompt wird auf eine eigene
Konstante USER_PROMPT_TEMPLATE umgestellt und via .format(...) gerendert,
sodass das gleiche Template auf der Methodik-Seite gezeigt werden kann
ohne den f-string-Code zu duplizieren.

Closes #145
2026-04-28 01:50:25 +02:00
Dotty Dotter
5f6bcac282 feat(#146): Fraktionen je Treffer in Landtag-Suche anzeigen
Adapter liefert fraktionen schon mit, das Frontend ignorierte sie bisher.
Treffer-Zeile bekommt jetzt unter dem Titel kleine Teal-Chips fuer jede
einreichende Fraktion (Beispiel: 'CDU SPD' bei kollektiven Antraegen).

Stylistisch konsistent zum Score-Chip-System (color-mix mit ecg-teal),
mono Font, uppercase 10px — bleibt auch bei vielen Fraktionen lesbar.

Closes #146
2026-04-28 01:47:54 +02:00
Dotty Dotter
09c29cac69 fix(#142): SL HTTP 5xx als Fehler raisen statt return []
Symptom: Monitoring-Scan zeigte bei SL seen=0 errors=OK, obwohl der
Umbraco-Backend HTTP 500 zurueckgab. Im _post_search wurde 5xx via
'logger.error + return []' geschluckt, sodass der Monitoring-Layer
die Fehlerursache nicht in monitoring_daily_summary persistierte.

Fix: bei resp.status_code != 200 httpx.HTTPStatusError raisen — das
propagiert durch search() ueber _search_adapter ins outer except in
daily_scan, das den Fehlertext in summary.errors schreibt.

Regression-Test test_search_propagates_http_500.

Closes #142
2026-04-28 01:46:35 +02:00
Dotty Dotter
6d587c1f3a feat(feedback): konfigurierbare Issue-Labels via GITEA_FEEDBACK_LABELS
Dev-Container setzt GITEA_FEEDBACK_LABELS=feedback,dev, damit
Feedback-Issues aus gwoe-dev.toppyr.de unterscheidbar markiert werden.
Label-Farben: feedback rot, dev gelb, Sonst grau.

Teil der Container-Duplikation fuer v1.x-Entwicklung.
2026-04-28 01:31:25 +02:00
Dotty Dotter
4b03448e29 fix(feedback): Screenshot scharf + ohne Feedback-UI
- Auflösung: scale = window.devicePixelRatio (statt min:2 cap) — Retina-scharf
- Vor dem html2canvas-Capture werden v2-feedback-{modal,overlay,btn} auf
  display:none gesetzt; finally-Block stellt UI zurueck. Damit ist die
  ausgegraute Modal-Schicht nicht im Bild
- Capture nur des sichtbaren Viewports (width/height/x/y/windowWidth/Height
  explizit), spart Bandbreite + zeigt was der User wirklich sieht
- MAX_W 800 -> 1600, JPEG 0.7 -> 0.85, imageSmoothingQuality high
- requestAnimationFrame x2 vor capture, damit Browser den Reflow vor dem Snap fertig hat
- app_version 1.0.1 -> 1.0.2 (Cache-Buster)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 01:10:36 +02:00
Dotty Dotter
a8d7b72702 feat(v2): Feedback-Widget mit Audit-Trail + Screenshot + direkter Gitea-Anbindung
- Component v2/components/feedback_widget.html: Button unten links oberhalb der
  Queue, Klick oeffnet Modal mit vorausgefuellten Kontext-Feldern (URL,
  Drucksache, Viewport, User-Agent, letzte 15 Klicks, letzte 10 Console-Errors,
  letzte 5 Page-Loads). Eingaben: Titel, Beschreibung, optional Screenshot
- Audit-Trail-Sammler in localStorage (Ringbuffer 30 Klicks, 10 Errors)
- Screenshot via self-hosted html2canvas 1.4.1 (194 KB unter app/static/v2/lib/)
- Backend POST /api/feedback (rate-limit 5/h):
  - validiert + html-strippt Inputs
  - erstellt Gitea-Issue per API mit Label 'feedback' (Label wird idempotent angelegt)
  - laedt Screenshot als Issue-Asset hoch (Gitea Issue-Attachment-API)
- 4 neue Settings: gitea_token, gitea_api_url, gitea_repo_owner, gitea_repo_name
- Server .env um GITEA_TOKEN ergaenzt
- 10 neue Unit-Tests (mit gemocktem httpx)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 01:00:44 +02:00
Dotty Dotter
fab1bddd3c fix(v2): Hamburger-Toggle wirklich ausblenden (Specificity-Konflikt + Cache)
Bug: .v2-topbar button {display:inline-flex} ueberschreibt .v2-menu-toggle{display:none}
wegen hoeherer Specificity. Fix: Selektor .v2-topbar .v2-menu-toggle + !important.

Plus app_version 1.0.0 -> 1.0.1 als Cache-Buster fuer alle CSS-Refs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:37:55 +02:00
Dotty Dotter
98787c8684 fix(v2): Cache-Buster fuer CSS via ?v=app_version
Browser-Cache zeigte alte v2.css ohne v2-menu-toggle-display:none-Regel.
Mit ?v=1.0.0 wird auf Versionsspruenge sauber neu geladen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:33:18 +02:00
Dotty Dotter
b1ad2bd45d fix(v2): Hamburger-Menü-Toggle nur auf Mobile (< 900 px) sichtbar
Auf Desktop ist die Sidebar permanent — der Burger-Button hatte dort keine
Funktion. display: none default + @media max-width:900px → inline-flex.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:28:51 +02:00
Dotty Dotter
7a64335e64 feat(auth): 'Passwort vergessen?'-Link im v2-Login-Modal
Klick öffnet /api/auth/forgot-password → 302 zur Keycloak-Reset-Page mit
client_id + redirect_uri (auf eigene Domain). Keycloak schickt Mail mit
Reset-Link, User setzt neues Passwort, kommt zurück.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:21:02 +02:00
Dotty Dotter
c1926ada4f feat(#143): Registrierungs-Bestätigungsmail an User direkt nach Anmeldung
Vorher: User registriert -> Keycloak-User mit enabled=false angelegt -> KEINE
Mail bis Admin manuell freischaltet. UX-Luecke: User weiss zwischen Klick und
Admin-Freischaltung nicht, ob etwas passiert ist.

Jetzt: nach erfolgreichem Keycloak-User-Create wird sofort eine Bestaetigungs-
Mail an die angegebene Adresse geschickt mit Hinweis auf den 3-Schritt-Flow
(Anmeldung -> Admin-Freischaltung -> Passwort-Setzen-Mail). Plain-Text + HTML.
Fehler beim Mail-Versand wird geloggt aber nicht weitergereicht — User-Anlage
ist davon unabhaengig.

Response-Message angepasst: 'Wir haben dir eine Bestaetigung per E-Mail geschickt.'

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:40:04 +02:00
Dotty Dotter
6581acd28e ux(v2): Partei-Dropdown statt Freitext in /v2/abos und /v2/feed
Beide Routes liefern jetzt all_canonical_keys() (ohne Landesregierung) als Dropdown-
Optionen. Verhindert Tippfehler und gibt nur tatsaechlich erkannte Parteien zur Auswahl.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:37:31 +02:00
Dotty Dotter
7cbd46f88d feat(v2): Atom-Feed-Konfig-Seite + Eigene-Abos-Verwaltung
Backend (Filter sind seit jeher da):
- /api/feed.xml?bundesland=&partei=&limit=
- /api/subscriptions GET/POST/DELETE

UI:
- /v2/feed: Form mit BL/Partei/Limit, generiert Feed-URL live, Buttons Oeffnen/
  URL-Kopieren/In-Feedly. Default-BL aus Header-Selektor uebernommen
- /v2/abos: Liste eigener Abos + Form zum Anlegen/Loeschen, BL-Dropdown,
  Partei-Freitext, Frequenz daily/weekly
- Sidebar 'Daten'-Gruppe um beide Eintraege erweitert (statt Direkt-Link auf
  /api/feed.xml)
- Beide Routen mit Depends(require_auth) — Anonyme bekommen 401-Redirect

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:34:55 +02:00
Dotty Dotter
7f070b5e6c fix(v2): Topbar harte Hoehe 32px + Kleine-Anfragen-Heuristik in Landtag-Suche
Topbar:
- height: 32px (statt auto), line-height: 1, alle children max 24px
- Topbar-Icons explizit auf 12x12 (statt 14)
- selects/buttons/a mit fester Hoehe 22px, padding 2px 6px

Landtag-Suche:
- search_landtag filtert jetzt Drucksachen aus, deren Titel typische
  Frage-Praefixe haben (Welche/Wie viele/Wann/Was/Hat/Ist/...)  oder mit '?'
  enden — bei NRW-OPAL liefert der Adapter alle als 'sonstige', daher
  Title-Heuristik. Server-side, damit alle Adapter profitieren.
- Neuer Helper drucksache_typen.likely_kleine_anfrage_titel()

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:23:22 +02:00
Dotty Dotter
fa5a5b6026 ux(v2): Prüfen + Daten-Sidebar-Gruppen ganz ausblenden ohne Auth (statt nur leere Labels)
Vorher: '— Pruefen' + '— Daten'-Labels waren sichtbar, aber alle Eintraege darin
hidden — nur ein verlorener Header. Jetzt: ganzer Gruppen-Container hinter
{% if is_authenticated %} → Anonymous-User sieht nur 'Lesen'-Gruppe.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:18:58 +02:00
Dotty Dotter
85a10b7fc3 ux(v2): bessere Anzeige für 'skipped' Drucksachen (Kleine Anfragen etc.)
Vorher: Button-Text 'Übersprungen', der Grund nur als Tooltip — User versteht
nicht warum. Jetzt: 'Nicht abstimmbar' + sichtbare Italic-Begruendung unter der
Zeile mit dem konkreten Reason-Text vom Server (Backend liefert reason, typ
und typ_normiert).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:17:11 +02:00
Dotty Dotter
997d59a9a5 fix(v2): Queue-Widget ist immer sichtbar (auch ohne aktive Jobs)
Vorher: filterte stale-Jobs raus, bei leerer aktiver Queue display:none → User sah nichts.
Jetzt: immer sichtbar mit 'Queue leer · N Worker bereit' wenn nichts aktiv.
Tooltip zeigt Stale-Jobs als 'letzter Lauf'-Liste, wenn keine aktiven Jobs da sind.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:13:30 +02:00
Dotty Dotter
273d45ea36 fix: PDF-Link mit #page=N-Hash — Browser-PDF-Viewer landet jetzt direkt auf der richtigen Seite
Browser-PDF-Reader (Chrome, Firefox) ignorieren das von /OpenAction-Eintrag im
PDF-Catalog (#88f9c7d) komplett. Der zuverlaessige Weg: URL-Hash-Anker '#page=N'.

Drei Stellen angepasst:
- redline_utils.build_pdf_href: haengt #page={seite} an die URL
- embeddings._build_zitat_url (rebind): analog
- v2/components/quote_card.html: bei alten DB-Eintraegen ohne Hash wird er
  on-the-fly aus dem 'seite='-Query-Param erzeugt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:09:46 +02:00
Dotty Dotter
88f9c7db6c fix: PDF-Endpoint setzt OpenAction auf gefundene Seite + Topbar weiter komprimiert
Vorher: /api/wahlprogramm-cite lieferte das gesamte PDF mit Highlight-Annot
auf der gefundenen Seite, aber der Browser-PDF-Viewer landete auf Seite 1.
Sieht User: 'PDF oeffnet, aber falsche Seite'.

Jetzt: doc.xref_set_key(catalog, 'OpenAction', '[<page-ref> 0 R /Fit]')
schreibt eine PDF-Open-Action ins Dokument-Catalog. Reader springt beim
Oeffnen direkt auf target_page_idx, ohne dass Browser-Hash-Anker noetig sind.

Plus: Topbar select/button padding-top/bottom 1px, links 0px (User: 'nur so
hoch wie noetig').

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:06:39 +02:00
Dotty Dotter
489a1915f8 fix: PDF-Highlight strippt führende Seitenzahl + Topbar noch kompakter
- render_highlighted_page: führende Seitenzahl-Tokens ('44 Gute Bildung …')
  vor search_for entfernen — LLMs ziehen den Header oft ins Zitat mit, was
  PyMuPDFs Volltext-Match scheitern lässt
- v2-Topbar: padding 4px -> 2px, line-height 1.2, min-height entfernt
  (auto-size, nur so hoch wie noetig)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:04:26 +02:00
Dotty Dotter
50c026e3a0 fix(v2): Topbar-Höhe runter, Share-Felder erweitert (Kopieren/LinkedIn/Email/Bild), Smoke-Test 401-Pattern
- Topbar padding 10px -> 4px, min-height 32px (User: 'Header weniger hoch')
- Share-Buttons im Antragsdetail erweitert auf 7 Plattformen analog v1:
  Kopieren (Clipboard), Threads, X, Mastodon, LinkedIn, E-Mail (mailto), Bild (Freepik)
- v2DetailShareCopy/Email/Image-Helper, ANTRAG_TOPICS ans Template uebergeben
- Smoke-Test akzeptiert 401 fuer auth-protected Routen (curl ohne Accept-Header
  bekommt 401-JSON, echte Browser bekommen 302-Redirect via _auth_redirect_handler)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:57:04 +02:00
Dotty Dotter
553e99d14e feat(v2): globaler BL-Selector im Header + Auth-gated Sidebar + Queue-Widget
Bundesland-Auswahl:
- Topbar: einziger BL-Selektor mit localStorage.gwoe.bl-Persistenz
- BL-Felder entfernt aus durchsuchen.html, landtag_suche.html, neu.html, auswertungen.html
- Screens hoeren auf v2-bl-changed CustomEvent + initial via window.v2GetGlobalBl()

Sichtbarkeit (Sidebar):
- Durchsuchen + Tags: immer
- Merkliste / Neuer Antrag / Landtag-Suche / Auswertungen / Export / Feed: nur eingeloggt
- Cluster + Batch-Analyse + Administration: nur Admin

Server-Side Schutz:
- _v2_template_context()-Helper liefert is_authenticated, is_admin, v2_bundeslaender
- HTML-Routen mit Depends(require_auth) bzw. require_admin
- 401/403-Browser-Requests redirecten auf /?login=1 statt JSON-Error

Queue-Widget (#149):
- Neues Component-Partial v2/components/queue_widget.html
- Statusbar unten links + Hover-Tooltip mit den letzten 20 Jobs
- 5s-Polling auf /api/queue/status, blendet sich aus wenn keine Jobs

Smoke-Test angepasst an neue Auth-Erwartungen (302 fuer auth-protected Routen).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:50:36 +02:00
Dotty Dotter
38bffb23fa fix: Job-Polling vor Redirect statt sofortigem Antrag-nicht-gefunden
Vorher: Klick 'Analysieren' -> POST /api/analyze-drucksache -> sofort
window.location.href = '/antrag/{ds}' -> aber Job laeuft noch im Hintergrund
-> Detail-Seite zeigt 'Antrag nicht gefunden'.

Jetzt:
- already_checked -> sofortiger Redirect
- skipped (nicht abstimmbar) -> Hinweistext im Form
- queued -> Polling auf /status/{job_id} alle 2s, max 3 Min
- completed -> Redirect zur Detail-Seite
- failed/rejected -> Fehlermeldung mit Grund

Anwendung in v2/screens/landtag_suche.html + v2/screens/neu.html.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:35:55 +02:00
Dotty Dotter
565849bd84 feat(#139,#129,#138,#141): v2-Frontend (ECOnGOOD-CD), Login-Modal, Auto-DL, OG-Cards
v2-Frontend (#139, ECOnGOOD CD Manual Juni 2024):
- app/static/v2/: tokens.css, fonts.css, v2.css, Nunito-Sans woff2, Phosphor-Icons (21 SVGs)
- app/templates/v2/: base.html + 11 Screens + 8 Component-Macros
- AppShell mit Sidebar (Lesen/Pruefen/Daten/Admin), v2-Detail mit allen Features
  (ScoreHero, MatrixMini, QuoteCard, Redline, Fraktions-Scores)
- v2 ist jetzt Default unter / — classic unter /classic
- Login-Modal in v2-Topbar mit Tabs Anmelden/Registrieren (#129)
- Phosphor-Icons in Sidebar + Topbar mit dynamischem Theme-Toggle
- Keyboard-Shortcuts (j/k/Enter/Esc/?/path), Landtag-Suche, Antrag-Historie,
  Sort-Dropdown, Matrix-Feld-Info-Modal, Bookmarks/Comments/Voting/Share/Re-Analyze

Backend-Erweiterungen:
- main.py: ~30 neue Routes (/v2/*, /antrag/{ds}, /api/auth/{login,refresh,logout},
  /api/me/merkliste/*, /api/admin/*, /v2/admin/*, OG-Cards, etc.)
- og_card.py + og_template: Open-Graph-Bilder via Playwright (#141)
- wahlprogramm_fetch.py + wahlprogramm-links.yaml: SHA-Gate Auto-DL (#138)
- auswertungen.py: BL-Filter + get_wahlperioden Helper (#137)
- auth.py: Direct-Access-Grant + Refresh-Token-Cookie

Classic-Updates:
- Header-DRY via _header.html, Auswertungen redirected, Batch-Inline raus

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:55:57 +02:00
Dotty Dotter
58731af83c feat(db): Merkliste server-seitig + Monitoring-Tabellen + abgeordnetenwatch
- merkliste(user_id, antrag_id, created_at, notiz) (#140 Schema)
- monitoring_scans + monitoring_daily_summary (#135)
- abgeordnetenwatch_polls + abgeordnetenwatch_votes (#106)
- merkliste_add/remove/list/bulk_add Funktionen
- list_all_subscriptions() fuer Admin-View
- get_abstimmungsverhalten(drucksache, bundesland) JOIN-Aggregation
- merkliste, fehlende_programme, share_*, monitoring-Spalten via ALTER TABLE

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:55:16 +02:00
Dotty Dotter
8f0f6d6e32 refactor(#136): DDD-Lightweight Tag 1-4 (Ports, Adapter, Repositories, Domain-Verhalten)
ADR 0008: Lightweight-Migration ohne Package-Split

- ports/llm_bewerter.py: Protocol + LlmRequest-Dataclass
- adapters/qwen_bewerter.py: Qwen/DashScope-Adapter mit Retry-Loop
- repositories/{antrag,bewertung,abonnement}_repository.py: Protocol + Sqlite-Impl + InMemory-Fake
- analyzer.py refactored: nimmt Optional[LlmBewerter], AsyncOpenAI-Import raus
- models.py: 5 Domain-Methoden auf Bewertung/MatrixEntry
  (ist_ablehnung, hat_fundamental_kritisches_feld, verletzt_score_cap, ...)
- analyzer loggt WARNING wenn LLM Score-Cap-Invariante verletzt

Folge-PR: Callsite-Migration in main.py (~21 direkte database.*-Aufrufe)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:55:16 +02:00
Dotty Dotter
2c0e94d29d feat(#106,#135,#128): Monitoring + abgeordnetenwatch + Wahlprogramm-Check
- monitoring.py: taeglicher Scan-Adapter aller aktiven BL, kein Auto-Fetch (#135)
- monitoring_digest.html: Mail-Template mit '0-Kontext'-Hinweis
- abgeordnetenwatch.py + sync_*.py: Phase 1 Roll-Call-Voting (#106)
  - 17 Parlamente (16 BL + BT)
  - 9 BL-spezifische Drucksachen-Patterns + Date-Title-Fallback
  - 28977 Votes fuer BUND in DB
- wahlprogramm_check.py: fehlende Programme erkennen (#128)
- NI-Skip-Liste, NRW Empty-Query-Fallback

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:55:16 +02:00
Dotty Dotter
ad1db2a924 feat: 16 BL-Adapter, Drucksache-Typen, Mail-Digest, Clustering, Redline-Parser
- 16 aktive BL-Adapter + BUND (parlamente.py 3397 LOC)
- drucksache_typen.py: BL-spezifische Typ-Normalisierung (#127)
- mail.py: SMTP + Daily-Digest (#124)
- clustering.py: Embedding-Naehe-Graph + Bubble-Chart (#105)
- redline_utils.py: §INS§/§DEL§-Parser + PDF-Cite-URL-Builder
- embeddings v3->v4 Migration (#123, ADR 0006)
- chart.js + d3.v7 als statische Assets fuer Auswertungen-Cluster

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:54:50 +02:00
Dotty Dotter
4fbdc1522a #114 Dark Mode: CSS-Variables + Toggle + prefers-color-scheme + localStorage 2026-04-10 23:56:29 +02:00
Dotty Dotter
16f8caedc1 #103 Registrierung + Admin-Freischaltung + Matrix-Modal-Fix + Issues
Registrierung:
- POST /api/auth/register: erstellt User in Keycloak mit enabled=false
- GET /api/auth/pending-users: Liste nicht-freigeschalteter User (Admin)
- POST /api/auth/approve-user: User freischalten (Admin)
- Registrierungs-Dialog im Hamburger-Menü
- Admin: "Freischaltungen"-Button (nur sichtbar mit admin-Rolle)

Matrix:
- Zeilen-Header klickbar → Erklärung der Berührungsgruppe mit
  konkretem Lebensalltag-Beispiel
- Spalten-Header klickbar → Erklärung des Werts mit Staatsprinzip
- Feld-Erklärungen: 25 konkrete Bürger:innen-Texte (Schule, Bus,
  Miete, Steuer, Spielplatz...)
- Spalten nummeriert: "1. Menschenwürde" etc.

Neue Issues angelegt:
#104 Zeitreihe, #105 Clustering, #106 Abstimmungsverhalten,
#107 Vergleichsansicht, #108 Empfehlungen, #109 Share-Buttons
2026-04-10 23:53:05 +02:00