Jouons avec Awk, Bash et Owncloud
Un souci de synchronisation du calendrier entre Owncloud et mon téléphone a été le prétexte à bidouiller une fonctionnalité de rappel des événements par e-mail. Pourquoi des e-mail ? Parce que je suis un fana de ce moyen de communication, la preuve ici et là.
Donc ce que je veux c'est un joli e-mail le lundi matin qui résume mes rendez- vous de la semaine (description, date et heure) et puis chaque matin au réveil un e-mail par événement avec le fichier ICS en pièce jointe. ICS késako ? Un vieux mais très actuel standard de description d'un événement reconnu par la plupart des calendriers. L'intérêt d'avoir le fichier ICS c'est de pouvoir l'ajouter au calendrier local du téléphone en un clic et de paramétrer le rappel en connaissance de cause (le matin on a une petite idée de comment va se profiler sa journée).
Plutôt que de coder dans un langage évolué, je me suis amusé à réaliser cela avec les outils présent en standard sur GNU/Linux (Awk et Bash) pour le backend MySQL d'Owncloud. C'est didactique car il est toujours préférable de privilégier l'accès aux données par une API qui sera plus ou moins bien maintenue dans le temps par les développeurs que d'attaquer directement la base de données. D'abord jetons un oeil à structure de la base de donnée Owncloud.
La table oc_clndr_calendars permet de retrouver l'id de calendrier de notre utilisateur.
mysql> SELECT * FROM oc_clndr_calendars; +----+------------+-------------+-----------+--------+------+---------------+---------------+----------+-----------------------+ | id | userid | displayname | uri | active | ctag | calendarorder | calendarcolor | timezone | components | +----+------------+-------------+-----------+--------+------+---------------+---------------+----------+-----------------------+ | 1 | yax | Personnel | personnel | 1 | 196 | 0 | NULL | NULL | VEVENT,VTODO,VJOURNAL | +----+------------+-------------+-----------+--------+------+---------------+---------------+----------+-----------------------+
Et la table oc_clndr_objects contient les évènements :
mysql> DESC oc_clndr_objects; +--------------+------------------+------+-----+---------------------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+------------------+------+-----+---------------------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | calendarid | int(10) unsigned | NO | | 0 | | | objecttype | varchar(40) | NO | | | | | startdate | datetime | YES | | 1970-01-01 00:00:00 | | | enddate | datetime | YES | | 1970-01-01 00:00:00 | | | repeating | int(11) | YES | | 0 | | | summary | varchar(255) | YES | | NULL | | | calendardata | longtext | YES | | NULL | | | uri | varchar(255) | YES | | NULL | | | lastmodified | int(11) | YES | | 0 | | +--------------+------------------+------+-----+---------------------+----------------+
On peut récupérer les évènements du jour courant avec la requête suivante :
mysql> SELECT startdate,summary,calendardata FROM oc_clndr_objects WHERE calendarid = 1 AND DATE(startdate) = DATE(NOW()) ORDER by startdate \\G; *************************** 1. row *************************** startdate: 2015-09-14 10:30:00 summary: Déjeuner avec M. calendardata: BEGIN:VCALENDAR VERSION:2.0 PRODID:ownCloud Calendar CALSCALE:GREGORIAN BEGIN:VEVENT UID:95d9221d97 DTSTAMP:20150914T103206Z CREATED:20150913T170426Z LAST-MODIFIED:20150914T103206Z SUMMARY:Déjeuner avec M. DTSTART;TZID=Europe/Paris:20150914T123000 DTEND;TZID=Europe/Paris:20150914T140000 LOCATION: DESCRIPTION: CATEGORIES: CLASS:PUBLIC END:VEVENT END:VCALENDAR *************************** 2. row *************************** startdate: 2015-09-14 16:15:00 summary: RV dentiste calendardata: BEGIN:VCALENDAR VERSION:2.0 PRODID:ownCloud Calendar CALSCALE:GREGORIAN BEGIN:VEVENT UID:1958bfe4a9 DTSTAMP:20150914T103134Z CREATED:20150914T103134Z LAST-MODIFIED:20150914T103134Z SUMMARY:RV dentiste DTSTART;TZID=Europe/Paris:20150914T181500 DTEND;TZID=Europe/Paris:20150914T183000 LOCATION: DESCRIPTION: CATEGORIES: END:VEVENT END:VCALENDAR
Les colonnes intéressantes sont :
- calendarid pour filtrer les évènements de notre utilisateur Owncloud
- summary : le libellé de l'évènement
- startdate : la date de début de l'évènement
- calendardata : l'évènement au format iCalendar
Ce qu'on veut c'est générer un shell script qui récupère les informations du jour et envoie un e-mail par évènement avec le fichier ICS en pièce jointe. L'envoi est réalisé par l'utilitaire mpack. Le résultat final espéré pour notre exemple est ce script :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #!/bin/sh # STARTDATE="`date -d '2015-09-14 10:30:00-000' '+%a %e %b %R'`" SUMMARY="Déjeuner avec M." echo "BEGIN:VCALENDAR" >event.ics echo "VERSION:2.0" >> event.ics echo "PRODID:ownCloud Calendar" >> event.ics echo "CALSCALE:GREGORIAN" >> event.ics echo "BEGIN:VEVENT" >> event.ics echo "UID:95d9221d97" >> event.ics echo "DTSTAMP:20150914T103206Z" >> event.ics echo "CREATED:20150913T170426Z" >> event.ics echo "LAST-MODIFIED:20150914T103206Z" >> event.ics echo "SUMMARY:Déjeuner avec M." >> event.ics echo "DTSTART;TZID=Europe/Paris:20150914T123000" >> event.ics echo "DTEND;TZID=Europe/Paris:20150914T140000" >> event.ics echo "LOCATION:" >> event.ics echo "DESCRIPTION:" >> event.ics echo "CATEGORIES:" >> event.ics echo "CLASS:PUBLIC" >> event.ics echo "END:VEVENT" >> event.ics echo "END:VCALENDAR" >>event.ics mpack -s "$SUMMARY - $STARTDATE" event.ics $1 # STARTDATE="`date -d '2015-09-14 16:15:00-000' '+%a %e %b %R'`" SUMMARY="RV dentiste" echo "BEGIN:VCALENDAR" >event.ics echo "VERSION:2.0" >> event.ics echo "PRODID:ownCloud Calendar" >> event.ics echo "CALSCALE:GREGORIAN" >> event.ics echo "BEGIN:VEVENT" >> event.ics echo "UID:1958bfe4a9" >> event.ics echo "DTSTAMP:20150914T103134Z" >> event.ics echo "CREATED:20150914T103134Z" >> event.ics echo "LAST-MODIFIED:20150914T103134Z" >> event.ics echo "SUMMARY:RV dentiste" >> event.ics echo "DTSTART;TZID=Europe/Paris:20150914T181500" >> event.ics echo "DTEND;TZID=Europe/Paris:20150914T183000" >> event.ics echo "LOCATION:" >> event.ics echo "DESCRIPTION:" >> event.ics echo "CATEGORIES:" >> event.ics echo "END:VEVENT" >> event.ics echo "END:VCALENDAR" >>event.ics mpack -s "$SUMMARY - $STARTDATE" event.ics $1 |
Comment fait-on ? On exécute la requête SQL et on la donne à manger à un script awk qui a pour objectif de générer le shell script ci-dessus. Awk a été inventé pour ce genre de tâche : prendre un fichier en entrée et le modifier pour créer un fichier en sortie. Le script est assez opaque si on n'a jamais pratiqué mais l'idée c'est de décrire la structure du document en entrée (comment distinguer les enregistrements) et de faire correspondre des traitements à certains enregistrements qu'on identifie par une expression régulière.
Voci le script awk complet :
BEGIN { FS="\\n" OFS="" ORS="\\n" print "#!/bin/sh" print " " } # blank lines /^$/ { next } # record header $1 ~ /^\\*\\*\\*\\*/ { next } # summary field $1 ~ /^[ ]*summary\\:/ { idx = match($1, /summary\\:(.*)/) print "SUMMARY=\\"" substr($1, idx + 9) "\\"" next } # startdate field $1 ~ /^[ ]*startdate\\: / { match($1, /startdate\\: /) print "STARTDATE=\\"`date -d '" substr($1, RSTART + RLENGTH) "-000' '+%a %e %b %R'`\\"" next } # vcalendar start tag $1 ~ /^[ ]*calendardata\\: / { match($1, /calendardata\\: /) print "echo \\"" substr($1, RSTART + RLENGTH) "\\" >event.ics" next } # vcalendar end tag $1 ~ /^END\\:VCALENDAR/ { print "echo \\"" $1 "\\" >>event.ics" print "mpack -s \\"$SUMMARY - $STARTDATE\\" event.ics $1" print "" next } # vcalendar body { print "echo \\"" $0 "\\" >> event.ics" }
Il ne reste plus qu'à orchestrer tout cela dans un shell script et de l'appeler par une tâche cron. Le script complet gère les rappels du jour et les rappels pour la semaine à venir. Il est disponible sur mon compte GitHub.