Show legacy CCS cases in interactive scene
This commit is contained in:
@@ -177,6 +177,39 @@ public sealed class CcsCaseTests
|
||||
Assert.Throws<InvalidOperationException>(() => CcsStore.ApplyDialogueChoice(attempt, definition, "ask_reason", DateTime.UtcNow));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LegacyCaseGetsInterviewFromExistingChartNarrative()
|
||||
{
|
||||
var definition = ValidImport().Cases[0];
|
||||
definition.InitialInterview = new();
|
||||
definition.InitialChart.HistorySections =
|
||||
[
|
||||
new() { Code = "hpi", Title = "History of present illness", Text = "The symptoms began this morning." },
|
||||
new() { Code = "medications", Title = "Medications", Text = "I take no regular medicines." }
|
||||
];
|
||||
|
||||
var interview = CcsStore.EffectiveInterview(definition);
|
||||
var attempt = new CcsAttempt { InterviewNodeId = interview.StartNodeId, OpeningCompleted = false };
|
||||
|
||||
Assert.Equal("opening", interview.StartNodeId);
|
||||
Assert.Equal(5, interview.Nodes.Count);
|
||||
CcsStore.ApplyDialogueChoice(attempt, definition, "ask_reason", DateTime.UtcNow);
|
||||
CcsStore.ApplyDialogueChoice(attempt, definition, "ask_history_1", DateTime.UtcNow);
|
||||
CcsStore.ApplyDialogueChoice(attempt, definition, "ask_history_2", DateTime.UtcNow);
|
||||
CcsStore.ApplyDialogueChoice(attempt, definition, "finish_interview", DateTime.UtcNow);
|
||||
Assert.Equal("complete", attempt.InterviewNodeId);
|
||||
Assert.True(attempt.OpeningCompleted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuthoredInterviewRemainsPreferredOverGeneratedFallback()
|
||||
{
|
||||
var definition = ValidImport().Cases[0];
|
||||
definition.InitialInterview = ValidInterview();
|
||||
|
||||
Assert.Same(definition.InitialInterview, CcsStore.EffectiveInterview(definition));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CcsExpectedScoreUsesSeparateUserAndCaseElo()
|
||||
{
|
||||
|
||||
@@ -108,6 +108,7 @@ public sealed class CcsStore
|
||||
}
|
||||
if (definition is null) throw new KeyNotFoundException("CCS case not found.");
|
||||
var now = DateTime.UtcNow;
|
||||
var interview = EffectiveInterview(definition);
|
||||
var attempt = new CcsAttempt
|
||||
{
|
||||
UserId = userId,
|
||||
@@ -117,7 +118,7 @@ public sealed class CcsStore
|
||||
CaseVersion = definition.Version,
|
||||
Mode = CcsAttemptModes.Timed,
|
||||
OpeningCompleted = false,
|
||||
InterviewNodeId = definition.InitialInterview.StartNodeId,
|
||||
InterviewNodeId = interview.StartNodeId,
|
||||
Location = definition.PresentingLocation,
|
||||
CurrentVitals = new Dictionary<string, string>(definition.InitialChart.Vitals),
|
||||
VitalHistory =
|
||||
@@ -415,7 +416,8 @@ public sealed class CcsStore
|
||||
? attempt.VitalHistory
|
||||
: [new CcsVitalSnapshot { SimulatedMinute = attempt.SimulatedMinute, Values = new Dictionary<string, string>(attempt.CurrentVitals) }];
|
||||
CcsInterviewStateResponse? interview = null;
|
||||
var currentInterviewNode = definition.InitialInterview.Nodes.FirstOrDefault(value =>
|
||||
var interviewDefinition = EffectiveInterview(definition);
|
||||
var currentInterviewNode = interviewDefinition.Nodes.FirstOrDefault(value =>
|
||||
value.Id.Equals(attempt.InterviewNodeId, StringComparison.OrdinalIgnoreCase));
|
||||
if (currentInterviewNode is not null)
|
||||
{
|
||||
@@ -458,12 +460,13 @@ public sealed class CcsStore
|
||||
public static void ApplyDialogueChoice(CcsAttempt attempt, CcsCaseRevision definition, string choiceId, DateTime now)
|
||||
{
|
||||
if (attempt.OpeningCompleted == true) throw new InvalidOperationException("The initial interview is already complete.");
|
||||
var node = definition.InitialInterview.Nodes.FirstOrDefault(value =>
|
||||
var interview = EffectiveInterview(definition);
|
||||
var node = interview.Nodes.FirstOrDefault(value =>
|
||||
value.Id.Equals(attempt.InterviewNodeId, StringComparison.OrdinalIgnoreCase))
|
||||
?? throw new InvalidOperationException("The interactive interview is unavailable.");
|
||||
var choice = node.Choices.FirstOrDefault(value => value.Id.Equals(choiceId, StringComparison.OrdinalIgnoreCase))
|
||||
?? throw new ArgumentException("The dialogue choice is not available at the current interview node.");
|
||||
var next = definition.InitialInterview.Nodes.FirstOrDefault(value =>
|
||||
var next = interview.Nodes.FirstOrDefault(value =>
|
||||
value.Id.Equals(choice.NextNodeId, StringComparison.OrdinalIgnoreCase))
|
||||
?? throw new InvalidOperationException("The dialogue choice target is unavailable.");
|
||||
attempt.Actions.Add(Action("dialogue_choice", choice.Id, attempt.SimulatedMinute, now));
|
||||
@@ -727,8 +730,66 @@ public sealed class CcsStore
|
||||
ExternalId = value.ExternalId, Title = value.Title, System = value.System,
|
||||
PresentingLocation = value.PresentingLocation, RealTimeMinutes = value.RealTimeMinutes,
|
||||
Difficulty = value.Difficulty, Tags = value.Tags,
|
||||
InteractiveInterviewAvailable = !string.IsNullOrWhiteSpace(value.InitialInterview.StartNodeId) && value.InitialInterview.Nodes.Count > 0
|
||||
InteractiveInterviewAvailable = EffectiveInterview(value).Nodes.Count > 0
|
||||
};
|
||||
|
||||
internal static CcsInitialInterview EffectiveInterview(CcsCaseRevision definition)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(definition.InitialInterview.StartNodeId)
|
||||
&& definition.InitialInterview.Nodes.Count > 0)
|
||||
return definition.InitialInterview;
|
||||
|
||||
var narrative = definition.InitialChart.HistorySections
|
||||
.Where(value => !string.IsNullOrWhiteSpace(value.Text))
|
||||
.Select((value, index) => new
|
||||
{
|
||||
Id = $"history_{index + 1}",
|
||||
Prompt = string.IsNullOrWhiteSpace(value.Title)
|
||||
? "Can you tell me more about your history?"
|
||||
: $"Can you tell me about your {value.Title.Trim().ToLowerInvariant()}?",
|
||||
value.Text
|
||||
}).ToList();
|
||||
if (narrative.Count == 0 && !string.IsNullOrWhiteSpace(definition.InitialChart.History))
|
||||
narrative.Add(new { Id = "history_1", Prompt = "Can you tell me more about your history?", Text = definition.InitialChart.History });
|
||||
|
||||
var nodes = new List<CcsDialogueNode>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = "opening", PatientText = "Hello, doctor.",
|
||||
Choices = [new() { Id = "ask_reason", Text = "What brings you in today?", NextNodeId = "reason" }]
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = "reason", PatientText = definition.InitialChart.ReasonForEncounter,
|
||||
Choices = [new()
|
||||
{
|
||||
Id = narrative.Count > 0 ? "ask_history_1" : "finish_interview",
|
||||
Text = narrative.Count > 0 ? narrative[0].Prompt : "Is there anything else you would like me to know?",
|
||||
NextNodeId = narrative.Count > 0 ? narrative[0].Id : "complete"
|
||||
}]
|
||||
}
|
||||
};
|
||||
for (var index = 0; index < narrative.Count; index++)
|
||||
{
|
||||
var next = index + 1 < narrative.Count ? narrative[index + 1] : null;
|
||||
nodes.Add(new CcsDialogueNode
|
||||
{
|
||||
Id = narrative[index].Id, PatientText = narrative[index].Text,
|
||||
Choices = [new()
|
||||
{
|
||||
Id = next is null ? "finish_interview" : $"ask_history_{index + 2}",
|
||||
Text = next?.Prompt ?? "Is there anything else you would like me to know?",
|
||||
NextNodeId = next?.Id ?? "complete"
|
||||
}]
|
||||
});
|
||||
}
|
||||
nodes.Add(new CcsDialogueNode
|
||||
{
|
||||
Id = "complete", PatientText = "That is everything I can think of, doctor.", IsTerminal = true
|
||||
});
|
||||
return new CcsInitialInterview { StartNodeId = "opening", Nodes = nodes };
|
||||
}
|
||||
internal static int Rank(CcsOrderDefinition order, string query)
|
||||
{
|
||||
var values = new[] { order.CanonicalName }.Concat(order.Aliases).Select(value => value.ToLowerInvariant());
|
||||
|
||||
Reference in New Issue
Block a user