import json from datetime import datetime, timezone from typing import Dict, Any, List, Optional from uuid import uuid4 from sqlalchemy.orm import Session from app.database.models import OnboardingSession, OnboardingState, OnboardingMessage def export_session_to_fhir_bundle(session_id: str, db: Session) -> Dict[str, Any]: """ Exports a completed or in-progress onboarding session state into standard HL7 FHIR R4 Bundle. Resources Generated: 1. Patient (Demographics) 2. Encounter (Clinical Intake Session) 3. Condition (Symptoms & Past Medical History) 4. MedicationStatement (Current Active Medications) 5. AllergyIntolerance (Confirmed Allergies) """ sess = db.query(OnboardingSession).filter(OnboardingSession.session_id == session_id).first() if not sess: raise ValueError(f"Session {session_id} not found.") state_obj = db.query(OnboardingState).filter(OnboardingState.session_id == session_id).first() state_data = json.loads(state_obj.state_json) if state_obj and state_obj.state_json else {} now_iso = datetime.now(timezone.utc).isoformat() patient_ref = f"Patient/{sess.patient_id or 'PAT-UNKNOWN'}" encounter_id = f"enc-{session_id.lower()}" entries: List[Dict[str, Any]] = [] # 1. FHIR Patient Resource patient_resource = { "resourceType": "Patient", "id": sess.patient_id or "PAT-UNKNOWN", "identifier": [{ "system": "http://hospital.org/patients", "value": sess.patient_id or "PAT-UNKNOWN" }], "name": [{ "text": sess.patient_name or "Patient", "family": (sess.patient_name or "Patient").split()[-1], "given": (sess.patient_name or "Patient").split()[:-1] or [sess.patient_name or "Patient"] }], "gender": sess.gender if sess.gender in ["male", "female", "other"] else "unknown" } entries.append({"resource": patient_resource}) # 2. FHIR Encounter Resource encounter_resource = { "resourceType": "Encounter", "id": encounter_id, "status": "finished" if sess.status == "completed" else "in-progress", "class": { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "AMB", "display": "ambulatory" }, "type": [{ "coding": [{ "system": "http://snomed.info/sct", "code": "32485007", "display": "Hospital admission intake" }], "text": sess.category_name or "General Intake" }], "subject": {"reference": patient_ref}, "period": { "start": sess.created_at.isoformat() if sess.created_at else now_iso } } entries.append({"resource": encounter_resource}) # 3. FHIR Condition Resources (Symptoms) symptoms = state_data.get("symptoms", []) if isinstance(symptoms, list): for idx, sym in enumerate(symptoms): if isinstance(sym, dict) and sym.get("name"): cond_resource = { "resourceType": "Condition", "id": f"cond-sym-{idx+1}-{uuid4().hex[:4]}", "clinicalStatus": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", "code": "active" }] }, "category": [{ "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/condition-category", "code": "encounter-diagnosis", "display": "Encounter Diagnosis" }] }], "code": { "text": sym["name"] }, "subject": {"reference": patient_ref}, "encounter": {"reference": f"Encounter/{encounter_id}"}, "onsetDateTime": now_iso, "note": [{"text": f"Duration: {sym.get('duration', 'N/A')}, Severity: {sym.get('severity', 'N/A')}"}] } entries.append({"resource": cond_resource}) # 4. FHIR Condition Resources (Medical History) history = state_data.get("medical_history", []) if isinstance(history, list): for idx, hist in enumerate(history): cond_name = hist.get("condition") if isinstance(hist, dict) else str(hist) if cond_name: cond_resource = { "resourceType": "Condition", "id": f"cond-hist-{idx+1}-{uuid4().hex[:4]}", "clinicalStatus": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", "code": "active" }] }, "category": [{ "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/condition-category", "code": "problem-list-item", "display": "Problem List Item" }] }], "code": { "text": cond_name }, "subject": {"reference": patient_ref} } entries.append({"resource": cond_resource}) # 5. FHIR MedicationStatement Resources medications = state_data.get("medications", []) if isinstance(medications, list): for idx, med in enumerate(medications): med_name = med.get("name") if isinstance(med, dict) else str(med) dosage_str = med.get("dosage", "") if isinstance(med, dict) else "" if med_name: med_resource = { "resourceType": "MedicationStatement", "id": f"med-{idx+1}-{uuid4().hex[:4]}", "status": "active", "medicationCodeableConcept": { "text": med_name }, "subject": {"reference": patient_ref}, "dosage": [{"text": dosage_str}] if dosage_str else [] } entries.append({"resource": med_resource}) # 6. FHIR AllergyIntolerance Resources allergies = state_data.get("allergies", []) if isinstance(allergies, list): for idx, alg in enumerate(allergies): alg_name = alg.get("name") if isinstance(alg, dict) else str(alg) if alg_name: alg_resource = { "resourceType": "AllergyIntolerance", "id": f"alg-{idx+1}-{uuid4().hex[:4]}", "clinicalStatus": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", "code": "active" }] }, "code": { "text": alg_name }, "patient": {"reference": patient_ref} } entries.append({"resource": alg_resource}) # Assemble FHIR R4 Bundle fhir_bundle = { "resourceType": "Bundle", "id": f"bundle-{session_id.lower()}", "type": "collection", "timestamp": now_iso, "total": len(entries), "entry": entries } return fhir_bundle