scripts/impression/impression_canon.py
Daniel STAN f33d5aa50b [impression] on laisse tomber passoir et l'imprimante de test
Ignore-this: 70e0461148be026e09b75149b3304c1c

darcs-hash:20121021163842-28565-ed3440c28dc39a19321ef92ef072e0125b5ee8da.gz
2012-10-21 18:38:42 +02:00

553 lines
19 KiB
Python
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# #############################################################
# ..
# .... ............ ........
# . ....... . .... ..
# . ... .. .. .. .. ..... . ..
# .. .. ....@@@. .. . ........ .
# .. . .. ..@.@@..@@. .@@@@@@@ @@@@@@. ....
# .@@@@. .@@@@. .@@@@..@@.@@..@@@..@@@..@@@@.... ....
# @@@@... .@@@.. @@ @@ .@..@@..@@...@@@. .@@@@@. ..
# .@@@.. . @@@. @@.@@..@@.@@..@@@ @@ .@@@@@@.. .....
# ...@@@.... @@@ .@@.......... ........ ..... ..
# . ..@@@@.. . .@@@@. .. ....... . .............
# . .. .... .. .. . ... ....
# . . .... ............. .. ...
# .. .. ... ........ ... ...
# ................................
#
# #############################################################
# impression_canon.py
#
# Classe impression pour l'imprimante canon iR C3580i
#
# Copyright (c) 2006, 2007, 2008, 2009 by Cr@ns (http://www.crans.org)
# #############################################################
"""
Classe pour gérer l'envoie de pdf à l'imprimante.
Calcule le coût des options d'impression.
"""
__version__ = '9.11'
import sys, os.path
sys.path.append('/usr/scripts/gestion')
from config import impression as config_impression
from commands import getstatusoutput
import crans.utils.logs
from subprocess import Popen, PIPE
# ######################################################## #
# CONSTANTES #
# ######################################################## #
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_COUNT_A4 = "enterprises.1602.1.11.1.4.1.4.113"
SNMP_COUNT_A3 = "enterprises.1602.1.11.1.4.1.4.112"
SNMP_COUNT_A4c = "enterprises.1602.1.11.1.4.1.4.123"
SNMP_COUNT_A3c = "enterprises.1602.1.11.1.4.1.4.122"
SNMP_COUNT_TOT = "enterprises.1602.1.11.1.4.1.4.101"
SNMP_ETAT = "hrPrinterStatus.1"
SNMP_ERR = "hrPrinterDetectedErrorState.1"
DECOUVERT_AUTHORISE = config_impression.decouvert
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" }
AVAIL_AGRAFES = ["None", "TopLeft", "TopRight", "Left", "BottomLeft", "BottomRight", "Right"]
DICT_PAPIER = { 'A4' : "Papier A4 ordinaire",
'A3' : "Papier A3 ordinaire",
'A4tr' : "Transparent A4" }
# ######################################################## #
# 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
def _uniq_jid():
""" Alloue un jid unique """
fname = '/var/impression/jid'
## Maybe need a lock ?
try:
f = file(fname,'r+')
cur = int(f.read())+1
f.seek(0)
except (IOError,ValueError):
cur = 0
f = file(fname,'w')
f.write(str(cur))
f.close()
return cur
# ######################################################## #
# 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
_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
# Format du pdf, tout droit sorti de pdfinfo
# (les dimensions sont donc en pts)
_format = '(A4)'
_width = 595.28
_height = 841.89
# Jid unique, à définir avant l'impression
_jid = 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.
"""
# On ouvre les logs
self.log = crans.utils.logs.getFileLogger('impression')
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 et on regarde le format
pdfinfo = Popen(["pdfinfo",self._fichier],stdout=PIPE,stderr=PIPE).communicate()
if pdfinfo[1] <> '':
raise FichierInvalide(u"pdfinfo n'arrive pas a lire le fichier (il est peut-etre corrompu ou protege par un mot de passe)",path_to_pdf)
self._pages = -1
for line in pdfinfo[0].split('\n'):
if line.startswith('Pages'):
self._pages = int(line.split()[1])
elif line.startswith('Page size'):
size = line.split()
if len(size) <= 6:
self._format = "Unknown"
else:
self._format = size[6]
self._width = float(size[2])
self._height = float(size[4])
# Hack pour mieux reconnaître les formats
w = min(self._width,self._height)
h = max(self._width,self._height)
err = 100
if abs(w - 595) < err and abs(h - 842) < err:
self._format = "(A4)"
elif abs(w - 842) < err and abs(h - 1180) < err:
self._format = "(A3)"
if self._pages <= 0:
raise FichierInvalide(u"Impossible de lire le nombre de pages",path_to_pdf)
if not self._format in ['(A4)','(A3)']:
raise FichierInvalide, u"Seuls les formats A3 et A4 sont supportes"
# calcule le prix de l'encre tout de suite
self._calcule_prix()
def _pdfbook(self):
page = "pdfinfo \"%s\" | grep Pages | awk '{print $2}'" % self._fichier
(status, npage) = getstatusoutput(page)
if status != 0:
self.log.error("pdfinfo status:%d | rep: %s" % (status, npage))
raise FichierInvalide, ("pdfinfo: Impossible de trouver le nombre de page du fichier",
self._fichier)
if int(int(npage)/4*4) < int(npage):
sig=int((int(npage)/4 +1)*4)
else:
sig=int(npage)
if self._settings['papier'] == 'A3':
newfile = self._fichier[:-4] + '-a3book.pdf'
pdfbook = "pdfbook --signature %s --paper a3paper %%s --outfile %s" % (sig,newfile)
else:
newfile = self._fichier[:-4] + '-book.pdf'
pdfbook = "pdfbook --signature %s %%s --outfile %s" % (sig,newfile)
(status, rep) = getstatusoutput(pdfbook % self._fichier)
self.log.info("%s | rep: %s" % ((pdfbook % self._fichier), rep))
self._fichier = newfile
if status != 0:
self.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
elif couleur == "True":
self._settings['couleur'] = True
elif couleur == "False":
self._settings['couleur'] = False
try:
if int(kw['copies']) >= 1:
self._settings['copies'] = int(kw['copies'])
except:
pass
recto_verso = kw.get('recto_verso', None)
if recto_verso == "True": recto_verso = True
if recto_verso == "False": recto_verso = False
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 == "True": livret = True
if livret == "False": livret = False
if livret in [True, False]:
self._settings['livret'] = livret
if livret:
self._settings['portrait'] = True #Le mode paysage est géré par _pdfbook
self._settings['recto_verso'] = True
self._settings['agrafage'] = 'None'
if self._settings['papier'] == 'A4tr':
self._settings['papier'] = 'A4'
else:
self._settings['portrait'] = self._width < self._height
return self._calcule_prix()
def printSettings(self):
"""printSettings()
Affiche les paramètres courrants sur la sortie standard
"""
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._settings['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._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)
"""
self._jid = _uniq_jid()
# 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(%d): %s" % (self._jid,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)
faces = 2 * feuilles
elif self._settings['recto_verso']:
feuilles = int(faces/2.+0.5)
else:
feuilles = faces
if (self._settings['papier'] == "A3"):
c_papier = config_impression.c_a3
pages = 2*faces
else:
pages = faces
if self._settings['papier'] == "A4tr":
c_papier = config_impression.c_trans
else:
c_papier = config_impression.c_a4
if self._settings['couleur']:
c_impression = c_papier * feuilles + config_impression.c_face_couleur * pages
else:
c_impression = c_papier * feuilles + config_impression.c_face_nb * pages
# Cout des agrafes
if self._settings['agrafage'] in ["Top", "Bottom", "Left", "Right"] or self._settings['livret']:
nb_agrafes = 2
elif self._settings['agrafage'] in ["None", None]:
nb_agrafes = 0
else:
nb_agrafes = 1
if feuilles <= 50:
c_agrafes = nb_agrafes * config_impression.c_agrafe
else:
c_agrafes = 0
c_total = int(self._settings['copies'] * ( c_impression +
c_agrafes ) + 0.5) # arrondi et facture
self._prix = float(c_total)/100
return self._prix
def _get_adh(self, adh):
if type(adh) == str:
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):
self.log.info('Impression(%d) [%s] : %s' % (self._jid, self._adh, self._fichier))
else:
self.log.info("Impression(%d) : %s" % (self._jid, 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 un jobname de la forme jid:adh:nom_du_fichier
jobname = '%d:%s:%s' % (self._jid, self._adh, self._fichier.split('/')[-1].replace("\"","\\\""))
# Ce nom apparaît sur l'interface d'impression de l'imprimante:
options += " -o CNDocName=\"%s\"" %jobname
# Et dans lpq:
options += " -T \"%s\"" % jobname
# Pour donner le login de l'adherent
options += ' -U \"%s\"' % self._adh
# Pour spécifier la version du language postscript utilisé par pdftops
# options += ' -o pdf-level3'
# 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['papier'] == 'A4tr':
options += ' -o InputSlot=SideDeck -o MediaType=OHP'
options += ' -o PageSize=A4'
elif self._settings['papier'] == 'A4':
options += ' -o PageSize=A4'
else:
options += ' -o pdf-expand -o pdf-paper=841x1190 -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 -o landscape'
else:
options += ' -o sides=one-sided -o landscape'
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 Collate=StapleCollate -o StapleLocation=%s' % self._settings['agrafage']
if not self._settings['livret'] and self._settings['agrafage'] in ['None', None]:
left = self._settings['copies']
while left >= 100:
cmd = "lpr %s -# %d %s" % (options, 99, self._fichier)
left -= 99
(status, rep) = getstatusoutput(cmd)
self.log.info("printing: %s" % cmd)
if status != 0:
self.log.error("erreur impression")
self.log.error("lpr status:%d | rep: %s" % (status, rep))
raise PrintError, "%s \n status:%d rep: %s" % (cmd, status, rep)
cmd = "lpr %s -# %d %s" % (options, left, self._fichier)
(status, rep) = getstatusoutput(cmd)
self.log.info("printing: %s" % cmd)
if status != 0:
self.log.error("erreur impression")
self.log.error("lpr status:%d | rep: %s" % (status, rep))
raise PrintError, "%s \n status:%d rep: %s" % (cmd, status, rep)
else:
cmd = "lpr %s %s" % (options, self._fichier)
self.log.info("printing [%s]: %s" % (cmd, self._settings['copies']))
for i in range(self._settings['copies']):
(status, rep) = getstatusoutput(cmd)
if status != 0:
self.log.error("erreur impression")
self.log.error("lpr status:%d | rep: %s" % (status, rep))
raise PrintError, "%s \n status:%d rep: %s" % (cmd, status, rep)