-
-
Save uGeek/d0d6624e2445f3f582178c2b0dcb6d91 to your computer and use it in GitHub Desktop.
Convert Remember the Milk tasks feed to Emacs org-mode file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# rtm2org.py | |
# | |
# Convert Remember the Milk tasks feed to Emacs org-mode file | |
# | |
# requires Python >= 2.7 (for ElementTree.iter()) | |
# | |
# Find your RTM Atom feed here: | |
# https://www.rememberthemilk.com/atom/<username> | |
# | |
# Assumes you organize your RTM todos by tags, not by lists (should be | |
# easily modifiable to do the latter). Outputs an org-mode hierarchy | |
# organized by first tag, like so: | |
# | |
# * <first tag> :<first tag>: | |
# ** TODO [<priority] Item 1 | |
# ** TODO Item 2 :<othertags>: | |
# ** TODO Item 3 | |
# <note title> | |
# <note content> | |
from collections import defaultdict | |
import os.path | |
import sys | |
import xml.etree.ElementTree as ET | |
_atom_ns = 'http://www.w3.org/2005/Atom' | |
_xhtml_ns = 'http://www.w3.org/1999/xhtml' | |
_priority_map = {'none': None, '1': 'A', '2': 'B', '3': 'C'} | |
class Rtm2Org(object): | |
def __init__(self): | |
self._by_tag = defaultdict(list) | |
def parseAtom(self, source): | |
tree = ET.parse(source) | |
for entry in tree.iter('{%s}entry' % _atom_ns): | |
title = entry.find('{%s}title' % _atom_ns).text | |
priority, tags, note = None, None, None | |
for span in entry.iter('{%s}span' % _xhtml_ns): | |
if span.get('class') == 'rtm_priority_value': | |
priority = _priority_map.get(span.text, span.text) | |
elif span.get('class') == 'rtm_tags_value': | |
if tags == 'none': | |
tags = [] | |
else: | |
tags = [x.strip() for x in span.text.split(',')] | |
for div in entry.iter('{%s}div' % _xhtml_ns): | |
# First line of a note appears in one XML element as the | |
# note's "title", and the remainder appears in a second | |
# XML element as the note's "content", so concatenate them | |
# BUG: I think RTM supports more than one note per item; | |
# should really put them all in a list and output | |
# as Org subitems or plain list | |
if div.get('class') == 'rtm_note_title_container': | |
span = div.find('{%s}span' % _xhtml_ns) | |
note = span.text | |
elif div.get('class') == 'rtm_note_content': | |
note += '\n' + div.text | |
self._by_tag[tags[0]].append((title, priority, tags, note)) | |
def writeOrg(self, stream=sys.stdout): | |
print >> stream, '#+STARTUP: content indent hidestars' | |
for tag in sorted(self._by_tag.keys()): | |
print >> stream | |
print >> stream, '* %s :%s:' % (tag, tag) | |
for title, priority, tags, note in self._by_tag[tag]: | |
priority = ' [#%s]' % priority if priority else '' | |
tags = filter(lambda x: x != tag, tags) | |
tags = ' :%s:' % ':'.join(tags) if tags else '' | |
print >> stream, '** TODO%s %s%s' % (priority, title, tags) | |
if note: | |
print >> stream, note | |
def main(): | |
args = sys.argv[1:] | |
if len(args) != 1: | |
print >> sys.stderr, \ | |
"usage: %s <file>" % os.path.basename(sys.argv[0]) | |
sys.exit(1) | |
rtm2org = Rtm2Org() | |
rtm2org.parseAtom(args[0]) | |
rtm2org.writeOrg() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment