Commit 69951ef5 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Merge branch 'master' of github.com:bram85/topydo

parents 68de5778 0f2266c0
...@@ -268,6 +268,61 @@ class AddCommandTest(CommandTest): ...@@ -268,6 +268,61 @@ class AddCommandTest(CommandTest):
self.assertEqual(self.output, "|wb3| {today} Bar p:1 @Context\n|wb3| {today} Bar @Context\n".format(today=self.today)) self.assertEqual(self.output, "|wb3| {today} Bar p:1 @Context\n|wb3| {today} Bar @Context\n".format(today=self.today))
def add_parentsof_helper(self, p_tag):
command = AddCommand.AddCommand(["Foo"], self.todolist, self.out,
self.error)
command.execute()
command = AddCommand.AddCommand(["Bar before:1"], self.todolist,
self.out, self.error)
command.execute()
command = AddCommand.AddCommand(["Baz {}:2".format(p_tag)],
self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.todo(3).has_tag('p', '1'))
def test_add_dep_parentsof01(self):
self.add_parentsof_helper('parentsof')
def test_add_dep_parentsof02(self):
self.add_parentsof_helper('parentof')
def test_add_dep_parentsof03(self):
self.add_parentsof_helper('parents-of')
def test_add_dep_parentsof04(self):
self.add_parentsof_helper('parent-of')
def add_childrenof_helper(self, p_tag):
command = AddCommand.AddCommand(["Foo"], self.todolist, self.out,
self.error)
command.execute()
command = AddCommand.AddCommand(["Bar before:1"], self.todolist,
self.out, self.error)
command.execute()
command = AddCommand.AddCommand(["Baz {}:1".format(p_tag)],
self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.todo(3).has_tag('id', '2'))
self.assertTrue(self.todolist.todo(2).has_tag('p', '2'))
def test_add_dep_childrenof01(self):
self.add_childrenof_helper('childrenof')
def test_add_dep_childrenof02(self):
self.add_childrenof_helper('childof')
def test_add_dep_childrenof03(self):
self.add_childrenof_helper('children-of')
def test_add_dep_childrenof04(self):
self.add_childrenof_helper('child-of')
def test_add_reldate1(self): def test_add_reldate1(self):
command = AddCommand.AddCommand(["Foo due:today"], self.todolist, command = AddCommand.AddCommand(["Foo due:today"], self.todolist,
self.out, self.error) self.out, self.error)
......
...@@ -121,6 +121,67 @@ class DepCommandTest(CommandTest): ...@@ -121,6 +121,67 @@ class DepCommandTest(CommandTest):
self.assertEqual(self.output, "") self.assertEqual(self.output, "")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def add_parentsof_helper(self, p_args):
command = DepCommand(p_args, self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.todo(4).has_tag('p', '1'))
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
def test_add10(self):
self.add_parentsof_helper(["add", "4", "parents-of", "2"])
def test_add11(self):
self.add_parentsof_helper(["add", "4", "parent-of", "2"])
def test_add12(self):
self.add_parentsof_helper(["add", "4", "parentsof", "2"])
def test_add13(self):
self.add_parentsof_helper(["add", "4", "parentof", "2"])
def test_add14(self):
command = DepCommand(["add", "4", "parents-of", "5"], self.todolist,
self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
def add_childrenof_helper(self, p_args):
command = DepCommand(p_args, self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.todo(2).has_tag('p', '2'))
self.assertTrue(self.todolist.todo(3).has_tag('p', '2'))
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
def test_add15(self):
self.add_childrenof_helper(["add", "4", "children-of", "1"])
def test_add16(self):
self.add_childrenof_helper(["add", "4", "child-of", "1"])
def test_add17(self):
self.add_childrenof_helper(["add", "4", "childrenof", "1"])
def test_add18(self):
self.add_childrenof_helper(["add", "4", "childof", "1"])
def test_add19(self):
command = DepCommand(["add", "4", "children-of", "5"], self.todolist,
self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
def rm_helper(self, p_args): def rm_helper(self, p_args):
""" """
Helper function that checks the removal of the dependency from todo 1 Helper function that checks the removal of the dependency from todo 1
......
...@@ -415,6 +415,30 @@ class TodoListDependencyTester(TopydoTest): ...@@ -415,6 +415,30 @@ class TodoListDependencyTester(TopydoTest):
self.assertTrue(self.todolist.dirty) self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo_by_dep_id('99')) self.assertTrue(self.todolist.todo_by_dep_id('99'))
def test_delete01(self):
""" Check that dependency tags are cleaned up. """
todo = self.todolist.todo(4)
self.todolist.delete(todo, p_leave_tags=False)
self.assertTrue(self.todolist.is_dirty())
self.assertEqual(self.todolist.todo(3).source(), "Baz p:1")
def test_delete02(self):
""" Check that dependency tags are left when requested. """
todo = self.todolist.todo(4)
self.todolist.delete(todo, p_leave_tags=True)
self.assertTrue(self.todolist.is_dirty())
self.assertEqual(self.todolist.todo(3).source(), "Baz p:1 id:2")
def test_delete03(self):
""" Check that dependency tags are left when requested. """
todo = self.todolist.todo(3)
self.todolist.delete(todo, p_leave_tags=True)
self.assertTrue(self.todolist.is_dirty())
self.assertEqual(self.todolist.todo(3).source(), "Buzz p:2")
class TodoListCleanDependencyTester(TopydoTest): class TodoListCleanDependencyTester(TopydoTest):
""" """
......
...@@ -96,6 +96,12 @@ class AddCommand(Command): ...@@ -96,6 +96,12 @@ class AddCommand(Command):
self.todolist.add_dependency(p_todo, dep) self.todolist.add_dependency(p_todo, dep)
elif p_tag == 'before' or p_tag == 'partof': elif p_tag == 'before' or p_tag == 'partof':
self.todolist.add_dependency(dep, p_todo) self.todolist.add_dependency(dep, p_todo)
elif p_tag.startswith('parent'):
for parent in self.todolist.parents(dep):
self.todolist.add_dependency(parent, p_todo)
elif p_tag.startswith('child'):
for child in self.todolist.children(dep):
self.todolist.add_dependency(p_todo, child)
except InvalidTodoException: except InvalidTodoException:
pass pass
...@@ -104,9 +110,22 @@ class AddCommand(Command): ...@@ -104,9 +110,22 @@ class AddCommand(Command):
convert_date(config().tag_start()) convert_date(config().tag_start())
convert_date(config().tag_due()) convert_date(config().tag_due())
add_dependencies('partof') keywords = [
add_dependencies('before') 'after',
add_dependencies('after') 'before',
'child-of',
'childof',
'children-of',
'childrenof',
'parent-of',
'parentof',
'parents-of',
'parentsof',
'partof',
]
for keyword in keywords:
add_dependencies(keyword)
if config().auto_creation_date(): if config().auto_creation_date():
p_todo.set_creation_date(date.today()) p_todo.set_creation_date(date.today())
...@@ -149,9 +168,9 @@ TEXT may contain: ...@@ -149,9 +168,9 @@ TEXT may contain:
* Priorities mid-sentence. Example: add "Water flowers (C)" * Priorities mid-sentence. Example: add "Water flowers (C)"
* Dependencies using before, after and partof tags. They are translated to the * Dependencies using before, after, partof, parents-of and children-of tags.
corresponding 'id' and 'p' tags. The values of these tags correspond to the These are translated to the corresponding 'id' and 'p' tags. The values of
todo number (not the dependency number). these tags correspond to the todo number (not the dependency number).
Example: add "Subtask partof:1" Example: add "Subtask partof:1"
......
...@@ -39,44 +39,66 @@ class DepCommand(Command): ...@@ -39,44 +39,66 @@ class DepCommand(Command):
self.printer = pretty_printer_factory(self.todolist) self.printer = pretty_printer_factory(self.todolist)
def _handle_add(self): def _handle_add(self):
(from_todo, to_todo) = self._get_todos() for from_todo, to_todo in self._get_todos():
if from_todo and to_todo:
self.todolist.add_dependency(from_todo, to_todo) self.todolist.add_dependency(from_todo, to_todo)
def _handle_rm(self): def _handle_rm(self):
(from_todo, to_todo) = self._get_todos() for from_todo, to_todo in self._get_todos():
if from_todo and to_todo:
self.todolist.remove_dependency(from_todo, to_todo) self.todolist.remove_dependency(from_todo, to_todo)
def _get_todos(self): def _get_todos(self):
from_todo = None result = []
to_todo = None
def get_parent_dependencies():
child_todo = first_todo
sibling_todo = second_todo
return [(parent, child_todo) for parent in self.todolist.parents(sibling_todo)]
def get_child_dependencies():
parent_todo = first_todo
sibling_todo = second_todo
return [(parent_todo, child) for child in self.todolist.children(sibling_todo)]
get_before_dependency = lambda: [(second_todo, first_todo)]
get_after_dependency = lambda: [(first_todo, second_todo)]
operators = {
"after": get_after_dependency,
"before": get_before_dependency,
"child-of": get_child_dependencies,
"childof": get_child_dependencies,
"children-of": get_child_dependencies,
"childrenof": get_child_dependencies,
"parent-of": get_parent_dependencies,
"parentof": get_parent_dependencies,
"parents-of": get_parent_dependencies,
"parentsof": get_parent_dependencies,
"partof": get_before_dependency,
"to": get_after_dependency,
}
try: try:
first_todo_nr = self.argument(1)
operator = self.argument(2) operator = self.argument(2)
if operator == 'before' or operator == 'partof': if operator in operators:
from_todo_nr = self.argument(3) second_todo_nr = self.argument(3)
to_todo_nr = self.argument(1)
elif operator == 'to' or operator == 'after':
from_todo_nr = self.argument(1)
to_todo_nr = self.argument(3)
else: else:
# the operator was omitted, assume 2nd argument is target task second_todo_nr = self.argument(2)
# default to 'to' behavior operator = "to"
from_todo_nr = self.argument(1)
to_todo_nr = self.argument(2) first_todo = self.todolist.todo(first_todo_nr)
second_todo = self.todolist.todo(second_todo_nr)
from_todo = self.todolist.todo(from_todo_nr) result = operators[operator]()
to_todo = self.todolist.todo(to_todo_nr)
except (InvalidTodoException): except (InvalidTodoException):
self.error("Invalid todo number given.") self.error("Invalid todo number given.")
except InvalidCommandArgument: except InvalidCommandArgument:
self.error(self.usage()) self.error(self.usage())
return (from_todo, to_todo) return result
def _handle_ls(self): def _handle_ls(self):
""" Handles the ls subsubcommand. """ """ Handles the ls subsubcommand. """
...@@ -129,7 +151,7 @@ class DepCommand(Command): ...@@ -129,7 +151,7 @@ class DepCommand(Command):
def usage(self): def usage(self):
return """Synopsis: return """Synopsis:
dep <add|rm> <NUMBER> [to] <NUMBER> dep <add|rm> <NUMBER> [to] <NUMBER>
dep add <NUMBER> <before|partof|after> <NUMBER> dep add <NUMBER> <before|partof|after|parents-of|children-of> <NUMBER>
dep ls <NUMBER> to dep ls <NUMBER> to
dep ls to <NUMBER> dep ls to <NUMBER>
dep clean""" dep clean"""
......
...@@ -27,10 +27,6 @@ from topydo.lib.TodoList import TodoList ...@@ -27,10 +27,6 @@ from topydo.lib.TodoList import TodoList
# the true and only editor # the true and only editor
DEFAULT_EDITOR = 'vi' DEFAULT_EDITOR = 'vi'
# Access the base class of the TodoList instance kept inside EditCommand. We
# cannot use super() inside the class itself
BASE_TODOLIST = lambda tl: super(TodoList, tl)
def _get_file_mtime(p_file): def _get_file_mtime(p_file):
return os.stat(p_file.name).st_mtime return os.stat(p_file.name).st_mtime
...@@ -115,7 +111,7 @@ class EditCommand(MultiCommand): ...@@ -115,7 +111,7 @@ class EditCommand(MultiCommand):
if _is_edited(orig_mtime, temp_todos): if _is_edited(orig_mtime, temp_todos):
for todo in self.todos: for todo in self.todos:
BASE_TODOLIST(self.todolist).delete(todo) self.todolist.delete(todo, p_leave_tags=True)
for todo in new_todos: for todo in new_todos:
self.todolist.add_todo(todo) self.todolist.add_todo(todo)
......
...@@ -124,18 +124,18 @@ class TodoList(TodoListBase): ...@@ -124,18 +124,18 @@ class TodoList(TodoListBase):
if self._initialized: if self._initialized:
self._register_todo(todo) self._register_todo(todo)
def delete(self, p_todo): def delete(self, p_todo, p_leave_tags=False):
""" Deletes a todo item from the list. """ """ Deletes a todo item from the list. """
try: try:
number = self._todos.index(p_todo) number = self._todos.index(p_todo)
if p_todo.has_tag('id'): if p_todo.has_tag('id'):
for child in self.children(p_todo): for child in self.children(p_todo):
self.remove_dependency(p_todo, child) self.remove_dependency(p_todo, child, p_leave_tags)
if p_todo.has_tag('p'): if p_todo.has_tag('p'):
for parent in self.parents(p_todo): for parent in self.parents(p_todo):
self.remove_dependency(parent, p_todo) self.remove_dependency(parent, p_todo, p_leave_tags)
del self._todos[number] del self._todos[number]
self._update_todo_ids() self._update_todo_ids()
...@@ -206,20 +206,22 @@ class TodoList(TodoListBase): ...@@ -206,20 +206,22 @@ class TodoList(TodoListBase):
self.dirty = True self.dirty = True
@_needs_dependencies @_needs_dependencies
def remove_dependency(self, p_from_todo, p_to_todo): def remove_dependency(self, p_from_todo, p_to_todo, p_leave_tags=False):
""" Removes a dependency between two todos. """ """ Removes a dependency between two todos. """
dep_id = p_from_todo.tag_value('id') dep_id = p_from_todo.tag_value('id')
if dep_id: if dep_id:
p_to_todo.remove_tag('p', dep_id)
self._depgraph.remove_edge(hash(p_from_todo), hash(p_to_todo)) self._depgraph.remove_edge(hash(p_from_todo), hash(p_to_todo))
self.dirty = True
# clean dangling dependency tags
if dep_id and not p_leave_tags:
p_to_todo.remove_tag('p', dep_id)
if not self.children(p_from_todo, True): if not self.children(p_from_todo, True):
p_from_todo.remove_tag('id') p_from_todo.remove_tag('id')
del self._parentdict[dep_id] del self._parentdict[dep_id]
self.dirty = True
@_needs_dependencies @_needs_dependencies
def parents(self, p_todo, p_only_direct=False): def parents(self, p_todo, p_only_direct=False):
""" """
......
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