X-Git-Url: https://plomlompom.com/repos/condition_descriptions?a=blobdiff_plain;ds=sidebyside;f=todo.py;h=a9e180712cafd8fd83e1b69a8cc8ec665489aa40;hb=187d7e878767dcd4072a562e834cb7abd4349a06;hp=08fb8b82f6e3921ce6d75c3e86428d31c6398902;hpb=2960beb644986049fd975eac8e348ca61ab122b8;p=misc diff --git a/todo.py b/todo.py index 08fb8b8..a9e1807 100644 --- a/todo.py +++ b/todo.py @@ -3,17 +3,42 @@ 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 = """
-' +form_header_tmpl = """ + """ 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 +85,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']) @@ -99,6 +116,14 @@ class Task: def title(self): return self._last_of_history(self.title_history, '') + def title_at(self, queried_date): + ret = self.title_history[sorted(self.title_history.keys())[0]] + for date_key, title in self.title_history.items(): + if date_key > f'{queried_date} 23:59:59': + break + ret = title + return ret + def set_tags(self, tags): self._set_with_history(self.tags_history, set(tags)) @@ -115,24 +140,20 @@ class Task: class Day: - def __init__(self, todos, comment=''): + def __init__(self, db, todos={}, comment=''): + self.db = db self.todos = todos self.comment = comment + self.archived = True @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 or todo.day_weight)]: - s += todo.weight - return s + day.add_todo(uuid, todo_dict) + return day def to_dict(self): d = {'comment': self.comment, 'todos': {}} @@ -140,72 +161,151 @@ 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, day_weight=None): + def __init__(self, day, done=False, day_weight=None): + self.day = day self.done = done self.day_weight = day_weight @classmethod - def from_dict(cls, d): - return cls(d['done'], d['day_weight']) + def from_dict(cls, day, d): + return cls(day, d['done'], d['day_weight']) def to_dict(self): 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.old_days = {} + self.hide_unchosen = hide_unchosen + self.days = {} self.tasks = {} - self.reset_today() self.t_tags = set() super().__init__(db_path) + if not hasattr(self, 'selected_day_date'): + self.switch_to_day() 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_date = d['selected_day_date'] + for date, day_dict in d['days'].items(): + self.days[date] = self.add_day(dict_source=day_dict) + self.selected_day.archived = False 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) def to_dict(self): d = { - 'today': self.today.to_dict(), - 'today_date': self.today_date, + 'selected_day_date': self.selected_day_date, 't_filter_and': list(self.t_filter_and), 't_filter_not': list(self.t_filter_not), 'tasks': {}, - 'old_days': {} + 'days': {} } for uuid, t in self.tasks.items(): d['tasks'][uuid] = t.to_dict() - for date, day in self.old_days.items(): - d['old_days'][date] = day.to_dict() + for date, day in self.days.items(): + d['days'][date] = day.to_dict() return d + @property + def selected_day(self): + return self.days[self.selected_day_date] + def write(self): self.write_text_to_db(json.dumps(self.to_dict())) - def reset_today(self, date=None): + def switch_to_day(self, date=None): + if self.selected_day_date in self.days.keys(): + self.selected_day.archived = True if date: - self.today_date = date - self.today = self.old_days[date] - del self.old_days[date] + self.selected_day_date = date else: - self.today_date = str(datetime.now())[:10] - self.today = Day({}) + self.selected_day_date = str(datetime.now())[:10] + if not self.selected_day_date in self.days.keys(): + self.days[self.selected_day_date] = self.add_day() + self.selected_day.archived = False + + 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.days.items(): + for task_uuid, todo in day.todos.items(): + todo.title = self.tasks[task_uuid].title_at(date) + return Template(form_header_tmpl + selected_day_tmpl + archived_days_tmpl + form_footer).render(db=self, action=self.prefix+'/all', days=self.days) + + def show_selected_day(self): + return Template(form_header_tmpl + selected_day_tmpl + form_footer).render(db=self, action=self.prefix+'/day') + + def show_coming(self): + from datetime import timedelta + todays_date = str(datetime.now())[:10] + days_to_show = self.days.copy() + for day in days_to_show.values(): + day.archived = False + last_date = sorted(days_to_show.keys())[-1] + start_date = datetime.strptime(todays_date, '%Y-%m-%d') + end_date = datetime.strptime(last_date, '%Y-%m-%d') + for n in range(int((end_date - start_date).days) + 1): + current_date_obj = start_date + timedelta(n) + current_date = current_date_obj.strftime('%Y-%m-%d') + if current_date not in days_to_show.keys(): + days_to_show[current_date] = self.add_day() + days_to_show[current_date].archived = True + days_to_show[current_date].weekday = datetime.strptime(current_date, '%Y-%m-%d').strftime('%A') + for task_uuid, todo in days_to_show[current_date].todos.items(): + todo.title = self.tasks[task_uuid].title_at(current_date) + return Template(form_header_tmpl + archived_days_tmpl + form_footer).render(db=self, action=self.prefix+'/day', days=days_to_show) class TodoHandler(PlomHandler): @@ -221,14 +321,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(): @@ -237,13 +336,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]: @@ -257,57 +356,69 @@ 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'] day_weight = float(postvars['day_weight'][i]) if postvars['day_weight'][i] else None - db.today.todos[uuid] = Todo(done, day_weight) - db.today_date = postvars['today_date'][0] - db.today.comment = postvars['comment'][0] + db.selected_day.add_todo(uuid, {'done': done, 'day_weight': day_weight}) + + if 'comment' in postvars.keys(): + db.selected_day.comment = postvars['comment'][0] + if 'selected_day_date' in postvars.keys(): + new_selected_day_date = postvars['selected_day_date'][0] + try: + datetime.strptime(new_selected_day_date, '%Y-%m-%d') + except ValueError: + raise PlomException(f"{prefix} bad date string: {new_selected_day_date}") + if new_selected_day_date != db.selected_day_date: + if new_selected_day_date in db.days.keys(): + raise PlomException('cannot use same date twice') + else: + db.days[new_selected_day_date] = db.selected_day + del db.days[db.selected_day_date] + db.selected_day_date = new_selected_day_date + switch_edited_day = None - for date in db.old_days.keys(): - if f'edit_{date}' in postvars.keys(): - switch_edited_day = date + day_edit_prefix = 'edit_' + for k in postvars.keys(): + if k.startswith(day_edit_prefix): + switch_edited_day = k[len(day_edit_prefix):] break - if 'archive_today' in postvars.keys() or switch_edited_day: - if db.today_date in db.old_days.keys(): - raise PlomException('cannot use same date twice') - db.old_days[db.today_date] = db.today - if switch_edited_day: - db.reset_today(date) - else: - db.reset_today() + if 'archive_day' in postvars.keys() or switch_edited_day: + db.switch_to_day(switch_edited_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 task_uuid, todo in db.today.todos.items(): - todo.weight = todo.day_weight if todo.day_weight else db.tasks[task_uuid].default_weight - for date, day in db.old_days.items(): - for task_uuid, todo in day.todos.items(): - todo.title = db.tasks[task_uuid].title - todo.weight = todo.day_weight if todo.day_weight else db.tasks[task_uuid].default_weight - 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() + elif parsed_url.path == prefix + '/coming': + page = db.show_coming() + else: + page = db.show_all() + header = Template(html_head).render(prefix=prefix) + self.send_HTML(header + page) if __name__ == "__main__":