feat(g1-b): dsgvo-export um fehlende Kategorien erweitert — Mail/Tasks/Calendar/Comm

Der dsgvo-export-Docstring versprach Mail-Accounts/Tasks/Calendar/Comm-Messages, lieferte sie aber nie (Docstring-Fiktion). _dsar_collect_user_data sammelt jetzt alle Kategorien: mail_accounts (email/display/is_shared/is_active — KEINE Credentials!), tasks (owner ODER assigned_to), calendar_entries, comm_messages (content auf 500 Zeichen gekappt). Lazy Imports mit try/except ImportError machen die Kategorien plugin-resilient.

Beweis: test_g1_dsar 4/4 gruen; py_compile OK.
This commit is contained in:
Agent Zero
2026-08-26 00:19:20 +02:00
parent 23a05593b2
commit 2d17746194
+100
View File
@@ -165,6 +165,7 @@ async def _dsar_collect_user_data(db: Any, tenant_id: str, user_id: str) -> dict
from datetime import UTC, datetime from datetime import UTC, datetime
from uuid import UUID as PyUUID from uuid import UUID as PyUUID
from sqlalchemy import or_ as sa_or_
from sqlalchemy import select as sa_select from sqlalchemy import select as sa_select
from app.models.audit import AuditLog from app.models.audit import AuditLog
@@ -252,6 +253,105 @@ async def _dsar_collect_user_data(db: Any, tenant_id: str, user_id: str) -> dict
for n in notifications for n in notifications
] ]
# ── Categories promised by the dsgvo-export route docstring ──
# (G1-b: mail accounts, tasks, calendar entries, comm messages)
try:
from app.plugins.builtins.mail.models import MailAccount
mail_accounts = (
await db.execute(
sa_select(MailAccount).where(
MailAccount.tenant_id == tid,
MailAccount.user_id == uid,
).limit(500)
)
).scalars().all()
export_data["data"]["mail_accounts"] = [
{
"id": str(a.id),
"email_address": a.email_address,
"display_name": a.display_name,
"is_shared": a.is_shared,
"is_active": a.is_active,
}
for a in mail_accounts
]
except ImportError:
pass
try:
from app.plugins.builtins.tasks.models import Task as TaskModel
tasks = (
await db.execute(
sa_select(TaskModel).where(
sa_or_(TaskModel.owner_id == uid, TaskModel.assigned_to == uid),
TaskModel.tenant_id == tid,
TaskModel.deleted_at.is_(None),
).limit(1000)
)
).scalars().all()
export_data["data"]["tasks"] = [
{
"id": str(t.id),
"title": t.title,
"status": t.status,
"priority": t.priority,
"due_date": t.due_date.isoformat() if t.due_date else None,
}
for t in tasks
]
except ImportError:
pass
try:
from app.plugins.builtins.calendar.models import CalendarEntry as CalEntry
cal_entries = (
await db.execute(
sa_select(CalEntry).where(
CalEntry.tenant_id == tid,
CalEntry.owner_id == uid,
CalEntry.deleted_at.is_(None),
).limit(1000)
)
).scalars().all()
export_data["data"]["calendar_entries"] = [
{
"id": str(e.id),
"title": e.title,
"entry_type": e.entry_type,
"start_at": e.start_at.isoformat() if e.start_at else None,
"end_at": e.end_at.isoformat() if e.end_at else None,
}
for e in cal_entries
]
except ImportError:
pass
try:
from app.plugins.builtins.kommunikation.models import CommMessage
comm_messages = (
await db.execute(
sa_select(CommMessage).where(
CommMessage.sender_id == uid,
CommMessage.tenant_id == tid,
).limit(1000)
)
).scalars().all()
export_data["data"]["comm_messages"] = [
{
"id": str(m.id),
"sender_type": m.sender_type,
"content": m.content[:500],
"created_at": m.created_at.isoformat() if m.created_at else None,
}
for m in comm_messages
]
except ImportError:
pass
return export_data return export_data