feat: DSR User Data Export — Art. 15 PDF + Art. 20 JSON/CSV
- DSRExportService: aggregates all CMP data about a user from
Banner Consents, Einwilligungen, Audit Trail, DSR History
- GET /dsr/{id}/export-user-data?format=json|csv|pdf endpoint
- PDF: A4 reportlab with 4 sections (Consents, Einwilligungen,
Audit-Trail, DSR-Anfragen) + cover page
- CSV: BOM-encoded for Excel with flattened data rows
- JSON: structured export with all data categories
- ActionButtons.tsx: PDF/JSON/CSV export buttons now functional
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -367,3 +367,42 @@ async def update_exception_check(
|
||||
):
|
||||
with translate_domain_errors():
|
||||
return svc.update_exception_check(dsr_id, check_id, body, tenant_id)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# User Data Export (Art. 15 / Art. 20)
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@router.get("/{dsr_id}/export-user-data")
|
||||
async def export_user_data(
|
||||
dsr_id: str,
|
||||
format: str = Query("json"),
|
||||
tenant_id: str = Depends(_get_tenant),
|
||||
svc: DSRService = Depends(_dsr_svc),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Export all CMP data about the data subject as JSON, CSV, or PDF."""
|
||||
import io
|
||||
from compliance.services.dsr_export_service import DSRExportService
|
||||
|
||||
with translate_domain_errors():
|
||||
dsr = svc.get(dsr_id, tenant_id)
|
||||
email = dsr.get("requester_email")
|
||||
if not email:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(400, "DSR has no requester email")
|
||||
|
||||
export_svc = DSRExportService(db)
|
||||
if format == "pdf":
|
||||
content, filename = export_svc.export_pdf(tenant_id, email)
|
||||
return StreamingResponse(io.BytesIO(content), media_type="application/pdf",
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'})
|
||||
elif format == "csv":
|
||||
content, filename = export_svc.export_csv(tenant_id, email)
|
||||
return StreamingResponse(io.BytesIO(content), media_type="text/csv",
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'})
|
||||
else:
|
||||
content, filename = export_svc.export_json(tenant_id, email)
|
||||
return StreamingResponse(io.BytesIO(content), media_type="application/json",
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'})
|
||||
|
||||
Reference in New Issue
Block a user