""" Executive-Summary-Block — der oberste Email-Abschnitt. Zeigt CFO / GF in 4 Zahlen den Gesamt-Mehrwert des Compliance-Checks: 1) Compliance-Score (Trend vs Vorlauf) 2) Anzahl analysierter Anbieter 3) Geschaetztes jaehrliches Sparpotenzial (Range) 4) Konsolidierungs-Potenzial (Anbieter koennen reduziert werden) Plus zwei Big-CTA-Buttons: - "Compliance-Maengel im Detail" → springt zum Doc-Pruefungs-Block - "Konsolidierungs-Plan ansehen" → springt zum Redundanz-Block Ziel: in 5 Sekunden sieht der Vorstand den ROI. Wenn neugierig, scrollt er weiter in die Detail-Bloecke (die UNTER dieser Summary liegen). """ from __future__ import annotations def _fmt_eur_range(low: int, high: int) -> str: if not low and not high: return "—" if low == high: return f"~{low:,} €".replace(",", ".") return f"{low:,}–{high:,} €".replace(",", ".") def build_exec_summary_html( scorecard: dict | None, previous_scorecard: dict | None, cmp_vendors: list[dict] | None, redundancy_report: dict | None, site_name: str = "", ) -> str: """Build the top-of-email Executive Summary with 4 KPIs + 2 CTAs.""" # 1) Compliance-Score pct = 0 delta_str = "" score_color = "#94a3b8" if scorecard: totals = scorecard.get("totals") or {} pct = int(totals.get("pct", 0)) score_color = ("#16a34a" if pct >= 80 else "#d97706" if pct >= 50 else "#dc2626") if previous_scorecard: prev_pct = int((previous_scorecard.get("totals") or {}).get("pct", 0)) d = pct - prev_pct if d: trend_color = "#16a34a" if d > 0 else "#dc2626" delta_str = ( f'' f'{"+" if d > 0 else ""}{d} pp' ) # 2) Vendor-Count n_vendors = len(cmp_vendors or []) # 3+4) Saving + Konsolidierung s = (redundancy_report or {}).get("summary") or {} sav_low, sav_high = s.get("estimated_saving_year_eur", [0, 0]) n_consolidation = s.get("consolidation_potential", 0) sav_pct = s.get("estimated_saving_pct", "—") parts = [ '
| '
f' DSGVO / TDDDG / TMG Score '
f''
f'{pct}%{delta_str} '
f''
f'aus {int((scorecard or {}).get("totals", {}).get("total", 0))} Pflicht-Pruefungen '
f' | ',
f''
f' Identifizierte Anbieter '
f'{n_vendors} '
f''
f'davon {n_consolidation} konsolidierbar '
f' | ',
'
| '
f' '
f'Geschaetztes Sparpotenzial pro Jahr (Tool-Lizenzen, ohne Media-Spend) '
f''
f'{_fmt_eur_range(sav_low, sav_high)}'
f'({sav_pct}) '
f''
f'durch Konsolidierung redundanter Anbieter auf je 1 EU-Tool pro '
f'Funktions-Kategorie. Schaetzbereich, mit dem Einkauf zu verifizieren.'
f' | ',
'|