aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--logic/database.gd72
-rw-r--r--logic/stage.gd41
-rw-r--r--menu/menu.gd66
-rw-r--r--readme.md4
4 files changed, 122 insertions, 61 deletions
diff --git a/logic/database.gd b/logic/database.gd
index 2258f55..91328c5 100644
--- a/logic/database.gd
+++ b/logic/database.gd
@@ -1,7 +1,8 @@
extends TouchItemList
class_name Database
-const DATABASE_FILE_PATH: String = "user://database.csv"
+const DATABASE_FILE_PATH: String = "user://database.json"
+const DATABASE_FILE_VERSION: int = 1
var db: Array
var selected_idx: int
@@ -84,7 +85,7 @@ func delete_action_confirmed():
db.remove(selected_idx)
self.remove_item(selected_idx)
selected_idx = -1
- store_database()
+ save_database()
clear_selection()
@@ -125,7 +126,7 @@ func save_stage(entry: Dictionary):
set_item_text(selected_idx, get_entry_view(db[selected_idx]))
ensure_current_is_visible()
- store_database()
+ save_database()
staged_idx = -1
self.visible = true
@@ -141,49 +142,84 @@ func discard_stage():
func load_database(file_path: String = DATABASE_FILE_PATH):
var file := File.new()
file.open(file_path, File.READ_WRITE)
- var headers: PoolStringArray
- var is_first_line := true
+ var file_content = file.get_as_text()
+ file.close()
+ 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
+
+ if parse_result.result["version"] != DATABASE_FILE_VERSION:
+ push_error("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"]
+
+
+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)
+
+ var file := File.new()
+ file.open(file_path, File.WRITE)
+ file.store_string(file_content)
+ file.close()
+
+
+static func import_database(file_path: String) -> Array:
+ var result: Array
+ var file := File.new()
+ file.open(file_path, File.READ_WRITE)
+ var headers := file.get_csv_line()
while file.get_position() < file.get_len():
var csv_entry := file.get_csv_line()
- if is_first_line:
- is_first_line = false
- headers = csv_entry
- continue
var entry = DatabaseEntry.instance_entry()
for idx in headers.size():
var field_name := headers[idx]
var field_value := csv_entry[idx]
match field_name:
+ "date":
+ DatabaseEntry.set_entry_date(entry, field_value)
"date_year", "date_month", "date_day":
entry[field_name] = int(field_value)
"is_urgency":
entry[field_name] = true if field_value.strip_edges().to_lower() == "true" else false
_:
- entry[field_name] = field_value
- db.append(entry)
+ if DatabaseEntry.ENTRY_PROTOTYPE.has(field_name):
+ entry[field_name] = field_value
+ if DatabaseEntry.is_valid_entry(entry):
+ result.append(entry)
file.close()
+ return result
-func store_database(file_path: String = DATABASE_FILE_PATH):
+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 db:
+ 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(save_changes: bool = false):
+func clear_database():
clear_selection()
self.clear()
db.resize(0)
- if save_changes:
- store_database()
-func fake_database(save_changes: bool = false):
+func fake_database():
clear_database()
for idx in range(500):
var today := OS.get_date(true)
@@ -199,7 +235,5 @@ func fake_database(save_changes: bool = false):
})
db.append(fake_entry)
self.add_item(get_entry_view(fake_entry))
- if save_changes:
- store_database()
diff --git a/logic/stage.gd b/logic/stage.gd
index 892dee4..c0583b0 100644
--- a/logic/stage.gd
+++ b/logic/stage.gd
@@ -5,6 +5,7 @@ signal save # (database_entry: Dictionary)
signal discard # ()
const OPTION_SETS_FILE_PATH: String = "user://option_sets.json"
+const OPTION_SETS_FILE_VERSION: int = 1
const OPTION_SETS_NOT_AVAILABLE: String = "--"
const OPTION_SETS_TREE_STRUCTURE := {
"place": null,
@@ -93,7 +94,7 @@ func save_action():
self.visible = false
var staged_entry := get_stage()
gather_option_sets(staged_entry)
- store_option_sets()
+ save_option_sets()
emit_signal("save", staged_entry)
@@ -188,35 +189,43 @@ func gather_option_sets(entry: Dictionary, target: Dictionary = option_sets, blu
gather_option_sets(entry, target[key][value], blueprint[key])
-func import_option_sets(file_path: String = OPTION_SETS_FILE_PATH):
- load_option_sets(file_path)
- sanitize_option_sets(option_sets)
- store_option_sets()
-
-
func load_option_sets(file_path: String = OPTION_SETS_FILE_PATH):
var file := File.new()
file.open(file_path, File.READ_WRITE)
var file_content = file.get_as_text()
file.close()
var parse_result = JSON.parse(file_content)
- if parse_result.error == OK && typeof(parse_result.result) == TYPE_DICTIONARY:
- option_sets = parse_result.result
- else:
- option_sets = {}
+
+ if parse_result.error != OK || typeof(parse_result.result) != TYPE_DICTIONARY:
push_error("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
+
+ if typeof(parse_result.result["option_sets"]) != TYPE_DICTIONARY:
+ push_error("Failed to load option sets file contents.")
+ return
+
+ option_sets = parse_result.result["option_sets"]
-func store_option_sets(file_path: String = OPTION_SETS_FILE_PATH):
+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(JSON.print(option_sets, "" if file_path == OPTION_SETS_FILE_PATH else "\t"))
+ file.store_string(file_content)
file.close()
-func clear_option_sets(save_changes: bool = false):
+func clear_option_sets():
option_sets = {}
- if save_changes:
- store_option_sets()
diff --git a/menu/menu.gd b/menu/menu.gd
index 5847d5b..9a5bdbb 100644
--- a/menu/menu.gd
+++ b/menu/menu.gd
@@ -1,13 +1,13 @@
extends MenuButton
const menu_items: Array = [
- { label = "IMPORT OPTION SETS", action = "_menu_import_option_sets_action" },
- { label = "EXPORT OPTION SETS", action = "_menu_export_option_sets_action" },
- { label = "CLEAR OPTION SETS", action = "_menu_clear_option_sets_action" },
- { label = "EXPORT DATA", action = "_menu_export_data_action" },
- { label = "CLEAR DATA", action = "_menu_clear_data_action" },
- { label = "ABOUT", action = "_menu_about_action" },
- { label = "FAKE_DB", action = "_menu_fake_db_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 = "EXPORT DATA", action = "export_data_action" },
+ { label = "CLEAR DATA", action = "clear_data_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"
const license_godot: String = "res://licenses/godot.txt"
@@ -29,64 +29,82 @@ func id_pressed(id: int):
self.call_deferred(menu_items[id].action)
-func _menu_import_option_sets_action():
+func import_option_sets_action():
file_picker.window_title = "IMPORT OPTION SETS"
file_picker.mode = FileDialog.MODE_OPEN_FILE
file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS)
- file_picker.filters[0] = "*.json"
+ file_picker.filters = ["*.json", "*.csv"]
file_picker.current_file = ""
- file_picker.connect("file_selected", stage, "import_option_sets", [true], CONNECT_ONESHOT)
+ file_picker.connect("file_selected", self, "import_option_sets_action_confirmed", [], CONNECT_ONESHOT)
file_picker.show_modal(true)
file_picker.invalidate()
-func _menu_export_option_sets_action():
+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.window_title = "EXPORT OPTION SETS"
file_picker.mode = FileDialog.MODE_SAVE_FILE
file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS)
- file_picker.filters[0] = "*.json"
+ file_picker.filters = ["*.json"]
file_picker.current_file = ""
- file_picker.connect("file_selected", stage, "store_option_sets", [], CONNECT_ONESHOT)
+ file_picker.connect("file_selected", stage, "save_option_sets", [], CONNECT_ONESHOT)
file_picker.show_modal(true)
file_picker.invalidate()
-func _menu_clear_option_sets_action():
+func clear_option_sets_action():
confirm_action.window_title = "CLEAR OPTION SETS"
confirm_action.dialog_text = "Do you want to delete all option sets?"
- confirm_action.connect("confirmed", stage, "clear_option_sets", [true], CONNECT_ONESHOT)
+ confirm_action.connect("confirmed", stage, "clear_option_sets", [], CONNECT_ONESHOT)
confirm_action.show_modal(true)
-func _menu_export_data_action():
+func export_data_action():
file_picker.window_title = "EXPORT DATA"
file_picker.mode = FileDialog.MODE_SAVE_FILE
file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS)
- file_picker.filters[0] = "*.csv"
+ file_picker.filters = ["*.csv"]
file_picker.current_file = ""
- file_picker.connect("file_selected", database, "store_database", [], CONNECT_ONESHOT)
+ file_picker.connect("file_selected", database, "save_database", [], CONNECT_ONESHOT)
file_picker.show_modal(true)
file_picker.invalidate()
-func _menu_clear_data_action():
+func clear_data_action():
confirm_action.window_title = "CLEAR DATA"
confirm_action.dialog_text = "Do you want to delete all entries from the database?"
- confirm_action.connect("confirmed", database, "clear_database", [true], CONNECT_ONESHOT)
+ confirm_action.connect("confirmed", database, "clear_database", [], CONNECT_ONESHOT)
confirm_action.show_modal(true)
-func _menu_about_action():
+func about_action():
confirm_action.window_title = "ABOUT"
confirm_action.dialog_text = "About text here!"
confirm_action.show_modal(true)
# @DAM Hide this debug method before release.
-func _menu_fake_db_action():
- confirm_action.window_title = "FAKE DB"
+func test_fake_db_action():
+ confirm_action.window_title = "TEST FAKE DB"
confirm_action.dialog_text = "Do you want to delete all entries from the database and replace by fake entries?"
- confirm_action.connect("confirmed", database, "fake_database", [true], CONNECT_ONESHOT)
+ confirm_action.connect("confirmed", database, "fake_database", [], CONNECT_ONESHOT)
confirm_action.show_modal(true)
diff --git a/readme.md b/readme.md
index bc01851..371eee6 100644
--- a/readme.md
+++ b/readme.md
@@ -46,7 +46,8 @@ Surgery Log
- [x] The stage control must be set to ignore the mouse, otherwise the touch-sensor conflicts with the built-in scroll;
- [x] On database, selecting an entry and removing it will leave the action buttons visible while no entry is selected;
- [x] Tweak 'POINTER_VELOCITY_DECAYING_FACTOR' and 'POINTER_VELOCITY_BOOST_FACTOR' on database and stage screens;
-- [ ] Allow to parse option sets from database file;
+- [x] Allow to parse option sets from database file;
+- [x] Check if import_option_sets, store_option_sets, store_database require the parameter save_changes; this requires changes on databse, stage and menu scripts;
- [ ] Fix back button:
- on stage screen should show pop-up asking it changes are to be discarded;
- on file-pickers screen should close them;
@@ -55,7 +56,6 @@ Surgery Log
- on database screen, should deselect selected item, otherwise should quit the app;
- [ ] Hide dialogs title bar (appear in the top with 1 or 2 pixels height);
- [ ] Database menu and action buttons are not nice; Improve appearance;
-- [ ] Check if import_option_sets, store_option_sets, store_database require the parameter save_changes; this requires changes on databse, stage and menu scripts;
- [ ] Improve menu appearance;
- [ ] Setup two themes:
- [ ] theme_light