187 lines
7.5 KiB
Python
187 lines
7.5 KiB
Python
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
|