# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Emmanuel Viennet emmanuel.viennet@gmail.com # ############################################################################## """Génération des bulletins de note: super-classe pour les générateurs (HTML et PDF) class BulletinGenerator: description supported_formats = [ 'pdf', 'html' ] .bul_title_pdf() .bul_table(format) .bul_part_below(format) .bul_signatures_pdf() .__init__ et .generate(format) methodes appelees par le client (sco_bulletin) La préférence 'bul_class_name' donne le nom de la classe generateur. La préférence 'bul_pdf_class_name' est obsolete (inutilisée). """ import collections import io import time import traceback import reportlab from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak from reportlab.platypus import Table, TableStyle, Image, KeepInFrame from flask import request from flask_login import current_user from app.scodoc import sco_utils as scu from app.scodoc.sco_exceptions import NoteProcessError from app import log from app.scodoc import sco_formsemestre from app.scodoc import sco_pdf from app.scodoc.sco_pdf import PDFLOCK import sco_version class BulletinGenerator: "Virtual superclass for PDF bulletin generators" "" # Here some helper methods # see sco_bulletins_standard.BulletinGeneratorStandard subclass for real methods supported_formats = [] # should list supported formats, eg [ 'html', 'pdf' ] description = "superclass for bulletins" # description for user interface list_in_menu = True # la classe doit-elle est montrée dans le menu de config ? scale_table_in_page = True # rescale la table sur 1 page def __init__( self, infos, authuser=None, version="long", filigranne=None, server_name=None, ): from app.scodoc import sco_preferences if not version in scu.BULLETINS_VERSIONS: raise ValueError("invalid version code !") self.infos = infos self.authuser = authuser # nécessaire pour version HTML qui contient liens dépendant de l'utilisateur self.version = version self.filigranne = filigranne self.server_name = server_name # Store preferences for convenience: formsemestre_id = self.infos["formsemestre_id"] self.preferences = sco_preferences.SemPreferences(formsemestre_id) self.diagnostic = None # error message if any problem # Common PDF styles: # - Pour tous les champs du bulletin sauf les cellules de table: self.FieldStyle = reportlab.lib.styles.ParagraphStyle({}) self.FieldStyle.fontName = self.preferences["SCOLAR_FONT_BUL_FIELDS"] self.FieldStyle.fontSize = self.preferences["SCOLAR_FONT_SIZE"] self.FieldStyle.firstLineIndent = 0 # - Pour les cellules de table: self.CellStyle = reportlab.lib.styles.ParagraphStyle({}) self.CellStyle.fontSize = self.preferences["SCOLAR_FONT_SIZE"] self.CellStyle.fontName = self.preferences["SCOLAR_FONT"] self.CellStyle.leading = ( 1.0 * self.preferences["SCOLAR_FONT_SIZE"] ) # vertical space # Marges du document PDF self.margins = ( self.preferences["left_margin"], self.preferences["top_margin"], self.preferences["right_margin"], self.preferences["bottom_margin"], ) def get_filename(self): """Build a filename to be proposed to the web client""" sem = sco_formsemestre.get_formsemestre(self.infos["formsemestre_id"]) return scu.bul_filename_old(sem, self.infos["etud"], "pdf") def generate(self, format="", stand_alone=True): """Return bulletin in specified format""" if not format in self.supported_formats: raise ValueError("unsupported bulletin format (%s)" % format) try: PDFLOCK.acquire() # this lock is necessary since reportlab is not re-entrant if format == "html": return self.generate_html() elif format == "pdf": return self.generate_pdf(stand_alone=stand_alone) else: raise ValueError("invalid bulletin format (%s)" % format) finally: PDFLOCK.release() def generate_html(self): """Return bulletin as an HTML string""" H = ['
" + traceback.format_exc() + "" return [] return Table(Pt, colWidths=colWidths, style=pdfTableStyle) # --------------------------------------------------------------------------- def make_formsemestre_bulletinetud( infos, version=None, # short, long, selectedevals format="pdf", # html, pdf stand_alone=True, ): """Bulletin de notes Appelle une fonction générant le bulletin au format spécifié à partir des informations infos, selon les préférences du semestre. """ from app.scodoc import sco_preferences version = version or "long" if not version in scu.BULLETINS_VERSIONS: raise ValueError("invalid version code !") formsemestre_id = infos["formsemestre_id"] bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id) gen_class = None if infos.get("type") == "BUT" and format.startswith("pdf"): gen_class = bulletin_get_class(bul_class_name + "BUT") if gen_class is None: gen_class = bulletin_get_class(bul_class_name) if gen_class is None: raise ValueError( "Type de bulletin PDF invalide (paramètre: %s)" % bul_class_name ) try: PDFLOCK.acquire() bul_generator = gen_class( infos, authuser=current_user, version=version, filigranne=infos["filigranne"], server_name=request.url_root, ) if format not in bul_generator.supported_formats: # use standard generator log( "Bulletin format %s not supported by %s, using %s" % (format, bul_class_name, bulletin_default_class_name()) ) bul_class_name = bulletin_default_class_name() gen_class = bulletin_get_class(bul_class_name) bul_generator = gen_class( infos, authuser=current_user, version=version, filigranne=infos["filigranne"], server_name=request.url_root, ) data = bul_generator.generate(format=format, stand_alone=stand_alone) finally: PDFLOCK.release() if bul_generator.diagnostic: log("bul_error: %s" % bul_generator.diagnostic) raise NoteProcessError(bul_generator.diagnostic) filename = bul_generator.get_filename() return data, filename #### # Liste des types des classes de générateurs de bulletins PDF: BULLETIN_CLASSES = collections.OrderedDict() def register_bulletin_class(klass): BULLETIN_CLASSES[klass.__name__] = klass def bulletin_class_descriptions(): return [ BULLETIN_CLASSES[class_name].description for class_name in BULLETIN_CLASSES if BULLETIN_CLASSES[class_name].list_in_menu ] def bulletin_class_names() -> list[str]: "Liste les noms des classes de bulletins à présenter à l'utilisateur" return [ class_name for class_name in BULLETIN_CLASSES if BULLETIN_CLASSES[class_name].list_in_menu ] def bulletin_default_class_name(): return bulletin_class_names()[0] def bulletin_get_class(class_name: str) -> BulletinGenerator: """La class de génération de bulletin de ce nom, ou None si pas trouvée """ return BULLETIN_CLASSES.get(class_name) def bulletin_get_class_name_displayed(formsemestre_id): """Le nom du générateur utilisé, en clair""" from app.scodoc import sco_preferences bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id) gen_class = bulletin_get_class(bul_class_name) if gen_class is None: return "invalide ! (voir paramètres)" return gen_class.description