Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
topydo
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
topydo
Commits
4ba571ca
Commit
4ba571ca
authored
Jan 15, 2015
by
Bram Schoenmakers
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow 'do' and 'del' to complete/delete multiple todos at once.
Fixes issue #1.
parent
7b4ccc57
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
135 additions
and
53 deletions
+135
-53
test/DeleteCommandTest.py
test/DeleteCommandTest.py
+21
-0
test/DoCommandTest.py
test/DoCommandTest.py
+47
-1
topydo/lib/DCommand.py
topydo/lib/DCommand.py
+22
-16
topydo/lib/DeleteCommand.py
topydo/lib/DeleteCommand.py
+7
-6
topydo/lib/DoCommand.py
topydo/lib/DoCommand.py
+16
-16
topydo/lib/TodoList.py
topydo/lib/TodoList.py
+13
-9
topydo/lib/TodoListBase.py
topydo/lib/TodoListBase.py
+9
-5
No files found.
test/DeleteCommandTest.py
View file @
4ba571ca
...
@@ -116,6 +116,27 @@ class DeleteCommandTest(CommandTest.CommandTest):
...
@@ -116,6 +116,27 @@ class DeleteCommandTest(CommandTest.CommandTest):
self
.
assertEquals
(
str
(
self
.
todolist
),
"Foo"
)
self
.
assertEquals
(
str
(
self
.
todolist
),
"Foo"
)
self
.
assertRaises
(
InvalidTodoException
,
self
.
todolist
.
todo
,
'b0n'
)
self
.
assertRaises
(
InvalidTodoException
,
self
.
todolist
.
todo
,
'b0n'
)
def
test_multi_del1
(
self
):
""" Test deletion of multiple items. """
command
=
DeleteCommand
([
"1"
,
"2"
],
self
.
todolist
,
self
.
out
,
self
.
error
,
_no_prompt
)
command
.
execute
()
self
.
assertEquals
(
self
.
todolist
.
count
(),
0
)
def
test_multi_del2
(
self
):
""" Test deletion of multiple items. """
command
=
DeleteCommand
([
"1"
,
"2"
],
self
.
todolist
,
self
.
out
,
self
.
error
,
_yes_prompt
)
command
.
execute
()
self
.
assertEquals
(
self
.
todolist
.
count
(),
0
)
def
test_multi_del3
(
self
):
""" Test deletion of multiple items. """
command
=
DeleteCommand
([
"99"
,
"2"
],
self
.
todolist
,
self
.
out
,
self
.
error
,
_yes_prompt
)
command
.
execute
()
self
.
assertEquals
(
self
.
todolist
.
count
(),
1
)
def
test_empty
(
self
):
def
test_empty
(
self
):
command
=
DeleteCommand
([],
self
.
todolist
,
self
.
out
,
self
.
error
)
command
=
DeleteCommand
([],
self
.
todolist
,
self
.
out
,
self
.
error
)
command
.
execute
()
command
.
execute
()
...
...
test/DoCommandTest.py
View file @
4ba571ca
# Topydo - A todo.txt client written in Python.
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014
- 2015
Bram Schoenmakers <me@bramschoenmakers.nl>
#
#
# This program is free software: you can redistribute it and/or modify
# 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
# it under the terms of the GNU General Public License as published by
...
@@ -279,6 +279,52 @@ class DoCommandTest(CommandTest.CommandTest):
...
@@ -279,6 +279,52 @@ class DoCommandTest(CommandTest.CommandTest):
self
.
assertEquals
(
self
.
output
,
"| 9| {today} Strict due:2014-01-02 rec:1d
\
n
Completed: x {yesterday} Strict due:2014-01-01 rec:1d
\
n
"
.
format
(
today
=
self
.
today
,
yesterday
=
self
.
yesterday
))
self
.
assertEquals
(
self
.
output
,
"| 9| {today} Strict due:2014-01-02 rec:1d
\
n
Completed: 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
):
command
=
DoCommand
([
"1"
,
"3"
],
self
.
todolist
,
self
.
out
,
self
.
error
,
_yes_prompt
)
command
.
execute
()
self
.
assertTrue
(
self
.
todolist
.
todo
(
1
).
is_completed
())
self
.
assertTrue
(
self
.
todolist
.
todo
(
2
).
is_completed
())
self
.
assertTrue
(
self
.
todolist
.
todo
(
3
).
is_completed
())
self
.
assertEquals
(
self
.
output
,
"| 2| Bar p:1
\
n
| 3| Baz p:1
\
n
Completed: x {today} Bar p:1
\
n
Completed: x {today} Baz p:1
\
n
Completed: x {today} Foo id:1
\
n
"
.
format
(
today
=
self
.
today
))
def
test_multi_do2
(
self
):
command
=
DoCommand
([
"1"
,
"3"
],
self
.
todolist
,
self
.
out
,
self
.
error
,
_no_prompt
)
command
.
execute
()
self
.
assertTrue
(
self
.
todolist
.
todo
(
1
).
is_completed
())
self
.
assertFalse
(
self
.
todolist
.
todo
(
2
).
is_completed
())
self
.
assertTrue
(
self
.
todolist
.
todo
(
3
).
is_completed
())
self
.
assertEquals
(
self
.
output
,
"| 2| Bar p:1
\
n
| 3| Baz p:1
\
n
Completed: x {today} Foo id:1
\
n
Completed: x {today} Baz p:1
\
n
"
.
format
(
today
=
self
.
today
))
def
test_multi_do3
(
self
):
command
=
DoCommand
([
"3"
,
"3"
],
self
.
todolist
,
self
.
out
,
self
.
error
,
_no_prompt
)
command
.
execute
()
self
.
assertTrue
(
self
.
todolist
.
todo
(
3
).
is_completed
())
self
.
assertEquals
(
self
.
output
,
"Completed: x {} Baz p:1
\
n
"
.
format
(
self
.
today
))
def
test_multi_do4
(
self
):
command
=
DoCommand
([
"99"
,
"3"
],
self
.
todolist
,
self
.
out
,
self
.
error
,
_no_prompt
)
command
.
execute
()
self
.
assertTrue
(
self
.
todolist
.
todo
(
3
).
is_completed
())
self
.
assertEquals
(
self
.
output
,
"Completed: x {} Baz p:1
\
n
"
.
format
(
self
.
today
))
self
.
assertEquals
(
self
.
errors
,
"Invalid todo number given.
\
n
"
)
def
test_multi_do5
(
self
):
"""
When a todo item was generated by a recurring todo item, make sure
it cannot be completed in the same invocation.
"""
command
=
DoCommand
([
"4"
,
"9"
],
self
.
todolist
,
self
.
out
,
self
.
error
,
_no_prompt
)
command
.
execute
()
self
.
assertTrue
(
self
.
todolist
.
todo
(
4
).
is_completed
())
self
.
assertFalse
(
self
.
todolist
.
todo
(
9
).
is_completed
())
def
test_empty
(
self
):
def
test_empty
(
self
):
command
=
DoCommand
([],
self
.
todolist
,
self
.
out
,
self
.
error
)
command
=
DoCommand
([],
self
.
todolist
,
self
.
out
,
self
.
error
)
command
.
execute
()
command
.
execute
()
...
...
topydo/lib/DCommand.py
View file @
4ba571ca
# Topydo - A todo.txt client written in Python.
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014
- 2015
Bram Schoenmakers <me@bramschoenmakers.nl>
#
#
# This program is free software: you can redistribute it and/or modify
# 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
# it under the terms of the GNU General Public License as published by
...
@@ -38,10 +38,12 @@ class DCommand(Command):
...
@@ -38,10 +38,12 @@ class DCommand(Command):
self
.
process_flags
()
self
.
process_flags
()
self
.
length
=
len
(
self
.
todolist
.
todos
())
# to determine newly activated todos
self
.
length
=
len
(
self
.
todolist
.
todos
())
# to determine newly activated todos
try
:
self
.
todos
=
[]
self
.
todo
=
self
.
todolist
.
todo
(
self
.
argument
(
0
))
for
number
in
self
.
args
:
except
(
InvalidCommandArgument
,
InvalidTodoException
):
try
:
self
.
todo
=
None
self
.
todos
.
append
(
self
.
todolist
.
todo
(
number
))
except
InvalidTodoException
:
self
.
todos
.
append
(
None
)
def
get_flags
(
self
):
def
get_flags
(
self
):
""" Default implementation of getting specific flags. """
""" Default implementation of getting specific flags. """
...
@@ -80,8 +82,8 @@ class DCommand(Command):
...
@@ -80,8 +82,8 @@ class DCommand(Command):
""" Prefix to use when printing a todo. """
""" Prefix to use when printing a todo. """
return
""
return
""
def
_process_subtasks
(
self
):
def
_process_subtasks
(
self
,
p_todo
):
children
=
self
.
_uncompleted_children
(
self
.
todo
)
children
=
self
.
_uncompleted_children
(
p_
todo
)
if
children
:
if
children
:
self
.
_print_list
(
children
)
self
.
_print_list
(
children
)
...
@@ -112,7 +114,7 @@ class DCommand(Command):
...
@@ -112,7 +114,7 @@ class DCommand(Command):
return
[
todo
for
todo
in
self
.
todolist
.
todos
()[:
self
.
length
]
return
[
todo
for
todo
in
self
.
todolist
.
todos
()[:
self
.
length
]
if
not
self
.
_uncompleted_children
(
todo
)
and
todo
.
is_active
()]
if
not
self
.
_uncompleted_children
(
todo
)
and
todo
.
is_active
()]
def
condition
(
self
):
def
condition
(
self
,
p_todo
):
"""
"""
An additional condition whether execute_specific should be executed.
An additional condition whether execute_specific should be executed.
"""
"""
...
@@ -121,7 +123,7 @@ class DCommand(Command):
...
@@ -121,7 +123,7 @@ class DCommand(Command):
def
condition_failed_text
(
self
):
def
condition_failed_text
(
self
):
return
""
return
""
def
execute_specific
(
self
):
def
execute_specific
(
self
,
_
):
pass
pass
def
execute_specific_core
(
self
,
p_todo
):
def
execute_specific_core
(
self
,
p_todo
):
...
@@ -137,14 +139,18 @@ class DCommand(Command):
...
@@ -137,14 +139,18 @@ class DCommand(Command):
if
len
(
self
.
args
)
==
0
:
if
len
(
self
.
args
)
==
0
:
self
.
error
(
self
.
usage
())
self
.
error
(
self
.
usage
())
elif
not
self
.
todo
:
else
:
self
.
error
(
"Invalid todo number given."
)
elif
self
.
todo
and
self
.
condition
():
old_active
=
self
.
_active_todos
()
old_active
=
self
.
_active_todos
()
self
.
_process_subtasks
()
self
.
execute_specific
()
for
todo
in
self
.
todos
:
if
not
todo
:
self
.
error
(
"Invalid todo number given."
)
elif
todo
and
self
.
condition
(
todo
):
self
.
_process_subtasks
(
todo
)
self
.
execute_specific
(
todo
)
else
:
self
.
error
(
self
.
condition_failed_text
())
current_active
=
self
.
_active_todos
()
current_active
=
self
.
_active_todos
()
self
.
_print_unlocked_todos
(
old_active
,
current_active
)
self
.
_print_unlocked_todos
(
old_active
,
current_active
)
else
:
self
.
error
(
self
.
condition_failed_text
())
topydo/lib/DeleteCommand.py
View file @
4ba571ca
# Topydo - A todo.txt client written in Python.
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014
- 2015
Bram Schoenmakers <me@bramschoenmakers.nl>
#
#
# This program is free software: you can redistribute it and/or modify
# 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
# it under the terms of the GNU General Public License as published by
...
@@ -34,12 +34,13 @@ class DeleteCommand(DCommand):
...
@@ -34,12 +34,13 @@ class DeleteCommand(DCommand):
def
execute_specific_core
(
self
,
p_todo
):
def
execute_specific_core
(
self
,
p_todo
):
self
.
todolist
.
delete
(
p_todo
)
self
.
todolist
.
delete
(
p_todo
)
def
execute_specific
(
self
):
def
execute_specific
(
self
,
p_todo
):
self
.
out
(
self
.
prefix
()
+
pretty_print
(
self
.
todo
))
self
.
out
(
self
.
prefix
()
+
pretty_print
(
p_
todo
))
self
.
execute_specific_core
(
self
.
todo
)
self
.
execute_specific_core
(
p_
todo
)
def
usage
(
self
):
def
usage
(
self
):
return
"""Synopsis: del [-f] <NUMBER
>
"""
return
"""Synopsis: del [-f] <NUMBER
1> [<NUMBER2> ...]
"""
def
help
(
self
):
def
help
(
self
):
return
"""Deletes the todo item with the given number from the list."""
return
"""
\
Deletes the todo item(s) with the given number(s) from the list."""
topydo/lib/DoCommand.py
View file @
4ba571ca
...
@@ -46,13 +46,13 @@ class DoCommand(DCommand):
...
@@ -46,13 +46,13 @@ class DoCommand(DCommand):
except
ValueError
:
except
ValueError
:
self
.
completion_date
=
date
.
today
()
self
.
completion_date
=
date
.
today
()
def
_handle_recurrence
(
self
):
def
_handle_recurrence
(
self
,
p_todo
):
if
self
.
todo
.
has_tag
(
'rec'
):
if
p_
todo
.
has_tag
(
'rec'
):
if
self
.
strict_recurrence
:
if
self
.
strict_recurrence
:
new_todo
=
strict_advance_recurring_todo
(
self
.
todo
,
new_todo
=
strict_advance_recurring_todo
(
p_
todo
,
self
.
completion_date
)
self
.
completion_date
)
else
:
else
:
new_todo
=
advance_recurring_todo
(
self
.
todo
,
new_todo
=
advance_recurring_todo
(
p_
todo
,
self
.
completion_date
)
self
.
completion_date
)
self
.
todolist
.
add_todo
(
new_todo
)
self
.
todolist
.
add_todo
(
new_todo
)
...
@@ -64,20 +64,20 @@ class DoCommand(DCommand):
...
@@ -64,20 +64,20 @@ class DoCommand(DCommand):
def
prefix
(
self
):
def
prefix
(
self
):
return
"Completed: "
return
"Completed: "
def
condition
(
self
):
def
condition
(
self
,
p_todo
):
"""
"""
An additional condition whether execute_specific should be executed.
An additional condition whether execute_specific should be executed.
"""
"""
return
not
self
.
todo
.
is_completed
()
return
not
p_
todo
.
is_completed
()
def
condition_failed_text
(
self
):
def
condition_failed_text
(
self
):
return
"Todo has already been completed."
return
"Todo has already been completed."
def
execute_specific
(
self
):
def
execute_specific
(
self
,
p_todo
):
""" Actions specific to this command. """
""" Actions specific to this command. """
self
.
_handle_recurrence
()
self
.
_handle_recurrence
(
p_todo
)
self
.
execute_specific_core
(
self
.
todo
)
self
.
execute_specific_core
(
p_
todo
)
self
.
out
(
self
.
prefix
()
+
pretty_print
(
self
.
todo
))
self
.
out
(
self
.
prefix
()
+
pretty_print
(
p_
todo
))
def
execute_specific_core
(
self
,
p_todo
):
def
execute_specific_core
(
self
,
p_todo
):
"""
"""
...
@@ -87,16 +87,16 @@ class DoCommand(DCommand):
...
@@ -87,16 +87,16 @@ class DoCommand(DCommand):
self
.
todolist
.
set_todo_completed
(
p_todo
,
self
.
completion_date
)
self
.
todolist
.
set_todo_completed
(
p_todo
,
self
.
completion_date
)
def
usage
(
self
):
def
usage
(
self
):
return
"""Synopsis: do [--date] [--force] [--strict] <NUMBER
>
"""
return
"""Synopsis: do [--date] [--force] [--strict] <NUMBER
1> [<NUMBER2> ...]
"""
def
help
(
self
):
def
help
(
self
):
return
"""Marks the todo
with given number
as complete.
return
"""Marks the todo
(s) with given number(s)
as complete.
In case
the todo has subitems, a question is asked whether the subitems should
In case
a todo has subitems, a question is asked whether the subitems should be
be marked as completed as well. When --force is given, no interaction is
marked as completed as well. When --force is given, no interaction is required
required
and the subitems are not marked completed.
and the subitems are not marked completed.
In case
the
completed todo is recurring, a new todo will be added to the list,
In case
a
completed todo is recurring, a new todo will be added to the list,
while the given todo item is marked as complete. The new date is calculated
while the given todo item is marked as complete. The new date is calculated
based on the todo item's due date. If the due date is in the past, today's date
based on the todo item's due date. If the due date is in the past, today's date
is used to calculate the new recurrence date. Using --strict prevents this,
is used to calculate the new recurrence date. Using --strict prevents this,
...
...
topydo/lib/TodoList.py
View file @
4ba571ca
# Topydo - A todo.txt client written in Python.
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014
- 2015
Bram Schoenmakers <me@bramschoenmakers.nl>
#
#
# This program is free software: you can redistribute it and/or modify
# 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
# it under the terms of the GNU General Public License as published by
...
@@ -90,18 +90,22 @@ class TodoList(TodoListBase):
...
@@ -90,18 +90,22 @@ class TodoList(TodoListBase):
def
delete
(
self
,
p_todo
):
def
delete
(
self
,
p_todo
):
""" Deletes a todo item from the list. """
""" Deletes a todo item from the list. """
number
=
self
.
_todos
.
index
(
p_todo
)
try
:
number
=
self
.
_todos
.
index
(
p_todo
)
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
)
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
)
del
self
.
_todos
[
number
]
del
self
.
_todos
[
number
]
self
.
_update_todo_ids
()
self
.
_update_todo_ids
()
self
.
dirty
=
True
self
.
dirty
=
True
except
ValueError
:
# todo item couldn't be found, ignore
pass
def
add_dependency
(
self
,
p_from_todo
,
p_to_todo
):
def
add_dependency
(
self
,
p_from_todo
,
p_to_todo
):
""" Adds a dependency from task 1 to task 2. """
""" Adds a dependency from task 1 to task 2. """
...
...
topydo/lib/TodoListBase.py
View file @
4ba571ca
# Topydo - A todo.txt client written in Python.
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014
- 2015
Bram Schoenmakers <me@bramschoenmakers.nl>
#
#
# This program is free software: you can redistribute it and/or modify
# 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
# it under the terms of the GNU General Public License as published by
...
@@ -153,10 +153,14 @@ class TodoListBase(object):
...
@@ -153,10 +153,14 @@ class TodoListBase(object):
def delete(self, p_todo):
def delete(self, p_todo):
""" Deletes a todo item from the list. """
""" Deletes a todo item from the list. """
number = self._todos.index(p_todo)
try:
del self._todos[number]
number = self._todos.index(p_todo)
self._update_todo_ids()
del self._todos[number]
self.dirty = True
self._update_todo_ids()
self.dirty = True
except ValueError:
# todo item couldn'
t
be
found
,
ignore
pass
def
erase
(
self
):
def
erase
(
self
):
""" Erases all todos from the list. """
""" Erases all todos from the list. """
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment