Aus Linux-Magazin 04/2012

"Getting Things Done" mit Evernote und Perl

© cydonna, photocase.com

Um vorläufig eingefrorene Projekte regelmäßig zu reanimieren, sortieren Anhänger der “Getting Things Done”-Methode Zettel in Hängeregistern nach Datum und sehen diese Tickler-Files regelmäßig durch. Perl und Evernote hingegen wecken den User automatisch mit Erinnerungsmeldungen in der Inbox.

Mind like Water, die geistige Ruhe und extreme Flexibilität eines Karatekämpfers beim Bewältigen der Alltagsaufgaben, das verspricht die Produktivitätsmethode “Getting Things Done” (GTD) von Erfolgsautor David Allen [2]. Die einfache Grundregel dazu lautet: Belaste dich nicht länger mit Aufgaben, bei denen du den nächsten Schritt momentan nicht erledigen kannst, sondern lege sie stattdessen in einem Ordnungssystem (Abbildung 1) ab.

Papiervorlage

Die bei der GTD-Methode zum Einsatz kommenden Hängeregister tragen Etiketten für die jeweiligen Tage des Monats und die Monate des Jahres. Hat ein Kollege zum Beispiel für den 14. eines Monats vollmundig ein Ergebnis angekündigt, schreibt der GTD-Jünger einen Zettel mit den Eckdaten und wirft ihn in den dazu passenden Order mit der Nummer 14. Und sollte bereits im Januar die Urlaubsplanung für den nächsten Sommer beginnen, weil es dann die günstigsten Flüge gibt, landet ein entsprechender Zettel mit der URL des Online-Buchungssystems im Ordner mit dem Reiter “Januar”.

Abbildung 1: Getting Things Done – ein Tickler-System für Papierzettel.

Abbildung 1: Getting Things Done – ein Tickler-System für Papierzettel.

Beim regelmäßigen Prüfen der Ordner fällt dann auf, dass für den nächsten Tag oder den kommenden Monat bestimmte Aufgaben anstehen. Die packt der pflichtbewusste GTD-Anhänger zum Erstaunen seiner unorganisierten Mitwelt dann pünktlich an und prüft zuverlässig nach, ob Terminversprechen tatsächlich eingehalten wurden.

Automatisch mit Evernote

Wie schon im Perl-Snapshot des Linux-Magazins 1/2012 [3] kommt der in der Grundversion kostenlose Service “Evernote” [4] wie gerufen, um Alltagsaufgaben gemäß GTD-Tipps zu optimieren. Der User definiert sich ein Eingangsfach als »00-Inbox« . Die vorangestellten Nullen sorgen dafür, dass Evernote den Ordner ganz nach oben sortiert. In das Fach (Abbildung 2) flattern später neue Anfragen hinein. Für diese bestimmt der Ordnungsliebhaber dann den nächsten Bearbeitungsschritt und packt ihn wahlweise sofort an oder legt die Notiz im richtigen Projektordner für eine spätere Wiedervorlage ab.

Abbildung 2: Vorher: Die Inbox des Evernote-Users zeigt zunächst nur einen lesenswerten Artikel …

Abbildung 2: Vorher: Die Inbox des Evernote-Users zeigt zunächst nur einen lesenswerten Artikel …

Ähnlich wie mit den bewährten Papierzetteln in Hängeregistern lässt sich mit Evernote ein Tickler-System aufsetzen: Das Notebook »01-Tickler« enthält Einzeleinträge, die in der Betreffzeile ihr Aktionsdatum im Format YYYY-MM-DD führen. Mittels des Evernote-API öffnet dann ein einmal täglich ablaufender Cronjob das Tickler-Notebook, wandert durch alle Einträge und prüft, ob darunter solche sind, die am folgenden Tag fällig werden (Abbildung 3). Bei einem Treffer schiebt das Skript pflichtschuldig die Notiz in die Inbox des Users, der so erfreut zur Kenntnis nimmt, dass er nun den nächsten Schritt eines Miniprojekts abarbeiten kann.

Abbildung 3: … doch nach dem Lauf des Tickler-Cronjobs landet der morgige Zahnarzttermin in der Inbox.

Abbildung 3: … doch nach dem Lauf des Tickler-Cronjobs landet der morgige Zahnarzttermin in der Inbox.

Planen mit Tickler

Der Eintrag “2013-01 Sommerferien planen” erinnert den Schnäppchenjäger daran, bereits im Januar 2013 einen Flug für den Urlaub im August zu buchen, und der Cronjob zieht den Vermerk pünktlich zum 31. Dezember 2012 aus dem Tickler-Notebook und stellt ihn in die Inbox, damit der urlaubsreife User ein neues Projekt “Ferienplanung” in Angriff nimmt und dort die Aktion (“Lufthansa-Angebote studieren”) startet. Und der Tickler-Eintrag »2012-04-14« “Müller hat Linux-Version fertig” wandert am Abend des 13. April automatisch in die Inbox des Anwenders, der dann seinen aus allen Wolken fallenden Kollegen am nächsten Tag an dessen nun fälliges Terminversprechen erinnert.

Mit Evernotes Web-API ist die Implementierung des Ticklers fast ein Kinderspiel. Der Snapshot in 1/2012 [3] hat bereits ausführlich dokumentiert, wie das verwendete Thrift-Protokoll mit Perl funktioniert und wie Applikationsschreiber einen Application-Key von der Evernote-Webseite holen, um auf der Evernote-Sandbox zunächst etwaige Bugs auszubügeln und dann Zugriff auf die Produktionsserver zu beantragen.

Listing 1 wechselt anfangs im »BEGIN« -Block in das Verzeichnis »$Bin« , in dem das Skript liegt. Dies stellt sicher, dass es die später eingeholten auto-generierten Thrift-Module im Unterverzeichnis »gen-perl« auch dann noch findet, falls es als Cronjob startet. Das CPAN-Modul »local::lib« sorgt dafür, dass es auch im Homeverzeichnis des Users installierte CPAN-Module aufspüren kann. Zeile 26 initialisiert »Log4perl« , das mit Debug-Anweisungen in einer Logdatei festschreibt, was das Skript so treibt. Besonders bei einem per Cronjob gestarteten Skript ist diese Methode sehr hilfreich, um die Ursachen etwaiger Fehlfunktionen aufzuspüren und Bugs auszumerzen. Neben dem Loglevel »$DEBUG« legt es zusätzlich die Kategorie »”main”« fest, die dafür sorgt, dass nicht gleich alle verwendeten CPAN-Module mit eingebautem Log4perl-Support zu loggen anfangen, sondern nur das Hauptprogramm. Abbildung 4 zeigt die Logdaten eines erfolgreichen Skriptlaufs.

Listing 1

evernote-tickler

001 #!/usr/local/bin/perl -w
002 use strict;
003
004 BEGIN {
005     use FindBin qw($Bin);
006     chdir $Bin;
007 }
008
009 use local::lib;
010 use Thrift;
011 use Thrift::HttpClient;
012 use Thrift::BinaryProtocol;
013
014 use lib 'gen-perl';
015 use EDAMUserStore::Constants;
016 use EDAMUserStore::UserStore;
017 use EDAMNoteStore::NoteStore;
018 use EDAMNoteStore::Types;
019 use EDAMErrors::Types;
020 use EDAMTypes::Types;
021 use DateTime;
022 use Log::Log4perl qw(:easy);
023
024 my( $home ) = glob "~";
025
026 Log::Log4perl->easy_init( {
027     level => $DEBUG, category => "main",
028     file =>
029    ">>$home/data/evernote-tickler.log" } );
030
031 my $username        = "my-user";
032 my $password        = "my-passwd";
033 my $consumer_key    = "perlsnapshot";
034 my $consumer_secret = "my-consumer-secret";
035
036 my $evernote_host = "evernote.com";
037 my $user_store_uri =
038     "https://$evernote_host/edam/user";
039 my $note_store_uri_base =
040     "https://$evernote_host/edam/note/";
041
042 my $http_client =
043   Thrift::HttpClient->new($user_store_uri);
044 my $protocol = Thrift::BinaryProtocol->new(
045   $http_client);
046
047 my $client =
048   EDAMUserStore::UserStoreClient->new(
049   $protocol);
050
051 my $result =
052   $client->authenticate( $username,
053   $password, $consumer_key,
054   $consumer_secret );
055
056 my $user = $result->user();
057
058 my $note_store_uri =
059   $note_store_uri_base . $user->shardId();
060
061 my $note_store_client =
062   Thrift::HttpClient->new($note_store_uri);
063
064 my $note_store_protocol =
065   Thrift::BinaryProtocol->new(
066     $note_store_client);
067
068 my $note_store =
069   EDAMNoteStore::NoteStoreClient->new(
070     $note_store_protocol);
071
072 my $notebooks =
073   $note_store->listNotebooks(
074     $result->authenticationToken() );
075
076 my $tickler_guid;
077 my $inbox_guid;
078
079 for my $notebook (@$notebooks) {
080   if ( $notebook->name() eq "01-Tickler" ){
081     $tickler_guid = $notebook->guid();
082     DEBUG "Found Tickler notebook";
083   }
084   if ( $notebook->name() eq "00-Inbox" ) {
085     $inbox_guid = $notebook->guid();
086     DEBUG "Found Inbox notebook";
087   }
088 }
089
090 if ( !defined $tickler_guid ) {
091   die "No Tickler notebook found";
092 }
093
094 if ( !defined $inbox_guid ) {
095   die "No Inbox notebook found";
096 }
097
098 my $filter =
099     EDAMNoteStore::NoteFilter->new();
100 $filter->notebookGuid( $tickler_guid );
101
102 my $note_list = $note_store->findNotes(
103     $result->authenticationToken(),
104         $filter, 0, 1000 );
105
106 my $tomorrow = DateTime->today(
107 time_zone => "local" )->add( days => 1 );
108 my $tomorrow_date_match = $tomorrow->ymd();
109
110 for my $note (
111     @{ $note_list->{ notes } } ) {
112   my $title = $note->title();
113
114   my( $date_in_title ) =
115       ( $title =~ /^(\S+)/ );
116
117   DEBUG "Check if $tomorrow_date_match ",
118         "matches '$date_in_title'";
119
120   if( $tomorrow_date_match =~
121       /^$date_in_title/ ) {
122
123     DEBUG "$title matches. Move to Inbox.";
124
125     my $worked = $note_store->copyNote(
126         $result->authenticationToken(),
127         $note->guid(), $inbox_guid );
128
129     die "copy note failed ($!)" if
130       !defined $worked;
131
132     DEBUG "Deleting note in Tickler file";
133
134     $note_store->deleteNote(
135         $result->authenticationToken(),
136         $note->guid() );
137     }
138 }
Abbildung 4: Der täglich startende Cronjob hat einen Tickler-Eintrag für den nächsten Tag gefunden und schiebt ihn pflichtgemäß in die Inbox des Users.

Abbildung 4: Der täglich startende Cronjob hat einen Tickler-Eintrag für den nächsten Tag gefunden und schiebt ihn pflichtgemäß in die Inbox des Users.

Operation am offenen Herzen

Zeile 52 authentisiert den User auf dem Evernote-Webserver. Stimmen das Passwort und der zugehörige Consumer-Key, erlaubt dieser Server dem Skript uneingeschränkten Lese- und Schreibzugriff. Da es sich um sensitive Daten handelt, die niemand gern verlieren möchte, ist entsprechende Vorsicht beim Programmieren angebracht. Außerdem sollte der User tunlichst sicherstellen, dass das Skript nur auf einem gesicherten System hinter einer Firewall läuft, um Missbrauch, zum Beispiel auf geknackten Webservern, vorzubeugen.

Um nun die Einträge des Notebooks »01-Tickler« aufzuspüren, benötigt das Skript dessen GUID. Zeile 79 iteriert deshalb über alle Notebooks des Accounts und prüft, ob das gerade bearbeitete den gesuchten Namen trägt. Das Gleiche gilt für die Inbox »00-Inbox« . Zudem legt »evernote-tickler« die GUIDs beider Notebooks in den Variablen »$tickler_guid« beziehungsweise »$inbox_guid« sowie in der Logdatei ab – falls es sie findet. Falls es nicht fündig wird, brechen die Zeilen 91 und 95 das Programm mit einem Fehler ab, denn eine Verarbeitung in einem Account ohne entsprechend angelegte Ordner wäre sinnlos.

Filterung

Das Evernote-API bietet keine Verzeichnisfunktion eines vorgegebenen Notebooks, sondern besteht auf einer Methode »findNotes()« die in allen Notebooks nach Notes sucht. Ein Filter vom Typ »EDAMNoteStore::NoteFilter« mit dem Parameter »notebookGuid« beschränkt die Suche allerdings auf ein Notebook mit der angegebenen GUID.

Der zweite Parameter für »findNotes()« gibt einen Offset an, mit dem sich ein Paging von gefundenen Notes einrichten lässt. Im vorliegenden Fall wünscht das Skript allerdings die vollständige Ergebnisliste und beschränkt diese mit dem dritten Parameter lediglich auf 1000, was aber selbst für die längeren Ticklerlisten sehr beschäftigter Nutzer noch ausreichen dürfte.

Zeile 106 berechnet mit dem CPAN-Modul »DateTime« das morgige Datum, indem es zum heutigen Datum (»today()« ) einfach einen einzelnen Tag hinzuaddiert. Die Methode »ymd()« wandelt das daraus resultierende »DateTime« -Objekt anschließend in einen String im Format YYYY-MM-DD um. Der reguläre Ausdruck in der Zeile 115 schneidet aus der Betreffzeile (»title()« ) der Note das Datum aus und legt es in der Variablen »$date_in_title« ab.

Die If-Bedingung in Zeile 120 prüft, ob das Betreff-Datum ganz oder teilweise mit dem morgigen Datum übereinstimmt. Sowohl eine Monatsangabe (YYYY-MM) als auch ein Tagesdatum (YYYY-MM-DD) fördert so Treffer zutage. Evernotes Web-API bietet von sich aus keinen Move-Befehl an, also kopiert Zeile 125 die Tickler-Notiz in die Inbox des Users, falls das Datum stimmt. Die Methode »deleteNote()« in Zeile 134 löscht anschließend die im Tickler-Notebook verbliebene Kopie.

Zuverlässiger Cron

Ein Eintrag in der Cron-Datei im Format »00 16 * * * /Pfad/evernote-tickler« sorgt dafür, dass der Tickler jeden Tag um vier Uhr nachmittags seine Arbeit beginnt und der User für den nächsten Tag anberaumte Aufgaben in seiner Inbox auftauchen sieht. Ist die Zeit für ein Projekt entgegen der Planung noch nicht reif, korrigiert der User schlicht das Datum und schiebt die Notiz wieder zurück in das Tickler-Notebook. Handelt es sich um zeitkritische Angelegenheiten, zum Beispiel ein Meeting, sucht der User dafür nun einen neuen Termin in einer Kalenderapplikation.

Kann er sogar den nächsten Schritt zur Bewältigung der vor ihm liegenden Aufgabe erledigen, tut er dies gemäß dem Regelwerk von GTD sofort, falls es weniger als zwei Minuten dauert. Im anderen Fall erzeugt er dafür eben eine neue Notiz in einem Notebook, das alle seine brandheißen Projekte führt, von denen der kampfbereite Alltagskrieger je nach Stimmung, Energielevel oder Kontext eines zur sofortigen Bearbeitung auswählen kann. (uba)

Online PLUS

In einem Screencast demonstriert Michael Schilli das Beispiel: https://www.linux-magazin.de/plus/2012/04

Infos

  1. Listings zu diesem Artikel:ftp://www.linux-magazin.de/pub/listings/magazin/2012/04/Perl
  2. David Allen, “Getting Things Done: The Art of Stress-Free Productivity”:http://www.amazon.com/dp/0142000280
  3. Michael Schilli, “Zettels Trauma”: Linux-Magazin 01/2012: https://www.linux-magazin.de/Heft-Abo/Ausgaben/2012/01/Perl-Snapshot
  4. Evernote, Web-Applikation und Apps: http://www.evernote.com

Der Autor

Michael Schilli arbeitet als Software-Engineer bei Yahoo in Sunnyvale, Kalifornien. In seiner seit 1997 erscheinenden Kolumne forscht er jeden Monat nach praktischen Anwendungen der Skriptsprache Perl. Unter mailto:mschilli@perlmeister.com beantwortet er gerne Ihre Fragen.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben