aboutsummaryrefslogtreecommitdiff
path: root/logic/stage.gd
diff options
context:
space:
mode:
authordam <dam@gudinoff>2022-04-18 09:06:19 +0000
committerdam <dam@gudinoff>2022-04-18 09:06:19 +0000
commit79691f93bab7aa093bb606bfb80d2b4b236ee091 (patch)
tree6e90c6601893895d7640f478f0cad402cc7590b4 /logic/stage.gd
parent697e1ba3c4cb0a96c4584f1553de368d46287ab7 (diff)
parentee31a9a3d387121030a5f4503adeac5816d7726f (diff)
downloadsurgery-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.gd294
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 = {}
+
+