Finish data rewrite
This commit is contained in:
parent
ceb7dbf3fd
commit
e9fb29aa1b
186
src/fime/data.py
186
src/fime/data.py
@ -144,9 +144,7 @@ class Tasks:
|
||||
|
||||
|
||||
class LogCommentsData:
|
||||
_log_key = "log"
|
||||
_comments_key = "comments"
|
||||
_init_day = {_log_key: [], _comments_key: {}}
|
||||
_init_day = {"log": [], "comments": {}}
|
||||
|
||||
# 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
|
||||
@ -160,12 +158,11 @@ class LogCommentsData:
|
||||
return
|
||||
if month_str not in self._data:
|
||||
return
|
||||
day_str = pdate.strftime("%d")
|
||||
for day, log in self._data[month_str].items():
|
||||
if type(log) is list:
|
||||
self._data[month_str][day] = {
|
||||
self._log_key: log,
|
||||
self._comments_key: {}
|
||||
"log": log,
|
||||
"comments": {}
|
||||
}
|
||||
self._converted.add(month_str)
|
||||
|
||||
@ -177,7 +174,7 @@ class LogCommentsData:
|
||||
return []
|
||||
if day_str not in self._data[month_str]:
|
||||
return []
|
||||
log_data = self._data[month_str][day_str][self._log_key]
|
||||
log_data = self._data[month_str][day_str]["log"]
|
||||
ret = []
|
||||
for entry in log_data:
|
||||
tstr, b64str = entry.split()
|
||||
@ -186,10 +183,7 @@ class LogCommentsData:
|
||||
ret.append((start_time, task))
|
||||
return ret
|
||||
|
||||
def set_log(self, log: List[Tuple[datetime, str]]):
|
||||
if not log:
|
||||
return
|
||||
pdate = log[0][0].date()
|
||||
def set_log(self, pdate: date, log: List[Tuple[datetime, str]]):
|
||||
self._ensure_format(pdate)
|
||||
encoded = []
|
||||
for entry in log:
|
||||
@ -199,7 +193,9 @@ class LogCommentsData:
|
||||
month_str = pdate.strftime("%Y-%m")
|
||||
day_str = pdate.strftime("%d")
|
||||
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
|
||||
# trigger save if necessary
|
||||
if self._data[month_str] != month:
|
||||
self._data[month_str] = month
|
||||
|
||||
def add_log_entry(self, task: str):
|
||||
@ -211,9 +207,30 @@ class LogCommentsData:
|
||||
month_str = now.strftime("%Y-%m")
|
||||
day_str = now.strftime("%d")
|
||||
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
|
||||
|
||||
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]:
|
||||
self._ensure_format(pdate)
|
||||
month_str = pdate.strftime("%Y-%m")
|
||||
@ -222,7 +239,7 @@ class LogCommentsData:
|
||||
return dict()
|
||||
if day_str not in self._data[month_str]:
|
||||
return dict()
|
||||
comment_data = self._data[month_str][day_str][self._comments_key]
|
||||
comment_data = self._data[month_str][day_str]["comments"]
|
||||
ret = dict()
|
||||
for k, v in comment_data:
|
||||
k_dec = base64.b64decode(k.encode("utf-8")).decode("utf-8")
|
||||
@ -240,7 +257,9 @@ class LogCommentsData:
|
||||
month_str = pdate.strftime("%Y-%m")
|
||||
day_str = pdate.strftime("%d")
|
||||
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
|
||||
# trigger save if necessary
|
||||
if self._data[month_str] != month:
|
||||
self._data[month_str] = month
|
||||
|
||||
|
||||
@ -260,114 +279,94 @@ class Log:
|
||||
log = self._data.get_log(date.today())
|
||||
if not log:
|
||||
return None
|
||||
if log[-1] == "End":
|
||||
if log[-1][1] == "End":
|
||||
del log[-1]
|
||||
self._data.set_log(log)
|
||||
self._data.set_log(date.today(), log)
|
||||
if not log:
|
||||
return None
|
||||
return log[-1]
|
||||
return log[-1][1]
|
||||
|
||||
def report(self):
|
||||
return Report(self._data, date.today())
|
||||
return Report(self._data)
|
||||
|
||||
def worklog(self):
|
||||
return Worklog(self._data)
|
||||
|
||||
|
||||
class Worklog:
|
||||
def __init__(self, data):
|
||||
self._data = data
|
||||
|
||||
|
||||
class Report:
|
||||
def __init__(self, data, pdate):
|
||||
self._data = data
|
||||
self._date = pdate
|
||||
self._sum_len = 0
|
||||
self._prev = None
|
||||
self._next = None
|
||||
self._update_prev_next()
|
||||
|
||||
def report(self):
|
||||
tmp = []
|
||||
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():
|
||||
tmp.append(("End", datetime.now()))
|
||||
|
||||
ret = []
|
||||
def summary(lcd: LogCommentsData, pdate: date) -> Tuple[Dict[str, timedelta], timedelta]:
|
||||
log = lcd.get_log(pdate)
|
||||
if pdate == date.today():
|
||||
log.append((datetime.now(), "End"))
|
||||
tasks_sums = {}
|
||||
total_sum = timedelta()
|
||||
for i, t in enumerate(tmp):
|
||||
task, start_time = t
|
||||
if i < len(tmp) - 1:
|
||||
end_time = tmp[i+1][1]
|
||||
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)
|
||||
ret.append([task, start_time.strftime("%H:%M"), f"{dhours:02d}:{dmins:02d}"])
|
||||
return f"{dhours:02d}:{dmins:02d}"
|
||||
|
||||
|
||||
class Report:
|
||||
def __init__(self, data: LogCommentsData):
|
||||
self._data = data
|
||||
self._date = date.today()
|
||||
self._not_log_len = 0
|
||||
self._prev = None
|
||||
self._next = None
|
||||
self._update_prev_next()
|
||||
|
||||
def report(self) -> Tuple[List[List[str]], int]:
|
||||
log = self._data.get_log(self._date)
|
||||
if self._date == date.today():
|
||||
log.append((datetime.now(), "End"))
|
||||
ret = []
|
||||
for i, t in enumerate(log):
|
||||
start_time, task = t
|
||||
if i < len(log) - 1:
|
||||
end_time = log[i+1][0]
|
||||
duration = end_time - start_time
|
||||
ret.append([task, start_time.strftime("%H:%M"), duration_to_str(duration)])
|
||||
else:
|
||||
ret.append([task, start_time.strftime("%H:%M"), ""])
|
||||
|
||||
ret.append(["", "", ""])
|
||||
ret.append(["", "Sums", ""])
|
||||
for k, v in tasks_sums.items():
|
||||
dhours, rem = divmod(v.seconds, 3600)
|
||||
dmins, _ = divmod(rem, 60)
|
||||
ret.append([k, "", f"{dhours:02d}:{dmins:02d}"])
|
||||
dhours, rem = divmod(total_sum.seconds, 3600)
|
||||
dmins, _ = divmod(rem, 60)
|
||||
ret.append(["Total sum", "", f"{dhours:02d}:{dmins:02d}"])
|
||||
self._sum_len = 3 + len(tasks_sums)
|
||||
|
||||
tasks_summary, total_sum = summary(self._data, self._date)
|
||||
for task, duration in tasks_summary.items():
|
||||
ret.append([task, "", duration_to_str(duration)])
|
||||
ret.append(["Total sum", "", duration_to_str(total_sum)])
|
||||
self._not_log_len = 3 + len(tasks_summary)
|
||||
if self._date == date.today():
|
||||
self._sum_len += 1
|
||||
return ret, len(ret) - (4 + len(tasks_sums))
|
||||
self._not_log_len += 1
|
||||
editable_len = len(ret) - (4 + len(tasks_summary))
|
||||
|
||||
return ret, editable_len
|
||||
|
||||
def save(self, report):
|
||||
report = report[:-self._sum_len]
|
||||
report = report[:-self._not_log_len]
|
||||
if not report:
|
||||
return
|
||||
save_list = []
|
||||
for tstr, ttime, _ in report:
|
||||
b64str = base64.b64encode(tstr.encode("utf-8")).decode("utf-8")
|
||||
save_string = f"{ttime} {b64str}"
|
||||
save_list.append(save_string)
|
||||
# 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
|
||||
report = list(map(
|
||||
lambda x: (datetime.combine(self._date, datetime.strptime(x[1], "%H:%M").time()), x[0]),
|
||||
report
|
||||
))
|
||||
self._data.set_log(self._date, report)
|
||||
|
||||
def _update_prev_next(self):
|
||||
self._prev = None
|
||||
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
|
||||
self._prev, self._next = self._data.get_prev_next_avail(self._date)
|
||||
|
||||
def prev_next_avail(self):
|
||||
return self._prev is not None, self._next is not None
|
||||
@ -382,3 +381,8 @@ class Report:
|
||||
|
||||
def date(self):
|
||||
return self._date.strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
class Worklog:
|
||||
def __init__(self, data: LogCommentsData):
|
||||
self._data = data
|
||||
|
@ -14,7 +14,7 @@ except ImportError:
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
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.import_task import ImportTask
|
||||
from fime.report import Report
|
||||
@ -29,7 +29,8 @@ class App:
|
||||
|
||||
data = 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.config = Config()
|
||||
|
Loading…
Reference in New Issue
Block a user