Commit e664ac52 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Handle absence or invalid recurrence patterns more gracefully.

When completing a command, show a warning when a 'rec' tag has an
invalid recurrence pattern.

Fixes issue #6.

Conflicts:
	topydo/lib/DoCommand.py
parent ae56aa78
...@@ -39,6 +39,7 @@ class DoCommandTest(CommandTest.CommandTest): ...@@ -39,6 +39,7 @@ class DoCommandTest(CommandTest.CommandTest):
"Inactive t:2030-12-31 id:2", "Inactive t:2030-12-31 id:2",
"Subtodo of inactive p:2", "Subtodo of inactive p:2",
"Strict due:2014-01-01 rec:1d", "Strict due:2014-01-01 rec:1d",
"Invalid rec:1",
] ]
self.todolist = TodoList(todos) self.todolist = TodoList(todos)
...@@ -121,7 +122,7 @@ class DoCommandTest(CommandTest.CommandTest): ...@@ -121,7 +122,7 @@ class DoCommandTest(CommandTest.CommandTest):
self.assertTrue(self.todolist.is_dirty()) self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.errors, "") self.assertEquals(self.errors, "")
self.assertEquals(self.todolist.count(), 9) self.assertEquals(self.todolist.count(), 10)
def test_recurrence(self): def test_recurrence(self):
self.assertFalse(self.todolist.todo(4).has_tag('due')) self.assertFalse(self.todolist.todo(4).has_tag('due'))
...@@ -129,22 +130,22 @@ class DoCommandTest(CommandTest.CommandTest): ...@@ -129,22 +130,22 @@ class DoCommandTest(CommandTest.CommandTest):
self._recurrence_helper(["4"]) self._recurrence_helper(["4"])
self.assertTrue(self.todolist.todo(4).is_completed()) self.assertTrue(self.todolist.todo(4).is_completed())
result = "| 9| {today} Recurring! rec:1d due:{tomorrow}\nCompleted: x {today} Recurring! rec:1d\n".format(today=self.today, tomorrow=self.tomorrow) result = "| 10| {today} Recurring! rec:1d due:{tomorrow}\nCompleted: x {today} Recurring! rec:1d\n".format(today=self.today, tomorrow=self.tomorrow)
self.assertEquals(self.output, result) self.assertEquals(self.output, result)
todo = self.todolist.todo(8) todo = self.todolist.todo(10)
self.assertFalse(todo.is_completed()) self.assertFalse(todo.is_completed())
self.assertTrue(todo.has_tag('due')) self.assertTrue(todo.has_tag('due'))
def test_strict_recurrence1(self): def test_strict_recurrence1(self):
self._recurrence_helper(["-s", "8"]) self._recurrence_helper(["-s", "8"])
result = "| 9| {today} Strict due:2014-01-02 rec:1d\nCompleted: x {today} Strict due:2014-01-01 rec:1d\n".format(today=self.today) result = "| 10| {today} Strict due:2014-01-02 rec:1d\nCompleted: x {today} Strict due:2014-01-01 rec:1d\n".format(today=self.today)
self.assertEquals(self.output, result) self.assertEquals(self.output, result)
def test_strict_recurrence2(self): def test_strict_recurrence2(self):
self._recurrence_helper(["--strict", "8"]) self._recurrence_helper(["--strict", "8"])
result = "| 9| {today} Strict due:2014-01-02 rec:1d\nCompleted: x {today} Strict due:2014-01-01 rec:1d\n".format(today=self.today) result = "| 10| {today} Strict due:2014-01-02 rec:1d\nCompleted: x {today} Strict due:2014-01-01 rec:1d\n".format(today=self.today)
self.assertEquals(self.output, result) self.assertEquals(self.output, result)
def test_invalid1(self): def test_invalid1(self):
...@@ -252,7 +253,7 @@ class DoCommandTest(CommandTest.CommandTest): ...@@ -252,7 +253,7 @@ class DoCommandTest(CommandTest.CommandTest):
command.execute() command.execute()
self.assertTrue(self.todolist.is_dirty()) self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, "| 9| {today} Recurring! rec:1d due:{today}\nCompleted: x {yesterday} Recurring! rec:1d\n".format(today=self.today, yesterday=self.yesterday)) self.assertEquals(self.output, "| 10| {today} Recurring! rec:1d due:{today}\nCompleted: x {yesterday} Recurring! rec:1d\n".format(today=self.today, yesterday=self.yesterday))
self.assertEquals(self.errors, "") self.assertEquals(self.errors, "")
def test_do_custom_date6(self): def test_do_custom_date6(self):
...@@ -265,7 +266,7 @@ class DoCommandTest(CommandTest.CommandTest): ...@@ -265,7 +266,7 @@ class DoCommandTest(CommandTest.CommandTest):
command.execute() command.execute()
self.assertTrue(self.todolist.is_dirty()) self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, "| 9| {today} Recurring! rec:1d due:{today}\nCompleted: x {yesterday} Recurring! rec:1d\n".format(today=self.today, yesterday=self.yesterday)) self.assertEquals(self.output, "| 10| {today} Recurring! rec:1d due:{today}\nCompleted: x {yesterday} Recurring! rec:1d\n".format(today=self.today, yesterday=self.yesterday))
self.assertEquals(self.errors, "") self.assertEquals(self.errors, "")
def test_do_custom_date7(self): def test_do_custom_date7(self):
...@@ -277,7 +278,7 @@ class DoCommandTest(CommandTest.CommandTest): ...@@ -277,7 +278,7 @@ class DoCommandTest(CommandTest.CommandTest):
command.execute() command.execute()
self.assertTrue(self.todolist.is_dirty()) self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, "| 9| {today} Strict due:2014-01-02 rec:1d\nCompleted: x {yesterday} Strict due:2014-01-01 rec:1d\n".format(today=self.today, yesterday=self.yesterday)) self.assertEquals(self.output, "| 10| {today} Strict due:2014-01-02 rec:1d\nCompleted: x {yesterday} Strict due:2014-01-01 rec:1d\n".format(today=self.today, yesterday=self.yesterday))
self.assertEquals(self.errors, "") self.assertEquals(self.errors, "")
def test_multi_do1(self): def test_multi_do1(self):
...@@ -320,11 +321,19 @@ class DoCommandTest(CommandTest.CommandTest): ...@@ -320,11 +321,19 @@ class DoCommandTest(CommandTest.CommandTest):
When a todo item was generated by a recurring todo item, make sure When a todo item was generated by a recurring todo item, make sure
it cannot be completed in the same invocation. it cannot be completed in the same invocation.
""" """
command = DoCommand(["4", "9"], self.todolist, self.out, self.error, _no_prompt) command = DoCommand(["4", "10"], self.todolist, self.out, self.error, _no_prompt)
command.execute() command.execute()
self.assertTrue(self.todolist.todo(4).is_completed()) self.assertTrue(self.todolist.todo(4).is_completed())
self.assertFalse(self.todolist.todo(9).is_completed()) self.assertFalse(self.todolist.todo(10).is_completed())
def test_invalid_recurrence(self):
""" Show error message when an item has an invalid recurrence pattern. """
command = DoCommand(["9"], self.todolist, self.out, self.error, _no_prompt)
command.execute()
self.assertEquals(self.output, "Completed: x {} Invalid rec:1\n".format(self.today))
self.assertEquals(self.errors, "Warning: todo item has an invalid recurrence pattern.\n")
def test_empty(self): def test_empty(self):
command = DoCommand([], self.todolist, self.out, self.error) command = DoCommand([], self.todolist, self.out, self.error)
......
...@@ -136,7 +136,12 @@ class RecurrenceTest(TopydoTest): ...@@ -136,7 +136,12 @@ class RecurrenceTest(TopydoTest):
def test_no_recurrence(self): def test_no_recurrence(self):
self.todo.remove_tag('rec') self.todo.remove_tag('rec')
self.assertRaises(NoRecurrenceException,advance_recurring_todo,self.todo) self.assertRaises(NoRecurrenceException, advance_recurring_todo, self.todo)
def test_invalid_recurrence(self):
""" Throw exception when 'rec' tag has an invalid value. """
self.todo.set_tag('rec', '1')
self.assertRaises(NoRecurrenceException, advance_recurring_todo, self.todo)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -19,7 +19,7 @@ from datetime import date ...@@ -19,7 +19,7 @@ from datetime import date
from topydo.lib.DCommand import DCommand from topydo.lib.DCommand import DCommand
from topydo.lib.PrettyPrinter import PrettyPrinter from topydo.lib.PrettyPrinter import PrettyPrinter
from topydo.lib.PrettyPrinterFilter import PrettyPrinterNumbers from topydo.lib.PrettyPrinterFilter import PrettyPrinterNumbers
from topydo.lib.Recurrence import advance_recurring_todo, strict_advance_recurring_todo from topydo.lib.Recurrence import advance_recurring_todo, strict_advance_recurring_todo, NoRecurrenceException
from topydo.lib.Utils import date_string_to_date from topydo.lib.Utils import date_string_to_date
class DoCommand(DCommand): class DoCommand(DCommand):
...@@ -49,18 +49,21 @@ class DoCommand(DCommand): ...@@ -49,18 +49,21 @@ class DoCommand(DCommand):
def _handle_recurrence(self, p_todo): def _handle_recurrence(self, p_todo):
if p_todo.has_tag('rec'): if p_todo.has_tag('rec'):
if self.strict_recurrence: try:
new_todo = strict_advance_recurring_todo(p_todo, if self.strict_recurrence:
self.completion_date) new_todo = strict_advance_recurring_todo(p_todo,
else: self.completion_date)
new_todo = advance_recurring_todo(p_todo, else:
self.completion_date) new_todo = advance_recurring_todo(p_todo,
self.completion_date)
self.todolist.add_todo(new_todo)
self.todolist.add_todo(new_todo)
printer = PrettyPrinter()
printer.add_filter(PrettyPrinterNumbers(self.todolist)) printer = PrettyPrinter()
self.out(printer.print_todo(new_todo)) printer.add_filter(PrettyPrinterNumbers(self.todolist))
self.out(printer.print_todo(new_todo))
except NoRecurrenceException:
self.error("Warning: todo item has an invalid recurrence pattern.")
def prompt_text(self): def prompt_text(self):
return "Also mark subtasks as done? [y/N] " return "Also mark subtasks as done? [y/N] "
......
...@@ -42,8 +42,11 @@ def _advance_recurring_todo_helper(p_todo, p_offset): ...@@ -42,8 +42,11 @@ def _advance_recurring_todo_helper(p_todo, p_offset):
raise NoRecurrenceException() raise NoRecurrenceException()
length = todo.length() length = todo.length()
new_due = relative_date_to_date(pattern, p_offset) new_due = relative_date_to_date(pattern, p_offset)
if not new_due:
raise NoRecurrenceException()
todo.set_tag(config().tag_due(), new_due.isoformat()) todo.set_tag(config().tag_due(), new_due.isoformat())
if todo.start_date(): if todo.start_date():
......
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