"""
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)})
| Schwere | Beschreibung |
{findings_rows if findings_rows else '| Keine Findings — alles OK |
'}
{'Dienstleister-Abgleich
| Status | Dienst | Land | Kategorie |
' + services_rows + '
' if services_rows else ''}
"""