Catégorie : Bureautique     Tags : ,      

    Retrouvez cet article dans : Linux Magazine 96

    Depuis le numéro 59, les Mongueurs de Perl vous proposent tous les mois de découvrir les scripts jetables qu’ils ont pu coder ou découvrir dans leur utilisation quotidienne de Perl. Bref, des choses trop courtes pour en faire un article, mais suffisamment intéressantes pour mériter d’être publiées. Ce sont les perles de Mongueurs.

    Suppression des doublons d’un agenda au format ICalendar

    La RFC 2445 [1] rappelle qu’il y a un vrai besoin de standard pour l’interopérabilité des services d’agendas ou de calendriers.
    Elle définit en outre le format iCalendar pour permettre d’échanger ou de stocker des informations variées comme les calendriers, des listes de choses à faire ou des évènements.
    De fait, le format iCalendar est utilisé par de nombreuses applications de gestion d’informations personnelles, qui savent lire et produire des fichiers .ics.
    Le format reste très libre, et l’interprétation de la sémantique reste à définir pour l’unicité des évènements, en particulier.
    Justement, notre grand chef, après une migration de son système sur une nouvelle version a essayé de synchroniser son Palm Treo680 avec son calendrier Evolution, mais après plusieurs essais infructueux, le système lui présente désormais ses rendez-vous en double ou en triple. C’est mal !
    Heureusement, Perl et CPAN sont là. Une brève recherche (Merci Jérôme) sur CPAN, grâce à l’excellent CPAN Suggest [2] fournit rapidement une réponse et nous aiguille entre autres sur quelques modules excellents et bien documentés de Jesse Vincent (papa entre autres de Request Tracker).

    Data::Ical [3]

    Pour explorer le contenu du calendrier, une ligne comme la suivante suffit (bon, deux, pour préciser le module qui va être utilisé).

         use Data::ICal;
        my $calendar = Data::ICal->new($filename);

    iCalendar définit de nombreux composants, rebaptisés entries qui généralement n’ont pas de sous-composants, mais certains peuvent en avoir. Chaque composant peut alors avoir des propriétés, dont certaines sont uniques et d’autres peuvent apparaître plusieurs fois.
    Pour filtrer les méchants doublons, il faut commencer par identifier ce qui fait de deux enregistrements similaires des doublons.
    Evolution va assez loin dans le respect des standards en utilisant iCalendar comme format de stockage des données. Un export n’est alors même pas nécessaire.
    Pour commencer, je ne m’intéresse qu’aux enregistrements de type VEVENT, qui sont les seuls qui apparaissent dans mon fichier de données. Il existe d’autres types de composants (VTODO, VTIMEZONE, etc.) et chacun a une structure particulière.
    Je suis parti du principe que deux VEVENT seraient identiques si leurs dates de début, de fin et leur titre étaient identiques. En fait, c’est erroné parce que certains VEVENT n’ont pas de titre.
    Après investigation, il s’avère que ces évènements sont des scories inutiles qui peuvent être supprimées sans état d’âme.
    Le résultat
    Le script suivant décrit l’intégralité du traitement. Après avoir ouvert un calendrier en entrée et un autre en sortie, je passe en revue tous les composants de mon fichier d’entrée. Le script est normalement invoqué avec deux fichiers en arguments, le fichier d’entrée et le fichier de sortie. À défaut, on utilisera un fichier nommé calendar.ics dans le répertoire courant en entrée et la sortie sera écrite dans le fichier output_calendar.ics.
    Dans le cas des VEVENT, il faut vérifier la présence de la propriété summary, et prévenir de son absence.
    Une table de hachage permet de conserver un index constitué des dates de début, de fin et du titre. Pour les évènements qui n’apparaissent pas dans cette liste, il suffit de passer son chemin. Dans le cas contraire, une sauvegarde s’impose, réalisée à l’aide de la méthode add_entry().
    Pour tous les autres composants, une conservation à l’identique est réalisée.
    En fin de traitement, l’ensemble du calendrier est sauvegardé et un petit résumé est affiché, pour rappeler le nombre de composants de chaque type.

      #!/usr/bin/perl
      use strict;
      use warnings;
      use Data::ICal;
      my $input_calendar = Data::ICal->new( filename => shift || ‘calendar.ics’ );
      my $output_calendar = Data::ICal->new();
      my %component = ();
      my %event     = ();
    
      open my $output, ‘>’, shift || ‘output_calendar.ics’
        or warn «Could not open output file: $!\n»;
    
      ENTRY:
      foreach my $entry ( @{ $input_calendar->entries } ) {
          $component{ $entry->ical_entry_type }++;
    
          if ( $entry->ical_entry_type =~ /VEVENT/ ) {
              my $check = ‘’;
              if ( $entry->property(‘summary’) ) {
    
                  foreach my $property (qw/ summary dtstart dtend /) {
                      foreach ( @{ $entry->property($property) } ) {
                          $check .= $_->value;
                      }
                  }
    
                  $component{‘ Duplicate VEVENT’}++, next ENTRY if exists $event{$check};
                  $event{$check} = undef;
              }
              else {
                  $component{‘ Without summary’}++;
                  next ENTRY;
              }
          }
    
          $component{‘ Backed up entry’}++;
          $output_calendar->add_entry($entry);
    
      }
    
      END {
    
          print «Saving output calendar... «;
          print $output $output_calendar->as_string;
          close $output or die «Could not close output file: $!\n»;
          print «done.\n»;
    
          print «Processed entries:\n»;
          foreach my $key ( reverse sort keys %component ) {
              printf «%10s %i\n», $key, $component{$key};
          }
      }

    Au départ, j’avais tenté d’afficher un résumé des événements rencontrés. Le problème, c’est que j’avais une erreur un peu méchante quand j’ai essayé de pointer sur une propriété inexistante (précisément la propriété summary).
    Avec le bloc END, j’avais au moins mon bilan, ce qui m’a permis de savoir que j’avais parcouru 148 VEVENT et que le 149ème était un VEVENT sans summary.
    Bien entendu, il pourrait y avoir de nombreuses améliorations dans la récupération des fichiers de calendrier en entrée et en sortie, mais, en l’état, ça fonctionne plutôt bien, et pour un calendrier comportant près de 8500 évènements, le fichier d’origine pesant un peu moins de 4 Mo, la suppression des doublons est opérée en 24 secondes sur mon ordi portable.

     % time perl remove_duplicates_from_ical
     Saving output calendar... done.
     Processed entries:
                    VTIMEZONE 5
                       VEVENT 8482
              Without summary 1518
             Duplicate VEVENT 3808
                    Backed up 3161
     perl remove_duplicates_from_ical  23,54s user 0,40s system 99% cpu 24,004 total

    Faciliter le déploiement d’un script à l’aide de PAR [4] et PAR::Packer [5]

    Comme notre grand chef ne goûte certainement pas aux joies du shell CPAN, il faut trouver un moyen d’empaqueter dans un programme prêt à l’emploi, sans se soucier des dépendances de modules.
    Ça tombe plutôt bien, PAR est fait pour cela. Ce module permet de trouver et d’empaqueter dans un binaire ou dans un énorme script Perl toutes les dépendances identifiées d’un script.
    Initialement écrit par Audrey Tang, et actuellement maintenu par Steffen Mueller, PAR dispose d’une commande pour générer directement des exécutables ou des binaires pour quelques plate-formes. Il semble y avoir quelques restrictions pour construire des binaires avec des interfaces graphiques sur quelques plate-formes. Consultez la perldoc pour plus d’informations.
    Un module a particulièrement retenu mon attention. PAR::Packer contient la commande pp, dont l’aide en ligne est accessible par perldoc également.
    Dans le cas de ce script, j’ai choisi de générer un binaire, qui, bien qu’il soit compressé (compression généraliste gzip, niveau de compression 9), pèse tout de même ses 3 Mo.

     % pp -C -x -z 9 -o remdupical  remove_duplicates_from_ical
     Saving output calendar... done.
     Processed entries:
                    VTIMEZONE 5
                       VEVENT 8482
              Without summary 1518
             Duplicate VEVENT 3808
                    Backed up 3161
     % ls -l remdupical remove_duplicates_from_ical
     -rwxr-xr-x 1 lg lg 3111695 mai 21 11:37 remdupical
     -rw-r--r-- 1 lg lg    1495 mai 21 11:36 remove_duplicates_from_ical

    La taille du binaire généré impressionne un peu (enfin, moi surtout), et j’ai eu la mauvaise idée de vouloir lui faire subir une cure d’amaigrissement. Un passage de strip sur le binaire permet d’enlever les symboles de débogage.

     % strip rempdupical
     % ll remdupical remove_duplicates_from_ical
     -rwxr-xr-x 1 lg lg 1316988 mai 21 11:55 remdupical
     -rw-r--r-- 1 lg lg    1495 mai 21 11:36 remove_duplicates_from_ical

    Sauf que le résultat est le suivant :

     % ./remdupical
     Usage: ./remdupical [ -Alib.par ] [ -Idir ] [ -Mmodule ] [ src.par ] [ program.pl ]
            ./remdupical [ -B|-b ] [-Ooutfile] src.par

    Le script Perl généré est un peu moins imposant que le binaire.

    Conclusion

    Avec un petit script en Perl et des outils respectueux des standards, j’ai pu facilement récupérer un calendrier utilisable.
    Reste à trouver les raisons des problèmes de synchronisation d’agendas. Mais ceci est une autre histoire.

    Références

    [1] RFC 2445 :
    ftp://ftp.rfc-editor.org/in-notes/rfc2445.txt
    [2] CPAN Suggest :
    http://cpantools.com/
    [3] Data::ICal sur CPAN :
    http://search.cpan.org/~jesse/Data-ICal/
    [4] PAR sur CPAN :
    http://search.cpan.org/~smueller/PAR/lib/PAR.pm
    [5] PAR::Packer : 
    http://search.cpan.org/~smueller/PAR-Packer/lib/PAR/Packer.pm

    Posté par admin-web (fabrice) | Signature : Laurent Gautrot | Article paru dans Creative Commons License

    Laissez une réponse

    Vous devez avoir ouvert une session pour écrire un commentaire.


    • Il y a actuellement

    • 465 articles/billets en ligne.