From 93756ded23b6f80333ef26e434f611a94bd04764 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Sun, 10 Dec 2023 17:54:01 +0100 Subject: [PATCH] Improve todo accounting. --- todo.py | 241 ++++++++++++++++++++++++++++++++++--------------------- unite.py | 1 - 2 files changed, 151 insertions(+), 91 deletions(-) diff --git a/todo.py b/todo.py index 8c7029c..4449232 100644 --- a/todo.py +++ b/todo.py @@ -3,17 +3,39 @@ import json from uuid import uuid4 from datetime import datetime from urllib.parse import parse_qs +from jinja2 import Template +from urllib.parse import urlparse db_path = '/home/plom/org/todo_new.json' # db_path = '/home/plom/public_repos/misc/todo_new.json' server_port = 8082 -tmpl = """ +html_head = """ -
+all | edit day: +choose +do +
+""" + +form_footer = '\n
' +old_days_tmpl = """ + +{% for date, day in db.old_days.items() | sort(reverse=True) %} + +{% for task, todo in day.todos.items() | sort(attribute='1.title', reverse=True) %} + +{% endfor %} +{% endfor %} +
{{ date }} ({{ day.todos_sum |round(2) }}) {{ day.comment|e }}
{{ todo.title }}{% if todo.done %}✓{% endif %}{{ todo.weight }}
+""" + +selected_day_tmpl = """ +
+hiden unchosen:
mandatory tags: {% for t_tag in db.t_tags | sort %} {{ t_tag }} {% endfor %} @@ -22,8 +44,8 @@ forbidden tags: {% for t_tag in db.t_tags | sort %} {{ t_tag }} {% endfor %} - - + + {% for uuid, t in db.tasks.items() | sort(attribute='1.title', reverse=True) %} {% if t.visible %} @@ -31,29 +53,20 @@ forbidden tags: {% for t_tag in db.t_tags | sort %} - - + + + {% endif %} {% endfor %}
datearchive?{{ db.today.todos_sum|round(2) }}comment:
default
weight
titletagstoday?done?day
weight
datearchive?{{ db.selected_day.todos_sum|round(2) }} ({{ db.selected_day.todos_sum2|round(2)}})comment:
default
weight
titletagschoose?done?day
weight
-
- - -{% for date, day in db.old_days.items() | sort(reverse=True) %} - -{% for task, todo in day.todos.items() | sort(attribute='1.title', reverse=True) %} - -{% endfor %} -{% endfor %} -
{{ date }} ({{ day.todos_sum|round(2) }}) {{ day.comment|e }}
{{ todo.title }}{% if todo.done %}✓{% endif %}{{ todo.weight }}
-
""" class Task: - def __init__(self, title_history={}, tags_history={}, default_weight_history={}): + def __init__(self, db, title_history={}, tags_history={}, default_weight_history={}): + self.db = db self.title_history = title_history.copy() self.tags_history = tags_history.copy() self.default_weight_history = default_weight_history.copy() @@ -69,8 +82,9 @@ class Task: return default if 0 == len(history) else history[keys[-1]] @classmethod - def from_dict(cls, d): + def from_dict(cls, db, d): return cls( + db, d['title_history'], {k: set(v) for k, v in d['tags_history'].items()}, d['default_weight_history']) @@ -123,24 +137,19 @@ class Task: class Day: - def __init__(self, todos, comment=''): + def __init__(self, db, todos={}, comment=''): + self.db = db self.todos = todos self.comment = comment @classmethod - def from_dict(cls, d): + def from_dict(cls, db, d): todos = {} comment = d['comment'] if 'comment' in d.keys() else '' + day = cls(db, todos, comment) for uuid, todo_dict in d['todos'].items(): - todos[uuid] = Todo.from_dict(todo_dict) - return cls(todos, comment) - - @property - def todos_sum(self): - s = 0 - for todo in [todo for todo in self.todos.values() if todo.done]: - s += todo.weight - return s + day.add_todo(uuid, todo_dict) + return day def to_dict(self): d = {'comment': self.comment, 'todos': {}} @@ -148,50 +157,80 @@ class Day: d['todos'][task_uuid] = todo.to_dict() return d + def add_todo(self, id_, dict_source=None): + self.todos[id_] = Todo.from_dict(self, dict_source) if dict_source else Todo(self) + + def _todos_sum(self, include_undone=False): + s = 0 + for todo in [todo for todo in self.todos.values() if todo.done]: + s += todo.weight + if include_undone: + for todo in [todo for todo in self.todos.values() if not todo.done]: + s += todo.day_weight if todo.day_weight else 0 + return s + + @property + def todos_sum(self): + return self._todos_sum() + + @property + def todos_sum2(self): + return self._todos_sum(True) class Todo: - def __init__(self, done=False, weight=None): + def __init__(self, day, done=False, day_weight=None): + self.day = day self.done = done - self.weight = weight + self.day_weight = day_weight @classmethod - def from_dict(cls, d): - return cls(d['done'], d['weight']) + def from_dict(cls, day, d): + return cls(day, d['done'], d['day_weight']) def to_dict(self): - return {'done': self.done, 'weight': self.weight} + return {'done': self.done, 'day_weight': self.day_weight} + + @property + def weight(self): + if self.day_weight: + return self.day_weight + else: + task_uuid = [k for k,v in self.day.todos.items() if v == self][0] + return self.day_weight if self.day_weight else self.day.db.tasks[task_uuid].default_weight class TodoDB(PlomDB): - def __init__(self, t_filter_and = set(), t_filter_not = set()): + def __init__(self, prefix, t_filter_and = set(), t_filter_not = set(), hide_unchosen=False): + self.prefix = prefix self.t_filter_and = t_filter_and self.t_filter_not = t_filter_not + self.hide_unchosen = hide_unchosen self.old_days = {} self.tasks = {} - self.reset_today() + self.reset_day() self.t_tags = set() super().__init__(db_path) def read_db_file(self, f): d = json.load(f) - self.today = Day.from_dict(d['today']) - self.today_date = d['today_date'] + self.selected_day = self.add_day(d['selected_day']) + self.selected_day_date = d['selected_day_date'] for uuid, t_dict in d['tasks'].items(): - t = Task.from_dict(t_dict) - self.tasks[uuid] = t + t = self.add_task(id_=uuid, dict_source=t_dict) t.visible = len([tag for tag in self.t_filter_and if not tag in t.tags]) == 0\ - and len([tag for tag in self.t_filter_not if tag in t.tags]) == 0 + and len([tag for tag in self.t_filter_not if tag in t.tags]) == 0\ + and ((not self.hide_unchosen) or uuid in self.selected_day.todos) for tag in t.tags: self.t_tags.add(tag) for date, day_dict in d['old_days'].items(): - self.old_days[date] = Day.from_dict(day_dict) + self.old_days[date] = self.add_day(dict_source=day_dict) # Day.from_dict(self, day_dict) def to_dict(self): d = { - 'today': self.today.to_dict(), - 'today_date': self.today_date, + 'selected_day': self.selected_day.to_dict(), + 'selected_day_date': self.selected_day_date, 't_filter_and': list(self.t_filter_and), 't_filter_not': list(self.t_filter_not), 'tasks': {}, @@ -206,22 +245,42 @@ class TodoDB(PlomDB): def write(self): self.write_text_to_db(json.dumps(self.to_dict())) - def save_today(self): - if self.today_date in self.old_days.keys(): + def save_selected_day(self): + if self.selected_day_date in self.old_days.keys(): raise PlomException('cannot use same date twice') - for task_uuid, todo in [(task_uuid, todo) for task_uuid, todo in self.today.todos.items() - if not todo.weight]: - todo.weight = self.tasks[task_uuid].default_weight - self.old_days[self.today_date] = self.today + self.old_days[self.selected_day_date] = self.selected_day - def reset_today(self, date=None): + def reset_day(self, date=None): if date: - self.today_date = date - self.today = self.old_days[date] + self.selected_day_date = date + self.selected_day = self.old_days[date] del self.old_days[date] else: - self.today_date = str(datetime.now())[:10] - self.today = Day({}) + self.selected_day_date = str(datetime.now())[:10] + self.selected_day = self.add_day() + + def add_task(self, id_=None, dict_source=None, return_id=False): + t = Task.from_dict(self, dict_source) if dict_source else Task(self) + id_ = id_ if id_ else str(uuid4()) + self.tasks[id_] = t + if return_id: + return id_, t + else: + return t + + def add_day(self, dict_source=None): + return Day.from_dict(self, dict_source) if dict_source else Day(self) + + def show_all(self): + for i in range(10): + self.add_task(id_=f'new{i}') + for date, day in self.old_days.items(): + for task_uuid, todo in day.todos.items(): + todo.title = self.tasks[task_uuid].title_at(date) + return Template(selected_day_tmpl + old_days_tmpl + form_footer).render(db=self, action=self.prefix+'/all') + + def show_selected_day(self): + return Template(selected_day_tmpl + form_footer).render(db=self, action=self.prefix+'/day') class TodoHandler(PlomHandler): @@ -237,14 +296,13 @@ class TodoHandler(PlomHandler): def write_db(self): from urllib.parse import urlencode - db = TodoDB() + prefix = self.apps['todo'] if hasattr(self, 'apps') else '' + db = TodoDB(prefix) length = int(self.headers['content-length']) postvars = parse_qs(self.rfile.read(length).decode(), keep_blank_values=1) - - import pprint - pp = pprint.PrettyPrinter(indent=4) - pp.pprint(postvars) - + # import pprint + # pp = pprint.PrettyPrinter(indent=4) + # pp.pprint(postvars) db.t_filter_and = set() db.t_filter_not = set() if 't_filter_and' in postvars.keys(): @@ -253,13 +311,13 @@ class TodoHandler(PlomHandler): if 't_filter_not' in postvars.keys(): for target in postvars['t_filter_not']: db.t_filter_not.add(target) + if 'hide_unchosen' in postvars.keys(): + db.hide_unchosen = True if 't_uuid' in postvars.keys(): new_postvars_t_uuid = postvars['t_uuid'].copy() for i, uuid in enumerate(postvars['t_uuid']): if len(uuid) < 36 and len(postvars['t_title'][i]) > 0: - t = Task() - new_uuid = str(uuid4()) - db.tasks[new_uuid] = t + new_uuid, t = db.add_task(return_id=True) new_postvars_t_uuid[i] = new_uuid for key in [k for k in postvars.keys() if not k == 't_uuid']: if uuid in postvars[key]: @@ -273,52 +331,55 @@ class TodoHandler(PlomHandler): t.set_title(postvars['t_title'][i]) t.tags_from_joined_string(postvars['t_tags'][i]) t.set_default_weight(float(postvars['t_default_weight'][i])) - if uuid in db.today.todos.keys() and ((not 'do_today' in postvars) or uuid not in postvars['do_today']): - del db.today.todos[uuid] - if 'do_today' in postvars.keys(): + if uuid in db.selected_day.todos.keys() and ((not 'choose' in postvars) or uuid not in postvars['choose']): + del db.selected_day.todos[uuid] + if 'choose' in postvars.keys(): for i, uuid in enumerate(postvars['t_uuid']): - if uuid in postvars['do_today']: + if uuid in postvars['choose']: done = 'done' in postvars and uuid in postvars['done'] - weight = float(postvars['weight'][i]) if postvars['weight'][i] else None - db.today.todos[uuid] = Todo(done, weight) - db.today_date = postvars['today_date'][0] - db.today.comment = postvars['comment'][0] + day_weight = float(postvars['day_weight'][i]) if postvars['day_weight'][i] else None + db.selected_day.add_todo(uuid, {'done': done, 'day_weight': day_weight}) + db.selected_day_date = postvars['selected_day_date'][0] + db.selected_day.comment = postvars['comment'][0] switch_edited_day = None for date in db.old_days.keys(): if f'edit_{date}' in postvars.keys(): switch_edited_day = date break - if 'archive_today' in postvars.keys() or switch_edited_day: - db.save_today() + if 'archive_day' in postvars.keys() or switch_edited_day: + db.save_selected_day() if switch_edited_day: - db.reset_today(date) + db.reset_day(date) else: - db.reset_today() + db.reset_day() db.write() - homepage = self.apps['todo'] if hasattr(self, 'apps') else self.homepage - data = [('t_and', f) for f in db.t_filter_and] + [('t_not', f) for f in db.t_filter_not] + data = [('t_and', f) for f in db.t_filter_and] + [('t_not', f) for f in db.t_filter_not] + [('hide_unchosen', int(db.hide_unchosen))] encoded_params = urlencode(data) - homepage += '?' + encoded_params + parsed_url = urlparse(self.path) + if prefix + '/day' == parsed_url.path: + homepage = f'{prefix}/day?{encoded_params}' + else: + homepage = f'{prefix}/all?{encoded_params}' self.redirect(homepage) def do_GET(self): self.try_do(self.show_db) def show_db(self): - from jinja2 import Template - from urllib.parse import urlparse + prefix = self.apps['todo'] if hasattr(self, 'apps') else '' parsed_url = urlparse(self.path) params = parse_qs(parsed_url.query) t_filter_and = set(params.get('t_and', [])) t_filter_not = set(params.get('t_not', ['deleted'])) - db = TodoDB(t_filter_and, t_filter_not) - for i in range(10): - db.tasks[f'new{i}'] = Task() - for date, day in db.old_days.items(): - for task_uuid, todo in day.todos.items(): - todo.title = db.tasks[task_uuid].title_at(date) - page = Template(tmpl).render(db=db) - self.send_HTML(page) + hide_unchosen_params = params.get('hide_unchosen', []) + hide_unchosen = len(hide_unchosen_params) > 0 and hide_unchosen_params[0] != '0' + db = TodoDB(prefix, t_filter_and, t_filter_not, hide_unchosen) + if parsed_url.path == prefix + '/day': + page = db.show_selected_day() + else: + page = db.show_all() + header = Template(html_head).render(prefix=prefix) + self.send_HTML(header + page) if __name__ == "__main__": diff --git a/unite.py b/unite.py index de8be07..8f90e67 100644 --- a/unite.py +++ b/unite.py @@ -45,7 +45,6 @@ class UnitedRequestHandler(PlomHandler): path_toks = parsed_url.path.split('/') while len(path_toks) > 0: target_path = '/'.join(path_toks) - print(target_path) if target_path in self.routes['GET'].keys(): self.routes['GET'][target_path](self) return -- 2.30.2