diff --git a/app/but/jury_but.py b/app/but/jury_but.py
index e4bf39aa..c2143019 100644
--- a/app/but/jury_but.py
+++ b/app/but/jury_but.py
@@ -242,7 +242,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
# Si on part d'un semestre IMPAIR, il n'y aura pas de décision année proposée
# (mais on pourra évidemment valider des UE et même des RCUE)
self.jury_annuel: bool = formsemestre.semestre_id in (2, 4, 6)
- "vrai si jury de fin d'année scolaire (propose code annuel)"
+ "vrai si jury de fin d'année scolaire (sem. pair, propose code annuel)"
self.formsemestre_impair = formsemestre_impair
"le 1er semestre du groupement (S1, S3, S5)"
@@ -634,6 +634,17 @@ class DecisionsProposeesAnnee(DecisionsProposees):
d[dec_rcue.rcue.ue_2.id] = dec_rcue
return d
+ def formsemestre_ects(self) -> float:
+ "ECTS validés dans le formsemestre de départ du deca"
+ ues = self.ues_impair if self.formsemestre.semestre_id % 2 else self.ues_pair
+ return sum(
+ [
+ self.decisions_ues[ue.id].ects_acquis()
+ for ue in ues
+ if ue.id in self.decisions_ues
+ ]
+ )
+
def next_semestre_ids(self, code: str) -> set[int]:
"""Les indices des semestres dans lequels l'étudiant est autorisé
à poursuivre après le semestre courant.
@@ -1331,6 +1342,14 @@ class DecisionsProposeesUE(DecisionsProposees):
return f"{self.ue.acronyme}"
return ""
+ def ects_acquis(self) -> float:
+ """ECTS enregistrés pour cette UE
+ (0 si pas de validation enregistrée)
+ """
+ if self.validation and self.code_valide in sco_codes.CODES_UE_VALIDES:
+ return self.ue.ects
+ return 0.0
+
class BUTCursusEtud: # WIP TODO
"""Validation du cursus d'un étudiant"""
diff --git a/app/but/jury_but_pv.py b/app/but/jury_but_pv.py
index 208d1ef2..c01d4c53 100644
--- a/app/but/jury_but_pv.py
+++ b/app/but/jury_but_pv.py
@@ -38,7 +38,7 @@ def _descr_cursus_but(etud: Identite) -> str:
return ", ".join(f"S{indice}" for indice in indices)
-def pvjury_table_but(formsemestre_id: int, format="html"):
+def pvjury_page_but(formsemestre_id: int, fmt="html"):
"""Page récapitulant les décisions de jury BUT
formsemestre peut être pair ou impair
"""
@@ -46,15 +46,62 @@ def pvjury_table_but(formsemestre_id: int, format="html"):
assert formsemestre.formation.is_apc()
title = "Procès-verbal de jury BUT annuel"
- if format == "html":
+ if fmt == "html":
line_sep = "
"
else:
line_sep = "\n"
+ rows, titles = pvjury_table_but(formsemestre, line_sep=line_sep)
+
+ # Style excel... passages à la ligne sur \n
+ xls_style_base = sco_excel.excel_make_style()
+ xls_style_base["alignment"] = Alignment(wrapText=True, vertical="top")
+
+ tab = GenTable(
+ base_url=f"{request.base_url}?formsemestre_id={formsemestre_id}",
+ caption=title,
+ columns_ids=titles.keys(),
+ html_caption=title,
+ html_class="pvjury_table_but table_leftalign",
+ html_title=f"""
+
+ """,
+ html_with_td_classes=True,
+ origin=f"Généré par {scu.sco_version.SCONAME} le {scu.timedate_human_repr()}",
+ page_title=title,
+ pdf_title=title,
+ preferences=sco_preferences.SemPreferences(),
+ rows=rows,
+ table_id="formation_table_recap",
+ titles=titles,
+ xls_columns_width={
+ "nom": 32,
+ "cursus": 12,
+ "ues": 32,
+ "niveaux": 32,
+ "decision_but": 14,
+ "diplome": 17,
+ "devenir": 8,
+ "observations": 12,
+ },
+ xls_style_base=xls_style_base,
+ )
+ return tab.make_page(format=fmt, javascripts=["js/etud_info.js"], init_qtip=True)
+
+
+def pvjury_table_but(
+ formsemestre: FormSemestre, line_sep: str = "\n"
+) -> tuple[list[dict], dict]:
+ "table avec résultats jury BUT pour PV"
# remplace pour le BUT la fonction sco_pvjury.pvjury_table
annee_but = (formsemestre.semestre_id + 1) // 2
titles = {
"nom": "Nom",
"cursus": "Cursus",
+ "ects": "ECTS",
"ues": "UE validées",
"niveaux": "Niveaux de compétences validés",
"decision_but": f"Décision BUT{annee_but}",
@@ -85,6 +132,7 @@ def pvjury_table_but(formsemestre_id: int, format="html"):
etudid=etud.id,
),
"cursus": _descr_cursus_but(etud),
+ "ects": f"{deca.formsemestre_ects():g}",
"ues": deca.descr_ues_validation(line_sep=line_sep) if deca else "-",
"niveaux": deca.descr_niveaux_validation(line_sep=line_sep)
if deca
@@ -98,42 +146,4 @@ def pvjury_table_but(formsemestre_id: int, format="html"):
rows.append(row)
rows.sort(key=lambda x: x["_nom_order"])
-
- # Style excel... passages à la ligne sur \n
- xls_style_base = sco_excel.excel_make_style()
- xls_style_base["alignment"] = Alignment(wrapText=True, vertical="top")
-
- tab = GenTable(
- base_url=f"{request.base_url}?formsemestre_id={formsemestre_id}",
- caption=title,
- columns_ids=titles.keys(),
- html_caption=title,
- html_class="pvjury_table_but table_leftalign",
- html_title=f"""
-
- """,
- html_with_td_classes=True,
- origin=f"Généré par {scu.sco_version.SCONAME} le {scu.timedate_human_repr()}",
- page_title=title,
- pdf_title=title,
- preferences=sco_preferences.SemPreferences(),
- rows=rows,
- table_id="formation_table_recap",
- titles=titles,
- xls_columns_width={
- "nom": 32,
- "cursus": 12,
- "ues": 32,
- "niveaux": 32,
- "decision_but": 14,
- "diplome": 17,
- "devenir": 8,
- "observations": 12,
- },
- xls_style_base=xls_style_base,
- )
- return tab.make_page(format=format, javascripts=["js/etud_info.js"], init_qtip=True)
+ return rows, titles
diff --git a/app/scodoc/sco_archives.py b/app/scodoc/sco_archives.py
index 8223a602..8b4a93e3 100644
--- a/app/scodoc/sco_archives.py
+++ b/app/scodoc/sco_archives.py
@@ -368,7 +368,7 @@ def do_formsemestre_archive(
PVArchive.store(archive_id, "Bulletins.json", data_js)
# Décisions de jury, en XLS
if formsemestre.formation.is_apc():
- response = jury_but_pv.pvjury_table_but(formsemestre_id, format="xls")
+ response = jury_but_pv.pvjury_page_but(formsemestre_id, fmt="xls")
data = response.get_data()
else: # formations classiques
data = sco_pvjury.formsemestre_pvjury(
diff --git a/app/scodoc/sco_pvjury.py b/app/scodoc/sco_pvjury.py
index c8924119..d1bd0e0b 100644
--- a/app/scodoc/sco_pvjury.py
+++ b/app/scodoc/sco_pvjury.py
@@ -228,7 +228,7 @@ def dict_pvjury(
'decisions_dict' : { etudid : decision (comme ci-dessus) },
}
"""
- formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
+ formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
if etudids is None:
etudids = nt.get_etudids()
@@ -510,7 +510,9 @@ def pvjury_table(
return lines, titles, columns_ids
-def formsemestre_pvjury(formsemestre_id, format="html", publish=True):
+# XXX TODO cette page a vocation a disparaitre,
+# remplacée par formsemestre_recapcomplet en mode jury (déjà le cas pour les BUT)
+def formsemestre_pvjury(formsemestre_id, format="html", publish=True): # XXX
"""Page récapitulant les décisions de jury"""
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
is_apc = formsemestre.formation.is_apc()
@@ -621,21 +623,21 @@ def formsemestre_pvjury(formsemestre_id, format="html", publish=True):
# ---------------------------------------------------------------------------
-def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None):
+def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid=None):
"""Generation PV jury en PDF: saisie des paramètres
Si etudid, PV pour un seul etudiant. Sinon, tout les inscrits au groupe indiqué.
"""
+ group_ids = group_ids or []
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
# Mise à jour des groupes d'étapes:
sco_groups.create_etapes_partition(formsemestre_id)
groups_infos = None
if etudid:
# PV pour ce seul étudiant:
- etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
- etuddescr = '%s' % (
- etudid,
- etud["nomprenom"],
- )
+ etud = Identite.query.get_or_404(etudid)
+ etuddescr = f"""{etud.nomprenom}"""
etudids = [etudid]
else:
etuddescr = ""
@@ -650,18 +652,22 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None):
H = [
html_sco_header.html_sem_header(
- "Edition du PV de jury %s" % etuddescr,
+ f"Édition du PV de jury {etuddescr}",
javascripts=sco_groups_view.JAVASCRIPTS,
cssstyles=sco_groups_view.CSSSTYLES,
init_qtip=True,
),
- """Utiliser cette page pour éditer des versions provisoires des PV.
- Il est recommandé d'archiver les versions définitives: voir cette page
-
"""
- % formsemestre_id,
+ f"""Utiliser cette page pour éditer des versions provisoires des PV.
+
Il est recommandé d'archiver les versions définitives:
+ voir cette page
+
""",
]
F = [
- """Voir aussi si besoin les réglages sur la page "Paramétrage" (accessible à l'administrateur du département).
+ """
Voir aussi si besoin les réglages sur la page "Paramétrage"
+ (accessible à l'administrateur du département).
""",
html_sco_header.sco_footer(),
]
@@ -692,7 +698,11 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None):
return "\n".join(H) + "\n" + tf[1] + "\n".join(F)
elif tf[0] == -1:
return flask.redirect(
- "formsemestre_pvjury?formsemestre_id=%s" % (formsemestre_id)
+ url_for(
+ "notes.formsemestre_pvjury",
+ scodoc_dept=g.scodoc_dept,
+ formsemestre_id=formsemestre_id,
+ )
)
else:
# submit
@@ -721,12 +731,12 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None):
finally:
PDFLOCK.release()
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
- dt = time.strftime("%Y-%m-%d")
+ date_iso = time.strftime("%Y-%m-%d")
if groups_infos:
groups_filename = "-" + groups_infos.groups_filename
else:
groups_filename = ""
- filename = "PV-%s%s-%s.pdf" % (sem["titre_num"], groups_filename, dt)
+ filename = f"""PV-{sem["titre_num"]}{groups_filename}-{date_iso}.pdf"""
return scu.sendPDFFile(pdfdoc, filename)
diff --git a/app/scodoc/sco_pvpdf.py b/app/scodoc/sco_pvpdf.py
index ed9cd775..eb0dfd41 100644
--- a/app/scodoc/sco_pvpdf.py
+++ b/app/scodoc/sco_pvpdf.py
@@ -679,10 +679,10 @@ def pvjury_pdf(
dpv,
only_diplome=False,
date_commission=date_commission,
- numeroArrete=numeroArrete,
- VDICode=VDICode,
+ numero_arrete=numeroArrete,
+ code_vdi=VDICode,
date_jury=date_jury,
- showTitle=showTitle,
+ show_title=showTitle,
pv_title=pv_title,
with_paragraph_nom=with_paragraph_nom,
anonymous=anonymous,
@@ -690,7 +690,8 @@ def pvjury_pdf(
jury_de_diplome = not dpv["semestre_non_terminal"]
- # Si Jury de passage et qu'un étudiant valide le parcours (car il a validé antérieurement le dernier semestre)
+ # Si Jury de passage et qu'un étudiant valide le parcours
+ # (car il a validé antérieurement le dernier semestre)
# alors on génère aussi un PV de diplome (à la suite dans le même doc PDF)
if not jury_de_diplome:
validations_parcours = [x["validation_parcours"] for x in dpv["decisions"]]
@@ -702,9 +703,9 @@ def pvjury_pdf(
only_diplome=True,
date_commission=date_commission,
date_jury=date_jury,
- numeroArrete=numeroArrete,
- VDICode=VDICode,
- showTitle=showTitle,
+ numero_arrete=numeroArrete,
+ code_vdi=VDICode,
+ show_title=showTitle,
pv_title=pv_title,
with_paragraph_nom=with_paragraph_nom,
anonymous=anonymous,
@@ -729,14 +730,39 @@ def pvjury_pdf(
return data
+def _make_pv_styles(formsemestre: FormSemestre):
+ style = reportlab.lib.styles.ParagraphStyle({})
+ style.fontSize = 12
+ style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre.id)
+ style.leading = 18
+ style.alignment = TA_JUSTIFY
+
+ indent = 1 * cm
+ style_bullet = reportlab.lib.styles.ParagraphStyle({})
+ style_bullet.fontSize = 12
+ style_bullet.fontName = sco_preferences.get_preference(
+ "PV_FONTNAME", formsemestre.id
+ )
+ style_bullet.leading = 12
+ style_bullet.alignment = TA_JUSTIFY
+ style_bullet.firstLineIndent = 0
+ style_bullet.leftIndent = indent
+ style_bullet.bulletIndent = indent
+ style_bullet.bulletFontName = "Times-Roman"
+ style_bullet.bulletFontSize = 11
+ style_bullet.spaceBefore = 5 * mm
+ style_bullet.spaceAfter = 5 * mm
+ return style, style_bullet
+
+
def _pvjury_pdf_type(
dpv,
only_diplome=False,
date_commission=None,
date_jury=None,
- numeroArrete=None,
- VDICode=None,
- showTitle=False,
+ numero_arrete=None,
+ code_vdi=None,
+ show_title=False,
pv_title=None,
anonymous=False,
with_paragraph_nom=False,
@@ -745,6 +771,7 @@ def _pvjury_pdf_type(
dpv: result of dict_pvjury
"""
from app.scodoc import sco_pvjury
+ from app.but import jury_but_pv
# Jury de diplome si sem. terminal OU que l'on demande les diplomés d'un semestre antérieur
diplome = (not dpv["semestre_non_terminal"]) or only_diplome
@@ -756,91 +783,71 @@ def _pvjury_pdf_type(
titre_diplome = pv_title or dpv["formation"]["titre_officiel"]
objects = []
- style = reportlab.lib.styles.ParagraphStyle({})
- style.fontSize = 12
- style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre_id)
- style.leading = 18
- style.alignment = TA_JUSTIFY
-
- indent = 1 * cm
- bulletStyle = reportlab.lib.styles.ParagraphStyle({})
- bulletStyle.fontSize = 12
- bulletStyle.fontName = sco_preferences.get_preference(
- "PV_FONTNAME", formsemestre_id
- )
- bulletStyle.leading = 12
- bulletStyle.alignment = TA_JUSTIFY
- bulletStyle.firstLineIndent = 0
- bulletStyle.leftIndent = indent
- bulletStyle.bulletIndent = indent
- bulletStyle.bulletFontName = "Times-Roman"
- bulletStyle.bulletFontSize = 11
- bulletStyle.spaceBefore = 5 * mm
- bulletStyle.spaceAfter = 5 * mm
+ style, style_bullet = _make_pv_styles(formsemestre)
objects += [Spacer(0, 5 * mm)]
objects += sco_pdf.make_paras(
- """
- Procès-verbal de %s du département %s - Session unique %s
- """
- % (
- titre_jury,
- sco_preferences.get_preference("DeptName", formsemestre_id) or "(sans nom)",
- sem["anneescolaire"],
- ),
+ f"""
+ Procès-verbal de {titre_jury} du département {
+ sco_preferences.get_preference("DeptName", formsemestre_id) or "(sans nom)"
+ } - Session unique {sem["anneescolaire"]}
+ """,
style,
)
objects += sco_pdf.make_paras(
- """
- %s
- """
- % titre_diplome,
+ f"""{titre_diplome}""",
style,
)
- if showTitle:
+ if show_title:
objects += sco_pdf.make_paras(
- """Semestre: %s""" % sem["titre"], style
+ f"""Semestre: {formsemestre.titre}""",
+ style,
)
if sco_preferences.get_preference("PV_TITLE_WITH_VDI", formsemestre_id):
objects += sco_pdf.make_paras(
- """VDI et Code: %s""" % (VDICode or ""), style
+ f"""VDI et Code: {(code_vdi or "")}""", style
)
if date_jury:
objects += sco_pdf.make_paras(
- """Jury tenu le %s""" % date_jury, style
+ f"""Jury tenu le {date_jury}""", style
)
objects += sco_pdf.make_paras(
""
+ (sco_preferences.get_preference("PV_INTRO", formsemestre_id) or "")
% {
- "Decnum": numeroArrete,
- "VDICode": VDICode,
+ "Decnum": numero_arrete,
+ "VDICode": code_vdi,
"UnivName": sco_preferences.get_preference("UnivName", formsemestre_id),
"Type": titre_jury,
"Date": date_commission, # deprecated
"date_commission": date_commission,
}
+ "",
- bulletStyle,
+ style_bullet,
)
objects += sco_pdf.make_paras(
"""Le jury propose les décisions suivantes :""", style
)
objects += [Spacer(0, 4 * mm)]
- lines, titles, columns_ids = sco_pvjury.pvjury_table(
- dpv,
- only_diplome=only_diplome,
- anonymous=anonymous,
- with_paragraph_nom=with_paragraph_nom,
- )
+
+ if formsemestre.formation.is_apc():
+ rows, titles = jury_but_pv.pvjury_table_but(formsemestre)
+ columns_ids = list(titles.keys())
+ else:
+ rows, titles, columns_ids = sco_pvjury.pvjury_table(
+ dpv,
+ only_diplome=only_diplome,
+ anonymous=anonymous,
+ with_paragraph_nom=with_paragraph_nom,
+ )
# convert to lists of tuples:
columns_ids = ["etudid"] + columns_ids
- lines = [[line.get(x, "") for x in columns_ids] for line in lines]
+ rows = [[line.get(x, "") for x in columns_ids] for line in rows]
titles = [titles.get(x, "") for x in columns_ids]
# Make a new cell style and put all cells in paragraphs
cell_style = styles.ParagraphStyle({})
@@ -863,7 +870,7 @@ def _pvjury_pdf_type(
("GRID", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),
("VALIGN", (0, 0), (-1, -1), "TOP"),
]
- titles = ["%s" % x for x in titles]
+ titles = [f"{x}" for x in titles]
def _format_pv_cell(x):
"""convert string to paragraph"""
@@ -872,22 +879,31 @@ def _pvjury_pdf_type(
else:
return x
- Pt = [[_format_pv_cell(x) for x in line[1:]] for line in ([titles] + lines)]
- widths = [6 * cm, 2.8 * cm, 2.8 * cm, None, None, None, None]
- if dpv["has_prev"]:
- widths[2:2] = [2.8 * cm]
- if sco_preferences.get_preference("bul_show_mention", formsemestre_id):
- widths += [None]
- objects.append(Table(Pt, repeatRows=1, colWidths=widths, style=table_style))
+ widths_by_id = {
+ "nom": 5 * cm,
+ "cursus": 2.8 * cm,
+ "ects": 1.4 * cm,
+ "devenir": 1.8 * cm,
+ "decision_but": 1.8 * cm,
+ }
+
+ table_cells = [[_format_pv_cell(x) for x in line[1:]] for line in ([titles] + rows)]
+ widths = [widths_by_id.get(col_id) for col_id in columns_ids[1:]]
+ # if dpv["has_prev"]:
+ # widths[2:2] = [2.8 * cm]
+ # if sco_preferences.get_preference("bul_show_mention", formsemestre_id):
+ # widths += [None]
+ objects.append(
+ Table(table_cells, repeatRows=1, colWidths=widths, style=table_style)
+ )
# Signature du directeur
objects += sco_pdf.make_paras(
- """
- %s, %s"""
- % (
- sco_preferences.get_preference("DirectorName", formsemestre_id) or "",
- sco_preferences.get_preference("DirectorTitle", formsemestre_id) or "",
- ),
+ f"""{
+ sco_preferences.get_preference("DirectorName", formsemestre_id) or ""
+ }, {
+ sco_preferences.get_preference("DirectorTitle", formsemestre_id) or ""
+ }""",
style,
)
diff --git a/app/scodoc/sco_recapcomplet.py b/app/scodoc/sco_recapcomplet.py
index c8fef8f0..d75cce6b 100644
--- a/app/scodoc/sco_recapcomplet.py
+++ b/app/scodoc/sco_recapcomplet.py
@@ -166,13 +166,14 @@ def formsemestre_recapcomplet(
if len(formsemestre.inscriptions) > 0:
H.append("""")
if sco_preferences.get_preference("use_ue_coefs", formsemestre_id):
diff --git a/app/views/notes.py b/app/views/notes.py
index c3e487b2..85a53dc6 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -2805,7 +2805,7 @@ def formsemestre_validation_suppress_etud(
# ------------- PV de JURY et archives
sco_publish("/formsemestre_pvjury", sco_pvjury.formsemestre_pvjury, Permission.ScoView)
-sco_publish("/pvjury_table_but", jury_but_pv.pvjury_table_but, Permission.ScoView)
+sco_publish("/pvjury_page_but", jury_but_pv.pvjury_page_but, Permission.ScoView)
@bp.route("/formsemestre_saisie_jury")