extends ItemList class_name Database const DATABASE_FILE_PATH: String = "user://database.csv" 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, "anesthetic": "", "first_assistant": "", "type": "", "sub_type": "", "sub_sub_type": "", "pathology": "", "intervention": "", "is_urgency": false, "notes": "", } var db: Array var selected_idx: int var staged_idx: int onready var stage: Stage = get_node("/root/main/stage") onready var delete_button: Button = get_node("actions/delete") onready var edit_button: Button = get_node("actions/edit") onready var add_button: Button = get_node("actions/add") onready var v_scroll_bar: ScrollBar = get_v_scroll() # @DAM Stop scroll inertia when buttons are pressed. onready var drag_sensor: PointerInputSensor = get_node("drag_sensor") func _init(): selected_idx = -1 staged_idx = -1 load_database() store_database() # @DAM Only for testing. func _ready(): self.connect("item_selected", self, "item_selected") self.connect("nothing_selected", self, "clear_selection") delete_button.connect("pressed", self, "delete_action") edit_button.connect("pressed", self, "edit_action") add_button.connect("pressed", self, "add_action") stage.connect("save", self, "save") stage.connect("discard", self, "discard") drag_sensor.connect("on_press", self, "on_press") drag_sensor.connect("on_drag", self, "on_drag") drag_sensor.connect("on_end_drag", self, "on_end_drag") drag_sensor.connect("on_click", self, "on_click") for it in db: self.add_item(get_entry_view(it)) # @DAM Cleanup this code. export var scroll_velocity_decaying_factor: float = 2.5 var drag_velocity := 0.0 var is_dragging := false func _process(delta: float): if is_dragging == false && abs(drag_velocity) > 0.5: drag_velocity *= clamp((1.0 - scroll_velocity_decaying_factor * delta), 0.0, 1.0) v_scroll_bar.value -= drag_velocity * delta func on_click(pointer: PointerInputSensor.PointerInputData): var target := self var position := target.get_global_mouse_position() - target.rect_global_position var event_touch := InputEventScreenTouch.new() event_touch.index = 0 event_touch.position = position var event_mouse := InputEventMouseButton.new() event_mouse.button_index = BUTTON_LEFT event_mouse.button_mask = BUTTON_MASK_LEFT event_mouse.position = position event_mouse.pressed = true event_touch.pressed = true target._gui_input(event_mouse) target._gui_input(event_touch) target.grab_focus() event_mouse.pressed = false event_touch.pressed = false target._gui_input(event_mouse) target._gui_input(event_touch) func on_press(pointer: PointerInputSensor.PointerInputData): is_dragging = true func on_end_drag(pointer: PointerInputSensor.PointerInputData): is_dragging = false func on_drag(pointer: PointerInputSensor.PointerInputData): is_dragging = true drag_velocity = pointer.velocity.y v_scroll_bar.value -= pointer.relative_position.y func _notification(what: int): if visible == false: return if what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST: if selected_idx >= 0: clear_selection() else: get_tree().quit() func get_entry_view(database_entry: Dictionary) -> String: return "%6s | %6s | %s" % [database_entry.process_id, database_entry.surgery_id, get_entry_date(database_entry)] func item_selected(index: int): if selected_idx >= 0: set_item_text(selected_idx, get_entry_view(db[selected_idx])) selected_idx = index set_item_text(selected_idx, "> " + get_entry_view(db[selected_idx]) + " <") func clear_selection(): if selected_idx >= 0: set_item_text(selected_idx, get_entry_view(db[selected_idx])) selected_idx = -1 unselect_all() func delete_action(): if selected_idx < 0: return db.remove(selected_idx) self.remove_item(selected_idx) selected_idx = -1 store_database() func edit_action(): if selected_idx < 0: return staged_idx = selected_idx self.visible = false stage.visible = true stage.set_stage(db[staged_idx]) func add_action(): self.visible = false stage.visible = true var staged := instance_entry() stage.set_stage(staged) func scroll_down(): v_scroll_bar.value = v_scroll_bar.max_value func save(database_entry: Dictionary): database_entry = instance_entry(database_entry) # @DAM Maybe we could not be creating endless dictionaries? var next_selected_idx: int if staged_idx >= 0: db[staged_idx] = database_entry next_selected_idx = staged_idx else: db.append(database_entry) add_item(get_entry_view(database_entry)) call_deferred("scroll_down") next_selected_idx = db.size() - 1 store_database() select(next_selected_idx) item_selected(next_selected_idx) # Calling "select" does not trigger the "item_selected" signal. v_scroll_bar.value = float(next_selected_idx)/float(db.size()) * v_scroll_bar.max_value - (v_scroll_bar.page * 0.5) staged_idx = -1 self.visible = true func discard(): staged_idx = -1 self.visible = true 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 # while database_file.eof_reached() == false: # @DAM Why this? 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 = ENTRY_PROTOTYPE.duplicate(true) for idx in headers.size(): var field_name := headers[idx] var field_value := csv_entry[idx] match field_name: "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) fake_database() return db = [ instance_entry({ "process_id": "000001", "surgery_id": "100000", "place": "central", "date": "2020-01-31", }), instance_entry({ "process_id": "000002", "surgery_id": "200000", "place": "central", "date": "2020-02-31", }), instance_entry({ "process_id": "000003", "surgery_id": "300000", "place": "central", "date": "2020-03-31", }), instance_entry({ "process_id": "000004", "surgery_id": "400000", "place": "central", "date": "2020-04-31", }), instance_entry({ "process_id": "000012", "surgery_id": "210000", "place": "central", "date": "2020-12-31", }), ] func store_database(file_path: String = DATABASE_FILE_PATH): var file := File.new() file.open(file_path, File.WRITE) var header := PoolStringArray(ENTRY_PROTOTYPE.keys()) file.store_csv_line(header) var entry := PoolStringArray() for it in db: file.store_csv_line(it.values()) file.close() 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.anesthetic = params.get("anesthetic", "") 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)) func fake_database(): db.resize(0) for idx in range(500): var date_year = 1 + int(float(idx) / 365.0) var date_month = idx % 12 var date_day = idx % 365 db.append(instance_entry({ "process_id": "%06d" % idx, "surgery_id": "s%05d" % idx, "date": "%04d-%02d-%02d" % [date_year, date_month, date_day] }))