Skip to content

Instantly share code, notes, and snippets.

@mshroyer
Created December 28, 2016 16:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mshroyer/189d6d0bcb79eef308b38bc1c41b3b84 to your computer and use it in GitHub Desktop.
Save mshroyer/189d6d0bcb79eef308b38bc1c41b3b84 to your computer and use it in GitHub Desktop.
Extract plaintext TODOs from Remember the Milk (RTM) iCalendar export
#!/usr/bin/env python3
import argparse
import functools
import icalendar
import operator
import re
TAGS_PATTERN = re.compile(r'Tags: ([\w\d]+(?:, [\w\d]+)*)')
# The RTM iCalendar export doesn't include information about which list
# VTODO items came from, so we have to use a heuristic to separate fiction
# books from fiction movies.
BOOK_PATTERN = re.compile(r'.+\, ".*"')
def main():
parser = argparse.ArgumentParser(
description='Extract TODOs from an RTM iCalendar export')
parser.add_argument('input')
parser.add_argument('--tag', default=None,
help='Only include items with the given tag')
parser.add_argument('--book', default=False, action='store_true',
help='Only include items that look like books')
parser.add_argument('--no-book', default=False, action='store_true',
help='Only include items that don\'t look like books')
args = parser.parse_args()
predicates = []
if args.book:
predicates.append(lambda item: todo_is_book(item))
if args.no_book:
predicates.append(lambda item: not todo_is_book(item))
if args.tag is not None:
predicates.append(lambda item: todo_has_tag(item, args.tag))
def predicate(item):
return functools.reduce(operator.and_,
map(lambda p: p(item), predicates), True)
for item in get_todos(get_ical_object(args.input), predicate):
print(get_scalar_property(item, 'SUMMARY'))
def get_ical_object(path):
with open(path, 'rb') as f:
cal = icalendar.Calendar.from_ical(f.read())
return cal
def get_todos(cal, predicate=None):
if predicate is None:
predicate = lambda _: True
for item in cal.walk(name='VTODO'):
if predicate(item):
yield item
def get_scalar_property(item, name):
return item.decoded(name).decode()
def todo_has_tag(item, tag):
m = re.search(TAGS_PATTERN, get_scalar_property(item, 'DESCRIPTION'))
if m is None:
return False
item_tags = m.group(1).split(', ')
return tag in item_tags
def todo_is_book(item):
return re.match(BOOK_PATTERN, get_scalar_property(item, 'SUMMARY')) is not None
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment