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 a4b5dabd07..59f77b20bd 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 f1fd112ad9..7e9e7e81de 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 5ccb6c9f34..658bcf50f7 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 245389ba5e..c80049b1d5 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 5560f21371..7ddcbcc9c9 100644
--- a/app/scodoc/sco_recapcomplet.py
+++ b/app/scodoc/sco_recapcomplet.py
@@ -157,11 +157,24 @@ def formsemestre_recapcomplet(
H.append(f'{label} ')
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 c7a4613b04..c6f8f5482a 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
"""