Catégorie : Administration réseau     Tags :      

    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.

    Préparation artisanale d’un mailing

    Mon épouse est enseignante et elle a récemment dit à ses élèves qu’elle allait, suite à une absence, leur envoyer par mail leur note au dernier devoir, individuellement.
    Les notes sont disponibles dans un fichier gnumeric, avec les noms des élèves. Elle veut cependant se laisser la possibilité de rajouter éventuellement un message personnalisé. Sans envoyer automatiquement des emails en masse à partir d’un fichier, je voulais lui faciliter la vie autant que possible.
    L’objectif est donc de prendre la liste des notes et de produire un message pour chaque élève de la classe dans sa boîte de " brouillons " (sous Pine, c’est le fichier ~/mail/postponed-msgs).

    Template Toolkit pour produire les messages

    Qui dit message formaté, dit utilisation d’un modèle. Nous n’allons pas nous encombrer de détails. Aussi, vais-je me servir de l’outil utilisé pour tous les sites des Mongueurs et qui est de plus déjà installé sur ma machine : Template Toolkit.
    Le mail sera défini dans un modèle, les emails, noms et notes des élèves le seront dans un fichier (obtenu à partir de son tableur). Nous allons donc produire un message au format mbox qu’il suffira ensuite d’ajouter à la fin de ~/mail/postponed-msgs.
    Le plus simple est évidemment d’écrire un brouillon de message avec Pine et de le sauver pour une édition ultérieure (postpone). Voici le résultat brut tel que sauvegardé par Pine :

        From prof@localhost.localdomain Sun Jan 14 23:37:48 2007 +0100
        Newsgroups:
        Date: Sun, 14 Jan 2007 23:37:48 +0100 (CET)
        From: Votre prof <prof@grande-ecole.fr>
        X-X-Sender: prof@localhost.localdomain
        To: «Eleve 71» <Eleve71@grande-ecole.fr>
        Reply-To: prof@grande-ecole.fr
        Subject: note de l’interro 2
        Fcc: sent-mail
        Message-ID: <Pine.LNX.4.64.0701192333580.7140@localhost.localdomain>
        X-Cursor-Pos: : 129
        X-Our-ReplyTo: Full
        MIME-Version: 1.0
        Content-Type: MULTIPART/MIXED; BOUNDARY=»8323329-2067678150-1169246090=:7187»
        Status: O
        X-Status:
        X-Keywords:
        X-UID: 1
        --8323329-2067678150-1169246090=:7187
        Content-Type: TEXT/PLAIN; CHARSET=iso-8859-1; format=flowed
        Content-Transfer-Encoding: QUOTED-PRINTABLE
        Bonsoir,
        voici ta note de l’interro 2: 12
        La moyenne du groupe est de 9,3. Les notes vont de 3,5 =E0 18.
        A bient=F4t,
        Votre prof
        --8323329-2067678150-1169246090=:7187

    Bref, c’est l’horreur : il y a plein d’en-têtes dont on se moque (et certains sont de plus supposés être des identifiants uniques ; nous devrions éviter de créer des doublons), le contenu du message est en multipart-MIME encodé en quoted-printable...
    Heureusement, Pine n’est pas si bête et sait travailler à partir d’une version minimale d’un message copié dans un fichier au format mbox. Le texte suivant suffit amplement :

        From prof@localhost.localdomain Sun Jan 14 23:37:48 2007 +0100
        From: prof <prof@grande-ecole.fr>
        Reply-To: prof@grande-ecole.fr>
        Subject: note de l’interro 2
        To: «Eleve 71» <Eleve71@grande-ecole.fr>
        Fcc: sent-mail
        Bonsoir,
        voici ta note de l’interro 2: 12
        La moyenne du groupe est de 9,3. Les notes vont de 3,5 à 18.
        A bientôt,
        Votre prof

    Quand on sauvegarde le message après vérification dans Pine, on constate que Pine l’a sauvé sous une forme similaire à notre premier exemple. Parfait.
    Pour utiliser notre outil de template, il suffit de remplacer deux lignes dans notre message épuré :

    • la ligne To: des en-têtes :
    To: "[% prenom %] [% nom %]" <[% email %]>
    • et la ligne du message contenant la note :
    voici ta note de l’interro 2: [% note %]

    On a remplacé les informations cruciales par les variables du modèle : [% prenom %], [% nom %], [% email %] et [% note %]. C’est avec [% %] que l’on peut insérer des directives Template Toolkit dans n’importe quel document^Wmodèle.

    Regexp::Assemble pour trouver les adresses

    Il reste cependant un petit problème pratique... Trouver l’email de chaque élève. En effet, si la plupart des adresses sont de la forme Prenom.Nom@grande-ecole.fr, ce n’est pas le cas pour toutes les adresses. L’administration a parfois dû ajouter les seconds prénoms pour gérer des problèmes d’homonymie.
    Heureusement, Estelle dispose des adresses email de tous ses élèves. Il reste donc à établir la correspondance entre le nom d’un élève dans le fichier de notes et son email dans la liste d’adresses.
    En fait, tout le travail a déjà été fait par David Landgren, l’auteur de Regexp::Assemble (que nous avons déjà vu dans la Perle 29).
    Regexp::Assemble peut produire des regexps " suivies " (tracked), c’est-à-dire qu’il est capable de retrouver laquelle des regexps ayant servi à la construire a déclenché la correspondance.

    my $re = Regexp::Assemble->new()->track(1);

    Il suffit ensuite d’ajouter les expressions avec add().
    Dans les quelques classes dont elle a la charge, il n’y a pas deux élèves qui portent le même nom. Nous allons donc utiliser les noms de famille des élèves comme clés d’un hachage dont les valeurs sont les adresses correspondantes, sans nous préoccuper des problèmes d’homonymie.
    Cette technique de table de correspondance basée sur des expressions régulières est extrêmement puissante, et d’une grande simplicité à mettre en œuvre grâce au travail de David.
    Au final, le script est assez court :

        #!/usr/bin/perl
        use strict;
        use warnings;
        use Template;
        use Regexp::Assemble;
    
        # utilisation: prepare_emails.pl notes.csv emails.txt modele.tt
    
        # noms de fichiers
        my ( $notes, $emails, $modele ) = @ARGV;
        my $fh;
    
        # récupération de la liste des emails
        # pour construire une table de distribution
        open $fh, $emails or die «Impossible d’ouvrir $emails: $!»;
        my %emails = map {
            chomp;            # supprime le saut de ligne final
            /\.([^.]*)\@/;    # trouve le nom dans l’adresse email
            ( my $re = $1 ) =~ s/-/\\W/g;    # remplace les - par \W
            ( $re => $_ )         # produit la table de distribution
        } <$fh>;
        close $fh;
        # invoque la puissante magie de Regexp::Assemble
        my $re
            = Regexp::Assemble->new( flags => ‘i’ )->track(1)->add( keys %emails );
        # création de l’objet Template
        my $tt = Template->new();
        # ouverture du fichier de notes
        open $fh, $notes or die “Impossible d’ouvrir $notes: $!”;
        # boucle de lecture des données élèves
        while (<$fh>) {
            next if /^\s*(?:#|$)/;    # ignore commentaires et lignes blanches
            chomp;                    # supprime le saut de ligne final
            my %vars;
            @vars{qw( nom prenom note )} = split /,/;    # CSV
            if ( $re->match( $vars{nom} ) ) {
                $vars{email} = $emails{ $re->matched() };
            }
            else {
                # message d’erreur si l’email n’a pas été trouvé
                print STDERR
                    «Impossible de trouver l’email de @vars{qw(prenom nom)}\n»;
                next;
            }
            # produit le message
            $tt->process( $modele, \%vars, \*STDOUT )
                or die $tt->error();
        }
        close $fh;

    Ce script affiche le résultat sur la sortie standard, afin de faciliter la vérification des messages produits. Pour ajouter les messages aux brouillons de Pine, il suffit de faire :

    ./prepare_emails.pl notes.csv emails.txt modele.tt >> ~/mail/postponed-msgs

    Il ne lui restera plus qu’à vérifier les messages et à les envoyer. Quant à moi, j’ai enfin pu utiliser les regexps suivies de Regexp::Assemble, et, surtout, j’ai une nouvelle fois réussi à convaincre ma douce de l’intérêt d’avoir un Mongueur de Perl à la maison ! ;-)

    Posté par Philippe Bruhat (BooK) | Signature : Philippe « BooK » Bruhat | Article paru dans Creative Commons License

    Laissez une réponse

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

    • Il y a actuellement

    • 861 articles/billets en ligne.