Skip to content

Instantly share code, notes, and snippets.

@spookylukey
Created February 24, 2012 14:27
Show Gist options
  • Save spookylukey/1901236 to your computer and use it in GitHub Desktop.
Save spookylukey/1901236 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# Quick and dirty script to convert ktimetracker.ics files
# into TimeCult files.
import os.path
import sys
import time
import uuid
import elementtree.ElementTree as ET
import vobject
# Structures to store the tree of data. Use native Python data types, and
# convert to what TimeCult expects when we 'render'
class Struct(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
class TimeCult(Struct):
# <timecult>
# Eventual attributes:
# name
# projectTree
# timeLog
def toXML(self):
root = ET.XML('<timecult appVersion="0.12" fileVersion="10" />')
root.attrib['uuid'] = str(uuid.uuid1())
root.attrib['name'] = self.name
pt = ET.Element('projectTree')
for n in self.projectTree:
pt.append(n.toXML())
root.append(pt)
tl = ET.Element('timeLog')
for tr in self.timeLog:
tl.append(tr.toXML())
root.append(tl)
return root
class Node(Struct):
# <project> and <task>
# Eventually will have these attributes:
# vtodo
# summary
# uid
# created
# parent
# children
# type = "project|task"
# finished
def __repr__(self):
return "<Node: uid=%s summary=%r, parent=%r>" % (
self.uid,
self.summary,
self.parent.summary if self.parent is not None else None)
def toXML(self):
e = ET.Element(self.type)
e.attrib['id'] = self.id
e.attrib['name'] = self.summary
e.attrib['created'] = str(int(time.mktime(self.created.timetuple()) * 1000))
if (self.type == "task"):
assert len(self.children) == 0
e.attrib['status'] = 'finished' if self.finished else 'inProgress'
else:
for c in self.children:
e.append(c.toXML())
return e
class TimeRec(Struct):
# <timerec>
# Eventual attributes:
# startTime # datetime
# duration # seconds
# task # Node
def toXML(self):
e = ET.Element("timeRec")
e.attrib['taskId'] = self.taskId
e.attrib['startTime'] = str(int(time.mktime(self.startTime.timetuple()) * 1000))
e.attrib['duration'] = str(int(self.duration * 1000))
e.attrib['notes'] = ""
return e
def convert(filename):
f = vobject.readOne(file(filename).read())
# First parse Todos
nodes = {}
for vtodo in f.vtodo_list:
n = Node(vtodo = vtodo,
uid=vtodo.uid.value,
summary=vtodo.summary.value,
created=vtodo.created.value,
finished=vtodo.contents['percent-complete'][0].value == "100"
)
nodes[n.uid] = n
children = {}
for uid, node in nodes.items():
vtodo = node.vtodo
try:
related_uid = vtodo.contents['related-to'][0].value
node.parent = nodes[related_uid]
children.setdefault(related_uid, []).append(node)
except KeyError:
node.parent = None
for uid, node in nodes.items():
node.children = children.get(uid, [])
if len(node.children) == 0:
node.type = "task"
else:
node.type = "project"
autouid = 0 # For newly created Tasks
# Now find the time information in the ICS file
time_log = []
for vevent in f.vevent_list:
#if not hasattr(vevent, 'dtstart'):
# sys.stedrr.write("Discarding %r" % vevent)
related_uid = vevent.contents['related-to'][0].value
task = nodes[related_uid]
if task.type == "project":
# TimeCult can only cope with time events being against 'leaf' nodes
# in the tree. So we need to adjust and create an additional leaf
project = task
undefined_l = [t for t in project.children if t.uid.startswith('autouid')]
if len(undefined_l) > 0:
task = undefined_l[0]
else:
task = Node(uid='autouid-%d' % autouid,
summary='other',
parent=project,
type="task",
created=project.created,
finished=False,
children=[])
autouid += 1
nodes[task.uid] = task
project.children.insert(0, task)
dtstart = vevent.dtstart.value
dtend = vevent.dtend.value
if dtstart.tzinfo is None:
dtstart = dtstart.replace(tzinfo=dtend.tzinfo)
if dtend.tzinfo is None:
dtend = dtend.replace(tzinfo=dtstart.tzinfo)
time_rec = TimeRec(startTime=dtstart,
duration=(dtend - dtstart).total_seconds(),
task=task)
time_log.append(time_rec)
# Now need to assign IDs
taskId = 0
for u, n in nodes.items():
taskId += 1
n.id = str(taskId)
for tr in time_log:
tr.taskId = tr.task.id
return TimeCult(
name=os.path.basename(filename),
projectTree=[n for u, n in nodes.items() if n.parent is None],
timeLog=time_log)
if __name__ == '__main__':
filename = sys.argv[1]
timecult = convert(filename)
sys.stdout.write(
"""<?xml version="1.0" encoding="UTF-8"?>""" +
ET.tostring(timecult.toXML()))
@spookylukey
Copy link
Author

This script has a bug - it doesn't output - without this timecult will create duplicate IDs, causing corruption

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment