""" P54 — Diff-Banner fuer End-User (USP-Feature). USP-Idee: bei wiederkehrenden Besuchern zeigt das Banner NICHT die Standard-Frage, sondern eine Diff-Mitteilung: "Seit deiner letzten Zustimmung haben wir hinzugefuegt: * Microsoft Bing (Werbung) * TikTok Pixel (Marketing) Bitte erneut zustimmen oder anpassen." Backend-Seite (hier): liefert pro Snapshot eine 'diff_for_user'-Struktur die zum Embedden in eigenen Banner / Hinweistext genutzt werden kann. Frontend-Banner-Lib (separate consent-sdk) konsumiert das. Vergleicht Vendor-Listen zwischen aktuellem Snapshot und dem letzten Snapshot mit gleicher site_domain. """ from __future__ import annotations import logging from typing import Iterable from sqlalchemy import text as sa_text from sqlalchemy.orm import Session logger = logging.getLogger(__name__) def _norm_vendor_set(vendors: Iterable) -> set[str]: out: set[str] = set() for v in (vendors or []): if isinstance(v, dict): n = (v.get("name") or "").strip() elif isinstance(v, str): n = v.strip() else: continue if n: out.add(n) return out def compute_user_facing_diff( db: Session, site_domain: str, current_check_id: str, current_cmp_vendors: list, ) -> dict | None: """Vergleicht aktuelle vs letzte cmp_vendors-Liste fuer die gleiche site_domain. Liefert {prev_at, added_vendors, removed_vendors, new_high_risk_categories} oder None wenn kein vorheriger Lauf.""" if not site_domain: return None try: row = db.execute(sa_text( """ SELECT cmp_vendors, created_at FROM compliance.compliance_check_snapshots WHERE site_domain = :dom AND check_id != :ex ORDER BY created_at DESC LIMIT 1 """ ), {"dom": site_domain, "ex": current_check_id}).fetchone() except Exception as e: logger.warning("diff lookup failed: %s", e) return None if not row: return None prev_vendors = row[0] or [] prev_at = row[1] curr_set = _norm_vendor_set(current_cmp_vendors) prev_set = _norm_vendor_set(prev_vendors) added = sorted(curr_set - prev_set) removed = sorted(prev_set - curr_set) if not added and not removed: return None # High-risk Kategorien aus added Vendors: Marketing / Tracking new_marketing: list[str] = [] for v in current_cmp_vendors: if not isinstance(v, dict): continue n = (v.get("name") or "").strip() cat = (v.get("category") or "").lower() if n in added and cat in ("marketing", "tracking", "advertising"): new_marketing.append(n) return { "prev_at": prev_at.isoformat() if prev_at else None, "added_vendors": added, "removed_vendors": removed, "new_marketing_vendors": new_marketing, "requires_reconsent": bool(new_marketing), } def build_diff_banner_snippet(diff: dict) -> str: """Liefert HTML-Snippet das der Site-Betreiber in seinen eigenen Cookie-Banner einbauen kann (z.B. via consent-sdk).""" if not diff or not diff.get("added_vendors"): return "" added = diff.get("added_vendors", []) n_marketing = len(diff.get("new_marketing_vendors") or []) items = "".join(f"
' f'{n_marketing} neue{"r" if n_marketing == 1 else ""} ' f'Marketing-Anbieter seit Ihrer letzten Zustimmung — ' 'bitte erneut bestaetigen.' '
' ) return ( '' )