feat: HTML email format, tab info hints, scan history
- Summary now renders as styled HTML (table layout, colored risk badge, warning banners) instead of plaintext in <div> - Tab info text explains scope: "Analysiert nur die eingegebene URL" vs "Scannt automatisch 5-10 Unterseiten" - Scan history with findings count badge and page count Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -105,7 +105,7 @@ async def analyze_url(req: AnalyzeRequest):
|
||||
email_result = send_email(
|
||||
recipient=req.recipient,
|
||||
subject=f"[{mode_label}] Compliance-Finding: {classification} — {req.url[:60]}",
|
||||
body_html=f"<div>{summary}</div>",
|
||||
body_html=summary,
|
||||
)
|
||||
|
||||
return AnalyzeResponse(
|
||||
@@ -349,53 +349,77 @@ def _risk_to_escalation(risk_level: str) -> str:
|
||||
return mapping.get(risk_level.upper() if risk_level else "", "E0")
|
||||
|
||||
|
||||
DOC_TYPE_LABELS = {
|
||||
"privacy_policy": "Datenschutzerklaerung",
|
||||
"cookie_banner": "Cookie-Banner",
|
||||
"terms_of_service": "AGB",
|
||||
"imprint": "Impressum",
|
||||
"dpa": "Auftragsverarbeitung (AVV)",
|
||||
"other": "Sonstiges",
|
||||
}
|
||||
|
||||
RISK_COLORS = {
|
||||
"MINIMAL": ("#16a34a", "Niedrig"),
|
||||
"LOW": ("#ca8a04", "Gering"),
|
||||
"LIMITED": ("#ea580c", "Mittel"),
|
||||
"HIGH": ("#dc2626", "Hoch"),
|
||||
"UNACCEPTABLE": ("#991b1b", "Kritisch"),
|
||||
}
|
||||
|
||||
|
||||
def _build_summary(
|
||||
url: str, classification: str, assessment: dict, role: str,
|
||||
findings_str: list[str], controls_str: list[str],
|
||||
mode: str = "post_launch",
|
||||
) -> str:
|
||||
"""Build a German manager summary, adapted to pre/post-launch context."""
|
||||
"""Build HTML summary for email and frontend."""
|
||||
risk = assessment.get("risk_level", "unbekannt")
|
||||
score = assessment.get("risk_score", 0)
|
||||
recommendation = assessment.get("recommendation", "")
|
||||
dsfa = assessment.get("dsfa_recommended", False)
|
||||
is_live = mode == "post_launch"
|
||||
risk_color, risk_label = RISK_COLORS.get(risk, ("#6b7280", risk))
|
||||
doc_label = DOC_TYPE_LABELS.get(classification, classification)
|
||||
|
||||
findings_text = "\n".join(f"- {f}" for f in findings_str[:5]) if findings_str else "Keine"
|
||||
controls_text = "\n".join(f"- {c}" for c in controls_str[:5]) if controls_str else "Keine"
|
||||
|
||||
mode_header = (
|
||||
"PRUEFUNG LIVE-WEBSITE — Das Dokument ist bereits oeffentlich zugaenglich."
|
||||
mode_banner = (
|
||||
'<div style="background:#fef2f2;border-left:4px solid #dc2626;padding:12px 16px;margin-bottom:16px;">'
|
||||
'<strong style="color:#991b1b;">LIVE-WEBSITE</strong> — Das Dokument ist bereits oeffentlich zugaenglich.</div>'
|
||||
if is_live else
|
||||
"INTERNE PRUEFUNG — Das Dokument ist noch nicht veroeffentlicht."
|
||||
'<div style="background:#eff6ff;border-left:4px solid #3b82f6;padding:12px 16px;margin-bottom:16px;">'
|
||||
'<strong style="color:#1e40af;">INTERNE PRUEFUNG</strong> — Dokument noch nicht veroeffentlicht.</div>'
|
||||
)
|
||||
|
||||
parts = [
|
||||
mode_header,
|
||||
"",
|
||||
f"Dokumenttyp: {classification}",
|
||||
f"Quelle: {url}",
|
||||
f"Risikobewertung: {risk} ({score}/100)",
|
||||
f"Zustaendig: {role}",
|
||||
f"DSFA empfohlen: {'Ja' if dsfa else 'Nein'}",
|
||||
"",
|
||||
f"Findings:\n{findings_text}",
|
||||
"",
|
||||
f"Erforderliche Massnahmen:\n{controls_text}",
|
||||
]
|
||||
findings_html = "".join(f'<li style="margin-bottom:4px;">{f}</li>' for f in findings_str[:8]) if findings_str else '<li style="color:#6b7280;">Keine</li>'
|
||||
controls_html = "".join(f'<li style="margin-bottom:4px;">{c}</li>' for c in controls_str[:8]) if controls_str else '<li style="color:#6b7280;">Keine</li>'
|
||||
|
||||
warning = ""
|
||||
if is_live and findings_str:
|
||||
parts.extend([
|
||||
"",
|
||||
"ACHTUNG: Diese Maengel sind bereits oeffentlich sichtbar. "
|
||||
"Sofortige Nachbesserung empfohlen um Abmahnrisiken zu minimieren.",
|
||||
])
|
||||
warning = (
|
||||
'<div style="background:#fef2f2;border:1px solid #fecaca;border-radius:8px;padding:12px 16px;margin-top:16px;">'
|
||||
'<strong style="color:#dc2626;">⚠ ACHTUNG:</strong> Diese Maengel sind bereits oeffentlich sichtbar. '
|
||||
'Sofortige Nachbesserung empfohlen um Abmahnrisiken zu minimieren.</div>'
|
||||
)
|
||||
elif not is_live and controls_str:
|
||||
parts.extend([
|
||||
"",
|
||||
"Empfehlung: Implementieren Sie die erforderlichen Kontrollen vor der Veroeffentlichung.",
|
||||
])
|
||||
warning = (
|
||||
'<div style="background:#f0fdf4;border:1px solid #bbf7d0;border-radius:8px;padding:12px 16px;margin-top:16px;">'
|
||||
'Empfehlung: Implementieren Sie die erforderlichen Kontrollen vor der Veroeffentlichung.</div>'
|
||||
)
|
||||
|
||||
if recommendation:
|
||||
parts.extend(["", f"Weitere Empfehlung: {recommendation}"])
|
||||
return "\n".join(parts)
|
||||
rec_html = f'<p style="color:#475569;margin-top:12px;"><em>{recommendation}</em></p>' if recommendation else ""
|
||||
|
||||
return f"""
|
||||
{mode_banner}
|
||||
<table style="width:100%;border-collapse:collapse;margin-bottom:16px;">
|
||||
<tr><td style="padding:6px 0;color:#64748b;width:180px;">Dokumenttyp</td><td style="padding:6px 0;font-weight:600;">{doc_label}</td></tr>
|
||||
<tr><td style="padding:6px 0;color:#64748b;">Quelle</td><td style="padding:6px 0;"><a href="{url}" style="color:#6366f1;">{url}</a></td></tr>
|
||||
<tr><td style="padding:6px 0;color:#64748b;">Risikobewertung</td><td style="padding:6px 0;"><span style="background:{risk_color};color:white;padding:2px 8px;border-radius:4px;font-size:13px;">{risk_label} ({score}/100)</span></td></tr>
|
||||
<tr><td style="padding:6px 0;color:#64748b;">Zustaendig</td><td style="padding:6px 0;font-weight:600;">{role}</td></tr>
|
||||
<tr><td style="padding:6px 0;color:#64748b;">DSFA empfohlen</td><td style="padding:6px 0;">{'Ja' if dsfa else 'Nein'}</td></tr>
|
||||
</table>
|
||||
<h3 style="color:#1e293b;font-size:15px;margin:16px 0 8px;">Findings</h3>
|
||||
<ul style="margin:0;padding-left:20px;color:#334155;">{findings_html}</ul>
|
||||
<h3 style="color:#1e293b;font-size:15px;margin:16px 0 8px;">Erforderliche Massnahmen</h3>
|
||||
<ul style="margin:0;padding-left:20px;color:#334155;">{controls_html}</ul>
|
||||
{warning}
|
||||
{rec_html}
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user