Commit 7df85792 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Make 'topydo ls foo:somestring' work.

If there was a todo with 'foo:somestring', it wouldn't be returned with
the command above, because somestring couldn't be converted to a date or
an integer, and therefore the OrdinalTagFilter just returns False.

As a final fallback, use a GrepFilter match to do the string comparison
such that the todo item(s) with this key:value combination will be
returned.
parent 895a99bb
...@@ -307,7 +307,7 @@ class OrdinalTagFilterTest(TopydoTest): ...@@ -307,7 +307,7 @@ class OrdinalTagFilterTest(TopydoTest):
self.todo1 = "Foo due:{}".format(self.today) self.todo1 = "Foo due:{}".format(self.today)
self.todo2 = "Bar due:{}".format(self.tomorrow) self.todo2 = "Bar due:{}".format(self.tomorrow)
self.todo3 = "Baz due:nonsense" self.todo3 = "Baz due:Nonsense"
self.todo4 = "Fnord due:2014-10-32" self.todo4 = "Fnord due:2014-10-32"
self.todos = [ self.todos = [
...@@ -358,6 +358,22 @@ class OrdinalTagFilterTest(TopydoTest): ...@@ -358,6 +358,22 @@ class OrdinalTagFilterTest(TopydoTest):
self.assertEqual(len(result), 1) self.assertEqual(len(result), 1)
self.assertEqual(result[0].source(), self.todo2) self.assertEqual(result[0].source(), self.todo2)
def test_filter6(self):
otf = Filter.OrdinalTagFilter('due:non')
result = otf.filter(self.todos)
self.assertEqual(len(result), 1)
self.assertEqual(result[0].source(), self.todo3)
def test_filter7(self):
otf = Filter.OrdinalTagFilter('due:Non')
result = otf.filter(self.todos)
self.assertEqual(len(result), 1)
self.assertEqual(result[0].source(), self.todo3)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -156,13 +156,28 @@ ORDINAL_TAG_MATCH = r"(?P<key>[^:]*):(?P<operator><=?|=|>=?|!)?(?P<value>\S+)" ...@@ -156,13 +156,28 @@ ORDINAL_TAG_MATCH = r"(?P<key>[^:]*):(?P<operator><=?|=|>=?|!)?(?P<value>\S+)"
class OrdinalTagFilter(Filter): class OrdinalTagFilter(Filter):
def __init__(self, p_expression): def __init__(self, p_expression):
super(OrdinalTagFilter, self).__init__() super(OrdinalTagFilter, self).__init__()
match = re.match(ORDINAL_TAG_MATCH, p_expression)
self.expression = p_expression
match = re.match(ORDINAL_TAG_MATCH, self.expression)
if match: if match:
self.key = match.group('key') self.key = match.group('key')
self.operator = match.group('operator') or '=' self.operator = match.group('operator') or '='
self.value = match.group('value') self.value = match.group('value')
def match(self, p_todo): def match(self, p_todo):
"""
Performs a match on a key:value tag in the todo.
First it tries to convert the value and the user-entered expression to
a date and makes a comparison if it succeeds, based on the given
operator (default ==).
Upon failure, it falls back to converting value and user-entered
expression to an integer and makes a numerical comparison based on the
given operator (default ==)
As a last resort, it falls back to using a Grep filter to see if the
user given expression is contained in the todo text.
"""
if not self.key or not p_todo.has_tag(self.key): if not self.key or not p_todo.has_tag(self.key):
return False return False
...@@ -174,11 +189,15 @@ class OrdinalTagFilter(Filter): ...@@ -174,11 +189,15 @@ class OrdinalTagFilter(Filter):
operand2 = date_string_to_date(self.value) operand2 = date_string_to_date(self.value)
except ValueError: except ValueError:
operand1 = p_todo.tag_value(self.key)
operand2 = self.value
try: try:
operand1 = int(p_todo.tag_value(self.key)) operand1 = int(operand1)
operand2 = int(self.value) operand2 = int(operand2)
except ValueError: except ValueError:
return False grep = GrepFilter(self.expression)
return grep.match(p_todo)
if self.operator == '<': if self.operator == '<':
return operand1 < operand2 return operand1 < operand2
......
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