File size: 3,045 Bytes
877add7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""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"],
    )