Portion of data.py rewrite
This commit is contained in:
parent
f390029899
commit
ceb7dbf3fd
276
src/fime/data.py
276
src/fime/data.py
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user