feature: Simulator Scenarios + Clinical Validation: SOFA/GCS

This commit is contained in:
voltsrage
2026-06-21 04:55:39 +08:00
parent bf46e6554a
commit 3638dd0669
39 changed files with 1619 additions and 213 deletions
@@ -0,0 +1,3 @@
public record GcsResponse(
int EyeScore, int VerbalScore, int MotorScore,
int TotalScore, string Classification, DateTimeOffset CalculatedAt);
@@ -0,0 +1 @@
public record QsofaResponse(int ActiveCriteria);
@@ -0,0 +1,7 @@
public record SofaResponse(
int TotalScore,
int RespiratoryScore, int CoagulationScore, int LiverScore,
int CardiovascularScore, int CnsScore, int RenalScore,
bool IsBaseline, int? DeltaFromBaseline,
SofaStalenessResponse? Staleness,
DateTimeOffset CalculatedAt);
@@ -0,0 +1,4 @@
public record SofaStalenessResponse(
IReadOnlyList<string> StaleComponents,
IReadOnlyList<string> MissingComponents,
bool UsedSpO2Fallback);
@@ -119,4 +119,28 @@ public class VigilCareApiClient
.ReadFromJsonAsync<ApiResponse<SepsisBundleResponse>>();
return envelope?.Data;
}
public async Task<GcsResponse?> GetCurrentGcsAsync(Guid encounterId)
{
var response = await _http.GetAsync($"/api/v1/encounters/{encounterId}/gcs");
if (!response.IsSuccessStatusCode) return null;
var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<GcsResponse>>();
return envelope?.Data;
}
public async Task<SofaResponse?> GetCurrentSofaAsync(Guid encounterId)
{
var response = await _http.GetAsync($"/api/v1/encounters/{encounterId}/sofa");
if (!response.IsSuccessStatusCode) return null;
var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<SofaResponse>>();
return envelope?.Data;
}
public async Task<QsofaResponse?> GetCurrentQsofaAsync(Guid encounterId)
{
var response = await _http.GetAsync($"/api/v1/encounters/{encounterId}/qsofa/current");
if (!response.IsSuccessStatusCode) return null;
var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<QsofaResponse>>();
return envelope?.Data;
}
}
+18 -1
View File
@@ -71,6 +71,22 @@ public static class SimulatorConsole
AnsiConsole.MarkupLine(
$"[cyan][[{simTime}]][/] NEWS2 = {poll.News2.TotalScore} ({poll.News2.RiskLevel})");
if (poll.Gcs is not null)
AnsiConsole.MarkupLine(
$"[cyan][[{simTime}]][/] GCS = {poll.Gcs.TotalScore}/15 ({poll.Gcs.Classification}) " +
$"E{poll.Gcs.EyeScore} V{poll.Gcs.VerbalScore} M{poll.Gcs.MotorScore}");
if (poll.Sofa is not null)
{
var delta = poll.Sofa.DeltaFromBaseline is int d ? $" Δ+{d}" : "";
AnsiConsole.MarkupLine(
$"[cyan][[{simTime}]][/] SOFA = {poll.Sofa.TotalScore}/24{delta}");
}
if (poll.Qsofa is not null)
AnsiConsole.MarkupLine(
$"[cyan][[{simTime}]][/] qSOFA screen = {poll.Qsofa.ActiveCriteria}/3");
foreach (var alert in poll.NewAlerts)
AnsiConsole.MarkupLine(
$"[red][[{simTime}]][/] ALERT {alert.AlertType} ({alert.Severity})");
@@ -78,6 +94,7 @@ public static class SimulatorConsole
if (poll.SepsisBundle is not null)
AnsiConsole.MarkupLine(
$"[cyan][[{simTime}]][/] SEPSIS BUNDLE {poll.SepsisBundle.ComplianceStatus} " +
$"({poll.SepsisBundle.ElementsCompleted}/4)");
$"({poll.SepsisBundle.ElementsCompleted}/4) " +
$"trigger={poll.SepsisBundle.TriggeringAlertType}");
}
}
+4 -1
View File
@@ -8,13 +8,16 @@ public class ApiPoller
public async Task PollAndDisplayAsync(Guid encounterId, string simTime)
{
var news2 = await _client.GetCurrentNews2Async(encounterId);
var gcs = await _client.GetCurrentGcsAsync(encounterId);
var sofa = await _client.GetCurrentSofaAsync(encounterId);
var qsofa = await _client.GetCurrentQsofaAsync(encounterId);
var allAlerts = await _client.GetAlertsAsync(encounterId);
var newAlerts = allAlerts.Where(a => _seenAlertIds.Add(a.Id)).ToList();
var bundle = await _client.GetSepsisBundleAsync(encounterId);
var result = new PollResult(news2, newAlerts, bundle);
var result = new PollResult(news2, gcs, sofa, qsofa, newAlerts, bundle);
SimulatorConsole.PollResults(simTime, result);
}
}
+4 -1
View File
@@ -1,4 +1,7 @@
public record PollResult(
News2Response? News2,
List<AlertResponse> NewAlerts,
GcsResponse? Gcs,
SofaResponse? Sofa,
QsofaResponse? Qsofa,
IReadOnlyList<AlertResponse> NewAlerts,
SepsisBundleResponse? SepsisBundle);
@@ -99,6 +99,36 @@
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_EYE",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_VERBAL",
"value": 5,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_MOTOR",
"value": 6,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 15,
"type": "observation",
@@ -101,6 +101,36 @@
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_EYE",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_VERBAL",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_MOTOR",
"value": 6,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 2,
"type": "observation",
@@ -110,6 +110,37 @@
},
"note": "Nasal cannula 2L/min started on arrival"
},
{
"offsetMinutes": 1,
"type": "observation",
"data": {
"code": "GCS_EYE",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 1,
"type": "observation",
"data": {
"code": "GCS_VERBAL",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 1,
"type": "observation",
"data": {
"code": "GCS_MOTOR",
"value": 5,
"unit": "score",
"source": "Manual"
},
"note": "GCS 13 — sluggish, confused from hypothermia"
},
{
"offsetMinutes": 0,
"type": "order",
@@ -56,6 +56,21 @@
"type": "observation",
"data": { "code": "AVPU", "value": 0, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_EYE", "value": 4, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_VERBAL", "value": 5, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_MOTOR", "value": 6, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 0,
@@ -0,0 +1,168 @@
{
"scenario": {
"id": "neurological-decline-gcs-01",
"name": "Traumatic Brain Injury — Progressive GCS Decline",
"description": "Post-trauma patient with initial GCS 14 (E4V4M6). Over 3 hours, GCS declines to 6. Triggers GCS_WARNING then GCS_CRITICAL. Vitals remain stable — neuro-specific deterioration, not sepsis.",
"durationMinutes": 180,
"tags": ["gcs", "neurological", "no-sepsis", "news2"]
},
"patient": {
"firstName": "David",
"lastName": "Okonkwo",
"dateOfBirth": "1981-07-22",
"gender": "Male"
},
"encounter": {
"department": "Icu",
"encounterType": "Inpatient",
"attendingPhysician": "Dr. Amara Singh",
"roomBed": "ICU-1A-3",
"admissionReason": "TBI — motor vehicle collision, GCS 14 on arrival"
},
"events": [
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "HEART_RATE", "value": 72, "unit": "bpm", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "RESP_RATE", "value": 16, "unit": "/min", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "SYSTOLIC_BP", "value": 135, "unit": "mmHg", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "DIASTOLIC_BP", "value": 82, "unit": "mmHg", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "TEMP_C", "value": 36.9, "unit": "°C", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "SPO2", "value": 99, "unit": "%", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "SUPPLEMENTAL_O2", "value": 0, "unit": "flag", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_EYE", "value": 4, "unit": "score", "source": "Manual" },
"note": "GCS 14 — confused speech"
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_VERBAL", "value": 4, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_MOTOR", "value": 6, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 60,
"type": "observation",
"data": { "code": "GCS_EYE", "value": 3, "unit": "score", "source": "Manual" },
"note": "GCS 12 → GCS_WARNING"
},
{
"offsetMinutes": 60,
"type": "observation",
"data": { "code": "GCS_VERBAL", "value": 3, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 60,
"type": "observation",
"data": { "code": "GCS_MOTOR", "value": 6, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 60,
"type": "observation",
"data": { "code": "HEART_RATE", "value": 68, "unit": "bpm", "source": "Device" }
},
{
"offsetMinutes": 60,
"type": "observation",
"data": { "code": "SYSTOLIC_BP", "value": 142, "unit": "mmHg", "source": "Device" },
"note": "Cushing response — HTN with bradycardia developing"
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "GCS_EYE", "value": 2, "unit": "score", "source": "Manual" },
"note": "GCS 9 — still GCS_WARNING band"
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "GCS_VERBAL", "value": 3, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "GCS_MOTOR", "value": 4, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "HEART_RATE", "value": 58, "unit": "bpm", "source": "Device" }
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "SYSTOLIC_BP", "value": 168, "unit": "mmHg", "source": "Device" }
},
{
"offsetMinutes": 150,
"type": "observation",
"data": { "code": "GCS_EYE", "value": 1, "unit": "score", "source": "Manual" },
"note": "GCS 6 → GCS_CRITICAL"
},
{
"offsetMinutes": 150,
"type": "observation",
"data": { "code": "GCS_VERBAL", "value": 2, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 150,
"type": "observation",
"data": { "code": "GCS_MOTOR", "value": 3, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 155,
"type": "order",
"data": {
"orderType": "Imaging",
"orderCode": "CT_HEAD",
"description": "STAT CT head — GCS decline from 14 to 6",
"orderedBy": "Dr. Amara Singh"
}
}
],
"expectedOutcomes": [
{
"afterOffsetMinutes": 60,
"type": "alert",
"alertType": "GCS_WARNING",
"description": "GCS 12 in 912 band"
},
{
"afterOffsetMinutes": 150,
"type": "alert",
"alertType": "GCS_CRITICAL",
"description": "GCS 6 ≤ 8"
}
]
}
@@ -110,6 +110,36 @@
},
"note": "Nasal cannula 2L/min \u2014 standard post-op order"
},
{
"offsetMinutes": 1,
"type": "observation",
"data": {
"code": "GCS_EYE",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 1,
"type": "observation",
"data": {
"code": "GCS_VERBAL",
"value": 5,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 1,
"type": "observation",
"data": {
"code": "GCS_MOTOR",
"value": 6,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 20,
"type": "observation",
@@ -718,6 +748,37 @@
{
"offsetMinutes": 120,
"type": "observation",
"data": {
"code": "GCS_EYE",
"value": 3,
"unit": "score",
"source": "Manual"
},
"note": "GCS 11 \u2014 hemorrhagic shock, declining consciousness"
},
{
"offsetMinutes": 120,
"type": "observation",
"data": {
"code": "GCS_VERBAL",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 120,
"type": "observation",
"data": {
"code": "GCS_MOTOR",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 121,
"type": "observation",
"data": {
"code": "SUPPLEMENTAL_O2",
"value": 1,
@@ -726,7 +787,7 @@
}
},
{
"offsetMinutes": 120,
"offsetMinutes": 121,
"type": "observation",
"data": {
"code": "LACTATE_MMOL_L",
@@ -98,6 +98,36 @@
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_EYE",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_VERBAL",
"value": 5,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_MOTOR",
"value": 6,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "order",
@@ -0,0 +1,263 @@
{
"scenario": {
"id": "sepsis-sofa-progression-01",
"name": "Pneumonia → Multi-Organ Dysfunction (SOFA Pathway)",
"description": "ICU patient with community-acquired pneumonia. Initial SOFA baseline ~2. Over 4 hours, respiratory failure, thrombocytopenia, and renal impairment develop. qSOFA screen fires early; SOFA delta ≥ 2 triggers sepsis bundle. GCS remains 15 until late decline to 13.",
"durationMinutes": 240,
"tags": ["sofa", "sepsis", "multi-organ", "gcs", "qsofa-screen"]
},
"patient": {
"firstName": "Margaret",
"lastName": "Torres",
"dateOfBirth": "1954-03-18",
"gender": "Female"
},
"encounter": {
"department": "Icu",
"encounterType": "Inpatient",
"attendingPhysician": "Dr. Elena Vasquez",
"roomBed": "ICU-2A-6",
"admissionReason": "Community-acquired pneumonia, worsening dyspnea"
},
"events": [
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "HEART_RATE", "value": 88, "unit": "bpm", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "RESP_RATE", "value": 18, "unit": "/min", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "SYSTOLIC_BP", "value": 128, "unit": "mmHg", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "DIASTOLIC_BP", "value": 78, "unit": "mmHg", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "TEMP_C", "value": 38.2, "unit": "°C", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "SPO2", "value": 94, "unit": "%", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "SUPPLEMENTAL_O2", "value": 1, "unit": "flag", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_EYE", "value": 4, "unit": "score", "source": "Manual" },
"note": "Baseline GCS 15 — fully alert"
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_VERBAL", "value": 5, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_MOTOR", "value": 6, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 5,
"type": "observation",
"data": { "code": "PAO2_MMHG", "value": 85, "unit": "mmHg", "source": "Lab" },
"note": "ABG baseline — P/F ~283 at FiO2 30%"
},
{
"offsetMinutes": 5,
"type": "observation",
"data": { "code": "FIO2_PCT", "value": 30, "unit": "%", "source": "Manual" }
},
{
"offsetMinutes": 5,
"type": "observation",
"data": { "code": "PLATELET_K_UL", "value": 185, "unit": "k/µL", "source": "Lab" }
},
{
"offsetMinutes": 5,
"type": "observation",
"data": { "code": "BILIRUBIN_MG_DL", "value": 0.9, "unit": "mg/dL", "source": "Lab" }
},
{
"offsetMinutes": 5,
"type": "observation",
"data": { "code": "CREATININE_MG_DL", "value": 1.0, "unit": "mg/dL", "source": "Lab" }
},
{
"offsetMinutes": 5,
"type": "observation",
"data": { "code": "LACTATE_MMOL_L", "value": 1.8, "unit": "mmol/L", "source": "Lab" }
},
{
"offsetMinutes": 60,
"type": "observation",
"data": { "code": "HEART_RATE", "value": 102, "unit": "bpm", "source": "Device" },
"note": "Mild tachycardia; qSOFA RR ≥ 22"
},
{
"offsetMinutes": 60,
"type": "observation",
"data": { "code": "RESP_RATE", "value": 24, "unit": "/min", "source": "Device" }
},
{
"offsetMinutes": 60,
"type": "observation",
"data": { "code": "SYSTOLIC_BP", "value": 105, "unit": "mmHg", "source": "Device" }
},
{
"offsetMinutes": 60,
"type": "observation",
"data": { "code": "TEMP_C", "value": 38.9, "unit": "°C", "source": "Manual" }
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "RESP_RATE", "value": 28, "unit": "/min", "source": "Device" }
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "SYSTOLIC_BP", "value": 92, "unit": "mmHg", "source": "Device" },
"note": "SBP ≤ 100 → qSOFA ≥ 2 → QSOFA_SCREEN"
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "DIASTOLIC_BP", "value": 58, "unit": "mmHg", "source": "Device" }
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "SPO2", "value": 89, "unit": "%", "source": "Device" }
},
{
"offsetMinutes": 125,
"type": "observation",
"data": { "code": "PAO2_MMHG", "value": 62, "unit": "mmHg", "source": "Lab" },
"note": "Repeat ABG — P/F 155 at FiO2 40% → SOFA Resp 2"
},
{
"offsetMinutes": 125,
"type": "observation",
"data": { "code": "FIO2_PCT", "value": 40, "unit": "%", "source": "Manual" }
},
{
"offsetMinutes": 125,
"type": "observation",
"data": { "code": "PLATELET_K_UL", "value": 88, "unit": "k/µL", "source": "Lab" },
"note": "Thrombocytopenia → SOFA Coag 1"
},
{
"offsetMinutes": 125,
"type": "observation",
"data": { "code": "CREATININE_MG_DL", "value": 2.3, "unit": "mg/dL", "source": "Lab" },
"note": "AKI → SOFA Renal 2"
},
{
"offsetMinutes": 125,
"type": "observation",
"data": { "code": "LACTATE_MMOL_L", "value": 3.1, "unit": "mmol/L", "source": "Lab" }
},
{
"offsetMinutes": 130,
"type": "medication",
"data": {
"drugName": "Norepinephrine",
"dose": 0.05,
"doseUnit": "mcg/kg/min",
"route": "IV",
"administeredBy": "RN-Torres"
},
"note": "Vasopressor → SOFA CV 3; delta ≥ 2 → SOFA_SEPSIS + bundle"
},
{
"offsetMinutes": 180,
"type": "observation",
"data": { "code": "PAO2_MMHG", "value": 55, "unit": "mmHg", "source": "Lab" }
},
{
"offsetMinutes": 180,
"type": "observation",
"data": { "code": "FIO2_PCT", "value": 60, "unit": "%", "source": "Manual" }
},
{
"offsetMinutes": 180,
"type": "observation",
"data": { "code": "PLATELET_K_UL", "value": 42, "unit": "k/µL", "source": "Lab" }
},
{
"offsetMinutes": 180,
"type": "observation",
"data": { "code": "CREATININE_MG_DL", "value": 3.8, "unit": "mg/dL", "source": "Lab" }
},
{
"offsetMinutes": 200,
"type": "observation",
"data": { "code": "GCS_EYE", "value": 3, "unit": "score", "source": "Manual" },
"note": "GCS 13 — SOFA CNS 1"
},
{
"offsetMinutes": 200,
"type": "observation",
"data": { "code": "GCS_VERBAL", "value": 4, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 200,
"type": "observation",
"data": { "code": "GCS_MOTOR", "value": 6, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 210,
"type": "order",
"data": {
"orderType": "Lab",
"orderCode": "BLOOD_CULTURE",
"description": "Blood cultures x2 — sepsis workup",
"orderedBy": "Dr. Elena Vasquez"
}
},
{
"offsetMinutes": 210,
"type": "order",
"data": {
"orderType": "Lab",
"orderCode": "SERUM_LACTATE",
"description": "Repeat lactate",
"orderedBy": "Dr. Elena Vasquez"
}
}
],
"expectedOutcomes": [
{
"afterOffsetMinutes": 120,
"type": "alert",
"alertType": "QSOFA_SCREEN",
"description": "qSOFA ≥ 2 — bedside screen positive, recommend SOFA labs"
},
{
"afterOffsetMinutes": 130,
"type": "alert",
"alertType": "SOFA_SEPSIS",
"description": "SOFA delta ≥ 2 from baseline after labs + vasopressor"
},
{
"afterOffsetMinutes": 130,
"type": "bundle",
"description": "Sepsis bundle auto-created; triggeringAlertType = SOFA_SEPSIS"
}
]
}
@@ -0,0 +1,166 @@
{
"scenario": {
"id": "sofa-partial-spo2-fallback-01",
"name": "Ward Patient — Partial SOFA with SpO₂/FiO₂ Proxy",
"description": "General medicine patient with worsening pneumonia. No ABG — SpO₂/FiO₂ used for respiratory SOFA. Partial scoring when labs missing; carry-forward and staleness flags when data ages.",
"durationMinutes": 360,
"tags": ["sofa", "partial-scoring", "spo2-fallback", "carry-forward", "ward"]
},
"patient": {
"firstName": "Priya",
"lastName": "Sharma",
"dateOfBirth": "1970-01-14",
"gender": "Female"
},
"encounter": {
"department": "GeneralMedicine",
"encounterType": "Inpatient",
"attendingPhysician": "Dr. Noah Chen",
"roomBed": "GM-8B-2",
"admissionReason": "Worsening pneumonia, transferred from ED"
},
"events": [
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "HEART_RATE", "value": 92, "unit": "bpm", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "RESP_RATE", "value": 20, "unit": "/min", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "SYSTOLIC_BP", "value": 118, "unit": "mmHg", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "DIASTOLIC_BP", "value": 72, "unit": "mmHg", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "SPO2", "value": 93, "unit": "%", "source": "Device" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "FIO2_PCT", "value": 28, "unit": "%", "source": "Manual" },
"note": "Nasal cannula 2L — S/F ~332, SOFA Resp 0"
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "SUPPLEMENTAL_O2", "value": 1, "unit": "flag", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "TEMP_C", "value": 38.5, "unit": "°C", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_EYE", "value": 4, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_VERBAL", "value": 5, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 0,
"type": "observation",
"data": { "code": "GCS_MOTOR", "value": 6, "unit": "score", "source": "Manual" }
},
{
"offsetMinutes": 30,
"type": "observation",
"data": { "code": "CREATININE_MG_DL", "value": 1.1, "unit": "mg/dL", "source": "Lab" },
"note": "Partial SOFA — no platelets/bilirubin yet"
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "PLATELET_K_UL", "value": 165, "unit": "k/µL", "source": "Lab" }
},
{
"offsetMinutes": 120,
"type": "observation",
"data": { "code": "BILIRUBIN_MG_DL", "value": 0.8, "unit": "mg/dL", "source": "Lab" },
"note": "Baseline SOFA now more complete"
},
{
"offsetMinutes": 180,
"type": "observation",
"data": { "code": "SPO2", "value": 88, "unit": "%", "source": "Device" }
},
{
"offsetMinutes": 180,
"type": "observation",
"data": { "code": "FIO2_PCT", "value": 40, "unit": "%", "source": "Manual" },
"note": "S/F 220 → SOFA Resp ~1; usedSpO2Fallback true"
},
{
"offsetMinutes": 180,
"type": "observation",
"data": { "code": "RESP_RATE", "value": 26, "unit": "/min", "source": "Device" }
},
{
"offsetMinutes": 240,
"type": "observation",
"data": { "code": "SPO2", "value": 84, "unit": "%", "source": "Device" }
},
{
"offsetMinutes": 240,
"type": "observation",
"data": { "code": "FIO2_PCT", "value": 60, "unit": "%", "source": "Manual" },
"note": "S/F 140 → SOFA Resp 2"
},
{
"offsetMinutes": 240,
"type": "observation",
"data": { "code": "CREATININE_MG_DL", "value": 1.9, "unit": "mg/dL", "source": "Lab" }
},
{
"offsetMinutes": 240,
"type": "observation",
"data": { "code": "PLATELET_K_UL", "value": 110, "unit": "k/µL", "source": "Lab" },
"note": "SOFA Coag 1"
},
{
"offsetMinutes": 300,
"type": "observation",
"data": { "code": "SYSTOLIC_BP", "value": 88, "unit": "mmHg", "source": "Device" }
},
{
"offsetMinutes": 300,
"type": "observation",
"data": { "code": "DIASTOLIC_BP", "value": 52, "unit": "mmHg", "source": "Device" },
"note": "MAP 64 → SOFA CV 1"
},
{
"offsetMinutes": 300,
"type": "observation",
"data": { "code": "CREATININE_MG_DL", "value": 2.5, "unit": "mg/dL", "source": "Lab" },
"note": "SOFA Renal 2; delta may reach ≥ 2"
}
],
"expectedOutcomes": [
{
"afterOffsetMinutes": 180,
"type": "score",
"scoreType": "SOFA",
"description": "SOFA computed with usedSpO2Fallback in staleness flags"
},
{
"afterOffsetMinutes": 300,
"type": "alert",
"alertType": "SOFA_SEPSIS",
"description": "Multi-organ decline — delta ≥ 2 from baseline"
}
]
}
@@ -99,6 +99,36 @@
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_EYE",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_VERBAL",
"value": 5,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_MOTOR",
"value": 6,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 30,
"type": "observation",
@@ -2,7 +2,7 @@
"scenario": {
"id": "uti-sepsis-elderly-01",
"name": "UTI Progressing to Sepsis in Elderly Patient",
"description": "72-year-old female presenting with UTI symptoms who deteriorates into sepsis over 4 hours, triggering SIRS, qSOFA, and NEWS2 alerts with sepsis bundle compliance.",
"description": "72-year-old female with UTI progressing to sepsis over 4 hours. qSOFA screen fires first; SOFA delta \u2265 2 triggers sepsis bundle. NEWS2 and bundle compliance still exercised.",
"durationMinutes": 240,
"tags": [
"sepsis",
@@ -98,6 +98,36 @@
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_EYE",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_VERBAL",
"value": 4,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 0,
"type": "observation",
"data": {
"code": "GCS_MOTOR",
"value": 6,
"unit": "score",
"source": "Manual"
}
},
{
"offsetMinutes": 20,
"type": "observation",
@@ -281,6 +311,56 @@
"source": "Lab"
}
},
{
"offsetMinutes": 45,
"type": "observation",
"data": {
"code": "PAO2_MMHG",
"value": 92,
"unit": "mmHg",
"source": "Lab"
}
},
{
"offsetMinutes": 45,
"type": "observation",
"data": {
"code": "FIO2_PCT",
"value": 28,
"unit": "%",
"source": "Manual"
}
},
{
"offsetMinutes": 45,
"type": "observation",
"data": {
"code": "PLATELET_K_UL",
"value": 210,
"unit": "k/\u00b5L",
"source": "Lab"
}
},
{
"offsetMinutes": 45,
"type": "observation",
"data": {
"code": "BILIRUBIN_MG_DL",
"value": 0.7,
"unit": "mg/dL",
"source": "Lab"
}
},
{
"offsetMinutes": 45,
"type": "observation",
"data": {
"code": "CREATININE_MG_DL",
"value": 1.0,
"unit": "mg/dL",
"source": "Lab"
}
},
{
"offsetMinutes": 40,
"type": "order",
@@ -450,7 +530,7 @@
"unit": "bpm",
"source": "Device"
},
"note": "SIRS criteria met: Temp >38.3 + HR >90 \u2192 SEPSIS_WARNING"
"note": "Temp >38.3 + HR >90 \u2014 early sepsis physiology"
},
{
"offsetMinutes": 90,
@@ -627,7 +707,7 @@
"unit": "bpm",
"source": "Device"
},
"note": "NEWS2 = 6, SIRS 3 of 4 criteria met (Temp, HR, RR)"
"note": "NEWS2 = 6, continued deterioration"
},
{
"offsetMinutes": 120,
@@ -698,7 +778,7 @@
"unit": "\u00d710\u00b3/\u00b5L",
"source": "Lab"
},
"note": "Repeat labs \u2014 WBC elevated, lactate rising, SIRS 4/4"
"note": "Repeat labs \u2014 WBC elevated, lactate rising"
},
{
"offsetMinutes": 120,
@@ -730,6 +810,48 @@
"source": "Lab"
}
},
{
"offsetMinutes": 125,
"type": "observation",
"data": {
"code": "PAO2_MMHG",
"value": 68,
"unit": "mmHg",
"source": "Lab"
},
"note": "Worsening gas exchange \u2014 SOFA Resp \u2191"
},
{
"offsetMinutes": 125,
"type": "observation",
"data": {
"code": "FIO2_PCT",
"value": 40,
"unit": "%",
"source": "Manual"
}
},
{
"offsetMinutes": 125,
"type": "observation",
"data": {
"code": "PLATELET_K_UL",
"value": 95,
"unit": "k/\u00b5L",
"source": "Lab"
}
},
{
"offsetMinutes": 125,
"type": "observation",
"data": {
"code": "CREATININE_MG_DL",
"value": 2.1,
"unit": "mg/dL",
"source": "Lab"
},
"note": "AKI \u2014 SOFA Renal \u2191; delta \u2265 2 \u2192 SOFA_SEPSIS"
},
{
"offsetMinutes": 125,
"type": "medication",
@@ -1308,46 +1430,34 @@
"description": "HR enters warning range (91 bpm)"
},
{
"afterOffsetMinutes": 90,
"afterOffsetMinutes": 105,
"type": "alert",
"alertType": "SEPSIS_WARNING",
"description": "SIRS criteria met: Temp 38.4\u00b0C (>38.3) + HR 96 (>90) = 2 of 4"
"alertType": "QSOFA_SCREEN",
"description": "qSOFA \u2265 2 \u2014 screen positive, recommend SOFA labs"
},
{
"afterOffsetMinutes": 90,
"afterOffsetMinutes": 125,
"type": "alert",
"alertType": "SOFA_SEPSIS",
"description": "SOFA delta \u2265 2 from baseline after repeat labs"
},
{
"afterOffsetMinutes": 125,
"type": "bundle",
"description": "Sepsis bundle auto-created with 4 orders: blood cultures, serum lactate, broad-spectrum antibiotics, IV fluid resuscitation"
"description": "Sepsis bundle auto-created; triggeringAlertType = SOFA_SEPSIS"
},
{
"afterOffsetMinutes": 105,
"type": "score",
"scoreType": "NEWS2",
"expectedMinimum": 5,
"description": "NEWS2 reaches 5 (HR=1, RR=2, Temp=1, SpO2=1) \u2192 WARNING threshold"
"description": "NEWS2 reaches medium risk"
},
{
"afterOffsetMinutes": 135,
"type": "alert",
"alertType": "NEWS2_EMERGENCY",
"description": "NEWS2 reaches 8 (HR=2, RR=2, SBP=1, Temp=2, SpO2=1) \u22657 \u2192 EMERGENCY"
},
{
"afterOffsetMinutes": 135,
"type": "score",
"scoreType": "NEWS2",
"expectedMinimum": 7,
"description": "NEWS2 \u22657 sustained through peak deterioration"
},
{
"afterOffsetMinutes": 150,
"type": "alert",
"alertType": "QSOFA_WARNING",
"description": "qSOFA 3/3: RR 24 (\u226522) + SBP 98 (\u2264100) + AVPU 1 (\u22651)"
},
{
"afterOffsetMinutes": 130,
"type": "bundle",
"description": "Sepsis bundle COMPLIANT \u2014 all 4 orders completed within 40 minutes (blood cultures T+92, lactate T+95, ceftriaxone T+125, IV fluids T+130)"
"description": "NEWS2 \u2265 7 at peak deterioration"
}
]
}
@@ -4,7 +4,12 @@ public static class ScenarioValidator
{
"HEART_RATE", "RESP_RATE", "SYSTOLIC_BP", "DIASTOLIC_BP",
"TEMP_C", "SPO2", "AVPU", "SUPPLEMENTAL_O2",
"WBC_K_UL", "POTASSIUM_MEQ_L", "LACTATE_MMOL_L", "GLUCOSE_MG_DL"
"WBC_K_UL", "POTASSIUM_MEQ_L", "LACTATE_MMOL_L", "GLUCOSE_MG_DL",
// Phase 25 — GCS components
"GCS_EYE", "GCS_VERBAL", "GCS_MOTOR",
// Phase 26 — SOFA lab / respiratory inputs
"PAO2_MMHG", "FIO2_PCT", "PLATELET_K_UL",
"BILIRUBIN_MG_DL", "CREATININE_MG_DL", "URINE_OUTPUT_ML_H",
};
private static readonly HashSet<string> ValidSources = new()
@@ -67,15 +72,15 @@ public static class ScenarioValidator
}
var placedOrders = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
double lastOffset = -1;
for (int i = 0; i < scenario.Events.Count; i++)
var indexedEvents = scenario.Events
.Select((evt, i) => (evt, i))
.OrderBy(x => x.evt.OffsetMinutes)
.ThenBy(x => x.i);
foreach (var (evt, i) in indexedEvents)
{
var evt = scenario.Events[i];
if (evt.OffsetMinutes < 0)
errors.Add($"events[{i}]: offsetMinutes cannot be negative");
if (evt.OffsetMinutes < lastOffset)
errors.Add($"events[{i}]: offsetMinutes goes backwards ({evt.OffsetMinutes} < {lastOffset})");
lastOffset = evt.OffsetMinutes;
if (!ValidEventTypes.Contains(evt.Type))
errors.Add($"events[{i}]: unknown type '{evt.Type}'");
@@ -123,7 +128,7 @@ public static class ScenarioValidator
{
errors.Add(
$"events[{i}]: order_result '{orderDescription}' has no matching prior 'order' event " +
"(sepsis bundle orders are auto-created when SEPSIS_WARNING or QSOFA_WARNING fires)");
"(sepsis bundle orders are auto-created when SOFA_SEPSIS or QSOFA_SCREEN fires)");
}
}
}
+14
View File
@@ -63,6 +63,20 @@
"description": { "type": "string" }
}
}
},
"observationCodes": {
"type": "array",
"description": "Valid observation codes",
"items": {
"enum": [
"HEART_RATE", "RESP_RATE", "SYSTOLIC_BP", "DIASTOLIC_BP",
"TEMP_C", "SPO2", "AVPU", "SUPPLEMENTAL_O2",
"WBC_K_UL", "POTASSIUM_MEQ_L", "LACTATE_MMOL_L", "GLUCOSE_MG_DL",
"GCS_EYE", "GCS_VERBAL", "GCS_MOTOR",
"PAO2_MMHG", "FIO2_PCT", "PLATELET_K_UL",
"BILIRUBIN_MG_DL", "CREATININE_MG_DL", "URINE_OUTPUT_ML_H"
]
}
}
}
}