aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dialog/dialog.gd58
-rw-r--r--logic/database.gd145
-rw-r--r--logic/database_entry.gd6
-rw-r--r--logic/popup.gd12
-rw-r--r--logic/stage.gd85
-rw-r--r--menu/menu.gd63
-rw-r--r--touch_item_list/touch_item_list.gd2
7 files changed, 231 insertions, 140 deletions
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