scripts/impression/class_impression.py
bobot 1d2386d1fe Pour régler le problème de l'impression de la bannière en A4 pour un
document A3, la bannièrene seras imprimée que pour le A4. séparerdes
documents A3 par du A4 ne semble pas très utile de toutes façons.

darcs-hash:20060218151509-9e428-e6d4421dab5c22f8a551f1d297dfc05a975ffd80.gz
2006-02-18 16:15:09 +01:00

456 lines
19 KiB
Python
Executable file

#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
"""
Class pour l'impression depuis le web par cups
Codé par Francois
Inspiré par le backend écrit par Benoit, Fred et Brice, inspirés par CUPSPykota
Licence : GNU General Public Licence, version 2
"""
import sys, time, tempfile, os, commands, string, random, syslog
sys.path.append('/usr/scripts/impression')
sys.path.append('/usr/scripts/gestion')
from config import impression
from ldap_crans import crans_ldap
duree_vie_pdf=3600
#fonction principal: impression (fichier_impression)
################################################################################
class ErreurImpression(Exception):
def __init__(self, value):
self.value=value
def __str__(self):
return repr(self.value)
################################################################################
class test:
def mail(self):
return u"francois.bobot@crans.org"
def Nom(self):
return u"francois"
def solde(self,modif=0.):
return (40.+modif)
def save(self):
pass
################################################################################
class fichier_impression :
nom_job=''
nbr_pages=1
nb_copie=1
taille="A4"
recto_verso=False
transparent=False
couleur=True
cout=0.0
portrait=True
user=""
user_ldap=None # sera une instance de l'utilisateur dans la base ldap.
imprime=-1 #-2 impression en suspend, -1 mise dans la file
# d'attente, 0 devis, time pour l'heure, -3 déja
# imprimé ou devis fait depuis trops longtemps : le
# pdf n'est plus disponible
nom_fichier_pdf="" # chemin et nom du pdf
nom_fichier_desc="" # chemin et nom du fichier contenant les
# options de l'utilisateur
list_messages=[] # liste d'erreur et de remarque
list_messages_importants=[]
list_messages_admin=[] # liste d'erreur réservé au administrateur
erreur_critique=False # True si une erreur empêchant
# l'impression est apparue
code=None # code pour le digicode
modif_epoch=0.0 # moment de création du job ou devis. exprimé
# en epoch (float) : nombre de seconde depuis
# la référence du temps unix (+-=1970)
job_id=None # champs pour de futures améliorations
def actualise_cout(self):
# Calcul du cout de l'impression :
retour = cout(self)
# Met le prix dans cout s'il n'y a pas d'erreur
if type(retour) is float :
self.cout = retour
self.list_messages_importants.append('cout')
else:
self.erreur_critique = True
self.list_messages_importants.append('erreur_critique')
self.list_messages_admin.append('erreur_cout')
syslog.openlog("impression")
syslog.syslog(retour)
# Repond vrai si le solde est assez élevé.
def test_cout(self):
if self.user_ldap == None:
self.user_ldap = utilisateur(self.user, False)
return not (self.cout > (self.user_ldap.solde() -
impression.decouvert)) # /!\ decouvert est négatif.
def fait_payer(self):
self.user_ldap = utilisateur(self.user,True)
self.user_ldap.solde(-self.cout, self.nom_job)
self.user_ldap.save()
return not (self.cout > (self.user_ldap.solde() - impression.decouvert))
def gen_code(self):
# Génération du code et écriture du code
rand=random.Random()
# Graine automatique avec le temps
rand.seed()
for i in range(1000):
# On génère un code
code = rand.randint(100000,999999)
# Si le code est libre, on sort de la boucle
if not os.path.exists("/var/impression/codes/%d" % code):
break
else:
# Pas de code disponible
print ("ERROR: Il n'y a pas de code disponible" )
sys.stderr.write ("ERROR: Il n'y a pas de code disponible" )
try:
self.list_messages_importants.append('erreur gen_code')
sys.stderr.write("DEBUG: Un rapport de bug a ete automatiquement envoye.\n")
except:
sys.stderr.write("ERROR: Impossible d'envoyer le rapport de bug.\n")
sys.stderr.write("ERROR: Plus de codes disponibles.\n")
sys.stderr.write("ERROR: Penser a ouvrir a l'adherent debite...\n")
return
# On enregistre le fichier avec le code pour numéro
codefichier = open("/var/impression/codes/%d" % code, 'w')
codefichier.write("Utilisateur %s\n" % self.user)
codefichier.close()
self.code=code
self.list_messages_importants.append('code')
def impression(self):
# Envoi du fichier à CUPS
# Création de la liste d'options
# pour le nombre de copies
options ='-# %d' % self.nb_copie
# Pour spécifier l'imprimante
options +=' -P laserjet'
# Pour spécifier la version du language postscript utilisé par pdftops
options +=' -o pdf-level3'
# Pour donner le titre de l'impression
options +=" -T '%s'" % self.nom_job
# Pour donner le login de l'adherent
options +=' -U %s' % self.user
# Pour demander une page de garde
#options += ' -o job-sheets=crans' #page de garde de type standard
options += " -o job-billing=%.2f" % self.cout
if self.transparent :
options += ' -o InputSlot=Tray1 -o Media=Transparency'
if self.taille == 'A4':
options += ' -o job-sheets=crans -o pdf-paper=571x817 -o PageSize=A4'
else:
options += ' -o pdf-paper=825x1166 -o InputSlot=Tray3 -o HPPaperPolicy=A3 -o PageSize=A3'
if self.portrait:
if self.recto_verso:
options += ' -o sides=two-sided-long-edge'
else:
options += ' -o sides=one-sided'
else:
options += ' -o landscape'
if self.recto_verso:
options += ' -o sides=two-sided-short-edge'
else:
options += ' -o sides=one-sided'
options += ' -o HPColorasGray=%s' % (not self.couleur)
(status,rep) = commands.getstatusoutput("lpr %s %s" % (options, self.nom_fichier_pdf))
if status != 0:
print "<p>status:%d rep: %s</p>" % (status, rep)
syslog.openlog("impression")
syslog.syslog("lpr status:%d | rep: %s" % (status, rep))
def corrige(self):
# Trouve le nombre de pages
self.nbr_pages = int(os.popen("pdfinfo %s | grep Pages " % (self.nom_fichier_pdf)).readline().split()[1])
# Corrige les aberrations
# Correction des aberations (transparent et recto_verso :) )
# Priorité des informations par ordre décroissant
# Transparent, paper, nbr_page, recto_verso
if (self.taille != 'A4') & (self.taille != 'A3'):
self.list_messages_importants.append("paper")
self.erreur_critique=True
if self.transparent:
if self.paper != 'A4':
self.list_messages.append("A3 transparent")
self.paper = 'A4'
if self.recto_verso:
self.list_messages.append("Recto_verso transparent")
self.recto_verso = False
if self.nbr_pages == 1 and self.recto_verso:
self.list_messages.append("Recto_verso 1 page")
self.recto_verso = False
def affiche_messages_html(self):
body=""
"""
if self.imprime:
body=string.join(map ((dico_message_laserjet.get),self.list_messages_importants))
if len(self.list_messages)<>0:
body=body+'\nRemarques:\n'+string.join(map ((dico_message_laserjet.get),self.list_messages))
else:
body=string.join(map ((dico_message_devis.get),self.list_messages_importants))
body=body+'\nRemarque:\n'+string.join(map ((dico_message_devis.get),self.list_messages))
body=body % {'taille' : self.taille, 'code' : self.code, 'solde' : self.user_ldap.solde(), 'cout' : self.cout , 'adresse_imprimante' : impression.From_imprimante}
"""
body=string.join(self.list_messages_importants)
return "<p>%s</p>" % body
def enregistre_pdf(self, f_value, f_nom, dossier):
syslog.openlog("impression")
try :
(fd_fichier_desc, self.nom_fichier_desc) = tempfile.mkstemp(suffix='.desc', prefix='job', dir=dossier)
os.close(fd_fichier_desc)
except Exception , inst:
raise ErreurImpression("class :erreur dans creation de desc %s : %s" % (self.nom_fichier_desc,str(inst)))
self.nom_fichier_pdf = self.nom_fichier_desc.replace('.desc', '.pdf')
try :
fd_fichier_pdf = open(self.nom_fichier_pdf, 'w')
fd_fichier_pdf.write(f_value)
fd_fichier_pdf.close()
except Exception, inst:
raise ErreurImpression("class :erreur dans enregistrement du .pdf %s: %s" % (self.nom_fichier_pdf,str(inst)))
os.chmod(self.nom_fichier_pdf, 0640)
os.chmod(self.nom_fichier_desc, 0640)
self.nbr_pages = int(os.popen("pdfinfo '%s' | grep Pages " % (self.nom_fichier_pdf)).readline().split()[1])
self.modif_epoch = time.time()
def sauve_desc(self):
try :
file_obj_desc = open(self.nom_fichier_desc,'w')
for key in ("erreur_critique", "nom_job", "nbr_pages",
"nb_copie","taille", "recto_verso",
"transparent", "couleur", "cout",
"portrait", "user", "imprime",
"nom_fichier_pdf", "nom_fichier_desc",
"code", "modif_epoch", "job_id"):
file_obj_desc.write(key+"="+str(getattr(self,key))+"\n")
file_obj_desc.close()
except Exception, inst :
raise ErreurImpression("class : erreur dans enregistrement du .desc %s : %s" % (self.nom_fichier_desc,str(inst)))
def read_desc(self):
try :
file_obj_desc = open(self.nom_fichier_desc, 'r')
ligne = file_obj_desc.readline()[0:-1]
while ligne != "":
parse = ligne.split("=")
if parse[1] == 'None':
setattr(self,parse[0],None)
else:
if parse[0] in ("nom_job", "taille", "user",
"nom_fichier_pdf", "nom_fichier_desc",
"job_id"):
setattr(self,parse[0],parse[1])
else:
if parse[0] in ("nbr_pages", "nb_copie",
"imprime", "code"):
setattr(self,parse[0],int(parse[1]))
else:
if parse[0] in ("recto_verso", "transparent",
"couleur", "portrait",
"erreur_critique"):
setattr(self,parse[0],(parse[1] == 'True'))
else:
if parse[0] in ("cout", "modif_epoch"):
setattr(self, parse[0], float(parse[1]))
else:
self.erreur_critique = True
print '<p>ligne non parsée: %s</p>' % ligne
ligne=file_obj_desc.readline()[0:-1]
file_obj_desc.close()
except Exception, inst :
raise ErreurImpression("class : erreur dans l'ouverture du .desc %s : %s" % (self.nom_fichier_desc,repr(inst)))
# Verifie que le fichier pdf existe bien si il doit exister.
if not os.path.exists(self.nom_fichier_pdf):
if self.imprime != -3:
self.imprime = -4
syslog.openlog("impression")
syslog.syslog("class : %s a disparue" % self.nom_fichier_pdf)
def __repr__(self):
""" renvoie le string canonique de l'objet
fonction utilisé par print et str """
dict_contraire = { 'couleur' : 'Noir et Blanc',
'transparent' : 'Normal',
'portrait': 'Paysage',
'recto_verso' : 'Recto',
'erreur_critique' : "Pas d'erreur" }
dict_normale = { 'couleur' : 'Couleur',
'transparent' : 'Transparent',
'portrait': 'Portrait',
'recto_verso' : 'Recto-Verso',
'erreur_critique' : "Problême survenue" }
corps = "<TABLE BORDER=0 CELLPADDDING=2>\n<TR><th ALIGN=CENTER COLSPAN=2><U>%s</U></th><td ALIGN=RIGHT>%s</TR>\n" % (self.nom_job, time.ctime(0.0))
corps += "<TR><td>\n<TABLE BORDER=2 RULES=ROWS CELLPADDDING=0>\n"
self.code = "%s#" % self.code
for key in ("nbr_pages", "nb_copie", "taille", "code"):
corps+='<TR><td>%s</td><td>%s</td></TR>\n' % (key,
str(getattr(self,key)))
self.code = self.code[:-1]
corps+="</TABLE>\n</td>\n<td>\n<TABLE BORDER=2 RULES=ROWS CELLPADDDING=0>\n"
for key in ("couleur", "transparent",
"portrait", "recto_verso"):
if getattr(self, key):
corps += '<TR><td>%s</td></TR>\n' % dict_normale[key]
else:
corps += '<TR><td>%s</td></TR>\n' % dict_contraire[key]
corps+="</TABLE>\n</td><td><U>"
if self.erreur_critique:
corps += "erreur de comptage"
else:
corps += "%s euros" % self.cout
corps+="</U></td></TR>\n</TABLE>"
return corps
################################################################################
def utilisateur(user, rw):
""" Renvoie l'adherent qui imprime le job
* user est l'utilisateur (argument n°2 des paramètres passés au backend)
* rw vaut True si on veut modifier le compte LDAP de l'adhérent,
False dans le cas contraire
"""
# Impression en local avec les certificats (possible pour le groupe adm)
if user == "root":
raise ErreurImpression("class : Utilisateur root passe en parametre.")
# Récupération de l'adhérent
try:
base = crans_ldap()
if rw:
res = base.search("login=%s" % user,'w')['adherent']
else:
res = base.search("login=%s" % user)['adherent']
except:
raise ErreurImpression("class : Base LDAP non joignable.")
# Si on ne trouve rien :
if len(res) != 1 :
raise ErreurImpression("class : adherent %s introuvable\n" % user)
adherent = res[0]
syslog.openlog("impression")
syslog.syslog(syslog.LOG_DEBUG,"class : Adherent %s (aid=%s) recupere.\n" % (user, adherent.id()))
return adherent
################################################################################
def cout(fic_impr):
""" Calcule le pourcentage de couleur sur les pages
fic_impr est une instance de fichier_impression
retourn :
un string avec le nom de l'erreur si erreur il y a
un float dans les cas de fonctionnement
"""
# taille peut valoir A3 ou A4
# nb_copie est le nombre de copies désirées
# nom_rep seras le dossier dans tmp ou tous les fichier créé par
# convert seront entreposé
nom_rep = tempfile.mkdtemp(prefix='tmpimpr')
nom_png = "%s/convert.png" % nom_rep # Nom prefixe et chemin des png créé par convert
if (fic_impr.taille == "A3"):
# Une feuille A3 couvre 2 fois plus de surface que A4
c_taille = impression.c_a3
cout_coul = 2*impression.c_coul
cout_noir = 2*impression.c_noir
else:
cout_coul = impression.c_coul
cout_noir = impression.c_noir
if fic_impr.transparent:
c_taille = impression.c_trans
else:
c_taille = impression.c_a4
if (fic_impr.couleur):
# Convertit les pdf en png couleur
(status, rep) = commands.getstatusoutput("nice -n 5 gs -sDEVICE=png16m -r30 -dBATCH -dNOPAUSE -dSAFER -dPARANOIDSAFER -dGraphicsAlphaBits=4 -dTextAlphaBits=4 -dMaxBitmap=50000000 -sOutputFile=%s -q %s" % (nom_png,fic_impr.nom_fichier_pdf))
if status:
return "ERREUR : Fichier invalide. Aucun png n'a ete cree.\n"
# Récupère la liste des fichiers
list_filepng=os.listdir(nom_rep)
# Calcule le nombre de pixel de couleur
remplissage = [0, 0, 0, 0, 0] # C, M, J, N, nombre de pages
for fichier in list_filepng:
resultats = commands.getoutput("nice -n 5 /usr/scripts/impression/percentcolour %s/%s" % (nom_rep, fichier))
l_resultats = resultats.split(":")
for i in [0, 1, 2, 3]:
remplissage[i] += float(l_resultats[i])*float(l_resultats[4])
remplissage[4] += float(l_resultats[4])
total_noir = remplissage[3]
total_couleur = sum(remplissage[0:3])
faces = int(remplissage[4])
if (fic_impr.recto_verso == False):
pages = faces # nb de pages par copies
else:
pages = int(faces/2.+0.5)
if total_couleur > 0:
c_total = (c_taille * pages + (impression.c_tambour_coul +
impression.c_tambour_noir) * faces +
cout_noir * total_noir + cout_coul * total_couleur)
else: # Pas de couleur, malgre l'indication
c_total = (c_taille * pages +
impression.c_tambour_noir * faces+cout_noir * total_noir)
else:
# Convertit les pdf en png
(status, rep) = commands.getstatusoutput("nice -n 5 gs -sDEVICE=pnggray -r30 -dBATCH -dNOPAUSE -dSAFER -dPARANOIDSAFER -dGraphicsAlphaBits=4 -dTextAlphaBits=4 -dMaxBitmap=50000000 -sOutputFile=%s -q %s" % (nom_png, fic_impr.nom_fichier_pdf))
if status:
return "ERREUR : Fichier invalide. Aucun png n'a été créé.\n"
#récupère la liste des fichiers
list_filepng=os.listdir(nom_rep)
remplissage = [0, 0] # Noir, nombre de pages
for fichier in list_filepng:
resultats = commands.getoutput("nice -n 5 /usr/scripts/impression/percentblack '%s/%s'" % (nom_rep, fichier))
l_resultats = resultats.split(":")
remplissage[0] += float(l_resultats[0])*float(l_resultats[1])
remplissage[1] += float(l_resultats[1])
total_noir = remplissage[0]
faces = int(remplissage[1])
if (fic_impr.recto_verso == False):
pages = faces # nb de pages par copies
else:
pages = int(faces/2.+0.5)
c_total = (c_taille * pages +
impression.c_tambour_noir * faces+cout_noir * total_noir)
c_total = int(fic_impr.nb_copie * c_total + impression.fact + 0.5) # arrondi et facture
if commands.getoutput("rm -r -f %s" % nom_rep):
self.list_messages_admin.append('erreur_cout')
sys.stderr.write("ERREUR : Impossible d'enlever le dossier.\n")
return float(c_total)/100