Commit 6789ac3a authored by Bram Schoenmakers's avatar Bram Schoenmakers

Initial commit.

Wrote the base class that represents a single todo item.
parents
"""
This module contains the class that represents a single todo item.
"""
import re
import TodoParser
class Todo(object):
"""
This class represents a single todo item in a todo.txt file. It maintains
an internal data dictionary of various attributes, but also keeps the plain
text format in shape such that it can be printed back to a file with as few
distortions as possible (no re-shuffling of attributes).
"""
src = None
fields = {}
def __init__(self, p_src):
self.src = p_src
self.fields = TodoParser.parse_line(p_src)
def tag_value(self, p_key):
"""
Returns a tag value associated with p_key. Returns None if p_key
does not exist.
"""
values = self.tag_values(p_key)
return values[0] if len(values) else None
def tag_values(self, p_key):
""" Returns a list of all tag values associated with p_key. Returns
empty list if p_key does not exist. """
tags = self.fields['tags']
matches = [tag[1] for tag in tags if tag[0] == p_key]
return matches if len(matches) else []
def has_tag(self, p_key):
"""
Returns true iff this todo has at least one tag with the given key.
"""
return len(self.tag_values(p_key)) > 0
def set_tag(self, p_key, p_value=""):
"""
Sets a occurrence of the tag identified by p_key. Sets an arbitrary
instance of the tag when the todo contains multiple tags with this key.
When p_key does not exist, the tag is added.
When p_value is not set, the tag will be removed.
"""
if p_value == "":
self.remove_tag(p_key)
return
value = self.tag_value(p_key)
if value:
# remove old value from the tags
self.fields['tags'] = [t for t in self.fields['tags'] \
if t[0] != p_key and t[1] != value]
self.src = re.sub(
r'\b' + p_key + ':' + value + r'\b',
p_key + ':' + p_value,
self.src
)
else:
self.src += ' ' + p_key + ':' + p_value
self.fields['tags'].append((p_key, p_value))
def remove_tag(self, p_key, p_value=""):
"""
Removes a tag from the todo.
When the value is empty (default), all occurrences of the tag will be
removed.
Else, only those tags with the value will be removed.
"""
# Build a new list that excludes the specified tag, match by value when
# p_value is given.
self.fields['tags'] = [t for t in self.fields['tags'] \
if not (t[0] == p_key and (p_value == "" or t[1] == p_value))]
# when value == "", match any value having key p_key
value = p_value if p_value != "" else r'\S+'
self.src = re.sub(r'\s?' + p_key + ':' + value, '', self.src)
def set_priority(self, p_priority):
"""
Sets the priority of the todo. Must be a single capital letter [A-Z].
Priority remains unchanged when invalid priority is given.
"""
if re.match('[A-Z]', p_priority):
self.fields['priority'] = p_priority
self.src = re.sub(r'^\([A-Z]\)', '(' + p_priority + ')', self.src)
def priority(self):
"""
Returns the priority of this todo, or None if no priority is set.
"""
return self.priority
def text(self):
""" Returns the todo text with tags stripped off. """
return self.text
def __print__(self):
""" A printer for the todo item. """
print self.src + "\n"
"""
This module contains the parse function which parses a single line of a
todo.txt file.
"""
import re
def parse_line(p_string):
"""
Parses a single line as can be encountered in a todo.txt file.
First checks whether the standard elements are present, such as priority,
creation date, completeness check and the completion date.
Then the rest of the analyzed for any occurences of contexts, projects or
tags.
Returns an dictionary with the default values as shown below.
"""
result = {
'completed': False,
'completionDate': None,
'priority': None,
'creationDate': None,
'text': "",
'projects': [],
'contexts': [],
'tags': []
}
date = r'\d{4}-\d{2}-\d{2}'
completed_head = re.match(
r'x ((?P<completionDate>' + date + ') )' +
'((?P<creationDate>' + date + ') )?(?P<rest>.*)',
p_string
)
normal_head = re.match(
r'(\((?P<priority>[A-Z])\) )?' +
'((?P<creationDate>' + date + ') )?(?P<rest>.*)',
p_string
)
rest = p_string
if completed_head:
result['completed'] = True
result['completionDate'] = completed_head.group('completionDate')
result['creationDate'] = completed_head.group('creationDate')
rest = completed_head.group('rest')
elif normal_head:
result['priority'] = normal_head.group('priority')
result['creationDate'] = normal_head.group('creationDate')
rest = normal_head.group('rest')
for word in rest.split():
project = re.match(r'\+(.*)', word)
if project:
result['projects'].append(project.group(1))
context = re.match('@(.*)', word)
if context:
result['contexts'].append(context.group(1))
tag = re.match('(?P<key>.*):(?P<value>.*)', word)
if tag:
result['tags'].append((tag.group('key'), tag.group('value')))
continue
result['text'] += word + ' '
# strip trailing space from resulting text
result['text'] = result['text'][:-1]
return result
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