UE capitalisées sur bulletins BUT PDF + code cleaning

This commit is contained in:
Emmanuel Viennet 2023-03-18 21:56:08 +01:00
parent e482e6bd3d
commit 8d453eb42b
12 changed files with 161 additions and 104 deletions

View File

@ -286,7 +286,7 @@ def bulletin(
if pdf: if pdf:
pdf_response, _ = do_formsemestre_bulletinetud( pdf_response, _ = do_formsemestre_bulletinetud(
formsemestre, formsemestre,
etud.id, etud,
version=version, version=version,
format="pdf", format="pdf",
with_img_signatures_pdf=with_img_signatures_pdf, with_img_signatures_pdf=with_img_signatures_pdf,

View File

@ -187,6 +187,8 @@ class BulletinBUT:
) )
if ue_capitalisee.formsemestre_id if ue_capitalisee.formsemestre_id
else None, else None,
"ressources": {}, # sans détail en BUT
"saes": {},
} }
if self.prefs["bul_show_ects"]: if self.prefs["bul_show_ects"]:
d[ue.acronyme]["ECTS"] = { d[ue.acronyme]["ECTS"] = {
@ -473,6 +475,7 @@ class BulletinBUT:
def bulletin_etud_complet(self, etud: Identite, version="long") -> dict: def bulletin_etud_complet(self, etud: Identite, version="long") -> dict:
"""Bulletin dict complet avec toutes les infos pour les bulletins BUT pdf """Bulletin dict complet avec toutes les infos pour les bulletins BUT pdf
(pas utilisé pour json/html)
Résultat compatible avec celui de sco_bulletins.formsemestre_bulletinetud_dict Résultat compatible avec celui de sco_bulletins.formsemestre_bulletinetud_dict
""" """
d = self.bulletin_etud( d = self.bulletin_etud(

View File

@ -5,6 +5,20 @@
############################################################################## ##############################################################################
"""Génération bulletin BUT au format PDF standard """Génération bulletin BUT au format PDF standard
La génération du bulletin PDF suit le chemin suivant:
- vue formsemestre_bulletinetud -> sco_bulletins.formsemestre_bulletinetud
bul_dict = bulletin_but.BulletinBUT(formsemestre).bulletin_etud_complet(etud)
- sco_bulletins_generator.make_formsemestre_bulletinetud(infos)
- instance de BulletinGeneratorStandardBUT(infos)
- BulletinGeneratorStandardBUT.generate(format="pdf")
sco_bulletins_generator.BulletinGenerator.generate()
.generate_pdf()
.bul_table() (ci-dessous)
""" """
from reportlab.lib.colors import blue from reportlab.lib.colors import blue
from reportlab.lib.units import cm, mm from reportlab.lib.units import cm, mm
@ -65,7 +79,9 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
return objects return objects
def but_table_synthese_ues(self, title_bg=(182, 235, 255)): def but_table_synthese_ues(
self, title_bg=(182, 235, 255), title_ue_cap_bg=(150, 207, 147)
):
"""La table de synthèse; pour chaque UE, liste des ressources et SAÉs avec leurs notes """La table de synthèse; pour chaque UE, liste des ressources et SAÉs avec leurs notes
et leurs coefs. et leurs coefs.
Renvoie: colkeys, P, pdf_style, colWidths Renvoie: colkeys, P, pdf_style, colWidths
@ -74,6 +90,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
- pdf_style : commandes table Platypus - pdf_style : commandes table Platypus
- largeurs de colonnes pour PDF - largeurs de colonnes pour PDF
""" """
# nb: self.infos a ici été donné par BulletinBUT.bulletin_etud_complet()
col_widths = { col_widths = {
"titre": None, "titre": None,
"min": 1.5 * cm, "min": 1.5 * cm,
@ -95,6 +112,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
col_keys += ["coef", "moyenne"] col_keys += ["coef", "moyenne"]
# Couleur fond: # Couleur fond:
title_bg = tuple(x / 255.0 for x in title_bg) title_bg = tuple(x / 255.0 for x in title_bg)
title_ue_cap_bg = tuple(x / 255.0 for x in title_ue_cap_bg)
# elems pour générer table avec gen_table (liste de dicts) # elems pour générer table avec gen_table (liste de dicts)
rows = [ rows = [
# Ligne de titres # Ligne de titres
@ -141,9 +159,13 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
blue, blue,
), ),
] ]
ues_capitalisees = self.infos.get("ues_capitalisees", {})
for ue_acronym, ue in self.infos["ues"].items(): for ue_acronym, ue in self.infos["ues"].items():
self.ue_rows(rows, ue_acronym, ue, title_bg) self._ue_rows(rows, ue_acronym, ue, title_bg)
if ue_acronym in ues_capitalisees:
self._ue_rows(
rows, ue_acronym, ues_capitalisees[ue_acronym], title_ue_cap_bg
)
# Global pdf style commands: # Global pdf style commands:
pdf_style = [ pdf_style = [
@ -152,20 +174,18 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
] ]
return col_keys, rows, pdf_style, col_widths return col_keys, rows, pdf_style, col_widths
def ue_rows(self, rows: list, ue_acronym: str, ue: dict, title_bg: tuple): def _ue_rows(self, rows: list, ue_acronym: str, ue: dict, title_bg: tuple):
"Décrit une UE dans la table synthèse: titre, sous-titre et liste modules" "Décrit une UE dans la table synthèse: titre, sous-titre et liste modules"
if (ue["type"] == UE_SPORT) and len(ue.get("modules", [])) == 0: if (ue["type"] == UE_SPORT) and len(ue.get("modules", [])) == 0:
# ne mentionne l'UE que s'il y a des modules # ne mentionne l'UE que s'il y a des modules
return return
# 1er ligne titre UE # 1er ligne titre UE
moy_ue = ue.get("moyenne") moy_ue = ue.get("moyenne", "-")
if isinstance(moy_ue, dict):
moy_ue = moy_ue.get("value", "-") if moy_ue is not None else "-"
t = { t = {
"titre": f"{ue_acronym} - {ue['titre']}", "titre": f"{ue_acronym} - {ue['titre']}",
"moyenne": Paragraph( "moyenne": Paragraph(f"""<para align=right><b>{moy_ue}</b></para>"""),
f"""<para align=right><b>{moy_ue.get("value", "-")
if moy_ue is not None else "-"
}</b></para>"""
),
"_css_row_class": "note_bold", "_css_row_class": "note_bold",
"_pdf_row_markup": ["b"], "_pdf_row_markup": ["b"],
"_pdf_style": [ "_pdf_style": [
@ -196,25 +216,40 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
# case Bonus/Malus/Rang "bmr" # case Bonus/Malus/Rang "bmr"
fields_bmr = [] fields_bmr = []
try: try:
value = float(ue["bonus"]) value = float(ue.get("bonus", 0.0))
if value != 0: if value != 0:
fields_bmr.append(f"Bonus: {ue['bonus']}") fields_bmr.append(f"Bonus: {ue['bonus']}")
except ValueError: except ValueError:
pass pass
try: try:
value = float(ue["malus"]) value = float(ue.get("malus", 0.0))
if value != 0: if value != 0:
fields_bmr.append(f"Malus: {ue['malus']}") fields_bmr.append(f"Malus: {ue['malus']}")
except ValueError: except ValueError:
pass pass
if self.preferences["bul_show_ue_rangs"]:
fields_bmr.append( moy_ue = ue.get("moyenne", "-")
f"Rang: {ue['moyenne']['rang']} / {ue['moyenne']['total']}" if isinstance(moy_ue, dict): # UE non capitalisées
if self.preferences["bul_show_ue_rangs"]:
fields_bmr.append(
f"Rang: {ue['moyenne']['rang']} / {ue['moyenne']['total']}"
)
ue_min, ue_max, ue_moy = (
ue["moyenne"]["min"],
ue["moyenne"]["max"],
ue["moyenne"]["moy"],
) )
else: # UE capitalisée
ue_min, ue_max, ue_moy = "", "", moy_ue
date_capitalisation = ue.get("date_capitalisation")
if date_capitalisation:
fields_bmr.append(
f"""Capitalisée le {date_capitalisation.strftime("%d/%m/%Y")}"""
)
t = { t = {
"titre": " - ".join(fields_bmr), "titre": " - ".join(fields_bmr),
"coef": ects_txt, "coef": ects_txt,
"_coef_pdf": Paragraph(f"""<para align=left>{ects_txt}</para>"""), "_coef_pdf": Paragraph(f"""<para align=right>{ects_txt}</para>"""),
"_coef_colspan": 2, "_coef_colspan": 2,
"_pdf_style": [ "_pdf_style": [
("BACKGROUND", (0, 0), (-1, 0), title_bg), ("BACKGROUND", (0, 0), (-1, 0), title_bg),
@ -222,9 +257,9 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
# ligne au dessus du bonus/malus, gris clair # ligne au dessus du bonus/malus, gris clair
("LINEABOVE", (0, 0), (-1, 0), self.PDF_LINEWIDTH, (0.7, 0.7, 0.7)), ("LINEABOVE", (0, 0), (-1, 0), self.PDF_LINEWIDTH, (0.7, 0.7, 0.7)),
], ],
"min": ue["moyenne"]["min"], "min": ue_min,
"max": ue["moyenne"]["max"], "max": ue_max,
"moy": ue["moyenne"]["moy"], "moy": ue_moy,
} }
rows.append(t) rows.append(t)

View File

@ -234,7 +234,7 @@ class ResultatsSemestreBUT(NotesTableCompat):
) )
# matrice de NaN: inscrits par défaut à AUCUNE UE: # matrice de NaN: inscrits par défaut à AUCUNE UE:
ues_inscr_parcours_df = pd.DataFrame( ues_inscr_parcours_df = pd.DataFrame(
np.nan, index=etuds_parcour_id.keys(), columns=ue_ids, dtype=float # XXX np.nan, index=etuds_parcour_id.keys(), columns=ue_ids, dtype=float
) )
# Construit pour chaque parcours du référentiel l'ensemble de ses UE # Construit pour chaque parcours du référentiel l'ensemble de ses UE
# (considère aussi le cas des semestres sans parcours: None) # (considère aussi le cas des semestres sans parcours: None)

View File

@ -6,6 +6,8 @@
import datetime import datetime
from functools import cached_property from functools import cached_property
from operator import attrgetter
from flask import abort, has_request_context, url_for from flask import abort, has_request_context, url_for
from flask import g, request from flask import g, request
import sqlalchemy import sqlalchemy
@ -155,9 +157,19 @@ class Identite(db.Model):
) )
def get_first_email(self, field="email") -> str: def get_first_email(self, field="email") -> str:
"Le mail associé à la première adrese de l'étudiant, ou None" "Le mail associé à la première adresse de l'étudiant, ou None"
return getattr(self.adresses[0], field) if self.adresses.count() > 0 else None return getattr(self.adresses[0], field) if self.adresses.count() > 0 else None
def get_formsemestres(self) -> list:
"""Liste des formsemestres dans lesquels l'étudiant est (a été) inscrit,
triée par date_debut
"""
return sorted(
[ins.formsemestre for ins in self.formsemestre_inscriptions],
key=attrgetter("date_debut"),
reverse=True,
)
def to_dict_short(self) -> dict: def to_dict_short(self) -> dict:
"""Les champs essentiels""" """Les champs essentiels"""
return { return {

View File

@ -82,11 +82,11 @@ def get_formsemestre_bulletin_etud_json(
) -> str: ) -> str:
"""Le JSON du bulletin d'un étudiant, quel que soit le type de formation.""" """Le JSON du bulletin d'un étudiant, quel que soit le type de formation."""
if formsemestre.formation.is_apc(): if formsemestre.formation.is_apc():
bul = bulletin_but.BulletinBUT(formsemestre) bulletins_sem = bulletin_but.BulletinBUT(formsemestre)
if not etud.id in bul.res.identdict: if not etud.id in bulletins_sem.res.identdict:
return json_error(404, "get_formsemestre_bulletin_etud_json: invalid etud") return json_error(404, "get_formsemestre_bulletin_etud_json: invalid etud")
return jsonify( return jsonify(
bul.bulletin_etud( bulletins_sem.bulletin_etud(
etud, etud,
formsemestre, formsemestre,
force_publishing=force_publishing, force_publishing=force_publishing,
@ -746,7 +746,10 @@ def etud_descr_situation_semestre(
infos["refcomp_specialite_long"] = "" infos["refcomp_specialite_long"] = ""
if formsemestre.formation.is_apc(): if formsemestre.formation.is_apc():
res: ResultatsSemestreBUT = res_sem.load_formsemestre_results(formsemestre) res: ResultatsSemestreBUT = res_sem.load_formsemestre_results(formsemestre)
parcour: ApcParcours = ApcParcours.query.get(res.etuds_parcour_id[etudid]) parcour_id = res.etuds_parcour_id[etudid]
parcour: ApcParcours = (
ApcParcours.query.get(parcour_id) if parcour_id is not None else None
)
if parcour: if parcour:
infos["parcours_titre"] = parcour.libelle or "" infos["parcours_titre"] = parcour.libelle or ""
infos["parcours_code"] = parcour.code or "" infos["parcours_code"] = parcour.code or ""
@ -930,13 +933,14 @@ def formsemestre_bulletinetud(
bulletin = do_formsemestre_bulletinetud( bulletin = do_formsemestre_bulletinetud(
formsemestre, formsemestre,
etud.id, etud,
format=format, format=format,
version=version, version=version,
xml_with_decisions=xml_with_decisions, xml_with_decisions=xml_with_decisions,
force_publishing=force_publishing, force_publishing=force_publishing,
prefer_mail_perso=prefer_mail_perso, prefer_mail_perso=prefer_mail_perso,
)[0] )[0]
if format not in {"html", "pdfmail"}: if format not in {"html", "pdfmail"}:
filename = scu.bul_filename(formsemestre, etud, format) filename = scu.bul_filename(formsemestre, etud, format)
mime, suffix = scu.get_mime_suffix(format) mime, suffix = scu.get_mime_suffix(format)
@ -973,7 +977,7 @@ def can_send_bulletin_by_mail(formsemestre_id):
def do_formsemestre_bulletinetud( def do_formsemestre_bulletinetud(
formsemestre: FormSemestre, formsemestre: FormSemestre,
etudid: int, etud: Identite,
version="long", # short, long, selectedevals version="long", # short, long, selectedevals
format=None, format=None,
xml_with_decisions: bool = False, xml_with_decisions: bool = False,
@ -1001,7 +1005,7 @@ def do_formsemestre_bulletinetud(
if format == "xml": if format == "xml":
bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud( bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud(
formsemestre.id, formsemestre.id,
etudid, etud.id,
xml_with_decisions=xml_with_decisions, xml_with_decisions=xml_with_decisions,
force_publishing=force_publishing, force_publishing=force_publishing,
version=version, version=version,
@ -1012,7 +1016,7 @@ def do_formsemestre_bulletinetud(
elif format == "json": # utilisé pour classic et "oldjson" elif format == "json": # utilisé pour classic et "oldjson"
bul = sco_bulletins_json.make_json_formsemestre_bulletinetud( bul = sco_bulletins_json.make_json_formsemestre_bulletinetud(
formsemestre.id, formsemestre.id,
etudid, etud.id,
xml_with_decisions=xml_with_decisions, xml_with_decisions=xml_with_decisions,
force_publishing=force_publishing, force_publishing=force_publishing,
version=version, version=version,
@ -1022,22 +1026,20 @@ def do_formsemestre_bulletinetud(
version = version[:-4] # enlève le "_mat" version = version[:-4] # enlève le "_mat"
if formsemestre.formation.is_apc(): if formsemestre.formation.is_apc():
etudiant = Identite.query.get(etudid) bulletins_sem = bulletin_but.BulletinBUT(formsemestre)
r = bulletin_but.BulletinBUT(formsemestre) bul_dict = bulletins_sem.bulletin_etud_complet(etud, version=version)
infos = r.bulletin_etud_complet(etudiant, version=version)
else: else:
infos = formsemestre_bulletinetud_dict(formsemestre.id, etudid) bul_dict = formsemestre_bulletinetud_dict(formsemestre.id, etud.id)
etud = infos["etud"]
if format == "html": if format == "html":
htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud( htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
infos, version=version, format="html" bul_dict, version=version, format="html"
) )
return htm, infos["filigranne"] return htm, bul_dict["filigranne"]
elif format == "pdf" or format == "pdfpart": elif format == "pdf" or format == "pdfpart":
bul, filename = sco_bulletins_generator.make_formsemestre_bulletinetud( bul, filename = sco_bulletins_generator.make_formsemestre_bulletinetud(
infos, bul_dict,
version=version, version=version,
format="pdf", format="pdf",
stand_alone=(format != "pdfpart"), stand_alone=(format != "pdfpart"),
@ -1046,10 +1048,10 @@ def do_formsemestre_bulletinetud(
if format == "pdf": if format == "pdf":
return ( return (
scu.sendPDFFile(bul, filename), scu.sendPDFFile(bul, filename),
infos["filigranne"], bul_dict["filigranne"],
) # unused ret. value ) # unused ret. value
else: else:
return bul, infos["filigranne"] return bul, bul_dict["filigranne"]
elif format == "pdfmail": elif format == "pdfmail":
# format pdfmail: envoie le pdf par mail a l'etud, et affiche le html # format pdfmail: envoie le pdf par mail a l'etud, et affiche le html
@ -1058,24 +1060,28 @@ def do_formsemestre_bulletinetud(
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
pdfdata, filename = sco_bulletins_generator.make_formsemestre_bulletinetud( pdfdata, filename = sco_bulletins_generator.make_formsemestre_bulletinetud(
infos, version=version, format="pdf" bul_dict, version=version, format="pdf"
) )
if prefer_mail_perso: if prefer_mail_perso:
recipient_addr = etud.get("emailperso", "") or etud.get("email", "") recipient_addr = (
etud.get_first_email("emailperso") or etud.get_first_email()
)
else: else:
recipient_addr = etud.get("email", "") or etud.get("emailperso", "") recipient_addr = etud.get_first_email() or etud.get_first_email(
"emailperso"
)
if not recipient_addr: if not recipient_addr:
flash(f"{etud['nomprenom']} n'a pas d'adresse e-mail !") flash(f"{etud.nomprenom} n'a pas d'adresse e-mail !")
return False, infos["filigranne"] return False, bul_dict["filigranne"]
else: else:
mail_bulletin(formsemestre.id, infos, pdfdata, filename, recipient_addr) mail_bulletin(formsemestre.id, bul_dict, pdfdata, filename, recipient_addr)
flash(f"mail envoyé à {recipient_addr}") flash(f"mail envoyé à {recipient_addr}")
return True, infos["filigranne"] return True, bul_dict["filigranne"]
raise ValueError("do_formsemestre_bulletinetud: invalid format (%s)" % format) raise ValueError(f"do_formsemestre_bulletinetud: invalid format ({format})")
def mail_bulletin(formsemestre_id, infos, pdfdata, filename, recipient_addr): def mail_bulletin(formsemestre_id, infos, pdfdata, filename, recipient_addr):

View File

@ -83,7 +83,7 @@ class BulletinGenerator:
def __init__( def __init__(
self, self,
infos, bul_dict,
authuser=None, authuser=None,
version="long", version="long",
filigranne=None, filigranne=None,
@ -92,16 +92,18 @@ class BulletinGenerator:
): ):
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
if not version in scu.BULLETINS_VERSIONS: if version not in scu.BULLETINS_VERSIONS:
raise ValueError("invalid version code !") raise ValueError("invalid version code !")
self.infos = infos self.bul_dict = bul_dict
self.authuser = authuser # nécessaire pour version HTML qui contient liens dépendant de l'utilisateur self.infos = bul_dict # legacy code compat
# authuser nécessaire pour version HTML qui contient liens dépendants de l'utilisateur
self.authuser = authuser
self.version = version self.version = version
self.filigranne = filigranne self.filigranne = filigranne
self.server_name = server_name self.server_name = server_name
self.with_img_signatures_pdf = with_img_signatures_pdf self.with_img_signatures_pdf = with_img_signatures_pdf
# Store preferences for convenience: # Store preferences for convenience:
formsemestre_id = self.infos["formsemestre_id"] formsemestre_id = self.bul_dict["formsemestre_id"]
self.preferences = sco_preferences.SemPreferences(formsemestre_id) self.preferences = sco_preferences.SemPreferences(formsemestre_id)
self.diagnostic = None # error message if any problem self.diagnostic = None # error message if any problem
# Common PDF styles: # Common PDF styles:
@ -127,13 +129,13 @@ class BulletinGenerator:
def get_filename(self): def get_filename(self):
"""Build a filename to be proposed to the web client""" """Build a filename to be proposed to the web client"""
sem = sco_formsemestre.get_formsemestre(self.infos["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(self.bul_dict["formsemestre_id"])
return scu.bul_filename_old(sem, self.infos["etud"], "pdf") return scu.bul_filename_old(sem, self.bul_dict["etud"], "pdf")
def generate(self, format="", stand_alone=True): def generate(self, format="", stand_alone=True):
"""Return bulletin in specified format""" """Return bulletin in specified format"""
if not format in self.supported_formats: if not format in self.supported_formats:
raise ValueError("unsupported bulletin format (%s)" % format) raise ValueError(f"unsupported bulletin format ({format})")
try: try:
PDFLOCK.acquire() # this lock is necessary since reportlab is not re-entrant PDFLOCK.acquire() # this lock is necessary since reportlab is not re-entrant
if format == "html": if format == "html":
@ -141,7 +143,7 @@ class BulletinGenerator:
elif format == "pdf": elif format == "pdf":
return self.generate_pdf(stand_alone=stand_alone) return self.generate_pdf(stand_alone=stand_alone)
else: else:
raise ValueError("invalid bulletin format (%s)" % format) raise ValueError(f"invalid bulletin format ({format})")
finally: finally:
PDFLOCK.release() PDFLOCK.release()
@ -163,11 +165,12 @@ class BulletinGenerator:
""" """
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
formsemestre_id = self.infos["formsemestre_id"] formsemestre_id = self.bul_dict["formsemestre_id"]
nomprenom = self.bul_dict["etud"]["nomprenom"]
marque_debut_bulletin = sco_pdf.DebutBulletin( marque_debut_bulletin = sco_pdf.DebutBulletin(
self.infos["etud"]["nomprenom"], nomprenom,
filigranne=self.infos["filigranne"], filigranne=self.bul_dict["filigranne"],
footer_content=f"""ScoDoc - Bulletin de {self.infos["etud"]["nomprenom"]} - {time.strftime("%d/%m/%Y %H:%M")}""", footer_content=f"""ScoDoc - Bulletin de {nomprenom} - {time.strftime("%d/%m/%Y %H:%M")}""",
) )
story = [] story = []
# partie haute du bulletin # partie haute du bulletin
@ -208,8 +211,7 @@ class BulletinGenerator:
document, document,
author="%s %s (E. Viennet) [%s]" author="%s %s (E. Viennet) [%s]"
% (sco_version.SCONAME, sco_version.SCOVERSION, self.description), % (sco_version.SCONAME, sco_version.SCOVERSION, self.description),
title="Bulletin %s de %s" title=f"""Bulletin {sem["titremois"]} de {nomprenom}""",
% (sem["titremois"], self.infos["etud"]["nomprenom"]),
subject="Bulletin de note", subject="Bulletin de note",
margins=self.margins, margins=self.margins,
server_name=self.server_name, server_name=self.server_name,
@ -247,7 +249,7 @@ class BulletinGenerator:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def make_formsemestre_bulletinetud( def make_formsemestre_bulletinetud(
infos, bul_dict,
version=None, # short, long, selectedevals version=None, # short, long, selectedevals
format="pdf", # html, pdf format="pdf", # html, pdf
stand_alone=True, stand_alone=True,
@ -262,10 +264,10 @@ def make_formsemestre_bulletinetud(
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
version = version or "long" version = version or "long"
if not version in scu.BULLETINS_VERSIONS: if version not in scu.BULLETINS_VERSIONS:
raise ValueError("invalid version code !") raise ValueError("invalid version code !")
formsemestre_id = infos["formsemestre_id"] formsemestre_id = bul_dict["formsemestre_id"]
bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id) bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id)
gen_class = None gen_class = None
@ -274,7 +276,7 @@ def make_formsemestre_bulletinetud(
# si pas trouvé (modifs locales bizarres ,), ré-essaye avec la valeur par défaut # si pas trouvé (modifs locales bizarres ,), ré-essaye avec la valeur par défaut
bulletin_default_class_name(), bulletin_default_class_name(),
): ):
if infos.get("type") == "BUT" and format.startswith("pdf"): if bul_dict.get("type") == "BUT" and format.startswith("pdf"):
gen_class = bulletin_get_class(bul_class_name + "BUT") gen_class = bulletin_get_class(bul_class_name + "BUT")
if gen_class is None: if gen_class is None:
gen_class = bulletin_get_class(bul_class_name) gen_class = bulletin_get_class(bul_class_name)
@ -285,10 +287,10 @@ def make_formsemestre_bulletinetud(
try: try:
PDFLOCK.acquire() PDFLOCK.acquire()
bul_generator = gen_class( bul_generator = gen_class(
infos, bul_dict,
authuser=current_user, authuser=current_user,
version=version, version=version,
filigranne=infos["filigranne"], filigranne=bul_dict["filigranne"],
server_name=request.url_root, server_name=request.url_root,
with_img_signatures_pdf=with_img_signatures_pdf, with_img_signatures_pdf=with_img_signatures_pdf,
) )
@ -301,24 +303,22 @@ def make_formsemestre_bulletinetud(
bul_class_name = bulletin_default_class_name() bul_class_name = bulletin_default_class_name()
gen_class = bulletin_get_class(bul_class_name) gen_class = bulletin_get_class(bul_class_name)
bul_generator = gen_class( bul_generator = gen_class(
infos, bul_dict,
authuser=current_user, authuser=current_user,
version=version, version=version,
filigranne=infos["filigranne"], filigranne=bul_dict["filigranne"],
server_name=request.url_root, server_name=request.url_root,
with_img_signatures_pdf=with_img_signatures_pdf, with_img_signatures_pdf=with_img_signatures_pdf,
) )
data = bul_generator.generate(format=format, stand_alone=stand_alone) data = bul_generator.generate(format=format, stand_alone=stand_alone)
finally: finally:
PDFLOCK.release() PDFLOCK.release()
if bul_generator.diagnostic: if bul_generator.diagnostic:
log("bul_error: %s" % bul_generator.diagnostic) log(f"bul_error: {bul_generator.diagnostic}")
raise NoteProcessError(bul_generator.diagnostic) raise NoteProcessError(bul_generator.diagnostic)
filename = bul_generator.get_filename() filename = bul_generator.get_filename()
return data, filename return data, filename

View File

@ -45,7 +45,7 @@ Pour définir un nouveau type de bulletin:
(s'inspirer de sco_bulletins_pdf_default); (s'inspirer de sco_bulletins_pdf_default);
- en fin du fichier sco_bulletins_pdf.py, ajouter la ligne - en fin du fichier sco_bulletins_pdf.py, ajouter la ligne
import sco_bulletins_pdf_xxxx import sco_bulletins_pdf_xxxx
- votre type sera alors (après redémarrage de ScoDoc) proposé dans le formulaire de paramètrage ScoDoc. - votre type sera alors (après redémarrage de ScoDoc) proposé dans le formulaire de paramètrage.
Chaque semestre peut si nécessaire utiliser un type de bulletin différent. Chaque semestre peut si nécessaire utiliser un type de bulletin différent.
@ -60,13 +60,12 @@ import traceback
from flask import g, request from flask import g, request
from app import log, ScoValueError from app import log, ScoValueError
from app.models import FormSemestre from app.models import FormSemestre, Identite
from app.scodoc import sco_cache from app.scodoc import sco_cache
from app.scodoc import codes_cursus from app.scodoc import codes_cursus
from app.scodoc import sco_pdf from app.scodoc import sco_pdf
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_etud
from app.scodoc.sco_logos import find_logo from app.scodoc.sco_logos import find_logo
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
@ -97,8 +96,8 @@ def assemble_bulletins_pdf(
document.addPageTemplates( document.addPageTemplates(
sco_pdf.ScoDocPageTemplate( sco_pdf.ScoDocPageTemplate(
document, document,
author="%s %s (E. Viennet)" % (sco_version.SCONAME, sco_version.SCOVERSION), author=f"{sco_version.SCONAME} {sco_version.SCOVERSION} (E. Viennet)",
title="Bulletin %s" % bul_title, title=f"Bulletin {bul_title}",
subject="Bulletin de note", subject="Bulletin de note",
server_name=server_name, server_name=server_name,
margins=margins, margins=margins,
@ -125,11 +124,13 @@ def replacement_function(match):
class WrapDict(object): class WrapDict(object):
"""Wrap a dict so that getitem returns '' when values are None""" """Wrap a dict so that getitem returns '' when values are None
and non existent keys returns an error message as value.
"""
def __init__(self, adict, NoneValue=""): def __init__(self, adict, none_value=""):
self.dict = adict self.dict = adict
self.NoneValue = NoneValue self.none_value = none_value
def __getitem__(self, key): def __getitem__(self, key):
try: try:
@ -137,12 +138,11 @@ class WrapDict(object):
except KeyError: except KeyError:
return f"XXX {key} invalide XXX" return f"XXX {key} invalide XXX"
if value is None: if value is None:
return self.NoneValue return self.none_value
else: return value
return value
def process_field(field, cdict, style, suppress_empty_pars=False, format="pdf"): def process_field(field, cdict, style, suppress_empty_pars=False, fmt="pdf"):
"""Process a field given in preferences, returns """Process a field given in preferences, returns
- if format = 'pdf': a list of Platypus objects - if format = 'pdf': a list of Platypus objects
- if format = 'html' : a string - if format = 'html' : a string
@ -183,7 +183,7 @@ def process_field(field, cdict, style, suppress_empty_pars=False, format="pdf"):
) )
# remove unhandled or dangerous tags: # remove unhandled or dangerous tags:
text = re.sub(r"<\s*img", "", text) text = re.sub(r"<\s*img", "", text)
if format == "html": if fmt == "html":
# convert <para> # convert <para>
text = re.sub(r"<\s*para(\s*)(.*?)>", r"<p>", text) text = re.sub(r"<\s*para(\s*)(.*?)>", r"<p>", text)
return text return text
@ -219,7 +219,7 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
for etud in formsemestre.get_inscrits(include_demdef=True, order=True): for etud in formsemestre.get_inscrits(include_demdef=True, order=True):
frag, _ = sco_bulletins.do_formsemestre_bulletinetud( frag, _ = sco_bulletins.do_formsemestre_bulletinetud(
formsemestre, formsemestre,
etud.id, etud,
format="pdfpart", format="pdfpart",
version=version, version=version,
) )
@ -256,22 +256,21 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"):
"Bulletins pdf de tous les semestres de l'étudiant, et filename" "Bulletins pdf de tous les semestres de l'étudiant, et filename"
from app.scodoc import sco_bulletins from app.scodoc import sco_bulletins
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] etud: Identite = Identite.query.get_or_404(etudid)
fragments = [] fragments = []
bookmarks = {} bookmarks = {}
filigrannes = {} filigrannes = {}
i = 1 i = 1
for sem in etud["sems"]: for formsemestre in etud.get_formsemestres():
formsemestre = FormSemestre.query.get(sem["formsemestre_id"])
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud( frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
formsemestre, formsemestre,
etudid, etud,
format="pdfpart", format="pdfpart",
version=version, version=version,
) )
fragments += frag fragments += frag
filigrannes[i] = filigranne filigrannes[i] = filigranne
bookmarks[i] = sem["session_id"] # eg RT-DUT-FI-S1-2015 bookmarks[i] = formsemestre.session_id() # eg RT-DUT-FI-S1-2015
i = i + 1 i = i + 1
infos = {"DeptName": sco_preferences.get_preference("DeptName")} infos = {"DeptName": sco_preferences.get_preference("DeptName")}
if request: if request:
@ -283,7 +282,7 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"):
pdfdoc = assemble_bulletins_pdf( pdfdoc = assemble_bulletins_pdf(
None, None,
fragments, fragments,
etud["nomprenom"], etud.nomprenom,
infos, infos,
bookmarks, bookmarks,
filigranne=filigrannes, filigranne=filigrannes,
@ -292,7 +291,7 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"):
finally: finally:
sco_pdf.PDFLOCK.release() sco_pdf.PDFLOCK.release()
# #
filename = "bul-%s" % (etud["nomprenom"]) filename = f"bul-{etud.nomprenom}"
filename = ( filename = (
scu.unescape_html(filename).replace(" ", "_").replace("&", "").replace(".", "") scu.unescape_html(filename).replace(" ", "_").replace("&", "").replace(".", "")
+ ".pdf" + ".pdf"

View File

@ -186,13 +186,13 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
self.preferences["bul_pdf_caption"], self.preferences["bul_pdf_caption"],
self.infos, self.infos,
self.FieldStyle, self.FieldStyle,
format="pdf", fmt="pdf",
) )
field = sco_bulletins_pdf.process_field( field = sco_bulletins_pdf.process_field(
self.preferences["bul_pdf_caption"], self.preferences["bul_pdf_caption"],
self.infos, self.infos,
self.FieldStyle, self.FieldStyle,
format="html", fmt="html",
) )
H.append('<div class="bul_decision">' + field + "</div>") H.append('<div class="bul_decision">' + field + "</div>")

View File

@ -939,7 +939,7 @@ def fill_etuds_info(etuds: list[dict], add_admission=True):
def etud_inscriptions_infos(etudid: int, ne="") -> dict: def etud_inscriptions_infos(etudid: int, ne="") -> dict:
"""Dict avec les informations sur les semestres passés et courant. """Dict avec les informations sur les semestres passés et courant.
{ {
"sems" : , "sems" : , # trie les semestres par date de debut, le plus recent d'abord
"ins" : , "ins" : ,
"cursem" : , "cursem" : ,
"inscription" : , "inscription" : ,

View File

@ -397,8 +397,8 @@ def gen_formsemestre_recapcomplet_json(
etudid = t[-1] etudid = t[-1]
if is_apc: if is_apc:
etud = Identite.query.get(etudid) etud = Identite.query.get(etudid)
r = bulletin_but.BulletinBUT(formsemestre) bulletins_sem = bulletin_but.BulletinBUT(formsemestre)
bul = r.bulletin_etud(etud, formsemestre) bul = bulletins_sem.bulletin_etud(etud, formsemestre)
else: else:
bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict( bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict(
formsemestre_id, formsemestre_id,

View File

@ -335,7 +335,8 @@ def formsemestre_bulletinetud(
if format == "oldjson": if format == "oldjson":
format = "json" format = "json"
r = sco_bulletins.formsemestre_bulletinetud(
response = sco_bulletins.formsemestre_bulletinetud(
etud, etud,
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
format=format, format=format,
@ -344,7 +345,8 @@ def formsemestre_bulletinetud(
force_publishing=force_publishing, force_publishing=force_publishing,
prefer_mail_perso=prefer_mail_perso, prefer_mail_perso=prefer_mail_perso,
) )
if format == "pdfmail":
if format == "pdfmail": # ne renvoie rien dans ce cas (mails envoyés)
return redirect( return redirect(
url_for( url_for(
"notes.formsemestre_bulletinetud", "notes.formsemestre_bulletinetud",
@ -353,7 +355,7 @@ def formsemestre_bulletinetud(
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
) )
) )
return r return response
sco_publish( sco_publish(
@ -2074,7 +2076,7 @@ def formsemestre_bulletins_mailetuds(
for inscription in inscriptions: for inscription in inscriptions:
sent, _ = sco_bulletins.do_formsemestre_bulletinetud( sent, _ = sco_bulletins.do_formsemestre_bulletinetud(
formsemestre, formsemestre,
inscription.etudid, inscription.etud,
version=version, version=version,
prefer_mail_perso=prefer_mail_perso, prefer_mail_perso=prefer_mail_perso,
format="pdfmail", format="pdfmail",