Commit 03fd7426 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Implement average importance.

The average importance of a todo item the average of the importance of
its parents (including its own importance).

This requires some data to ship together with each todo item, since the
sorter function needs access to the parents. Because its unaware of
TodoList which maintains the hierarchy, copy a bit of that information
into the Todo itself.
parent b1a99d64
"""
Provides a function to calculate the importance value of a task.
Provides functions to calculate the importance value of a task.
For those who are familiar with the Toodledo website, the importance value is a
combination of the priority and the todo's due date. Low priority tasks due
......@@ -57,3 +57,14 @@ def importance(p_todo, p_ignore_weekend=False):
result += 1
return result if not p_todo.is_completed() else 0
def average_importance(p_todo, p_ignore_weekend=False):
sum_importance = importance(p_todo, p_ignore_weekend)
parents = []
if 'parents' in p_todo.attributes:
parents = p_todo.attributes['parents']
for parent in parents:
sum_importance += importance(parent, p_ignore_weekend)
return float(sum_importance) / float(1 + len(parents))
......@@ -4,7 +4,7 @@ import datetime
import re
import Config
from Importance import importance
from Importance import importance, average_importance
def is_priority_field(p_field):
""" Returns True when the field name denotes the priority. """
......@@ -30,6 +30,8 @@ def get_field_function(p_field):
else datetime.date.max)
elif p_field == 'importance':
result = lambda a: importance(a, Config.IGNORE_WEEKENDS)
elif p_field == 'importance-avg' or p_field == 'importance-average':
result = lambda a: average_importance(a, Config.IGNORE_WEEKENDS)
elif p_field == 'text':
result = lambda a: a.text()
else:
......
......@@ -16,6 +16,7 @@ class Todo(TodoBase.TodoBase):
def __init__(self, p_str, p_number=-1):
TodoBase.TodoBase.__init__(self, p_str, p_number)
self.attributes = {}
def get_date(self, p_tag):
""" Given a date tag, return a date object. """
......
......@@ -101,6 +101,7 @@ class TodoList(object):
self._todos.append(todo)
self._maintain_dep_graph(todo)
self._update_parent_cache()
return todo
......@@ -193,6 +194,7 @@ class TodoList(object):
to_todo.add_tag('p', dep_id)
self._depgraph.add_edge(p_number1, p_number2, int(dep_id))
self._update_parent_cache()
def remove_dependency(self, p_number1, p_number2):
""" Removes a dependency between two todos. """
......@@ -207,6 +209,7 @@ class TodoList(object):
if dep_id:
to_todo.remove_tag('p', dep_id)
self._depgraph.remove_edge(p_number1, p_number2)
self._update_parent_cache()
if not self.children(p_number1, True):
from_todo.remove_tag('id')
......@@ -247,6 +250,17 @@ class TodoList(object):
clean_by_tag('p')
clean_by_tag('id')
def _update_parent_cache(self):
"""
Sets the attribute to the list of parents, such that others may access
it outside this todo list.
This is used for calculating the average importance, that requires
access to a todo's parents.
"""
for todo in self._todos:
todo.attributes['parents'] = self.parents(todo.number)
def __str__(self):
return '\n'.join(pretty_print(self._todos))
import pdb
import unittest
import Sorter
from TestFacilities import load_file, todolist_to_string
import TodoList
import View
from TestFacilities import load_file, todolist_to_string, load_file_to_todolist
class SorterTest(unittest.TestCase):
def sort_file(self,p_filename, p_filename_ref, p_sorter):
......@@ -87,3 +91,37 @@ class SorterTest(unittest.TestCase):
sorter = Sorter.Sorter('fnord')
self.sort_file('data/SorterTest9.txt', 'data/SorterTest9.txt', sorter)
def test_sort12(self):
"""
Descendingly sorted by average importance.
Reusing input and output for normal importance test, since without
dependencies the average importance should be equal.
"""
sorter = Sorter.Sorter('desc:importance-avg')
self.sort_file('data/SorterTest9.txt', 'data/SorterTest9-result.txt', sorter)
def test_sort13(self):
sorter = Sorter.Sorter('desc:importance-average')
pdb.set_trace()
todolist = load_file_to_todolist('data/SorterTest10.txt')
view = todolist.view(sorter, [])
result = load_file('data/SorterTest10-result.txt')
self.assertEquals(str(view), todolist_to_string(result))
def test_sort14(self):
"""
Test that own importance is used when average turns out to be
lower.
"""
sorter = Sorter.Sorter('desc:importance-average')
pdb.set_trace()
todolist = load_file_to_todolist('data/SorterTest11.txt')
view = todolist.view(sorter, [])
result = load_file('data/SorterTest11-result.txt')
self.assertEquals(str(view), todolist_to_string(result))
import Todo
import TodoFile
import TodoList
def load_file(p_filename):
"""
......@@ -16,6 +17,13 @@ def load_file_to_raw_list(p_filename):
todofile = TodoFile.TodoFile(p_filename)
return todofile.read()
def load_file_to_todolist(p_filename):
"""
Loads a todo file to a TodoList instance.
"""
todolist = load_file_to_raw_list(p_filename)
return TodoList.TodoList(todolist)
def todolist_to_string(p_list):
""" Converts a todo list to a single string. """
return '\n'.join([str(t) for t in p_list])
(A) Important parent task id:1 (5)
(A) Another important task id:2 (5)
(C) Less important child task p:1 p:2 ((3+5+5)/3=4.3)
(B) Normal task (4)
(B) Normal task (4)
(A) Important parent task id:1 (5)
(A) Another important task id:2 (5)
(C) Less important child task p:1 p:2 ((3+5+5)/3=4.3)
(B) Normal task (4)
(C) Less important child task p:1 p:2 ((3+0+0)/3=1 -> 3)
(D) Not-so important parent task id:1 (2)
(D) Another not-so important task id:2 (2)
(B) Normal task (4)
(D) Not-so important parent task id:1 (2)
(D) Another not-so important task id:2 (2)
(C) Less important child task p:1 p:2 ((3+0+0)/3=1 -> 3)
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