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
25162351
Commit
25162351
authored
Jun 15, 2014
by
Bram Schoenmakers
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add dependency support to the todo list.
parent
8d1f2e16
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
161 additions
and
3 deletions
+161
-3
TodoList.py
TodoList.py
+102
-3
test/TodoListTest.py
test/TodoListTest.py
+59
-0
No files found.
TodoList.py
View file @
25162351
...
@@ -4,6 +4,7 @@ A list of todo items.
...
@@ -4,6 +4,7 @@ A list of todo items.
import
re
import
re
import
Graph
from
PrettyPrinter
import
pretty_print
from
PrettyPrinter
import
pretty_print
import
Todo
import
Todo
import
View
import
View
...
@@ -23,6 +24,8 @@ class TodoList(object):
...
@@ -23,6 +24,8 @@ class TodoList(object):
The string will be parsed.
The string will be parsed.
"""
"""
self
.
_todos
=
[]
self
.
_todos
=
[]
self
.
_depgraph
=
Graph
.
DirectedGraph
()
for
string
in
p_todostrings
:
for
string
in
p_todostrings
:
self
.
add
(
string
)
self
.
add
(
string
)
...
@@ -40,9 +43,32 @@ class TodoList(object):
...
@@ -40,9 +43,32 @@ class TodoList(object):
return
result
return
result
def
todo_by_dep_id
(
self
,
p_dep_id
):
"""
Returns the todo that has the id tag set to the value p_dep_id.
There is only one such task, the behavior is undefined when a tag has
more than one id tag.
"""
hits
=
[
t
for
t
in
self
.
_todos
if
t
.
tag_value
(
'id'
)
==
p_dep_id
]
return
hits
[
0
]
if
len
(
hits
)
else
None
def
add
(
self
,
p_src
):
def
add
(
self
,
p_src
):
"""
"""
Given a todo string, parse it and put it to the end of the list.
Given a todo string, parse it and put it to the end of the list.
Also maintains the dependency graph to track the dependencies between
tasks.
The node ids are the todo numbers.
The edge ids are the numbers denoted by id: and p: tags.
For example:
(C) Parent task id:4
(B) Child task p:4
Then there will be an edge 1 --> 2 with ID 4.
"""
"""
if
re
.
search
(
r'\
S
', p_src):
if
re
.
search
(
r'\
S
', p_src):
...
@@ -50,12 +76,27 @@ class TodoList(object):
...
@@ -50,12 +76,27 @@ class TodoList(object):
todo = Todo.Todo(p_src, number)
todo = Todo.Todo(p_src, number)
self._todos.append(todo)
self._todos.append(todo)
# maintain dependency graph
if todo.has_tag('
id
'):
self._depgraph.add_node(todo.number)
for child in todo.tag_values('
p
'):
parent = self.todo_by_dep_id(child)
if parent:
self._depgraph.add_edge(parent.number, todo.number, child)
def delete(self, p_number):
def delete(self, p_number):
""" Deletes a todo item from the list. """
""" Deletes a todo item from the list. """
try:
todo = self.todo(p_number)
if todo:
for child in self.children(p_number):
self.remove_dependency(todo.number, child.number)
for parent in self.parents(p_number):
self.remove_dependency(parent.number, todo.number)
del self._todos[p_number - 1]
del self._todos[p_number - 1]
except IndexError:
pass # just ignore it
def count(self):
def count(self):
""" Returns the number of todos on this list. """
""" Returns the number of todos on this list. """
...
@@ -100,6 +141,64 @@ class TodoList(object):
...
@@ -100,6 +141,64 @@ class TodoList(object):
"""
"""
return View.View(p_sorter, p_filters, self._todos)
return View.View(p_sorter, p_filters, self._todos)
def add_dependency(self, p_number1, p_number2):
""" Adds a dependency from task 1 to task 2. """
def find_next_id():
"""
Find a new unused ID.
Unused means that no task has it as an '
id
' value or as a '
p
'
value.
"""
new_id = 1
while self._depgraph.has_edge_id(new_id):
new_id += 1
return '
%
d
' % new_id
from_todo = self.todo(p_number1)
to_todo = self.todo(p_number2)
dep_id = None
if from_todo.has_tag('
id
'):
dep_id = from_todo.tag_value('
id
')
else:
dep_id = find_next_id()
to_todo.add_tag('
p
', dep_id)
self._depgraph.add_edge(p_number1, p_number2, int(dep_id))
def remove_dependency(self, p_number1, p_number2):
""" Removes a dependency between two todos. """
from_todo = self.todo(p_number1)
to_todo = self.todo(p_number2)
dep_id = from_todo.tag_value('
id
')
if dep_id:
to_todo.remove_tag('
p
', dep_id)
self._depgraph.remove_edge(p_number1, p_number2)
if not self.children(p_number1, True):
from_todo.remove_tag('
id
')
def parents(self, p_number, p_only_direct=False):
"""
Returns a list of parent todos that (in)directly depend on the
given todo.
"""
parents = self._depgraph.incoming_neighbors(p_number, not p_only_direct)
return [self.todo(parent) for parent in parents]
def children(self, p_number, p_only_direct=False):
"""
Returns a list of child todos that the given todo (in)directly depends
on.
"""
children =
\
self._depgraph.outgoing_neighbors(p_number, not p_only_direct)
return [self.todo(child) for child in children]
def __str__(self):
def __str__(self):
return '
\
n
'.join(pretty_print(self._todos))
return '
\
n
'.join(pretty_print(self._todos))
test/TodoListTest.py
View file @
25162351
...
@@ -115,3 +115,62 @@ class TodoListTester(unittest.TestCase):
...
@@ -115,3 +115,62 @@ class TodoListTester(unittest.TestCase):
def
test_count
(
self
):
def
test_count
(
self
):
""" Test that empty lines are not counted. """
""" Test that empty lines are not counted. """
self
.
assertEquals
(
self
.
todolist
.
count
(),
5
)
self
.
assertEquals
(
self
.
todolist
.
count
(),
5
)
def
test_todo_by_dep_id
(
self
):
""" Tests that todos can be retrieved by their id tag. """
self
.
todolist
.
add
(
"(C) Foo id:1"
)
self
.
assertTrue
(
self
.
todolist
.
todo_by_dep_id
(
'1'
))
self
.
assertFalse
(
self
.
todolist
.
todo_by_dep_id
(
'2'
))
class
TodoListDependencyTester
(
unittest
.
TestCase
):
def
setUp
(
self
):
self
.
todolist
=
TodoList
.
TodoList
([])
self
.
todolist
.
add
(
"Foo id:1"
)
self
.
todolist
.
add
(
"Bar p:1"
)
self
.
todolist
.
add
(
"Baz p:1 id:2"
)
self
.
todolist
.
add
(
"Buzz p:2"
)
def
test_check_dep
(
self
):
children
=
self
.
todolist
.
children
(
1
)
self
.
assertEqual
([
todo
.
source
()
for
todo
in
children
],
\
[
'Bar p:1'
,
'Baz p:1 id:2'
,
'Buzz p:2'
])
children
=
self
.
todolist
.
children
(
1
,
True
)
self
.
assertEqual
([
todo
.
source
()
for
todo
in
children
],
\
[
'Bar p:1'
,
'Baz p:1 id:2'
])
children
=
self
.
todolist
.
children
(
3
)
self
.
assertEqual
([
todo
.
source
()
for
todo
in
children
],
\
[
'Buzz p:2'
])
parents
=
self
.
todolist
.
parents
(
4
)
self
.
assertEqual
([
todo
.
source
()
for
todo
in
parents
],
\
[
'Foo id:1'
,
'Baz p:1 id:2'
])
parents
=
self
.
todolist
.
parents
(
4
,
True
)
self
.
assertEqual
([
todo
.
source
()
for
todo
in
parents
],
\
[
'Baz p:1 id:2'
])
self
.
assertEqual
(
self
.
todolist
.
children
(
2
),
[])
self
.
assertEqual
(
self
.
todolist
.
parents
(
1
),
[])
def
test_remove_dep1
(
self
):
self
.
todolist
.
remove_dependency
(
3
,
4
)
self
.
assertFalse
(
self
.
todolist
.
todo
(
3
).
has_tag
(
'id'
))
self
.
assertFalse
(
self
.
todolist
.
todo
(
4
).
has_tag
(
'p'
))
def
test_remove_dep2
(
self
):
old
=
str
(
self
.
todolist
)
self
.
todolist
.
remove_dependency
(
1
,
4
)
self
.
assertEquals
(
str
(
self
.
todolist
),
old
)
def
test_remove_task
(
self
):
self
.
todolist
.
delete
(
3
)
self
.
assertFalse
(
self
.
todolist
.
todo
(
3
).
has_tag
(
'p'
,
'2'
))
children
=
self
.
todolist
.
children
(
1
)
self
.
assertEqual
([
todo
.
source
()
for
todo
in
children
],
\
[
'Bar p:1'
])
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