gwoe-antragspruefer/app
Dotty Dotter 4ec6190416 #47 PDF Zitat-Highlighting via PyMuPDF Single-Page-Render
Klick auf eine Zitat-Quelle im Report öffnet jetzt eine 1-Seiten-PDF-
Variante des Wahlprogramms mit gelb markiertem Snippet, statt nur zum
Page-Anchor zu springen und den Leser selbst suchen zu lassen.

Implementation:

embeddings.render_highlighted_page(programm_id, seite, query)
- Validiert programm_id gegen PROGRAMME (Path-Traversal-Schutz)
- Lädt das volle Wahlprogramm-PDF, extrahiert via insert_pdf nur die
  angeforderte Seite in einen neuen Document → kleinere Response
- search_for(query[:200]) → Bounding-Boxes aller Treffer
- Fallback: 5-Wort-Anker wenn Volltext-Match leer (LLM-Truncation,
  identisch zu find_chunk_for_text/Sub-D-Logik)
- add_highlight_annot mit gelber stroke-Color (1.0, 0.93, 0.0)
- Returns serialisierte PDF-Bytes oder None

embeddings._chunk_pdf_url
- Wenn chunk["text"] vorhanden: emittiert /api/wahlprogramm-cite-URL
  mit pid=, seite=, q=urlencoded(text[:200])
- Sonst: alter statischer /static/referenzen/X.pdf#page=N (Pre-#47
  rückwärts-kompatibel)
- text wird auf 200 Zeichen abgeschnitten, sonst blasen
  500-Zeichen-Snippets jedes Assessment-JSON auf

main.py /api/wahlprogramm-cite Endpoint
- Validiert pid gegen PROGRAMME registry
- seite: 1 ≤ n ≤ 2000
- Response: application/pdf, Cache-Control max-age=86400
- 404 bei unknown pid oder fehlendem PDF, 400 bei seite out of range

Reconstruct-Pipeline (Issue #60 Option B) zieht das automatisch durch:
reconstruct_zitate ruft _chunk_pdf_url(matched_chunk) auf, der jetzt
bevorzugt die Cite-URL emittiert. Keine Änderung an reconstruct_zitate
selbst nötig.

Tests: 194/194 grün (185 + 9 neue):

- TestChunkPdfUrl: 4 Cases (cite vs static, unknown prog, 200-char-truncate)
- TestRenderHighlightedPage: 5 Cases (unknown pid, invalid seite, valid
  render, empty query, query-not-found-falls-back-zu-leerem-Highlight)
- Plus Bridge im Test-Stub: pymupdf-as-fitz Shim falls eine
  third-party "fitz" das Pkg shadowt (kommt auf älteren Dev-Setups vor)

Refs: #47
2026-04-10 01:09:45 +02:00
..
kontext Activate LSA: Wahlprogramme + ingest + frontend (#2) 2026-04-07 22:12:32 +02:00
routers Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
static/referenzen Add 30 Wahlprogramme für TH/BB/HH/SH/BW/RP (#37, #39, #40, #32, #41, #42) 2026-04-09 08:03:11 +02:00
templates Phase E (substituted): Auswertungen-Drilldown-Modal (#59) 2026-04-09 11:30:10 +02:00
__init__.py Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
analyzer.py #60 Reopen — Option B: server-side reconstruct of zitat quelle/url 2026-04-09 22:52:17 +02:00
auswertungen.py Phase C: Auswertungen-Dashboard #58 + CSV-Export #45 (Roadmap #59) 2026-04-09 11:25:57 +02:00
bundeslaender.py Phase J: SN EDAS-XML-Adapter (#26/#38) — Sachsen aktiv via XML-Export 2026-04-09 14:39:03 +02:00
config.py Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
database.py Phase A: Audit-Restbefunde #57.3/4/7 (Roadmap #59) 2026-04-09 11:15:16 +02:00
embeddings.py #47 PDF Zitat-Highlighting via PyMuPDF Single-Page-Render 2026-04-10 01:09:45 +02:00
main.py #47 PDF Zitat-Highlighting via PyMuPDF Single-Page-Render 2026-04-10 01:09:45 +02:00
models.py Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
parlamente.py #23 BayernAdapter — TYPO3-Solr HTML scraping (Anträge in WP19) 2026-04-10 01:00:47 +02:00
parteien.py Phase J: SN EDAS-XML-Adapter (#26/#38) — Sachsen aktiv via XML-Export 2026-04-09 14:39:03 +02:00
report.py Security hotfixes #1, #2, #6 from audit (#57) 2026-04-09 10:45:43 +02:00
validators.py Phase A: Audit-Restbefunde #57.3/4/7 (Roadmap #59) 2026-04-09 11:15:16 +02:00
wahlperioden.py Phase C: Auswertungen-Dashboard #58 + CSV-Export #45 (Roadmap #59) 2026-04-09 11:25:57 +02:00
wahlprogramme.py Add 30 Wahlprogramme für TH/BB/HH/SH/BW/RP (#37, #39, #40, #32, #41, #42) 2026-04-09 08:03:11 +02:00