Spaces:
Running
Running
| """Synthetic patient generation.""" | |
| from __future__ import annotations | |
| import random | |
| from app.common.enums import Difficulty, DoseBucket | |
| from app.common.types import LabSummary, Medication, PatientProfile | |
| _DRUG_POOL = [ | |
| ("warfarin_like", "anticoagulant"), | |
| ("benzodiazepine_like", "sedative"), | |
| ("metformin_like", "glucose_lowering"), | |
| ("statin_like", "lipid_lowering"), | |
| ("ace_inhibitor_like", "antihypertensive"), | |
| ("nsaid_like", "analgesic"), | |
| ("opioid_like", "analgesic"), | |
| ("ssri_like", "antidepressant"), | |
| ("ppi_like", "gastro"), | |
| ("beta_blocker_like", "antihypertensive"), | |
| ] | |
| def generate_patient_profile(seed: int, difficulty: Difficulty, patient_id: str | None = None) -> PatientProfile: | |
| random.seed(seed) | |
| med_count = {Difficulty.EASY: 5, Difficulty.MEDIUM: 8, Difficulty.HARD: 10}[difficulty] | |
| selected = random.sample(_DRUG_POOL, k=med_count) | |
| medications = [ | |
| Medication( | |
| drug=drug, | |
| class_name=cls, | |
| dose_bucket=random.choice([DoseBucket.LOW, DoseBucket.MEDIUM, DoseBucket.HIGH]), | |
| indication=f"indication_{idx}", | |
| requires_taper=drug in {"benzodiazepine_like", "opioid_like"}, | |
| ) | |
| for idx, (drug, cls) in enumerate(selected) | |
| ] | |
| return PatientProfile( | |
| patient_id=patient_id or f"patient_{seed}", | |
| age=random.randint(55, 90), | |
| sex=random.choice(["F", "M"]), | |
| comorbidities=random.sample( | |
| ["htn", "dm2", "afib", "ckd", "copd", "depression", "fall_risk"], k=3 | |
| ), | |
| medications=medications, | |
| labs=LabSummary( | |
| egfr=round(random.uniform(20, 95), 1), | |
| ast=round(random.uniform(10, 120), 1), | |
| alt=round(random.uniform(10, 120), 1), | |
| inr=round(random.uniform(1.0, 4.0), 2), | |
| glucose=round(random.uniform(70, 280), 1), | |
| ), | |
| vitals={ | |
| "sbp": random.randint(100, 180), | |
| "dbp": random.randint(60, 105), | |
| "hr": random.randint(50, 120), | |
| "egfr_trend": round(random.uniform(-8.0, 3.0), 2), | |
| "inr_trend": round(random.uniform(-0.5, 0.7), 2), | |
| "glucose_trend": round(random.uniform(-35.0, 45.0), 2), | |
| }, | |
| specialist_conflicts=[ | |
| "duplicate_analgesic_strategy", | |
| "cardio_vs_pain_med_conflict", | |
| ] | |
| if difficulty != Difficulty.EASY | |
| else [], | |
| prior_ade_history=["fall_event", "sedation_event"] if difficulty == Difficulty.HARD else [], | |
| frailty_score=round(random.uniform(0.1, 0.9), 2), | |
| adherence_estimate=round(random.uniform(0.4, 0.95), 2), | |
| latent_confounders={ | |
| "metabolism_variability": round(random.uniform(0.1, 0.9), 3), | |
| "social_support_risk": round(random.uniform(0.0, 1.0), 3), | |
| "polyprovider_fragmentation": round(random.uniform(0.1, 0.95), 3), | |
| }, | |
| monitoring_gaps=["no_recent_inr", "missing_liver_panel"] if difficulty == Difficulty.HARD else ["missing_followup_bp"], | |
| ) | |