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):
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):
command = AddCommand.AddCommand(["Foo due:today"], self.todolist,
self.out, self.error)
......
......@@ -121,6 +121,67 @@ class DepCommandTest(CommandTest):
self.assertEqual(self.output, "")
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):
"""
Helper function that checks the removal of the dependency from todo 1
......
......@@ -415,6 +415,30 @@ class TodoListDependencyTester(TopydoTest):
self.assertTrue(self.todolist.dirty)
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):
"""
......
......@@ -96,6 +96,12 @@ class AddCommand(Command):
self.todolist.add_dependency(p_todo, dep)
elif p_tag == 'before' or p_tag == 'partof':
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:
pass
......@@ -104,9 +110,22 @@ class AddCommand(Command):
convert_date(config().tag_start())
convert_date(config().tag_due())
add_dependencies('partof')
add_dependencies('before')
add_dependencies('after')
keywords = [
'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():
p_todo.set_creation_date(date.today())
......@@ -149,9 +168,9 @@ TEXT may contain:
* Priorities mid-sentence. Example: add "Water flowers (C)"
* Dependencies using before, after and partof tags. They are translated to the
corresponding 'id' and 'p' tags. The values of these tags correspond to the
todo number (not the dependency number).
* Dependencies using before, after, partof, parents-of and children-of tags.
These are translated to the corresponding 'id' and 'p' tags. The values of
these tags correspond to the todo number (not the dependency number).
Example: add "Subtask partof:1"
......
......@@ -39,44 +39,66 @@ class DepCommand(Command):
self.printer = pretty_printer_factory(self.todolist)
def _handle_add(self):
(from_todo, to_todo) = self._get_todos()
if from_todo and to_todo:
for from_todo, to_todo in self._get_todos():
self.todolist.add_dependency(from_todo, to_todo)
def _handle_rm(self):
(from_todo, to_todo) = self._get_todos()
if from_todo and to_todo:
for from_todo, to_todo in self._get_todos():
self.todolist.remove_dependency(from_todo, to_todo)
def _get_todos(self):
from_todo = None
to_todo = None
result = []
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:
first_todo_nr = self.argument(1)
operator = self.argument(2)
if operator == 'before' or operator == 'partof':
from_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)
if operator in operators:
second_todo_nr = self.argument(3)
else:
# the operator was omitted, assume 2nd argument is target task
# default to 'to' behavior
from_todo_nr = self.argument(1)
to_todo_nr = self.argument(2)
second_todo_nr = self.argument(2)
operator = "to"
first_todo = self.todolist.todo(first_todo_nr)
second_todo = self.todolist.todo(second_todo_nr)
from_todo = self.todolist.todo(from_todo_nr)
to_todo = self.todolist.todo(to_todo_nr)
result = operators[operator]()
except (InvalidTodoException):
self.error("Invalid todo number given.")
except InvalidCommandArgument:
self.error(self.usage())
return (from_todo, to_todo)
return result
def _handle_ls(self):
""" Handles the ls subsubcommand. """
......@@ -129,7 +151,7 @@ class DepCommand(Command):
def usage(self):
return """Synopsis:
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 to <NUMBER>
dep clean"""
......
......@@ -27,10 +27,6 @@ from topydo.lib.TodoList import TodoList
# the true and only editor
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):
return os.stat(p_file.name).st_mtime
......@@ -115,7 +111,7 @@ class EditCommand(MultiCommand):
if _is_edited(orig_mtime, temp_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:
self.todolist.add_todo(todo)
......
......@@ -124,18 +124,18 @@ class TodoList(TodoListBase):
if self._initialized:
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. """
try:
number = self._todos.index(p_todo)
if p_todo.has_tag('id'):
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'):
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]
self._update_todo_ids()
......@@ -206,20 +206,22 @@ class TodoList(TodoListBase):
self.dirty = True
@_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. """
dep_id = p_from_todo.tag_value('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.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):
p_from_todo.remove_tag('id')
del self._parentdict[dep_id]
self.dirty = True
@_needs_dependencies
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