diff options
| author | dam <dam@gudinoff> | 2022-04-18 09:06:19 +0000 |
|---|---|---|
| committer | dam <dam@gudinoff> | 2022-04-18 09:06:19 +0000 |
| commit | 79691f93bab7aa093bb606bfb80d2b4b236ee091 (patch) | |
| tree | 6e90c6601893895d7640f478f0cad402cc7590b4 /logic/stage.gd | |
| parent | 697e1ba3c4cb0a96c4584f1553de368d46287ab7 (diff) | |
| parent | ee31a9a3d387121030a5f4503adeac5816d7726f (diff) | |
| download | surgery-log-1.0.tar.zst surgery-log-1.0.zip | |
Merge godot branch.v1.0
Diffstat (limited to 'logic/stage.gd')
| -rw-r--r-- | logic/stage.gd | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/logic/stage.gd b/logic/stage.gd new file mode 100644 index 0000000..e6d9474 --- /dev/null +++ b/logic/stage.gd @@ -0,0 +1,294 @@ +extends TouchVerticalContainer +class_name Stage + +signal save # (database_entry: Dictionary) +signal discard # () + +const OPTION_SETS_FILE_PATH: String = "user://option_sets.json" +const OPTION_SETS_FILE_VERSION: String = "SL_OS_V1" +const OPTION_SETS_NOT_AVAILABLE: String = "--" +const OPTION_SETS_TREE_STRUCTURE := { + "place": null, + "anesthesia": null, + "first_assistant": null, + "type": { + "sub_type": { + "sub_sub_type": null, + "pathology": null, + "intervention": null, + } + } +} + +var staged_entry_hash: int +var option_sets: Dictionary + +onready var title := get_node("controls/title") as Label +onready var process_id := get_node("controls/process_id") as LineEdit +onready var surgery_id := get_node("controls/surgery_id") as LineEdit +onready var date := get_node("controls/date_picker") as DatePicker +onready var place := get_node("controls/place") as OptionSet +onready var anesthesia := get_node("controls/anesthesia") as OptionSet +onready var first_assistant := get_node("controls/first_assistant") as OptionSet +onready var type := get_node("controls/type") as OptionSet +onready var sub_type := get_node("controls/sub_type") as OptionSet +onready var sub_sub_type := get_node("controls/sub_sub_type") as OptionSet +onready var pathology := get_node("controls/pathology") as OptionSet +onready var intervention := get_node("controls/intervention") as OptionSet +onready var is_urgency := get_node("controls/is_urgency") as Button +onready var notes := get_node("controls/notes") as LineEdit +onready var save_button := get_node("controls/buttons/save") as Button +onready var discard_button := get_node("controls/buttons/discard") as Button +onready var scrollbar := get_v_scrollbar() +onready var popup := get_node("/root/main/popup") as ModalPopup +onready var dialog := get_node("/root/main/dialog") as Dialog + + +func _init(): + exclude_controls = ["date_picker", "save", "discard"] + load_option_sets() + + +func _ready(): + # Fix height of buttons container. + (get_node("controls/buttons") as Control).rect_min_size.y = save_button.rect_size.y + + scrollbar.connect("visibility_changed", self, "adjust_layout") + save_button.connect("pressed", self, "save_action") + discard_button.connect("pressed", self, "discard_action") + + for it in controls.get_children(): + if it is LineEdit: + it.connect("focus_entered", it, "set", ["caret_position", it.max_length]) + it.connect("focus_exited", it, "deselect") + + # Map option sets buttons. + var option_sets_map := { + "place": place, + "anesthesia": anesthesia, + "first_assistant": first_assistant, + "type": type, + "sub_type": sub_type, + "sub_sub_type": sub_sub_type, + "pathology": pathology, + "intervention": intervention + } + for key in option_sets_map: + var button := option_sets_map[key].get_node("button") as Button + button.connect("pressed", self, "show_options", [key]) + + +func adjust_layout(): + var margin_size := scrollbar.rect_size.x + self.margin_left = margin_size + self.margin_top = margin_size + self.margin_bottom = -margin_size + self.margin_right = 0.0 if scrollbar.visible else -margin_size + + +func show_options(field: String): + var option_set_field := self[field] as OptionSet + var options: Array + + match field: + "sub_type": + options = option_sets.get("type", {}).get(type.text, {}).get(field, {}).keys() + "sub_sub_type", "pathology", "intervention": + options = option_sets.get("type", {}).get(type.text, {}).get("sub_type", {}).get(sub_type.text, {}).get(field, {}).keys() + _: + options = option_sets.get(field, {}).keys() + + options.sort() + option_set_field.show_options(options) + + +func save_action(): + self.visible = false + var staged_entry := get_stage() + gather_option_sets(staged_entry, option_sets) + save_option_sets() + emit_signal("save", staged_entry) + + +func discard_action(): + if get_stage().hash() != staged_entry_hash: + dialog.setup("Changes made to this entry will be discarded.", "Discard", "No") + dialog.connect("accepted", self, "discard_action_confirmed") + popup.open_popup("Discard changes?", dialog) + else: + discard_action_confirmed() + + +func discard_action_confirmed(): + self.visible = false + emit_signal("discard") + + +func set_stage(entry: Dictionary, title: String): + self.title.text = title + staged_entry_hash = entry.hash() + process_id.text = entry.process_id + surgery_id.text = entry.surgery_id + date.set_date(entry.date_year, entry.date_month, entry.date_day) + place.text = entry.place + anesthesia.text = entry.anesthesia + first_assistant.text = entry.first_assistant + type.text = entry.type + sub_type.text = entry.sub_type + sub_sub_type.text = entry.sub_sub_type + pathology.text = entry.pathology + intervention.text = entry.intervention + is_urgency.pressed = entry.is_urgency + notes.text = entry.notes + self.scroll_vertical = 0 + + +func get_stage() -> Dictionary: + var entry := { + "process_id": process_id.text, + "surgery_id": surgery_id.text, + "date_year": date.get_year(), + "date_month": date.get_month(), + "date_day": date.get_day(), + "place": place.text, + "anesthesia": anesthesia.text, + "first_assistant": first_assistant.text, + "type": type.text, + "sub_type": sub_type.text, + "sub_sub_type": sub_sub_type.text, + "pathology": pathology.text, + "intervention": intervention.text, + "is_urgency": is_urgency.pressed, + "notes": notes.text, + } + return entry + + +static func sanitize_option_sets(entry: Dictionary, blueprint: Dictionary = OPTION_SETS_TREE_STRUCTURE): + # Delete extra keys. + var keys_to_delete: Array + for key in entry: + if blueprint.has(key) == false: + keys_to_delete.append(key) + for key in keys_to_delete: + entry.erase(key) + + for key in blueprint: + # Add missing keys. + if typeof(entry.get(key)) != TYPE_DICTIONARY: + entry[key] = {} + # Process sub-keys + if blueprint[key] != null: + for sub_key in entry[key]: + if typeof(entry[key][sub_key]) != TYPE_DICTIONARY: + entry[key][sub_key] = {} + sanitize_option_sets(entry[key][sub_key], blueprint[key]) + + +static func gather_option_sets(entry: Dictionary, target: Dictionary, blueprint: Dictionary = OPTION_SETS_TREE_STRUCTURE): + for key in blueprint: + if target.get(key) == null: + target[key] = {} + + var value := (entry[key] as String).strip_edges() + if value == "" || value == OPTION_SETS_NOT_AVAILABLE: + continue + + if target[key].has(value) == false: + target[key][value] = null if blueprint[key] == null else {} + + if blueprint[key] != null: + gather_option_sets(entry, target[key][value], blueprint[key]) + + +func save_option_sets(file_path: String = OPTION_SETS_FILE_PATH): + match file_path.get_extension(): + "json": + export_json(file_path, option_sets) + +# "csv": +# export_csv(file_path, option_sets) + + _: + printerr("Invalid option sets file extension '%s', expected 'json'." % file_path.get_file()) + return + + +static func export_json(file_path: String, data: Dictionary): + var option_sets_file := { + "version": OPTION_SETS_FILE_VERSION, + "option_sets": data, + } + var indentation_char := "" if file_path == OPTION_SETS_FILE_PATH else "\t" + var file_content := JSON.print(option_sets_file, indentation_char) + + var file := File.new() + file.open(file_path, File.WRITE) + file.store_string(file_content) + file.close() + + +#static func export_csv(file_path: String, data: Dictionary): +# pass + + +func load_option_sets(file_path: String = OPTION_SETS_FILE_PATH): + match file_path.get_extension(): + "json": + clear_option_sets() + option_sets = import_json(file_path) + if file_path != OPTION_SETS_FILE_PATH: + sanitize_option_sets(option_sets) + + "csv": + clear_option_sets() + option_sets = import_csv(file_path) + + _: + printerr("Invalid option sets file extension '%s', expected 'json' or 'csv'." % file_path.get_file()) + return + + +static func import_json(file_path: String) -> Dictionary: + var result := {} + + var file := File.new() + var error := file.open(file_path, File.READ_WRITE) + if error != OK: + printerr("Failed to open option sets file '%s' (error %d)." % [file_path, error]) + return result + var file_content = file.get_as_text() + file.close() + var parse_result = JSON.parse(file_content) + + if parse_result.error != OK: + printerr("Failed to parse option sets file '%s' (error %d)." % [file_path, parse_result.error]) + return result + + if typeof(parse_result.result) != TYPE_DICTIONARY: + printerr("Invalid option sets file type '%s', expected '%s'." % [typeof(parse_result.result), TYPE_DICTIONARY]) + return result + + if parse_result.result["version"] != OPTION_SETS_FILE_VERSION: + printerr("Invalid option sets file version '%s', expected '%s'." % [parse_result.result["version"], OPTION_SETS_FILE_VERSION]) + return result + + if typeof(parse_result.result["option_sets"]) != TYPE_DICTIONARY: + printerr("Invalid option sets content type '%s', expected '%s'." % [typeof(parse_result.result["option_sets"]), TYPE_DICTIONARY]) + return result + + result = parse_result.result["option_sets"] + return result + + +static func import_csv(file_path: String) -> Dictionary: + var result := {} + for it in Database.import_csv(file_path): + gather_option_sets(it, result) + return result + + +func clear_option_sets(): + option_sets = {} + + |
