Full SOFA Score: Data Layer + Scoring Engine Glasgow Coma Scale: Data Layer + Scoring Engine
90 lines
3.1 KiB
C#
90 lines
3.1 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Options;
|
|
using StackExchange.Redis;
|
|
using System.Text.Json;
|
|
|
|
public class SofaVasopressorResolver
|
|
{
|
|
private static readonly HashSet<string> VasopressorDrugs = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"DOPAMINE", "DOBUTAMINE", "EPINEPHRINE", "NOREPINEPHRINE",
|
|
"VASOPRESSIN", "PHENYLEPHRINE"
|
|
};
|
|
|
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
PropertyNameCaseInsensitive = true
|
|
};
|
|
|
|
private readonly AppDbContext _db;
|
|
private readonly IConnectionMultiplexer _redis;
|
|
private readonly SofaOptions _options;
|
|
|
|
public SofaVasopressorResolver(
|
|
AppDbContext db,
|
|
IConnectionMultiplexer redis,
|
|
IOptions<SofaOptions> options)
|
|
{
|
|
_db = db;
|
|
_redis = redis;
|
|
_options = options.Value;
|
|
}
|
|
|
|
public static string VasopressorCacheKey(Guid encounterId) =>
|
|
$"sofa:{encounterId}:vasopressor";
|
|
|
|
public async Task CacheFromAdministrationAsync(MedicationAdministration med, CancellationToken ct)
|
|
{
|
|
if (!VasopressorDrugs.Contains(med.DrugName)) return;
|
|
|
|
var info = new VasopressorInfo(med.DrugName, NormalizeDose(med.DrugName, med.Dose, med.DoseUnit));
|
|
var json = JsonSerializer.Serialize(new
|
|
{
|
|
info.DrugName,
|
|
info.DoseUgKgMin,
|
|
med.AdministeredAt
|
|
}, JsonOptions);
|
|
|
|
await _redis.GetDatabase().StringSetAsync(
|
|
VasopressorCacheKey(med.EncounterId),
|
|
json,
|
|
TimeSpan.FromHours(_options.VasopressorWindowHours));
|
|
}
|
|
|
|
public async Task<VasopressorInfo?> GetActiveVasopressorAsync(
|
|
Guid encounterId, CancellationToken ct)
|
|
{
|
|
var cached = await _redis.GetDatabase()
|
|
.StringGetAsync(VasopressorCacheKey(encounterId));
|
|
if (cached.HasValue)
|
|
{
|
|
using var doc = JsonDocument.Parse((string)cached!);
|
|
var root = doc.RootElement;
|
|
return new VasopressorInfo(
|
|
root.GetProperty("drugName").GetString()!,
|
|
root.GetProperty("doseUgKgMin").GetDecimal());
|
|
}
|
|
|
|
var since = DateTimeOffset.UtcNow.AddHours(-_options.VasopressorWindowHours);
|
|
var med = await _db.MedicationAdministrations
|
|
.AsNoTracking()
|
|
.Where(m => m.EncounterId == encounterId
|
|
&& VasopressorDrugs.Contains(m.DrugName)
|
|
&& m.AdministeredAt >= since)
|
|
.OrderByDescending(m => m.AdministeredAt)
|
|
.FirstOrDefaultAsync(ct);
|
|
|
|
if (med is null) return null;
|
|
return new VasopressorInfo(med.DrugName, NormalizeDose(med.DrugName, med.Dose, med.DoseUnit));
|
|
}
|
|
|
|
private static decimal NormalizeDose(string drug, decimal dose, string unit) =>
|
|
unit.ToLowerInvariant() switch
|
|
{
|
|
"mcg/kg/min" or "µg/kg/min" => dose,
|
|
"mcg/min" or "µg/min" => dose / 70m,
|
|
"mg/hr" => dose * 1000m / 60m / 70m,
|
|
_ => dose
|
|
};
|
|
} |