File size: 3,815 Bytes
fa64d6b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# explainer.py
# ECG Condition Classifier - Clinical Explanation Generator

CONDITION_MAP = {
    "NORM": {
        "summary": "Normal Sinus Rhythm",
        "urgency": "Routine",
        "description": "The ECG shows a normal sinus rhythm with regular rate and rhythm, normal P waves, QRS complexes, and T waves. No significant ST changes or conduction abnormalities detected.",
        "recommendation": "No immediate cardiac intervention required. Continue routine follow-up as clinically indicated.",
    },
    "AFIB": {
        "summary": "Atrial Fibrillation",
        "urgency": "Elevated",
        "description": "The ECG pattern is consistent with Atrial Fibrillation. Characteristic findings include an irregularly irregular rhythm, absence of distinct P waves, and variable R-R intervals. AFib is associated with increased risk of stroke and heart failure.",
        "recommendation": "Clinical evaluation recommended. Rate/rhythm management and stroke risk assessment advised.",
    },
    "MI": {
        "summary": "Myocardial Infarction",
        "urgency": "URGENT",
        "description": "The ECG pattern is consistent with Myocardial Infarction. Findings may include ST-segment elevation or depression and pathological Q waves suggesting myocardial injury or ischemia.",
        "recommendation": "⚠️ IMMEDIATE clinical review strongly recommended. Activate emergency cardiac protocols if STEMI suspected.",
    },
    "STTC": {
        "summary": "ST/T-Wave Change",
        "urgency": "Moderate",
        "description": "Non-specific ST-segment and/or T-wave changes detected. These findings can represent myocardial ischemia, electrolyte abnormalities, medication effects, or normal variants. Clinical context is essential for interpretation.",
        "recommendation": "Clinical correlation advised. Serial ECGs and lab results may be warranted.",
    },
    "CD": {
        "summary": "Conduction Disturbance",
        "urgency": "Moderate",
        "description": "The ECG shows features consistent with a conduction disturbance, such as bundle branch block or AV conduction delay. These findings alter the normal electrical pathway through the ventricles.",
        "recommendation": "Cardiology consultation recommended to evaluate hemodynamic significance and underlying etiology.",
    },
}

DISCLAIMER = "⚠️ DISCLAIMER: For educational and research purposes only. Does not constitute medical advice. Always consult a qualified healthcare provider."


def match_condition(label):
    label_upper = (label or "").upper()
    for key, val in CONDITION_MAP.items():
        if key in label_upper:
            return {**val, "key": key}
    return {
        "key": "UNKNOWN",
        "summary": "Unrecognized Pattern",
        "urgency": "Unknown",
        "description": "The model returned a label that could not be mapped to a known cardiac condition. This may indicate an unusual ECG pattern or a label category outside the standard NORM/AFIB/MI/STTC/CD set.",
        "recommendation": "Manual review by a qualified cardiologist is recommended.",
    }


def generate_explanation(label, confidence):
    condition = match_condition(label)
    urgency = condition["urgency"]

    text = (
        f"Condition: {condition['summary']}\n"
        f"Raw Model Label: {label}\n"
        f"Confidence: {confidence:.1f}%\n"
        f"Urgency: {urgency}\n\n"
        f"{condition['description']}\n\n"
        f"Recommendation: {condition['recommendation']}\n\n"
        f"{DISCLAIMER}"
    )
    return text, urgency


def format_top3(results):
    lines = ["Top Predictions:"]
    for i, r in enumerate(results[:3], 1):
        score = r["score"] * 100
        bar = "█" * int(score / 5)
        lines.append(f"  {i}. {r['label']}: {score:.1f}% {bar}")
    return "\n".join(lines)