home · contact · privacy
Ensure POST /day "new_todo" item order commutative.
authorChristian Heller <c.heller@plomlompom.de>
Fri, 2 Aug 2024 09:29:43 +0000 (11:29 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Fri, 2 Aug 2024 09:29:43 +0000 (11:29 +0200)
plomtask/http.py
plomtask/todos.py

index 15c17db62278b1e8d1338d9c4d312bed86ebf385..36a5d78cf608e31e128e5367056173da1305c55b 100644 (file)
@@ -596,7 +596,7 @@ class TaskHandler(BaseHTTPRequestHandler):
         except NotFoundException as e:
             raise BadFormatException from e
         old_todos = self._form_data.get_all_int('todo_id')
-        new_todos = self._form_data.get_all_int('new_todo')
+        new_todos_by_process = self._form_data.get_all_int('new_todo')
         comments = self._form_data.get_all_str('comment')
         efforts = self._form_data.get_all_floats_or_nones('effort')
         done_todos = self._form_data.get_all_int('done')
@@ -611,13 +611,15 @@ class TaskHandler(BaseHTTPRequestHandler):
         day = Day.by_id_or_create(self.conn, date)
         day.comment = day_comment
         day.save(self.conn)
-        for process_id in sorted(new_todos):
-            if 'empty' == make_type:
-                process = Process.by_id(self.conn, process_id)
-                todo = Todo(None, process, False, date)
-                todo.save(self.conn)
-            else:
-                Todo.create_with_children(self.conn, process_id, date)
+        new_todos = []
+        for process_id in sorted(new_todos_by_process):
+            process = Process.by_id(self.conn, process_id)
+            todo = Todo(None, process, False, date)
+            todo.save(self.conn)
+            new_todos += [todo]
+        if 'full' == make_type:
+            for todo in new_todos:
+                todo.ensure_children(self.conn)
         for i, todo_id in enumerate(old_todos):
             todo = Todo.by_id(self.conn, todo_id)
             todo.is_done = is_done[i]
@@ -679,7 +681,10 @@ class TaskHandler(BaseHTTPRequestHandler):
             made.save(self.conn)
             todo.add_child(made)
         for process_id in processes_to_make_full:
-            made = Todo.create_with_children(self.conn, process_id, todo.date)
+            process = Process.by_id(self.conn, process_id)
+            made = Todo(None, process, False, todo.date)
+            made.save(self.conn)
+            made.ensure_children(self.conn)
             todo.add_child(made)
         todo.set_condition_relations(self.conn, *cond_rel_id_lists)
         todo.update_attrs(**to_update)
index 03c454cdb8d75aad81f7c276f22124b9a75f7277..5782df0ec7e181cccfb21e36e00c2f61b3ffd060 100644 (file)
@@ -117,23 +117,15 @@ class Todo(BaseModel[int], ConditionsRelations):
         todos, _, _ = cls.by_date_range_with_limits(db_conn, date_range)
         return todos
 
-    @classmethod
-    def create_with_children(cls, db_conn: DatabaseConnection,
-                             process_id: int, date: str) -> Todo:
-        """Create Todo of process for date, ensure children demanded by chain.
-
-        At minimum creates Todo of process_id, but checks the respective
-        Process for its step tree, and walks down that to provide the initial
-        Todo with all descendants defined there, either adopting existing
-        Todos, or creating them where necessary.
-        """
+    def ensure_children(self, db_conn: DatabaseConnection) -> None:
+        """Ensure Todo children (create or adopt) demanded by Process chain."""
 
         def key_order_func(n: ProcessStepsNode) -> int:
             assert isinstance(n.process.id_, int)
             return n.process.id_
 
         def walk_steps(parent: Todo, step_node: ProcessStepsNode) -> Todo:
-            adoptables = [t for t in cls.by_date(db_conn, date)
+            adoptables = [t for t in Todo.by_date(db_conn, parent.date)
                           if (t not in parent.children)
                           and (t != parent)
                           and step_node.process == t.process]
@@ -142,7 +134,7 @@ class Todo(BaseModel[int], ConditionsRelations):
                 satisfier = adoptable
                 break
             if not satisfier:
-                satisfier = cls(None, step_node.process, False, date)
+                satisfier = Todo(None, step_node.process, False, parent.date)
                 satisfier.save(db_conn)
             sub_step_nodes = list(step_node.steps.values())
             sub_step_nodes.sort(key=key_order_func)
@@ -160,16 +152,13 @@ class Todo(BaseModel[int], ConditionsRelations):
             satisfier.save(db_conn)
             return satisfier
 
-        process = Process.by_id(db_conn, process_id)
-        todo = cls(None, process, False, date)
-        todo.save(db_conn)
+        process = Process.by_id(db_conn, self.process_id)
         steps_tree = process.get_steps(db_conn)
         for step_node in steps_tree.values():
             if step_node.is_suppressed:
                 continue
-            todo.add_child(walk_steps(todo, step_node))
-        todo.save(db_conn)
-        return todo
+            self.add_child(walk_steps(self, step_node))
+        self.save(db_conn)
 
     @classmethod
     def from_table_row(cls, db_conn: DatabaseConnection,
@@ -241,8 +230,9 @@ class Todo(BaseModel[int], ConditionsRelations):
         return 0
 
     @property
-    def process_id(self) -> int | str | None:
+    def process_id(self) -> int:
         """Needed for super().save to save Processes as attributes."""
+        assert isinstance(self.process.id_, int)
         return self.process.id_
 
     @property