Commit 05cdba77 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Refactor get_field_function with giant if-else to dict lookups

Also, the label of a field is passed in the output of the group()
method.
parent 8ff2b1bf
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
""" This module provides functionality to sort lists with todo items. """ """ This module provides functionality to sort lists with todo items. """
from collections import OrderedDict from collections import OrderedDict, namedtuple
from itertools import groupby from itertools import groupby
import re import re
from datetime import date from datetime import date
...@@ -24,56 +24,91 @@ from datetime import date ...@@ -24,56 +24,91 @@ from datetime import date
from topydo.lib.Importance import average_importance, importance from topydo.lib.Importance import average_importance, importance
def is_priority_field(p_field): Field = namedtuple('Field', ['sort', 'group', 'label'])
""" Returns True when the field name denotes the priority. """
return p_field.startswith('prio')
def get_field_function(p_field, p_group=False): FIELDS = {
""" 'completed': Field(
Given a property (string) of a todo, return a function that attempts to # when a task has no completion date, push it to the end by assigning it
access that property. If the property could not be located, return the # the maximum possible date.
identity function. sort=(lambda t: t.completion_date() if t.completion_date() else date.max),
""" group=(lambda t: t.completion_date() if t.completion_date() else 'None'),
# default result label='Completed',
result = lambda a: a ),
'context': Field(
lower = lambda s: s if p_group else s.lower() sort=lambda t: sorted(c.lower() for c in t.contexts()),
group=lambda t: sorted(t.contexts()),
if is_priority_field(p_field): label='Context'
# assign dummy priority when a todo has no priority ),
result = lambda a: a.priority() or ('None' if p_group else 'ZZ') 'created': Field(
elif p_field == 'context' or p_field == 'contexts':
result = lambda a: sorted([lower(c) for c in a.contexts()])
elif p_field == 'creationdate' or p_field == 'creation':
# when a task has no creation date, push it to the end by assigning it # when a task has no creation date, push it to the end by assigning it
# the maximum possible date. # the maximum possible date.
result = (lambda a: a.creation_date() if a.creation_date() sort=(lambda t: t.creation_date() if t.creation_date() else date.max),
else ('None' if p_group else date.max)) group=(lambda t: t.creation_date() if t.creation_date() else 'None'),
elif p_field == 'done' or p_field == 'completed' or p_field == 'completion': label='Created',
result = (lambda a: a.completion_date() if a.completion_date() ),
else ('None' if p_group else date.max)) 'importance': Field(
elif p_field == 'importance': sort=importance,
result = importance group=importance,
elif p_field == 'importance-avg' or p_field == 'importance-average': label='Importance',
result = average_importance ),
elif p_field == 'length': 'importance-avg': Field(
result = lambda a: a.length() sort= average_importance,
elif p_field == 'project' or p_field == 'projects': group=average_importance,
result = lambda a: sorted([lower(p) for p in a.projects()]) label='Importance (avg)',
elif p_field == 'text': ),
result = lambda a: a.text() 'length': Field(
else: sort=lambda t: t.length(),
# try to find the corresponding tag group=lambda t: t.length(),
# when a tag is not present, push it to the end of the list by giving label='Length',
# it an artificially higher value ),
if p_group: 'priority': Field(
result = (lambda a: a.tag_value(p_field) if a.has_tag(p_field) else '') sort=(lambda t: t.priority() or 'ZZ'),
else: group=(lambda t: t.priority() or 'None'),
result = (lambda a: "0" + a.tag_value(p_field) if a.has_tag(p_field) label='Priority',
else "1") ),
'project': Field(
return result sort=lambda t: sorted(p.lower() for p in t.projects()),
group=lambda t: sorted(t.projects()),
label='Project',
),
'text': Field(
sort=lambda t: t.text().lower(),
group=lambda t: t.text(),
label='Text',
),
}
# map UI properties to properties in the FIELDS hash
FIELD_MAP = {
'completed': 'completed',
'completion': 'completed',
'completion_date': 'completed',
'done': 'completed',
'context': 'context',
'contexts': 'context',
'created': 'created',
'creation': 'created',
'creation_date': 'created',
'importance': 'importance',
'importance-avg': 'importance-avg',
'importance-average': 'importance-avg',
'length': 'length',
'len': 'length',
'prio': 'priority',
'priorities': 'priority',
'priority': 'priority',
'project': 'project',
'projects': 'project',
'text': 'text',
}
def _apply_sort_functions(p_todos, p_functions): def _apply_sort_functions(p_todos, p_functions):
sorted_todos = p_todos sorted_todos = p_todos
...@@ -136,7 +171,7 @@ class Sorter(object): ...@@ -136,7 +171,7 @@ class Sorter(object):
# initialize result with a single group # initialize result with a single group
result = OrderedDict([((), p_todos)]) result = OrderedDict([((), p_todos)])
for function, _ in self.groupfunctions: for (function, label), _ in self.groupfunctions:
oldresult = result oldresult = result
result = OrderedDict() result = OrderedDict()
for oldkey, oldgroup in oldresult.items(): for oldkey, oldgroup in oldresult.items():
...@@ -147,6 +182,7 @@ class Sorter(object): ...@@ -147,6 +182,7 @@ class Sorter(object):
key = [key] key = [key]
for subkey in key: for subkey in key:
subkey = "{}: {}".format(label, subkey)
newkey = oldkey + (subkey,) newkey = oldkey + (subkey,)
if newkey in result: if newkey in result:
...@@ -162,9 +198,26 @@ class Sorter(object): ...@@ -162,9 +198,26 @@ class Sorter(object):
def _parse(self, p_string, p_group): def _parse(self, p_string, p_group):
""" """
Parses a sort string and returns a list of functions and the Parses a sort/group string and returns a list of functions and the
desired order. desired order.
""" """
def get_field_function(p_field, p_group=False):
"""
Turns a field, part of a sort/group string, into a lambda that
takes a todo item and returns the field value.
"""
compose = lambda i: i.sort if not p_group else (i.group, i.label)
if p_field in FIELD_MAP:
return compose(FIELDS[FIELD_MAP[p_field]])
else:
# treat it as a tag value
return compose(Field(
sort=lambda t: '0' + t.tag_value(p_field) if t.has_tag(p_field) else '1',
group=lambda t: t.tag_value(p_field) if t.has_tag(p_field) else '',
label=p_field,
))
result = [] result = []
fields = p_string.lower().split(',') fields = p_string.lower().split(',')
...@@ -185,7 +238,7 @@ class Sorter(object): ...@@ -185,7 +238,7 @@ class Sorter(object):
# reverse order for priority: lower characters have higher # reverse order for priority: lower characters have higher
# priority # priority
if is_priority_field(field): if field in FIELD_MAP and FIELD_MAP[field] == 'priority':
order = 'asc' if order == 'desc' else 'desc' order = 'asc' if order == 'desc' else 'desc'
result.append((function, order)) result.append((function, order))
......
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