Schedule graphical CCS procedures

This commit is contained in:
hz
2026-08-14 17:54:03 -05:00
parent 375b3ea62f
commit 25c2188264
7 changed files with 204 additions and 26 deletions
+54 -6
View File
@@ -411,6 +411,23 @@ public sealed class CcsStore
{
var definition = await DefinitionAsync(attempt);
var completed = attempt.State == CcsAttemptStates.Completed;
var optionCodes = definition.OrderResponses.Select(response => response.OrderCode).ToList();
List<CcsOrderDefinition> optionCatalog = definition.ExperienceMode == CcsExperienceModes.GraphicalInterview
? await _orders.Find(value => optionCodes.Contains(value.Code)).ToListAsync()
: [];
var orderOptions = definition.ExperienceMode == CcsExperienceModes.GraphicalInterview
? definition.OrderResponses.Select(response =>
{
var catalog = optionCatalog.FirstOrDefault(value => value.Code.Equals(response.OrderCode, StringComparison.OrdinalIgnoreCase));
return new CcsOrderOption
{
Code = response.OrderCode,
TurnaroundMinutes = response.TurnaroundMinutes ?? catalog?.DefaultTurnaroundMinutes ?? 0,
PerformanceLocation = PerformanceLocation(response, catalog, attempt.Location),
RequiresPatientVisit = response.RequiresPatientVisit
};
}).ToList()
: [];
List<CcsOrderDefinition> activeCatalog = attempt.ActiveOrderCodes.Count == 0
? []
: await _orders.Find(value => attempt.ActiveOrderCodes.Contains(value.Code)).ToListAsync();
@@ -423,11 +440,19 @@ public sealed class CcsStore
var reported = attempt.Chart.LastOrDefault(value =>
value.Code.Equals(order.Code, StringComparison.OrdinalIgnoreCase)
&& value.Category is not CcsChartCategories.Orders);
var response = definition.OrderResponses.FirstOrDefault(value =>
value.OrderCode.Equals(order.Code, StringComparison.OrdinalIgnoreCase));
var performanceLocation = pending?.PerformanceLocation ?? PerformanceLocation(response, order, attempt.Location);
var requiresPatientVisit = pending?.RequiresPatientVisit ?? response?.RequiresPatientVisit == true;
return new CcsActiveOrder
{
Code = order.Code, Name = order.CanonicalName, Category = order.Category,
Route = order.DisplayRoute, Frequency = order.DisplayFrequency, OrderedMinute = orderedMinute,
ReportMinute = pending is not null ? null : reported?.SimulatedMinute,
DueMinute = pending?.DueMinute, PerformanceLocation = performanceLocation,
RequiresPatientVisit = requiresPatientVisit,
Status = pending is not null ? (requiresPatientVisit ? "scheduled" : "pending")
: reported is not null ? "completed" : "active",
IsPending = pending is not null, HasResult = reported is not null
};
}).OrderBy(value => value.OrderedMinute).ThenBy(value => value.Name).ToList();
@@ -469,10 +494,11 @@ public sealed class CcsStore
? [] : definition.InitialChart.HistorySections,
MedicalHistory = MedicalHistory(definition),
Vitals = attempt.CurrentVitals, Chart = attempt.Chart, ActiveOrderCodes = attempt.ActiveOrderCodes,
ActiveOrders = activeOrders, VitalHistory = vitalHistory,
ActiveOrders = activeOrders, OrderOptions = orderOptions, VitalHistory = vitalHistory,
PendingResults = attempt.PendingResults.Select(value => new CcsPendingResult
{
OrderCode = value.OrderCode, DueMinute = value.DueMinute, Category = value.Category, Title = value.Title
OrderCode = value.OrderCode, DueMinute = value.DueMinute, Category = value.Category, Title = value.Title,
PerformanceLocation = value.PerformanceLocation, RequiresPatientVisit = value.RequiresPatientVisit
}).ToList(),
AvailableExams = attempt.State == CcsAttemptStates.Active ? definition.Exams : [],
AllowedLocations = attempt.State == CcsAttemptStates.Active ? definition.AllowedLocations : [],
@@ -525,23 +551,31 @@ public sealed class CcsStore
{
code = code.Trim().ToLowerInvariant();
var catalog = await _orders.Find(value => value.Code == code).FirstOrDefaultAsync() ?? throw new ArgumentException("Unknown order code.");
if (catalog.AvailableLocations.Count > 0 && !catalog.AvailableLocations.Contains(attempt.Location, StringComparer.OrdinalIgnoreCase))
var response = definition.OrderResponses.FirstOrDefault(value => value.OrderCode.Equals(code, StringComparison.OrdinalIgnoreCase));
if (definition.ExperienceMode == CcsExperienceModes.GraphicalInterview && response is null)
throw new InvalidOperationException("That order is not configured for this case.");
if (response is null && catalog.AvailableLocations.Count > 0 && !catalog.AvailableLocations.Contains(attempt.Location, StringComparer.OrdinalIgnoreCase))
throw new InvalidOperationException("That order is unavailable at the current location.");
attempt.Actions.Add(Action("order", code, attempt.SimulatedMinute, now));
if (!attempt.ActiveOrderCodes.Contains(code, StringComparer.OrdinalIgnoreCase)) attempt.ActiveOrderCodes.Add(code);
var response = definition.OrderResponses.FirstOrDefault(value => value.OrderCode.Equals(code, StringComparison.OrdinalIgnoreCase));
var delay = response?.TurnaroundMinutes ?? catalog.DefaultTurnaroundMinutes;
var performanceLocation = PerformanceLocation(response, catalog, attempt.Location);
var requiresPatientVisit = response?.RequiresPatientVisit == true;
var resultCategory = ResultCategory(response?.ResultSection, catalog.ResultSection, catalog.Category);
if (response is not null && (!string.IsNullOrWhiteSpace(response.Result) || response.VitalPatches.Count > 0))
attempt.PendingResults.Add(new CcsPendingResult
{
OrderCode = code, Category = resultCategory, Title = catalog.CanonicalName,
DueMinute = attempt.SimulatedMinute + delay, Result = response.Result, VitalPatches = response.VitalPatches
DueMinute = attempt.SimulatedMinute + delay, Result = response.Result, VitalPatches = response.VitalPatches,
PerformanceLocation = performanceLocation, RequiresPatientVisit = requiresPatientVisit
});
attempt.Chart.Add(new CcsChartEntry
{
Category = CcsChartCategories.Orders, Code = code, Title = catalog.CanonicalName,
SimulatedMinute = attempt.SimulatedMinute, Text = $"Ordered: {catalog.CanonicalName}"
SimulatedMinute = attempt.SimulatedMinute,
Text = requiresPatientVisit
? $"Scheduled: {catalog.CanonicalName} at {Humanize(performanceLocation)}; expected in {delay} minutes."
: $"Ordered: {catalog.CanonicalName}"
});
if (delay == 0 && processImmediately) ProcessDue(attempt);
}
@@ -606,6 +640,13 @@ public sealed class CcsStore
var vitalsChanged = false;
foreach (var result in due)
{
if (result.RequiresPatientVisit)
attempt.Chart.Add(new CcsChartEntry
{
Category = CcsChartCategories.Progress, Code = $"{result.OrderCode}_completed", Title = "Patient returned",
SimulatedMinute = attempt.SimulatedMinute,
Text = $"The patient completed {result.Title} at {Humanize(result.PerformanceLocation)} and returned."
});
if (!string.IsNullOrWhiteSpace(result.Result))
attempt.Chart.Add(new CcsChartEntry
{
@@ -619,6 +660,13 @@ public sealed class CcsStore
if (vitalsChanged) SnapshotVitals(attempt);
}
private static string PerformanceLocation(CcsCaseOrderDefinition? response, CcsOrderDefinition? catalog, string currentLocation)
{
if (!string.IsNullOrWhiteSpace(response?.PerformanceLocation)) return response.PerformanceLocation;
if (catalog?.AvailableLocations.Contains(currentLocation, StringComparer.OrdinalIgnoreCase) == true) return currentLocation;
return catalog?.AvailableLocations.FirstOrDefault() ?? currentLocation;
}
private static bool ProcessEvents(CcsAttempt attempt, CcsCaseRevision definition)
{
var activeOrders = attempt.Actions.Where(value => value.Type == "order").Select(value => value.Code).ToHashSet(StringComparer.OrdinalIgnoreCase);