From f19823e9e77ae0017022dbfe63f66d2b065ba33a Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Mon, 29 Apr 2024 05:08:40 +0200 Subject: [PATCH] In Day view, differentiate done and undone Todos, and collect doneness checkboxes. --- plomtask/http.py | 13 ++++++--- plomtask/todos.py | 38 ++++++++++++++++++++++++- templates/base.html | 3 -- templates/day.html | 69 ++++++++++++++++++++++++++++++++++----------- tests/todos.py | 12 ++++---- 5 files changed, 105 insertions(+), 30 deletions(-) diff --git a/plomtask/http.py b/plomtask/http.py index 2b41db8..316fd08 100644 --- a/plomtask/http.py +++ b/plomtask/http.py @@ -127,10 +127,10 @@ class TaskHandler(BaseHTTPRequestHandler): date = self.params.get_str('date', todays_date()) top_todos = [t for t in Todo.by_date(self.conn, date) if not t.parents] - seen_todos: set[int] = set() - seen_conditions: set[int] = set() - todo_trees = [t.get_step_tree(seen_todos, seen_conditions) - for t in top_todos] + todo_trees = [t.get_undone_steps_tree() for t in top_todos] + done_trees = [] + for t in top_todos: + done_trees += t.get_done_steps_tree() condition_listings: list[ConditionListing] = [] for cond in Condition.all(self.conn): enablers = Todo.enablers_for_at(self.conn, cond, date) @@ -138,6 +138,7 @@ class TaskHandler(BaseHTTPRequestHandler): condition_listings += [ConditionListing(cond, enablers, disablers)] return {'day': Day.by_id(self.conn, date, create=True), 'todo_trees': todo_trees, + 'done_trees': done_trees, 'processes': Process.all(self.conn), 'condition_listings': condition_listings} @@ -207,6 +208,10 @@ class TaskHandler(BaseHTTPRequestHandler): todo.adopt_from(existing_todos) todo.make_missing_children(self.conn) todo.save(self.conn) + for todo_id in self.form_data.get_all_int('done'): + todo = Todo.by_id(self.conn, todo_id) + todo.is_done = True + todo.save(self.conn) return f'/day?date={date}' def do_POST_todo(self) -> str: diff --git a/plomtask/todos.py b/plomtask/todos.py index a874a6d..d53674b 100644 --- a/plomtask/todos.py +++ b/plomtask/todos.py @@ -17,6 +17,7 @@ class TodoStepsNode: is_todo: bool children: list[TodoStepsNode] seen: bool + hide: bool class Todo(BaseModel[int], ConditionsRelations): @@ -185,11 +186,46 @@ class Todo(BaseModel[int], ConditionsRelations): else: seen = step.id_ in seen_conditions seen_conditions.add(step.id_) - return TodoStepsNode(step, is_todo, children, seen) + return TodoStepsNode(step, is_todo, children, seen, False) node = make_node(self) return node + def get_undone_steps_tree(self) -> TodoStepsNode: + """Return tree of depended-on undone Todos and Conditions.""" + + def walk_tree(node: TodoStepsNode) -> None: + if isinstance(node.item, Todo) and node.item.is_done: + node.hide = True + for child in node.children: + walk_tree(child) + + seen_todos: set[int] = set() + seen_conditions: set[int] = set() + step_tree = self.get_step_tree(seen_todos, seen_conditions) + walk_tree(step_tree) + return step_tree + + def get_done_steps_tree(self) -> list[TodoStepsNode]: + """Return tree of depended-on done Todos.""" + + def make_nodes(node: TodoStepsNode) -> list[TodoStepsNode]: + children: list[TodoStepsNode] = [] + if not isinstance(node.item, Todo): + return children + for child in node.children: + children += make_nodes(child) + if node.item.is_done: + node.children = children + return [node] + return children + + seen_todos: set[int] = set() + seen_conditions: set[int] = set() + step_tree = self.get_step_tree(seen_todos, seen_conditions) + nodes = make_nodes(step_tree) + return nodes + def add_child(self, child: Todo) -> None: """Add child to self.children, avoid recursion, update parenthoods.""" diff --git a/templates/base.html b/templates/base.html index 71c79b4..87b1864 100644 --- a/templates/base.html +++ b/templates/base.html @@ -5,9 +5,6 @@ body { font-family: monospace; } -ul { - list-style-type: none; -} input.btn-harmless { color: green; } diff --git a/templates/day.html b/templates/day.html index e3fe5ef..efa1c9b 100644 --- a/templates/day.html +++ b/templates/day.html @@ -1,18 +1,50 @@ {% extends 'base.html' %} -{% macro node_with_children(node, indent) %} -<li>{% for i in range(indent) %}+{% endfor %} + +{% macro show_node(node, indent) %} {% if node.is_todo %} -{% if not node.item.is_doable %}<del>{% endif %}[{% if node.item.is_done %}x{% else %} {% endif %}]{% if not node.item.is_doable %}</del>{% endif %} +{% for i in range(indent) %} {% endfor %} + {% if node.seen %}({% else %}{% endif %}<a href="todo?id={{node.item.id_}}">{{node.item.process.title.newest|e}}</a>{% if node.seen %}){% else %}{% endif %} {% else %} -< {% if node.seen %}({% else %}{% endif %}<a href="condition?id={{node.item.id_}}">{{node.item.title.newest|e}}</a>{% if node.seen %}){% else %}{% endif %} +{% for i in range(indent) %} {% endfor %} + +{% if node.seen %}({% else %}{% endif %}<a href="condition?id={{node.item.id_}}">{{node.item.title.newest|e}}</a>{% if node.seen %}){% else %}{% endif %} +{% endif %} +{% endmacro %} + + +{% macro undone_with_children(node, indent) %} +{% if not node.hide %} +<tr> +<td> +{% if node.is_todo %} +<input name="done" value="{{node.item.id_}}" type="checkbox" {% if node.seen or not node.item.is_doable %}disabled{% endif %} {% if node.item.is_done %} checked {% endif %} /> +{% endif %} +</td> +<td> +{{ show_node(node, indent) }} +</td> +</tr> +{% endif %} +{% for child in node.children %} +{{ undone_with_children(child, indent+1) }} +{% endfor %} +{% endmacro %} + + +{% macro done_with_children(node, indent) %} +{% if not node.hide %} +<tr> +<td> +{{ show_node(node, indent) }} +</td> +</tr> {% endif %} {% for child in node.children %} -{{ node_with_children(child, indent+1) }} +{{ done_with_children(child, indent+1) }} {% endfor %} {% endmacro %} + {% block content %} <h3>{{day.date}} / {{day.weekday}}</h3> <p> @@ -27,25 +59,30 @@ add todo: <input name="new_todo" list="processes" autocomplete="off" /> <option value="{{process.id_}}">{{process.title.newest|e}}</option> {% endfor %} </datalist> -</form> <h4>conditions</h4> +<ul> {% for node in condition_listings %} <li>[{% if node.condition.is_active %}x{% else %} {% endif %}] <a href="condition?id={{node.condition.id_}}">{{node.condition.title.newest|e}}</a> -<ul> -{% for enabler in node.enablers %} -<li>< {{enabler.process.title.newest|e}}</li> +({% for enabler in node.enablers %} +< {{enabler.process.title.newest|e}}; {% endfor %} {% for disabler in node.disablers %} -<li>! {{disabler.process.title.newest|e}}</li> +! {{disabler.process.title.newest|e}}; +{% endfor %}) {% endfor %} </ul> -</li> -{% endfor %} -<h4>todos</h4> -<ul> +<h4>to do</h4> +<table> {% for node in todo_trees %} -{{ node_with_children(node, 0) }} +{{ undone_with_children(node, indent=0) }} {% endfor %} -</ul> +</table> +<h4>done</h4> +<table> +{% for node in done_trees %} +{{ done_with_children(node, indent=0) }} +{% endfor %} +</table> +</form> {% endblock %} diff --git a/tests/todos.py b/tests/todos.py index b85f2d1..5736e5d 100644 --- a/tests/todos.py +++ b/tests/todos.py @@ -164,7 +164,7 @@ class TestsWithDB(TestCaseWithDB): todo_1.save(self.db_conn) assert isinstance(todo_1.id_, int) # test minimum - node_0 = TodoStepsNode(todo_1, True, [], False) + node_0 = TodoStepsNode(todo_1, True, [], False, False) self.assertEqual(todo_1.get_step_tree(set(), set()), node_0) # test non_emtpy seen_todo does something node_0.seen = True @@ -174,7 +174,7 @@ class TestsWithDB(TestCaseWithDB): todo_2.save(self.db_conn) assert isinstance(todo_2.id_, int) todo_1.add_child(todo_2) - node_2 = TodoStepsNode(todo_2, True, [], False) + node_2 = TodoStepsNode(todo_2, True, [], False, False) node_0.children = [node_2] node_0.seen = False self.assertEqual(todo_1.get_step_tree(set(), set()), node_0) @@ -183,22 +183,22 @@ class TestsWithDB(TestCaseWithDB): todo_3.save(self.db_conn) assert isinstance(todo_3.id_, int) todo_2.add_child(todo_3) - node_3 = TodoStepsNode(todo_3, True, [], False) + node_3 = TodoStepsNode(todo_3, True, [], False, False) node_2.children = [node_3] self.assertEqual(todo_1.get_step_tree(set(), set()), node_0) # test same todo can be child-ed multiple times at different locations todo_1.add_child(todo_3) - node_4 = TodoStepsNode(todo_3, True, [], True) + node_4 = TodoStepsNode(todo_3, True, [], True, False) node_0.children += [node_4] self.assertEqual(todo_1.get_step_tree(set(), set()), node_0) # test condition shows up todo_1.set_conditions(self.db_conn, [self.cond1.id_]) - node_5 = TodoStepsNode(self.cond1, False, [], False) + node_5 = TodoStepsNode(self.cond1, False, [], False, False) node_0.children += [node_5] self.assertEqual(todo_1.get_step_tree(set(), set()), node_0) # test second condition shows up todo_2.set_conditions(self.db_conn, [self.cond2.id_]) - node_6 = TodoStepsNode(self.cond2, False, [], False) + node_6 = TodoStepsNode(self.cond2, False, [], False, False) node_2.children += [node_6] self.assertEqual(todo_1.get_step_tree(set(), set()), node_0) # test second condition is not hidden if fulfilled by non-sibling -- 2.30.2