class_name TimeSheet var source_path := "" var entries := [] # warning-ignore:integer_division var _last_update := Time.get_ticks_msec() / 1000 ## Loads the data file func load_file() -> bool: var file := File.new() var success := file.open(source_path, File.READ) if success != OK: success = file.open(source_path, File.WRITE) if success != OK: printerr("Failed to open file %s"%[ProjectSettings.globalize_path(source_path)]) return false return true while not file.eof_reached(): var line := file.get_csv_line() if line.size() == 0 or "".join(line).length() == 0: continue if not TimeEntry.is_csv_line_valid(line): push_warning("CSV Line `%s` is not conform"%[",".join(line)]) continue var entry := TimeEntry.new().from_csv_line(line) entries.append(entry) file.close() return true func get_active_entry_from_name(task_name: String) -> TimeEntry: for _entry in entries: var current_time_entry := _entry as TimeEntry if current_time_entry.name == task_name and not current_time_entry.is_closed: return current_time_entry return null ## Adds a new time entry to the tree and to the data file func add_entry(entry_name: String) -> TimeEntry: var current_entry := TimeEntry.new().start_recording() current_entry.name = entry_name current_entry.is_closed = false var file := File.new() var success := file.open(source_path, File.READ_WRITE) if success != OK: printerr("Could not open file") return null file.seek_end() entries.append(current_entry) file.store_csv_line(current_entry.to_csv_line()) return current_entry func stop_entry(entry_name: String, do_save := true) -> bool: for _entry in entries: var current_time_entry := _entry as TimeEntry if current_time_entry.name == entry_name and not current_time_entry.is_closed: current_time_entry.close() if do_save: save() return true return false func toggle_entry(entry_name: String, do_save := true) -> void: if stop_entry(entry_name, do_save): return else: # warning-ignore:return_value_discarded add_entry(entry_name) func update() -> void: # warning-ignore:integer_division var current_time := Time.get_ticks_msec() / 1000 if current_time == _last_update: return _last_update = current_time for entry in entries: var time_entry := entry as TimeEntry if time_entry.is_closed == false: time_entry.update() func save() -> void: var file := File.new() var success := file.open(source_path, File.WRITE) if success != OK: printerr("Could not open file") return for time_entry in entries: file.store_csv_line(time_entry.to_csv_line()) func make_items_tree() -> TimeEntryTreeItem: var sorted_entries := EntrySorter.new(entries).sort_by([["name"], ["start_date", true]]).entries var tree := TimeEntryTreeItem.new() for entry_index in sorted_entries.size(): var entry := sorted_entries[entry_index] as TimeEntry var parts := entry.name.split("/") var repo: TimeEntryTreeItem = tree.get_child(parts, true) repo.append(entry) return tree static func restore(file_path: String) -> TimeSheet: var timesheet = load("res://scripts/time_sheet.gd").new() timesheet.source_path = file_path var success: bool = timesheet.load_file() if success: return timesheet return null class EntrySorter: var entries: Array var _sorters := PoolStringArray() func _init(initial_entries: Array) -> void: entries = initial_entries.duplicate() func by_name(reverse := false) -> EntrySorter: return sort_by_one("name", reverse) func by_date(reverse := false) -> EntrySorter: return sort_by_one("date", reverse) func sort_by_one(property: String, reverse := false) -> EntrySorter: var method_name := "_by_%s"%[property] assert(has_method(method_name), "%s is not a valid sorting property"%[property]) entries.sort_custom(self, method_name) if reverse: entries.invert() return self func sort_by(initial_sorters: Array) -> EntrySorter: for item in initial_sorters: var property = item[0] var reversed = item[1] if item.size() > 1 else false var method_name := "_by_%s"%[property] assert(has_method(method_name), "%s is not a valid sorting property"%[property]) assert(reversed == null or reversed is bool, "The second item is not a boolean") return self _sorters = initial_sorters entries.sort_custom(self, "__by_multiple") _sorters = PoolStringArray() return self func __by_multiple(a: TimeEntry, b: TimeEntry) -> bool: for item in _sorters: var property = item[0] var reversed = item[1] var method_name := "_by_%s"%[property] var result: bool = call(method_name, a, b) if reversed: result = not result if result == false: return false return true func _by_name(a: TimeEntry, b: TimeEntry) -> bool: return a.name < b.name func _by_date(a: TimeEntry, b: TimeEntry) -> bool: return a.start_time < b.start_time