Resume stale graphical CCS interviews
This commit is contained in:
@@ -250,6 +250,19 @@ public sealed class CcsCaseTests
|
|||||||
Assert.True(CcsStore.CurrentInterviewNode(attempt, definition)!.IsTerminal);
|
Assert.True(CcsStore.CurrentInterviewNode(attempt, definition)!.IsTerminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TopicInterviewRepairsLegacyOpeningCompletionFlag()
|
||||||
|
{
|
||||||
|
var definition = ValidImport().Cases[0];
|
||||||
|
definition.InitialInterview = TopicInterview();
|
||||||
|
var attempt = new CcsAttempt { InterviewNodeId = "opening", OpeningCompleted = true };
|
||||||
|
|
||||||
|
CcsStore.ApplyDialogueChoice(attempt, definition, "chief_concern", DateTime.UtcNow);
|
||||||
|
|
||||||
|
Assert.False(attempt.OpeningCompleted);
|
||||||
|
Assert.Equal("chief_concern", attempt.InterviewNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TopicInterviewRejectsPrerequisiteCycles()
|
public void TopicInterviewRejectsPrerequisiteCycles()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -436,6 +436,9 @@ public sealed class CcsStore
|
|||||||
: [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 = CurrentInterviewNode(attempt, definition);
|
var currentInterviewNode = CurrentInterviewNode(attempt, definition);
|
||||||
|
var openingCompleted = attempt.OpeningCompleted ?? true;
|
||||||
|
if (definition.InitialInterview.Topics.Count > 0 && currentInterviewNode?.IsTerminal != true)
|
||||||
|
openingCompleted = false;
|
||||||
if (currentInterviewNode is not null)
|
if (currentInterviewNode is not null)
|
||||||
{
|
{
|
||||||
interview = new CcsInterviewStateResponse
|
interview = new CcsInterviewStateResponse
|
||||||
@@ -443,7 +446,7 @@ public sealed class CcsStore
|
|||||||
CurrentNodeId = currentInterviewNode.Id,
|
CurrentNodeId = currentInterviewNode.Id,
|
||||||
CurrentNode = currentInterviewNode,
|
CurrentNode = currentInterviewNode,
|
||||||
VisitedChoiceIds = attempt.InterviewChoiceIds,
|
VisitedChoiceIds = attempt.InterviewChoiceIds,
|
||||||
Completed = attempt.OpeningCompleted ?? false
|
Completed = openingCompleted
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return new CcsAttemptResponse
|
return new CcsAttemptResponse
|
||||||
@@ -452,7 +455,7 @@ public sealed class CcsStore
|
|||||||
CaseTitle = completed ? definition.Title : $"CCS Case {attempt.CaseExternalId}",
|
CaseTitle = completed ? definition.Title : $"CCS Case {attempt.CaseExternalId}",
|
||||||
Mode = attempt.Mode, State = attempt.State, Revision = attempt.Revision,
|
Mode = attempt.Mode, State = attempt.State, Revision = attempt.Revision,
|
||||||
SimulatedMinute = attempt.SimulatedMinute, SimulatedStartUtc = definition.InitialChart.SimulatedStartUtc,
|
SimulatedMinute = attempt.SimulatedMinute, SimulatedStartUtc = definition.InitialChart.SimulatedStartUtc,
|
||||||
OpeningCompleted = attempt.OpeningCompleted ?? true, Location = attempt.Location,
|
OpeningCompleted = openingCompleted, Location = attempt.Location,
|
||||||
DeadlineUtc = attempt.DeadlineUtc, HardDeadlineUtc = attempt.HardDeadlineUtc,
|
DeadlineUtc = attempt.DeadlineUtc, HardDeadlineUtc = attempt.HardDeadlineUtc,
|
||||||
FinalOrdersDeadlineUtc = attempt.FinalOrdersDeadlineUtc,
|
FinalOrdersDeadlineUtc = attempt.FinalOrdersDeadlineUtc,
|
||||||
CreatedUtc = attempt.CreatedUtc, RealTimeMinutes = definition.RealTimeMinutes,
|
CreatedUtc = attempt.CreatedUtc, RealTimeMinutes = definition.RealTimeMinutes,
|
||||||
@@ -482,12 +485,18 @@ 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 (definition.InitialInterview.Topics.Count > 0)
|
if (definition.InitialInterview.Topics.Count > 0)
|
||||||
{
|
{
|
||||||
|
if (attempt.OpeningCompleted == true
|
||||||
|
&& attempt.InterviewNodeId.Equals("complete", StringComparison.OrdinalIgnoreCase))
|
||||||
|
throw new InvalidOperationException("The initial interview is already complete.");
|
||||||
|
// Attempts that passed through the legacy classic opening can have
|
||||||
|
// this flag set before any graphical topic was completed.
|
||||||
|
attempt.OpeningCompleted = false;
|
||||||
ApplyTopicChoice(attempt, definition.InitialInterview, choiceId, now);
|
ApplyTopicChoice(attempt, definition.InitialInterview, choiceId, now);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (attempt.OpeningCompleted == true) throw new InvalidOperationException("The initial interview is already complete.");
|
||||||
var interview = EffectiveInterview(definition);
|
var interview = EffectiveInterview(definition);
|
||||||
var node = interview.Nodes.FirstOrDefault(value =>
|
var node = interview.Nodes.FirstOrDefault(value =>
|
||||||
value.Id.Equals(attempt.InterviewNodeId, StringComparison.OrdinalIgnoreCase))
|
value.Id.Equals(attempt.InterviewNodeId, StringComparison.OrdinalIgnoreCase))
|
||||||
|
|||||||
@@ -102,7 +102,8 @@ func _render_attempt(attempt: Dictionary) -> void:
|
|||||||
if node.is_empty():
|
if node.is_empty():
|
||||||
_show_unsupported_attempt("The graphical interview could not load its current dialogue.")
|
_show_unsupported_attempt("The graphical interview could not load its current dialogue.")
|
||||||
return
|
return
|
||||||
if bool(attempt.get("openingCompleted", false)) and not bool(node.get("isTerminal", false)):
|
if str(attempt.get("experienceMode", "management")) != "graphical_interview" \
|
||||||
|
and bool(attempt.get("openingCompleted", false)) and not bool(node.get("isTerminal", false)):
|
||||||
_show_unsupported_attempt("This case has progressed beyond the graphical interview.")
|
_show_unsupported_attempt("This case has progressed beyond the graphical interview.")
|
||||||
return
|
return
|
||||||
_show_doctor_office(node, attempt)
|
_show_doctor_office(node, attempt)
|
||||||
|
|||||||
@@ -62,6 +62,13 @@ func _run() -> void:
|
|||||||
_assert(not screen.active_scene.is_patient_dialogue_visible(), "the opening should wait for a clinician dialogue selection")
|
_assert(not screen.active_scene.is_patient_dialogue_visible(), "the opening should wait for a clinician dialogue selection")
|
||||||
_assert(screen.active_scene.choice_container.get_child_count() == 1, "the scene should render server-provided dialogue choices")
|
_assert(screen.active_scene.choice_container.get_child_count() == 1, "the scene should render server-provided dialogue choices")
|
||||||
_assert(is_equal_approx(screen.stage_root.scale.x, screen.stage_root.scale.y), "the graphical stage should preserve its aspect ratio")
|
_assert(is_equal_approx(screen.stage_root.scale.x, screen.stage_root.scale.y), "the graphical stage should preserve its aspect ratio")
|
||||||
|
var stale_graphical_attempt := _sample_attempt(false)
|
||||||
|
stale_graphical_attempt.experienceMode = "graphical_interview"
|
||||||
|
stale_graphical_attempt.openingCompleted = true
|
||||||
|
service.attempt = stale_graphical_attempt
|
||||||
|
screen._render_state()
|
||||||
|
await process_frame
|
||||||
|
_assert(screen.current_view == "doctor_office", "legacy completion flags should not block a graphical interview")
|
||||||
service.attempt = _sample_branching_attempt()
|
service.attempt = _sample_branching_attempt()
|
||||||
screen._render_state()
|
screen._render_state()
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|||||||
Reference in New Issue
Block a user