#!/usr/bin/env python # -*- coding: iso-8859-15 -*- # ############################################################# # .. # .... ............ ........ # . ....... . .... .. # . ... .. .. .. .. ..... . .. # .. .. ....@@@. .. . ........ . # .. . .. ..@.@@..@@. .@@@@@@@ @@@@@@. .... # .@@@@. .@@@@. .@@@@..@@.@@..@@@..@@@..@@@@.... .... # @@@@... .@@@.. @@ @@ .@..@@..@@...@@@. .@@@@@. .. # .@@@.. . @@@. @@.@@..@@.@@..@@@ @@ .@@@@@@.. ..... # ...@@@.... @@@ .@@.......... ........ ..... .. # . ..@@@@.. . .@@@@. .. ....... . ............. # . .. .... .. .. . ... .... # . . .... ............. .. ... # .. .. ... ........ ... ... # ................................ # # ############################################################# # __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 sys.path.append('/usr/scripts/gestion') import config import cout from crans.utils import QuoteForPOSIX as escapeForShell import crans.utils.logs log = crans.utils.logs.getFileLogger('impression') # ######################################################## # # CONSTANTES # # ######################################################## # # # DECOUVERT_AUTHORISE = config.impression.decouvert PAS_D_AGRAPHES = "pasdagraphes" AGRAPHE_DIAGONALE = "agraphediagonale" UNE_AGRAPHE = "uneagraphe" DEUX_AGRAPHE = "Deuxagraphes" TROIS_AGRAPHE = "troisAgraphes" STITCHING = "stitching" AGRAPHES_VALEURS_POSSIBLES = [PAS_D_AGRAPHES, AGRAPHE_DIAGONALE, UNE_AGRAPHE, DEUX_AGRAPHE, TROIS_AGRAPHE, STITCHING] NB_AGRAPHES = { PAS_D_AGRAPHES: 0, AGRAPHE_DIAGONALE: 1, UNE_AGRAPHE: 1, DEUX_AGRAPHE: 2, TROIS_AGRAPHE: 3, STITCHING: 6, } PAPIER_A4 = "A4" PAPIER_A4_TRANSPARENT = "A4tr" PAPIER_A3 = "A3" PAPIER_VALEURS_POSSIBLES = [PAPIER_A4, PAPIER_A4_TRANSPARENT, PAPIER_A3] IMPRESSION_COULEUR = "couleurs" IMPRESSION_NB = "nb" COULEURS_VALEURS_POSSIBLES = [IMPRESSION_COULEUR, IMPRESSION_NB] IMPRESSION_RECTO = "recto" IMPRESSION_RECTO_VERSO = "rectoverso" DISPOSITION_VALEURS_POSSIBLES = [IMPRESSION_RECTO, IMPRESSION_RECTO_VERSO] PU_AGRAPHE = config.impression.c_agrafe / 100. PU_FEUILLE = { PAPIER_A4: config.impression.c_a4/100, PAPIER_A4_TRANSPARENT: config.impression.c_trans/100, PAPIER_A3: config.impression.c_a3/1000, } LABELS = { PAS_D_AGRAPHES: "pas d'agrafe", AGRAPHE_DIAGONALE: 'une agrafe en diagonale', UNE_AGRAPHE: 'une agrafe en haut', DEUX_AGRAPHE: '2 agrafes', TROIS_AGRAPHE: '3 agrafes', STITCHING: 'stitching (6 agrafes)', PAPIER_A4: "A4", PAPIER_A4_TRANSPARENT: "A4 transparent", PAPIER_A3: "A3", IMPRESSION_COULEUR: "impression couleurs", IMPRESSION_NB: "impression noir et blanc", IMPRESSION_RECTO: "impression recto", IMPRESSION_RECTO_VERSO: "impression recto-verso", } LPR_OPTIONS = { PAS_D_AGRAPHES: ' -o StapleLocation=None', AGRAPHE_DIAGONALE: ' -o StapleLocation=1diagonal', UNE_AGRAPHE: ' -o StapleLocation=1parallel', DEUX_AGRAPHE: ' -o StapleLocation=2parallel', TROIS_AGRAPHE: ' -o StapleLocation=3parallel', STITCHING: ' -o StapleLocation=Stitching', PAPIER_A4: ' -o InputSlot=Tray4 -o pdf-paper=571x817 -o PageSize=A4', PAPIER_A4_TRANSPARENT: ' -o PageSize=A4 -o InputSlot=Tray1 -o Media=Transparency', PAPIER_A3: ' -o pdf-expand -o pdf-paper=825x1166 -o InputSlot=Tray3 -o HPPaperPolicy=A3 -o PageSize=A3', IMPRESSION_COULEUR: ' -o HPColorasGray=False', IMPRESSION_NB: ' -o HPColorasGray=True', IMPRESSION_RECTO: ' -o sides=one-sided', IMPRESSION_RECTO_VERSO: ' -o sides=two-sided-long-edge', } # ######################################################## # # 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 chaine 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 = { 'agraphes': PAS_D_AGRAPHES, 'papier': PAPIER_A4, 'couleurs': IMPRESSION_COULEUR, 'recto_verso': IMPRESSION_RECTO_VERSO, 'copies':1, } # le prix de l'impression _prix = 0.0 _nb_pages = 0 _details_devis = [] # 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 # 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) self._adh = adh # calcule le prix de l'encre tout de suite try: self._base_prix_couleurs, self._nb_pages = cout.base_prix_couleurs(path_to_pdf) self._base_prix_nb, self._nb_pages = cout.base_prix_nb(path_to_pdf) except ValueError, e: raise FichierInvalide, ("PDF bugge, Analyse impossible : %s." % e, path_to_pdf) except Exception, e: raise Exception, "Erreur dans le calcul du cout : %s " % str(e) self._calcule_prix() def changeSettings(self, agraphes = None, papier = None, couleurs = None, recto_verso=None, copies=None): """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 if (couleurs): if couleurs not in COULEURS_VALEURS_POSSIBLES: raise SettingsError, "Valeur de couleurs inconnue : %s" % str(couleurs) self._settings['couleurs'] = couleurs if (papier): if papier not in PAPIER_VALEURS_POSSIBLES: raise SettingsError, "Valeur de papier inconnue : %s" % str(papier) self._settings['papier'] = papier if (copies): try: int(copies) if int(copies) <1: raise Exception except: raise SettingsError, "Valeur incorect pour le nombre de copies" self._settings['copies'] = copies if (recto_verso): if recto_verso not in DISPOSITION_VALEURS_POSSIBLES: raise SettingsError, "Valeur inconnue : %s" % str(recto_verso) if papier==PAPIER_A4_TRANSPARENT and recto_verso==IMPRESSION_RECTO_VERSO: raise SettingsError, "Pas de recto-verso sur du papier transparent !!" self._settings['recto_verso'] = recto_verso if (agraphes): if agraphes not in AGRAPHES_VALEURS_POSSIBLES: raise SettingsError, "Valeur inconnue pour agrafes : %s" % str(agrafes) if papier!=PAPIER_A4 and agraphes!=PAS_D_AGRAPHES: raise SettingsError, "Le type de papier choisi ne permet pas d'utiliser l'agrafeuse" if recto_verso==IMPRESSION_RECTO_VERSO: if self._nb_pages > 100 and agraphes!=PAS_D_AGRAPHES: raise SettingsError, "Le document est trop volumineux pour utiliser l'agrafeuse" else: if self._nb_pages > 50 and agraphes!=PAS_D_AGRAPHES: raise SettingsError, "Le document est trop volumineux pour utiliser l'agrafeuse" self._settings['agraphes'] = agraphes return self._calcule_prix() def printSettings(self): """printSettings() Affiche les maramètres courrants sur la sortie standard """ print "Type impression: " + LABELS[self._settings['couleurs']] print "Agraphes: " + LABELS[self._settings['agraphes']] print "Papier: " + LABELS[self._settings['papier']] print "Disposition: " + LABELS[self._settings['recto_verso']] 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 devisDetaille(self): """devisDetaille() renvoie un disctionnaire contenant le devis (intitulé, PU., quantité) pour tous les éléments de l'impression (papier, encre, agrafes...) """ return self._details_devis 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'adherent 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 printDevis(self): """printDevis() Affiche le devis sur la sortie standard. """ print "titre \t\t | p.u. \t quandtite \t total" for anItem in self._details_devis: print anItem[0][:5] + "\t\t | " + str(anItem[1]) + "\t\t | " + str(anItem[2]) + "\t\t | " + str(anItem[1]*anItem[2]) def _calcule_prix(self): prix = 0 facture = [] # clacul du prix total pour l'encre if (self._settings['couleurs'] == IMPRESSION_COULEUR): base_prix_encre = self._base_prix_couleurs else: base_prix_encre = self._base_prix_nb if (self._settings['papier'] == PAPIER_A3): pu_encre = base_prix_encre * 2 else: pu_encre = base_prix_encre facture.append(( LABELS[self._settings['couleurs']] + ' sur papier ' + LABELS[self._settings['papier']], pu_encre, self._settings['copies'])) prix+= self._settings['copies'] * pu_encre #calcul du prix des feuilles if (self._settings['recto_verso'] == IMPRESSION_RECTO_VERSO): nbfeuilles = int(self._nb_pages/2.+0.5) * self._settings['copies'] else: nbfeuilles = self._nb_pages * self._settings['copies'] facture.append(( 'feuilles ' + LABELS[self._settings['papier']], PU_FEUILLE[self._settings['papier']], nbfeuilles)) prix+= PU_FEUILLE[self._settings['papier']] * nbfeuilles # calcul prix des Agraphes facture.append(( 'agrafes', PU_AGRAPHE, NB_AGRAPHES[self._settings['agraphes']] * self._settings['copies'])) prix+= NB_AGRAPHES[self._settings['agraphes']] * self._settings['copies'] * PU_AGRAPHE # arrondit prix = int((prix*100) + 0.5)/100. self._prix = prix self._details_devis = facture return prix 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._adh != None): log.info('Impression [%s] : %s' % (self._adh, self._fichier)) else: log.info("Impression : %s" % self._fichier) # Envoi du fichier a CUPS options ='' # Creation de la liste d'options # pour le nombre de copies et specifie non assemblee #options += '-# %d -o Collate=True' % self.nb_copies # Pour specifier l'imprimante options += ' -P laserjet' #Indique la presence d'un bac de sortie avec agrafeuse options += " -o Option20=MBMStaplerStacker -o OutputBin=StackerStapled" # Pour specifier la version du language postscript utilise par pdftops options += ' -o pdf-level3' # Pour donner le titre de l'impression options += " -T '%s'" % self._fichier # Pour donner le login de l'adherent if (self._adh != None): options += ' -U %s' % self._adh # 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' # option agrafes options += LPR_OPTIONS[self._settings['agraphes']] # option papier options += LPR_OPTIONS[self._settings['papier']] # option disposition options += LPR_OPTIONS[self._settings['recto_verso']] # options couleurs options += LPR_OPTIONS[self._settings['couleurs']] liste_nom_fichier_pdf=(' '+escapeForShell( self._fichier ) )*self._settings['copies'] import commands #(status,rep) = commands.getstatusoutput("lpr %s %s" % (options, self.nom_fichier_pdf)) (status,rep) = commands.getstatusoutput("lpr %s %s" % (options, liste_nom_fichier_pdf)) if status != 0: log.error("erreur impression") log.error("lpr status:%d | rep: %s" % (status, rep)) raise PrintError, "lpr %s %s \n status:%d rep: %s" % (options, liste_nom_fichier_pdf, status, rep)