use crate::domain::{
triage::TriageCalculator, Priority, Survivor, TriageStatus, VitalSignsReading,
};
pub struct TriageService;
impl TriageService {
pub fn calculate_triage(vitals: &VitalSignsReading) -> TriageStatus {
TriageCalculator::calculate(vitals)
}
pub fn should_upgrade(survivor: &Survivor) -> bool {
TriageCalculator::should_upgrade(survivor.triage_status(), survivor.is_deteriorating())
}
pub fn upgrade_status(current: &TriageStatus) -> TriageStatus {
TriageCalculator::upgrade(current)
}
pub fn evaluate_mass_casualty(survivors: &[&Survivor]) -> MassCasualtyAssessment {
let total = survivors.len() as u32;
let mut immediate = 0u32;
let mut delayed = 0u32;
let mut minor = 0u32;
let mut deceased = 0u32;
let mut unknown = 0u32;
for survivor in survivors {
match survivor.triage_status() {
TriageStatus::Immediate => immediate += 1,
TriageStatus::Delayed => delayed += 1,
TriageStatus::Minor => minor += 1,
TriageStatus::Deceased => deceased += 1,
TriageStatus::Unknown => unknown += 1,
}
}
let severity = Self::calculate_severity(immediate, delayed, total);
let resource_level = Self::calculate_resource_level(immediate, delayed, minor);
MassCasualtyAssessment {
total,
immediate,
delayed,
minor,
deceased,
unknown,
severity,
resource_level,
}
}
fn calculate_severity(immediate: u32, delayed: u32, total: u32) -> SeverityLevel {
if total == 0 {
return SeverityLevel::Minimal;
}
let critical_ratio = (immediate + delayed) as f64 / total as f64;
if immediate >= 10 || critical_ratio > 0.5 {
SeverityLevel::Critical
} else if immediate >= 5 || critical_ratio > 0.3 {
SeverityLevel::Major
} else if immediate >= 1 || critical_ratio > 0.1 {
SeverityLevel::Moderate
} else {
SeverityLevel::Minimal
}
}
fn calculate_resource_level(immediate: u32, delayed: u32, minor: u32) -> ResourceLevel {
let rescuers_needed = immediate * 4 + delayed * 2 + minor / 2;
if rescuers_needed >= 100 {
ResourceLevel::MutualAid
} else if rescuers_needed >= 50 {
ResourceLevel::MultiAgency
} else if rescuers_needed >= 20 {
ResourceLevel::Enhanced
} else if rescuers_needed >= 5 {
ResourceLevel::Standard
} else {
ResourceLevel::Minimal
}
}
}
pub struct PriorityCalculator;
impl PriorityCalculator {
pub fn from_triage(status: &TriageStatus) -> Priority {
Priority::from_triage(status)
}
pub fn calculate_with_factors(
status: &TriageStatus,
deteriorating: bool,
time_since_detection_mins: u64,
depth_meters: Option<f64>,
) -> Priority {
let base_priority = Priority::from_triage(status);
let priority = if deteriorating && base_priority != Priority::Critical {
match base_priority {
Priority::High => Priority::Critical,
Priority::Medium => Priority::High,
Priority::Low => Priority::Medium,
Priority::Critical => Priority::Critical,
}
} else {
base_priority
};
let priority = if time_since_detection_mins > 30 && priority == Priority::Medium {
Priority::High
} else {
priority
};
if let Some(depth) = depth_meters {
if depth > 3.0 && priority == Priority::High {
return Priority::Critical;
}
}
priority
}
}
#[derive(Debug, Clone)]
pub struct MassCasualtyAssessment {
pub total: u32,
pub immediate: u32,
pub delayed: u32,
pub minor: u32,
pub deceased: u32,
pub unknown: u32,
pub severity: SeverityLevel,
pub resource_level: ResourceLevel,
}
impl MassCasualtyAssessment {
pub fn living(&self) -> u32 {
self.immediate + self.delayed + self.minor
}
pub fn needs_rescue(&self) -> u32 {
self.immediate + self.delayed
}
pub fn summary(&self) -> String {
format!(
"MCI Assessment:\n\
Total: {} (Living: {}, Deceased: {})\n\
Immediate: {}, Delayed: {}, Minor: {}\n\
Severity: {:?}, Resources: {:?}",
self.total,
self.living(),
self.deceased,
self.immediate,
self.delayed,
self.minor,
self.severity,
self.resource_level
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SeverityLevel {
Minimal,
Moderate,
Major,
Critical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResourceLevel {
Minimal,
Standard,
Enhanced,
MultiAgency,
MutualAid,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::domain::{BreathingPattern, BreathingType, ConfidenceScore, ScanZoneId};
use chrono::Utc;
fn create_test_vitals(rate_bpm: f32) -> VitalSignsReading {
VitalSignsReading {
breathing: Some(BreathingPattern {
rate_bpm,
amplitude: 0.8,
regularity: 0.9,
pattern_type: BreathingType::Normal,
}),
heartbeat: None,
movement: Default::default(),
timestamp: Utc::now(),
confidence: ConfidenceScore::new(0.8),
}
}
#[test]
fn test_calculate_triage() {
let normal = create_test_vitals(16.0);
assert!(matches!(
TriageService::calculate_triage(&normal),
TriageStatus::Immediate | TriageStatus::Delayed | TriageStatus::Minor
));
let fast = create_test_vitals(35.0);
assert!(matches!(
TriageService::calculate_triage(&fast),
TriageStatus::Immediate
));
}
#[test]
fn test_priority_from_triage() {
assert_eq!(
PriorityCalculator::from_triage(&TriageStatus::Immediate),
Priority::Critical
);
assert_eq!(
PriorityCalculator::from_triage(&TriageStatus::Delayed),
Priority::High
);
}
#[test]
fn test_mass_casualty_assessment() {
let survivors: Vec<Survivor> = (0..10)
.map(|i| {
let rate = if i < 3 {
35.0
} else if i < 6 {
16.0
} else {
18.0
};
Survivor::new(ScanZoneId::new(), create_test_vitals(rate), None)
})
.collect();
let survivor_refs: Vec<&Survivor> = survivors.iter().collect();
let assessment = TriageService::evaluate_mass_casualty(&survivor_refs);
assert_eq!(assessment.total, 10);
assert!(assessment.living() >= assessment.needs_rescue());
}
#[test]
fn test_priority_with_factors() {
let priority =
PriorityCalculator::calculate_with_factors(&TriageStatus::Delayed, true, 0, None);
assert_eq!(priority, Priority::Critical);
let priority =
PriorityCalculator::calculate_with_factors(&TriageStatus::Delayed, false, 0, Some(4.0));
assert_eq!(priority, Priority::Critical);
}
}