Commit 1b7b8412 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Add postpone subcommand.

parent 99ca6057
...@@ -39,6 +39,7 @@ Subcommands implemented: ...@@ -39,6 +39,7 @@ Subcommands implemented:
* ls * ls
* lscon * lscon
* lsprj * lsprj
* postpone [x]
* pri * pri
* sort [xx] * sort [xx]
* tag [x] * tag [x]
......
...@@ -32,6 +32,7 @@ from DoCommand import DoCommand ...@@ -32,6 +32,7 @@ from DoCommand import DoCommand
from ListCommand import ListCommand from ListCommand import ListCommand
from ListContextCommand import ListContextCommand from ListContextCommand import ListContextCommand
from ListProjectCommand import ListProjectCommand from ListProjectCommand import ListProjectCommand
from PostponeCommand import PostponeCommand
from PrettyPrinter import * from PrettyPrinter import *
from PriorityCommand import PriorityCommand from PriorityCommand import PriorityCommand
from SortCommand import SortCommand from SortCommand import SortCommand
...@@ -119,6 +120,7 @@ class CLIApplication(object): ...@@ -119,6 +120,7 @@ class CLIApplication(object):
'listproj': ListProjectCommand, 'listproj': ListProjectCommand,
'listproject': ListProjectCommand, 'listproject': ListProjectCommand,
'listprojects': ListProjectCommand, 'listprojects': ListProjectCommand,
'postpone': PostponeCommand,
'pri': PriorityCommand, 'pri': PriorityCommand,
'rm': DeleteCommand, 'rm': DeleteCommand,
'sort': SortCommand, 'sort': SortCommand,
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from datetime import date, timedelta
from Command import *
from PrettyPrinter import *
from RelativeDate import relative_date_to_date
from TodoList import InvalidTodoException
from Utils import convert_todo_number, InvalidTodoNumberException, date_string_to_date
class PostponeCommand(Command):
def __init__(self, p_args, p_todolist,
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(PostponeCommand, self).__init__(p_args, p_todolist, p_out, p_err, p_prompt)
self.move_start_date = False
def _process_flags(self):
opts, args = self.getopt('s')
for o, a in opts:
if o == '-s':
self.move_start_date = True
self.args = args
def _get_offset(self, p_todo):
offset = p_todo.tag_value(Config.TAG_DUE, date.today().isoformat())
offset_date = date_string_to_date(offset)
if offset_date < date.today():
offset_date = date.today()
return offset_date
def execute(self):
if not super(PostponeCommand, self).execute():
return False
self._process_flags()
try:
number = convert_todo_number(self.argument(0))
todo = self.todolist.todo(number)
pattern = self.argument(1)
# pdb.set_trace()
offset = self._get_offset(todo)
new_due = relative_date_to_date(pattern, offset)
if new_due:
if self.move_start_date and todo.has_tag(Config.TAG_START):
length = todo.length()
new_start = new_due - timedelta(length)
todo.set_tag(Config.TAG_START, new_start.isoformat())
todo.set_tag(Config.TAG_DUE, new_due.isoformat())
self.todolist.set_dirty()
self.out(pretty_print(todo, [self.todolist.pp_number()]))
else:
self.error("Invalid date pattern given.")
except InvalidCommandArgument:
self.error(self.usage())
except (InvalidTodoNumberException, InvalidTodoException):
self.error("Invalid todo number given.")
def usage(self):
return "Synopsis: postpone [-s] <NUMBER> <PATTERN>"
def help(self):
return """Postpone a todo item with the given number and the given pattern.
Postponing is done by adjusting the due date of the todo, and if the -s flag is
given, the start date accordingly.
The pattern is a relative date, written in the format <COUNT><PERIOD> where
count is a number and <PERIOD> is either 'd', 'w', 'm' or 'y', which stands for
days, weeks, months and years respectively. Example: 'postpone 1 1w' postpones
todo number 1 for 1 week."""
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from datetime import date, timedelta
import CommandTest
import PostponeCommand
import TodoList
class PostponeCommandTest(CommandTest.CommandTest):
def setUp(self):
self.today = date.today()
self.past = date.today() - timedelta(1)
self.future = date.today() + timedelta(1)
self.start = date.today() - timedelta(2)
self.future_start = self.future - timedelta(2)
todos = [
"Foo",
"Bar due:%s" % self.today.isoformat(),
"Baz due:%s t:%s" % (self.today.isoformat(), self.start.isoformat()),
"Past due:%s" % self.past.isoformat(),
"Future due:%s t:%s" % (self.future.isoformat(), self.future_start.isoformat()),
]
self.todolist = TodoList.TodoList(todos)
def test_postpone1(self):
command = PostponeCommand.PostponeCommand(["1", "1w"], self.todolist, self.out, self.error)
command.execute()
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, " 1 Foo due:%s\n" % due.isoformat())
self.assertEquals(self.errors, "")
def test_postpone2(self):
command = PostponeCommand.PostponeCommand(["2", "1w"], self.todolist, self.out, self.error)
command.execute()
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, " 2 Bar due:%s\n" % due.isoformat())
self.assertEquals(self.errors, "")
def test_postpone3(self):
command = PostponeCommand.PostponeCommand(["-s", "2", "1w"], self.todolist, self.out, self.error)
command.execute()
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, " 2 Bar due:%s\n" % due.isoformat())
self.assertEquals(self.errors, "")
def test_postpone4(self):
command = PostponeCommand.PostponeCommand(["3", "1w"], self.todolist, self.out, self.error)
command.execute()
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, " 3 Baz due:%s t:%s\n" % (due.isoformat(), self.start.isoformat()))
self.assertEquals(self.errors, "")
def test_postpone5(self):
command = PostponeCommand.PostponeCommand(["-s", "3", "1w"], self.todolist, self.out, self.error)
command.execute()
due = self.today + timedelta(7)
start = self.start + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, " 3 Baz due:%s t:%s\n" % (due.isoformat(), start.isoformat()))
self.assertEquals(self.errors, "")
def test_postpone6(self):
command = PostponeCommand.PostponeCommand(["4", "1w"], self.todolist, self.out, self.error)
command.execute()
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, " 4 Past due:%s\n" % due.isoformat())
self.assertEquals(self.errors, "")
def test_postpone7(self):
command = PostponeCommand.PostponeCommand(["5", "1w"], self.todolist, self.out, self.error)
command.execute()
due = self.future + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, " 5 Future due:%s t:%s\n" % (due.isoformat(), self.future_start.isoformat()))
self.assertEquals(self.errors, "")
def test_postpone8(self):
command = PostponeCommand.PostponeCommand(["-s", "5", "1w"], self.todolist, self.out, self.error)
command.execute()
due = self.future + timedelta(7)
start = self.future_start + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertEquals(self.output, " 5 Future due:%s t:%s\n" % (due.isoformat(), start.isoformat()))
self.assertEquals(self.errors, "")
def test_postpone9(self):
command = PostponeCommand.PostponeCommand(["1", "foo"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertEquals(self.output, "")
self.assertEquals(self.errors, "Invalid date pattern given.\n")
def test_postpone10(self):
command = PostponeCommand.PostponeCommand(["99", "foo"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertEquals(self.output, "")
self.assertEquals(self.errors, "Invalid todo number given.\n")
def test_postpone11(self):
command = PostponeCommand.PostponeCommand(["A", "foo"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertEquals(self.output, "")
self.assertEquals(self.errors, "Invalid todo number given.\n")
def test_postpone12(self):
command = PostponeCommand.PostponeCommand(["1"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertEquals(self.output, "")
self.assertEquals(self.errors, command.usage() + "\n")
def test_help(self):
command = PostponeCommand.PostponeCommand(["help"], self.todolist, self.out, self.error)
command.execute()
self.assertEquals(self.output, "")
self.assertEquals(self.errors, command.usage() + "\n\n" + command.help() + "\n")
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