gwoe-antragspruefer/app
Dotty Dotter 80e16df288 Append original Antrag-PDF to GWÖ-Report (#9)
Extends generate_pdf_report() with a best-effort second stage that
appends the original Antrag PDF to the freshly rendered GWÖ-Report so
the analysis and its source document live in the same file.

Pipeline
1. WeasyPrint renders the report PDF as before.
2. _append_original_antrag() then:
   - Skips silently if assessment.link is empty or non-HTTP (manual
     uploads / pasted text leave nothing to fetch).
   - Downloads the original PDF via httpx (30s timeout, follow redirects,
     custom user agent).
   - Validates the response is actually a PDF (Content-Length not relied
     on; the magic bytes %PDF- are checked).
   - Adds a single A4 separator page that says "Original-Antrag",
     repeats the Drucksachen-ID and title, and either confirms the
     append or shows the failure reason (HTTP code, network error,
     parse error) plus the source URL.
   - Appends the downloaded PDF via PyMuPDF doc.insert_pdf().
   - Saves to a sibling .tmp file and atomically replaces the original
     (PyMuPDF refuses non-incremental save into the same file).

Edge cases handled
- No link / pasted-text upload → no append, no divider, original report
  unchanged.
- Download error / 404 / non-PDF response → divider page with explicit
  error message and source URL, report still ships.
- PDF parse error → divider page without appended content, error logged.
- Hard failure during save → fall back to the original WeasyPrint PDF.

Verified live in production container against drucksache 8/6645
(Untrending Frauenhass, BÜNDNIS 90/DIE GRÜNEN LSA):
- Report 4 pages + 1 divider + 3 pages original = 8 pages total
- Divider correctly placed at index 4
- Page 5 starts with "(Ausgegeben am 24.02.2026) … Drucksache 8/6645 …
  Antrag — Fraktion BÜNDNIS 90/DIE GRÜNEN — Untrending Frauenhass …"
- Negative test with a synthetic 404 link: 5 pages total, divider at
  index 4 with "Original-PDF konnte nicht angehängt werden. Grund: HTTP
  404".

Resolves #9.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 23:15:05 +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 Activate LSA: Wahlprogramme + ingest + frontend (#2) 2026-04-07 22:12:32 +02:00
templates Bundesland filter & transparency: stringent split + visible source (#8) 2026-04-07 23:00:39 +02:00
__init__.py Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
analyzer.py Refactor wahlprogramme/embeddings/analyzer for multi-state (#5) 2026-04-07 18:48:11 +02:00
bundeslaender.py Activate LSA: Wahlprogramme + ingest + frontend (#2) 2026-04-07 22:12:32 +02:00
config.py Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
database.py Bundesland filter & transparency: stringent split + visible source (#8) 2026-04-07 23:00:39 +02:00
embeddings.py Activate LSA: Wahlprogramme + ingest + frontend (#2) 2026-04-07 22:12:32 +02:00
main.py Bundesland filter & transparency: stringent split + visible source (#8) 2026-04-07 23:00:39 +02:00
models.py Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
parlamente.py Add PortalaAdapter for PADOKA / Sachsen-Anhalt (#2) 2026-04-07 21:50:23 +02:00
report.py Append original Antrag-PDF to GWÖ-Report (#9) 2026-04-07 23:15:05 +02:00
wahlprogramme.py Activate LSA: Wahlprogramme + ingest + frontend (#2) 2026-04-07 22:12:32 +02:00