From 0eeacf3d142b40427ea5ca5b2806fe714c00f9b4 Mon Sep 17 00:00:00 2001 From: dam Date: Thu, 6 Jan 2022 11:39:06 +0000 Subject: Isolate database entry code to avoid cyclic references. --- logic/database_entry.gd | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 logic/database_entry.gd (limited to 'logic/database_entry.gd') diff --git a/logic/database_entry.gd b/logic/database_entry.gd new file mode 100644 index 0000000..19f2255 --- /dev/null +++ b/logic/database_entry.gd @@ -0,0 +1,64 @@ +extends Reference +class_name DatabaseEntry + +const DATE_SEPARATOR: String = "-" +const DATE_FORMAT: String = "%04d-%02d-%02d" +const ENTRY_PROTOTYPE: Dictionary = { + "process_id": "", + "surgery_id": "", + "place": "", + "date": "", + "date_year": 0, + "date_month": 0, + "date_day": 0, + "anesthesia": "", + "first_assistant": "", + "type": "", + "sub_type": "", + "sub_sub_type": "", + "pathology": "", + "intervention": "", + "is_urgency": false, + "notes": "", +} + + +static func instance_entry(params: Dictionary = {}) -> Dictionary: + var new_entry := ENTRY_PROTOTYPE.duplicate(true) + new_entry.process_id = params.get("process_id", "") + new_entry.surgery_id = params.get("surgery_id", "") + new_entry.place = params.get("place", "") + + var today = OS.get_date() + new_entry.date_year = params.get("date_year", today.year) + new_entry.date_month = params.get("date_month", today.month) + new_entry.date_day = params.get("date_day", today.day) + new_entry.date = params.get("date", get_entry_date(new_entry)) # @DAM We should store only one version of the date. + + new_entry.anesthesia = params.get("anesthesia", "") + new_entry.first_assistant = params.get("first_assistant", "") + new_entry.type = params.get("type", "") + new_entry.sub_type = params.get("sub_type", "") + new_entry.sub_sub_type = params.get("sub_sub_type", "") + new_entry.pathology = params.get("pathology", "") + new_entry.intervention = params.get("intervention", "") + new_entry.is_urgency = params.get("is_urgency", false) + new_entry.notes = params.get("notes", "") + + return new_entry + + +static func get_entry_date(entry: Dictionary) -> String: + return DATE_FORMAT % [entry.date_year, entry.date_month, entry.date_day] + + +static func set_entry_date(entry: Dictionary, date: String): + date = date.strip_edges().replace(" ", DATE_SEPARATOR).replace("/", DATE_SEPARATOR).replace("\\", DATE_SEPARATOR) + var year_month_idx := date.find(DATE_SEPARATOR) + var month_day_idx := date.find(DATE_SEPARATOR, year_month_idx + 1) + entry.date = date + entry.date_year = int(date.substr(0, year_month_idx)) + entry.date_month = int(date.substr(year_month_idx + 1, month_day_idx - year_month_idx - 1)) + entry.date_day = int(date.substr(month_day_idx + 1)) + + -- cgit v1.2.3 From 431f042390ad36297a5ec986772c77da23b7fb67 Mon Sep 17 00:00:00 2001 From: dam Date: Thu, 6 Jan 2022 15:42:44 +0000 Subject: Remove date field and reduce usage of dictionary instances. --- logic/database.gd | 17 +++++++------ logic/database_entry.gd | 20 +++++++++++---- logic/stage.gd | 67 ++++++++++++++++++++++++------------------------- 3 files changed, 58 insertions(+), 46 deletions(-) (limited to 'logic/database_entry.gd') diff --git a/logic/database.gd b/logic/database.gd index 551b9bf..044681f 100644 --- a/logic/database.gd +++ b/logic/database.gd @@ -77,6 +77,10 @@ func clear_selection(): unselect_all() +func scroll_to_selected(): + v_scroll_bar.value = float(selected_idx)/float(db.size()) * v_scroll_bar.max_value - (v_scroll_bar.page * 0.5) + + func delete_action(): if selected_idx < 0: return @@ -104,12 +108,11 @@ func add_action(): stage.set_stage(staged) -func scroll_down(): - v_scroll_bar.value = v_scroll_bar.max_value - - func save_stage(entry: Dictionary): - entry = DatabaseEntry.instance_entry(entry) # @DAM Maybe we could not be creating endless dictionaries? + if DatabaseEntry.is_valid_entry(entry) == false: + printerr("INVALID ENTRY RECEIVED") + return # @DAM Deal with this. + var next_selected_idx: int if staged_idx >= 0: db[staged_idx] = entry @@ -117,13 +120,12 @@ func save_stage(entry: Dictionary): else: db.append(entry) add_item(get_entry_view(entry)) - call_deferred("scroll_down") next_selected_idx = db.size() - 1 select(next_selected_idx) emit_signal("item_selected", next_selected_idx) # Calling "select" does not trigger the "item_selected" signal. set_item_text(selected_idx, get_entry_view(db[selected_idx])) - v_scroll_bar.value = float(selected_idx)/float(db.size()) * v_scroll_bar.max_value - (v_scroll_bar.page * 0.5) + call_deferred("scroll_to_selected") store_database() @@ -171,6 +173,7 @@ func store_database(file_path: String = DATABASE_FILE_PATH): file.store_csv_line(header) var entry := PoolStringArray() for it in db: + # @DAM This approach depends on the order the dictionary fields are created. file.store_csv_line(it.values()) file.close() diff --git a/logic/database_entry.gd b/logic/database_entry.gd index 19f2255..496b752 100644 --- a/logic/database_entry.gd +++ b/logic/database_entry.gd @@ -6,11 +6,10 @@ const DATE_FORMAT: String = "%04d-%02d-%02d" const ENTRY_PROTOTYPE: Dictionary = { "process_id": "", "surgery_id": "", - "place": "", - "date": "", "date_year": 0, "date_month": 0, "date_day": 0, + "place": "", "anesthesia": "", "first_assistant": "", "type": "", @@ -27,14 +26,13 @@ static func instance_entry(params: Dictionary = {}) -> Dictionary: var new_entry := ENTRY_PROTOTYPE.duplicate(true) new_entry.process_id = params.get("process_id", "") new_entry.surgery_id = params.get("surgery_id", "") - new_entry.place = params.get("place", "") var today = OS.get_date() new_entry.date_year = params.get("date_year", today.year) new_entry.date_month = params.get("date_month", today.month) new_entry.date_day = params.get("date_day", today.day) - new_entry.date = params.get("date", get_entry_date(new_entry)) # @DAM We should store only one version of the date. + new_entry.place = params.get("place", "") new_entry.anesthesia = params.get("anesthesia", "") new_entry.first_assistant = params.get("first_assistant", "") new_entry.type = params.get("type", "") @@ -48,6 +46,19 @@ static func instance_entry(params: Dictionary = {}) -> Dictionary: return new_entry +static func is_valid_entry(entry: Dictionary) -> bool: + var is_valid: bool + + is_valid = entry.has_all(ENTRY_PROTOTYPE.keys()) && ENTRY_PROTOTYPE.keys().size() == entry.keys().size() + + for it in ENTRY_PROTOTYPE.keys(): + if typeof(ENTRY_PROTOTYPE[it]) != typeof(entry[it]): + is_valid = false + break + + return is_valid + + static func get_entry_date(entry: Dictionary) -> String: return DATE_FORMAT % [entry.date_year, entry.date_month, entry.date_day] @@ -56,7 +67,6 @@ static func set_entry_date(entry: Dictionary, date: String): date = date.strip_edges().replace(" ", DATE_SEPARATOR).replace("/", DATE_SEPARATOR).replace("\\", DATE_SEPARATOR) var year_month_idx := date.find(DATE_SEPARATOR) var month_day_idx := date.find(DATE_SEPARATOR, year_month_idx + 1) - entry.date = date entry.date_year = int(date.substr(0, year_month_idx)) entry.date_month = int(date.substr(year_month_idx + 1, month_day_idx - year_month_idx - 1)) entry.date_day = int(date.substr(month_day_idx + 1)) diff --git a/logic/stage.gd b/logic/stage.gd index 511068e..f6f6e47 100644 --- a/logic/stage.gd +++ b/logic/stage.gd @@ -7,9 +7,10 @@ signal discard # () const FILTERS_FILE_PATH: String = "user://filters.csv" const POINTER_VELOCITY_DECAYING_FACTOR: float = 2.5 +var staged_entry := {} +var filters := {} var is_pointer_dragging := false var pointer_drag_velocity := 0.0 -var filters := {} onready var process_id := get_node("controls/process_id") as LineEdit onready var surgery_id := get_node("controls/surgery_id") as LineEdit @@ -79,42 +80,40 @@ func discard_action(): func set_stage(entry: Dictionary): - 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 # @DAM TODO + staged_entry = entry.duplicate(true) + process_id.text = staged_entry.process_id + surgery_id.text = staged_entry.surgery_id + date.set_date(staged_entry.date_year, staged_entry.date_month, staged_entry.date_day) + place.text = staged_entry.place + anesthesia.text = staged_entry.anesthesia + first_assistant.text = staged_entry.first_assistant + type.text = staged_entry.type + sub_type.text = staged_entry.sub_type + sub_sub_type.text = staged_entry.sub_sub_type + pathology.text = staged_entry.pathology + intervention.text = staged_entry.intervention + is_urgency.pressed = staged_entry.is_urgency + notes.text = staged_entry.notes + self.scroll_vertical = 0 func get_stage() -> Dictionary: - # @DAM Simplify all this... avoid creating multiple entries/dictionaries. - var entry: Dictionary = { - "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 + staged_entry.process_id = process_id.text + staged_entry.surgery_id = surgery_id.text + staged_entry.date_year = date.get_year() + staged_entry.date_month = date.get_month() + staged_entry.date_day = date.get_day() + staged_entry.place = place.text + staged_entry.anesthesia = anesthesia.text + staged_entry.first_assistant= first_assistant.text + staged_entry.type = type.text + staged_entry.sub_type = sub_type.text + staged_entry.sub_sub_type = sub_sub_type.text + staged_entry.pathology = pathology.text + staged_entry.intervention = intervention.text + staged_entry.is_urgency = is_urgency.pressed + staged_entry.notes = notes.text + return staged_entry func _notification(what: int): -- cgit v1.2.3 From 1fe674aec4bd6ce3fc666c8545dadb4f0614067d Mon Sep 17 00:00:00 2001 From: dam Date: Wed, 6 Apr 2022 22:52:14 +0000 Subject: Improved file operations for database and option sets. Added confirmation dialogs when importing database and option sets files. Standardized the error print calls. Fixed capitalization on menu entries. --- dialog/dialog.gd | 58 +++++++-------- logic/database.gd | 145 ++++++++++++++++++++++++------------- logic/database_entry.gd | 6 +- logic/popup.gd | 12 +-- logic/stage.gd | 85 ++++++++++++++++------ menu/menu.gd | 63 ++++++++-------- touch_item_list/touch_item_list.gd | 2 +- 7 files changed, 231 insertions(+), 140 deletions(-) (limited to 'logic/database_entry.gd') diff --git a/dialog/dialog.gd b/dialog/dialog.gd index a22a3a6..89fee5c 100644 --- a/dialog/dialog.gd +++ b/dialog/dialog.gd @@ -8,37 +8,37 @@ signal rejected # () export var clear_signals_on_hide := true var message : Label -var accept : Button -var reject : Button +var accept_button : Button +var reject_button : Button func _init(): - self.anchor_right = 1.0 - self.anchor_bottom = 1.0 - self.rect_clip_content = true + self.anchor_right = 1.0 + self.anchor_bottom = 1.0 + self.rect_clip_content = true self.connect("hide", self, "_clear_signals") - reject = Button.new() - reject.anchor_top = 1.0 - reject.anchor_left = 0.0 - reject.anchor_right = 0.5 - reject.margin_right = -5.0 - reject.rect_min_size.y = 62.0 - reject.grow_vertical = Control.GROW_DIRECTION_BEGIN - reject.name = "reject" - reject.connect("pressed", self, "_signal_rejected") - add_child(reject) + reject_button = Button.new() + reject_button.anchor_top = 1.0 + reject_button.anchor_left = 0.0 + reject_button.anchor_right = 0.5 + reject_button.margin_right = -5.0 + reject_button.rect_min_size.y = 62.0 + reject_button.grow_vertical = Control.GROW_DIRECTION_BEGIN + reject_button.name = "reject" + reject_button.connect("pressed", self, "_signal_rejected") + add_child(reject_button) - accept = Button.new() - accept.anchor_top = 1.0 - accept.anchor_left = 0.5 - accept.anchor_right = 1.0 - accept.margin_left = 5.0 - accept.rect_min_size.y = 62.0 - accept.grow_vertical = Control.GROW_DIRECTION_BEGIN - accept.name = "accept" - accept.connect("pressed", self, "_signal_accepted") - add_child(accept) + accept_button = Button.new() + accept_button.anchor_top = 1.0 + accept_button.anchor_left = 0.5 + accept_button.anchor_right = 1.0 + accept_button.margin_left = 5.0 + accept_button.rect_min_size.y = 62.0 + accept_button.grow_vertical = Control.GROW_DIRECTION_BEGIN + accept_button.name = "accept" + accept_button.connect("pressed", self, "_signal_accepted") + add_child(accept_button) message = Label.new() message.autowrap = true @@ -71,9 +71,9 @@ func _signal_accepted(): func setup(message: String, accept_label: String = "Accept", reject_label: String = "Reject"): self.message.text = message - accept.visible = accept_label != "" - accept.text = accept_label - reject.visible = reject_label != "" - reject.text = reject_label + accept_button.visible = accept_label != "" + accept_button.text = accept_label + reject_button.visible = reject_label != "" + reject_button.text = reject_label diff --git a/logic/database.gd b/logic/database.gd index 262a18a..8acf1b6 100644 --- a/logic/database.gd +++ b/logic/database.gd @@ -8,7 +8,7 @@ var db: Array var selected_idx: int var staged_idx: int -onready var stage := get_node("/root/main/stage") as Stage +onready var stage := get_node("/root/main/stage") # as Stage @DAM Commented to avoid cyclic dependency. onready var delete_button := get_node("actions/delete") as Button onready var edit_button := get_node("actions/edit") as Button onready var add_button := get_node("actions/add") as Button @@ -18,7 +18,6 @@ onready var dialog := get_node("/root/main/dialog") as Dialog func _init(): selected_idx = -1 staged_idx = -1 - load_database() func _ready(): @@ -32,10 +31,7 @@ func _ready(): stage.connect("save", self, "save_stage") stage.connect("discard", self, "discard_stage") - for it in db: - self.add_item(get_entry_view(it)) - - clear_selection() + load_database() func get_entry_view(database_entry: Dictionary) -> String: @@ -62,7 +58,7 @@ func delete_action(): if selected_idx < 0: return - dialog.setup("The entry with process ID '%s' will be deleted from the database." % db[selected_idx].process_id, "Delete", "No") + dialog.setup("The entry #%d with process ID '%s' will be deleted from the database." % [selected_idx+1, db[selected_idx].process_id], "Delete", "No") dialog.connect("accepted", self, "delete_action_confirmed") popup.open_popup("Delete entry?", dialog) @@ -95,8 +91,8 @@ func add_action(): func save_stage(entry: Dictionary): if DatabaseEntry.is_valid_entry(entry) == false: - printerr("INVALID ENTRY RECEIVED") - return # @DAM Deal with this. + printerr("Invalid entry detected.") + return var next_selected_idx: int if staged_idx >= 0: @@ -125,7 +121,89 @@ func discard_stage(): grab_focus() +func save_database(file_path: String = DATABASE_FILE_PATH): + match file_path.get_extension(): + "json": + export_json(file_path, db) + + "csv": + export_csv(file_path, db) + + _: + printerr("Invalid database file extension: '%s'." % file_path.get_file()) + return + + +static func export_json(file_path: String, data: Array): + var database_file := { + "version": DATABASE_FILE_VERSION, + "database": data, + } + var indentation_char := "" if file_path == DATABASE_FILE_PATH else "\t" + var file_content := JSON.print(database_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: Array): + var file := File.new() + file.open(file_path, File.WRITE) + var header := PoolStringArray(DatabaseEntry.ENTRY_PROTOTYPE.keys()) + file.store_csv_line(header) + for it in data: + file.store_csv_line(it.values()) + file.close() + + func load_database(file_path: String = DATABASE_FILE_PATH): + match file_path.get_extension(): + "json": + clear_database() + db = import_json(file_path) + if file_path != DATABASE_FILE_PATH: + sanitize_database(db) + + "csv": + clear_database() + db = import_csv(file_path) + + _: + printerr("Invalid database file extension: '%s'." % file_path.get_file()) + return + + for it in db: + # The JSON specification does not define integer or float types, but only a number type. + # Therefore, converting a Variant to JSON text will convert all numerical values to float types. + # Thus, we cast all integer values once we load them. + it["date_year"] = int(it["date_year"]) + it["date_month"] = int(it["date_month"]) + it["date_day"] = int(it["date_day"]) + + self.add_item(get_entry_view(it)) + + +static func sanitize_database(database: Array): + var blueprint := DatabaseEntry.ENTRY_PROTOTYPE + + for entry in database: + # 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) + + # Fix wrong value types. + for key in blueprint: + if entry.has(key) == false || typeof(entry[key]) != typeof(blueprint[key]): + entry[key] = blueprint[key] + + +static func import_json(file_path: String) -> Array: var file := File.new() file.open(file_path, File.READ_WRITE) var file_content = file.get_as_text() @@ -133,43 +211,21 @@ func load_database(file_path: String = DATABASE_FILE_PATH): var parse_result = JSON.parse(file_content) if parse_result.error != OK || typeof(parse_result.result) != TYPE_DICTIONARY: - push_error("Failed to parse database file: '%s'.") - return + printerr("Failed to parse database file: '%s'.") + return [] if parse_result.result["version"] != DATABASE_FILE_VERSION: - push_error("Invalid database file version '%s', expected '%s'." % DATABASE_FILE_VERSION) - return + printerr("Invalid database file version '%s', expected '%s'." % DATABASE_FILE_VERSION) + return [] if typeof(parse_result.result["database"]) != TYPE_ARRAY: - push_error("Failed to load database file contents.") - return - - db = parse_result.result["database"] - - # The JSON specification does not define integer or float types, but only a number type. - # Therefore, converting a Variant to JSON text will convert all numerical values to float types. - # Thus, we cast all integer values once we load them. - for it in db: - it.date_year = int(it.date_year) - it.date_month = int(it.date_month) - it.date_day = int(it.date_day) - - -func save_database(file_path: String = DATABASE_FILE_PATH): - var database_file := { - "version": DATABASE_FILE_VERSION, - "database": db, - } - var indentation_char := "" if file_path == DATABASE_FILE_PATH else "\t" - var file_content := JSON.print(database_file, indentation_char) + printerr("Failed to load database file contents.") + return [] - var file := File.new() - file.open(file_path, File.WRITE) - file.store_string(file_content) - file.close() + return parse_result.result["database"] -static func import_database(file_path: String) -> Array: +static func import_csv(file_path: String) -> Array: var result: Array var file := File.new() file.open(file_path, File.READ_WRITE) @@ -196,17 +252,6 @@ static func import_database(file_path: String) -> Array: return result -static func export_database(file_path: String, database: Array = db): - var file := File.new() - file.open(file_path, File.WRITE) - var header := PoolStringArray(DatabaseEntry.ENTRY_PROTOTYPE.keys()) - file.store_csv_line(header) - for it in database: - # @DAM This approach depends on the order the dictionary fields are created. - file.store_csv_line(it.values()) - file.close() - - func clear_database(): clear_selection() self.clear() diff --git a/logic/database_entry.gd b/logic/database_entry.gd index 496b752..8b0c51f 100644 --- a/logic/database_entry.gd +++ b/logic/database_entry.gd @@ -6,9 +6,9 @@ const DATE_FORMAT: String = "%04d-%02d-%02d" const ENTRY_PROTOTYPE: Dictionary = { "process_id": "", "surgery_id": "", - "date_year": 0, - "date_month": 0, - "date_day": 0, + "date_year": 1, + "date_month": 1, + "date_day": 1, "place": "", "anesthesia": "", "first_assistant": "", diff --git a/logic/popup.gd b/logic/popup.gd index d7c98f9..95fc2c3 100644 --- a/logic/popup.gd +++ b/logic/popup.gd @@ -5,12 +5,12 @@ signal dismissed # () export var clear_signals_on_hide := true -var control : Control -var control_parent : Node +var control : Control +var control_parent : Node -onready var title := get_node("title") as Label -onready var background := get_node("background") as Panel -onready var dismiss := get_node("dismiss") as Button +onready var title := get_node("title") as Label +onready var background := get_node("background") as Panel +onready var dismiss_button := get_node("dismiss") as Button func _init(): @@ -21,7 +21,7 @@ func _init(): func _ready(): - dismiss.connect("pressed", self, "dismiss") + dismiss_button.connect("pressed", self, "dismiss") func _clear_signals(): diff --git a/logic/stage.gd b/logic/stage.gd index e2248fa..764ee52 100644 --- a/logic/stage.gd +++ b/logic/stage.gd @@ -105,7 +105,7 @@ func show_options(field: String): func save_action(): self.visible = false var staged_entry := get_stage() - gather_option_sets(staged_entry) + gather_option_sets(staged_entry, option_sets) save_option_sets() emit_signal("save", staged_entry) @@ -164,7 +164,7 @@ func get_stage() -> Dictionary: return entry -func sanitize_option_sets(entry: Dictionary, blueprint: Dictionary = OPTION_SETS_TREE_STRUCTURE): +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: @@ -185,7 +185,7 @@ func sanitize_option_sets(entry: Dictionary, blueprint: Dictionary = OPTION_SETS sanitize_option_sets(entry[key][sub_key], blueprint[key]) -func gather_option_sets(entry: Dictionary, target: Dictionary = option_sets, blueprint: Dictionary = OPTION_SETS_TREE_STRUCTURE): +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] = {} @@ -201,7 +201,55 @@ func gather_option_sets(entry: Dictionary, target: Dictionary = option_sets, blu 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'." % 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'." % file_path.get_file()) + return + + +static func import_json(file_path: String) -> Dictionary: var file := File.new() file.open(file_path, File.READ_WRITE) var file_content = file.get_as_text() @@ -209,32 +257,25 @@ func load_option_sets(file_path: String = OPTION_SETS_FILE_PATH): var parse_result = JSON.parse(file_content) if parse_result.error != OK || typeof(parse_result.result) != TYPE_DICTIONARY: - push_error("Failed to parse option sets file: '%s'.") - return + printerr("Failed to parse option sets file: '%s'.") + return {} if parse_result.result["version"] != OPTION_SETS_FILE_VERSION: - push_error("Invalid option sets file version '%s', expected '%s'." % OPTION_SETS_FILE_VERSION) - return + printerr("Invalid option sets file version '%s', expected '%s'." % OPTION_SETS_FILE_VERSION) + return {} if typeof(parse_result.result["option_sets"]) != TYPE_DICTIONARY: - push_error("Failed to load option sets file contents.") - return + printerr("Failed to load option sets file contents.") + return {} - option_sets = parse_result.result["option_sets"] + return parse_result.result["option_sets"] -func save_option_sets(file_path: String = OPTION_SETS_FILE_PATH): - var option_sets_file := { - "version": OPTION_SETS_FILE_VERSION, - "option_sets": option_sets, - } - 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 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(): diff --git a/menu/menu.gd b/menu/menu.gd index 5f13949..735f86a 100644 --- a/menu/menu.gd +++ b/menu/menu.gd @@ -1,12 +1,13 @@ extends MenuButton const menu_items: Array = [ - { label = "IMPORT OPTION SETS", action = "import_option_sets_action" }, - { label = "EXPORT OPTION SETS", action = "export_option_sets_action" }, - { label = "CLEAR OPTION SETS", action = "clear_option_sets_action" }, - { label = "EXPORT DATA", action = "export_data_action" }, - { label = "CLEAR DATA", action = "clear_data_action" }, - { label = "ABOUT", action = "about_action" }, + { label = "Import Option Sets", action = "import_option_sets_action" }, + { label = "Export Option Sets", action = "export_option_sets_action" }, + { label = "Clear Option Sets", action = "clear_option_sets_action" }, + { label = "Import Database", action = "import_database_action" }, + { label = "Export Database", action = "export_database_action" }, + { label = "Clear Database", action = "clear_database_action" }, + { label = "About", action = "about_action" }, { label = "TEST_FAKE_DB", action = "test_fake_db_action" }, ] const license_font_b612: String = "res://licenses/font_b612.txt" @@ -31,39 +32,27 @@ func id_pressed(id: int): func import_option_sets_action(): + dialog.setup("All option sets from the dropdown menus will be replaced.", "Continue", "No") + dialog.connect("accepted", self, "import_option_sets_action_accepted") + popup.open_popup("Replace option sets?", dialog) + + +func import_option_sets_action_accepted(): file_picker.mode = FileDialog.MODE_OPEN_FILE file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) file_picker.filters = ["*.json", "*.csv"] file_picker.current_file = "" - file_picker.connect("file_selected", self, "import_option_sets_action_confirmed") + file_picker.connect("file_selected", stage, "load_option_sets") file_picker.show_modal(true) file_picker.invalidate() -func import_option_sets_action_confirmed(file_path: String): - match file_path.get_extension(): - "json": - stage.load_option_sets(file_path) - stage.sanitize_option_sets(stage.option_sets) - stage.save_option_sets() - - "csv": - var database := Database.import_database(file_path) - for it in database: - stage.gather_option_sets(it) - stage.save_option_sets() - - _: - push_error("Invalid file extension selected to be parsed for option sets: '%s'." % file_path.get_file()) - return - - func export_option_sets_action(): file_picker.mode = FileDialog.MODE_SAVE_FILE file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) file_picker.filters = ["*.json"] file_picker.current_file = "" - file_picker.connect("file_selected", stage, "save_option_sets", [], CONNECT_ONESHOT) + file_picker.connect("file_selected", stage, "save_option_sets") file_picker.show_modal(true) file_picker.invalidate() @@ -74,17 +63,33 @@ func clear_option_sets_action(): popup.open_popup("Clear option sets?", dialog) -func export_data_action(): +func import_database_action(): + dialog.setup("All entries from the database will be replaced.", "Continue", "No") + dialog.connect("accepted", self, "import_database_action_accepted") + popup.open_popup("Replace database?", dialog) + + +func import_database_action_accepted(): + file_picker.mode = FileDialog.MODE_OPEN_FILE + file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) + file_picker.filters = ["*.json", "*.csv"] + file_picker.current_file = "" + file_picker.connect("file_selected", database, "load_database") + file_picker.show_modal(true) + file_picker.invalidate() + + +func export_database_action(): file_picker.mode = FileDialog.MODE_SAVE_FILE file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) file_picker.filters = ["*.csv"] file_picker.current_file = "" - file_picker.connect("file_selected", database, "save_database", [], CONNECT_ONESHOT) + file_picker.connect("file_selected", database, "save_database") file_picker.show_modal(true) file_picker.invalidate() -func clear_data_action(): +func clear_database_action(): dialog.setup("All entries from the database will be deleted.", "Delete all", "No") dialog.connect("accepted", database, "clear_database") popup.open_popup("Clear database?", dialog) diff --git a/touch_item_list/touch_item_list.gd b/touch_item_list/touch_item_list.gd index 67f88d7..6794138 100644 --- a/touch_item_list/touch_item_list.gd +++ b/touch_item_list/touch_item_list.gd @@ -3,7 +3,7 @@ class_name TouchItemList const POINTER_VELOCITY_DECAYING_FACTOR: float = PI const POINTER_VELOCITY_BOOST_FACTOR: float = 1.25 -const EXACT_SELECTION: bool = false +const EXACT_SELECTION: bool = true var is_pointer_dragging := false var pointer_drag_velocity := 0.0 -- cgit v1.2.3