From 0f860f912c70bc1f7193a5156389217e99d94332 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 6 Dec 2023 20:04:40 +0100 Subject: [PATCH] Classeur PDF des bulletins BUT courts --- app/but/bulletin_but_court.py | 48 ++++++++++++++++++++++--------- app/but/bulletin_but_court_pdf.py | 37 ++++++++++++++---------- app/scodoc/sco_bulletins_pdf.py | 29 ++++++++++++------- app/scodoc/sco_cache.py | 2 +- app/scodoc/sco_recapcomplet.py | 17 +++++++++-- app/views/notes.py | 20 ++++++------- 6 files changed, 99 insertions(+), 54 deletions(-) diff --git a/app/but/bulletin_but_court.py b/app/but/bulletin_but_court.py index a4b5dabd0..59f77b20b 100644 --- a/app/but/bulletin_but_court.py +++ b/app/but/bulletin_but_court.py @@ -18,7 +18,7 @@ Ces données sont des objets passés au template. - `bul: dict` : le bulletin (dict, même structure que le json publié) - `cursus: EtudCursusBUT`: infos sur le cursus BUT (niveaux validés etc) - `decision_ues: dict`: `{ acronyme_ue : { 'code' : 'ADM' }}` accès aux décisions - de jury d'UE + de jury d'UE - `ects_total` : nombre d'ECTS validées dans ce cursus - `ue_validation_by_niveau : dict` : les validations d'UE de chaque niveau du cursus """ @@ -65,6 +65,38 @@ def bulletin_but(formsemestre_id: int, etudid: int = None, fmt="html"): ) if not formsemestre.formation.is_apc(): raise ScoValueError("formation non BUT") + + args = _build_bulletin_but_infos(etud, formsemestre, fmt=fmt) + + 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 + ".pdf") + + return render_template( + "but/bulletin_court_page.j2", + datetime=datetime, + sco=ScoData(formsemestre=formsemestre, etud=etud), + time=time, + version="butcourt", + **args, + ) + + +def bulletin_but_court_pdf_frag(etud: Identite, formsemestre: FormSemestre) -> bytes: + """Le code PDF d'un bulletin BUT court, à intégrer dans un document + (pour les classeurs de tous les bulletins) + """ + args = _build_bulletin_but_infos(etud, formsemestre) + return bulletin_but_court_pdf.make_bulletin_but_court_pdf(args, stand_alone=False) + + +def _build_bulletin_but_infos( + etud: Identite, formsemestre: FormSemestre, fmt="pdf" +) -> dict: + """Réuni toutes les information pour le contenu d'un bulletin BUT court. + On indique le format ("html" ou "pdf") car il y a moins d'infos en HTML. + """ bulletins_sem = BulletinBUT(formsemestre) if fmt == "pdf": bul: dict = bulletins_sem.bulletin_etud_complet(etud) @@ -106,16 +138,4 @@ def bulletin_but(formsemestre_id: int, etudid: int = None, fmt="html"): if ue.type == UE_STANDARD and ue.acronyme in ue_acronyms ], } - 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 + ".pdf") - - return render_template( - "but/bulletin_court_page.j2", - datetime=datetime, - sco=ScoData(formsemestre=formsemestre, etud=etud), - time=time, - version="butcourt", - **args, - ) + return args diff --git a/app/but/bulletin_but_court_pdf.py b/app/but/bulletin_but_court_pdf.py index f1fd112ad..7e9e7e81d 100644 --- a/app/but/bulletin_but_court_pdf.py +++ b/app/but/bulletin_but_court_pdf.py @@ -6,7 +6,7 @@ """Génération bulletin BUT PDF synthétique en une page -On génère du PDF avec reportLab en utilisant les classes +On génère du PDF avec reportLab en utilisant les classes ScoDoc BulletinGenerator et GenTable. """ @@ -34,25 +34,32 @@ from app.scodoc.sco_preferences import SemPreferences 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, - prefs: SemPreferences = None, - title: str = "", - ue_validation_by_niveau: dict[tuple[int, str], ScolarFormSemestreValidation] = None, - ues_acronyms: list[str] = None, + args: dict, + stand_alone: bool = True, ) -> bytes: - "génère le bulletin court BUT en pdf" + """génère le bulletin court BUT en pdf. + Si stand_alone, génère un doc pdf complet (une page ici), + sinon un morceau (fragment) à intégrer dans un autre document. + + args donne toutes les infos du contenu du bulletin: + 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, + prefs: SemPreferences = None, + title: str = "", + ue_validation_by_niveau: dict[tuple[int, str], ScolarFormSemestreValidation] = None, + ues_acronyms: list[str] = None, + """ # 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(fmt="pdf") + bul_generator = BulletinGeneratorBUTCourt(**args) + bul_pdf = bul_generator.generate(fmt="pdf", stand_alone=stand_alone) finally: PDFLOCK.release() return bul_pdf diff --git a/app/scodoc/sco_bulletins_pdf.py b/app/scodoc/sco_bulletins_pdf.py index 5ccb6c9f3..658bcf50f 100644 --- a/app/scodoc/sco_bulletins_pdf.py +++ b/app/scodoc/sco_bulletins_pdf.py @@ -61,7 +61,6 @@ from flask import g, request from app import log, ScoValueError from app.models import FormSemestre, Identite - from app.scodoc import sco_cache from app.scodoc import codes_cursus from app.scodoc import sco_pdf @@ -213,23 +212,33 @@ def process_field( def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"): "Document pdf avec tous les bulletins du semestre, et filename" + from app.but import bulletin_but_court from app.scodoc import sco_bulletins - if version not in scu.BULLETINS_VERSIONS: - raise ScoValueError("version de bulletin demandée invalide") + formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) + versions = ( + scu.BULLETINS_VERSIONS_BUT + if formsemestre.formation.is_apc() + else scu.BULLETINS_VERSIONS + ) + if version not in versions: + raise ScoValueError("version de bulletin demandée invalide !") cached = sco_cache.SemBulletinsPDFCache.get(str(formsemestre_id) + "_" + version) if cached: return cached[1], cached[0] fragments = [] # Make each bulletin - formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) + for etud in formsemestre.get_inscrits(include_demdef=True, order=True): - frag, _ = sco_bulletins.do_formsemestre_bulletinetud( - formsemestre, - etud, - fmt="pdfpart", - version=version, - ) + if version == "butcourt": + frag = bulletin_but_court.bulletin_but_court_pdf_frag(etud, formsemestre) + else: + frag, _ = sco_bulletins.do_formsemestre_bulletinetud( + formsemestre, + etud, + fmt="pdfpart", + version=version, + ) fragments += frag # infos = {"DeptName": sco_preferences.get_preference("DeptName", formsemestre_id)} diff --git a/app/scodoc/sco_cache.py b/app/scodoc/sco_cache.py index 245389ba5..c80049b1d 100644 --- a/app/scodoc/sco_cache.py +++ b/app/scodoc/sco_cache.py @@ -190,7 +190,7 @@ class SemBulletinsPDFCache(ScoDocCache): @classmethod def invalidate_sems(cls, formsemestre_ids): """Clear cached pdf for all given formsemestres""" - for version in scu.BULLETINS_VERSIONS: + for version in scu.BULLETINS_VERSIONS_BUT: oids = [ str(formsemestre_id) + "_" + version for formsemestre_id in formsemestre_ids diff --git a/app/scodoc/sco_recapcomplet.py b/app/scodoc/sco_recapcomplet.py index 5560f2137..7ddcbcc9c 100644 --- a/app/scodoc/sco_recapcomplet.py +++ b/app/scodoc/sco_recapcomplet.py @@ -157,11 +157,24 @@ def formsemestre_recapcomplet( H.append(f'') H.append( f""" -  (cliquer sur un nom pour afficher son bulletin ou +  cliquer sur un nom pour afficher son bulletin ou ici avoir le classeur papier) + }">ici avoir le classeur pdf + """ + ) + if formsemestre.formation.is_apc(): + H.append( + f""" ou en version courte BUT + """ + ) + + H.append( + """ """ ) diff --git a/app/views/notes.py b/app/views/notes.py index c7a4613b0..c6f8f5482 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -271,9 +271,7 @@ sco_publish( ) -@bp.route( - "/formsemestre_bulletinetud", methods=["GET", "POST"] -) # POST pour compat anciens clients PHP (deprecated) +@bp.route("/formsemestre_bulletinetud") @scodoc @permission_required_compat_scodoc7(Permission.ScoView) @scodoc7func @@ -790,7 +788,7 @@ def formation_import_xml_form():
  • Supprimer cette formation + )}">Supprimer cette formation (en cas d'erreur, par exemple pour charger auparavant le référentiel de compétences)
  • @@ -987,7 +985,7 @@ def edit_enseignants_form(moduleimpl_id):

    Pour changer le responsable du module, passez par la page "Modification du semestre", accessible uniquement au responsable de la formation (chef de département) @@ -1200,7 +1198,7 @@ def view_module_abs(moduleimpl_id, fmt="html"): H = [ html_sco_header.html_sem_header( f"""Absences du module {modimpl.module.titre_str()}""", page_title=f"Absences du module {modimpl.module.titre_str()}", @@ -1692,8 +1690,8 @@ def evaluation_delete(evaluation_id): if etat["nb_notes"]: H.append( f"""

    Suppression impossible (effacer les notes d'abord)

    -

    retour au tableau de bord du module

    @@ -1727,7 +1725,7 @@ def evaluation_delete(evaluation_id): "\n".join(H) + f"""

    OK, évaluation supprimée.

    Continuer

    """ + html_sco_header.sco_footer() @@ -1863,8 +1861,6 @@ sco_publish( @scodoc7func def formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"): "Publie les bulletins dans un classeur PDF" - if version not in scu.BULLETINS_VERSIONS: - raise ScoValueError("version de bulletin demandée invalide") pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf( formsemestre_id, version=version ) @@ -2499,7 +2495,7 @@ def formsemestre_validation_but( enregistrer des UEs antérieures """