home · contact · privacy
In Day view, differentiate done and undone Todos, and collect doneness checkboxes.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 29 Apr 2024 03:08:40 +0000 (05:08 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 29 Apr 2024 03:08:40 +0000 (05:08 +0200)
plomtask/http.py
plomtask/todos.py
templates/base.html
templates/day.html
tests/todos.py

index 2b41db82e00e5d4cfac862161fb352607d09dfbb..316fd0805588a00b59dc28fbd8094fc8634dcd87 100644 (file)
@@ -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:
index a874a6d7d6ba9a3596301ead4c4a024c3594748b..d53674b6339679fa31ece5f78c0d871cfd90af01 100644 (file)
@@ -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."""
 
index 71c79b407c1580760796c0cb118867040392ef04..87b18648a721b693a544f5d16bedbb4dfd60434b 100644 (file)
@@ -5,9 +5,6 @@
 body {
   font-family: monospace;
 }
-ul {
-  list-style-type: none;
-}
 input.btn-harmless {
   color: green;
 }
index e3fe5ef29c5865551d42d751a5d45ea380921065..efa1c9bb1e78fc5ea455c5a678b4727fb22efdd3 100644 (file)
@@ -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) %}&nbsp; {% 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 %}
-&lt; {% 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) %}&nbsp;{% 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>&lt; {{enabler.process.title.newest|e}}</li>
+({% for enabler in node.enablers %}
+&lt; {{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 %}
 
index b85f2d105f0f09e174f13de88506b309ccfc4f3d..5736e5da9717616d47e09f0698ecf5865c4524f4 100644 (file)
@@ -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