feature: Self-Service Clinical Testing Sessions
CI / backend (push) Successful in 8m52s
CI / frontend (push) Failing after 1m39s

This commit is contained in:
voltsrage
2026-08-06 04:03:04 +08:00
parent 1d28880920
commit 80b009fd23
41 changed files with 4922 additions and 123 deletions
@@ -63,6 +63,51 @@ public class AlertQualityMetricsService : IAlertQualityMetricsService
: weightedResolveSeconds / snapshots.Sum(s => s.ResolvedCount));
}
public async Task<IReadOnlyList<AlertFeedbackQualityRow>> ListFeedbackAsync(
DateTimeOffset periodStart, DateTimeOffset periodEnd, string? scenarioId)
{
// Left-join ClinicalAlert → SimulationRun on EncounterId for attribution.
var query =
from f in _db.AlertFeedbacks.AsNoTracking()
join a in _db.ClinicalAlerts.AsNoTracking() on f.AlertId equals a.Id
join r in _db.SimulationRuns.AsNoTracking()
on a.EncounterId equals r.EncounterId into runs
from r in runs.DefaultIfEmpty()
where f.CreatedAt >= periodStart && f.CreatedAt <= periodEnd
select new { Feedback = f, Alert = a, Run = r };
if (!string.IsNullOrWhiteSpace(scenarioId))
{
var needle = scenarioId.ToLowerInvariant();
query = query.Where(x =>
x.Run != null
&& x.Run.ScenarioId != null
&& x.Run.ScenarioId.ToLower() == needle);
}
var rows = await query
.OrderByDescending(x => x.Feedback.CreatedAt)
.ToListAsync();
// Distinct by feedback id in case multiple SimulationRun rows share an EncounterId.
return rows
.GroupBy(x => x.Feedback.Id)
.Select(g =>
{
var x = g.First();
return new AlertFeedbackQualityRow(
x.Feedback.Id,
x.Alert.Id,
x.Alert.AlertType.ToDbString(),
x.Feedback.FeedbackType.ToDbString(),
x.Feedback.Comment,
x.Feedback.CreatedAt,
x.Run?.ScenarioId,
x.Run?.SessionId);
})
.ToList();
}
private static AlertQualityMetricResponse Map(AlertQualityMetric m) =>
new(
m.Id,
@@ -84,4 +129,4 @@ public class AlertQualityMetricsService : IAlertQualityMetricsService
m.AvgSecondsToAcknowledge,
m.AvgSecondsToResolution,
m.ComputedAt);
}
}