From 210092c2d6f980a759346511600637172c132bdf Mon Sep 17 00:00:00 2001 From: gdetrez Date: Sat, 7 Oct 2006 15:40:50 +0200 Subject: [PATCH] classe impression refaite en plus propre. darcs-hash:20061007134050-f46e9-c19b8c477905c7a3077668ca02851775b929e8bf.gz --- lib/impression/__init__.py | 367 +++++++++++++++++++++++++++++++++++++ lib/impression/cout.py | 130 +++++++++++++ lib/impression/digicode.py | 59 ++++++ 3 files changed, 556 insertions(+) create mode 100644 lib/impression/__init__.py create mode 100755 lib/impression/cout.py create mode 100644 lib/impression/digicode.py diff --git a/lib/impression/__init__.py b/lib/impression/__init__.py new file mode 100644 index 00000000..fad7b78c --- /dev/null +++ b/lib/impression/__init__.py @@ -0,0 +1,367 @@ +""" +# ############################################################# +# .. +# .... ............ ........ +# . ....... . .... .. +# . ... .. .. .. .. ..... . .. +# .. .. ....@@@. .. . ........ . +# .. . .. ..@.@@..@@. .@@@@@@@ @@@@@@. .... +# .@@@@. .@@@@. .@@@@..@@.@@..@@@..@@@..@@@@.... .... +# @@@@... .@@@.. @@ @@ .@..@@..@@...@@@. .@@@@@. .. +# .@@@.. . @@@. @@.@@..@@.@@..@@@ @@ .@@@@@@.. ..... +# ...@@@.... @@@ .@@.......... ........ ..... .. +# . ..@@@@.. . .@@@@. .. ....... . ............. +# . .. .... .. .. . ... .... +# . . .... ............. .. ... +# .. .. ... ........ ... ... +# ................................ +# +# ############################################################# +# __init__.py +# +# Classe impression +# +# Copyright (c) 2006 by www.crans.org +# ############################################################# +""" +__version__ = '1' + + +import sys, syslog, os.path +sys.path.append('/usr/scripts/gestion') +import config +#def __init__(): +# pass + +import cout +# ######################################################## # +# 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 pdf-paper=571x817 -o PageSize=A4', + PAPIER_A4_TRANSPARENT: ' -o InputSlot=Tray1 -o Media=Transparency', + PAPIER_A3: ' -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): + pass +class SoldeInsuffisant(Exception): + pass +class PrintError(Exception): + pass +class SettingsError(Exception): + pass +# ######################################################## # +# CLASSE IMPRESSION # +# ######################################################## # +# +# +class impression: + # 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): + self._fichier = path_to_pdf + self._adh = self._get_adh(adh) + # calcule le prix de l'encre tout de suite + 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) + self._calcule_prix() + + + def changeSettings(self, agraphes = None, papier = None, couleurs = None, recto_verso=None, copies=None): + # 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): + 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): + return self._prix + + def fileName(self): + return os.path.basename(self._fichier) + + def filePath(self): + return self._fichier + + + def devisDetaille(self): + return self._details_devis + + def pages(self): + return self._nb_pages + + + def imprime(self): + # debite l'adherent si adherent il y a + if (self._adh != None): + if (self._prix > (self._adh.solde() - DECOUVERT_AUTHORISE)): + raise SoldeInsuffisant + self._adh.solde(-self._prix, "impression: " + self._fichier) + self._adh.save() + # imprime le document + self._exec_imprime() + + def printDevis(self): + 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 crans_ldap + adh = crans_ldap().search('uid=' + adh, 'w')['adherent'][0] + return adh + + ## ################################# ## + ## fonction qui imprime pour de vrai ## + ## ################################# ## + ## + def _exec_imprime(self): + """ Envoie l'impression a l'imprimante avec les parametres actuels """ + # 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' + + # Pour specifier le bac sortie + options += ' -o OutputBin=Left' + + # 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.mail() + + # 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 presence d'un bac de sortie avec agrafeuse + options += " -o Option20=MBMStaplerStacker -o OutputBin=StackerDown" + + # option agrafes + options += LPR_OPTIONS[self._settings['agraphes']] + + # option papier + options += LPR_OPTIONS[self._settings['papier']] + if self._settings['papier'] == PAPIER_A4_TRANSPARENT: + options += ' -o InputSlot=Tray1 -o Media=Transparency' + if self._settings['papier'] == PAPIER_A4: + options += ' -o pdf-paper=571x817 -o PageSize=A4' + else: + options += ' -o pdf-paper=825x1166 -o InputSlot=Tray3 -o HPPaperPolicy=A3 -o PageSize=A3' + + # option disposition + options += LPR_OPTIONS[self._settings['recto_verso']] + + # options couleurs + options += LPR_OPTIONS[self._settings['couleurs']] + + + liste_nom_fichier_pdf=(' '+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: + raise PrintError, "lpr %s %s \n status:%d rep: %s" % (options, liste_nom_fichier_pdf, status, rep) + syslog.openlog("impression") + syslog.syslog("lpr status:%d | rep: %s" % (status, rep)) + + diff --git a/lib/impression/cout.py b/lib/impression/cout.py new file mode 100755 index 00000000..843bff2c --- /dev/null +++ b/lib/impression/cout.py @@ -0,0 +1,130 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-15 -*- +# ############################################################# +# .. +# .... ............ ........ +# . ....... . .... .. +# . ... .. .. .. .. ..... . .. +# .. .. ....@@@. .. . ........ . +# .. . .. ..@.@@..@@. .@@@@@@@ @@@@@@. .... +# .@@@@. .@@@@. .@@@@..@@.@@..@@@..@@@..@@@@.... .... +# @@@@... .@@@.. @@ @@ .@..@@..@@...@@@. .@@@@@. .. +# .@@@.. . @@@. @@.@@..@@.@@..@@@ @@ .@@@@@@.. ..... +# ...@@@.... @@@ .@@.......... ........ ..... .. +# . ..@@@@.. . .@@@@. .. ....... . ............. +# . .. .... .. .. . ... .... +# . . .... ............. .. ... +# .. .. ... ........ ... ... +# ................................ +# +# ############################################################# +# cout.py +# +# Fonctions pour calculer le prix d'une impression +# (ne calcul que le prix de l'encre. +# retourne le prix pour une copie en A4) +# +# Copyright (c) 2006 by www.crans.org +# ############################################################# +import sys, time, tempfile, os, commands, string, random +sys.path.append('/usr/scripts/gestion') +import config +def __init__(): + pass + +# ########################################################### # +# CONSTANTES # +# ########################################################### # +# +# +import config +COUT_UNITE_COULEUR = config.impression.c_coul +COUT_UNITE_NOIRE = config.impression.c_noir +COUT_PASSAGE_TAMBOUR_NOIR = config.impression.c_tambour_noir +COUT_PASSAGE_TAMBOUR_COULEUR = config.impression.c_tambour_coul + +# ########################################################### # +# ERREURS # +# ########################################################### # +# +# +class FichierInvalide(Exception): + pass +# ########################################################### # +# PRIX COULEURS # +# ########################################################### # +# +# Clacul le prix d'une impression couleur +# +def base_prix_couleurs(path_fichier_pdf): + # nom_rep seras le dossier dans tmp ou tous les fichier créé par + # convert seront entreposé + nom_rep = tempfile.mkdtemp(prefix='tmpimpr') + nom_png = "%s/convert.png" % nom_rep # Nom prefixe et chemin des png créé par convert + + # Convertit les pdf en png couleur + (status, rep) = commands.getstatusoutput("nice -n 5 gs -sDEVICE=png16m -r30 -dBATCH -dNOPAUSE -dSAFER -dPARANOIDSAFER -dGraphicsAlphaBits=4 -dTextAlphaBits=4 -dMaxBitmap=50000000 -sOutputFile=%s%%d -q %s" % (nom_png, path_fichier_pdf)) + if status: + raise FichierInvalide("ERREUR : Fichier invalide. Aucun png n'a ete cree.\n") + + # Récupère la liste des fichiers + list_filepng=os.listdir(nom_rep) + # Calcule le nombre de pixel de couleur + remplissage = [0, 0, 0, 0, 0] # C, M, J, N, nombre de pages + for fichier in list_filepng: + resultats = commands.getoutput("nice -n 5 /usr/scripts/impression/percentcolour %s/%s" % (nom_rep, fichier)) + l_resultats = resultats.split(":") + for i in [0, 1, 2, 3]: + remplissage[i] += float(l_resultats[i])*float(l_resultats[4]) + remplissage[4] += float(l_resultats[4]) + total_noir = remplissage[3] + total_couleur = sum(remplissage[0:3]) + faces = int(remplissage[4]) + + if total_couleur > 0: + c_total = ( (COUT_PASSAGE_TAMBOUR_COULEUR + COUT_PASSAGE_TAMBOUR_NOIR) * faces # passage dans les toners + + COUT_UNITE_NOIRE * total_noir # cout encre noire + + COUT_UNITE_COULEUR * total_couleur # cout encre couleur + ) + else: # Pas de couleur, malgre l'indication + c_total = (COUT_PASSAGE_TAMBOUR_NOIR * faces # passage dans les toners + + COUT_UNITE_NOIRE * total_noir # cout encre noire + ) + return (float(c_total)/100, faces) + + + +# ########################################################### # +# PRIX N&B # +# ########################################################### # +# +# calcul le prix d'une impression en noir et blanc +# +def base_prix_nb(path_fichier_pdf): + # nom_rep seras le dossier dans tmp ou tous les fichier créé par + # convert seront entreposé + nom_rep = tempfile.mkdtemp(prefix='tmpimpr') + nom_png = "%s/convert.png" % nom_rep # Nom prefixe et chemin des png créé par convert + + # Convertit les pdf en png + (status, rep) = commands.getstatusoutput("nice -n 5 gs -sDEVICE=pnggray -r30 -dBATCH -dNOPAUSE -dSAFER -dPARANOIDSAFER -dGraphicsAlphaBits=4 -dTextAlphaBits=4 -dMaxBitmap=50000000 -sOutputFile=%s%%d -q %s" % (nom_png, path_fichier_pdf)) + if status: + raise FichierInvalide("ERREUR : Fichier invalide. Aucun png n'a ete cree.\n") + + #récupère la liste des fichiers + list_filepng=os.listdir(nom_rep) + + remplissage = [0, 0] # Noir, nombre de pages + for fichier in list_filepng: + resultats = commands.getoutput("nice -n 5 /usr/scripts/impression/percentblack '%s/%s'" % (nom_rep, fichier)) + l_resultats = resultats.split(":") + remplissage[0] += float(l_resultats[0])*float(l_resultats[1]) + remplissage[1] += float(l_resultats[1]) + total_noir = remplissage[0] + faces = int(remplissage[1]) + + c_total = (COUT_PASSAGE_TAMBOUR_NOIR * faces # passage dans les toners + + COUT_UNITE_NOIRE * total_noir # cout encre noire + ) + + return (float(c_total)/100, faces) diff --git a/lib/impression/digicode.py b/lib/impression/digicode.py new file mode 100644 index 00000000..b6da918d --- /dev/null +++ b/lib/impression/digicode.py @@ -0,0 +1,59 @@ +# ############################################################# +# .. +# .... ............ ........ +# . ....... . .... .. +# . ... .. .. .. .. ..... . .. +# .. .. ....@@@. .. . ........ . +# .. . .. ..@.@@..@@. .@@@@@@@ @@@@@@. .... +# .@@@@. .@@@@. .@@@@..@@.@@..@@@..@@@..@@@@.... .... +# @@@@... .@@@.. @@ @@ .@..@@..@@...@@@. .@@@@@. .. +# .@@@.. . @@@. @@.@@..@@.@@..@@@ @@ .@@@@@@.. ..... +# ...@@@.... @@@ .@@.......... ........ ..... .. +# . ..@@@@.. . .@@@@. .. ....... . ............. +# . .. .... .. .. . ... .... +# . . .... ............. .. ... +# .. .. ... ........ ... ... +# ................................ +# +# ############################################################# +# digicode.py +# +# Fonctions pour controler le digicode du 4@J +# +# Copyright (c) 2006 by www.crans.org +# ############################################################# +import sys, time, tempfile, os, commands, string, random +def __init__(): + pass + +def gen_code(user_name): + """ Genere le code et l'enregistre dans /var/impression/codes pour radius """ + # Generation du code et ecriture du code + rand=random.Random() + # Graine automatique avec le temps + rand.seed() + + for i in range(1000): + # On genere un code + code = rand.randint(100000, 999999) + # Si le code est libre, on sort de la boucle + if not os.path.exists("/var/impression/codes/%d" % code): + break + + else: + # Pas de code disponible + print ("ERROR: Il n'y a pas de code disponible" ) + sys.stderr.write ("ERROR: Il n'y a pas de code disponible" ) + try: + sys.stderr.write("DEBUG: Un rapport de bug a ete automatiquement envoye.\n") + except: + sys.stderr.write("ERROR: Impossible d'envoyer le rapport de bug.\n") + sys.stderr.write("ERROR: Plus de codes disponibles.\n") + sys.stderr.write("ERROR: Penser a ouvrir a l'adherent debite...\n") + return + + # On enregistre le fichier avec le code pour numero + codefichier = open("/var/impression/codes/%d" % code, 'w') + codefichier.write("Utilisateur %s\n" % user_name) + codefichier.close() + return code