From f14b6a4f2bca1c186720e36989a624af6f3b88d7 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 30 Aug 2023 15:59:11 +0200 Subject: [PATCH] WIP: Bulletins BUT courts pdf --- app/but/bulletin_but_court.py | 36 ++++++--- app/but/bulletin_but_court_pdf.py | 122 ++++++++++++++++++++++++++++++ app/scodoc/sco_utils.py | 4 +- 3 files changed, 148 insertions(+), 14 deletions(-) create mode 100644 app/but/bulletin_but_court_pdf.py diff --git a/app/but/bulletin_but_court.py b/app/but/bulletin_but_court.py index 132e2655a..d0670c7b6 100644 --- a/app/but/bulletin_but_court.py +++ b/app/but/bulletin_but_court.py @@ -31,7 +31,7 @@ from flask import render_template, url_for from flask import g, request from app.but.bulletin_but import BulletinBUT -from app.but import cursus_but, validations_view +from app.but import bulletin_but_court_pdf, cursus_but, validations_view from app.decorators import ( scodoc, permission_required, @@ -40,14 +40,18 @@ from app.models import FormSemestre, FormSemestreInscription, Identite from app.scodoc.sco_exceptions import ScoNoReferentielCompetences from app.scodoc.sco_logos import find_logo from app.scodoc.sco_permissions import Permission +import app.scodoc.sco_utils as scu from app.views import notes_bp as bp from app.views import ScoData @bp.route("/bulletin_but//") +@bp.route( + "/bulletin_but///pdf", defaults={"fmt": "pdf"} +) @scodoc @permission_required(Permission.ScoView) -def bulletin_but(formsemestre_id: int, etudid: int = None): +def bulletin_but(formsemestre_id: int, etudid: int = None, fmt="html"): """Page HTML affichant le bulletin BUT simplifié""" etud: Identite = Identite.query.get_or_404(etudid) formsemestre: FormSemestre = ( @@ -70,19 +74,27 @@ def bulletin_but(formsemestre_id: int, etudid: int = None): logo = find_logo(logoname="header", dept_id=g.scodoc_dept_id) + args = { + "bul": bul, + "cursus": cursus, + "decision_ues": decision_ues, + "ects_total": ects_total, + "etud": etud, + "formsemestre": formsemestre, + "logo": logo, + "title": f"Bul. {etud.nom_disp()} BUT (court)", + "ue_validation_by_niveau": ue_validation_by_niveau, + } + + if fmt == "pdf": + filename = scu.bul_filename(formsemestre, etud, prefix="bul-but") + bul_pdf = bulletin_but_court_pdf.make_bulletin_but_court_pdf(**args) + return scu.sendPDFFile(bul_pdf, filename) + return render_template( "but/bulletin_court_page.j2", - bul=bul, - bulletins_sem=bulletins_sem, - cursus=cursus, datetime=datetime, - decision_ues=decision_ues, - ects_total=ects_total, - etud=etud, - formsemestre=formsemestre, - logo=logo, sco=ScoData(formsemestre=formsemestre, etud=etud), time=time, - title=f"Bul. {etud.nom_disp()} BUT (court)", - ue_validation_by_niveau=ue_validation_by_niveau, + **args, ) diff --git a/app/but/bulletin_but_court_pdf.py b/app/but/bulletin_but_court_pdf.py new file mode 100644 index 000000000..173630c18 --- /dev/null +++ b/app/but/bulletin_but_court_pdf.py @@ -0,0 +1,122 @@ +############################################################################## +# ScoDoc +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. +# See LICENSE +############################################################################## + +"""Génération bulletin BUT PDF synthétique en une page + +On génère du PDF avec reportLab en utilisant les classes +ScoDoc BulletinGenerator et GenTable. + +""" +from flask_login import current_user + +from reportlab.lib.colors import blue +from reportlab.lib.units import cm, mm +from reportlab.platypus import Paragraph, Spacer + +from app.but import cursus_but +from app.models import FormSemestre, Identite, ScolarFormSemestreValidation + +from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard +from app.scodoc.sco_logos import Logo +from app.scodoc import gen_tables, sco_pdf, sco_preferences +from app.scodoc.sco_pdf import PDFLOCK + + +def make_bulletin_but_court_pdf( + bul: dict = None, + cursus: cursus_but.EtudCursusBUT = None, + decision_ues: dict = None, + ects_total: float = 0.0, + etud: Identite = None, + formsemestre: FormSemestre = None, + logo: Logo = None, + title: str = "", + ue_validation_by_niveau: dict[tuple[int, str], ScolarFormSemestreValidation] = None, +) -> bytes: + # A priori ce verrou n'est plus nécessaire avec Flask (multi-process) + # mais... + try: + PDFLOCK.acquire() + bul_generator = BulletinGeneratorBUTCourt(**locals()) + bul_pdf = bul_generator.generate(format="pdf") + finally: + PDFLOCK.release() + return bul_pdf + + +class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): + """Ce générateur de bulletin BUT court est assez différent des autres bulletins. + Ne génére que du PDF. + Il reprend la mise en page et certains éléments (pied de page, signature). + """ + + # spécialisation du BulletinGeneratorStandard, ne pas présenter à l'utilisateur: + list_in_menu = False + scale_table_in_page = True # pas de mise à l'échelle pleine page auto + multi_pages = False # une page par bulletin + small_fontsize = "8" + + def __init__( + self, + bul: dict = None, + cursus: cursus_but.EtudCursusBUT = None, + decision_ues: dict = None, + ects_total: float = 0.0, + etud: Identite = None, + formsemestre: FormSemestre = None, + logo: Logo = None, + title: str = "", + ue_validation_by_niveau: dict[ + tuple[int, str], ScolarFormSemestreValidation + ] = None, + ): + # données pour anciens codes bulletins... à moderniser + infos = { + "etud": etud.to_dict_bul(), + "filigranne": None, + "formsemestre_id": formsemestre.id, + "nbabs": 0, + "nbabsjust": 0, + } + super().__init__(infos, authuser=current_user) + self.bul = bul + self.cursus = cursus + self.decision_ues = decision_ues + self.ects_total = ects_total + self.etud = etud + self.formsemestre = formsemestre + self.logo = logo + self.title = title + self.ue_validation_by_niveau = ue_validation_by_niveau + + def bul_table(self, fmt=None): + """Génère la table centrale du bulletin de notes + Renvoie: une liste d'objets PLATYPUS (eg instance de Table). + L'argument fmt est ici ignoré (toujours en PDF) + """ + ue_table = self.build_ue_table() + + return ue_table.gen(format="pdf") + + def build_ue_table(self) -> gen_tables.GenTable: + """Table avec les résultats d'UE du semestre courant""" + columns_ids = ("titre", "UE1", "UE2") + rows = [ + {"titre": "ligne 1", "UE1": "12.", "UE2": "13"}, + {"titre": "ligne 2", "UE1": "14.", "UE2": "15"}, + ] + pdf_style = [ + ("VALIGN", (0, 0), (-1, -1), "TOP"), + ("BOX", (0, 0), (-1, -1), 0.4, blue), # ajoute cadre extérieur bleu + ] + col_widths = {"titre": 3 * cm, "UE1": 1 * cm, "UE2": 1 * cm} + return gen_tables.GenTable( + rows=rows, + columns_ids=columns_ids, + pdf_table_style=pdf_style, + pdf_col_widths=[col_widths[k] for k in columns_ids], + preferences=sco_preferences.SemPreferences(self.formsemestre.id), + ) diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 4dedcffe6..9cb08ce86 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -887,10 +887,10 @@ def bul_filename_old(sem: dict, etud: dict, format): return filename -def bul_filename(formsemestre, etud): +def bul_filename(formsemestre, etud, prefix="bul"): """Build a filename for this bulletin (without suffix)""" dt = time.strftime("%Y-%m-%d") - filename = f"bul-{formsemestre.titre_num()}-{dt}-{etud.nom}" + filename = f"{prefix}-{formsemestre.titre_num()}-{dt}-{etud.nom}" filename = make_filename(filename) return filename