feature: Sepsis Engine Refactor: Remove SIRS, Rewire qSOFA + Bundle
Frontend: GCS Entry, SOFA Display, Sepsis UI Refactor
This commit is contained in:
@@ -43,28 +43,47 @@ public class SepsisBundleTests : IAsyncLifetime
|
||||
var cache = redis.GetDatabase();
|
||||
foreach (var key in QsofaCalculator.AllCriterionKeys(_encounterId))
|
||||
await cache.KeyDeleteAsync(key);
|
||||
foreach (var key in SirsEvaluator.AllCriterionKeys(_encounterId))
|
||||
await cache.KeyDeleteAsync(key);
|
||||
}
|
||||
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Fact]
|
||||
public async Task QsofaAlert_CreatesBundleWithFourOrders()
|
||||
private async Task CreateSofaSepsisBundleAsync(IServiceScope scope)
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var detector = scope.ServiceProvider.GetRequiredService<QsofaDetector>();
|
||||
var handler = scope.ServiceProvider.GetRequiredService<SepsisAlertHandler>();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "RESP_RATE", 24m);
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "SYSTOLIC_BP", 95m);
|
||||
var alertId = Guid.NewGuid();
|
||||
db.ClinicalAlerts.Add(new ClinicalAlert
|
||||
{
|
||||
Id = alertId,
|
||||
EncounterId = _encounterId,
|
||||
PatientId = _patientId,
|
||||
AlertType = AlertType.SofaSepsis,
|
||||
Severity = AlertSeverity.Critical,
|
||||
Details = "SOFA delta +2",
|
||||
Status = AlertStatus.Open,
|
||||
TriggeredAt = DateTimeOffset.UtcNow
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await handler.OnSepsisAlertCreatedAsync(
|
||||
_encounterId, alertId, AlertType.SofaSepsis, CancellationToken.None);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SofaSepsisAlert_CreatesBundleWithFourOrders()
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
await CreateSofaSepsisBundleAsync(scope);
|
||||
|
||||
var bundle = await db.SepsisBundles
|
||||
.Include(b => b.Elements)
|
||||
.SingleAsync(b => b.EncounterId == _encounterId);
|
||||
|
||||
bundle.ComplianceStatus.Should().Be(SepsisBundleComplianceStatus.InProgress);
|
||||
bundle.TriggeringAlertType.Should().Be("QSOFA_WARNING");
|
||||
bundle.TriggeringAlertType.Should().Be("SOFA_SEPSIS");
|
||||
bundle.DeadlineAt.Should().BeCloseTo(bundle.RecognizedAt.AddHours(1), TimeSpan.FromSeconds(5));
|
||||
bundle.Elements.Should().HaveCount(4);
|
||||
|
||||
@@ -86,40 +105,45 @@ public class SepsisBundleTests : IAsyncLifetime
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SirsAlert_CreatesBundle()
|
||||
public async Task QsofaScreen_DoesNotTriggerBundle()
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var detector = scope.ServiceProvider.GetRequiredService<SirsDetector>();
|
||||
var detector = scope.ServiceProvider.GetRequiredService<QsofaDetector>();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "HEART_RATE", 95m);
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "TEMP_C", 38.5m);
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "RESP_RATE", 24m);
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "SYSTOLIC_BP", 95m);
|
||||
|
||||
var bundle = await db.SepsisBundles
|
||||
.Include(b => b.Elements)
|
||||
.SingleAsync(b => b.EncounterId == _encounterId);
|
||||
|
||||
bundle.TriggeringAlertType.Should().Be("SEPSIS_WARNING");
|
||||
bundle.ComplianceStatus.Should().Be(SepsisBundleComplianceStatus.InProgress);
|
||||
bundle.Elements.Should().HaveCount(4);
|
||||
(await db.SepsisBundles.CountAsync()).Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SecondAlert_IdempotentBundle()
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var qsofaDetector = scope.ServiceProvider.GetRequiredService<QsofaDetector>();
|
||||
var sirsDetector = scope.ServiceProvider.GetRequiredService<SirsDetector>();
|
||||
var handler = scope.ServiceProvider.GetRequiredService<SepsisAlertHandler>();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
await qsofaDetector.ProcessObservationAsync(_encounterId, _patientId, "RESP_RATE", 24m);
|
||||
await qsofaDetector.ProcessObservationAsync(_encounterId, _patientId, "SYSTOLIC_BP", 95m);
|
||||
await CreateSofaSepsisBundleAsync(scope);
|
||||
|
||||
var bundleCountAfterFirst = await db.SepsisBundles.CountAsync(b => b.EncounterId == _encounterId);
|
||||
bundleCountAfterFirst.Should().Be(1);
|
||||
|
||||
await sirsDetector.ProcessObservationAsync(_encounterId, _patientId, "HEART_RATE", 95m);
|
||||
await sirsDetector.ProcessObservationAsync(_encounterId, _patientId, "TEMP_C", 38.5m);
|
||||
var secondAlertId = Guid.NewGuid();
|
||||
db.ClinicalAlerts.Add(new ClinicalAlert
|
||||
{
|
||||
Id = secondAlertId,
|
||||
EncounterId = _encounterId,
|
||||
PatientId = _patientId,
|
||||
AlertType = AlertType.SofaSepsis,
|
||||
Severity = AlertSeverity.Critical,
|
||||
Details = "SOFA delta +3",
|
||||
Status = AlertStatus.Open,
|
||||
TriggeredAt = DateTimeOffset.UtcNow
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
await handler.OnSepsisAlertCreatedAsync(
|
||||
_encounterId, secondAlertId, AlertType.SofaSepsis, CancellationToken.None);
|
||||
|
||||
var bundleCount = await db.SepsisBundles.CountAsync(b => b.EncounterId == _encounterId);
|
||||
bundleCount.Should().Be(1, "a second bundle must not be created while one is IN_PROGRESS");
|
||||
@@ -132,12 +156,10 @@ public class SepsisBundleTests : IAsyncLifetime
|
||||
public async Task OrderResulted_CompletesElement()
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var detector = scope.ServiceProvider.GetRequiredService<QsofaDetector>();
|
||||
var bundleService = scope.ServiceProvider.GetRequiredService<ISepsisBundleService>();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "RESP_RATE", 24m);
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "SYSTOLIC_BP", 95m);
|
||||
await CreateSofaSepsisBundleAsync(scope);
|
||||
|
||||
var lactateElement = await db.SepsisBundleElements
|
||||
.Include(e => e.Order)
|
||||
@@ -161,12 +183,10 @@ public class SepsisBundleTests : IAsyncLifetime
|
||||
public async Task AllElementsResulted_BundleCompliant()
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var detector = scope.ServiceProvider.GetRequiredService<QsofaDetector>();
|
||||
var bundleService = scope.ServiceProvider.GetRequiredService<ISepsisBundleService>();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "RESP_RATE", 24m);
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "SYSTOLIC_BP", 95m);
|
||||
await CreateSofaSepsisBundleAsync(scope);
|
||||
|
||||
var elements = await db.SepsisBundleElements.ToListAsync();
|
||||
foreach (var element in elements)
|
||||
@@ -188,12 +208,10 @@ public class SepsisBundleTests : IAsyncLifetime
|
||||
public async Task DeadlinePassed_CompletionMarksNonCompliant()
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var detector = scope.ServiceProvider.GetRequiredService<QsofaDetector>();
|
||||
var bundleService = scope.ServiceProvider.GetRequiredService<ISepsisBundleService>();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "RESP_RATE", 24m);
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "SYSTOLIC_BP", 95m);
|
||||
await CreateSofaSepsisBundleAsync(scope);
|
||||
|
||||
var bundle = await db.SepsisBundles.SingleAsync();
|
||||
bundle.DeadlineAt = DateTimeOffset.UtcNow.AddHours(-1);
|
||||
@@ -212,11 +230,9 @@ public class SepsisBundleTests : IAsyncLifetime
|
||||
public async Task DeadlinePassed_MonitorMarksNonCompliant()
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var detector = scope.ServiceProvider.GetRequiredService<QsofaDetector>();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "RESP_RATE", 24m);
|
||||
await detector.ProcessObservationAsync(_encounterId, _patientId, "SYSTOLIC_BP", 95m);
|
||||
await CreateSofaSepsisBundleAsync(scope);
|
||||
|
||||
var bundle = await db.SepsisBundles.SingleAsync();
|
||||
bundle.DeadlineAt = DateTimeOffset.UtcNow.AddHours(-1);
|
||||
|
||||
Reference in New Issue
Block a user