""" FastAPI routes for Notfallplan (Emergency Plan) -- Art. 33/34 DSGVO. Endpoints: GET /notfallplan/contacts -- List emergency contacts POST /notfallplan/contacts -- Create contact PUT /notfallplan/contacts/{id} -- Update contact DELETE /notfallplan/contacts/{id} -- Delete contact GET /notfallplan/scenarios -- List scenarios POST /notfallplan/scenarios -- Create scenario PUT /notfallplan/scenarios/{id} -- Update scenario DELETE /notfallplan/scenarios/{id} -- Delete scenario GET /notfallplan/checklists -- List checklists POST /notfallplan/checklists -- Create checklist item PUT /notfallplan/checklists/{id} -- Update checklist item DELETE /notfallplan/checklists/{id} -- Delete checklist item GET /notfallplan/exercises -- List exercises POST /notfallplan/exercises -- Create exercise GET /notfallplan/stats -- Statistics overview GET /notfallplan/incidents -- List incidents POST /notfallplan/incidents -- Create incident PUT /notfallplan/incidents/{id} -- Update incident DELETE /notfallplan/incidents/{id} -- Delete incident GET /notfallplan/templates -- List templates POST /notfallplan/templates -- Create template PUT /notfallplan/templates/{id} -- Update template DELETE /notfallplan/templates/{id} -- Delete template Phase 1 Step 4 refactor: handlers delegate to NotfallplanService and NotfallplanWorkflowService. Schemas re-exported for legacy test imports. """ import logging from typing import Optional from fastapi import APIRouter, Depends, Header, Query from sqlalchemy.orm import Session from classroom_engine.database import get_db from compliance.api._http_errors import translate_domain_errors from compliance.schemas.notfallplan import ( # noqa: F401 -- re-export ChecklistCreate, ChecklistUpdate, ContactCreate, ContactUpdate, ExerciseCreate, IncidentCreate, IncidentUpdate, ScenarioCreate, ScenarioUpdate, TemplateCreate, TemplateUpdate, ) from compliance.services.notfallplan_service import NotfallplanService from compliance.services.notfallplan_workflow_service import ( NotfallplanWorkflowService, ) __all__ = [ "ContactCreate", "ContactUpdate", "ScenarioCreate", "ScenarioUpdate", "ChecklistCreate", "ChecklistUpdate", "ExerciseCreate", "IncidentCreate", "IncidentUpdate", "TemplateCreate", "TemplateUpdate", "router", ] logger = logging.getLogger(__name__) router = APIRouter(prefix="/notfallplan", tags=["notfallplan"]) # ============================================================================ # Dependencies # ============================================================================ def _get_tenant( x_tenant_id: Optional[str] = Header(None, alias="X-Tenant-ID"), ) -> str: return x_tenant_id or "default" def _get_service(db: Session = Depends(get_db)) -> NotfallplanService: return NotfallplanService(db) def _get_workflow(db: Session = Depends(get_db)) -> NotfallplanWorkflowService: return NotfallplanWorkflowService(db) # ============================================================================ # Contacts # ============================================================================ @router.get("/contacts") async def list_contacts( svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.list_contacts(tenant_id) @router.post("/contacts", status_code=201) async def create_contact( request: ContactCreate, svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.create_contact(tenant_id, request) @router.put("/contacts/{contact_id}") async def update_contact( contact_id: str, request: ContactUpdate, svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.update_contact(tenant_id, contact_id, request) @router.delete("/contacts/{contact_id}") async def delete_contact( contact_id: str, svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.delete_contact(tenant_id, contact_id) # ============================================================================ # Scenarios # ============================================================================ @router.get("/scenarios") async def list_scenarios( svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.list_scenarios(tenant_id) @router.post("/scenarios", status_code=201) async def create_scenario( request: ScenarioCreate, svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.create_scenario(tenant_id, request) @router.put("/scenarios/{scenario_id}") async def update_scenario( scenario_id: str, request: ScenarioUpdate, svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.update_scenario(tenant_id, scenario_id, request) @router.delete("/scenarios/{scenario_id}") async def delete_scenario( scenario_id: str, svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.delete_scenario(tenant_id, scenario_id) # ============================================================================ # Checklists # ============================================================================ @router.get("/checklists") async def list_checklists( scenario_id: Optional[str] = Query(None), svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.list_checklists(tenant_id, scenario_id) @router.post("/checklists", status_code=201) async def create_checklist( request: ChecklistCreate, svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.create_checklist(tenant_id, request) @router.put("/checklists/{checklist_id}") async def update_checklist( checklist_id: str, request: ChecklistUpdate, svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.update_checklist(tenant_id, checklist_id, request) @router.delete("/checklists/{checklist_id}") async def delete_checklist( checklist_id: str, svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.delete_checklist(tenant_id, checklist_id) # ============================================================================ # Exercises # ============================================================================ @router.get("/exercises") async def list_exercises( svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.list_exercises(tenant_id) @router.post("/exercises", status_code=201) async def create_exercise( request: ExerciseCreate, svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.create_exercise(tenant_id, request) # ============================================================================ # Stats # ============================================================================ @router.get("/stats") async def get_stats( svc: NotfallplanService = Depends(_get_service), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return svc.get_stats(tenant_id) # ============================================================================ # Incidents # ============================================================================ @router.get("/incidents") async def list_incidents( status: Optional[str] = None, severity: Optional[str] = None, wf: NotfallplanWorkflowService = Depends(_get_workflow), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return wf.list_incidents(tenant_id, status, severity) @router.post("/incidents", status_code=201) async def create_incident( request: IncidentCreate, wf: NotfallplanWorkflowService = Depends(_get_workflow), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return wf.create_incident(tenant_id, request) @router.put("/incidents/{incident_id}") async def update_incident( incident_id: str, request: IncidentUpdate, wf: NotfallplanWorkflowService = Depends(_get_workflow), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return wf.update_incident(tenant_id, incident_id, request) @router.delete("/incidents/{incident_id}", status_code=204) async def delete_incident( incident_id: str, wf: NotfallplanWorkflowService = Depends(_get_workflow), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): wf.delete_incident(tenant_id, incident_id) # ============================================================================ # Templates # ============================================================================ @router.get("/templates") async def list_templates( type: Optional[str] = None, wf: NotfallplanWorkflowService = Depends(_get_workflow), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return wf.list_templates(tenant_id, type) @router.post("/templates", status_code=201) async def create_template( request: TemplateCreate, wf: NotfallplanWorkflowService = Depends(_get_workflow), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return wf.create_template(tenant_id, request) @router.put("/templates/{template_id}") async def update_template( template_id: str, request: TemplateUpdate, wf: NotfallplanWorkflowService = Depends(_get_workflow), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): return wf.update_template(tenant_id, template_id, request) @router.delete("/templates/{template_id}", status_code=204) async def delete_template( template_id: str, wf: NotfallplanWorkflowService = Depends(_get_workflow), tenant_id: str = Depends(_get_tenant), ): with translate_domain_errors(): wf.delete_template(tenant_id, template_id)