from pathlib import Path import json from typing import Any, Dict, List, Optional from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel # --- Domain models --- class Answer(BaseModel): question_code: str value: Any class SessionCreate(BaseModel): language: str answers: List[Answer] class MtsPreparation(BaseModel): session_id: str proposed_presenting_flowchart: Optional[str] = None red_flag_indicators: List[str] = [] suggested_priority_level: Optional[str] = None class QuestionOption(BaseModel): value: str label: str icon: str | None = None class Question(BaseModel): code: str type: str title: str options: Optional[List[QuestionOption]] = None min: Optional[int] = None max: Optional[int] = None class QuestionsResponse(BaseModel): language: str questions: List[Question] BASE_DIR = Path(__file__).resolve().parents[2] MTS_CONFIG_DIR = BASE_DIR / "mts-config" def load_questions(language: str) -> QuestionsResponse: """Load questions configuration for the given language from mts-config. This keeps the triage logic data-driven and allows adding further languages by simply providing a new questions..json file. """ file_path = MTS_CONFIG_DIR / f"questions.{language}.json" if not file_path.exists(): raise HTTPException(status_code=404, detail=f"No question config for language '{language}'") with file_path.open("r", encoding="utf-8") as f: raw: Dict[str, Any] = json.load(f) return QuestionsResponse(**raw) def load_flowcharts_config() -> List[Dict[str, Any]]: """Load flowchart mapping configuration. This maps chief-complaint codes (e.g. "chest_pain") to MTS presenting flowchart codes (e.g. "CHEST_PAIN"). """ file_path = MTS_CONFIG_DIR / "flowcharts.json" if not file_path.exists(): return [] with file_path.open("r", encoding="utf-8") as f: raw: Dict[str, Any] = json.load(f) flowcharts = raw.get("flowcharts", []) if not isinstance(flowcharts, list): return [] return flowcharts app = FastAPI(title="Triage-Fragen API", version="0.3.0") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/health") async def health() -> Dict[str, str]: return {"status": "ok"} @app.get("/questions/{language}", response_model=QuestionsResponse) async def get_questions(language: str) -> QuestionsResponse: """Return the triage question configuration for the selected language.""" return load_questions(language) @app.post("/sessions", response_model=MtsPreparation) async def create_session(payload: SessionCreate) -> MtsPreparation: """Create a triage preparation proposal from the given answers. Aktuell wird bewusst nur ein kleiner, aber sinnvoller Ausschnitt der Manchester-Triage-Logik abgebildet: - Zuordnung von "chief_complaint" zu einem der konfigurierten MTS-Präsentations-Flowcharts (10 Pfade in mts-config/flowcharts.json). - Ableitung einfacher Red-Flag-Indikatoren wie "severe_pain" aus der Schmerzskala. - Grobe Prioritätsstufe, insbesondere für Hochrisiko-Flowcharts wie Brustschmerz, Atemnot, Kollaps und Intoxikation. """ session_id = "dummy-session-id" # Antworten bequem zugreifbar machen answers_by_code: Dict[str, Any] = {a.question_code: a.value for a in payload.answers} # 1) Flowchart aus chief_complaint bestimmen (Daten-getrieben aus mts-config) flowchart: Optional[str] = None flowcharts = load_flowcharts_config() complaint_to_flowchart: Dict[str, str] = {} for fc in flowcharts: linked = fc.get("linked_chief_complaints") or [] if not isinstance(linked, list): continue for cc in linked: # Erstes Mapping gewinnt; spätere Einträge überschreiben nicht complaint_to_flowchart.setdefault(cc, fc.get("code")) chief = answers_by_code.get("chief_complaint") if isinstance(chief, str): mapped = complaint_to_flowchart.get(chief) if isinstance(mapped, str): flowchart = mapped # 2) Red-Flag-Indikatoren aus vorhandenen Antworten ableiten red_flags: List[str] = [] severe_pain = False # Atemnot: vorgesehen für spätere UI-Erweiterung if answers_by_code.get("breathlessness") is True: red_flags.append("breathlessness") pain_value = answers_by_code.get("pain_intensity") if isinstance(pain_value, int): if pain_value >= 8: red_flags.append("severe_pain") severe_pain = True elif pain_value >= 5: red_flags.append("moderate_pain") # 3) Grobe Priorität abhängig vom Flowchart und den Red Flags priority: Optional[str] = None high_risk_flowcharts = { "CHEST_PAIN", "SHORTNESS_OF_BREATH", "COLLAPSED_ADULT", "OVERDOSE_POISONING", } if "breathlessness" in red_flags: # Starke Atemnot ist immer hoch dringlich priority = "RED_OR_ORANGE" elif severe_pain and flowchart in high_risk_flowcharts: # Starke Schmerzen bei Hochrisiko-Präsentation → hohe Dringlichkeit priority = "RED_OR_ORANGE" elif severe_pain: # Starke Schmerzen bei anderen Flowcharts → mindestens erhöhte Dringlichkeit priority = "YELLOW_OR_ORANGE" return MtsPreparation( session_id=session_id, proposed_presenting_flowchart=flowchart, red_flag_indicators=red_flags, suggested_priority_level=priority, )