""" Agent PDF Export — generates printable compliance scan reports. Uses WeasyPrint to convert HTML report to PDF. """ import logging from datetime import datetime, timezone from io import BytesIO logger = logging.getLogger(__name__) def generate_scan_pdf(scan_data: dict) -> bytes: """Generate a PDF report from scan results.""" from weasyprint import HTML html = _build_report_html(scan_data) pdf_buffer = BytesIO() HTML(string=html).write_pdf(pdf_buffer) return pdf_buffer.getvalue() def _severity_color(sev: str) -> str: return {"HIGH": "#dc2626", "CRITICAL": "#991b1b", "MEDIUM": "#ea580c", "LOW": "#2563eb"}.get(sev, "#6b7280") def _build_report_html(data: dict) -> str: """Build HTML for the PDF report.""" url = data.get("url", "") scan_type = data.get("scan_type", "scan") mode = data.get("analysis_mode", "post_launch") findings = data.get("findings", []) services = data.get("services", []) risk = data.get("risk_level", "") score = data.get("risk_score", 0) pages = data.get("pages_scanned", 0) now = datetime.now(timezone.utc).strftime("%d.%m.%Y %H:%M UTC") mode_label = "Live-Website Pruefung" if mode == "post_launch" else "Interne Pruefung" type_label = {"quick": "Schnellanalyse", "scan": "Website-Scan", "consent_test": "Cookie-Test"}.get(scan_type, scan_type) findings_rows = "" for f in findings: sev = f.get("severity", "MEDIUM") if isinstance(f, dict) else "MEDIUM" text = f.get("text", str(f)) if isinstance(f, dict) else str(f) color = _severity_color(sev) findings_rows += f'{sev}{text}' services_rows = "" for s in services: if isinstance(s, dict): status_icon = "✓" if s.get("in_dse") or s.get("status") == "ok" else "✗" status_color = "#16a34a" if status_icon == "✓" else "#dc2626" services_rows += f'{status_icon}{s.get("name","")}{s.get("country","")}{s.get("category","")}' return f"""

Compliance Agent Report

{type_label} | {mode_label} | {now}

URL{url}
Risikobewertung{risk} ({score}/100)
Seiten gescannt{pages}
Findings{len(findings)}
{'
ACHTUNG: Maengel auf einer bereits veroeffentlichten Website. Sofortige Korrektur empfohlen.
' if mode == "post_launch" and findings else ''}

Findings ({len(findings)})

{findings_rows if findings_rows else ''}
SchwereBeschreibung
Keine Findings — alles OK
{'

Dienstleister-Abgleich

' + services_rows + '
StatusDienstLandKategorie
' if services_rows else ''} """