| | | 1 | | using Intentum.Core.Behavior; |
| | | 2 | | using Intentum.Core.Models; |
| | | 3 | | |
| | | 4 | | namespace Intentum.Core.Healthcare; |
| | | 5 | | |
| | | 6 | | public static class HealthcareRules |
| | | 7 | | { |
| | | 8 | | public static Func<BehaviorSpace, RuleMatch?> PatientDeterioration( |
| | 4 | 9 | | double confidence = 0.9) => space => |
| | 3 | 10 | | { |
| | 3 | 11 | | var vitalAlerts = space.Events.Count(e => |
| | 7 | 12 | | e.Action.Contains("vital.signs.alert", StringComparison.OrdinalIgnoreCase) || |
| | 7 | 13 | | e.Action.Contains("VitalAlert", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 14 | | var rapidResponse = space.Events.Any(e => |
| | 7 | 15 | | e.Action.Contains("rapid.response", StringComparison.OrdinalIgnoreCase) || |
| | 7 | 16 | | e.Action.Contains("RapidResponse", StringComparison.OrdinalIgnoreCase)); |
| | 4 | 17 | | |
| | 3 | 18 | | if (vitalAlerts >= 1 && rapidResponse) |
| | 1 | 19 | | return new RuleMatch("PatientDeterioration", confidence, |
| | 1 | 20 | | $"Vital alerts: {vitalAlerts}, rapid response: true"); |
| | 2 | 21 | | if (vitalAlerts >= 2) |
| | 1 | 22 | | return new RuleMatch("PatientDeterioration", confidence * 0.7, |
| | 1 | 23 | | $"Multiple vital alerts: {vitalAlerts}, no rapid response"); |
| | 1 | 24 | | return null; |
| | 7 | 25 | | }; |
| | | 26 | | |
| | | 27 | | public static Func<BehaviorSpace, RuleMatch?> MedicationConflict( |
| | 3 | 28 | | double confidence = 0.85) => space => |
| | 2 | 29 | | { |
| | 2 | 30 | | var medicationOrders = space.Events.Count(e => |
| | 5 | 31 | | e.Action.StartsWith("medication.ordered", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 32 | | |
| | 2 | 33 | | if (medicationOrders >= 2) |
| | 1 | 34 | | return new RuleMatch("MedicationConflict", confidence, |
| | 1 | 35 | | $"Distinct medication orders: {medicationOrders}"); |
| | 1 | 36 | | return null; |
| | 5 | 37 | | }; |
| | | 38 | | |
| | | 39 | | public static Func<BehaviorSpace, RuleMatch?> HospitalReadmissionRisk( |
| | 3 | 40 | | double confidence = 0.8) => space => |
| | 2 | 41 | | { |
| | 2 | 42 | | var discharged = space.Events.Any(e => |
| | 4 | 43 | | e.Action.Contains("discharge", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 44 | | var symptoms = space.Events.Count(e => |
| | 5 | 45 | | e.Action.Contains("symptom.report", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 46 | | var followUp = space.Events.Any(e => |
| | 5 | 47 | | e.Action.Contains("followup.visit", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 48 | | |
| | 2 | 49 | | if (discharged && (symptoms >= 1 || followUp)) |
| | 1 | 50 | | return new RuleMatch("HospitalReadmissionRisk", confidence, |
| | 1 | 51 | | $"Discharged: true, symptoms: {symptoms}, follow-up: {followUp}"); |
| | 1 | 52 | | return null; |
| | 5 | 53 | | }; |
| | | 54 | | |
| | | 55 | | public static Func<BehaviorSpace, RuleMatch?> SepsisAlert( |
| | 3 | 56 | | double confidence = 0.95) => space => |
| | 2 | 57 | | { |
| | 2 | 58 | | var infectionConfirmed = space.Events.Any(e => |
| | 4 | 59 | | e.Action.Contains("infection.confirmed", StringComparison.OrdinalIgnoreCase) || |
| | 4 | 60 | | e.Action.Contains("InfectionConfirmed", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 61 | | var abnormalVitals = space.Events.Count(e => |
| | 5 | 62 | | e.Action.Contains("vital.abnormal", StringComparison.OrdinalIgnoreCase) || |
| | 5 | 63 | | e.Action.Contains("AbnormalVital", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 64 | | |
| | 2 | 65 | | if (infectionConfirmed && abnormalVitals >= 1) |
| | 1 | 66 | | return new RuleMatch("SepsisAlert", confidence, |
| | 1 | 67 | | $"Infection confirmed: true, abnormal vitals: {abnormalVitals}"); |
| | 1 | 68 | | return null; |
| | 5 | 69 | | }; |
| | | 70 | | |
| | | 71 | | public static Func<BehaviorSpace, RuleMatch?> FallRisk( |
| | 3 | 72 | | double confidence = 0.8) => space => |
| | 2 | 73 | | { |
| | 2 | 74 | | var signals = space.Events.Count(e => |
| | 4 | 75 | | e.Action.Contains("mobility.issue", StringComparison.OrdinalIgnoreCase) || |
| | 4 | 76 | | e.Action.Contains("age.fall.risk", StringComparison.OrdinalIgnoreCase) || |
| | 4 | 77 | | e.Action.Contains("fall.incident", StringComparison.OrdinalIgnoreCase) || |
| | 4 | 78 | | e.Action.Contains("balance.problem", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 79 | | |
| | 2 | 80 | | if (signals >= 2) |
| | 1 | 81 | | return new RuleMatch("FallRisk", confidence, |
| | 1 | 82 | | $"Fall risk signals: {signals}"); |
| | 1 | 83 | | return null; |
| | 5 | 84 | | }; |
| | | 85 | | |
| | | 86 | | public static Func<BehaviorSpace, RuleMatch?> AbnormalLabResult( |
| | 3 | 87 | | double confidence = 0.9) => space => |
| | 2 | 88 | | { |
| | 2 | 89 | | var signals = space.Events.Count(e => |
| | 3 | 90 | | e.Action.Contains("lab.critical", StringComparison.OrdinalIgnoreCase) || |
| | 3 | 91 | | e.Action.Contains("lab.trend.abnormal", StringComparison.OrdinalIgnoreCase) || |
| | 3 | 92 | | e.Action.Contains("lab.result.abnormal", StringComparison.OrdinalIgnoreCase) || |
| | 3 | 93 | | e.Action.Contains("lab.panic.value", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 94 | | |
| | 2 | 95 | | if (signals >= 1) |
| | 1 | 96 | | return new RuleMatch("AbnormalLabResult", confidence, |
| | 1 | 97 | | $"Abnormal lab signals: {signals}"); |
| | 1 | 98 | | return null; |
| | 5 | 99 | | }; |
| | | 100 | | |
| | | 101 | | public static Func<BehaviorSpace, RuleMatch?> TreatmentNonCompliance( |
| | 3 | 102 | | double confidence = 0.75) => space => |
| | 2 | 103 | | { |
| | 2 | 104 | | var signals = space.Events.Count(e => |
| | 4 | 105 | | e.Action.Contains("appointment.missed", StringComparison.OrdinalIgnoreCase) || |
| | 4 | 106 | | e.Action.Contains("medication.gap", StringComparison.OrdinalIgnoreCase) || |
| | 4 | 107 | | e.Action.Contains("treatment.refused", StringComparison.OrdinalIgnoreCase) || |
| | 4 | 108 | | e.Action.Contains("followup.missed", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 109 | | |
| | 2 | 110 | | if (signals >= 2) |
| | 1 | 111 | | return new RuleMatch("TreatmentNonCompliance", confidence, |
| | 1 | 112 | | $"Compliance signals: {signals}"); |
| | 1 | 113 | | return null; |
| | 5 | 114 | | }; |
| | | 115 | | |
| | | 116 | | public static Func<BehaviorSpace, RuleMatch?> EmergencyTriage( |
| | 3 | 117 | | double confidence = 0.85) => space => |
| | 2 | 118 | | { |
| | 2 | 119 | | var signals = space.Events.Count(e => |
| | 4 | 120 | | e.Action.Contains("symptom.severe", StringComparison.OrdinalIgnoreCase) || |
| | 4 | 121 | | e.Action.Contains("triage.urgent", StringComparison.OrdinalIgnoreCase) || |
| | 4 | 122 | | e.Action.Contains("wait.time.critical", StringComparison.OrdinalIgnoreCase) || |
| | 4 | 123 | | e.Action.Contains("resource.shortage", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 124 | | |
| | 2 | 125 | | if (signals >= 2) |
| | 1 | 126 | | return new RuleMatch("EmergencyTriage", confidence, |
| | 1 | 127 | | $"Triage signals: {signals}"); |
| | 1 | 128 | | return null; |
| | 5 | 129 | | }; |
| | | 130 | | |
| | | 131 | | public static IReadOnlyList<Func<BehaviorSpace, RuleMatch?>> AllRules() => |
| | 1 | 132 | | [ |
| | 1 | 133 | | SepsisAlert(), |
| | 1 | 134 | | PatientDeterioration(), |
| | 1 | 135 | | MedicationConflict(), |
| | 1 | 136 | | HospitalReadmissionRisk(), |
| | 1 | 137 | | FallRisk(), |
| | 1 | 138 | | AbnormalLabResult(), |
| | 1 | 139 | | TreatmentNonCompliance(), |
| | 1 | 140 | | EmergencyTriage() |
| | 1 | 141 | | ]; |
| | | 142 | | } |