""" P88 — PDF-Export der Audit-Mail. Rendert dieselbe HTML wie die Mail via WeasyPrint zu PDF. Endpoint: GET /api/compliance/agent/snapshots/{snapshot_id}/pdf → application/pdf Verwendung: - GF/Lawyer-Uebergabe (kein E-Mail-Programm noetig) - Archivierung - Mandatsausgabe an externen Berater """ from __future__ import annotations import logging from datetime import datetime, timezone from sqlalchemy.orm import Session from compliance.services.check_replay import replay_from_snapshot logger = logging.getLogger(__name__) _PDF_WRAPPER_HEAD = """ {title}

BreakPilot Compliance-Audit — {site}

PDF-Export erstellt am {ts} · Snapshot {snap_short}
""" def render_snapshot_as_pdf( db: Session, snapshot_id: str, ) -> bytes | None: """Returns PDF bytes or None on failure.""" try: from weasyprint import HTML # noqa: WPS433 — Optional dep except Exception as e: logger.error("WeasyPrint nicht verfuegbar: %s", e) return None res = replay_from_snapshot(db, snapshot_id, recipient=None, dry_run=True) if not res or res.get("error"): logger.warning("PDF-Export: Snapshot %s nicht gefunden", snapshot_id) return None # The replay returns html via "preview" (truncated) — fetch the full # render by injecting site_label into a wrapper. full_html = _build_full_html(res, snapshot_id) try: pdf_bytes = HTML(string=full_html).write_pdf() return pdf_bytes except Exception as e: logger.exception("WeasyPrint PDF render failed: %s", e) return None def _build_full_html(replay_result: dict, snapshot_id: str) -> str: """Wraps the replay's full_html in the PDF-print wrapper.""" full = replay_result.get("full_html") or replay_result.get("preview") or "" site = replay_result.get("site_domain") or "—" snap_short = snapshot_id[:8] ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC") header = _PDF_WRAPPER_HEAD.format( title=f"BreakPilot Audit — {site}", site=site, snap_short=snap_short, ts=ts, ) return header + full + ""