""" P18 — Erweiterter Banner-Block fuer die Email. Rendert die Daten aus dem consent-tester die heute weggeworfen wurden: - 3-Phasen-Cookie-Tabelle (before_consent / after_reject / after_accept) - Banner-Quality-Score (completeness/correctness/violations) - Per-Category-Tracker-Listing - Violations-Liste mit Rechtsgrundlagen """ from __future__ import annotations def _color_for(pct: int) -> str: return ("#16a34a" if pct >= 80 else "#d97706" if pct >= 50 else "#dc2626") def _short_phase_label(key: str) -> str: return { "before_consent": "Vor Consent", "after_reject": "Nach Ablehnung", "after_accept": "Nach Annahme", }.get(key, key) def _phase_color(key: str, cookie_count: int) -> str: if key == "before_consent": return "#16a34a" if cookie_count == 0 else "#dc2626" if key == "after_reject": return "#16a34a" if cookie_count <= 1 else "#d97706" return "#94a3b8" def build_banner_deep_html(banner_result: dict | None) -> str: """Render: Banner-Quality + Phases + Violations. Konsumiert das volle consent-tester-Response. Komplementiert `build_provider_list_html` (das nur Summary + TCF-Vendor-Tabelle macht). """ if not banner_result: return "" parts: list[str] = [ '
' '

' 'Cookie-Banner — technische Analyse

' ] # 1) Quality-Score-Cards compl = banner_result.get("completeness_pct") corr = banner_result.get("correctness_pct") summary = banner_result.get("summary") or {} n_critical = summary.get("critical", 0) n_high = summary.get("high", 0) if compl is not None or corr is not None: parts.append( '' ) if compl is not None: c = _color_for(int(compl)) parts.append( f'' ) if corr is not None: c = _color_for(int(corr)) parts.append( f'' ) viol_c = ("#dc2626" if n_critical + n_high > 0 else "#d97706" if (summary.get("total_violations") or 0) > 0 else "#16a34a") parts.append( f'' ) parts.append('
' f'
' f'Vollstaendigkeit
' f'
{compl}%
' f'
' f'
' f'Korrektheit
' f'
{corr}%
' f'
' f'
' f'Verstoesse
' f'
' f'{summary.get("total_violations", 0)}' f'' f'(crit:{n_critical} high:{n_high})
') # 2) 3-Phasen-Tabelle phases = banner_result.get("phases") or {} if phases: parts.append( '
Cookie-Setzungen pro Phase ' '(echter Browser-Test):
' '' '' '' '' '' '' '' ) for key in ("before_consent", "after_reject", "after_accept"): ph = phases.get(key) or {} if not isinstance(ph, dict): continue cookies = ph.get("cookies") or [] trackers = ph.get("tracking_services") or [] new_track = ph.get("new_tracking") or [] violations = ph.get("violations") or [] undoc = ph.get("undocumented") or [] color = _phase_color(key, len(cookies)) issues_parts = [] if violations: issues_parts.append(f"{len(violations)} Verstoss") if new_track: issues_parts.append(f"{len(new_track)} neue Tracker") if undoc: issues_parts.append(f"{len(undoc)} undokumentiert") issues_str = ", ".join(issues_parts) or "—" parts.append( f'' f'' f'' f'' f'' f'' ) parts.append('
PhaseCookiesTrackerAuffaelligkeiten
' f'' f'{_short_phase_label(key)}{len(cookies)}{len(trackers)}{issues_str}
') # 3) Per-Category-Tracker cats = banner_result.get("category_tests") or [] if cats: non_essential = [c for c in cats if c.get("category") != "necessary"] if non_essential: parts.append( '
Provider-Listing pro Banner-Kategorie:
' '' '' '' '' '' '' ) for c in non_essential: n = len(c.get("tracking_services") or []) label = c.get("category_label") or c.get("category", "?") if n == 0: color = "#dc2626" hint = ("Keine Anbieter sichtbar — Nutzer kann nicht " "informiert einwilligen (Art. 7 DSGVO)") else: color = "#16a34a" hint = "" parts.append( f'' f'' f'' f'' ) parts.append('
KategorieAnbieterHinweis
{label}{n}' f'{hint}
') # 4) Violations mit Rechtsgrundlage violations = (banner_result.get("banner_checks") or {}).get("violations", []) if violations: parts.append( '
Erkannte Banner-Verstoesse:
' '') parts.append('
') return "".join(parts)