diff --git a/intranet/modules/impression/main.py b/intranet/modules/impression/main.py index cd4a8b16..fda4bf93 100644 --- a/intranet/modules/impression/main.py +++ b/intranet/modules/impression/main.py @@ -25,7 +25,7 @@ # ############################################################# import cherrypy, tempfile, shutil, os, commands, re -import crans.impression +from crans.impression.impression_canon import FichierInvalide,SoldeInsuffisant,impression import crans.impression.digicode import crans.impression.etat_imprimante import crans.cp @@ -40,14 +40,14 @@ from ClassesIntranet.ModuleBase import ModuleBase # ############################################################# # Classe d'impression en multithread # ############################################################# -class threadedImpression(Thread, crans.impression.impression): +class threadedImpression(Thread, impression): def __init__(self, path_to_pdf, adh = None, callback = None): self.tpath_to_pdf = path_to_pdf self.tadh = adh Thread.__init__(self) def run(self): - crans.impression.impression.__init__(self, self.tpath_to_pdf, self.tadh) + impression(self, self.tpath_to_pdf, self.tadh) if self.tcallback: self.tcallback(self) @@ -158,10 +158,10 @@ class main(ModuleBase): def useFile(self, fileName): try: filepath = os.path.join(os.path.join(FILE_UPLOAD_BASE_FOLDER, cherrypy.session['uid']+"/"), fileName) - cherrypy.session['impression'] = crans.impression.impression(filepath, cherrypy.session['uid']) + cherrypy.session['impression'] = impression(filepath, cherrypy.session['uid']) crans.cp.log("useFile returns: %s" % str( cherrypy.session['impression'].pages() )) return {'nbPages': cherrypy.session['impression'].pages()} - except crans.impression.FichierInvalide, e: + except FichierInvalide, e: crans.cp.log("useFile : %s (%s)" % (str(e), e.file()), 'IMPRESSION', 1) return {'erreur':str(e) } except Exception, e: @@ -190,7 +190,7 @@ class main(ModuleBase): def lancerImpression(self): try: cherrypy.session['impression'].imprime() - except crans.impression.SoldeInsuffisant: + except SoldeInsuffisant: return {"SoldeInsuffisant":1} except Exception, e: crans.cp.log("lancerImpression : %s" % str(e), 'IMPRESSION', 1) diff --git a/lib/impression/etat_imprimante.py b/lib/impression/etat_imprimante.py index 5f1e2865..af3d4520 100644 --- a/lib/impression/etat_imprimante.py +++ b/lib/impression/etat_imprimante.py @@ -26,7 +26,7 @@ def etat_canon(): try: liste_msg = [] comm = hptools.snmp(host="imprimante.adm.crans.org", version="1", community="public") - for i in [".hrPrinterStatus.1"; ".hrPrinterDetectedErrorState.1"]: + for oid in [".hrPrinterStatus.1", ".hrPrinterDetectedErrorState.1"]: msg = comm.get_string(oid) msg = dico.get(msg, msg) if msg: liste_msg.append(msg) diff --git a/lib/impression/impression_canon.py b/lib/impression/impression_canon.py new file mode 100755 index 00000000..bb17dec2 --- /dev/null +++ b/lib/impression/impression_canon.py @@ -0,0 +1,439 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ############################################################# +# .. +# .... ............ ........ +# . ....... . .... .. +# . ... .. .. .. .. ..... . .. +# .. .. ....@@@. .. . ........ . +# .. . .. ..@.@@..@@. .@@@@@@@ @@@@@@. .... +# .@@@@. .@@@@. .@@@@..@@.@@..@@@..@@@..@@@@.... .... +# @@@@... .@@@.. @@ @@ .@..@@..@@...@@@. .@@@@@. .. +# .@@@.. . @@@. @@.@@..@@.@@..@@@ @@ .@@@@@@.. ..... +# ...@@@.... @@@ .@@.......... ........ ..... .. +# . ..@@@@.. . .@@@@. .. ....... . ............. +# . .. .... .. .. . ... .... +# . . .... ............. .. ... +# .. .. ... ........ ... ... +# ................................ +# +# ############################################################# +# __init__.py +# +# Classe impression +# +# Copyright (c) 2006 by www.crans.org +# ############################################################# +""" +Classe pour gérer l'envoie de pdf à l'imprimante. +Calcule le coût des options d'impression. +""" +__version__ = '1' + +import sys, syslog, os.path, time +sys.path.append('/usr/scripts/gestion') +from config import impression +from hptools import snmp +from crans.utils import QuoteForPOSIX as escapeForShell +import crans.utils.logs + +# ######################################################## # +# CONSTANTES # +# ######################################################## # + +LOG = crans.utils.logs.getFileLogger('impression') +COMM = hptools.snmp(host="imprimante.adm.crans.org",version="1",community="public") +FICHIER_LOG="/var/log/log_couts/impressions" + +SNMP_CAPA_B = "mib-2.43.11.1.1.8.1.1" +SNMP_CAPA_C = "mib-2.43.11.1.1.8.1.2" +SNMP_CAPA_M = "mib-2.43.11.1.1.8.1.3" +SNMP_CAPA_Y = "mib-2.43.11.1.1.8.1.4" +SNMP_TON_B = "mib-2.43.11.1.1.9.1.1" +SNMP_TON_C = "mib-2.43.11.1.1.9.1.2" +SNMP_TON_M = "mib-2.43.11.1.1.9.1.3" +SNMP_TON_Y = "mib-2.43.11.1.1.9.1.4" +SNMP_BAC1 = "mib-2.43.8.2.1.10.1.2" +SNMP_BAC2 = "mib-2.43.8.2.1.10.1.3" +SNMP_BAC3 = "mib-2.43.8.2.1.10.1.4" +SNMP_BAC4 = "mib-2.43.8.2.1.10.1.5" +SNMP_ETAT = "hrPrinterStatus.1" +SNMP_ERR = "hrPrinterDetectedErrorState.1" + +DECOUVERT_AUTHORISE = config.impression.decouvert + +# ######################################################## # +# ERREURS # +# ######################################################## # +# + +class FichierInvalide(Exception): + """ + Exception renvoyée lorsqu'un fichier ne passe pas. + utilisée avec deux arguments : une chaîne décrivant l'erreur et une chaîne avec le nom du fichier + """ + def __str__(self): + """ + Description de l'erreur. + """ + return self.args[0] + def file(self): + """ + Nom du fichier qui pose problème + """ + try: + return self.args[1] + except: + return "n/a" + +class SoldeInsuffisant(Exception): + """ + Solde insuffisant pour l'impression demandée + """ + pass +class PrintError(Exception): + """ + Erreur lors de l'impression + """ + pass +class SettingsError(Exception): + """ + Erreur de paramètres. + """ + pass +# ######################################################## # +# CLASSE IMPRESSION # +# ######################################################## # +# +# +class impression: + """impression + + Un objet impression correspond à un fichier pdf et un adhérent. + """ + # fichier (chemin) + _fichier = "" + # adherent (instance) + _adh = None + # paramettres + _settings = { + 'agrafage': 'None', + 'papier': 'A4' + 'couleur': False, + 'recto_verso': False, + 'livret': False, + 'copies': 1, + 'portrait': True, + } + # le prix de l'impression + _prix = 0.0 + _nb_pages = 0 + # le cout de base encre pour une impression en couleurs/n&b + # (prix pour papier A4) + _base_prix_nb = 0.0 + _base_prix_couleurs = 0.0 + + + def __init__(self, path_to_pdf, adh = None): + """impression(path_to_pdf [, adh]) + + Crée un nouvel objet impression à partir du fichier pdf pointé + par path_to_pdf. Si adh ext donné, il peut être soit une + instance d'un objet adhérent de crans_ldap soit le login de + l'adhérent. Lève l'exception FichierInvalide si le fichier + n'existe pas ou si ce n'est pas un pdf. + """ + self._fichier = path_to_pdf + self._adh = adh + + # on verifie que le fichier existe + if not os.path.isfile(path_to_pdf): + raise FichierInvalide, ("Fichier introuvable", path_to_pdf) + if not open(path_to_pdf).read().startswith("%PDF"): + raise FichierInvalide, ("Le fichier ne semble pas etre un PDF", path_to_pdf) + + # on compte les pages + self._pages = int(os.popen("pdfinfo %s | grep Pages " % (self._fichier)).readline().split()[1]) + + # calcule le prix de l'encre tout de suite + self._calcule_prix() + + + def _pdfbook(self): + if self.taille == 'A3': + pdfbook = "pdfbook --paper a3paper %s" + else: + pdfbook = "pdfbook %s" + (status, rep) = commands.getstatusoutput(pdfbook % self._fichier) + self._fichier = "%s-book.pdf" % self.n_fichier[:-4] + if status != 0: + log.error("pdfbook status:%d | rep: %s" % (status, rep)) + raise FichierInvalide, ("pdfbook: Impossible de convertir le fichier", + self._fichier) + + + def changeSettings(self, **kw): + """changeSettings([keyword=value]) + + Change les parametres de l'impression, recalcule et renvoie le nouveau prix. + Lève une exceotion SettingError si les paramètres son invalides. + """ + #recalcule et renvoie le prix + + couleur = kw.get('couleur', None) + if couleur in [True, False]: + self._settings['couleur'] = couleur + + try: + if int(kw['copies']) >= 1: + self._settings['copies'] = int(kw['copies']) + except: + pass + + recto_verso = kw.get('recto_verso', None) + if recto_verso in [True, False]: + self._settings['recto_verso'] = recto_verso + + papier = kw.get('papier', None) + if papier in ['A4', 'A3', 'A4tr']: + self._settings['papier'] = papier + if papier == 'A4tr': + self._settings['recto_verso'] = False + self._settings['agrafage'] = 'None' + + agrafage = kw.get('agrafage', None) + if agrafage in ["None", "TopLeft", "Top", "TopRight", + "Left", "Right", "BottomLeft", "BottomRight"]: + self._settings['agrafage'] = agrafage + + livret = kw.get('livret', None) + if livret in [True, False]: + self._settings['livret'] = livret + self._settings['portrait'] = not(livret) + if livret: + self._settings['recto_verso'] = True + self._settings['agrafage'] = 'None' + if self._settings['papier'] == 'A4tr': + self._settings['papier'] = 'A4' + + return self._calcule_prix() + + def printSettings(self): + """printSettings() + + Affiche les paramètres courrants sur la sortie standard + """ + dict_agrafage = { "None" : "aucune agrafe", + "TopLeft" : u"agrafe en haut à gauche", + "TopRight" : u"agrafe en haut à droite", + "BottomLeft" : u"agrafe en bas à gauche", + "BottomRight" : u"agrafe en bas à droite", + "Left": u"deux agrafes sur le bord gauche", + "Right" : u"deux agrafes sur le bord droit", + "Top" : u"deux agrafes sur le bord supérieur", + "Bottom" : u"deux agrafes sur le bord inférieur" } + dict_papier = { 'A4' : "Papier A4 ordinaire", + 'A3' : "Papier A3 ordinaire", + 'A4tr' : "Transparent A4" } + + if self._settings['couleur']: + print "Type impression: Couleur" + else: + print "Type impression: Noir et blanc" + + print "Papier: " + dict_papier[self._settings['papier']] + + if self._settings['livret']: + print u"Agrafage: Livret (piqûre à cheval)" + else: + print "Agrafage: " + dict_agrafage[self._settings['agrafage']] + if self._setting['recto_verso']: + print "Disposition: recto/verso" + else: + print "Disposition: recto" + + print "Copies: " + str(self._settings['copies']) + + + def prix(self): + """prix() + + Renvoie le prix courrant de l'impression + """ + return self._prix + + def fileName(self): + """fileName() + + renvoie le nom du fichier pdf (exemple : monPdf.pdf) + """ + return os.path.basename(self._fichier) + + def filePath(self): + """filePath() + + renvoie le chemin d'accès au fichier pdf. + """ + return self._fichier + + def pages(self): + """pages() + + renvoie le nombre de pages du document (page au sens nombre de + faces à imprimer et non le nombre de feuilles) + """ + return self._nb_pages + + + def imprime(self): + """imprime() + + imprime le document pdf. débite l'adhérent si adhérent il y a. + (si il a été indiqué à l'initialisation de l'objet) + """ + # debite l'adhérent si adherent il y a + if (self._adh != None): + adh = self._get_adh(self._adh) + if (self._prix > (adh.solde() - DECOUVERT_AUTHORISE)): + raise SoldeInsuffisant + adh.solde(-self._prix, "impression: " + self._fichier) + adh.save() + del adh + # imprime le document + self._exec_imprime() + + + def _calcule_prix(self): + + faces = self._pages + + if self._settings['livret']: + feuilles = int(faces+3/4) + elif self._settings['recto_verso']: + feuilles = int(faces/2.+0.5) + else: + feuilles = faces + + if (self._settings['papier'] == "A3"): + c_papier = impression.c_a3 + pages = 2*faces + else: + pages = faces + if self._settings['papier'] == "A4tr": + c_papier = impression.c_trans + else: + c_papier = impression.c_a4 + + if self._settings['couleur']: + c_impression = c_papier * pages + impression.c_face_couleur * pages + else: + c_impression = c_papier * pages + impression.c_face_nb * pages + + # Cout des agrafes + if self._settings['agrafe'] in ["Top", "Bottom", "Left", "Right"] or self._settings['livret']: + nb_agrafes = 2 + elif self._settings['agrafe'] in ["None", None]: + nb_agrafes = 0 + else: + nb_agrafes = 1 + + if feuilles <= 50: + c_agrafes = nb_agrafes * impression.c_agrafe + else: + c_agrafes = 0 + + c_total = int(self._settings['copies'] * ( c_impression + impression.fact + + c_agrafes ) + 0.5) # arrondi et facture + + return float(c_total)/100 + + def _get_adh(self, adh): + if type(adh) == str: + import sys + sys.path.append("/usr/scripts/gestion/") + #from ldap_crans_test import crans_ldap + from ldap_crans import CransLdap + adh = CransLdap().getProprio(adh, 'w') + return adh + + ## ################################# ## + ## fonction qui imprime pour de vrai ## + ## ################################# ## + ## + def _exec_imprime(self): + """ Envoie l'impression a l'imprimante avec les parametres actuels """ + + if self._settings['livret']: + self._pdfbook() + + if (self._adh != None): + log.info('Impression [%s] : %s' % (self._adh, self._fichier)) + else: + log.info("Impression : %s" % self._fichier) + + # Envoi du fichier à CUPS + options = '' + # Création de la liste d'options + # pour le nombre de copies et specifie non assemblee + #options += '-# %d -o Collate=True' % self.nb_copies + + # Pour spécifier l'imprimante + options += ' -P canon_irc3580' + + # 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.replace("\"","\\\"") + + # Pour donner le login de l'adherent + options += ' -U \"%s\"' % self.user.replace("\"","\\\"") + + # Pour demander une page de garde + #options += ' -o job-sheets=crans' #page de garde de type standard + #options += " -o job-billing=%.2f" % self.cout + #options += ' -o job-sheets=none' + + #Indique la présence d'un bac de sortie avec agrafeuse + # options += " -o Option20=MBMStaplerStacker -o OutputBin=StackerDown" + + if self._settings['paper'] == 'A4tr': + options += ' -o InputSlot=SideDeck -o MediaType=OHP' + options += ' -o pdf-paper=571x817 -o PageSize=A4' + elif self._settings['paper'] == 'A4': + options += ' -o pdf-paper=571x817 -o PageSize=A4' + else: + options += ' -o pdf-expand -o pdf-paper=825x1166 -o PageSize=A3' + + if self._settings['portrait']: + if self._settings['recto_verso']: + options += ' -o sides=two-sided-long-edge' + else: + options += ' -o sides=one-sided' + else: + if self._settings['recto_verso']: + options += ' -o sides=two-sided-short-edge' + else: + options += ' -o sides=one-sided' + if self._settings['couleur']: + options += ' -o CNColorMode=color' + else: + options += ' -o CNColorMode=mono' + + if self._settings['livret']: + options += ' -o CNSaddleStitch=True' + options += ' -o OutputBin=TrayC' + else: + options += ' -o OutputBin=TrayA' + options += ' -o StapleLocation=%s' % self._settings['agrafage'] + + cmd = "lpr %s -# %d %s" % (options, self._settings['copies'], + self._fichier) + (status, rep) = commands.getstatusoutput(cmd) + if status != 0: + log.error("erreur impression") + log.error("lpr status:%d | rep: %s" % (status, rep)) + raise PrintError, "%s \n status:%d rep: %s" % (cmp, status, rep) + + diff --git a/lib/impression/__init__.py b/lib/impression/impression_laserjet.py similarity index 98% rename from lib/impression/__init__.py rename to lib/impression/impression_laserjet.py index 871ef10f..bcb1214b 100644 --- a/lib/impression/__init__.py +++ b/lib/impression/impression_laserjet.py @@ -156,8 +156,8 @@ LPR_OPTIONS = { class FichierInvalide(Exception): """ - Exception renvoyée lorsqu'un fichier ne passe pas. - utilisée avec deux arguments : une chaîndéÃécrivant l'erreur et une chÃine avec le nom du fichier + Exception renvoyée lorsqu'un fichier ne passe pas. + utilisée avec deux arguments : une chaîne décrivant l'erreur et une chaîne avec le nom du fichier """ def __str__(self): """ @@ -196,7 +196,7 @@ class SettingsError(Exception): class impression: """impression - Un objet impression correspond ˆ un fichier pdf et un adhŽrent. + Un objet impression correspond à un fichier pdf et un adhérent. """ # fichier (chemin) _fichier = ""