diff --git a/backend-compliance/compliance/api/agent_compliance_check_routes.py b/backend-compliance/compliance/api/agent_compliance_check_routes.py index 7f3fe20a..a514711f 100644 --- a/backend-compliance/compliance/api/agent_compliance_check_routes.py +++ b/backend-compliance/compliance/api/agent_compliance_check_routes.py @@ -1220,6 +1220,16 @@ async def _run_compliance_check(check_id: str, req: ComplianceCheckRequest): except Exception as e: logger.warning("P73 MC-Solution-Generator skipped: %s", e) + # P71: JC-vs-AVV Entscheidungsbaum (nur wenn DSE ambig) + jc_decision_html = "" + try: + from compliance.services.jc_avv_decision import ( + build_jc_avv_decision_html, + ) + jc_decision_html = build_jc_avv_decision_html(doc_texts.get("dse")) + except Exception as e: + logger.warning("P71 jc_avv_decision skipped: %s", e) + # P82: GF-1-Pager ganz oben in der Mail — 5-Bullet-Zusammenfassung # damit die GF nicht 124k Char lesen muss. gf_one_pager_html = "" @@ -1285,6 +1295,7 @@ async def _run_compliance_check(check_id: str, req: ComplianceCheckRequest): + scorecard_html + redundancy_html + providers_html + banner_deep_html + library_mismatch_html + consistency_html + signals_html + solutions_html + + jc_decision_html + vvt_html + report_html ) diff --git a/backend-compliance/compliance/services/jc_avv_decision.py b/backend-compliance/compliance/services/jc_avv_decision.py new file mode 100644 index 00000000..599f3e84 --- /dev/null +++ b/backend-compliance/compliance/services/jc_avv_decision.py @@ -0,0 +1,116 @@ +""" +P71 — JC-vs-AVV Entscheidungsbaum. + +Hilft dem Nutzer zu bestimmen, ob ein bestimmtes Verarbeitungsverhaeltnis +gemeinsame Verantwortlichkeit (Art. 26 DSGVO) oder Auftragsverarbeitung +(Art. 28 DSGVO) ist. EDPB 7/2020 ist die Grundlage. + +Wird gerendert als kleiner Block am Ende der Mail, wenn im DSE-Text +Konstrukte vorkommen die ambivalent sind (z.B. 'gemeinsame Auswertung +mit Schwesterunternehmen', 'gemeinsame Plattform-Nutzung'). Liefert +3-4 Leitfragen + jeweilige Empfehlung. +""" + +from __future__ import annotations + +import logging + +logger = logging.getLogger(__name__) + +_JC_SIGNALS = ( + "schwesterunternehmen", "konzernschwester", "gemeinsame plattform", + "gemeinsame auswertung", "gemeinsame studie", "joint venture", + "konzernweite analyse", "gemeinsame zwecke", "gemeinsame ziele", + "konzernweit", "gemeinsamer kunde", "gemeinsamer datenpool", +) + +_AVV_SIGNALS = ( + "auftragsverarbeiter", "auftragsverarbeitung", "weisungsgebunden", + "im auftrag von", "im namen des verantwortlichen", + "art. 28 dsgvo", "art 28 dsgvo", "dpa (data processing agreement", +) + +_QUESTIONS = [ + { + "q": "Bestimmen beide Seiten gemeinsam Zweck UND Mittel der Verarbeitung?", + "yes": "JC (Art. 26)", + "no": "AVV-Indikator", + "explain": "EDPB 7/2020 Rn. 51-65: beidseitige Zweckbestimmung ist " + "das Hauptmerkmal der gemeinsamen Verantwortlichkeit.", + }, + { + "q": "Verfolgen die Parteien eigene, getrennte Zwecke (z.B. eigene " + "Kundenbeziehung) oder einen gemeinsamen Zweck?", + "yes": "Wenn getrennt: AVV (oder zwei getrennte Verantwortliche)", + "no": "Wenn gemeinsam: JC (Art. 26)", + "explain": "EuGH C-25/17 Zeugen Jehovas: getrennte Zwecke " + "schliessen JC aus.", + }, + { + "q": "Existiert eine schriftliche Weisungs-Hierarchie und Pflicht " + "zur Loeschung am Vertragsende?", + "yes": "AVV (Art. 28 Pflichten erfuellt)", + "no": "Pruefen ob JC vorliegt + Art. 26-Vereinbarung noetig", + "explain": "Art. 28 (3)(g) DSGVO + EDPB 7/2020 Rn. 88.", + }, + { + "q": "Haben Betroffene gegenueber beiden Stellen vollstaendige " + "Rechte (Art. 15-22)?", + "yes": "JC — Art. 26 (3) verlangt einheitliche Anlaufstelle", + "no": "AVV — Auftragsverarbeiter weist Rechtsausuebung an " + "Verantwortlichen zurueck", + "explain": "Art. 26 (3) DSGVO macht beide Stellen als gemeinsame " + "Anlaufstelle ansprechbar.", + }, +] + + +def detect_ambiguous_jc_avv(dse_text: str | None) -> bool: + """Heuristik: liegen sowohl JC- als auch AVV-Signale im DSE? Dann + ist die Konstellation typischerweise unklar und der Entscheidungsbaum + hilft.""" + if not dse_text: + return False + t = dse_text.lower() + has_jc = any(s in t for s in _JC_SIGNALS) + has_avv = any(s in t for s in _AVV_SIGNALS) + return has_jc and has_avv + + +def build_jc_avv_decision_html(dse_text: str | None) -> str: + if not detect_ambiguous_jc_avv(dse_text): + return "" + items = [] + for i, q in enumerate(_QUESTIONS, 1): + items.append( + f'
' + 'Pruefen Sie mit dem DSB die folgenden 4 Leitfragen aus EDPB 7/2020. ' + 'Das Ergebnis bestimmt ob eine Art. 26-Vereinbarung (JC) oder ein ' + 'Art. 28-AVV vorliegen muss.' + '
' + 'Quelle: EDPB Guidelines 7/2020 (Controller/Processor) ' + '+ EuGH C-25/17, C-40/17.
' + '