Run initial test for Climate Resilience Verification Suite

Add first part of Alert Quality Analytics
This commit is contained in:
voltsrage
2026-06-24 03:01:00 +08:00
parent 032cd1d240
commit 185dc93fa1
50 changed files with 4116 additions and 55 deletions
@@ -27,6 +27,11 @@ public class VigilCareApiClient
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
public void SetBearerToken(string token)
{
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
public async Task<PatientResponse> RegisterPatientAsync(RegisterPatientRequest req)
{
var response = await _http.PostAsJsonAsync("/api/v1/patients", req);
@@ -44,8 +49,34 @@ public class VigilCareApiClient
}
public async Task SendObservationBatchAsync(
Guid encounterId, List<IngestObservationRequest> observations)
Guid encounterId, List<IngestObservationRequest> observations,
ReplayTarget target = ReplayTarget.Central)
{
if (target == ReplayTarget.Gateway)
{
foreach (var obs in observations)
{
var response = await _http.PostAsJsonAsync(
$"/api/v1/encounters/{encounterId}/observations",
new
{
observationCode = obs.ObservationCode,
value = obs.Value,
unit = obs.Unit,
source = obs.Source,
recordedAt = obs.RecordedAt,
idempotencyKey = obs.IdempotencyKey ?? Guid.NewGuid().ToString()
});
if (!response.IsSuccessStatusCode)
{
var body = await response.Content.ReadAsStringAsync();
throw new HttpRequestException(
$"Observation ingest failed ({(int)response.StatusCode} {response.StatusCode}): {body}");
}
}
return;
}
foreach (var chunk in observations.Chunk(10))
{
var batch = new BatchIngestRequest(chunk.ToList());
@@ -161,4 +192,50 @@ public class VigilCareApiClient
var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<QsofaResponse>>();
return envelope?.Data;
}
public async Task<bool> TryAcknowledgeAlertAsync(
Guid encounterId, string alertType, string clinicianId, string? note,
TimeSpan? waitForAlert = null, CancellationToken ct = default)
{
const int pollIntervalMs = 500;
var deadline = waitForAlert.HasValue
? DateTimeOffset.UtcNow.Add(waitForAlert.Value)
: (DateTimeOffset?)null;
while (true)
{
ct.ThrowIfCancellationRequested();
var alerts = await GetAlertsAsync(encounterId);
var alert = alerts.FirstOrDefault(a =>
IsMatchingAlertType(a.AlertType, alertType)
&& IsOpenAlertStatus(a.Status));
if (alert is not null)
{
var response = await _http.PostAsJsonAsync(
$"/api/v1/alerts/{alert.Id}/acknowledge",
new { note, clinicianId }, ct);
return response.IsSuccessStatusCode;
}
if (!deadline.HasValue || DateTimeOffset.UtcNow >= deadline.Value)
return false;
await Task.Delay(pollIntervalMs, ct);
}
}
private static bool IsOpenAlertStatus(string status) =>
string.Equals(status, "OPEN", StringComparison.OrdinalIgnoreCase);
private static bool IsMatchingAlertType(string actual, string expected)
{
if (string.Equals(actual, expected, StringComparison.OrdinalIgnoreCase))
return true;
static string Normalize(string s) =>
s.Replace("_", "", StringComparison.Ordinal).ToUpperInvariant();
return Normalize(actual) == Normalize(expected);
}
}