feature: Console Replay Simulator
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
public record AlertResponse(
|
||||
Guid Id, string AlertType, string Severity, string Status,
|
||||
string Details, DateTimeOffset TriggeredAt);
|
||||
@@ -0,0 +1,2 @@
|
||||
public record ApiResponse<T>(bool Success, T Data, string? Error);
|
||||
public record PagedResponse<T>(List<T> Items, int TotalCount, int Page, int PageSize);
|
||||
@@ -0,0 +1 @@
|
||||
public record BatchIngestRequest(List<IngestObservationRequest> Observations);
|
||||
@@ -0,0 +1,3 @@
|
||||
public record CreateMedicationAdministrationRequest(
|
||||
string DrugName, decimal Dose, string DoseUnit, string Route,
|
||||
DateTimeOffset? AdministeredAt, string AdministeredBy);
|
||||
@@ -0,0 +1 @@
|
||||
public record CreateOrderRequest(string OrderType, string Description, string OrderedBy);
|
||||
@@ -0,0 +1,4 @@
|
||||
public record EncounterResponse(
|
||||
Guid Id, Guid PatientId, string EncounterType, string Status,
|
||||
string Department, string AttendingPhysician, string? RoomBed,
|
||||
string? AdmissionReason, DateTimeOffset AdmittedAt);
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
public record IngestObservationRequest(
|
||||
string ObservationCode, decimal Value, string Unit,
|
||||
string Source, DateTimeOffset RecordedAt, string? IdempotencyKey);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
public record News2Response(
|
||||
Guid Id, int TotalScore, string RiskLevel, bool HasSingleParamThree,
|
||||
int RespRateScore, int Spo2Score, int SystolicBpScore,
|
||||
int HeartRateScore, int ConsciousnessScore, int TemperatureScore,
|
||||
int SupplementalO2Score, DateTimeOffset CalculatedAt);
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
public record OpenEncounterRequest(
|
||||
string EncounterType, string Department, string AttendingPhysician,
|
||||
string? RoomBed = null, string? AdmissionReason = null);
|
||||
@@ -0,0 +1 @@
|
||||
public record OrderResponse(Guid Id, string Description, string Status);
|
||||
@@ -0,0 +1,3 @@
|
||||
public record PatientResponse(
|
||||
Guid Id, string Mrn, string FirstName, string LastName,
|
||||
DateOnly DateOfBirth, string Gender, string Status, DateTimeOffset CreatedAt);
|
||||
@@ -0,0 +1 @@
|
||||
public record RecordOrderResultRequest(string? ResultSummary);
|
||||
@@ -0,0 +1,2 @@
|
||||
public record RegisterPatientRequest(
|
||||
string FirstName, string LastName, DateOnly DateOfBirth, string Gender);
|
||||
@@ -0,0 +1,11 @@
|
||||
public record SepsisBundleElementResponse(string Status);
|
||||
|
||||
public record SepsisBundleResponse(
|
||||
Guid Id, string TriggeringAlertType, string ComplianceStatus,
|
||||
DateTimeOffset RecognizedAt, DateTimeOffset DeadlineAt,
|
||||
DateTimeOffset? CompletedAt,
|
||||
List<SepsisBundleElementResponse>? Elements = null)
|
||||
{
|
||||
public int ElementsCompleted =>
|
||||
Elements?.Count(e => e.Status == "Completed") ?? 0;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using System.Net.Http.Json;
|
||||
|
||||
public class VigilCareApiClient
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public VigilCareApiClient(HttpClient http)
|
||||
{
|
||||
_http = http;
|
||||
}
|
||||
|
||||
public async Task<PatientResponse> RegisterPatientAsync(RegisterPatientRequest req)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync("/api/v1/patients", req);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<PatientResponse>>();
|
||||
return envelope!.Data;
|
||||
}
|
||||
|
||||
public async Task<EncounterResponse> OpenEncounterAsync(Guid patientId, OpenEncounterRequest req)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync($"/api/v1/patients/{patientId}/encounters", req);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<EncounterResponse>>();
|
||||
return envelope!.Data;
|
||||
}
|
||||
|
||||
public async Task SendObservationBatchAsync(
|
||||
Guid encounterId, List<IngestObservationRequest> observations)
|
||||
{
|
||||
foreach (var chunk in observations.Chunk(10))
|
||||
{
|
||||
var batch = new BatchIngestRequest(chunk.ToList());
|
||||
var response = await _http.PostAsJsonAsync(
|
||||
$"/api/v1/encounters/{encounterId}/observations", batch);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
throw new HttpRequestException(
|
||||
$"Observation batch failed ({(int)response.StatusCode} {response.StatusCode}): {body}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TrySendMedicationAsync(
|
||||
Guid encounterId, CreateMedicationAdministrationRequest req)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync(
|
||||
$"/api/v1/encounters/{encounterId}/medications", req);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
return false;
|
||||
response.EnsureSuccessStatusCode();
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> TryCreateOrderAsync(
|
||||
Guid encounterId, CreateOrderRequest req)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync(
|
||||
$"/api/v1/encounters/{encounterId}/orders", req);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<bool> TryResultOrderAsync(
|
||||
Guid encounterId, string orderDescription, string? resultSummary)
|
||||
{
|
||||
var ordersResponse = await _http.GetAsync(
|
||||
$"/api/v1/encounters/{encounterId}/orders?status=Pending");
|
||||
if (!ordersResponse.IsSuccessStatusCode)
|
||||
return false;
|
||||
|
||||
var envelope = await ordersResponse.Content
|
||||
.ReadFromJsonAsync<ApiResponse<PagedResponse<OrderResponse>>>();
|
||||
var order = FindPendingOrder(envelope?.Data.Items ?? [], orderDescription);
|
||||
if (order is null)
|
||||
return false;
|
||||
|
||||
var resultResponse = await _http.PatchAsJsonAsync(
|
||||
$"/api/v1/orders/{order.Id}/result",
|
||||
new RecordOrderResultRequest(resultSummary));
|
||||
return resultResponse.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
private static OrderResponse? FindPendingOrder(
|
||||
List<OrderResponse> items, string description)
|
||||
{
|
||||
return items.FirstOrDefault(o => o.Description == description)
|
||||
?? items.FirstOrDefault(o =>
|
||||
description.StartsWith(o.Description, StringComparison.OrdinalIgnoreCase))
|
||||
?? items.FirstOrDefault(o =>
|
||||
o.Description.StartsWith(description, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
// --- Polling endpoints ---
|
||||
|
||||
public async Task<List<AlertResponse>> GetAlertsAsync(Guid encounterId)
|
||||
{
|
||||
var response = await _http.GetAsync($"/api/v1/encounters/{encounterId}/alerts");
|
||||
if (!response.IsSuccessStatusCode) return new();
|
||||
var envelope = await response.Content
|
||||
.ReadFromJsonAsync<ApiResponse<PagedResponse<AlertResponse>>>();
|
||||
return envelope?.Data.Items.ToList() ?? new();
|
||||
}
|
||||
|
||||
public async Task<News2Response?> GetCurrentNews2Async(Guid encounterId)
|
||||
{
|
||||
var response = await _http.GetAsync($"/api/v1/encounters/{encounterId}/news2/current");
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<News2Response>>();
|
||||
return envelope?.Data;
|
||||
}
|
||||
|
||||
public async Task<SepsisBundleResponse?> GetSepsisBundleAsync(Guid encounterId)
|
||||
{
|
||||
var response = await _http.GetAsync(
|
||||
$"/api/v1/encounters/{encounterId}/sepsis-bundle/current");
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
var envelope = await response.Content
|
||||
.ReadFromJsonAsync<ApiResponse<SepsisBundleResponse>>();
|
||||
return envelope?.Data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user