File size: 7,807 Bytes
6dc9d46
 
 
 
 
 
 
696f787
6dc9d46
 
 
 
 
696f787
6dc9d46
 
 
7caf4dc
9659593
696f787
6dc9d46
9659593
6dc9d46
 
 
9659593
6dc9d46
 
 
 
aefac4f
9659593
6dc9d46
 
 
 
 
 
 
 
 
 
9659593
6dc9d46
696f787
6dc9d46
9659593
696f787
6dc9d46
9659593
 
 
 
 
6dc9d46
9659593
6dc9d46
9659593
696f787
9659593
 
 
 
696f787
6dc9d46
 
 
696f787
aefac4f
6dc9d46
 
 
 
 
 
 
aefac4f
 
6dc9d46
 
 
 
aefac4f
6dc9d46
 
 
696f787
6dc9d46
696f787
6dc9d46
9659593
6dc9d46
696f787
6dc9d46
9659593
696f787
6dc9d46
 
9659593
aefac4f
 
 
 
9659593
6dc9d46
 
 
 
 
696f787
6dc9d46
 
 
696f787
6dc9d46
 
9659593
 
 
 
 
 
 
 
6dc9d46
 
9659593
 
 
 
 
 
 
 
696f787
6dc9d46
696f787
 
6dc9d46
 
aefac4f
 
 
 
696f787
 
6dc9d46
 
9659593
6dc9d46
 
 
9659593
ad2e847
9659593
 
 
 
 
 
 
ad2e847
 
9659593
 
 
 
 
 
 
6dc9d46
 
9659593
 
 
 
 
 
6dc9d46
9659593
6dc9d46
9659593
 
 
 
 
6dc9d46
 
9659593
 
 
 
 
 
 
 
 
 
 
6dc9d46
696f787
6dc9d46
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
"""
MediGuard AI RAG-Helper
Biomarker analysis and validation utilities
"""

import json
from pathlib import Path

from src.state import BiomarkerFlag, SafetyAlert


class BiomarkerValidator:
    """Validates biomarker values against reference ranges"""

    def __init__(self, reference_file: str = "config/biomarker_references.json"):
        """Load biomarker reference ranges from JSON file"""
        ref_path = Path(__file__).parent.parent / reference_file
        with open(ref_path, encoding="utf-8") as f:
            self.references = json.load(f)["biomarkers"]

    def validate_biomarker(
        self, name: str, value: float, gender: str | None = None, threshold_pct: float = 0.0
    ) -> BiomarkerFlag:
        """
        Validate a single biomarker value against reference ranges.

        Args:
            name: Biomarker name
            value: Measured value
            gender: "male" or "female" (for gender-specific ranges)
            threshold_pct: Only flag LOW/HIGH if deviation from boundary exceeds this fraction (e.g. 0.15 = 15%)

        Returns:
            BiomarkerFlag object with status and warnings
        """
        if name not in self.references:
            return BiomarkerFlag(
                name=name,
                value=value,
                unit="unknown",
                status="UNKNOWN",
                reference_range="No reference data available",
                warning=f"No reference range found for {name}",
            )

        ref = self.references[name]
        unit = ref["unit"]

        # Handle gender-specific ranges
        if ref.get("gender_specific", False) and gender:
            if gender.lower() in ["male", "m"]:
                normal = ref["normal_range"]["male"]
            elif gender.lower() in ["female", "f"]:
                normal = ref["normal_range"]["female"]
            else:
                normal = ref["normal_range"]
        else:
            normal = ref["normal_range"]

        min_val = normal.get("min", 0)
        max_val = normal.get("max", float("inf"))
        critical_low = ref.get("critical_low")
        critical_high = ref.get("critical_high")

        # Determine status
        status = "NORMAL"
        warning = None

        # Check critical values first (threshold_pct does not suppress critical alerts)
        if critical_low and value < critical_low:
            status = "CRITICAL_LOW"
            warning = f"CRITICAL: {name} is {value} {unit}, below critical threshold of {critical_low} {unit}. {ref['clinical_significance'].get('low', 'Seek immediate medical attention.')}"
        elif critical_high and value > critical_high:
            status = "CRITICAL_HIGH"
            warning = f"CRITICAL: {name} is {value} {unit}, above critical threshold of {critical_high} {unit}. {ref['clinical_significance'].get('high', 'Seek immediate medical attention.')}"
        elif value < min_val:
            # Only flag if deviation exceeds threshold_pct fraction of the boundary
            deviation = (min_val - value) / min_val if min_val != 0 else 1.0
            if deviation > threshold_pct:
                status = "LOW"
                warning = f"{name} is {value} {unit}, below normal range ({min_val}-{max_val} {unit}). {ref['clinical_significance'].get('low', '')}"
        elif value > max_val:
            deviation = (value - max_val) / max_val if max_val != 0 else 1.0
            if deviation > threshold_pct:
                status = "HIGH"
                warning = f"{name} is {value} {unit}, above normal range ({min_val}-{max_val} {unit}). {ref['clinical_significance'].get('high', '')}"

        reference_range = f"{min_val}-{max_val} {unit}"

        return BiomarkerFlag(
            name=name, value=value, unit=unit, status=status, reference_range=reference_range, warning=warning
        )

    def validate_all(
        self, biomarkers: dict[str, float], gender: str | None = None, threshold_pct: float = 0.0
    ) -> tuple[list[BiomarkerFlag], list[SafetyAlert]]:
        """
        Validate all biomarker values.

        Args:
            biomarkers: Dict of biomarker name -> value
            gender: "male" or "female" (for gender-specific ranges)
            threshold_pct: Only flag LOW/HIGH if deviation exceeds this fraction (e.g. 0.15 = 15%)

        Returns:
            Tuple of (biomarker_flags, safety_alerts)
        """
        flags = []
        alerts = []

        for name, value in biomarkers.items():
            flag = self.validate_biomarker(name, value, gender, threshold_pct)
            flags.append(flag)

            # Generate safety alerts for critical values
            if flag.status in ["CRITICAL_LOW", "CRITICAL_HIGH"]:
                alerts.append(
                    SafetyAlert(
                        severity="CRITICAL",
                        biomarker=name,
                        message=flag.warning or f"{name} at critical level",
                        action="SEEK IMMEDIATE MEDICAL ATTENTION",
                    )
                )
            elif flag.status in ["LOW", "HIGH"]:
                severity = "HIGH" if "severe" in (flag.warning or "").lower() else "MEDIUM"
                alerts.append(
                    SafetyAlert(
                        severity=severity,
                        biomarker=name,
                        message=flag.warning or f"{name} out of normal range",
                        action="Consult with healthcare provider",
                    )
                )

        return flags, alerts

    def get_biomarker_info(self, name: str) -> dict | None:
        """Get reference information for a biomarker"""
        return self.references.get(name)

    def expected_biomarker_count(self) -> int:
        """Return expected number of biomarkers from reference ranges."""
        return len(self.references)

    def get_disease_relevant_biomarkers(self, disease: str) -> list[str]:
        """
        Get list of biomarkers most relevant to a specific disease.

        This is a simplified mapping - in production, this would be more sophisticated.
        """
        disease_map = {
            "Diabetes": ["Glucose", "HbA1c", "Insulin", "BMI", "Triglycerides", "HDL Cholesterol", "LDL Cholesterol"],
            "Type 2 Diabetes": [
                "Glucose",
                "HbA1c",
                "Insulin",
                "BMI",
                "Triglycerides",
                "HDL Cholesterol",
                "LDL Cholesterol",
            ],
            "Type 1 Diabetes": [
                "Glucose",
                "HbA1c",
                "Insulin",
                "BMI",
                "Triglycerides",
                "HDL Cholesterol",
                "LDL Cholesterol",
            ],
            "Anemia": [
                "Hemoglobin",
                "Red Blood Cells",
                "Hematocrit",
                "Mean Corpuscular Volume",
                "Mean Corpuscular Hemoglobin",
                "Mean Corpuscular Hemoglobin Concentration",
            ],
            "Thrombocytopenia": ["Platelets", "White Blood Cells", "Hemoglobin"],
            "Thalassemia": [
                "Hemoglobin",
                "Red Blood Cells",
                "Mean Corpuscular Volume",
                "Mean Corpuscular Hemoglobin",
                "Hematocrit",
            ],
            "Heart Disease": [
                "Cholesterol",
                "LDL Cholesterol",
                "HDL Cholesterol",
                "Triglycerides",
                "Troponin",
                "C-reactive Protein",
                "Systolic Blood Pressure",
                "Diastolic Blood Pressure",
                "Heart Rate",
                "BMI",
            ],
        }

        return disease_map.get(disease, [])