191 lines
7.5 KiB
Python
Executable file
191 lines
7.5 KiB
Python
Executable file
#! /usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
print_status.py
|
||
|
||
Récupère le statut des dernières impressions auprès de l'imprimante et envoie un
|
||
mail à l'intérressé quand son impression est sortie, ou envoie un message aux
|
||
imprimeurs si l'impression semble avoir échoué.
|
||
|
||
Auteur : Sylvain Boilard <boilard@crans.org>
|
||
License : BSD 2 clauses.
|
||
|
||
"""
|
||
|
||
import sys, os
|
||
sys.path.append("/usr/scripts/gestion")
|
||
|
||
import time
|
||
import httplib2
|
||
import re
|
||
import smtplib
|
||
from ldap_crans import crans_ldap
|
||
|
||
last_print_filename = "/var/run/print_status/last_print.txt"
|
||
error_filename = "/var/run/print_status/error.txt"
|
||
# Cette chaîne est utilisée pour construire une regexp, il faut que ce soit une chaîne brute.
|
||
files_directory = r"/var/impression/fichiers/"
|
||
printer_host = "imprimante.adm.crans.org"
|
||
smtp_server = "smtp.crans.org"
|
||
error_send_to = u"impression@crans.org"
|
||
ok_mail = u"""Content-Type: text/plain; charset=utf-8
|
||
Content-Disposition: inline
|
||
Content-Transfer-Encoding: quoted-printable
|
||
From: %s
|
||
To: %s
|
||
Subject: [Impression] Impression de "%s" terminée.
|
||
|
||
Bonjour %s,
|
||
|
||
Le fichier "%s" que tu as envoyé %s
|
||
a été imprimé. Tu peux venir le récupérer au local impression, au 4ème étage du
|
||
bâtiment J, muni du code secret qui t'a été donné lorsque tu as envoyé ton
|
||
document. Si tu as oublié ce code, ou pour tout autre problème lié au service
|
||
d'impression, tu peux contacter les imprimeurs à l'adresse %s .
|
||
|
||
Bonne journée,
|
||
|
||
--
|
||
Les imprimeurs du Cr@ns
|
||
"""
|
||
error_mail = u"""Content-Transfer-Encoding: quoted-printable
|
||
Content-Type: text/plain; charset=utf-8
|
||
Content-Disposition: inline
|
||
From: %s
|
||
To: %s
|
||
Subject: [Impression][print_status.py] Impression potentiellement échouée.
|
||
|
||
Bonjour,
|
||
|
||
Il semble y avoir eu un problème avec l'impression du fichier
|
||
"%s" (tâche n°%s) de %s (uid=%s),
|
||
lancée %s.
|
||
Le statut de la tâche est "%s".
|
||
|
||
Aucun autre mail ne sera envoyé à ce sujet. Si nécéssaire, veuillez tenir
|
||
l'intéressé au courant aux adresses suivantes :
|
||
|
||
%s
|
||
|
||
Bonne journée,
|
||
|
||
--
|
||
print_status.py
|
||
"""
|
||
|
||
litMonths = [u"Janvier", u"Février", u"Mars", u"Avril", u"Mai", u"Juin", u"Juillet", u"Août", u"Septembre", u"Octobre", u"Novembre", u"Décembre"]
|
||
|
||
def getMSE():
|
||
"""Retourne le nombre de millisecondes depuis le 1 Janvier 1970 sous forme d'une chaîne de caractères."""
|
||
return str(int(time.time()*1000.))
|
||
|
||
def buildDate(date):
|
||
"""Retourne la date et l'heure passée en argument après l'avoir mise en forme."""
|
||
dm, year, hms = date.split(' ')
|
||
day, month = dm.split('/')
|
||
hour, minute, _ = hms.split(':')
|
||
return u"le " + day + u" " + litMonths[int(month) - 1] + u" " + year + u" à " + hour + u":" + minute
|
||
|
||
def sendMail(from_addr, to_addrs, mail_content):
|
||
"""Wrapper pour smtplib.sendmail(). Permet d'attendre un peu en cas d'erreur temporaire (typiquement, si on essaie d'envoyer trop de mails à la minute)."""
|
||
conn = smtplib.SMTP(smtp_server)
|
||
try:
|
||
conn.sendmail(from_addr, to_addrs, mail_content)
|
||
except smtplib.SMTPSenderRefused as (code, _, _):
|
||
if code == 450:
|
||
time.sleep(65)
|
||
conn.sendmail(from_addr, to_addrs, mail_content)
|
||
else:
|
||
raise
|
||
conn.quit()
|
||
|
||
http = httplib2.Http()
|
||
|
||
# On récupère la liste des tâches. Pour faire la requête, on doit récupérer un cookie de session.
|
||
headers, _ = http.request("http://" + printer_host + "/twelcome.cgi?CorePGTAG=0&Dummy=" + getMSE())
|
||
_, content = http.request("http://" + printer_host + "/pprint.csv?Flag=Csv_Data&LogType=0&Dummy=" + getMSE(), 'GET', headers={'Cookie': headers['set-cookie']})
|
||
task_list = content.split('\n')
|
||
# On vérifie que ce qu’on a récupéré ressemble à du CSV. Si tout se passe bien, l’imprimante envoie 13 champs par tâche.
|
||
# On fait le test sur les entêtes, ce qui permet de s’en débarasser au passage.
|
||
if len(task_list.pop(0).split(',')) != 13:
|
||
# En cas d’erreur sur l’imprimante, plutôt que de spamer sur roots@crans.org, on note l’échec quelque part à l’attention d’un service de monitoring.
|
||
error_file = open(error_filename, "w")
|
||
error_file.write("La liste des tâches renvoyée par l’imprimante n’a pas le format attendu.")
|
||
error_file.close()
|
||
exit(0)
|
||
os.system("rm -f " + error_filename)
|
||
# On enlève aussi les deux lignes vides à la fin de la liste.
|
||
task_list.pop()
|
||
task_list.pop()
|
||
|
||
# On récupère la dernière tâche à avoir été traitée la dernière fois quel le script a été lancé.
|
||
# Si le fichier n'existe pas, on le crée en y mettant la première tâche parmi celles que l'on vient de récupérer, et on s'arrête là.
|
||
try:
|
||
last_file = open(last_print_filename, "r")
|
||
last = last_file.read()
|
||
last_file.close()
|
||
except:
|
||
last_file = open(last_print_filename, "w")
|
||
last_file.write(task_list[0].split(',')[0])
|
||
last_file.close()
|
||
exit(0)
|
||
|
||
db = crans_ldap()
|
||
|
||
# On parcoure les tâches récupérées et on ne garde que celles qui n'ont pas déja été traitées.
|
||
# Au passage, on inverse leur ordre pour traiter la plus ancienne en premier.
|
||
# Ainsi, si le script plante, on n'envoie pas un mail aux adhérents à chaque fois que le cron le relance.
|
||
tasks_to_treat = []
|
||
for item in task_list:
|
||
if item.split(',', 1)[0] == last:
|
||
break
|
||
tasks_to_treat.append(item)
|
||
tasks_to_treat.reverse()
|
||
|
||
for item in tasks_to_treat:
|
||
fields = item.split(',', 6)
|
||
# On met à jour le numéro de la dernière tâche traitée.
|
||
last_file = open(last_print_filename, "w")
|
||
last_file.write(fields[0])
|
||
last_file.close()
|
||
if fields[3].strip('"') in ["root",'DIRECT PRINT']:
|
||
result = fields[1].strip('"')
|
||
jobinfos = fields[2].strip('"').split(':', 2)
|
||
if len(jobinfos) <= 2:
|
||
print "Skipping: %s" % fields[2]
|
||
continue
|
||
taskID, user, _ = jobinfos
|
||
user = user.split('@').pop() # On récupère le nom du club si besoin.
|
||
date = buildDate(fields[5])
|
||
match_taskID = re.compile(r"impression\(%s\)" % taskID)
|
||
db_query = db.search("uid=" + user)
|
||
if db_query['adherent']:
|
||
target = db_query['adherent'][0]
|
||
full_name = target.prenom() + u" " + target.nom()
|
||
if not '@' in target.mail():
|
||
mail = target.mail() + '@crans.org'
|
||
else:
|
||
mail = target.mail()
|
||
send_to = [ mail ]
|
||
else:
|
||
target = db_query['club'][0]
|
||
full_name = u"Club " + target.nom()
|
||
send_to = map(lambda aid : db.search("aid=" + aid)['adherent'][0].mail(), target.imprimeurs())
|
||
if send_to == []:
|
||
print "Skipping sending to %s, empty recipient" % full_name
|
||
continue
|
||
historique = target.historique()
|
||
historique.reverse() # Ce qu'on cherche a des chances d'être récent et donc d'être à la fin de l'historique.
|
||
file_dirname = files_directory + user + r"/"
|
||
match_doc_name = re.compile(file_dirname + r"[^ ]* ")
|
||
for hist_line in historique:
|
||
if match_taskID.search(hist_line):
|
||
filename = match_doc_name.search(hist_line).group()[len(file_dirname):-1]
|
||
break
|
||
if result == "OK":
|
||
mail_content = ok_mail % (error_send_to, u", ".join(send_to), filename, full_name, filename, date, error_send_to)
|
||
sendMail(error_send_to, send_to, mail_content.encode("utf-8"))
|
||
else:
|
||
mail_content = error_mail % (error_send_to, error_send_to, filename, taskID, full_name, user, date, result, u"\n".join(send_to))
|
||
sendMail(error_send_to, error_send_to, mail_content.encode("utf-8"))
|