From af48eb8fb8800ef3b064451940a8628936fa6f75 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 7 Jan 2022 10:37:48 +0100 Subject: [PATCH] recapcomplet sans NotesTable --- app/comp/moy_mod.py | 4 ++++ app/comp/res_classic.py | 19 +++++++++++++++ app/comp/res_sem.py | 43 ++++++++++++++++++++-------------- app/scodoc/notes_table.py | 1 + app/scodoc/sco_recapcomplet.py | 16 ++++++------- 5 files changed, 58 insertions(+), 25 deletions(-) diff --git a/app/comp/moy_mod.py b/app/comp/moy_mod.py index a75055b11..95524597f 100644 --- a/app/comp/moy_mod.py +++ b/app/comp/moy_mod.py @@ -69,6 +69,8 @@ class ModuleImplResults: "nombre d'inscrits (non DEM) au module" self.evaluations_completes = [] "séquence de booléens, indiquant les évals à prendre en compte." + self.evaluations_completes_dict = {} + "{ evaluation.id : bool } indique si à prendre en compte ou non." self.evaluations_etat = {} "{ evaluation_id: EvaluationEtat }" # @@ -122,6 +124,7 @@ class ModuleImplResults: # dataFrame vide, index = tous les inscrits au SEMESTRE evals_notes = pd.DataFrame(index=self.etudids, dtype=float) self.evaluations_completes = [] + self.evaluations_completes_dict = {} for evaluation in moduleimpl.evaluations: eval_df = self._load_evaluation_notes(evaluation) # is_complete ssi tous les inscrits (non dem) au semestre ont une note @@ -131,6 +134,7 @@ class ModuleImplResults: == self.nb_inscrits_module ) or evaluation.publish_incomplete # immédiate self.evaluations_completes.append(is_complete) + self.evaluations_completes_dict[evaluation.id] = is_complete # NULL en base => ABS (= -999) eval_df.fillna(scu.NOTES_ABSENCE, inplace=True) diff --git a/app/comp/res_classic.py b/app/comp/res_classic.py index b203ec589..6cf092a81 100644 --- a/app/comp/res_classic.py +++ b/app/comp/res_classic.py @@ -60,6 +60,25 @@ class ResultatsSemestreClassic(NotesTableCompat): """ return self.modimpls_results[moduleimpl_id].etuds_moy_module.get(etudid, "NI") + def get_mod_stats(self, moduleimpl_id: int) -> dict: + """Stats sur les notes obtenues dans un modimpl""" + notes_series: pd.Series = self.modimpls_results[moduleimpl_id].etuds_moy_module + nb_notes = len(notes_series) + if not nb_notes: + super().get_mod_stats(moduleimpl_id) + return { + # Series: Statistical methods from ndarray have been overridden to automatically + # exclude missing data (currently represented as NaN) + "moy": notes_series.mean(), # donc sans prendre en compte les NaN + "max": notes_series.max(), + "min": notes_series.min(), + "nb_notes": nb_notes, + "nb_missing": sum(notes_series.isna()), + "nb_valid_evals": sum( + self.modimpls_results[moduleimpl_id].evaluations_completes + ), + } + def notes_sem_load_matrix(formsemestre: FormSemestre) -> tuple: """Calcule la matrice des notes du semestre diff --git a/app/comp/res_sem.py b/app/comp/res_sem.py index 5eccd3253..cb9290b4e 100644 --- a/app/comp/res_sem.py +++ b/app/comp/res_sem.py @@ -9,6 +9,7 @@ from functools import cached_property import numpy as np import pandas as pd from app.comp.aux import StatsMoyenne +from app.comp.moy_mod import ModuleImplResults from app.models import FormSemestre, ModuleImpl from app.scodoc import sco_utils as scu from app.scodoc.sco_cache import ResultatsSemestreCache @@ -36,11 +37,12 @@ class ResultatsSemestre: self.formsemestre = formsemestre # BUT ou standard ? (apc == "approche par compétences") self.is_apc = formsemestre.formation.is_apc() - # Attributs "virtuels", définis pas les sous-classes + # Attributs "virtuels", définis dans les sous-classes # ResultatsSemestreBUT ou ResultatsSemestreStd self.etud_moy_ue = {} self.etud_moy_gen = {} self.etud_moy_gen_ranks = {} + self.modimpls_results: ModuleImplResults = None # TODO def load_cached(self) -> bool: @@ -215,7 +217,7 @@ class NotesTableCompat(ResultatsSemestre): def get_etud_moy_gen(self, etudid): # -> float | str """Moyenne générale de cet etudiant dans ce semestre. - Prend en compte les UE capitalisées. (TODO) + Prend(ra) en compte les UE capitalisées. (TODO) XXX Si apc, moyenne indicative. Si pas de notes: 'NA' """ @@ -241,30 +243,37 @@ class NotesTableCompat(ResultatsSemestre): return (None, 0) # XXX unimplemented TODO def get_evals_in_mod(self, moduleimpl_id: int) -> list[dict]: - "liste des évaluations valides dans un module" + """Liste d'informations (compat NotesTable) sur évaluations completes + de ce module. + Évaluation "complete" ssi toutes notes saisies ou en attente. + """ modimpl = ModuleImpl.query.get(moduleimpl_id) evals_results = [] for e in modimpl.evaluations: - d = e.to_dict() - d["heure_debut"] = e.heure_debut # datetime.time - d["heure_fin"] = e.heure_fin - d["jour"] = e.jour # datetime - d["notes"] = { - etud.id: { - "etudid": etud.id, - "value": self.results.modimpls_evals_notes[e.moduleimpl_id][e.id][ - etud.id - ], + if self.modimpls_results[moduleimpl_id].evaluations_completes_dict[e.id]: + d = e.to_dict() + d["heure_debut"] = e.heure_debut # datetime.time + d["heure_fin"] = e.heure_fin + d["jour"] = e.jour # datetime + d["notes"] = { + etud.id: { + "etudid": etud.id, + "value": self.modimpls_evals_notes[e.moduleimpl_id][e.id][ + etud.id + ], + } + for etud in self.etuds } - for etud in self.results.etuds - } - evals_results.append(d) + evals_results.append(d) return evals_results def get_moduleimpls_attente(self): return [] # XXX TODO - def get_mod_stats(self, moduleimpl_id): + def get_mod_stats(self, moduleimpl_id: int) -> dict: + """Stats sur les notes obtenues dans un modimpl + Vide en APC + """ return { "moy": "-", "max": "-", diff --git a/app/scodoc/notes_table.py b/app/scodoc/notes_table.py index 828ea228c..0e21efb24 100644 --- a/app/scodoc/notes_table.py +++ b/app/scodoc/notes_table.py @@ -170,6 +170,7 @@ class NotesTable: """ def __init__(self, formsemestre_id): + # XXX breakpoint() log(f"NotesTable( formsemestre_id={formsemestre_id} )") if not formsemestre_id: raise ValueError("invalid formsemestre_id (%s)" % formsemestre_id) diff --git a/app/scodoc/sco_recapcomplet.py b/app/scodoc/sco_recapcomplet.py index 6c3311467..293dd2084 100644 --- a/app/scodoc/sco_recapcomplet.py +++ b/app/scodoc/sco_recapcomplet.py @@ -303,9 +303,11 @@ def make_formsemestre_recapcomplet( sem = sco_formsemestre.do_formsemestre_list( args={"formsemestre_id": formsemestre_id} )[0] - nt = sco_cache.NotesTableCache.get(formsemestre_id) + parcours = formsemestre.formation.get_parcours() + + # nt = sco_cache.NotesTableCache.get(formsemestre_id) # XXX EXPERIMENTAL - # nt = ResultatsSemestreClassic(formsemestre) + nt = ResultatsSemestreClassic(formsemestre) modimpls = nt.get_modimpls_dict() ues = nt.get_ues_stat_dict() # incluant le(s) UE de sport # @@ -701,9 +703,7 @@ def make_formsemestre_recapcomplet( idx_col_moy = idx_col_gr + 1 cssclass = "recap_col_moy" try: - if float(nsn[idx_col_moy]) < ( - nt.parcours.BARRE_MOY - scu.NOTES_TOLERANCE - ): + if float(nsn[idx_col_moy]) < (parcours.BARRE_MOY - scu.NOTES_TOLERANCE): cssclass = "recap_col_moy_inf" except: pass @@ -718,11 +718,11 @@ def make_formsemestre_recapcomplet( if (ir < (nblines - 4)) or (ir == nblines - 3): try: - if float(nsn[i]) < nt.parcours.get_barre_ue( + if float(nsn[i]) < parcours.get_barre_ue( ue["type"] ): # NOTES_BARRE_UE cssclass = "recap_col_ue_inf" - elif float(nsn[i]) >= nt.parcours.NOTES_BARRE_VALID_UE: + elif float(nsn[i]) >= parcours.NOTES_BARRE_VALID_UE: cssclass = "recap_col_ue_val" except: pass @@ -732,7 +732,7 @@ def make_formsemestre_recapcomplet( ir == nblines - 3 ): # si moyenne generale module < barre ue, surligne: try: - if float(nsn[i]) < nt.parcours.get_barre_ue(ue["type"]): + if float(nsn[i]) < parcours.get_barre_ue(ue["type"]): cssclass = "recap_col_moy_inf" except: pass