diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py
index 4af56a3cacd47d7ccb4f860b35d9ea600068041e..9f0a9b85c6fb0d7daf0ad067978847f5a1540f98 100644
--- a/app/but/bulletin_but.py
+++ b/app/but/bulletin_but.py
@@ -385,7 +385,7 @@ class BulletinBUT:
"injustifie": nbabs - nbabsjust,
"total": nbabs,
}
- decisions_ues = self.res.get_etud_decision_ues(etud.id) or {}
+ decisions_ues = self.res.get_etud_decisions_ue(etud.id) or {}
if self.prefs["bul_show_ects"]:
ects_tot = res.etud_ects_tot_sem(etud.id)
ects_acquis = res.get_etud_ects_valides(etud.id, decisions_ues)
diff --git a/app/comp/res_compat.py b/app/comp/res_compat.py
index e973a7a6c39c1da4250b9802bdd145361e1aa654..1ebac8b176f8448355b548cba97c9dcbe907188d 100644
--- a/app/comp/res_compat.py
+++ b/app/comp/res_compat.py
@@ -271,9 +271,9 @@ class NotesTableCompat(ResultatsSemestre):
def etud_has_decision(self, etudid):
"""True s'il y a une décision de jury pour cet étudiant"""
- return self.get_etud_decision_ues(etudid) or self.get_etud_decision_sem(etudid)
+ return self.get_etud_decisions_ue(etudid) or self.get_etud_decision_sem(etudid)
- def get_etud_decision_ues(self, etudid: int) -> dict:
+ def get_etud_decisions_ue(self, etudid: int) -> dict:
"""Decisions du jury pour les UE de cet etudiant, ou None s'il n'y en pas eu.
Ne tient pas compte des UE capitalisées.
{ ue_id : { 'code' : ADM|CMP|AJ, 'event_date' : "d/m/y", 'ects' : x }
@@ -288,10 +288,10 @@ class NotesTableCompat(ResultatsSemestre):
def get_etud_ects_valides(self, etudid: int, decisions_ues: dict = False) -> 0:
"""Le total des ECTS validés (et enregistrés) par l'étudiant dans ce semestre.
NB: avant jury, rien d'enregistré, donc zéro ECTS.
- Optimisation: si decisions_ues est passé, l'utilise, sinon appelle get_etud_decision_ues()
+ Optimisation: si decisions_ues est passé, l'utilise, sinon appelle get_etud_decisions_ue()
"""
if decisions_ues is False:
- decisions_ues = self.get_etud_decision_ues(etudid)
+ decisions_ues = self.get_etud_decisions_ue(etudid)
if not decisions_ues:
return 0.0
return sum([d.get("ects", 0.0) for d in decisions_ues.values()])
diff --git a/app/models/etudiants.py b/app/models/etudiants.py
index cc1d4d528314948724a60ebff96036bb6ae0aaa6..c49717111bc049e003934390146418c9051cb1ff 100644
--- a/app/models/etudiants.py
+++ b/app/models/etudiants.py
@@ -183,6 +183,10 @@ class Identite(db.Model):
e["etudid"] = self.id
e["date_naissance"] = ndb.DateISOtoDMY(e["date_naissance"])
e["ne"] = self.e
+ e["nomprenom"] = self.nomprenom
+ adresse = self.adresses.first()
+ if adresse:
+ e.update(adresse.to_dict())
return {k: e[k] or "" for k in e} # convert_null_outputs_to_empty
def to_dict_bul(self, include_urls=True):
diff --git a/app/scodoc/notes_table.py b/app/scodoc/notes_table.py
index 0379bbc2926429b8a7e3d01be314849808456f88..953697484bbd1e9db770d8cebcd627c0a29543c8 100644
--- a/app/scodoc/notes_table.py
+++ b/app/scodoc/notes_table.py
@@ -1102,7 +1102,7 @@ class NotesTable:
else:
return self.decisions_jury.get(etudid, None)
- def get_etud_decision_ues(self, etudid):
+ def get_etud_decisions_ue(self, etudid):
"""Decisions du jury pour les UE de cet etudiant, ou None s'il n'y en pas eu.
Ne tient pas compte des UE capitalisées.
{ ue_id : { 'code' : ADM|CMP|AJ, 'event_date' : }
@@ -1122,7 +1122,7 @@ class NotesTable:
def etud_has_decision(self, etudid):
"""True s'il y a une décision de jury pour cet étudiant"""
- return self.get_etud_decision_ues(etudid) or self.get_etud_decision_sem(etudid)
+ return self.get_etud_decisions_ue(etudid) or self.get_etud_decision_sem(etudid)
def all_etuds_have_sem_decisions(self):
"""True si tous les étudiants du semestre ont une décision de jury.
diff --git a/app/scodoc/sco_apogee_csv.py b/app/scodoc/sco_apogee_csv.py
index 1045e7ba49702feb13466cbac81ca747dfd751ad..1e9610c1bab4955f70bebee621f80bb1ef3b2759 100644
--- a/app/scodoc/sco_apogee_csv.py
+++ b/app/scodoc/sco_apogee_csv.py
@@ -433,7 +433,7 @@ class ApoEtud(dict):
return VOID_APO_RES
# Elements UE
- decisions_ue = nt.get_etud_decision_ues(etudid)
+ decisions_ue = nt.get_etud_decisions_ue(etudid)
for ue in nt.get_ues_stat_dict():
if ue["code_apogee"] and code in {
x.strip() for x in ue["code_apogee"].split(",")
diff --git a/app/scodoc/sco_bulletins_pdf.py b/app/scodoc/sco_bulletins_pdf.py
index 0e0883704511a15aa187712a8a5eccc3d3637ef3..a26397915ded351ebaeca4aa0eebed96304bac24 100644
--- a/app/scodoc/sco_bulletins_pdf.py
+++ b/app/scodoc/sco_bulletins_pdf.py
@@ -124,6 +124,24 @@ def replacement_function(match):
)
+class WrapDict(object):
+ """Wrap a dict so that getitem returns '' when values are None"""
+
+ def __init__(self, adict, NoneValue=""):
+ self.dict = adict
+ self.NoneValue = NoneValue
+
+ def __getitem__(self, key):
+ try:
+ value = self.dict[key]
+ except KeyError:
+ return f"XXX {key} invalide XXX"
+ if value is None:
+ return self.NoneValue
+ else:
+ return value
+
+
def process_field(field, cdict, style, suppress_empty_pars=False, format="pdf"):
"""Process a field given in preferences, returns
- if format = 'pdf': a list of Platypus objects
@@ -137,18 +155,19 @@ def process_field(field, cdict, style, suppress_empty_pars=False, format="pdf"):
If format = 'html', replaces <para> by <p>. HTML does not allow logos.
"""
try:
- text = (field or "") % scu.WrapDict(
- cdict
- ) # note that None values are mapped to empty strings
+ # None values are mapped to empty strings by WrapDict
+ text = (field or "") % WrapDict(cdict)
except KeyError as exc:
+ missing_key = exc.args[0] if len(exc.args) > 0 else "?"
log(
- f"""process_field: KeyError on field={field!r}
+ f"""process_field: KeyError {missing_key} on field={field!r}
values={pprint.pformat(cdict)}
"""
)
- if len(exc.args) > 0:
- missing_field = exc.args[0]
- text = f"""<para><i>format invalide: champs</i> {missing_field} <i>inexistant !</i></para>"""
+ text = f"""<para><i>format invalide: champs</i> {missing_key} <i>inexistant !</i></para>"""
+ scu.flash_once(
+ f"Attention: format PDF invalide (champs {field}, clef {missing_key})"
+ )
except: # pylint: disable=bare-except
log(
f"""process_field: invalid format. field={field!r}
@@ -156,7 +175,7 @@ def process_field(field, cdict, style, suppress_empty_pars=False, format="pdf"):
"""
)
# ne sera pas visible si lien vers pdf:
- scu.flash_once(f"Attention: format PDF invalide (champs {field}")
+ scu.flash_once(f"Attention: format PDF invalide (champs {field})")
text = (
"<para><i>format invalide !</i></para><para>"
+ traceback.format_exc()
diff --git a/app/scodoc/sco_pv_dict.py b/app/scodoc/sco_pv_dict.py
index 0dcc421ffd39459da89897ec8c596ec1cb468cd8..9c059b62a1293712b4945146e378d51a2b63dfca 100644
--- a/app/scodoc/sco_pv_dict.py
+++ b/app/scodoc/sco_pv_dict.py
@@ -42,6 +42,7 @@ from app.models import (
but_validations,
)
from app.scodoc import codes_cursus
+from app.scodoc import sco_edit_ue
from app.scodoc import sco_etud
from app.scodoc import sco_formsemestre
from app.scodoc import sco_cursus
@@ -109,7 +110,7 @@ def dict_pvjury(
etudid
) # I|D|DEF (inscription ou démission ou défaillant)
d["decision_sem"] = nt.get_etud_decision_sem(etudid)
- d["decisions_ue"] = nt.get_etud_decision_ues(etudid)
+ d["decisions_ue"] = nt.get_etud_decisions_ue(etudid)
if formsemestre.formation.is_apc():
d.update(but_validations.dict_decision_jury(etud, formsemestre))
d["last_formsemestre_id"] = Se.get_semestres()[
@@ -241,17 +242,17 @@ def _comp_ects_capitalises_by_ue_code(nt: NotesTableCompat, etudid: int):
return ects_by_ue_code
-def _comp_ects_by_ue_code(nt, decision_ues):
+def _comp_ects_by_ue_code(nt, decisions_ue):
"""Calcul somme des ECTS validés dans ce semestre (sans les UE capitalisées)
- decision_ues est le resultat de nt.get_etud_decision_ues
+ decisions_ue est le resultat de nt.get_etud_decisions_ue
Chaque resultat est un dict: { ue_code : ects }
"""
- if not decision_ues:
+ if not decisions_ue:
return {}
ects_by_ue_code = {}
- for ue_id in decision_ues:
- d = decision_ues[ue_id]
+ for ue_id in decisions_ue:
+ d = decisions_ue[ue_id]
ue = UniteEns.query.get(ue_id)
ects_by_ue_code[ue.ue_code] = d["ects"]
@@ -269,26 +270,22 @@ def _descr_decisions_ues(nt, etudid, decisions_ue, decision_sem) -> list[dict]:
return []
uelist = []
# Les UE validées dans ce semestre:
- for ue_id in decisions_ue.keys():
- try:
- if decisions_ue[ue_id] and (
- codes_cursus.code_ue_validant(decisions_ue[ue_id]["code"])
- or (
- (not nt.is_apc)
- and (
- # XXX ceci devrait dépendre du parcours et non pas être une option ! #sco8
- decision_sem
- and scu.CONFIG.CAPITALIZE_ALL_UES
- and codes_cursus.code_semestre_validant(decision_sem["code"])
- )
+ for ue_id in decisions_ue:
+ if decisions_ue[ue_id] and (
+ codes_cursus.code_ue_validant(decisions_ue[ue_id].get("code"))
+ or (
+ (not nt.is_apc)
+ and (
+ # XXX ceci devrait dépendre du parcours et non pas être une option ! #sco8
+ decision_sem
+ and scu.CONFIG.CAPITALIZE_ALL_UES
+ and decision_sem
+ and codes_cursus.code_semestre_validant(decision_sem.get("code"))
)
- ):
- ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
- uelist.append(ue)
- except:
- log(
- f"Exception in descr_decisions_ues: ue_id={ue_id} decisions_ue={decisions_ue}"
)
+ ):
+ ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
+ uelist.append(ue)
# Les UE capitalisées dans d'autres semestres:
if etudid in nt.validations.ue_capitalisees.index:
for ue_id in nt.validations.ue_capitalisees.loc[[etudid]]["ue_id"]:
diff --git a/app/scodoc/sco_pv_lettres_inviduelles.py b/app/scodoc/sco_pv_lettres_inviduelles.py
index 7a4f2f50ab34ee32b94bc81d5be0b50ece986469..66868bcbe3db69d27c92c90710d9ed0507a76429 100644
--- a/app/scodoc/sco_pv_lettres_inviduelles.py
+++ b/app/scodoc/sco_pv_lettres_inviduelles.py
@@ -47,7 +47,6 @@ from app.models import FormSemestre, Identite
import app.scodoc.sco_utils as scu
from app.scodoc import sco_bulletins_pdf
from app.scodoc import sco_pv_dict
-from app.scodoc import sco_etud
from app.scodoc import sco_pdf
from app.scodoc import sco_preferences
from app.scodoc.sco_exceptions import ScoValueError
@@ -70,9 +69,6 @@ def pdf_lettres_individuelles(
dpv = sco_pv_dict.dict_pvjury(formsemestre_id, etudids=etudids, with_prev=True)
if not dpv:
return ""
- # Ajoute infos sur etudiants
- etuds = [x["identite"] for x in dpv["decisions"]]
- sco_etud.fill_etuds_info(etuds)
#
formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id)
prefs = sco_preferences.SemPreferences(formsemestre_id)
@@ -95,9 +91,10 @@ def pdf_lettres_individuelles(
decision["decision_sem"]
or decision.get("decision_annee")
or decision.get("decision_rcue")
+ or decision.get("decisions_ue")
): # decision prise
etud: Identite = Identite.query.get(decision["identite"]["etudid"])
- params["nomEtud"] = etud.nomprenom
+ params["nomEtud"] = etud.nomprenom # backward compat
bookmarks[npages + 1] = scu.suppress_accents(etud.nomprenom)
try:
objects += pdf_lettre_individuelle(
@@ -217,7 +214,7 @@ def pdf_lettre_individuelle(sem, decision, etud: Identite, params, signature=Non
params.update(decision["identite"])
# fix domicile
- if params["domicile"]:
+ if params.get("domicile"):
params["domicile"] = params["domicile"].replace("\\n", "<br/>")
# UE capitalisées:
diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py
index bf8d8c296122b05402e3fbd3231c0d459be51258..b8a2de08ca8668fef71a12532bab8ea263812f28 100644
--- a/app/scodoc/sco_saisie_notes.py
+++ b/app/scodoc/sco_saisie_notes.py
@@ -953,7 +953,7 @@ def has_existing_decision(M, E, etudid):
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
if nt.get_etud_decision_sem(etudid):
return True
- dec_ues = nt.get_etud_decision_ues(etudid)
+ dec_ues = nt.get_etud_decisions_ue(etudid)
if dec_ues:
mod = sco_edit_module.module_list({"module_id": M["module_id"]})[0]
ue_id = mod["ue_id"]
diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py
index 34ea2f0d1c3453d859dd87a1d0313bab8cb47653..abdff099e6658303f922f027f2a25c56d13fa51b 100644
--- a/app/scodoc/sco_utils.py
+++ b/app/scodoc/sco_utils.py
@@ -291,21 +291,6 @@ class DictDefault(dict): # obsolete, use collections.defaultdict
return value
-class WrapDict(object):
- """Wrap a dict so that getitem returns '' when values are None"""
-
- def __init__(self, adict, NoneValue=""):
- self.dict = adict
- self.NoneValue = NoneValue
-
- def __getitem__(self, key):
- value = self.dict[key]
- if value is None:
- return self.NoneValue
- else:
- return value
-
-
def group_by_key(d, key):
gr = DictDefault(defaultvalue=[])
for e in d:
diff --git a/app/tables/jury_recap.py b/app/tables/jury_recap.py
index 78ed33b5cc43a26aa5cff8d3f4732b9703443b38..f10437a8daa96a1a2da55436e7145b0b89843354 100644
--- a/app/tables/jury_recap.py
+++ b/app/tables/jury_recap.py
@@ -158,7 +158,7 @@ class TableJury(TableRecap):
titre,
validation_rcue.code,
group="cursus_" + annee,
- classes=["recorded_code"],
+ classes=[],
column_classes=["cursus_but" + (" first" if first else "")],
target_attrs={
"title": f"{niveau.competence.titre} niveau {niveau.ordre}"
@@ -221,7 +221,7 @@ class RowJury(RowRecap):
"Ajoute 2 colonnes: moyenne d'UE et code jury"
# table recap standard (mais avec group différent)
super().add_ue_cols(ue, ue_status, col_group=col_group or "col_ue")
- dues = self.table.res.get_etud_decision_ues(self.etud.id)
+ dues = self.table.res.get_etud_decisions_ue(self.etud.id)
due = dues.get(ue.id) if dues else None
col_id = f"moy_ue_{ue.id}_code"
diff --git a/tests/unit/test_sco_basic.py b/tests/unit/test_sco_basic.py
index 5b5f85e6f6bc4e47a7e65149f1c5cc66c68dfed8..0030d036d8686dd001ba78e50a7636ba5c914a5b 100644
--- a/tests/unit/test_sco_basic.py
+++ b/tests/unit/test_sco_basic.py
@@ -233,7 +233,7 @@ def run_sco_basic(verbose=False) -> FormSemestre:
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
for etud in etuds[:5]:
- dec_ues = nt.get_etud_decision_ues(etud["etudid"])
+ dec_ues = nt.get_etud_decisions_ue(etud["etudid"])
for ue_id in dec_ues:
assert dec_ues[ue_id]["code"] in {"ADM", "CMP"}