feat(sdk): VVT master libraries, process templates, Loeschfristen profiling + document
VVT: Master library tables (7 catalogs), 500+ seed entries, process templates with instantiation, library API endpoints + 18 tests. Loeschfristen: Baseline catalog, compliance checks, profiling engine, HTML document generator, MkDocs documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -174,6 +174,20 @@ def _activity_to_response(act: VVTActivityDB) -> VVTActivityResponse:
|
||||
next_review_at=act.next_review_at,
|
||||
created_by=act.created_by,
|
||||
dsfa_id=str(act.dsfa_id) if act.dsfa_id else None,
|
||||
# Library refs
|
||||
purpose_refs=act.purpose_refs,
|
||||
legal_basis_refs=act.legal_basis_refs,
|
||||
data_subject_refs=act.data_subject_refs,
|
||||
data_category_refs=act.data_category_refs,
|
||||
recipient_refs=act.recipient_refs,
|
||||
retention_rule_ref=act.retention_rule_ref,
|
||||
transfer_mechanism_refs=act.transfer_mechanism_refs,
|
||||
tom_refs=act.tom_refs,
|
||||
source_template_id=act.source_template_id,
|
||||
risk_score=act.risk_score,
|
||||
linked_loeschfristen_ids=act.linked_loeschfristen_ids,
|
||||
linked_tom_measure_ids=act.linked_tom_measure_ids,
|
||||
art30_completeness=act.art30_completeness,
|
||||
created_at=act.created_at,
|
||||
updated_at=act.updated_at,
|
||||
)
|
||||
@@ -336,6 +350,107 @@ async def delete_activity(
|
||||
return {"success": True, "message": f"Activity {activity_id} deleted"}
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Art. 30 Completeness Check
|
||||
# ============================================================================
|
||||
|
||||
@router.get("/activities/{activity_id}/completeness")
|
||||
async def get_activity_completeness(
|
||||
activity_id: str,
|
||||
tid: str = Depends(get_tenant_id),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Calculate Art. 30 completeness score for a VVT activity."""
|
||||
act = db.query(VVTActivityDB).filter(
|
||||
VVTActivityDB.id == activity_id,
|
||||
VVTActivityDB.tenant_id == tid,
|
||||
).first()
|
||||
if not act:
|
||||
raise HTTPException(status_code=404, detail=f"Activity {activity_id} not found")
|
||||
return _calculate_completeness(act)
|
||||
|
||||
|
||||
def _calculate_completeness(act: VVTActivityDB) -> dict:
|
||||
"""Calculate Art. 30 completeness — required fields per DSGVO Art. 30 Abs. 1."""
|
||||
missing = []
|
||||
warnings = []
|
||||
total_checks = 10
|
||||
passed = 0
|
||||
|
||||
# 1. Name/Zweck
|
||||
if act.name:
|
||||
passed += 1
|
||||
else:
|
||||
missing.append("name")
|
||||
|
||||
# 2. Verarbeitungszwecke
|
||||
has_purposes = bool(act.purposes) or bool(act.purpose_refs)
|
||||
if has_purposes:
|
||||
passed += 1
|
||||
else:
|
||||
missing.append("purposes")
|
||||
|
||||
# 3. Rechtsgrundlage
|
||||
has_legal = bool(act.legal_bases) or bool(act.legal_basis_refs)
|
||||
if has_legal:
|
||||
passed += 1
|
||||
else:
|
||||
missing.append("legal_bases")
|
||||
|
||||
# 4. Betroffenenkategorien
|
||||
has_subjects = bool(act.data_subject_categories) or bool(act.data_subject_refs)
|
||||
if has_subjects:
|
||||
passed += 1
|
||||
else:
|
||||
missing.append("data_subjects")
|
||||
|
||||
# 5. Datenkategorien
|
||||
has_categories = bool(act.personal_data_categories) or bool(act.data_category_refs)
|
||||
if has_categories:
|
||||
passed += 1
|
||||
else:
|
||||
missing.append("data_categories")
|
||||
|
||||
# 6. Empfaenger
|
||||
has_recipients = bool(act.recipient_categories) or bool(act.recipient_refs)
|
||||
if has_recipients:
|
||||
passed += 1
|
||||
else:
|
||||
missing.append("recipients")
|
||||
|
||||
# 7. Drittland-Uebermittlung (checked but not strictly required)
|
||||
passed += 1 # always passes — no transfer is valid state
|
||||
|
||||
# 8. Loeschfristen
|
||||
has_retention = bool(act.retention_period and act.retention_period.get('description')) or bool(act.retention_rule_ref)
|
||||
if has_retention:
|
||||
passed += 1
|
||||
else:
|
||||
missing.append("retention_period")
|
||||
|
||||
# 9. TOM-Beschreibung
|
||||
has_tom = bool(act.tom_description) or bool(act.tom_refs) or bool(act.structured_toms)
|
||||
if has_tom:
|
||||
passed += 1
|
||||
else:
|
||||
missing.append("tom_description")
|
||||
|
||||
# 10. Verantwortlicher
|
||||
if act.responsible:
|
||||
passed += 1
|
||||
else:
|
||||
missing.append("responsible")
|
||||
|
||||
# Warnings
|
||||
if act.dpia_required and not act.dsfa_id:
|
||||
warnings.append("dpia_required_but_no_dsfa_linked")
|
||||
if act.third_country_transfers and not act.transfer_mechanism_refs:
|
||||
warnings.append("third_country_transfer_without_mechanism")
|
||||
|
||||
score = int((passed / total_checks) * 100)
|
||||
return {"score": score, "missing": missing, "warnings": warnings, "passed": passed, "total": total_checks}
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Audit Log
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user