Split notfallplan_routes.py (1018 LOC) into clean architecture layers: - compliance/schemas/notfallplan.py (146 LOC): all Pydantic models - compliance/services/notfallplan_service.py (500 LOC): contacts, scenarios, checklists, exercises, stats - compliance/services/notfallplan_workflow_service.py (309 LOC): incidents, templates - compliance/api/notfallplan_routes.py (361 LOC): thin handlers with domain error translation All 250 tests pass. Schemas re-exported via __all__ for legacy test imports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
147 lines
4.1 KiB
Python
147 lines
4.1 KiB
Python
"""
|
|
Notfallplan (Emergency Plan) schemas -- Art. 33/34 DSGVO.
|
|
|
|
Phase 1 Step 4: extracted from ``compliance.api.notfallplan_routes``.
|
|
"""
|
|
|
|
from typing import Any, List, Optional
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
# ============================================================================
|
|
# Contacts
|
|
# ============================================================================
|
|
|
|
|
|
class ContactCreate(BaseModel):
|
|
name: str
|
|
role: Optional[str] = None
|
|
email: Optional[str] = None
|
|
phone: Optional[str] = None
|
|
is_primary: bool = False
|
|
available_24h: bool = False
|
|
|
|
|
|
class ContactUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
role: Optional[str] = None
|
|
email: Optional[str] = None
|
|
phone: Optional[str] = None
|
|
is_primary: Optional[bool] = None
|
|
available_24h: Optional[bool] = None
|
|
|
|
|
|
# ============================================================================
|
|
# Scenarios
|
|
# ============================================================================
|
|
|
|
|
|
class ScenarioCreate(BaseModel):
|
|
title: str
|
|
category: Optional[str] = None
|
|
severity: str = "medium"
|
|
description: Optional[str] = None
|
|
response_steps: List[Any] = []
|
|
estimated_recovery_time: Optional[int] = None
|
|
is_active: bool = True
|
|
|
|
|
|
class ScenarioUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
category: Optional[str] = None
|
|
severity: Optional[str] = None
|
|
description: Optional[str] = None
|
|
response_steps: Optional[List[Any]] = None
|
|
estimated_recovery_time: Optional[int] = None
|
|
last_tested: Optional[str] = None
|
|
is_active: Optional[bool] = None
|
|
|
|
|
|
# ============================================================================
|
|
# Checklists
|
|
# ============================================================================
|
|
|
|
|
|
class ChecklistCreate(BaseModel):
|
|
title: str
|
|
scenario_id: Optional[str] = None
|
|
description: Optional[str] = None
|
|
order_index: int = 0
|
|
is_required: bool = True
|
|
|
|
|
|
class ChecklistUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
description: Optional[str] = None
|
|
order_index: Optional[int] = None
|
|
is_required: Optional[bool] = None
|
|
|
|
|
|
# ============================================================================
|
|
# Exercises
|
|
# ============================================================================
|
|
|
|
|
|
class ExerciseCreate(BaseModel):
|
|
title: str
|
|
scenario_id: Optional[str] = None
|
|
exercise_type: str = "tabletop"
|
|
exercise_date: Optional[str] = None
|
|
participants: List[Any] = []
|
|
outcome: Optional[str] = None
|
|
notes: Optional[str] = None
|
|
|
|
|
|
# ============================================================================
|
|
# Incidents
|
|
# ============================================================================
|
|
|
|
|
|
class IncidentCreate(BaseModel):
|
|
title: str
|
|
description: Optional[str] = None
|
|
detected_by: Optional[str] = None
|
|
status: str = "detected"
|
|
severity: str = "medium"
|
|
affected_data_categories: List[Any] = []
|
|
estimated_affected_persons: int = 0
|
|
measures: List[Any] = []
|
|
art34_required: bool = False
|
|
art34_justification: Optional[str] = None
|
|
|
|
|
|
class IncidentUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
description: Optional[str] = None
|
|
detected_by: Optional[str] = None
|
|
status: Optional[str] = None
|
|
severity: Optional[str] = None
|
|
affected_data_categories: Optional[List[Any]] = None
|
|
estimated_affected_persons: Optional[int] = None
|
|
measures: Optional[List[Any]] = None
|
|
art34_required: Optional[bool] = None
|
|
art34_justification: Optional[str] = None
|
|
reported_to_authority_at: Optional[str] = None
|
|
notified_affected_at: Optional[str] = None
|
|
closed_at: Optional[str] = None
|
|
closed_by: Optional[str] = None
|
|
lessons_learned: Optional[str] = None
|
|
|
|
|
|
# ============================================================================
|
|
# Templates
|
|
# ============================================================================
|
|
|
|
|
|
class TemplateCreate(BaseModel):
|
|
type: str = "art33"
|
|
title: str
|
|
content: str
|
|
|
|
|
|
class TemplateUpdate(BaseModel):
|
|
type: Optional[str] = None
|
|
title: Optional[str] = None
|
|
content: Optional[str] = None
|