Aus Linux-Magazin 04/2012

"Getting Things Done" mit Evernote und Perl (Seite 2)

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.

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