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));
|
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]
|
[Fact]
|
||||||
public void CcsExpectedScoreUsesSeparateUserAndCaseElo()
|
public void CcsExpectedScoreUsesSeparateUserAndCaseElo()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ public sealed class CcsStore
|
|||||||
}
|
}
|
||||||
if (definition is null) throw new KeyNotFoundException("CCS case not found.");
|
if (definition is null) throw new KeyNotFoundException("CCS case not found.");
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
|
var interview = EffectiveInterview(definition);
|
||||||
var attempt = new CcsAttempt
|
var attempt = new CcsAttempt
|
||||||
{
|
{
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
@@ -117,7 +118,7 @@ public sealed class CcsStore
|
|||||||
CaseVersion = definition.Version,
|
CaseVersion = definition.Version,
|
||||||
Mode = CcsAttemptModes.Timed,
|
Mode = CcsAttemptModes.Timed,
|
||||||
OpeningCompleted = false,
|
OpeningCompleted = false,
|
||||||
InterviewNodeId = definition.InitialInterview.StartNodeId,
|
InterviewNodeId = interview.StartNodeId,
|
||||||
Location = definition.PresentingLocation,
|
Location = definition.PresentingLocation,
|
||||||
CurrentVitals = new Dictionary<string, string>(definition.InitialChart.Vitals),
|
CurrentVitals = new Dictionary<string, string>(definition.InitialChart.Vitals),
|
||||||
VitalHistory =
|
VitalHistory =
|
||||||
@@ -415,7 +416,8 @@ public sealed class CcsStore
|
|||||||
? attempt.VitalHistory
|
? attempt.VitalHistory
|
||||||
: [new CcsVitalSnapshot { SimulatedMinute = attempt.SimulatedMinute, Values = new Dictionary<string, string>(attempt.CurrentVitals) }];
|
: [new CcsVitalSnapshot { SimulatedMinute = attempt.SimulatedMinute, Values = new Dictionary<string, string>(attempt.CurrentVitals) }];
|
||||||
CcsInterviewStateResponse? interview = null;
|
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));
|
value.Id.Equals(attempt.InterviewNodeId, StringComparison.OrdinalIgnoreCase));
|
||||||
if (currentInterviewNode is not null)
|
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)
|
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.");
|
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))
|
value.Id.Equals(attempt.InterviewNodeId, StringComparison.OrdinalIgnoreCase))
|
||||||
?? throw new InvalidOperationException("The interactive interview is unavailable.");
|
?? throw new InvalidOperationException("The interactive interview is unavailable.");
|
||||||
var choice = node.Choices.FirstOrDefault(value => value.Id.Equals(choiceId, StringComparison.OrdinalIgnoreCase))
|
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.");
|
?? 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))
|
value.Id.Equals(choice.NextNodeId, StringComparison.OrdinalIgnoreCase))
|
||||||
?? throw new InvalidOperationException("The dialogue choice target is unavailable.");
|
?? throw new InvalidOperationException("The dialogue choice target is unavailable.");
|
||||||
attempt.Actions.Add(Action("dialogue_choice", choice.Id, attempt.SimulatedMinute, now));
|
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,
|
ExternalId = value.ExternalId, Title = value.Title, System = value.System,
|
||||||
PresentingLocation = value.PresentingLocation, RealTimeMinutes = value.RealTimeMinutes,
|
PresentingLocation = value.PresentingLocation, RealTimeMinutes = value.RealTimeMinutes,
|
||||||
Difficulty = value.Difficulty, Tags = value.Tags,
|
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)
|
internal static int Rank(CcsOrderDefinition order, string query)
|
||||||
{
|
{
|
||||||
var values = new[] { order.CanonicalName }.Concat(order.Aliases).Select(value => value.ToLowerInvariant());
|
var values = new[] { order.CanonicalName }.Concat(order.Aliases).Select(value => value.ToLowerInvariant());
|
||||||
|
|||||||
Reference in New Issue
Block a user