diff --git a/app/comp/res_but.py b/app/comp/res_but.py index 116ec09b4..d7fec7863 100644 --- a/app/comp/res_but.py +++ b/app/comp/res_but.py @@ -32,7 +32,7 @@ class ResultatsSemestreBUT(NotesTableCompat): def __init__(self, formsemestre): super().__init__(formsemestre) - """DataFrame, row UEs(sans bonus), cols modimplid, value coef""" + self.sem_cube = None """ndarray (etuds x modimpl x ue)""" @@ -43,7 +43,8 @@ class ResultatsSemestreBUT(NotesTableCompat): self.store() t2 = time.time() log( - f"ResultatsSemestreBUT: cached formsemestre_id={formsemestre.id} ({(t1-t0):g}s +{(t2-t1):g}s)" + f"""ResultatsSemestreBUT: cached formsemestre_id={formsemestre.id + } ({(t1-t0):g}s +{(t2-t1):g}s)""" ) def compute(self): diff --git a/app/comp/res_classic.py b/app/comp/res_classic.py index 605ba64c3..5577546b7 100644 --- a/app/comp/res_classic.py +++ b/app/comp/res_classic.py @@ -50,7 +50,8 @@ class ResultatsSemestreClassic(NotesTableCompat): self.store() t2 = time.time() log( - f"ResultatsSemestreClassic: cached formsemestre_id={formsemestre.id} ({(t1-t0):g}s +{(t2-t1):g}s)" + f"""ResultatsSemestreClassic: cached formsemestre_id={ + formsemestre.id} ({(t1-t0):g}s +{(t2-t1):g}s)""" ) # recalculé (aussi rapide que de les cacher) self.moy_min = self.etud_moy_gen.min() @@ -220,36 +221,29 @@ class ResultatsSemestreClassic(NotesTableCompat): moyenne générale. Coef = somme des coefs des modules de l'UE auxquels il est inscrit """ - c = comp_etud_sum_coef_modules_ue(self.formsemestre.id, etudid, ue["ue_id"]) - if c is not None: # inscrit à au moins un module de cette UE - return c + coef = comp_etud_sum_coef_modules_ue(self.formsemestre.id, etudid, ue["ue_id"]) + if coef is not None: # inscrit à au moins un module de cette UE + return coef # arfff: aucun moyen de déterminer le coefficient de façon sûre log( - "* oups: calcul coef UE impossible\nformsemestre_id='%s'\netudid='%s'\nue=%s" - % (self.formsemestre.id, etudid, ue) + f"""* oups: calcul coef UE impossible\nformsemestre_id='{self.formsemestre.id + }'\netudid='{etudid}'\nue={ue}""" ) etud: Identite = Identite.query.get(etudid) raise ScoValueError( - """

Coefficient de l'UE capitalisée %s impossible à déterminer - pour l'étudiant %s

-

Il faut saisir le coefficient de cette UE avant de continuer

+ f"""

Coefficient de l'UE capitalisée {ue.acronyme} + impossible à déterminer pour l'étudiant {etud.nom_disp()}

+

Il faut saisir le coefficient de cette UE avant de continuer

""" - % ( - ue.acronyme, - url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid), - etud.nom_disp(), - url_for( - "notes.formsemestre_edit_uecoefs", - scodoc_dept=g.scodoc_dept, - formsemestre_id=self.formsemestre.id, - err_ue_id=ue["ue_id"], - ), - ) ) - return 0.0 # ? - def notes_sem_load_matrix(formsemestre: FormSemestre) -> tuple[np.ndarray, dict]: """Calcule la matrice des notes du semestre @@ -279,7 +273,7 @@ def notes_sem_assemble_matrix(modimpls_notes: list[pd.Series]) -> np.ndarray: (Series rendus par compute_module_moy, index: etud) Resultat: ndarray (etud x module) """ - if not len(modimpls_notes): + if not modimpls_notes: return np.zeros((0, 0), dtype=float) modimpls_notes_arr = [s.values for s in modimpls_notes] modimpls_notes = np.stack(modimpls_notes_arr) diff --git a/app/comp/res_common.py b/app/comp/res_common.py index 30468da3d..519e9aec3 100644 --- a/app/comp/res_common.py +++ b/app/comp/res_common.py @@ -4,6 +4,9 @@ # See LICENSE ############################################################################## +"""Résultats semestre: méthodes communes aux formations classiques et APC +""" + from collections import Counter from functools import cached_property import numpy as np @@ -25,7 +28,6 @@ from app.scodoc import sco_groups from app.scodoc import sco_users from app.scodoc import sco_utils as scu - # Il faut bien distinguer # - ce qui est caché de façon persistente (via redis): # ce sont les attributs listés dans `_cached_attrs` @@ -41,6 +43,8 @@ class ResultatsSemestre(ResultatsCache): """ _cached_attrs = ( + "bonus", + "bonus_ues", "etud_moy_gen_ranks", "etud_moy_gen", "etud_moy_ue", @@ -55,6 +59,10 @@ class ResultatsSemestre(ResultatsCache): # BUT ou standard ? (apc == "approche par compétences") self.is_apc = formsemestre.formation.is_apc() # Attributs "virtuels", définis dans les sous-classes + self.bonus: pd.Series = None # virtuel + "Bonus sur moy. gen. Series de float, index etudid" + self.bonus_ues: pd.DataFrame = None # virtuel + "DataFrame de float, index etudid, columns: ue.id" # ResultatsSemestreBUT ou ResultatsSemestreClassic self.etud_moy_ue = {} "etud_moy_ue: DataFrame columns UE, rows etudid" @@ -102,6 +110,14 @@ class ResultatsSemestre(ResultatsCache): "dict { etudid : indice dans les inscrits }" return {e.id: idx for idx, e in enumerate(self.etuds)} + def modimpl_notes(self, modimpl_id: int, ue_id: int) -> np.ndarray: + """Les notes moyennes des étudiants du sem. à ce modimpl dans cette ue. + Utile pour stats bottom tableau recap. + Résultat: 1d array of float + """ + # différent en BUT et classique: virtuelle + raise NotImplementedError + @cached_property def etuds_dict(self) -> dict[int, Identite]: """dict { etudid : Identite } inscrits au semestre, @@ -230,6 +246,13 @@ class ResultatsSemestre(ResultatsCache): 0.0, min(self.etud_moy_gen[etudid], 20.0) ) + def get_etud_etat(self, etudid: int) -> str: + "Etat de l'etudiant: 'I', 'D', DEF ou '' (si pas connu dans ce semestre)" + ins = self.formsemestre.etuds_inscriptions.get(etudid, None) + if ins is None: + return "" + return ins.etat + def _get_etud_ue_cap(self, etudid: int, ue: UniteEns) -> dict: """Donne les informations sur la capitalisation de l'UE ue pour cet étudiant. Résultat: @@ -304,11 +327,11 @@ class ResultatsSemestre(ResultatsCache): if coef_ue is None: orig_sem = FormSemestre.query.get(ue_cap["formsemestre_id"]) raise ScoValueError( - f"""L'UE capitalisée {ue_capitalized.acronyme} + f"""L'UE capitalisée {ue_capitalized.acronyme} du semestre {orig_sem.titre_annee()} n'a pas d'indication d'ECTS. - Corrigez ou faite corriger le programme - via cette page. """ ) @@ -333,6 +356,11 @@ class ResultatsSemestre(ResultatsCache): "capitalized_ue_id": ue_cap["ue_id"] if is_capitalized else None, } + def compute_etud_ue_coef(self, etudid: int, ue: UniteEns) -> float: + "Détermine le coefficient de l'UE pour cet étudiant." + # calcul différent en classqiue et BUT + raise NotImplementedError() + def get_etud_ue_cap_coef(self, etudid, ue, ue_cap): """Calcule le coefficient d'une UE capitalisée, pour cet étudiant, injectée dans le semestre courant. @@ -401,7 +429,6 @@ class ResultatsSemestre(ResultatsCache): titles = { "rang": "Rg", # ordre des colonnes: - "_rang_col_order": 1, "_civilite_str_col_order": 2, "_nom_disp_col_order": 3, "_prenom_col_order": 4, @@ -544,8 +571,7 @@ class ResultatsSemestre(ResultatsCache): # --- TABLE FOOTER: ECTS, moyennes, min, max... footer_rows = [] - for bottom_line in bottom_infos: - row = bottom_infos[bottom_line] + for (bottom_line, row) in bottom_infos.items(): # Cases vides à styler: row["moy_gen"] = row.get("moy_gen", "") row["_moy_gen_class"] = "col_moy_gen" @@ -621,7 +647,7 @@ class ResultatsSemestre(ResultatsCache): # if dec: # codes_nb[dec["code"]] += 1 row_class = "" - etud_etat = self.get_etud_etat(etudid) # dans NotesTableCompat, à revoir + etud_etat = self.get_etud_etat(etudid) if etud_etat == DEM: gr_name = "Dém." row_class = "dem" diff --git a/app/comp/res_compat.py b/app/comp/res_compat.py index c18f549ef..8bbed0904 100644 --- a/app/comp/res_compat.py +++ b/app/comp/res_compat.py @@ -32,8 +32,6 @@ class NotesTableCompat(ResultatsSemestre): """ _cached_attrs = ResultatsSemestre._cached_attrs + ( - "bonus", - "bonus_ues", "malus", "etud_moy_gen_ranks", "etud_moy_gen_ranks_int", @@ -44,8 +42,6 @@ class NotesTableCompat(ResultatsSemestre): super().__init__(formsemestre) nb_etuds = len(self.etuds) - self.bonus = None # virtuel - self.bonus_ues = None # virtuel self.ue_rangs = {u.id: (None, nb_etuds) for u in self.ues} self.mod_rangs = None # sera surchargé en Classic, mais pas en APC """{ modimpl_id : (rangs, effectif) }""" @@ -251,13 +247,6 @@ class NotesTableCompat(ResultatsSemestre): ) return self.validations.decisions_jury.get(etudid, None) - def get_etud_etat(self, etudid: int) -> str: - "Etat de l'etudiant: 'I', 'D', DEF ou '' (si pas connu dans ce semestre)" - ins = self.formsemestre.etuds_inscriptions.get(etudid, None) - if ins is None: - return "" - return ins.etat - def get_etud_mat_moy(self, matiere_id: int, etudid: int) -> str: """moyenne d'un étudiant dans une matière (ou NA si pas de notes)""" if not self.moyennes_matieres: @@ -316,6 +305,7 @@ class NotesTableCompat(ResultatsSemestre): return self.etud_moy_gen_ranks.get(etudid, 99999) def get_etud_rang_group(self, etudid: int, group_id: int): + "Le rang de l'étudiant dans ce groupe (NON IMPLEMENTE)" return (None, 0) # XXX unimplemented TODO def get_evals_in_mod(self, moduleimpl_id: int) -> list[dict]: