457 lines
17 KiB
Python
Executable file
457 lines
17 KiB
Python
Executable file
#!/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
|
||
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"
|
||
IMPRESSION_RECTO_VERSO_SHORT = "rectoversoshort"
|
||
DISPOSITION_VALEURS_POSSIBLES = [IMPRESSION_RECTO, IMPRESSION_RECTO_VERSO, IMPRESSION_RECTO_VERSO_SHORT]
|
||
|
||
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",
|
||
IMPRESSION_RECTO_VERSO_SHORT: "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',
|
||
IMPRESSION_RECTO_VERSO_SHORT: ' -o sides=two-sided-short-edge',
|
||
}
|
||
|
||
|
||
# ######################################################## #
|
||
# ERREURS #
|
||
# ######################################################## #
|
||
#
|
||
|
||
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
|
||
"""
|
||
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 <20>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<61>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 incorecte 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 or recto_verso==IMPRESSION_RECTO_VERSO_SHORT):
|
||
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 or recto_verso==IMPRESSION_RECTO_VERSO_SHORT:
|
||
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 param<61>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<EFBFBD>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'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 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) or (self._settings['recto_verso'] == IMPRESSION_RECTO_VERSO_SHORT):
|
||
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)
|
||
|
||
|