Integrate examinations into CCS interview

This commit is contained in:
hz
2026-08-11 22:27:37 -05:00
parent 6108011f74
commit 9a54bfc6d6
4 changed files with 204 additions and 57 deletions
+162 -15
View File
@@ -1,7 +1,6 @@
extends Control
signal choice_selected(choice_id: String)
signal interview_continue_requested
signal physical_exam_selected(exam_code: String)
signal physical_exam_finish_requested
signal debrief_return_requested
@@ -32,6 +31,7 @@ const DIALOGUE_BUBBLE_VERTICAL_CHROME := 95.0
@onready var page_label: Label = $DialogueChoiceArea/ChoicePager/Page
@onready var next_page_button: Button = $DialogueChoiceArea/ChoicePager/Next
@onready var computer_launcher: Button = $ComputerLauncher
@onready var examination_launcher: Button = $ExaminationLauncher
@onready var computer_overlay: Control = $ComputerOverlay
@onready var computer_close_button: Button = $ComputerOverlay/Close
@onready var vitals_launcher: Button = $ComputerOverlay/VitalsLauncher
@@ -57,6 +57,15 @@ var physical_exam_finding_text: Label
var physical_exam_status: Label
var physical_exam_finish_button: Button
var physical_exam_buttons := {}
var physical_exam_close_button: Button
var physical_exam_result_button: Button
var physical_exam_terminal := false
var available_exams: Array = []
var attempt_chart: Array = []
var medical_history_section_title: Label
var medical_history_exam_text: RichTextLabel
var medical_history_tab_button: Button
var physical_exam_tab_button: Button
var experience_mode := "management"
var pending_choices: Array = []
var available_choices: Array = []
@@ -77,6 +86,7 @@ func _ready() -> void:
previous_page_button.pressed.connect(_change_choice_page.bind(-1))
next_page_button.pressed.connect(_change_choice_page.bind(1))
computer_launcher.pressed.connect(open_computer)
examination_launcher.pressed.connect(show_exam_selector)
computer_close_button.pressed.connect(close_computer)
vitals_launcher.pressed.connect(open_vitals)
vitals_close_button.pressed.connect(close_vitals)
@@ -92,6 +102,7 @@ func _ready() -> void:
_style_computer_window(medical_history_window)
_style_desktop_shortcut(vitals_launcher)
_style_desktop_shortcut(medical_history_launcher)
_build_medical_history_tabs()
_resize_computer_windows()
_update_clock()
_schedule_patient_blink()
@@ -117,7 +128,7 @@ func _update_clock() -> void:
func configure_presentation(
presentation: Dictionary, simulated_start: String, simulated_minute: int,
vitals: Dictionary = {}, medical_history: String = "") -> void:
vitals: Dictionary = {}, medical_history: String = "", chart: Array = []) -> void:
if str(presentation.get("patientAssetKey", "")) != "patient_44_white_male":
push_warning("Unsupported CCS patient asset key; using the office default.")
var date_time := Time.get_datetime_dict_from_datetime_string(simulated_start, false)
@@ -129,6 +140,8 @@ func configure_presentation(
medical_history_text.text = medical_history.strip_edges() \
if not medical_history.strip_edges().is_empty() \
else "No past medical history is available."
attempt_chart = chart.duplicate(true)
_render_physical_exam_history()
func configure_experience_mode(value: String) -> void:
@@ -217,7 +230,7 @@ func configure_interview(node: Dictionary) -> void:
if bool(node.get("isTerminal", false)):
next_choices = [{
"id": "__continue",
"text": "Begin Physical Examination" if experience_mode == "graphical_interview" else "Continue to Patient Management"
"text": "Review Examinations" if experience_mode == "graphical_interview" else "Continue to Patient Management"
}]
else:
next_choices = node.get("choices", [])
@@ -269,6 +282,7 @@ func complete_response_transition() -> void:
dialogue_page_index = 0
dialogue_reveal_complete = false
show_dialogue_choices(choices)
_update_examination_launcher()
func _input(event: InputEvent) -> void:
@@ -324,6 +338,14 @@ func is_computer_open() -> bool:
func close_transient_overlay() -> bool:
if physical_exam_panel != null and physical_exam_panel.visible:
if physical_exam_result_button.visible:
_close_exam_result()
elif not physical_exam_terminal:
close_exam_overlay()
else:
return false
return true
var top_window := _topmost_computer_window()
if top_window == vitals_window:
return close_vitals()
@@ -374,6 +396,61 @@ func is_medical_history_open() -> bool:
return medical_history_window.visible
func _build_medical_history_tabs() -> void:
var content := medical_history_text.get_parent() as VBoxContainer
medical_history_section_title = content.get_node("SectionTitle") as Label
var tabs := HBoxContainer.new()
tabs.add_theme_constant_override("separation", 8)
content.add_child(tabs)
content.move_child(tabs, medical_history_section_title.get_index())
medical_history_tab_button = Button.new()
medical_history_tab_button.text = "Medical History"
medical_history_tab_button.pressed.connect(_show_chart_tab.bind(false))
tabs.add_child(medical_history_tab_button)
physical_exam_tab_button = Button.new()
physical_exam_tab_button.text = "Physical Exam"
physical_exam_tab_button.pressed.connect(_show_chart_tab.bind(true))
tabs.add_child(physical_exam_tab_button)
medical_history_exam_text = RichTextLabel.new()
medical_history_exam_text.fit_content = true
medical_history_exam_text.custom_minimum_size = Vector2(0.0, 120.0)
medical_history_exam_text.add_theme_font_size_override("normal_font_size", 18)
content.add_child(medical_history_exam_text)
_show_chart_tab(false)
func _show_chart_tab(show_exams: bool) -> void:
medical_history_section_title.visible = not show_exams
medical_history_text.visible = not show_exams
medical_history_exam_text.visible = show_exams
medical_history_tab_button.disabled = not show_exams
physical_exam_tab_button.disabled = show_exams
_resize_computer_windows()
func _render_physical_exam_history() -> void:
if medical_history_exam_text == null:
return
var exam_codes := {}
for raw_exam in available_exams:
exam_codes[str(raw_exam.get("code", ""))] = true
var findings: Array[String] = []
for raw_entry in attempt_chart:
var entry: Dictionary = raw_entry
if exam_codes.has(str(entry.get("code", ""))):
findings.append("[b]%s[/b]\n%s" % [str(entry.get("title", "Examination")), str(entry.get("text", ""))])
medical_history_exam_text.text = "\n\n".join(findings) if not findings.is_empty() \
else "No physical examinations have been completed."
func _update_examination_launcher() -> void:
if examination_launcher == null:
return
examination_launcher.visible = choice_container.visible and not computer_overlay.visible \
and (physical_exam_panel == null or not physical_exam_panel.visible)
examination_launcher.disabled = available_exams.is_empty()
func _render_vitals(vitals: Dictionary) -> void:
for child in vitals_rows.get_children():
vitals_rows.remove_child(child)
@@ -490,9 +567,11 @@ func _resize_computer_windows() -> void:
minf(720.0, maxf(320.0, size.x - 48.0)),
minf(vitals_content_height, maxf(300.0, size.y - 48.0)))
_set_centered_window_rect(vitals_window, vitals_size, Vector2(-55.0, -25.0))
var exam_count := medical_history_exam_text.text.count("[b]") if medical_history_exam_text != null else 0
var history_height := 340.0 if not medical_history_exam_text.visible else clampf(250.0 + exam_count * 90.0, 340.0, 650.0)
var history_size := Vector2(
minf(560.0, maxf(320.0, size.x - 48.0)),
minf(300.0, maxf(260.0, size.y - 48.0)))
minf(680.0, maxf(320.0, size.x - 48.0)),
minf(history_height, maxf(300.0, size.y - 48.0)))
_set_centered_window_rect(medical_history_window, history_size, Vector2(55.0, 25.0))
@@ -667,6 +746,7 @@ func _render_choice_page() -> void:
next_page_button.disabled = choice_page >= page_count - 1
choice_area.visible = true
choice_container.visible = true
_update_examination_launcher()
func hide_dialogue_choices() -> void:
@@ -676,6 +756,7 @@ func hide_dialogue_choices() -> void:
choice_container.visible = false
choice_pager.visible = false
choice_area.visible = false
_update_examination_launcher()
func _clear_choice_buttons() -> void:
@@ -723,18 +804,19 @@ func _on_choice_pressed_visual(button: TextureButton, pressed: bool) -> void:
func _on_dialogue_choice_pressed(choice_id: String) -> void:
if choice_id == "__continue":
interview_continue_requested.emit()
show_exam_selector(true)
else:
choice_selected.emit(choice_id)
func show_physical_exam(exams: Array, chart: Array, pending_code: String = "") -> void:
hide_patient_dialogue()
hide_dialogue_choices()
computer_available = true
computer_launcher.visible = not computer_overlay.visible
available_exams = exams.duplicate(true)
attempt_chart = chart.duplicate(true)
_ensure_physical_exam_panel()
physical_exam_panel.visible = true
physical_exam_finding_title.visible = true
physical_exam_finding_text.visible = true
physical_exam_result_button.visible = false
for child in physical_exam_grid.get_children():
physical_exam_grid.remove_child(child)
child.queue_free()
@@ -777,6 +859,59 @@ func show_physical_exam(exams: Array, chart: Array, pending_code: String = "") -
set_physical_exam_pending(pending_code)
func configure_examinations(exams: Array, chart: Array) -> void:
available_exams = exams.duplicate(true)
attempt_chart = chart.duplicate(true)
_render_physical_exam_history()
_update_examination_launcher()
func show_exam_selector(terminal: bool = false) -> void:
if not choice_container.visible or (physical_exam_panel != null and physical_exam_panel.visible):
return
physical_exam_terminal = terminal
show_physical_exam(available_exams, attempt_chart)
physical_exam_finish_button.visible = terminal
physical_exam_close_button.visible = not terminal
func show_exam_result(chart: Array, exam_code: String) -> void:
attempt_chart = chart.duplicate(true)
_render_physical_exam_history()
_ensure_physical_exam_panel()
physical_exam_panel.visible = true
for child in physical_exam_grid.get_children():
child.visible = false
var finding := _chart_exam_entry(chart, exam_code)
physical_exam_finding_title.text = str(finding.get("title", "Examination Finding"))
physical_exam_finding_text.text = str(finding.get("text", "No finding was reported."))
physical_exam_result_button.visible = true
physical_exam_close_button.visible = false
physical_exam_finish_button.visible = false
func close_exam_overlay() -> void:
if physical_exam_panel != null:
physical_exam_panel.visible = false
_update_examination_launcher()
func _close_exam_result() -> void:
if physical_exam_terminal:
physical_exam_panel.visible = false
show_exam_selector(true)
else:
close_exam_overlay()
func _chart_exam_entry(chart: Array, code: String) -> Dictionary:
for index in range(chart.size() - 1, -1, -1):
var entry: Dictionary = chart[index]
if str(entry.get("code", "")) == code:
return entry
return {}
func set_physical_exam_pending(exam_code: String) -> void:
var pending := not exam_code.is_empty()
for code in physical_exam_buttons:
@@ -801,12 +936,13 @@ func _ensure_physical_exam_panel() -> void:
physical_exam_panel = PanelContainer.new()
physical_exam_panel.name = "PhysicalExamination"
physical_exam_panel.z_index = 50
physical_exam_panel.custom_minimum_size = Vector2(980.0, 650.0)
add_child(physical_exam_panel)
physical_exam_panel.set_anchors_preset(Control.PRESET_FULL_RECT)
physical_exam_panel.offset_left = 44.0
physical_exam_panel.offset_top = 28.0
physical_exam_panel.offset_right = -44.0
physical_exam_panel.offset_bottom = -28.0
physical_exam_panel.set_anchors_preset(Control.PRESET_CENTER)
physical_exam_panel.offset_left = -490.0
physical_exam_panel.offset_top = -325.0
physical_exam_panel.offset_right = 490.0
physical_exam_panel.offset_bottom = 325.0
var panel_style := StyleBoxFlat.new()
panel_style.bg_color = Color(0.96, 0.97, 0.98, 0.97)
panel_style.border_color = Color(0.12, 0.22, 0.28, 0.9)
@@ -869,6 +1005,17 @@ func _ensure_physical_exam_panel() -> void:
physical_exam_finish_button.custom_minimum_size = Vector2(170.0, 48.0)
physical_exam_finish_button.pressed.connect(func(): physical_exam_finish_requested.emit())
actions.add_child(physical_exam_finish_button)
physical_exam_close_button = Button.new()
physical_exam_close_button.text = "Return to Interview"
physical_exam_close_button.custom_minimum_size = Vector2(190.0, 48.0)
physical_exam_close_button.pressed.connect(close_exam_overlay)
actions.add_child(physical_exam_close_button)
physical_exam_result_button = Button.new()
physical_exam_result_button.text = "Continue"
physical_exam_result_button.custom_minimum_size = Vector2(150.0, 48.0)
physical_exam_result_button.visible = false
physical_exam_result_button.pressed.connect(_close_exam_result)
actions.add_child(physical_exam_result_button)
func _style_physical_exam_button(button: Button, complete: bool) -> void: