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>