Spaces:
Sleeping
Sleeping
| import pandas as pd | |
| import joblib | |
| import gradio as gr | |
| import json | |
| import requests | |
| MODEL_PATH = "risk_model.joblib" | |
| ZAPIER_WEBHOOK_URL = "https://hooks.zapier.com/hooks/catch/27083565/uvtxiub/" | |
| DISCLAIMER = ( | |
| "Educational demo only. This tool does not provide diagnosis, " | |
| "prescriptions, or treatment plans. If symptoms are severe or urgent, seek emergency care." | |
| ) | |
| RED_FLAGS = [ | |
| "chest pain", | |
| "chest tightness", | |
| "severe shortness of breath", | |
| "fainting", | |
| "loss of consciousness", | |
| "stroke" | |
| ] | |
| risk_model = joblib.load(MODEL_PATH) | |
| def send_urgent_alert(symptom_text, risk_score): | |
| try: | |
| requests.post(ZAPIER_WEBHOOK_URL, json={ | |
| "triage_level": "urgent", | |
| "symptom": symptom_text, | |
| "risk_score": risk_score, | |
| "message": "URGENT triage case detected by HealthBridge AI" | |
| }, timeout=5) | |
| except Exception: | |
| pass | |
| def assess(sexvar, educa, genhlth, menthlth, stress, sleep_hours, sleep_quality, duration_days, adherence_prob, symptom_text): | |
| text_lower = str(symptom_text).lower() | |
| red_flag_hit = any(flag in text_lower for flag in RED_FLAGS) | |
| input_df = pd.DataFrame([{ | |
| "user_id": 0, | |
| "SEXVAR": sexvar, | |
| "EDUCA": educa, | |
| "GENHLTH": genhlth, | |
| "MENTHLTH": menthlth, | |
| "stress_score_0_10": stress, | |
| "sleep_hours": sleep_hours, | |
| "sleep_quality_0_10": sleep_quality, | |
| "symptom_duration_days": duration_days, | |
| "adherence_prob": adherence_prob, | |
| "symptom_text": symptom_text, | |
| "triage_label": "medium" | |
| }]) | |
| risk_score = float(risk_model.predict_proba(input_df)[0][1]) | |
| if red_flag_hit: | |
| triage_level = "urgent" | |
| elif risk_score >= 0.7: | |
| triage_level = "high" | |
| elif risk_score >= 0.4: | |
| triage_level = "medium" | |
| else: | |
| triage_level = "low" | |
| if triage_level == "urgent": | |
| send_urgent_alert(symptom_text, round(risk_score, 3)) | |
| if triage_level == "urgent": | |
| next_steps = [ | |
| "๐จ Seek emergency care immediately or call emergency services.", | |
| "Do not drive yourself โ ask someone nearby to help.", | |
| "Do not wait to see if symptoms improve." | |
| ] | |
| elif triage_level == "high": | |
| next_steps = [ | |
| "Contact a healthcare provider as soon as possible.", | |
| "Monitor symptoms closely and note any changes.", | |
| "Seek emergency care if symptoms worsen rapidly." | |
| ] | |
| elif triage_level == "medium": | |
| next_steps = [ | |
| "Schedule an appointment with your doctor within the next few days.", | |
| "Rest and monitor your symptoms.", | |
| "Seek urgent care if symptoms escalate." | |
| ] | |
| else: | |
| next_steps = [ | |
| "Monitor symptoms and maintain healthy routines.", | |
| "Seek professional care if symptoms persist beyond a week.", | |
| "Stay hydrated and rest as needed." | |
| ] | |
| LEVEL_COLORS = { | |
| "urgent": "#FF2D2D", | |
| "high": "#FF8C00", | |
| "medium": "#F5C518", | |
| "low": "#2ECC71" | |
| } | |
| LEVEL_BG = { | |
| "urgent": "#FFF0F0", | |
| "high": "#FFF5E6", | |
| "medium": "#FFFBEA", | |
| "low": "#F0FFF6" | |
| } | |
| LEVEL_ICONS = { | |
| "urgent": "๐จ", | |
| "high": "โ ๏ธ", | |
| "medium": "๐ถ", | |
| "low": "โ " | |
| } | |
| color = LEVEL_COLORS[triage_level] | |
| bg = LEVEL_BG[triage_level] | |
| icon = LEVEL_ICONS[triage_level] | |
| risk_pct = int(risk_score * 100) | |
| bar_color = color | |
| steps_html = "".join(f"<li style='margin-bottom:6px'>{s}</li>" for s in next_steps) | |
| alert_banner = "" | |
| if triage_level == "urgent": | |
| alert_banner = f""" | |
| <div style='background:#FF2D2D;color:white;padding:14px 18px;border-radius:8px; | |
| font-weight:700;font-size:15px;letter-spacing:0.5px;margin-bottom:16px; | |
| animation: pulse 1.5s infinite;'> | |
| ๐จ URGENT โ An automated alert email has been sent. | |
| </div> | |
| """ | |
| html = f""" | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap'); | |
| .hb-card * {{ font-family: 'DM Sans', sans-serif; box-sizing: border-box; }} | |
| @keyframes pulse {{ | |
| 0%, 100% {{ opacity: 1; }} | |
| 50% {{ opacity: 0.75; }} | |
| }} | |
| @keyframes fadeIn {{ | |
| from {{ opacity: 0; transform: translateY(8px); }} | |
| to {{ opacity: 1; transform: translateY(0); }} | |
| }} | |
| .hb-card {{ animation: fadeIn 0.4s ease; }} | |
| </style> | |
| <div class='hb-card' style='background:{bg};border:2px solid {color};border-radius:12px; | |
| padding:24px;max-width:600px;'> | |
| {alert_banner} | |
| <!-- Triage Level Badge --> | |
| <div style='display:flex;align-items:center;gap:12px;margin-bottom:20px;'> | |
| <span style='font-size:32px;'>{icon}</span> | |
| <div> | |
| <div style='font-size:11px;font-weight:600;color:#888;letter-spacing:1.5px; | |
| text-transform:uppercase;'>Triage Level</div> | |
| <div style='font-size:28px;font-weight:700;color:{color};line-height:1.1;'> | |
| {triage_level.upper()} | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Risk Score Bar --> | |
| <div style='margin-bottom:20px;'> | |
| <div style='display:flex;justify-content:space-between;margin-bottom:6px;'> | |
| <span style='font-size:12px;font-weight:600;color:#555;text-transform:uppercase; | |
| letter-spacing:1px;'>Risk Score</span> | |
| <span style='font-size:14px;font-weight:700;color:{color}; | |
| font-family:"DM Mono",monospace;'>{risk_pct}%</span> | |
| </div> | |
| <div style='background:#E8E8E8;border-radius:99px;height:10px;overflow:hidden;'> | |
| <div style='background:{bar_color};width:{risk_pct}%;height:100%; | |
| border-radius:99px;transition:width 0.6s ease;'></div> | |
| </div> | |
| </div> | |
| <!-- Red Flag --> | |
| <div style='display:flex;align-items:center;gap:8px;margin-bottom:20px; | |
| padding:10px 14px;background:white;border-radius:8px; | |
| border:1px solid #E0E0E0;'> | |
| <span style='font-size:16px;'>{"๐ฉ" if red_flag_hit else "โ๏ธ"}</span> | |
| <span style='font-size:13px;font-weight:600;color:{"#FF2D2D" if red_flag_hit else "#2ECC71"};'> | |
| Red Flag: {"DETECTED" if red_flag_hit else "None detected"} | |
| </span> | |
| </div> | |
| <!-- Next Steps --> | |
| <div style='margin-bottom:20px;'> | |
| <div style='font-size:12px;font-weight:600;color:#555;letter-spacing:1px; | |
| text-transform:uppercase;margin-bottom:10px;'>Recommended Next Steps</div> | |
| <ul style='margin:0;padding-left:20px;color:#333;font-size:14px;line-height:1.6;'> | |
| {steps_html} | |
| </ul> | |
| </div> | |
| <!-- Disclaimer --> | |
| <div style='font-size:11px;color:#999;border-top:1px solid #E0E0E0; | |
| padding-top:12px;line-height:1.5;'> | |
| โ ๏ธ {DISCLAIMER} | |
| </div> | |
| </div> | |
| """ | |
| return html | |
| demo = gr.Interface( | |
| fn=assess, | |
| inputs=[ | |
| gr.Dropdown([1, 2], value=1, label="Sex (BRFSS-coded)"), | |
| gr.Slider(1, 6, value=4, step=1, label="Education level"), | |
| gr.Slider(1, 5, value=3, step=1, label="General health"), | |
| gr.Slider(0, 30, value=5, step=1, label="Mental health bad days"), | |
| gr.Slider(0, 10, value=5, step=0.1, label="Stress score"), | |
| gr.Slider(3, 10, value=7, step=0.1, label="Sleep hours"), | |
| gr.Slider(0, 10, value=7, step=0.1, label="Sleep quality"), | |
| gr.Slider(1, 30, value=5, step=1, label="Symptom duration (days)"), | |
| gr.Slider(0.05, 0.95, value=0.65, step=0.01, label="Adherence probability"), | |
| gr.Textbox(lines=4, placeholder="e.g. chest pain for 2 days, high stress, poor sleep", label="Symptom description") | |
| ], | |
| outputs=gr.HTML(label="Triage Result"), | |
| title="HealthBridge AI โ Non-diagnostic Triage Demo", | |
| description="Educational demo only. Not a substitute for professional medical advice.", | |
| theme=gr.themes.Soft() | |
| ) | |
| demo.launch() |