PV: lettres individuelles: affichage des UEs et autres réparations.

This commit is contained in:
Emmanuel Viennet 2023-02-19 23:06:44 +01:00
parent 9bf505ff11
commit 7e56dc730d
12 changed files with 67 additions and 65 deletions

View File

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

View File

@ -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()])

View File

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

View File

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

View File

@ -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(",")

View File

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

View File

@ -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"]:

View File

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

View File

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

View File

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

View File

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

View File

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