scripts/archive/impression/impression_canon.py
Pierre-Elliott Bécue bbcd49c88c Nettoyage d'hiver
2015-11-23 23:54:23 +01:00

530 lines
19 KiB
Python
Raw Permalink 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.

# -*- 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
from gestion.config import impression as config_impression
from commands import getstatusoutput
if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts')
from cranslib.utils import logs
from subprocess import Popen, PIPE
from base import FichierInvalide, SoldeInsuffisant, PrintError, SettingsError
# ######################################################## #
# 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"]
#les agrafes sur les côtés (*2) sont désactivées, car elles nécessitent des
#feuilles en A4. On alimente désormais l'imprimante exclusivement en A4R
#(plus faible probabilité de bourrer)
AVAIL_AGRAFES = ["None", "TopLeft", "TopRight", "BottomLeft", "BottomRight"]
DICT_PAPIER = { 'A4' : "Papier A4 ordinaire",
'A3' : "Papier A3 ordinaire",
#'A4tr' : "Transparent A4",
}
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 = 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), https://wiki.crans.org/VieCrans/ImpressionReseau#Format_des_fichiers",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)']:
pass
#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)
"""
# Ce fichier est rempli par le script print_status.py du même dossier
# à intervalle régulier
with open('/usr/scripts/var/print_status/error.txt', 'r') as stat:
err = stat.read()
if err:
raise PrintError('Imprimante en panne :' + err)
self._jid = _uniq_jid()
# debite l'adhérent si adherent il y a
if (self._adh != None):
adh = self._adh.split('@')
if len(adh) > 1:
adh = adh[1:]
adh = self._get_adh(adh[0])
self._calcule_prix() # Normalement inutile, mais évite les races
if (self._prix > (adh.solde() - DECOUVERT_AUTHORISE)):
raise SoldeInsuffisant
adh.solde(-self._prix, "impression(%d): %s par %s" % (self._jid,self._fichier,self._adh))
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:
from gestion.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)