Complete CCS results and rating flow
This commit is contained in:
@@ -4,19 +4,22 @@ const Style := preload("res://scripts/ui/ui_style.gd")
|
||||
var main: Control
|
||||
var list: VBoxContainer
|
||||
var prediction: Label
|
||||
var rating: Label
|
||||
|
||||
func _ready() -> void:
|
||||
set_anchors_preset(Control.PRESET_FULL_RECT); add_theme_constant_override("separation", 12)
|
||||
var back := Button.new(); back.text = "< Profile"; back.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN; back.pressed.connect(func(): main.show_screen("profile")); add_child(back)
|
||||
var title := Label.new(); title.text = "CCS Case History"; Style.set_label(title, 32, Style.TEXT); add_child(title)
|
||||
prediction = Label.new(); prediction.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART; Style.set_label(prediction, 16, Style.CYAN); add_child(prediction)
|
||||
rating = Label.new(); Style.set_label(rating, 18, Style.TEXT); add_child(rating)
|
||||
var scroll := ScrollContainer.new(); scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL; add_child(scroll)
|
||||
list = VBoxContainer.new(); list.size_flags_horizontal = Control.SIZE_EXPAND_FILL; list.add_theme_constant_override("separation", 8); scroll.add_child(list)
|
||||
CCSService.history_changed.connect(_render)
|
||||
CCSService.prediction_changed.connect(_render_prediction)
|
||||
CCSService.rating_changed.connect(_render_rating)
|
||||
|
||||
func on_show(_payload: Variant = null) -> void:
|
||||
CCSService.load_history(); CCSService.load_prediction()
|
||||
CCSService.load_history(); CCSService.load_prediction(); CCSService.load_rating()
|
||||
|
||||
func _render() -> void:
|
||||
for child in list.get_children(): child.queue_free()
|
||||
@@ -25,7 +28,9 @@ func _render() -> void:
|
||||
for item in CCSService.history:
|
||||
var button := Button.new(); button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
var score: Variant = item.get("score", null)
|
||||
button.text = "%s\n%s | %s%s" % [item.get("caseTitle", "Case"), str(item.get("mode", "")).capitalize(), str(item.get("state", "")).capitalize(), " | %d%%" % int(round(float(score))) if score != null else ""]
|
||||
var rating_change: Dictionary = item.get("rating", {}) if typeof(item.get("rating", {})) == TYPE_DICTIONARY else {}
|
||||
var rating_text := " | CCS ELO %+d" % rating_change.get("userEloDelta", 0) if bool(rating_change.get("rated", false)) else ""
|
||||
button.text = "%s\n%s | %s%s%s" % [item.get("caseTitle", "Case"), str(item.get("mode", "")).capitalize(), str(item.get("state", "")).capitalize(), " | %d%%" % int(round(float(score))) if score != null else "", rating_text]
|
||||
button.disabled = str(item.get("state", "")) == "abandoned"
|
||||
button.pressed.connect(_open.bind(str(item.get("id", "")), str(item.get("state", "")))); list.add_child(button)
|
||||
|
||||
@@ -39,3 +44,6 @@ func _render_prediction() -> void:
|
||||
elif data.get("ccsScore", null) != null:
|
||||
prediction.text = "CCS performance: %.0f%% from %d first timed cases. Complete a Day 1 simulation for a combined prediction." % [data.get("ccsScore", 0), data.get("contributingCases", 0)]
|
||||
else: prediction.text = "Complete timed CCS cases to begin estimating CCS performance."
|
||||
|
||||
func _render_rating() -> void:
|
||||
rating.text = "CCS ELO %d | %d rated cases" % [CCSService.rating.get("elo", 1000), CCSService.rating.get("ratedCaseCount", 0)]
|
||||
|
||||
@@ -17,6 +17,8 @@ var time_row: HBoxContainer
|
||||
var report_dialog: Control
|
||||
var report_button: Button
|
||||
var workspace: HSplitContainer
|
||||
var leave_button: Button
|
||||
var leaving := false
|
||||
|
||||
func _ready() -> void:
|
||||
set_anchors_preset(Control.PRESET_FULL_RECT); add_theme_constant_override("separation", 8)
|
||||
@@ -46,17 +48,20 @@ func _ready() -> void:
|
||||
var button := Button.new(); button.text = "+%s" % ("1 day" if minutes == 1440 else "%d min" % minutes); button.pressed.connect(CCSService.send_command.bind("advance", "", minutes)); time_row.add_child(button)
|
||||
var reassess := Button.new(); reassess.text = "Interval history"; reassess.pressed.connect(CCSService.send_command.bind("interval_history", "", 0)); clerk.add_child(reassess)
|
||||
end_button = Button.new(); end_button.text = "End case"; Style.style_chip(end_button, Style.AMBER); end_button.pressed.connect(_end_case); clerk.add_child(end_button)
|
||||
var leave := Button.new(); leave.text = "Leave case"; Style.style_chip(leave, Style.RED); leave.pressed.connect(_leave); clerk.add_child(leave)
|
||||
leave_button = Button.new(); leave_button.text = "Leave case"; Style.style_chip(leave_button, Style.RED); leave_button.pressed.connect(_leave); clerk.add_child(leave_button)
|
||||
status = Label.new(); Style.set_label(status, 14, Style.RED); add_child(status)
|
||||
timer = Timer.new(); timer.wait_time = 1; timer.timeout.connect(_tick); add_child(timer)
|
||||
CCSService.attempt_changed.connect(_render)
|
||||
CCSService.attempt_abandoned.connect(_on_abandoned)
|
||||
CCSService.orders_changed.connect(_render_search)
|
||||
CCSService.request_failed.connect(func(message: String): status.text = message)
|
||||
CCSService.request_failed.connect(_request_failed)
|
||||
CCSService.report_submitted.connect(_report_submitted)
|
||||
report_dialog = preload("res://scripts/ui/ccs_report_dialog.gd").new(); add_child(report_dialog)
|
||||
|
||||
func on_show(payload: Variant = null) -> void:
|
||||
status.text = ""
|
||||
leaving = false
|
||||
leave_button.disabled = false
|
||||
if typeof(payload) == TYPE_STRING and not str(payload).is_empty(): CCSService.load_attempt(str(payload))
|
||||
_render(); timer.start()
|
||||
|
||||
@@ -101,8 +106,27 @@ func _tick() -> void:
|
||||
clock.text = "%02d:%02d | Case time %d min" % [floori(float(remaining) / 60.0), remaining % 60, int(CCSService.attempt.get("simulatedMinute", 0))]
|
||||
if remaining == 0 and not CCSService.is_loading: CCSService.load_attempt(str(CCSService.attempt.get("id", "")))
|
||||
|
||||
func leave_case() -> void:
|
||||
_leave()
|
||||
|
||||
func _leave() -> void:
|
||||
CCSService.abandon(); main.show_screen("ccs_hub")
|
||||
if leaving or CCSService.attempt.is_empty(): return
|
||||
leaving = true
|
||||
leave_button.disabled = true
|
||||
status.text = "Leaving case..."
|
||||
CCSService.abandon()
|
||||
|
||||
func _on_abandoned() -> void:
|
||||
if not leaving: return
|
||||
leaving = false
|
||||
main.show_screen("selection", "step3")
|
||||
|
||||
func _request_failed(message: String) -> void:
|
||||
if not visible: return
|
||||
status.text = message
|
||||
if leaving:
|
||||
leaving = false
|
||||
leave_button.disabled = false
|
||||
|
||||
func _end_case() -> void:
|
||||
CCSService.send_command("exit_case" if str(CCSService.attempt.get("state", "")) == "final_orders" else "end_case")
|
||||
@@ -149,9 +173,9 @@ func _zoom(delta: float) -> void:
|
||||
CCSService.adjust_text_scale(delta); _apply_text_scale()
|
||||
|
||||
func _apply_text_scale() -> void:
|
||||
var size := int(round(18.0 * CCSService.text_scale))
|
||||
var font_size := int(round(18.0 * CCSService.text_scale))
|
||||
for label in [summary, chart]:
|
||||
label.add_theme_font_size_override("normal_font_size", size); label.add_theme_font_size_override("bold_font_size", size)
|
||||
label.add_theme_font_size_override("normal_font_size", font_size); label.add_theme_font_size_override("bold_font_size", font_size)
|
||||
_scale_controls(workspace)
|
||||
|
||||
func _scale_controls(node: Node) -> void:
|
||||
|
||||
@@ -9,6 +9,8 @@ var references: VBoxContainer
|
||||
var report_dialog: Control
|
||||
var report_button: Button
|
||||
var current_attempt := {}
|
||||
var status: Label
|
||||
var starting_case := false
|
||||
|
||||
func _ready() -> void:
|
||||
set_anchors_preset(Control.PRESET_FULL_RECT); add_theme_constant_override("separation", 10)
|
||||
@@ -26,13 +28,17 @@ func _ready() -> void:
|
||||
CCSService.attempt_changed.connect(_refresh_loaded_attempt); CCSService.report_submitted.connect(_report_submitted)
|
||||
report_dialog = preload("res://scripts/ui/ccs_report_dialog.gd").new(); add_child(report_dialog)
|
||||
var row := HBoxContainer.new(); add_child(row)
|
||||
var cases := Button.new(); cases.text = "More cases"; Style.style_primary_button(cases); cases.pressed.connect(func(): main.show_screen("ccs_hub")); row.add_child(cases)
|
||||
var history := Button.new(); history.text = "Case history"; history.pressed.connect(func(): main.show_screen("ccs_history")); row.add_child(history)
|
||||
var cases := Button.new(); cases.text = "Another Case"; Style.style_primary_button(cases); cases.pressed.connect(_another_case); row.add_child(cases)
|
||||
var back := Button.new(); back.text = "Back to Step 3 Modes"; back.pressed.connect(func(): main.show_screen("selection", "step3")); row.add_child(back)
|
||||
status = Label.new(); Style.set_label(status, 14, Style.RED); row.add_child(status)
|
||||
CCSService.request_failed.connect(_request_failed)
|
||||
|
||||
func _study_label(region: String) -> RichTextLabel:
|
||||
var label := RichTextLabel.new(); label.bbcode_enabled = true; label.fit_content = true; label.selection_enabled = true; label.set_meta("ccs_region", region); label.gui_input.connect(_text_input.bind(label)); return label
|
||||
|
||||
func on_show(payload: Variant = null) -> void:
|
||||
starting_case = false
|
||||
if status != null: status.text = ""
|
||||
if typeof(payload) == TYPE_STRING: CCSService.load_attempt(str(payload)); return
|
||||
current_attempt = payload if typeof(payload) == TYPE_DICTIONARY else CCSService.attempt
|
||||
if current_attempt.is_empty(): return
|
||||
@@ -41,9 +47,17 @@ func on_show(payload: Variant = null) -> void:
|
||||
_render_text(); _render_references(); _apply_text_scale()
|
||||
|
||||
func _score_plain() -> String:
|
||||
var lines: Array[String] = ["Scoring review"]
|
||||
var score: Dictionary = current_attempt.get("score", {})
|
||||
var lines: Array[String] = ["Case result: %d/%d points (%.0f%%)" % [score.get("earnedPoints", 0), score.get("possiblePoints", 0), score.get("percent", 0)]]
|
||||
var rating: Dictionary = current_attempt.get("rating", {})
|
||||
if bool(rating.get("rated", false)):
|
||||
lines.append("CCS ELO: %d (%+d)" % [rating.get("userElo", 1000), rating.get("userEloDelta", 0)])
|
||||
else:
|
||||
lines.append("CCS ELO unchanged: this case was not rating-eligible.")
|
||||
for criterion in current_attempt.get("score", {}).get("criteria", []):
|
||||
lines.append("%s %s%d\n%s" % [str(criterion.get("status", "missed")).capitalize(), "+" if int(criterion.get("points", 0)) >= 0 else "", criterion.get("points", 0), criterion.get("rationale", "")])
|
||||
var observed := str(criterion.get("observedAction", "Not performed"))
|
||||
if criterion.get("observedMinute", null) != null: observed += " at minute %d" % criterion.get("observedMinute", 0)
|
||||
lines.append("%s: %s %s%d\nExpected: %s\nObserved: %s\n%s" % [criterion.get("title", criterion.get("criterionId", "Criterion")), str(criterion.get("status", "missed")).capitalize(), "+" if int(criterion.get("points", 0)) >= 0 else "", criterion.get("points", 0), criterion.get("expectedAction", ""), observed, criterion.get("rationale", "")])
|
||||
return "\n\n".join(lines)
|
||||
|
||||
func _timeline_plain() -> String:
|
||||
@@ -76,8 +90,8 @@ func _zoom(delta: float) -> void:
|
||||
CCSService.adjust_text_scale(delta); _apply_text_scale(); _render_references()
|
||||
|
||||
func _apply_text_scale() -> void:
|
||||
var size := int(round(18.0 * CCSService.text_scale))
|
||||
for label in [score_body, timeline_body]: label.add_theme_font_size_override("normal_font_size", size); label.add_theme_font_size_override("bold_font_size", size)
|
||||
var font_size := int(round(18.0 * CCSService.text_scale))
|
||||
for label in [score_body, timeline_body]: label.add_theme_font_size_override("normal_font_size", font_size); label.add_theme_font_size_override("bold_font_size", font_size)
|
||||
|
||||
func _clear_highlights() -> void:
|
||||
CCSService.clear_highlights(str(current_attempt.get("id", ""))); _render_text()
|
||||
@@ -85,4 +99,18 @@ func _clear_highlights() -> void:
|
||||
func _report() -> void: report_dialog.show_for(str(current_attempt.get("id", "")))
|
||||
func _report_submitted(_id: String) -> void: Style.style_chip(report_button, Style.RED); report_button.tooltip_text = "Report submitted; report another issue"
|
||||
func _refresh_loaded_attempt() -> void:
|
||||
if visible and str(CCSService.attempt.get("state", "")) == "completed": on_show(CCSService.attempt)
|
||||
if not visible: return
|
||||
if starting_case and str(CCSService.attempt.get("state", "")) in ["active", "final_orders"]:
|
||||
starting_case = false; main.show_screen("ccs_player"); return
|
||||
if str(CCSService.attempt.get("state", "")) == "completed": on_show(CCSService.attempt)
|
||||
|
||||
func _another_case() -> void:
|
||||
if starting_case: return
|
||||
starting_case = true
|
||||
status.text = "Loading a random timed case..."
|
||||
CCSService.create_attempt("", "timed")
|
||||
|
||||
func _request_failed(message: String) -> void:
|
||||
if not visible or not starting_case: return
|
||||
starting_case = false
|
||||
status.text = message
|
||||
|
||||
+7
-3
@@ -317,11 +317,11 @@ func _toggle_app_menu() -> void:
|
||||
app_menu.visible = true
|
||||
app_menu.size = app_menu.custom_minimum_size
|
||||
app_menu.position = (get_viewport_rect().size - app_menu.size) * 0.5
|
||||
menu_exit_button.visible = current_screen_name == "player" and BlockSessionService.block_started
|
||||
menu_exit_button.visible = (current_screen_name == "player" and BlockSessionService.block_started) or current_screen_name == "ccs_player"
|
||||
if menu_exit_button.visible:
|
||||
menu_exit_button.text = "Leave (timer continues)" if BlockSessionService.session_kind == "simulation" else "Exit Run"
|
||||
menu_exit_button.text = "Leave Case" if current_screen_name == "ccs_player" else "Leave (timer continues)" if BlockSessionService.session_kind == "simulation" else "Exit Run"
|
||||
Style.style_chip(menu_exit_button, Style.AMBER)
|
||||
BlockSessionService.pause_current_question_timer()
|
||||
if current_screen_name == "player": BlockSessionService.pause_current_question_timer()
|
||||
|
||||
|
||||
func _close_app_menu() -> void:
|
||||
@@ -332,6 +332,10 @@ func _close_app_menu() -> void:
|
||||
|
||||
func _exit_run() -> void:
|
||||
_close_app_menu()
|
||||
if current_screen_name == "ccs_player":
|
||||
var ccs_player = screens.get("ccs_player")
|
||||
if ccs_player != null and ccs_player.has_method("leave_case"): ccs_player.leave_case()
|
||||
return
|
||||
if BlockSessionService.block_started and BlockSessionService.session_kind != "simulation":
|
||||
BlockSessionService.finish_block()
|
||||
elif BlockSessionService.session_kind == "simulation":
|
||||
|
||||
@@ -12,6 +12,7 @@ var outcome_consent: CheckBox
|
||||
var outcome_status: Label
|
||||
var outcome_delete_button: Button
|
||||
var outcome_grid: GridContainer
|
||||
var ccs_rating_label: Label
|
||||
|
||||
func _ready() -> void:
|
||||
set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
@@ -29,6 +30,9 @@ func _ready() -> void:
|
||||
content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
content.add_theme_constant_override("separation", 14)
|
||||
scroll.add_child(content)
|
||||
ccs_rating_label = Label.new()
|
||||
Style.set_label(ccs_rating_label, 20, Style.CYAN)
|
||||
content.add_child(ccs_rating_label)
|
||||
|
||||
var panel := PanelContainer.new()
|
||||
panel.add_theme_stylebox_override("panel", Style.panel_box(Color("172337"), Color("405475")))
|
||||
@@ -80,12 +84,15 @@ func _ready() -> void:
|
||||
ExamSimulationService.outcome_changed.connect(_refresh_outcome)
|
||||
if not ExamSimulationService.request_failed.is_connected(_on_outcome_request_failed):
|
||||
ExamSimulationService.request_failed.connect(_on_outcome_request_failed)
|
||||
if not CCSService.rating_changed.is_connected(_refresh_ccs_rating):
|
||||
CCSService.rating_changed.connect(_refresh_ccs_rating)
|
||||
resized.connect(_refresh_outcome_layout)
|
||||
_refresh_outcome_layout()
|
||||
|
||||
|
||||
func on_show(_payload: Variant = null) -> void:
|
||||
_reset_qbank_confirmation()
|
||||
_refresh_ccs_rating()
|
||||
var profile := ProfileService.get_profile()
|
||||
var text := "[b]Overall ELO %d[/b]\n%d rated questions\nCurrent streak: %d\nBest streak: %d\nToday: %d/%d questions\n\n[b]Category ELO[/b]\n" % [
|
||||
QuestionBankService.overall_elo,
|
||||
@@ -123,10 +130,15 @@ func on_show(_payload: Variant = null) -> void:
|
||||
block.get("total", 0)
|
||||
]
|
||||
profile_text.text = text
|
||||
CCSService.load_rating()
|
||||
outcome_status.text = "Loading official outcome..."
|
||||
ExamSimulationService.load_outcome()
|
||||
|
||||
|
||||
func _refresh_ccs_rating() -> void:
|
||||
ccs_rating_label.text = "CCS ELO %d | %d rated cases" % [CCSService.rating.get("elo", 1000), CCSService.rating.get("ratedCaseCount", 0)]
|
||||
|
||||
|
||||
func _build_outcome_panel(parent: VBoxContainer) -> void:
|
||||
var panel := PanelContainer.new()
|
||||
panel.add_theme_stylebox_override("panel", Style.panel_box(Color("172337"), Color("405475")))
|
||||
|
||||
Reference in New Issue
Block a user