From d30c547306552bc6ee2ce3f72cc0bb8fe72d04c1 Mon Sep 17 00:00:00 2001 From: bernat Date: Sat, 12 Nov 2005 21:23:43 +0100 Subject: [PATCH] Envoi de mail. Plus de flexibilit. Par Stphane. darcs-hash:20051112202343-d1718-8362a0de7a93945ae9250cbabb102e33e02e2271.gz --- admin/controle_tresorier.py | 348 ++++++++++++++++++++++-------------- 1 file changed, 218 insertions(+), 130 deletions(-) diff --git a/admin/controle_tresorier.py b/admin/controle_tresorier.py index 9b95cf3b..2a7a7ebe 100755 --- a/admin/controle_tresorier.py +++ b/admin/controle_tresorier.py @@ -4,15 +4,36 @@ # Copyright (C) Stéphane Glondu # Licence : GPLv2 -u"""Ce script permet au trésorier de contrôler plus facilement le travail -des câbleurs. +u"""Ce script permet au trésorier de repérer plus facilement les papiers que +les câbleurs n'ont pas encore rendus. -Usage: %(prog)s {paiement|carte|mail} +Utilisations possibles : + * %(prog)s {paiement|carte|list} [--debug ] + * %(prog)s mail [--debug ] + +L'unique option est : + --debug envoyer tous les mails à l' indiquée, plutôt + qu'aux vrais destinataires + +Les commandes sont : + * paiement contrôle interactif des nouvelles (ré-)adhésions + * carte contrôle interactif des nouvelles cartes d'étudiant + * list affiche le récapitulatif des papiers manquants + * mail envoyer les mails de rappel aux câbleurs dont le + login est dans la , 'bureau' désignant le mail + récapitulatif envoyé à bureau ; si la liste est vide + tous les mails nécessaires seront envoyés + +Le modèle du mail envoyé aux câbleurs est lu sur l'entrée standard (typiquement +via une redirection). Les trois premières lignes doivent être : + Encoding: + Subject: + +Le reste est le corps du message. Il doit contenir une occurrence de '%%s' qui +sera remplacée par la liste des papiers manquants. + +Le mail récapitulatif envoyé à bureau est immuable.""" -Les options sont : - * paiement : contrôle interactif des nouvelles (ré-)adhésions - * carte : contrôle interactif des nouvelles cartes d'étudiant - * mails : envoyer les mails de rappel""" import sys, os, re sys.path.append('/usr/scripts/gestion') @@ -37,16 +58,26 @@ if u'Contr print u"Il faut être contrôleur pour exécuter ce script !" sys.exit(1) -# Lors des tests sur egon, on m'envoie tous les mails ! +# Lors des tests, on m'envoie tous les mails ! from socket import gethostname -testing = gethostname().split(".")[0] == 'egon' -if testing: - print coul(u'Mode testing !', 'violet') +debug = False + +if gethostname().split(".")[0] == 'egon': + debug = 'glondu@crans.org' ann_scol = 2004 +if __name__ == '__main__': + if len(sys.argv) > 3 and sys.argv[-2] == '--debug': + debug = sys.argv[-1] + sys.argv.pop() + sys.argv.pop() + +if debug: + print u'Mode debug, tous les mails seront envoyés à %s.' % debug + def _controle_interactif_adherents(liste, quoi): - u""" + """ Contrôle interactif des adhérents de la liste (quoi = p ou c). Retourne (nb_OK, nb_pas_OK). """ @@ -81,7 +112,7 @@ def _controle_interactif_adherents(liste, quoi): def _controle_interactif_clubs(liste): - u""" + """ Contrôle interactif des clubs de la liste (uniquement la charte). Retourne (nb_OK, nb_pas_OK). """ @@ -114,7 +145,7 @@ def _controle_interactif_clubs(liste): def controle_interactif(quoi): - u""" + """ Procédure interactive de contrôle des paiements/chartes (quoi=p) et cartes (quoi=c). """ if quoi not in 'pc': raise ValueError @@ -144,53 +175,58 @@ def controle_interactif(quoi): def formater_pour_cableur(liste): - u""" + """ Formate la liste d'adhérents ou de clubs avec les dates correspondantes. liste est une liste de couples (date, objet). """ lignes = [(u'id', u'Nom', u'Date Heure')] + total = 0 + liste.sort(lambda x, y: cmp(x[1].nom(), y[1].nom())) for date, a in liste: lignes.append((a.id(), a.Nom(), date)) + total += 1 - return tableau([6, 40, 18], lignes) + return tableau([6, 40, 18], lignes) + u'\nTotal : %d' % total def formater_pour_bureau(dico): - u""" + """ Formate la liste d'adhérents ou de clubs avec les câbleurs correspondantes pour le mail récapitulatif envoyé à bureau. """ lignes = [(u'id', u'Nom', u'Câbleur')] - total = 0 - for cableur in dico.keys(): + + liste = dico.keys() + liste.sort() + for cableur in liste: for date, a in dico[cableur]: lignes.append((a.id(), a.Nom(), cableur)) total += 1 - return tableau([6, 40, 17], lignes) + '\nTotal : %d' % total + return tableau([6, 40, 18], lignes) + u'\nTotal : %d' % total def qui(historique, quoi): - u""" + """ Recherche le câbleur qui a effectué la dernière modification quoi dans l'historique, ou qui a inscrit l'adhérent. - Retourne le couple (date_heure, nom_cableur). + Retourne le couple (date, cableur) ou ('_inconnu_', '_inconnu_'). """ regexp = re.compile(r'^([^,]*), ([^ :]*)') cableur = ('_inconnu_', '_inconnu_') for ligne in historique: if quoi in ligne or 'inscription' in ligne: - matched = regexp.match(ligne) + matched = regexp.search(ligne) if matched: cableur = matched.group(1), matched.group(2) return cableur def chercher_cableurs(liste, quoi): - u""" + """ Renvoie un dictionnaire cableur->adherents à partir de liste, quoi désigne le champ qui sera recherché dans l'historique. """ @@ -204,128 +240,180 @@ def chercher_cableurs(liste, quoi): return resultat -from smtplib import SMTP -from email.MIMEText import MIMEText +from email_tools import send_email, parse_mail_template +# Mise en application du mémorable cours de Fred sur les objets :-) -def envoyer_mails_rappel(): - u""" - Envoyer les mails de rappel aux câbleurs avec bureau en cc, - ainsi que le récapitulatif à bureau. - """ - # On recherche tous les câbleurs concernés - todo_list = db.search('paiement=%d&controle!=*p*' % ann_scol) - paiements = chercher_cableurs(todo_list['adherent'], 'paiement') - chartes = chercher_cableurs(todo_list['club'], 'paiement') - cartes = chercher_cableurs(db.search('carteEtudiant=%d&controle!=*c*' % ann_scol)['adherent'], 'carteEtudiant') - - # Méthode de Vince - cableurs = {} - for a in paiements.keys(): - if a != '_inconnu_': cableurs[a] = None - for a in chartes.keys(): - if a != '_inconnu_': cableurs[a] = None - for a in cartes.keys(): - if a != '_inconnu_': cableurs[a] = None - - # Squelette à modifier après - msg_skeleton = u"""\ -Salut, - -Tu me dois des papiers. J'ai mis l'aid ou le cid afin que tu puisses -vérifier facilement dans la base. -%s -Peux-tu confirmer et donner ces papiers le plus rapidement possible à -Florian, Augustin, les laisser au 2B ou directement me les donner ? - --- -Stéphane -""" +class ControleMailer: - # Vérification de l'alias pour l'envoi des mails - if u'tresorier' in cableur.alias(): - moi = (u'%s ' % cableur.Nom()).encode('utf8') - else: - moi = (u'%s <%s@crans.org>' % (cableur.Nom(), cableur.canonical_alias())).encode('utf8') - - # On se connecte au serveur - s = SMTP() - s.connect() + def __init__(self): + # Recherche des câbleurs possédant des cotisations/chartes + todo_list = db.search('paiement=%d&controle!=*p*' % ann_scol) + self._paiements = chercher_cableurs(todo_list['adherent'], 'paiement') + self._chartes = chercher_cableurs(todo_list['club'], 'paiement') + + # Recherche des câbleurs possédant des cartes d'étudiant + todo_list = db.search('carteEtudiant=%d&controle!=*c*' % ann_scol) + self._cartes = chercher_cableurs(todo_list['adherent'], 'carteEtudiant') + + # Récupère tous les câbleurs qui doivent quelque chose + cableurs = {} + for a in self._paiements.keys(): + if a != '_inconnu_': cableurs[a] = None + for a in self._chartes.keys(): + if a != '_inconnu_': cableurs[a] = None + for a in self._cartes.keys(): + if a != '_inconnu_': cableurs[a] = None + self._cableurs = cableurs.keys() - # On envoie les mails - nb = 0 - for c in cableurs.keys(): + # Vérification de l'alias pour l'expéditeur + if u'tresorier' in cableur.alias(): + self._sender = u'%s ' % cableur.Nom() + else: + self._sender = u'%s <%s@crans.org>' % (cableur.Nom(), + cableur.cannonical_alias() or cableur.mail()) + + # Se connecte au serveur SMTP par défaut + from smtplib import SMTP + self._server = SMTP() + self._server.connect() + + def __del__(self): + # Termine la connexion au serveur SMTP + self._server.quit() + + def recapitulatif(self): + """ + Renvoie le récapitulatif pour le bureau. + """ msg = u'' - if c in paiements.keys(): - msg += u"\nFiches d'adhésion et cotisations des adhérents :\n" - msg += formater_pour_cableur(paiements[c]) + '\n' + if self._paiements: + msg += u"Fiches d'adhésion et cotisations des adhérents :\n" + msg += formater_pour_bureau(self._paiements) + '\n\n' - if c in chartes.keys(): - msg += u"\nChartes signées des clubs :\n" - msg += formater_pour_cableur(chartes[c]) + '\n' + if self._chartes: + msg += u"Chartes signées des clubs :\n" + msg += formater_pour_bureau(self._chartes) + '\n\n' - if c in cartes.keys(): - msg += u"\nCarte d'étudiant des adhérents :\n" - msg += formater_pour_cableur(cartes[c]) + '\n' + if self._cartes: + msg += u"Carte d'étudiant des adhérents :\n" + msg += formater_pour_bureau(self._cartes) + '\n\n' - mail = MIMEText((msg_skeleton % msg).encode('utf8'), _charset='UTF-8') - mail['Subject'] = u'Papiers manquants'.encode('utf8') - mail['From'] = moi - adresse_cableur = '%s@crans.org' % c - mail['To'] = adresse_cableur - mail['Cc'] = 'Bureau ' - if testing: - recipients = ['glondu@crans.org'] - else: - recipients = [adresse_cableur, 'bureau@crans.org'] - s.sendmail(moi, recipients, mail.as_string()) - nb += 1 + return msg - # On envoie le mail récapitulatif sur bureau - if paiements or chartes or cartes: - msg = u'' - - msg += u"\nFiches d'adhésion et cotisations des adhérents :\n" - msg += formater_pour_bureau(paiements) + '\n' - - msg += u"\nChartes signées des clubs :\n" - msg += formater_pour_bureau(chartes) + '\n' - - msg += u"\nCarte d'étudiant des adhérents :\n" - msg += formater_pour_bureau(cartes) + '\n' - - msg += u"\n-- \nScript exécuté par %s\n" % cableur.Nom() - mail = MIMEText(msg.encode('utf8'), _charset='utf8') - mail['Subject'] = u'Récapitulatif des papiers manquants'.encode('utf8') - mail['From'] = moi - mail['To'] = 'Bureau ' - if testing: - recipients = ['glondu@crans.org'] + def mail_bureau(self): + """ + Envoie le mail récapitulatif à bureau (s'il y a qqch à envoyer). + """ + msg = self.recapitulatif() + if msg: + msg += u"-- \nScript exécuté par %s\n" % cableur.Nom() + send_email(self._sender, + u"Bureau ", + u"Récapitulatif des papiers manquants", + msg, + server = self._server, + debug = debug) + return coul(u'Mail envoyé à bureau avec succès !', 'vert') else: - recipients = ['bureau@crans.org'] - s.sendmail(moi, recipients, mail.as_string()) - nb += 1 + return coul(u'Tout est à jour, aucun mail envoyé.', 'vert') - s.close() - print coul(u"%d mail(s) envoyé(s) avec succès !" % nb, "vert") + def mail_cableurs(self, subject, body, liste=None): + """ + Envoie un mail aux câbleurs concernés qui figurent dans la liste. + Si liste vaut None, envoie un mail à tous les câbleurs concernés. + Les arguments subject, body permettent de personnaliser (un peu) + le mail qui sera envoyé. + """ + nb = 0 + if liste == None: + liste = self._cableurs + + for c in liste: + msg = u'' + + if self._paiements.has_key(c): + msg += u"Fiches d'adhésion et cotisations des adhérents :\n" + msg += formater_pour_cableur(self._paiements[c]) + '\n\n' + + if self._chartes.has_key(c): + msg += u"Chartes signées des clubs :\n" + msg += formater_pour_cableur(self._chartes[c]) + '\n\n' + + if self._cartes.has_key(c): + msg += u"Carte d'étudiant des adhérents :\n" + msg += formater_pour_cableur(self._cartes[c]) + '\n\n' + + if msg: + send_email(self._sender, + "%s@crans.org" % c, + subject, + body % msg, + server = self._server, + cc = u'Bureau ', + debug = debug) + nb += 1 + + return coul(u'%d mail(s) envoyé(s) aux câbleurs avec succès !' % nb, 'vert') -def __usage(): - u""" Comment ça marche ? """ - print __doc__ % {'prog': sys.argv[0]} +def __usage(message=None): + """ Comment ça marche ? """ + print __doc__ % { 'prog': sys.argv[0] } + if message: + print message sys.exit(1) + if __name__ == '__main__' : - if len(sys.argv) == 2: - if sys.argv[1] == 'paiement': - controle_interactif('p') - elif sys.argv[1] == 'carte': - controle_interactif('c') - elif sys.argv[1] == 'mail': - envoyer_mails_rappel() - else: - __usage() - else: + + if len(sys.argv) <= 1: __usage() + + elif sys.argv[1] == 'paiement': + if len(sys.argv) != 2: + __usage(u'Mauvaise utilisation de paiement') + controle_interactif('p') + + elif sys.argv[1] == 'carte': + if len(sys.argv) != 2: + __usage(u'Mauvaise utilisation de carte') + controle_interactif('c') + + elif sys.argv[1] == 'list': + if len(sys.argv) != 2: + __usage(u'Mauvaise utilisation de list') + print ControleMailer().recapitulatif(), + + elif sys.argv[1] == 'mail': + mailer = ControleMailer() + cableurs = sys.argv[2:] + if cableurs: + bureau = False + if 'bureau' in cableurs: + cableurs.remove('bureau') + bureau = True + else: + bureau = True + cableurs = mailer._cableurs + if cableurs: + print u'Des mails vont être envoyés aux câbleurs, lecture du modèle...' + subject, body = parse_mail_template(sys.stdin) + try: + body % u'' + except TypeError: + print u"Le format du modèle n'est pas correct, arrêt." + sys.exit(1) + print u'Modèle OK, on envoie les mails...' + print mailer.mail_cableurs(subject, body, cableurs) + if bureau: + print mailer.mail_bureau() + + else: + __usage(u'Commande inconnue : %s' % sys.argv[1]) + sys.exit(0) + + +# pydoc n'aime pas l'unicode :-(