Portion of data.py rewrite

This commit is contained in:
Fabian 2021-11-28 01:17:30 +01:00
parent f390029899
commit ceb7dbf3fd

View File

@ -5,7 +5,7 @@ import os
from collections.abc import MutableMapping from collections.abc import MutableMapping
from datetime import datetime, date, timedelta from datetime import datetime, date, timedelta
from threading import Thread, Event from threading import Thread, Event
from typing import List from typing import List, Tuple, Dict
try: try:
from PySide6 import QtCore from PySide6 import QtCore
@ -16,66 +16,6 @@ save_delay = 3 * 60
max_jira_tasks = 50 max_jira_tasks = 50
class Tasks:
def __init__(self, data):
self._data = data
if "tasks" in self._data:
self._tasks = list(map(lambda x: base64.b64decode(x.encode("utf-8")).decode("utf-8"), self._data["tasks"]))
else:
self._tasks = []
if "jira_tasks" in self._data:
self._jira_tasks_usage = dict()
for k, v in self._data["jira_tasks"].items():
key = base64.b64decode(k.encode("utf-8")).decode("utf-8")
self._jira_tasks_usage[key] = datetime.fromisoformat(v)
self._jira_tasks = sorted(self._jira_tasks_usage.keys(), key=lambda x: self._jira_tasks_usage[x])
else:
self._jira_tasks_usage = dict()
self._jira_tasks = []
@property
def tasks(self) -> List[str]:
return self._tasks
@tasks.setter
def tasks(self, tasks):
self._tasks = tasks
encoded_tasks = list(map(lambda x: base64.b64encode(x.encode("utf-8")).decode("utf-8"), self._tasks))
self._data["tasks"] = encoded_tasks
@property
def jira_tasks(self):
return self._jira_tasks
def add_jira_task(self, task_name):
if task_name in self._jira_tasks_usage:
self._jira_tasks.remove(task_name) # move to end, to make visible again
self._jira_tasks.append(task_name)
self._jira_tasks_usage[task_name] = datetime.now()
if len(self._jira_tasks_usage) > max_jira_tasks:
sorted_tasks = sorted(self._jira_tasks_usage.keys(), key=lambda x: self._jira_tasks_usage[x])
overhang_tasks = sorted_tasks[:len(sorted_tasks) - max_jira_tasks]
for task in overhang_tasks:
del self._jira_tasks_usage[task]
self._save_jira_tasks()
def update_jira_task_usage(self, task_name):
if task_name in self._jira_tasks_usage:
self._jira_tasks_usage[task_name] = datetime.now()
self._save_jira_tasks()
def _save_jira_tasks(self):
serialized = dict()
for k, v in self._jira_tasks_usage.items():
key = base64.b64encode(k.encode("utf-8")).decode("utf-8")
serialized[key] = datetime.isoformat(v)
self._data["jira_tasks"] = serialized
@property
def all_tasks(self):
return self.tasks + self.jira_tasks
class Data(MutableMapping): class Data(MutableMapping):
def __init__(self): def __init__(self):
data_dir_path = QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppDataLocation) data_dir_path = QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppDataLocation)
@ -143,8 +83,169 @@ class Data(MutableMapping):
return f"{type(self).__name__}({self._cache})" return f"{type(self).__name__}({self._cache})"
class Tasks:
def __init__(self, data: Data):
self._data = data
if "tasks" in self._data:
self._tasks = list(map(lambda x: base64.b64decode(x.encode("utf-8")).decode("utf-8"), self._data["tasks"]))
else:
self._tasks = []
if "jira_tasks" in self._data:
self._jira_tasks_usage = dict()
for k, v in self._data["jira_tasks"].items():
key = base64.b64decode(k.encode("utf-8")).decode("utf-8")
self._jira_tasks_usage[key] = datetime.fromisoformat(v)
self._jira_tasks = sorted(self._jira_tasks_usage.keys(), key=lambda x: self._jira_tasks_usage[x])
else:
self._jira_tasks_usage = dict()
self._jira_tasks = []
@property
def tasks(self) -> List[str]:
return self._tasks
@tasks.setter
def tasks(self, tasks):
self._tasks = tasks
encoded_tasks = list(map(lambda x: base64.b64encode(x.encode("utf-8")).decode("utf-8"), self._tasks))
self._data["tasks"] = encoded_tasks
@property
def jira_tasks(self):
return self._jira_tasks
def add_jira_task(self, task_name):
if task_name in self._jira_tasks_usage:
self._jira_tasks.remove(task_name) # move to end, to make visible again
self._jira_tasks.append(task_name)
self._jira_tasks_usage[task_name] = datetime.now()
if len(self._jira_tasks_usage) > max_jira_tasks:
sorted_tasks = sorted(self._jira_tasks_usage.keys(), key=lambda x: self._jira_tasks_usage[x])
overhang_tasks = sorted_tasks[:len(sorted_tasks) - max_jira_tasks]
for task in overhang_tasks:
del self._jira_tasks_usage[task]
self._save_jira_tasks()
def update_jira_task_usage(self, task_name):
if task_name in self._jira_tasks_usage:
self._jira_tasks_usage[task_name] = datetime.now()
self._save_jira_tasks()
def _save_jira_tasks(self):
serialized = dict()
for k, v in self._jira_tasks_usage.items():
key = base64.b64encode(k.encode("utf-8")).decode("utf-8")
serialized[key] = datetime.isoformat(v)
self._data["jira_tasks"] = serialized
@property
def all_tasks(self):
return self.tasks + self.jira_tasks
class LogCommentsData:
_log_key = "log"
_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)
# this necessitates reassigning the whole month, when triggering a save is intended
def __init__(self, data: Data):
self._data = data
self._converted = set()
def _ensure_format(self, pdate: date):
month_str = pdate.strftime("%Y-%m")
if month_str in self._converted:
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: {}
}
self._converted.add(month_str)
def get_log(self, pdate: date) -> List[Tuple[datetime, str]]:
self._ensure_format(pdate)
month_str = pdate.strftime("%Y-%m")
day_str = pdate.strftime("%d")
if month_str not in self._data:
return []
if day_str not in self._data[month_str]:
return []
log_data = self._data[month_str][day_str][self._log_key]
ret = []
for entry in log_data:
tstr, b64str = entry.split()
start_time = datetime.combine(pdate, datetime.strptime(tstr, "%H:%M").time())
task = base64.b64decode(b64str.encode("utf-8")).decode("utf-8")
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()
self._ensure_format(pdate)
encoded = []
for entry in log:
tstr = entry[0].strftime("%H:%M")
b64str = base64.b64encode(entry[1].encode("utf-8")).decode("utf-8")
encoded.append(f"{tstr} {b64str}")
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
self._data[month_str] = month
def add_log_entry(self, task: str):
now = datetime.now()
self._ensure_format(now)
tstr = now.strftime("%H:%M")
b64str = base64.b64encode(task.encode("utf-8")).decode("utf-8")
encoded = f"{tstr} {b64str}"
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)
self._data[month_str] = month
def get_comments(self, pdate: date) -> Dict[str, str]:
self._ensure_format(pdate)
month_str = pdate.strftime("%Y-%m")
day_str = pdate.strftime("%d")
if month_str not in self._data:
return dict()
if day_str not in self._data[month_str]:
return dict()
comment_data = self._data[month_str][day_str][self._comments_key]
ret = dict()
for k, v in comment_data:
k_dec = base64.b64decode(k.encode("utf-8")).decode("utf-8")
v_dec = base64.b64decode(v.encode("utf-8")).decode("utf-8")
ret[k_dec] = v_dec
return ret
def set_comments(self, pdate: date, comments: Dict[str, str]):
self._ensure_format(pdate)
encoded = dict()
for k, v in comments:
k_enc = base64.b64encode(k.encode("utf-8")).decode("utf-8")
v_enc = base64.b64encode(v.encode("utf-8")).decode("utf-8")
encoded[k_enc] = v_enc
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
self._data[month_str] = month
class Log: class Log:
def __init__(self, data): def __init__(self, data: LogCommentsData):
self._data = data self._data = data
def cleanup(): def cleanup():
@ -152,41 +253,30 @@ class Log:
atexit.register(cleanup) atexit.register(cleanup)
def log(self, task, ptime=None): def log(self, task):
if ptime is None: self._data.add_log_entry(task)
ptime = datetime.now()
# round to nearest minute
round_min = timedelta(minutes=round(ptime.second/60))
ptime = ptime - timedelta(seconds=ptime.second) + round_min
# month dance necessary to trigger Data.__setitem__
month = self._data.setdefault(ptime.strftime("%Y-%m"), {})
month.setdefault(ptime.strftime("%d"), [])\
.append(f"{ptime.strftime('%H:%M')} {base64.b64encode(task.encode('utf-8')).decode('utf-8')}")
self._data[ptime.strftime("%Y-%m")] = month
def last_log(self, pdate=None): def last_log(self):
if pdate is None: log = self._data.get_log(date.today())
pdate = date.today() if not log:
if pdate.strftime("%Y-%m") not in self._data \
or pdate.strftime("%d") not in self._data[pdate.strftime("%Y-%m")] \
or len(self._data[pdate.strftime("%Y-%m")][pdate.strftime("%d")]) == 0:
return None return None
last = base64.b64decode( if log[-1] == "End":
self._data[pdate.strftime("%Y-%m")][pdate.strftime("%d")][-1].split()[1].encode("utf-8")).decode("utf-8") del log[-1]
if last == "End": self._data.set_log(log)
month = self._data[pdate.strftime("%Y-%m")] if not log:
del month[pdate.strftime("%d")][-1]
self._data[pdate.strftime("%Y-%m")] = month
if len(self._data[pdate.strftime("%Y-%m")][pdate.strftime("%d")]) == 0:
return None return None
last = base64.b64decode( return log[-1]
self._data[pdate.strftime("%Y-%m")][pdate.strftime("%d")][-1].split()[1].encode("utf-8")).decode("utf-8")
return last
def report(self, pdate=None): def report(self):
if pdate is None: return Report(self._data, date.today())
pdate = date.today()
return Report(self._data, pdate) def worklog(self):
return Worklog(self._data)
class Worklog:
def __init__(self, data):
self._data = data
class Report: class Report: