+ # days_to_show = {}
+ # for n in range(int((end_date_obj - start_date_obj).days) + 1):
+ # date_obj = start_date_obj + timedelta(n)
+ # date_str = date_obj.strftime(DATE_FORMAT)
+ # if date_str not in self.days.keys():
+ # days_to_show[date_str] = self.add_day(date_str)
+ # else:
+ # days_to_show[date_str] = self.days[date_str]
+ # days_to_show[date_str].month_title = date_obj.strftime('%B') if date_obj.day == 1 else None
+ # days_to_show[date_str].weekday = datetime.strptime(date_str, DATE_FORMAT).strftime('%A')[:2]
+
+ days_to_show = self.init_calendar_items(start_date_str, end_date_str)
+ return j2env.get_template('calendar.html').render(
+ selected_date=self.selected_date,
+ days=days_to_show,
+ start_date=start_date_str,
+ end_date=end_date_str)
+
+ def show_day_todos(self, undone_sort_order=None, done_sort_order=None, is_tree_shaped=False, todo_parenthood=None):
+ legal_undone_sort_keys = {'title', 'sort_done', 'default_effort', 'importance'}
+ legal_done_sort_keys = {'title', 'effort_at_selected_date', 'family_effort'}
+
+ current_date = datetime.strptime(self.selected_date, DATE_FORMAT)
+ prev_date = current_date - timedelta(days=1)
+ prev_date_str = prev_date.strftime(DATE_FORMAT)
+ next_date = current_date + timedelta(days=1)
+ next_date_str = next_date.strftime(DATE_FORMAT)
+
+ adoptable_past_todos = []
+ for todo in [t for t in self.todos.values()
+ if t.visible
+ and (not t.done)
+ and t.earliest_date < self.selected_date]:
+ adoptable_past_todos += [todo]
+ undone_todos = self.selected_day.sorted_todos(False, is_tree_shaped, undone_sort_order,
+ legal_undone_sort_keys)
+ done_todos = self.selected_day.sorted_todos(True, is_tree_shaped, done_sort_order,
+ legal_done_sort_keys)
+
+ return j2env.get_template('day_todos.html').render(
+ day=self.selected_day,
+ tags=self.t_tags,
+ filter_and=self.tag_filter_and,
+ filter_not=self.tag_filter_not,
+ prev_date=prev_date_str,
+ adoptable_past_todos=adoptable_past_todos,
+ next_date=next_date_str,
+ all_tasks=[t for t in self.tasks.values()],
+ undone_todos=undone_todos,
+ done_todos=done_todos,
+ is_tree_shaped=is_tree_shaped,
+ undone_sort=undone_sort_order,
+ done_sort=done_sort_order,
+ parenthood=todo_parenthood)
+
+ def show_todo(self, id_, parenthood):
+ todo = self.todos[id_]
+ filtered_tasks = [t for t in self.tasks.values()
+ if t != todo.task]
+ filtered_todos = [t for t in self.todos.values()
+ if t != todo
+ and t not in todo.deps]
+
+ legal_dates = list(todo.efforts.keys())
+ date_filtered_todos = []
+ for date in legal_dates:
+ for filtered_todo in filtered_todos:
+ if filtered_todo in date_filtered_todos:
+ continue
+ if date in filtered_todo.efforts.keys():
+ date_filtered_todos += [filtered_todo]
+
+ dep_slots = []
+ for dep in todo.task.deps:
+ dep_slots += [{'task': dep,
+ 'todos': [t for t in todo.deps if t.task == dep]}]
+
+ suggested_todos = {}
+ for dep in todo.task.deps:
+ suggested_todos[dep.id_] = [t for t in date_filtered_todos if t.task.id_ == dep.id_]
+ additional_deps = [t for t in todo.deps if not t.task in todo.task.deps]
+
+ return j2env.get_template('todo.html').render(
+ tags=self.t_tags,
+ todo=todo,
+ filtered_todos=date_filtered_todos,
+ filtered_tasks=filtered_tasks,
+ dep_slots=dep_slots,
+ suggested_todos=suggested_todos,
+ additional_deps=additional_deps,
+ parentood=parenthood,
+ dep_todos=todo.deps)
+
+ def show_task(self, id_):
+ if id_:
+ if not id_ in self.tasks.keys():
+ raise PlomException('no Task for ID')
+ task = self.tasks[id_]
+ else:
+ task = self.add_task()
+ if not id_:
+ task.default_effort.set(1.0)
+ filtered_tasks = [t for t in self.tasks.values()
+ if t != task
+ and (t not in task.deps)]
+
+ return j2env.get_template('task.html').render(
+ selected_date=self.selected_date,
+ tags=self.t_tags,
+ filtered_tasks=filtered_tasks,
+ task=task)
+
+ def show_tasks(self, search, sort_order=None):
+ filtered_tasks = []
+ for task in [t for t in self.tasks.values() if (not search) or t.matches(search)]:
+ filtered_tasks += [task]
+ reverse = False
+ sort_column = sort_order
+ if sort_order and '-' == sort_order[0]:
+ reverse = True
+ sort_column = sort_order[1:]
+ if sort_column == 'title':
+ filtered_tasks.sort(key=lambda t: t.title.now)
+ elif sort_column == 'default_effort':
+ filtered_tasks.sort(key=lambda t: t.default_effort.now)
+ elif sort_column == 'weight':
+ filtered_tasks.sort(key=lambda t: t.deps_weight)
+ elif sort_column == 'latest_effort_date':
+ filtered_tasks.sort(key=lambda t: t.latest_effort_date)
+ if reverse:
+ filtered_tasks.reverse()
+ return j2env.get_template('tasks.html').render(
+ sort=sort_order,
+ tasks=filtered_tasks,
+ tags=self.t_tags,
+ filter_and=self.tag_filter_and,
+ filter_not=self.tag_filter_not,
+ search=search)
+
+ # helpers
+
+ def init_calendar_items(self, start_date_str, end_date_str):
+ self.tag_filter_and = ['calendar']
+ self.tag_filter_not = ['deleted']
+
+ todays_date_obj = datetime.strptime(today_date(), DATE_FORMAT)
+ yesterdays_date_obj = todays_date_obj - timedelta(1)
+ def get_day_limit_obj(index, day_limit_string):
+ date_obj = datetime.strptime(sorted(self.days.keys())[index], DATE_FORMAT)
+ if day_limit_string and len(day_limit_string) > 0:
+ if day_limit_string in {'today', 'yesterday'}:
+ date_obj = todays_date_obj if day_limit_string == 'today' else yesterdays_date_obj
+ else:
+ date_obj = datetime.strptime(day_limit_string, DATE_FORMAT)
+ return date_obj
+ start_date_obj = get_day_limit_obj(0, start_date_str)
+ end_date_obj = get_day_limit_obj(-1, end_date_str)
+
+ days_to_show = {}
+ for n in range(int((end_date_obj - start_date_obj).days) + 1):
+ date_obj = start_date_obj + timedelta(n)
+ date_str = date_obj.strftime(DATE_FORMAT)
+ if date_str not in self.days.keys():
+ days_to_show[date_str] = self.add_day(date_str)
+ else:
+ days_to_show[date_str] = self.days[date_str]
+ days_to_show[date_str].month_title = date_obj.strftime('%B') if date_obj.day == 1 else None
+ days_to_show[date_str].weekday = datetime.strptime(date_str, DATE_FORMAT).strftime('%A')[:2]
+ return days_to_show
+
+
+
+class ParamsParser:
+
+ def __init__(self, parsed_url_query, cookie_db):
+ self.params = parse_qs(parsed_url_query)
+ self.cookie_db = cookie_db
+
+ def get(self, key, default=None, as_bool=False):
+ # boolean = bool == type(default)
+ param = self.params.get(key, [default])[0]
+ if as_bool:
+ if param == '0':
+ param = False
+ elif param is not None:
+ param = True
+ return param
+
+ def cookie_key_from_params_key(self, prefix, key):
+ return f'{prefix}:{key}' if prefix else key
+
+ def get_cookied(self, key, default=None, prefix=None, as_bool=False):
+ cookie_key = self.cookie_key_from_params_key(prefix, key)
+ param = self.get(key, default, as_bool)
+ if param == '-':
+ param = None
+ if cookie_key in self.cookie_db.keys():
+ del self.cookie_db[cookie_key]
+ if param is None and cookie_key in self.cookie_db.keys():
+ param = self.cookie_db[cookie_key]
+ if param is not None:
+ self.cookie_db[cookie_key] = param
+ return param
+
+ def get_cookied_chain(self, key, default=None, prefix=None):
+ cookie_key = self.cookie_key_from_params_key(prefix, key)
+ params = self.params.get(key, default)
+ if params == ['-']:
+ params = None
+ if cookie_key in self.cookie_db.keys():
+ del self.cookie_db[cookie_key]
+ if params is None and cookie_key in self.cookie_db.keys():
+ params = self.cookie_db[cookie_key]
+ if params is not None:
+ self.cookie_db[cookie_key] = params
+ return params
+
+
+
+class PostvarsParser:
+
+ def __init__(self, postvars):
+ self.postvars = postvars
+
+ def has(self, key):
+ return key in self.postvars.keys()
+
+ def get(self, key, on_empty=None, float_if_possible=False):
+ return self.get_at_index(key, 0, on_empty, float_if_possible)
+
+ def get_at_index(self, key, i, on_empty=None, float_if_possible=False):
+ if self.has(key) and len(self.postvars[key][i]) > 0:
+ val = self.postvars[key][i]
+ else:
+ val = on_empty
+ if float_if_possible and val is not None:
+ return float(val)
+ else:
+ return val
+
+ def get_all(self, key, on_empty=None):
+ if self.has(key) and len(self.postvars[key]) > 0:
+ return [v for v in self.postvars[key] if len(v) > 0]
+ return on_empty
+
+ def set(self, key, value):
+ self.postvars[key] = [value]