Commit 20935465 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Fix transitive reduction for parent items with multiple children

Given these dependencies:

A id:1
B p:1
C p:1 id:2
D p:1 p:2

A -> B
A -> C
A -> D
C -> D

The relation A -> D is superfluous (A -> C, C -> D). However, the p:1
tag in D would not be removed, because relation 1 still exists (A -> B).

Therefore a new function is added that identifies p: tags which have no
corresponding edge in the internal dependency graph (rather than looking
at the value of the edge).
parent 3237d802
...@@ -367,13 +367,13 @@ class TodoListDependencyTester(TopydoTest): ...@@ -367,13 +367,13 @@ class TodoListDependencyTester(TopydoTest):
class TodoListCleanDependencyTester(TopydoTest): class TodoListCleanDependencyTester(TopydoTest):
def setUp(self): def setUp(self):
super(TodoListCleanDependencyTester, self).setUp() super(TodoListCleanDependencyTester, self).setUp()
self.todolist = TodoList([]) self.todolist = TodoList([])
def test_clean_dependencies1(self):
self.todolist.add("Bar p:1") self.todolist.add("Bar p:1")
self.todolist.add("Baz p:1 id:2") self.todolist.add("Baz p:1 id:2")
self.todolist.add("Buzz p:2") self.todolist.add("Buzz p:2")
def test_clean_dependencies(self):
self.todolist.clean_dependencies() self.todolist.clean_dependencies()
self.assertFalse(self.todolist.todo(1).has_tag('p')) self.assertFalse(self.todolist.todo(1).has_tag('p'))
...@@ -381,5 +381,17 @@ class TodoListCleanDependencyTester(TopydoTest): ...@@ -381,5 +381,17 @@ class TodoListCleanDependencyTester(TopydoTest):
self.assertTrue(self.todolist.todo(2).has_tag('id', '2')) self.assertTrue(self.todolist.todo(2).has_tag('id', '2'))
self.assertTrue(self.todolist.todo(3).has_tag('p', '2')) self.assertTrue(self.todolist.todo(3).has_tag('p', '2'))
def test_clean_dependencies2(self):
self.todolist.add("Foo id:1")
self.todolist.add("Bar p:1")
self.todolist.add("Baz p:1 id:2")
self.todolist.add("Buzz p:1 p:2")
self.todolist.clean_dependencies()
self.assertFalse(self.todolist.todo(4).has_tag('p', '1'))
self.assertTrue(self.todolist.todo(1).has_tag('id', '1'))
self.assertTrue(self.todolist.todo(2).has_tag('p', '1'))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -203,19 +203,39 @@ class TodoList(TodoListBase): ...@@ -203,19 +203,39 @@ class TodoList(TodoListBase):
graph and removing unused dependency ids from the graph (in that graph and removing unused dependency ids from the graph (in that
order). order).
""" """
def clean_by_tag(tag_name): def remove_tag(p_todo, p_tag, p_value):
""" Generic function to handle 'p' and 'id' tags. """ """
for todo in [todo for todo in self._todos Removes a tag from a todo item.
if todo.has_tag(tag_name)]: """
p_todo.remove_tag(p_tag, p_value)
self.dirty = True
def clean_parent_relations():
"""
Remove id: tags for todos without child todo items.
"""
value = todo.tag_value(tag_name) for todo in [todo for todo in self._todos if todo.has_tag('id')]:
value = todo.tag_value('id')
if not self._depgraph.has_edge_id(value): if not self._depgraph.has_edge_id(value):
todo.remove_tag(tag_name, value) remove_tag(todo, 'id', value)
self.dirty = True
def clean_orphan_relations():
"""
Remove p: tags for todos referring to a parent that is not in the
dependency graph anymore.
"""
for todo in [todo for todo in self._todos if todo.has_tag('p')]:
value = todo.tag_value('p')
parent = self.todo_by_dep_id(value)
if not self._depgraph.has_edge(hash(parent), hash(todo)):
remove_tag(todo, 'p', value)
self._depgraph.transitively_reduce() self._depgraph.transitively_reduce()
clean_by_tag('p') clean_parent_relations()
clean_by_tag('id') clean_orphan_relations()
def _update_parent_cache(self): def _update_parent_cache(self):
""" """
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment