feature: Trend Detection & Alert Suppression Windows
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
using StackExchange.Redis;
|
||||
|
||||
public static class TrendCalculator
|
||||
{
|
||||
// NEWS2-relevant codes that support trend detection.
|
||||
public static readonly IReadOnlyList<string> TrendCodes = new[]
|
||||
{
|
||||
"HEART_RATE", "RESP_RATE", "SYSTOLIC_BP", "TEMP_C", "SPO2"
|
||||
};
|
||||
|
||||
public static bool IsTrendCode(string observationCode) =>
|
||||
TrendCodes.Contains(observationCode);
|
||||
|
||||
public static string HistoryKey(Guid encounterId, string code) =>
|
||||
$"trend:{encounterId}:{code}";
|
||||
|
||||
public static RedisKey[] AllHistoryKeys(Guid encounterId) =>
|
||||
TrendCodes.Select(c => (RedisKey)HistoryKey(encounterId, c)).ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Computes rate of change (units per minute) between the oldest and newest
|
||||
/// entries within the window. Returns null if fewer than 2 entries or window exceeded.
|
||||
/// </summary>
|
||||
public static decimal? ComputeRatePerMinute(
|
||||
IReadOnlyList<TrendHistoryEntry> entries,
|
||||
int windowMinutes)
|
||||
{
|
||||
if (entries.Count < 2) return null;
|
||||
|
||||
var newest = entries[^1];
|
||||
var oldest = entries[0];
|
||||
|
||||
var deltaMinutes = (newest.RecordedAt - oldest.RecordedAt).TotalMinutes;
|
||||
if (deltaMinutes <= 0 || deltaMinutes > windowMinutes) return null;
|
||||
|
||||
return (newest.Value - oldest.Value) / (decimal)deltaMinutes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the rate exceeds the configured threshold.
|
||||
/// For SPO2 and SYSTOLIC_BP, checks negative rate (decline) as clinically significant.
|
||||
/// </summary>
|
||||
public static bool ExceedsThreshold(
|
||||
string observationCode,
|
||||
decimal ratePerMinute,
|
||||
decimal thresholdPerMinute) =>
|
||||
observationCode switch
|
||||
{
|
||||
"SPO2" or "SYSTOLIC_BP" => ratePerMinute <= -thresholdPerMinute,
|
||||
_ => ratePerMinute >= thresholdPerMinute
|
||||
};
|
||||
|
||||
public static string DescribeTrend(
|
||||
string observationCode, decimal ratePerMinute, decimal currentValue) =>
|
||||
observationCode switch
|
||||
{
|
||||
"SPO2" or "SYSTOLIC_BP" =>
|
||||
$"Rapid decline: {observationCode} falling at {Math.Abs(ratePerMinute):F2}/min (current {currentValue})",
|
||||
_ =>
|
||||
$"Rapid rise: {observationCode} rising at {ratePerMinute:F2}/min (current {currentValue})"
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user