Finish data rewrite

This commit is contained in:
Fabian 2021-11-28 13:19:42 +01:00
parent ceb7dbf3fd
commit e9fb29aa1b
2 changed files with 99 additions and 94 deletions

View File

@ -144,9 +144,7 @@ class Tasks:
class LogCommentsData: class LogCommentsData:
_log_key = "log" _init_day = {"log": [], "comments": {}}
_comments_key = "comments"
_init_day = {_log_key: [], _comments_key: {}}
# Data.__setitem__ only gets triggered, when there is a direct assignment (in contrast to assignment down the tree) # Data.__setitem__ only gets triggered, when there is a direct assignment (in contrast to assignment down the tree)
# this necessitates reassigning the whole month, when triggering a save is intended # this necessitates reassigning the whole month, when triggering a save is intended
@ -160,12 +158,11 @@ class LogCommentsData:
return return
if month_str not in self._data: if month_str not in self._data:
return return
day_str = pdate.strftime("%d")
for day, log in self._data[month_str].items(): for day, log in self._data[month_str].items():
if type(log) is list: if type(log) is list:
self._data[month_str][day] = { self._data[month_str][day] = {
self._log_key: log, "log": log,
self._comments_key: {} "comments": {}
} }
self._converted.add(month_str) self._converted.add(month_str)
@ -177,7 +174,7 @@ class LogCommentsData:
return [] return []
if day_str not in self._data[month_str]: if day_str not in self._data[month_str]:
return [] return []
log_data = self._data[month_str][day_str][self._log_key] log_data = self._data[month_str][day_str]["log"]
ret = [] ret = []
for entry in log_data: for entry in log_data:
tstr, b64str = entry.split() tstr, b64str = entry.split()
@ -186,10 +183,7 @@ class LogCommentsData:
ret.append((start_time, task)) ret.append((start_time, task))
return ret return ret
def set_log(self, log: List[Tuple[datetime, str]]): def set_log(self, pdate: date, log: List[Tuple[datetime, str]]):
if not log:
return
pdate = log[0][0].date()
self._ensure_format(pdate) self._ensure_format(pdate)
encoded = [] encoded = []
for entry in log: for entry in log:
@ -199,8 +193,10 @@ class LogCommentsData:
month_str = pdate.strftime("%Y-%m") month_str = pdate.strftime("%Y-%m")
day_str = pdate.strftime("%d") day_str = pdate.strftime("%d")
month = self._data.setdefault(month_str, {}) month = self._data.setdefault(month_str, {})
month.setdefault(day_str, self._init_day)[self._log_key] = encoded month.setdefault(day_str, self._init_day)["log"] = encoded
self._data[month_str] = month # trigger save if necessary
if self._data[month_str] != month:
self._data[month_str] = month
def add_log_entry(self, task: str): def add_log_entry(self, task: str):
now = datetime.now() now = datetime.now()
@ -211,9 +207,30 @@ class LogCommentsData:
month_str = now.strftime("%Y-%m") month_str = now.strftime("%Y-%m")
day_str = now.strftime("%d") day_str = now.strftime("%d")
month = self._data.setdefault(month_str, {}) month = self._data.setdefault(month_str, {})
month.setdefault(day_str, self._init_day)[self._log_key].append(encoded) month.setdefault(day_str, self._init_day)["log"].append(encoded)
self._data[month_str] = month self._data[month_str] = month
def get_prev_next_avail(self, pdate: date) -> Tuple[date, date]:
prev = None
next = None
for i in range(1, 32):
new_date = pdate - timedelta(days=i)
if new_date.strftime("%Y-%m") not in self._data:
break
if new_date.strftime("%d") in self._data[new_date.strftime("%Y-%m")]:
prev = new_date
break
for i in range(1, 32):
new_date = pdate + timedelta(days=i)
if new_date > date.today():
break
if new_date.strftime("%Y-%m") not in self._data:
break
if new_date.strftime("%d") in self._data[new_date.strftime("%Y-%m")]:
next = new_date
break
return prev, next
def get_comments(self, pdate: date) -> Dict[str, str]: def get_comments(self, pdate: date) -> Dict[str, str]:
self._ensure_format(pdate) self._ensure_format(pdate)
month_str = pdate.strftime("%Y-%m") month_str = pdate.strftime("%Y-%m")
@ -222,7 +239,7 @@ class LogCommentsData:
return dict() return dict()
if day_str not in self._data[month_str]: if day_str not in self._data[month_str]:
return dict() return dict()
comment_data = self._data[month_str][day_str][self._comments_key] comment_data = self._data[month_str][day_str]["comments"]
ret = dict() ret = dict()
for k, v in comment_data: for k, v in comment_data:
k_dec = base64.b64decode(k.encode("utf-8")).decode("utf-8") k_dec = base64.b64decode(k.encode("utf-8")).decode("utf-8")
@ -240,8 +257,10 @@ class LogCommentsData:
month_str = pdate.strftime("%Y-%m") month_str = pdate.strftime("%Y-%m")
day_str = pdate.strftime("%d") day_str = pdate.strftime("%d")
month = self._data.setdefault(month_str, {}) month = self._data.setdefault(month_str, {})
month.setdefault(day_str, self._init_day)[self._comments_key] = encoded month.setdefault(day_str, self._init_day)["comments"] = encoded
self._data[month_str] = month # trigger save if necessary
if self._data[month_str] != month:
self._data[month_str] = month
class Log: class Log:
@ -260,114 +279,94 @@ class Log:
log = self._data.get_log(date.today()) log = self._data.get_log(date.today())
if not log: if not log:
return None return None
if log[-1] == "End": if log[-1][1] == "End":
del log[-1] del log[-1]
self._data.set_log(log) self._data.set_log(date.today(), log)
if not log: if not log:
return None return None
return log[-1] return log[-1][1]
def report(self): def report(self):
return Report(self._data, date.today()) return Report(self._data)
def worklog(self): def worklog(self):
return Worklog(self._data) return Worklog(self._data)
class Worklog: def summary(lcd: LogCommentsData, pdate: date) -> Tuple[Dict[str, timedelta], timedelta]:
def __init__(self, data): log = lcd.get_log(pdate)
self._data = data if pdate == date.today():
log.append((datetime.now(), "End"))
tasks_sums = {}
total_sum = timedelta()
for i, le in enumerate(log):
start_time, task = le
if i < len(log) - 1:
end_time = log[i+1][0]
duration = end_time - start_time
if task != "Pause":
task_sum = tasks_sums.setdefault(task, timedelta())
task_sum += duration
tasks_sums[task] = task_sum
total_sum += duration
return tasks_sums, total_sum
def duration_to_str(duration: timedelta) -> str:
dhours, rem = divmod(duration.seconds, 3600)
dmins, _ = divmod(rem, 60)
return f"{dhours:02d}:{dmins:02d}"
class Report: class Report:
def __init__(self, data, pdate): def __init__(self, data: LogCommentsData):
self._data = data self._data = data
self._date = pdate self._date = date.today()
self._sum_len = 0 self._not_log_len = 0
self._prev = None self._prev = None
self._next = None self._next = None
self._update_prev_next() self._update_prev_next()
def report(self): def report(self) -> Tuple[List[List[str]], int]:
tmp = [] log = self._data.get_log(self._date)
if self._date.strftime("%Y-%m") in self._data \
and self._date.strftime("%d") in self._data[self._date.strftime("%Y-%m")]:
for e in self._data[self._date.strftime("%Y-%m")][self._date.strftime("%d")]:
tstr, b64str = e.split()
task = base64.b64decode(b64str.encode("utf-8")).decode("utf-8")
start_time = datetime.combine(self._date, datetime.strptime(tstr, "%H:%M").time())
tmp.append((task, start_time))
if self._date == date.today(): if self._date == date.today():
tmp.append(("End", datetime.now())) log.append((datetime.now(), "End"))
ret = [] ret = []
tasks_sums = {} for i, t in enumerate(log):
total_sum = timedelta() start_time, task = t
for i, t in enumerate(tmp): if i < len(log) - 1:
task, start_time = t end_time = log[i+1][0]
if i < len(tmp) - 1:
end_time = tmp[i+1][1]
duration = end_time - start_time duration = end_time - start_time
if task != "Pause": ret.append([task, start_time.strftime("%H:%M"), duration_to_str(duration)])
task_sum = tasks_sums.setdefault(task, timedelta())
task_sum += duration
tasks_sums[task] = task_sum
total_sum += duration
dhours, rem = divmod(duration.seconds, 3600)
dmins, _ = divmod(rem, 60)
ret.append([task, start_time.strftime("%H:%M"), f"{dhours:02d}:{dmins:02d}"])
else: else:
ret.append([task, start_time.strftime("%H:%M"), ""]) ret.append([task, start_time.strftime("%H:%M"), ""])
ret.append(["", "", ""]) ret.append(["", "", ""])
ret.append(["", "Sums", ""]) ret.append(["", "Sums", ""])
for k, v in tasks_sums.items():
dhours, rem = divmod(v.seconds, 3600) tasks_summary, total_sum = summary(self._data, self._date)
dmins, _ = divmod(rem, 60) for task, duration in tasks_summary.items():
ret.append([k, "", f"{dhours:02d}:{dmins:02d}"]) ret.append([task, "", duration_to_str(duration)])
dhours, rem = divmod(total_sum.seconds, 3600) ret.append(["Total sum", "", duration_to_str(total_sum)])
dmins, _ = divmod(rem, 60) self._not_log_len = 3 + len(tasks_summary)
ret.append(["Total sum", "", f"{dhours:02d}:{dmins:02d}"])
self._sum_len = 3 + len(tasks_sums)
if self._date == date.today(): if self._date == date.today():
self._sum_len += 1 self._not_log_len += 1
return ret, len(ret) - (4 + len(tasks_sums)) editable_len = len(ret) - (4 + len(tasks_summary))
return ret, editable_len
def save(self, report): def save(self, report):
report = report[:-self._sum_len] report = report[:-self._not_log_len]
if not report: if not report:
return return
save_list = [] report = list(map(
for tstr, ttime, _ in report: lambda x: (datetime.combine(self._date, datetime.strptime(x[1], "%H:%M").time()), x[0]),
b64str = base64.b64encode(tstr.encode("utf-8")).decode("utf-8") report
save_string = f"{ttime} {b64str}" ))
save_list.append(save_string) self._data.set_log(self._date, report)
# month dance necessary to trigger Data.__setitem__
month = self._data[self._date.strftime("%Y-%m")]
if month[self._date.strftime("%d")] == save_list: # no changes
return
month[self._date.strftime("%d")] = save_list
self._data[self._date.strftime("%Y-%m")] = month
def _update_prev_next(self): def _update_prev_next(self):
self._prev = None self._prev, self._next = self._data.get_prev_next_avail(self._date)
self._next = None
for i in range(1, 32):
new_date = self._date - timedelta(days=i)
if new_date.strftime("%Y-%m") not in self._data:
break
if new_date.strftime("%d") in self._data[new_date.strftime("%Y-%m")]:
self._prev = new_date
break
for i in range(1, 32):
new_date = self._date + timedelta(days=i)
if new_date > date.today():
break
if new_date.strftime("%Y-%m") not in self._data:
break
if new_date.strftime("%d") in self._data[new_date.strftime("%Y-%m")]:
self._next = new_date
break
def prev_next_avail(self): def prev_next_avail(self):
return self._prev is not None, self._next is not None return self._prev is not None, self._next is not None
@ -382,3 +381,8 @@ class Report:
def date(self): def date(self):
return self._date.strftime("%Y-%m-%d") return self._date.strftime("%Y-%m-%d")
class Worklog:
def __init__(self, data: LogCommentsData):
self._data = data

View File

@ -14,7 +14,7 @@ except ImportError:
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
import fime.icons import fime.icons
from fime.data import Tasks, Log, Data from fime.data import Tasks, Log, Data, LogCommentsData
from fime.exceptions import FimeException from fime.exceptions import FimeException
from fime.import_task import ImportTask from fime.import_task import ImportTask
from fime.report import Report from fime.report import Report
@ -29,7 +29,8 @@ class App:
data = Data() data = Data()
self.tasks = Tasks(data) self.tasks = Tasks(data)
self.log = Log(data) lcd = LogCommentsData(data)
self.log = Log(lcd)
self._active_task = self.log.last_log() or "Nothing" self._active_task = self.log.last_log() or "Nothing"
self.config = Config() self.config = Config()