Ich migriere derzeit meine Zeiterfassung für Hobbyprojekte vom kostenpflichtigen Web-Tool mite auf das Open-Source-Command-Line Projekt Watson. Mite hat einen größeren Funktionsumfang mit dem Management von Kunden und Stundensätzen, allerdings benötige ich diese Funktionen derzeit nicht. Den Kommentar zu jeder Buchung in mite nutze ich auch nahezu kaum.

Deshalb zeige ich an dieser Stelle, wie man seine Daten von mite in Watson integrieren kann.

Vorarbeit

Als erstes muss man sich die bestehenden Daten von mite exportieren. Das geht ganz leicht über die Funktion Account -> Backup in mite. Hier kann man sich einen XML-Export der eigenen Daten herunterladen.

Aus diesen Daten habe ich mir zunächst die Projekte und Kunden auflisten lassen. Kundeneinträge werden von Watson nicht unterstützt, man könnte sich allerdings ein entsprechendes Tag zu jedem Kunden anlegen oder jeden Kunden als ein Projekt verwalten. In meinem Fall ist der Eintrag relativ irrelevant, weil ich hier eher Themen verwalte als echte Kunden (z.B. Fernuni Hagen).

Die Projekte sind hingegen sehr wichtig, allerdings will ich diese teilweise nicht als Projekt in Watson importieren, sondern als Tag zu einem neuen Sammelprojekt. Deshalb habe ich mir für jedes Projekt aus mite ein Mapping auf einen neuen Projektnamen mit ggfs. Tags angelegt.

Die bestehenden Projekte kann man aus dem mite-Export anzeigen mit:

xmllint --xpath '/backup/projects/project/name/text()' mite-backup.xml.gz

Daraus habe ich mir folgendermaßen ein Mapping für den Import in Watson aufgebaut:

mapping = {
    'Studium / Vorlesung-X': ('university', ['vorlesung-x']),
    'Molescrape': ('molescrape', ['platform']),
    # ...
}

Hierbei steht der erste Eintrag im Tupel für das Projekt, das ich in Watson anlegen möchte, und der zweite Eintrag steht für die Liste an Tags, die ich dafür vergeben möchte.

Konvertierung

Damit können wir mit einem kleinen Python-Skript die Einträge aus dem mite-Export auslesen und per Command-Line-Befehl in Watson eintragen. Theoretisch könnte man auch die Watson-Log-Datei direkt ändern, allerdings ist deren Format nicht dokumentiert und ihr Format ist damit als instabiler anzusehen als die öffentliche Command-Line-API. Außerdem müsste man sich dann die ID für jeden Eintrag selbst berechnen und es ist nicht dokumentiert, ob dies ein zufälliger Wert ist oder ein Hash aus den Daten.

from lxml import etree
from datetime import datetime, timedelta
import subprocess


mapping = {
    'Studium / Vorlesung-X': ('university', ['vorlesung-x']),
    'Molescrape': ('molescrape', ['platform']),
}

tree = etree.parse('mite-backup.xml.gz')

# create a lookup table of all project ids to project names
projects = {}
for project in tree.xpath('/backup/projects/project'):
    id_ = int(project.xpath('./id/text()')[0])
    name = project.xpath('./name/text()')[0]

    projects[id_] = name

# loop through all time entries and add them to watson with a command
# line call
for time_entry in tree.xpath('/backup/time-entries/time-entry'):
    try:
        project_id = int(time_entry.xpath('./project-id/text()')[0])
    except IndexError:
        # some entries have no project assigned, ignore them
        print('skipped one entry')
        continue

    project_name = projects[project_id]
    date = time_entry.xpath('./date-at/text()')[0]
    duration = int(time_entry.xpath('./minutes/text()')[0])

    # mite does not have exact dates when something was done, thus we just
    # use 8 am as the start time
    startdate = datetime.strptime(date, '%Y-%m-%d') + timedelta(hours=8)
    enddate = startdate + timedelta(minutes=duration)

    watson_project = mapping[project_name][0]
    watson_tags = ['+{}'.format(tag) for tag in mapping[project_name][1]]

    command = [
        'watson',
        'add',
        '--from',
        datetime.strftime(startdate, '%Y-%m-%d %H:%M:%S'),
        '--to',
        datetime.strftime(enddate, '%Y-%m-%d %H:%M:%S'),
        watson_project
    ] + watson_tags

    subprocess.run(command)
I do not maintain a comments section. If you have any questions or comments regarding my posts, please do not hesitate to send me an e-mail to blog@stefan-koch.name.