Full SOFA Score: Data Layer + Scoring Engine

Glasgow Coma Scale: Data Layer + Scoring Engine
This commit is contained in:
voltsrage
2026-06-21 01:09:50 +08:00
parent 78c043e4d3
commit 93ea473d2b
62 changed files with 7133 additions and 72 deletions
@@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
public class GcsService : IGcsService
{
private readonly AppDbContext _db;
public GcsService(AppDbContext db) => _db = db;
public async Task<GcsScore?> GetCurrentAsync(Guid encounterId)
{
return await _db.GcsScores
.AsNoTracking()
.Where(s => s.EncounterId == encounterId)
.OrderByDescending(s => s.CalculatedAt)
.FirstOrDefaultAsync();
}
}
@@ -0,0 +1,4 @@
public interface IGcsService
{
Task<GcsScore?> GetCurrentAsync(Guid encounterId);
}
@@ -0,0 +1,7 @@
public interface ISofaService
{
Task<SofaScore?> GetCurrentAsync(Guid encounterId);
Task<SofaScore?> GetBaselineAsync(Guid encounterId);
Task<CursorPage<SofaScore>> GetHistoryAsync(
Guid encounterId, int limit, string? cursorToken);
}
@@ -0,0 +1,6 @@
public static class MapCalculator
{
// Returns MAP in mmHg from systolic and diastolic BP.
public static decimal Calculate(decimal systolicBp, decimal diastolicBp) =>
Math.Round(diastolicBp + (systolicBp - diastolicBp) / 3m, 1);
}
@@ -17,6 +17,15 @@ public static class PlausibilityValidator
["LACTATE_MMOL_L"] = (0.1m, 30),
["AVPU"] = (0, 3),
["SUPPLEMENTAL_O2"] = (0, 1),
["GCS_EYE"] = (1, 4),
["GCS_VERBAL"] = (1, 5),
["GCS_MOTOR"] = (1, 6),
["PAO2_MMHG"] = (20, 600),
["FIO2_PCT"] = (21, 100),
["PLATELET_K_UL"] = (1, 1500),
["BILIRUBIN_MG_DL"] = (0.1m, 50),
["CREATININE_MG_DL"] = (0.1m, 20),
["URINE_OUTPUT_ML_H"] = (0, 500),
};
public static bool IsPlausible(string observationCode, decimal value, out string? reason)
@@ -0,0 +1,45 @@
using Microsoft.EntityFrameworkCore;
public class SofaService : ISofaService
{
private readonly AppDbContext _db;
public SofaService(AppDbContext db) => _db = db;
public async Task<SofaScore?> GetCurrentAsync(Guid encounterId)
{
return await _db.SofaScores
.AsNoTracking()
.Where(s => s.EncounterId == encounterId)
.OrderByDescending(s => s.CalculatedAt)
.FirstOrDefaultAsync();
}
public async Task<SofaScore?> GetBaselineAsync(Guid encounterId)
{
return await _db.SofaScores
.AsNoTracking()
.FirstOrDefaultAsync(s => s.EncounterId == encounterId && s.IsBaseline);
}
public async Task<CursorPage<SofaScore>> GetHistoryAsync(
Guid encounterId, int limit, string? cursorToken)
{
limit = Math.Clamp(limit, 1, 100);
var query = _db.SofaScores
.AsNoTracking()
.Where(s => s.EncounterId == encounterId);
var items = await query
.OrderByDescending(s => s.CalculatedAt)
.ThenByDescending(s => s.Id)
.Take(limit + 1)
.ToListAsync();
var hasMore = items.Count > limit;
if (hasMore) items.RemoveAt(limit);
return new CursorPage<SofaScore>(items, null, hasMore);
}
}