1
0
Fork 0
mirror of https://github.com/topydo/topydo.git synced 2024-06-02 18:48:33 +00:00

Add postpone subcommand.

This commit is contained in:
Bram Schoenmakers 2014-10-27 22:01:39 +01:00
parent 99ca605763
commit 1b7b8412f1
4 changed files with 261 additions and 0 deletions

View file

@ -39,6 +39,7 @@ Subcommands implemented:
* ls
* lscon
* lsprj
* postpone [x]
* pri
* sort [xx]
* tag [x]

View file

@ -32,6 +32,7 @@ from DoCommand import DoCommand
from ListCommand import ListCommand
from ListContextCommand import ListContextCommand
from ListProjectCommand import ListProjectCommand
from PostponeCommand import PostponeCommand
from PrettyPrinter import *
from PriorityCommand import PriorityCommand
from SortCommand import SortCommand
@ -119,6 +120,7 @@ class CLIApplication(object):
'listproj': ListProjectCommand,
'listproject': ListProjectCommand,
'listprojects': ListProjectCommand,
'postpone': PostponeCommand,
'pri': PriorityCommand,
'rm': DeleteCommand,
'sort': SortCommand,

97
lib/PostponeCommand.py Normal file
View file

@ -0,0 +1,97 @@
# 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."""

161
test/PostponeCommandTest.py Normal file
View file

@ -0,0 +1,161 @@
# 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")