"""Update Todo and its children."""
id_ = self.params.get_int('id')
todo = Todo.by_id(self.conn, id_)
- child_id = self.form_data.get_int_or_none('adopt')
- if child_id is not None:
+ adopted_child_ids = self.form_data.get_all_int('adopt')
+ for child in todo.children:
+ if child.id_ not in adopted_child_ids:
+ assert isinstance(child.id_, int)
+ child = Todo.by_id(self.conn, child.id_)
+ todo.remove_child(child)
+ for child_id in adopted_child_ids:
+ if child_id in [c.id_ for c in todo.children]:
+ continue
child = Todo.by_id(self.conn, child_id)
todo.add_child(child)
todo.set_conditions(self.conn, self.form_data.get_all_int('condition'))
return node
def add_child(self, child: Todo) -> None:
- """Add child to self.children, guard against recursion"""
+ """Add child to self.children, avoid recursion, update parenthoods."""
def walk_steps(node: Todo) -> None:
if node.id_ == self.id_:
raise BadFormatException('bad child choice causes recursion')
self.children += [child]
child.parents += [self]
+ def remove_child(self, child: Todo) -> None:
+ """Remove child from self.children, update counter relations."""
+ if child not in self.children:
+ raise HandledException('Cannot remove un-parented child.')
+ self.children.remove(child)
+ child.parents.remove(self)
+
def save(self, db_conn: DatabaseConnection) -> None:
"""Write self and children to DB and its cache."""
if self.process.id_ is None:
return Todo.by_date(self.db_conn, '2024-01-01')[0]
# test minimum
form_data = {'title': '', 'description': '', 'effort': 1}
- self.check_post(form_data, '/process', 302, '/')
+ self.check_post(form_data, '/process', 302)
form_data = {'comment': '', 'new_todo': 1}
- self.check_post(form_data, '/day?date=2024-01-01', 302, '/')
+ self.check_post(form_data, '/day?date=2024-01-01', 302)
# test posting to bad URLs
form_data = {}
self.check_post(form_data, '/todo=', 404)
self.check_post(form_data, '/todo?id=1', 404)
# test posting second todo of same process
form_data = {'comment': '', 'new_todo': 1}
- self.check_post(form_data, '/day?date=2024-01-01', 302, '/')
+ self.check_post(form_data, '/day?date=2024-01-01', 302)
# test todo 1 adopting todo 2
form_data = {'adopt': 2}
todo1 = post_and_reload(form_data)
self.assertEqual(todo1.parents, [])
self.assertEqual(todo2.children, [])
self.assertEqual(todo2.parents, [todo1])
- # test failure of re-adopting same child
- self.check_post(form_data, '/todo?id=1', 400, '/')
# test todo1 cannot be set done with todo2 not done yet
- form_data = {'done': ''}
+ form_data = {'done': '', 'adopt': 2}
todo1 = post_and_reload(form_data, 400)
self.assertEqual(todo1.is_done, False)
+ # test todo1 un-adopting todo 2 by just not sending an adopt
+ form_data = {}
+ todo1 = post_and_reload(form_data, 302)
+ todo2 = Todo.by_date(self.db_conn, '2024-01-01')[1]
+ self.assertEqual(todo1.children, [])
+ self.assertEqual(todo1.parents, [])
+ self.assertEqual(todo2.children, [])
+ self.assertEqual(todo2.parents, [])
def test_do_GET_todo(self) -> None:
"""Test GET /todo response codes."""