Classeur PDF des bulletins BUT courts

This commit is contained in:
Emmanuel Viennet 2023-12-06 20:04:40 +01:00
parent 78fbaf1ac8
commit 0f860f912c
6 changed files with 99 additions and 54 deletions

View File

@ -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

View File

@ -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

View File

@ -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)}

View File

@ -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

View File

@ -157,11 +157,24 @@ def formsemestre_recapcomplet(
H.append(f'<option value="{fmt}"{selected}>{label}</option>')
H.append(
f"""
</select>&nbsp;(cliquer sur un nom pour afficher son bulletin ou
</select>&nbsp;<span class="help">cliquer sur un nom pour afficher son bulletin ou
<a class="stdlink"
href="{url_for('notes.formsemestre_bulletins_pdf',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id)
}">ici avoir le classeur papier</a>)
}">ici avoir le classeur pdf</a>
"""
)
if formsemestre.formation.is_apc():
H.append(
f"""&nbsp;ou en <a class="stdlink"
href="{url_for('notes.formsemestre_bulletins_pdf',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, version="butcourt")
}">version courte BUT</a>
"""
)
H.append(
"""</span>
</form>
"""
)

View File

@ -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():
</li>
<li><a class="stdlink" href="{
url_for("notes.formation_delete", scodoc_dept=g.scodoc_dept, formation_id=formation_id
)}">Supprimer cette formation</a>
)}">Supprimer cette formation</a>
(en cas d'erreur, par exemple pour charger auparavant le référentiel de compétences)
</li>
</ul>
@ -987,7 +985,7 @@ def edit_enseignants_form(moduleimpl_id):
</p>
<p class="help">Pour changer le responsable du module, passez par la
page "<a class="stdlink" href="{
url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept,
url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept,
formsemestre_id=M["formsemestre_id"])
}">Modification du semestre</a>",
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 <a href="{
url_for("notes.moduleimpl_status",
url_for("notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id
)}">module {modimpl.module.titre_str()}</a>""",
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"""<p>Suppression impossible (effacer les notes d'abord)</p>
<p><a class="stdlink" href="{
url_for("notes.moduleimpl_status",
<p><a class="stdlink" href="{
url_for("notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept, moduleimpl_id=evaluation.moduleimpl_id)
}">retour au tableau de bord du module</a>
</p>
@ -1727,7 +1725,7 @@ def evaluation_delete(evaluation_id):
"\n".join(H)
+ f"""<p>OK, évaluation supprimée.</p>
<p><a class="stdlink" href="{
url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept,
url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept,
moduleimpl_id=evaluation.moduleimpl_id)
}">Continuer</a></p>"""
+ 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(
<a style="margin-left: 16px;" class="stdlink"
href="{
url_for("notes.formsemestre_validate_previous_ue",
scodoc_dept=g.scodoc_dept,
scodoc_dept=g.scodoc_dept,
etudid=deca.etud.id, formsemestre_id=formsemestre_id)}"
>enregistrer des UEs antérieures</a>
"""