updated with real triage methodology

This commit is contained in:
Raika Furude 2025-11-01 15:19:24 -04:00
parent 9a70435636
commit 7cf5510401
12 changed files with 2326 additions and 317 deletions

View File

@ -12,6 +12,7 @@ from contextlib import asynccontextmanager
import asyncio
import json
from collections import defaultdict
from triage_engine import TriageEngine, VitalSigns, TriageLevel, triage_from_vitals
# ============================================================================
# LIFESPAN MANAGEMENT
@ -118,41 +119,40 @@ wristband_details_cache = {}
def calculate_priority_score(patient: Patient) -> float:
score = 0.0
"""
Calculate priority score using centralized triage engine
"""
tier_scores = {"EMERGENCY": 100, "ALERT": 50, "NORMAL": 0}
score += tier_scores.get(patient.current_tier, 0)
# If no vitals yet, use basic scoring
if not patient.last_vitals:
severity_scores = {"severe": 50, "moderate": 30, "mild": 20}
return severity_scores.get(patient.severity, 20)
wait_minutes = (datetime.now() - patient.check_in_time).total_seconds() / 60
if wait_minutes > 30:
score += (wait_minutes - 30) * 0.5
elif wait_minutes > 60:
score += (wait_minutes - 60) * 1.0
# Create VitalSigns object
vitals = VitalSigns(
heart_rate=patient.last_vitals.get("hr_bpm", 75),
spo2=patient.last_vitals.get("spo2", 98),
temperature=patient.last_vitals.get("temp_c", 37.0),
activity=patient.last_vitals.get("activity", 0.0),
)
severity_scores = {"severe": 20, "moderate": 10, "mild": 5}
score += severity_scores.get(patient.severity, 0)
# Calculate wait time
wait_minutes = int((datetime.now() - patient.check_in_time).total_seconds() / 60)
if patient.last_vitals:
hr = patient.last_vitals.get("hr_bpm", 75)
spo2 = patient.last_vitals.get("spo2", 98)
temp = patient.last_vitals.get("temp_c", 37.0)
# Use triage engine to assess
assessment = TriageEngine.assess_patient(
vitals=vitals,
symptoms=patient.symptoms,
severity=patient.severity,
age=None, # Add age field to Patient model if needed
preexisting=[], # Add preexisting field to Patient model if needed
wait_time_minutes=wait_minutes,
)
if hr > 110 or hr < 50:
score += 10
if hr > 140 or hr < 40:
score += 30
# Update patient tier based on assessment
patient.current_tier = assessment["tier_name"]
if spo2 < 92:
score += 15
if spo2 < 88:
score += 40
if temp > 38.5:
score += 15
if temp > 39.5:
score += 25
return score
return assessment["priority_score"]
# ============================================================================
@ -203,24 +203,57 @@ async def check_in_patient(data: PatientCheckIn):
@app.post("/api/vitals")
async def receive_vitals(data: VitalsData):
"""Receive vitals data from base station"""
patient_id = data.patient_id
if patient_id not in patients_db:
raise HTTPException(status_code=404, detail="Patient not found")
patient = patients_db[patient_id]
patient.current_tier = data.tier
# Use triage engine to determine tier from vitals
vitals = VitalSigns(
heart_rate=data.hr_bpm,
spo2=data.spo2,
temperature=data.temp_c,
activity=data.activity,
)
# Quick tier assessment
tier = triage_from_vitals(data.hr_bpm, data.spo2, data.temp_c)
patient.current_tier = tier
patient.last_vitals = data.dict()
# Store in history
vitals_history[patient_id].append(data)
if len(vitals_history[patient_id]) > 1000:
vitals_history[patient_id] = vitals_history[patient_id][-1000:]
# Check for deterioration if we have history
if len(vitals_history[patient_id]) >= 3:
previous = [
VitalSigns(v.hr_bpm, v.spo2, v.temp_c, v.activity)
for v in vitals_history[patient_id][-4:-1]
]
deterioration = TriageEngine.detect_deterioration(vitals, previous)
if deterioration["deteriorating"]:
print(f"⚠️ DETERIORATION DETECTED: {patient_id}")
print(f" Concerns: {', '.join(deterioration['concerns'])}")
# Broadcast update
await broadcast_update(
{"type": "vitals_update", "patient_id": patient_id, "vitals": data.dict()}
{
"type": "vitals_update",
"patient_id": patient_id,
"vitals": data.dict(),
"tier": tier,
}
)
return {"status": "received"}
return {"status": "received", "tier": tier}
@app.get("/api/queue")

View File

@ -0,0 +1,723 @@
"""
VitalLink Triage Engine
Centralized triage assessment algorithm based on hospital emergency standards
Combines elements from US ESI, UK Manchester, and Canadian CTAS systems
"""
from datetime import datetime
from enum import IntEnum
from typing import List, Optional, Dict
from dataclasses import dataclass
# ============================================================================
# TRIAGE LEVELS (Lower number = Higher priority)
# ============================================================================
class TriageLevel(IntEnum):
"""
Four-tier triage system
Based on Emergency Severity Index (ESI) and similar systems
"""
LEVEL_1_EMERGENCY = 1 # Immediate life threat - needs resuscitation
LEVEL_2_WARNING = 2 # High risk - deteriorating/unstable
LEVEL_3_ALERT = 3 # Urgent - abnormal vitals or moderate risk
LEVEL_4_NORMAL = 4 # Standard - stable with minor complaints
# ============================================================================
# VITAL SIGN THRESHOLDS
# ============================================================================
class VitalThresholds:
"""Clinical thresholds for vital sign assessment with hysteresis"""
# Heart Rate (BPM) - Upgrade thresholds
HR_CRITICAL_LOW = 40
HR_CRITICAL_HIGH = 140
HR_ABNORMAL_LOW = 50
HR_ABNORMAL_HIGH = 110
# Heart Rate - Downgrade thresholds (with hysteresis gap)
HR_CRITICAL_LOW_RECOVERY = 45 # Must go above 45 to leave critical
HR_CRITICAL_HIGH_RECOVERY = 135 # Must go below 135 to leave critical
HR_ABNORMAL_LOW_RECOVERY = 55
HR_ABNORMAL_HIGH_RECOVERY = 105
# SpO2 (%) - Upgrade thresholds
SPO2_CRITICAL = 88
SPO2_ABNORMAL = 92
# SpO2 - Downgrade thresholds
SPO2_CRITICAL_RECOVERY = 90 # Must go above 90 to leave critical
SPO2_ABNORMAL_RECOVERY = 94 # Must go above 94 to leave abnormal
# Temperature (°C) - Upgrade thresholds
TEMP_CRITICAL_LOW = 35.0
TEMP_CRITICAL_HIGH = 39.5
TEMP_ABNORMAL_LOW = 35.5
TEMP_ABNORMAL_HIGH = 38.3
# Temperature - Downgrade thresholds
TEMP_CRITICAL_LOW_RECOVERY = 35.5
TEMP_CRITICAL_HIGH_RECOVERY = 39.0
TEMP_ABNORMAL_LOW_RECOVERY = 36.0
TEMP_ABNORMAL_HIGH_RECOVERY = 38.0
# Respiratory Rate (breaths/min)
RR_CRITICAL_LOW = 10
RR_CRITICAL_HIGH = 30
RR_ABNORMAL_LOW = 12
RR_ABNORMAL_HIGH = 20
@dataclass
class TierStabilityTracker:
"""Tracks tier changes to prevent oscillation"""
current_tier: str = "NORMAL"
tier_since: float = 0.0 # Timestamp when tier was set
consecutive_readings: int = 0
tier_change_count: int = 0
# Confirmation requirements
CONFIRMATION_READINGS_UPGRADE = 3 # Need 3 consecutive readings to upgrade
CONFIRMATION_READINGS_DOWNGRADE = 5 # Need 5 consecutive readings to downgrade
MIN_TIME_IN_TIER = 30.0 # Minimum 30 seconds before tier can change
# ============================================================================
# CLINICAL KNOWLEDGE BASE
# ============================================================================
CRITICAL_CONDITIONS = [
"cardiac arrest",
"respiratory arrest",
"major trauma",
"severe hemorrhage",
"stroke",
"seizure ongoing",
"anaphylaxis",
"severe chest pain",
"unresponsive",
]
HIGH_RISK_SYMPTOMS = [
"chest pain",
"difficulty breathing",
"altered mental status",
"severe headache",
"excessive bleeding",
"abdominal pain",
"head injury",
"suspected sepsis",
]
MODERATE_RISK_SYMPTOMS = [
"fever",
"nausea/vomiting",
"dizziness",
"moderate pain",
"injury/trauma",
"infection signs",
]
PREEXISTING_CONDITIONS = [
"diabetes",
"copd",
"asthma",
"cancer",
"hypertension",
"heart disease",
"kidney disease",
"immunocompromised",
]
# ============================================================================
# VITAL SIGNS DATA CLASS
# ============================================================================
@dataclass
class VitalSigns:
"""Current vital signs for a patient"""
heart_rate: int
spo2: int
temperature: float
activity: float = 0.0
respiratory_rate: Optional[int] = None
blood_pressure_systolic: Optional[int] = None
consciousness_level: str = "Alert"
pain_level: int = 0 # 0-10 scale
def is_critical(self) -> bool:
"""Check if any vital is in critical range"""
return (
self.heart_rate < VitalThresholds.HR_CRITICAL_LOW
or self.heart_rate > VitalThresholds.HR_CRITICAL_HIGH
or self.spo2 < VitalThresholds.SPO2_CRITICAL
or self.temperature < VitalThresholds.TEMP_CRITICAL_LOW
or self.temperature > VitalThresholds.TEMP_CRITICAL_HIGH
or (
self.respiratory_rate
and (
self.respiratory_rate < VitalThresholds.RR_CRITICAL_LOW
or self.respiratory_rate > VitalThresholds.RR_CRITICAL_HIGH
)
)
)
def is_abnormal(self) -> bool:
"""Check if any vital is abnormal but not critical"""
return (
self.heart_rate < VitalThresholds.HR_ABNORMAL_LOW
or self.heart_rate > VitalThresholds.HR_ABNORMAL_HIGH
or self.spo2 < VitalThresholds.SPO2_ABNORMAL
or self.temperature < VitalThresholds.TEMP_ABNORMAL_LOW
or self.temperature > VitalThresholds.TEMP_ABNORMAL_HIGH
)
def get_abnormalities(self) -> List[str]:
"""Get list of abnormal vital signs"""
abnormalities = []
if self.heart_rate < VitalThresholds.HR_CRITICAL_LOW:
abnormalities.append(f"Severe Bradycardia ({self.heart_rate} bpm)")
elif self.heart_rate > VitalThresholds.HR_CRITICAL_HIGH:
abnormalities.append(f"Severe Tachycardia ({self.heart_rate} bpm)")
elif self.heart_rate < VitalThresholds.HR_ABNORMAL_LOW:
abnormalities.append(f"Bradycardia ({self.heart_rate} bpm)")
elif self.heart_rate > VitalThresholds.HR_ABNORMAL_HIGH:
abnormalities.append(f"Tachycardia ({self.heart_rate} bpm)")
if self.spo2 < VitalThresholds.SPO2_CRITICAL:
abnormalities.append(f"Critical Hypoxia ({self.spo2}%)")
elif self.spo2 < VitalThresholds.SPO2_ABNORMAL:
abnormalities.append(f"Hypoxia ({self.spo2}%)")
if self.temperature > VitalThresholds.TEMP_CRITICAL_HIGH:
abnormalities.append(f"High Fever ({self.temperature:.1f}°C)")
elif self.temperature > VitalThresholds.TEMP_ABNORMAL_HIGH:
abnormalities.append(f"Fever ({self.temperature:.1f}°C)")
elif self.temperature < VitalThresholds.TEMP_CRITICAL_LOW:
abnormalities.append(f"Hypothermia ({self.temperature:.1f}°C)")
return abnormalities
def is_critical_with_hysteresis(self, current_tier: str) -> bool:
"""Check if critical, with hysteresis based on current tier"""
if current_tier == "EMERGENCY":
# Already in EMERGENCY - use recovery thresholds (harder to leave)
return (
self.heart_rate < VitalThresholds.HR_CRITICAL_LOW_RECOVERY
or self.heart_rate > VitalThresholds.HR_CRITICAL_HIGH_RECOVERY
or self.spo2 < VitalThresholds.SPO2_CRITICAL_RECOVERY
or self.temperature < VitalThresholds.TEMP_CRITICAL_LOW_RECOVERY
or self.temperature > VitalThresholds.TEMP_CRITICAL_HIGH_RECOVERY
)
else:
# Not in EMERGENCY - use standard thresholds (easier to enter)
return self.is_critical()
def is_abnormal_with_hysteresis(self, current_tier: str) -> bool:
"""Check if abnormal, with hysteresis based on current tier"""
if current_tier in ["ALERT", "EMERGENCY"]:
# Already elevated - use recovery thresholds
return (
self.heart_rate < VitalThresholds.HR_ABNORMAL_LOW_RECOVERY
or self.heart_rate > VitalThresholds.HR_ABNORMAL_HIGH_RECOVERY
or self.spo2 < VitalThresholds.SPO2_ABNORMAL_RECOVERY
or self.temperature < VitalThresholds.TEMP_ABNORMAL_LOW_RECOVERY
or self.temperature > VitalThresholds.TEMP_ABNORMAL_HIGH_RECOVERY
)
else:
# Normal tier - use standard thresholds
return self.is_abnormal()
# ============================================================================
# TRIAGE ENGINE
# ============================================================================
class TriageEngine:
"""
Central triage assessment engine
Implements hospital-standard triage algorithms
"""
@staticmethod
def assess_patient(
vitals: VitalSigns,
symptoms: List[str],
severity: str,
age: Optional[int] = None,
preexisting: List[str] = None,
wait_time_minutes: int = 0,
) -> Dict:
"""
Comprehensive triage assessment
Returns triage level, priority score, and reasoning
"""
preexisting = preexisting or []
# Step 1: Check for immediate life threats
if TriageEngine._is_life_threatening(vitals, symptoms):
return {
"triage_level": TriageLevel.LEVEL_1_EMERGENCY,
"tier_name": "EMERGENCY",
"priority_score": 200.0,
"reasoning": "Life-threatening condition detected",
"abnormalities": vitals.get_abnormalities(),
"recommended_action": "Immediate intervention required",
}
# Step 2: Assess high-risk situations
if TriageEngine._is_high_risk(vitals, symptoms, severity, age, preexisting):
base_score = 100.0
# Add points for vital abnormalities
if vitals.is_abnormal():
base_score += 30.0
# Add wait time urgency
if wait_time_minutes > 30:
base_score += (wait_time_minutes - 30) * 0.5
return {
"triage_level": TriageLevel.LEVEL_2_WARNING,
"tier_name": "ALERT",
"priority_score": base_score,
"reasoning": "High-risk patient with concerning symptoms",
"abnormalities": vitals.get_abnormalities(),
"recommended_action": "Rapid assessment needed",
}
# Step 3: Evaluate moderate urgency
resources_needed = TriageEngine._estimate_resources(symptoms, severity)
if resources_needed >= 2 or vitals.is_abnormal():
base_score = 50.0
if vitals.is_abnormal():
base_score += 20.0
if wait_time_minutes > 45:
base_score += (wait_time_minutes - 45) * 0.8
# Check for preexisting conditions
if any(
cond.lower() in [p.lower() for p in preexisting]
for cond in PREEXISTING_CONDITIONS
):
base_score += 15.0
return {
"triage_level": TriageLevel.LEVEL_3_ALERT,
"tier_name": "ALERT",
"priority_score": base_score,
"reasoning": "Urgent care needed - abnormal vitals or complex case",
"abnormalities": vitals.get_abnormalities(),
"recommended_action": "See within 30 minutes",
}
# Step 4: Standard care
base_score = 20.0
# Severity modifier
if severity == "severe":
base_score += 15.0
elif severity == "moderate":
base_score += 10.0
else:
base_score += 5.0
# Long wait compensation
if wait_time_minutes > 60:
base_score += (wait_time_minutes - 60) * 1.0
return {
"triage_level": TriageLevel.LEVEL_4_NORMAL,
"tier_name": "NORMAL",
"priority_score": base_score,
"reasoning": "Stable patient - standard care",
"abnormalities": [],
"recommended_action": "See within 120 minutes",
}
@staticmethod
def _is_life_threatening(vitals: VitalSigns, symptoms: List[str]) -> bool:
"""Detect immediate life threats"""
# Critical vital signs
if vitals.is_critical():
return True
# Unresponsive patient
if vitals.consciousness_level.lower() in ["unresponsive", "unconscious"]:
return True
# Critical conditions in symptoms
return any(
cond.lower() in " ".join(symptoms).lower() for cond in CRITICAL_CONDITIONS
)
@staticmethod
def _is_high_risk(
vitals: VitalSigns,
symptoms: List[str],
severity: str,
age: Optional[int],
preexisting: List[str],
) -> bool:
"""Detect high-risk patients"""
# High-risk age groups
if age and (age < 1 or age > 65):
if vitals.is_abnormal() or severity == "severe":
return True
# High-risk symptoms
symptom_text = " ".join(symptoms).lower()
if any(symptom.lower() in symptom_text for symptom in HIGH_RISK_SYMPTOMS):
return True
# Severe self-reported severity with abnormal vitals
if severity == "severe" and vitals.is_abnormal():
return True
# Preexisting conditions with concerning vitals
if preexisting and vitals.is_abnormal():
return True
return False
@staticmethod
def _estimate_resources(symptoms: List[str], severity: str) -> int:
"""
Estimate number of resources needed
Resources: labs, imaging, IV access, procedures, consultations
"""
resource_count = 0
symptom_text = " ".join(symptoms).lower()
# Likely needs labs
if any(s in symptom_text for s in ["fever", "infection", "pain", "bleeding"]):
resource_count += 1
# Likely needs imaging
if any(
s in symptom_text
for s in ["head injury", "chest pain", "abdominal", "trauma"]
):
resource_count += 1
# Likely needs IV access
if any(
s in symptom_text
for s in ["difficulty breathing", "severe", "bleeding", "dehydration"]
):
resource_count += 1
# Complex intervention likely
if severity == "severe":
resource_count += 1
return resource_count
@staticmethod
def calculate_priority_score(
triage_level: TriageLevel,
vitals: VitalSigns,
severity: str,
wait_time_minutes: int,
preexisting: List[str] = None,
) -> float:
"""
Calculate final priority score for queue ordering
Higher score = higher priority
"""
preexisting = preexisting or []
# Base score from triage level
level_scores = {
TriageLevel.LEVEL_1_EMERGENCY: 200.0,
TriageLevel.LEVEL_2_WARNING: 100.0,
TriageLevel.LEVEL_3_ALERT: 50.0,
TriageLevel.LEVEL_4_NORMAL: 20.0,
}
score = level_scores.get(triage_level, 0.0)
# Vital sign modifiers
if vitals.is_critical():
score += 50.0
elif vitals.is_abnormal():
score += 20.0
# Specific vital concerns
if vitals.heart_rate > 140 or vitals.heart_rate < 40:
score += 30.0
if vitals.spo2 < 88:
score += 40.0
if vitals.temperature > 39.5:
score += 25.0
# Severity modifier
severity_scores = {"severe": 25, "moderate": 15, "mild": 5}
score += severity_scores.get(severity, 0)
# Wait time escalation
if wait_time_minutes > 30:
score += (wait_time_minutes - 30) * 0.5
if wait_time_minutes > 60:
score += (wait_time_minutes - 60) * 1.0 # Accelerate
if wait_time_minutes > 120:
score += (wait_time_minutes - 120) * 2.0 # Major escalation
# Preexisting conditions
if preexisting and triage_level != TriageLevel.LEVEL_1_EMERGENCY:
score += 10.0 * len(preexisting)
return round(score, 1)
@staticmethod
def detect_deterioration(
current_vitals: VitalSigns,
previous_vitals: List[VitalSigns],
time_window_minutes: int = 10,
) -> Dict:
"""
Detect if patient is deteriorating based on vital sign trends
Returns trend analysis and recommendation
"""
if len(previous_vitals) < 3:
return {
"deteriorating": False,
"reason": "Insufficient data for trend analysis",
}
# Take last 3 readings
recent = previous_vitals[-3:]
# Calculate trends
hr_trend = current_vitals.heart_rate - sum(v.heart_rate for v in recent) / len(
recent
)
spo2_trend = sum(v.spo2 for v in recent) / len(recent) - current_vitals.spo2
temp_trend = current_vitals.temperature - sum(
v.temperature for v in recent
) / len(recent)
# Detect concerning trends
concerns = []
if hr_trend > 15:
concerns.append(f"Increasing HR (+{hr_trend:.0f} bpm)")
if spo2_trend > 3:
concerns.append(f"Decreasing SpO2 (-{spo2_trend:.0f}%)")
if temp_trend > 0.5:
concerns.append(f"Rising temperature (+{temp_trend:.1f}°C)")
# Crossed into abnormal range
if current_vitals.is_critical() and not any(v.is_critical() for v in recent):
concerns.append("Vitals crossed into CRITICAL range")
elif current_vitals.is_abnormal() and not any(v.is_abnormal() for v in recent):
concerns.append("Vitals crossed into ABNORMAL range")
deteriorating = len(concerns) > 0
return {
"deteriorating": deteriorating,
"concerns": concerns,
"hr_trend": hr_trend,
"spo2_trend": spo2_trend,
"temp_trend": temp_trend,
"recommendation": "Escalate care immediately"
if deteriorating
else "Continue monitoring",
}
@staticmethod
def should_escalate_tier(
current_tier: TriageLevel, vitals: VitalSigns, wait_time_minutes: int
) -> tuple[bool, Optional[TriageLevel], str]:
"""
Determine if patient tier should be escalated
Returns (should_escalate, new_tier, reason)
"""
# Always escalate if vitals become critical
if vitals.is_critical() and current_tier != TriageLevel.LEVEL_1_EMERGENCY:
return (
True,
TriageLevel.LEVEL_1_EMERGENCY,
"Vitals entered critical range",
)
# Escalate NORMAL to ALERT if vitals abnormal
if current_tier == TriageLevel.LEVEL_4_NORMAL and vitals.is_abnormal():
return (True, TriageLevel.LEVEL_3_ALERT, "Vitals became abnormal")
# Escalate ALERT to WARNING if vitals worsening
if current_tier == TriageLevel.LEVEL_3_ALERT and vitals.is_critical():
return (
True,
TriageLevel.LEVEL_2_WARNING,
"Vitals worsening toward critical",
)
# Time-based escalation for long waits
if wait_time_minutes > 120 and current_tier == TriageLevel.LEVEL_4_NORMAL:
return (True, TriageLevel.LEVEL_3_ALERT, "Excessive wait time (>2 hours)")
if wait_time_minutes > 180 and current_tier == TriageLevel.LEVEL_3_ALERT:
return (True, TriageLevel.LEVEL_2_WARNING, "Critical wait time (>3 hours)")
return (False, None, "No escalation needed")
# ============================================================================
# CONVENIENCE FUNCTIONS
# ============================================================================
def triage_from_vitals(hr: int, spo2: int, temp: float) -> str:
"""
Quick triage tier determination from vital signs only
Returns: "EMERGENCY", "ALERT", or "NORMAL"
"""
vitals = VitalSigns(heart_rate=hr, spo2=spo2, temperature=temp)
if vitals.is_critical():
return "EMERGENCY"
elif vitals.is_abnormal():
return "ALERT"
else:
return "NORMAL"
def assess_new_patient(
hr: int,
spo2: int,
temp: float,
symptoms: List[str],
severity: str,
age: Optional[int] = None,
preexisting: List[str] = None,
) -> Dict:
"""
Complete assessment for new patient
Returns full triage result with score and recommendations
"""
vitals = VitalSigns(heart_rate=hr, spo2=spo2, temperature=temp)
result = TriageEngine.assess_patient(
vitals=vitals,
symptoms=symptoms,
severity=severity,
age=age,
preexisting=preexisting,
wait_time_minutes=0,
)
return result
def reassess_patient(
patient_data: Dict, current_vitals: Dict, wait_time_minutes: int
) -> Dict:
"""
Reassess existing patient with updated vitals and wait time
Returns updated triage assessment
"""
vitals = VitalSigns(
heart_rate=current_vitals.get("hr_bpm", 75),
spo2=current_vitals.get("spo2", 98),
temperature=current_vitals.get("temp_c", 37.0),
activity=current_vitals.get("activity", 0.0),
)
result = TriageEngine.assess_patient(
vitals=vitals,
symptoms=patient_data.get("symptoms", []),
severity=patient_data.get("severity", "moderate"),
age=patient_data.get("age"),
preexisting=patient_data.get("preexisting", []),
wait_time_minutes=wait_time_minutes,
)
return result
# ============================================================================
# EXAMPLE USAGE
# ============================================================================
if __name__ == "__main__":
print("=" * 80)
print("VitalLink Triage Engine - Test Cases")
print("=" * 80 + "\n")
# Test Case 1: Critical patient
print("Test 1: Critical Emergency")
result = assess_new_patient(
hr=155,
spo2=85,
temp=40.2,
symptoms=["Severe Chest Pain", "Difficulty Breathing"],
severity="severe",
)
print(f" Level: {result['triage_level'].name}")
print(f" Tier: {result['tier_name']}")
print(f" Score: {result['priority_score']}")
print(f" Reason: {result['reasoning']}")
print(f" Abnormalities: {result['abnormalities']}")
print()
# Test Case 2: Deteriorating patient
print("Test 2: Deteriorating Patient")
result = assess_new_patient(
hr=118,
spo2=93,
temp=38.5,
symptoms=["Fever", "Difficulty Breathing"],
severity="moderate",
)
print(f" Level: {result['triage_level'].name}")
print(f" Tier: {result['tier_name']}")
print(f" Score: {result['priority_score']}")
print()
# Test Case 3: Stable patient
print("Test 3: Stable Patient")
result = assess_new_patient(
hr=75, spo2=98, temp=36.9, symptoms=["Minor Pain"], severity="mild"
)
print(f" Level: {result['triage_level'].name}")
print(f" Tier: {result['tier_name']}")
print(f" Score: {result['priority_score']}")
print()
# Test Case 4: Long wait escalation
print("Test 4: Long Wait Time Escalation")
vitals = VitalSigns(heart_rate=78, spo2=97, temperature=37.1)
should_escalate, new_tier, reason = TriageEngine.should_escalate_tier(
current_tier=TriageLevel.LEVEL_4_NORMAL, vitals=vitals, wait_time_minutes=130
)
print(f" Should escalate: {should_escalate}")
print(f" New tier: {new_tier.name if new_tier else 'N/A'}")
print(f" Reason: {reason}")
print()
print("=" * 80)

View File

@ -1,4 +0,0 @@
200440
200455
200464
200523

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
200440

View File

@ -3,7 +3,7 @@
> vite
VITE v7.1.10 ready in 116 ms
VITE v7.1.10 ready in 107 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose

View File

@ -1 +0,0 @@
200464

View File

@ -1 +0,0 @@
200523

View File

@ -1 +0,0 @@
200455

View File

@ -181,18 +181,18 @@ class WristbandSimulator:
return int(hr), int(spo2), temp, self.activity_level
def _determine_tier(self, hr: int, spo2: int, temp: float) -> tuple:
"""Determine alert tier based on vitals"""
"""Determine flags based on vitals - tier determined by backend"""
flags = 0
# Battery warning
if self.battery < 15:
flags |= VitalFlags.LOW_BATT
# Check for critical vitals (EMERGENCY)
# Set EMERGENCY flag if critical vitals
if hr > 140 or hr < 45 or spo2 < 88 or temp > 39.5 or temp < 35.0:
flags |= VitalFlags.EMERGENCY
tier = "EMERGENCY"
# Check for concerning vitals (ALERT)
# Set ALERT flag if concerning vitals
elif hr > 110 or hr < 50 or spo2 < 92 or temp > 38.3 or temp < 35.5:
flags |= VitalFlags.ALERT
tier = "ALERT"