using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using StackExchange.Redis; using System.Text.Json; public class SofaVasopressorResolver { private static readonly HashSet 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 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 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 }; }