From 635269ff36e87a17f914856ebc4eb85498423306 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 22 Mar 2024 11:49:51 +0100 Subject: [PATCH] Modifie FormSemestre.etudids_actifs: retire @cached_property. Tests OK. --- app/comp/moy_mod.py | 36 ++++++++++++++++++--------------- app/comp/moy_ue.py | 21 +++++++++++-------- app/comp/res_classic.py | 3 ++- app/models/formsemestre.py | 12 +++++++---- app/scodoc/sco_liste_notes.py | 16 +++++++-------- app/scodoc/sco_saisie_notes.py | 2 +- tests/api/test_api_etudiants.py | 3 +++ tests/unit/test_but_modules.py | 10 +++++---- 8 files changed, 61 insertions(+), 42 deletions(-) diff --git a/app/comp/moy_mod.py b/app/comp/moy_mod.py index dacc4d87b..9418f96d5 100644 --- a/app/comp/moy_mod.py +++ b/app/comp/moy_mod.py @@ -72,7 +72,15 @@ class ModuleImplResults: les caches sont gérés par ResultatsSemestre. """ - def __init__(self, moduleimpl: ModuleImpl): + def __init__( + self, moduleimpl: ModuleImpl, etudids: list[int], etudids_actifs: set[int] + ): + """ + Args: + - etudids : liste des etudids, qui donne l'index du dataframe + (doit être tous les étudiants inscrits au semestre incluant les DEM et DEF) + - etudids_actifs l'ensemble des étudiants inscrits au semestre, non DEM/DEF. + """ self.moduleimpl_id = moduleimpl.id self.module_id = moduleimpl.module.id self.etudids = None @@ -105,14 +113,21 @@ class ModuleImplResults: """ self.evals_etudids_sans_note = {} """dict: evaluation_id : set des etudids non notés dans cette eval, sans les démissions.""" - self.load_notes() + self.load_notes(etudids, etudids_actifs) self.etuds_use_session2 = pd.Series(False, index=self.evals_notes.index) """1 bool par etud, indique si sa moyenne de module vient de la session2""" self.etuds_use_rattrapage = pd.Series(False, index=self.evals_notes.index) """1 bool par etud, indique si sa moyenne de module utilise la note de rattrapage""" - def load_notes(self): # ré-écriture de df_load_modimpl_notes + def load_notes( + self, etudids: list[int], etudids_actifs: set[int] + ): # ré-écriture de df_load_modimpl_notes """Charge toutes les notes de toutes les évaluations du module. + Args: + - etudids : liste des etudids, qui donne l'index du dataframe + (doit être tous les étudiants inscrits au semestre incluant les DEM et DEF) + - etudids_actifs l'ensemble des étudiants inscrits au semestre, non DEM/DEF. + Dataframe evals_notes colonnes: le nom de la colonne est l'evaluation_id (int) index (lignes): etudid (int) @@ -135,12 +150,12 @@ class ModuleImplResults: qui ont des notes ATT. """ moduleimpl = db.session.get(ModuleImpl, self.moduleimpl_id) - self.etudids = self._etudids() + self.etudids = etudids # --- Calcul nombre d'inscrits pour déterminer les évaluations "completes": # on prend les inscrits au module ET au semestre (donc sans démissionnaires) inscrits_module = {ins.etud.id for ins in moduleimpl.inscriptions}.intersection( - moduleimpl.formsemestre.etudids_actifs + etudids_actifs ) self.nb_inscrits_module = len(inscrits_module) @@ -235,17 +250,6 @@ class ModuleImplResults: eval_df[str(evaluation.id)] = pd.to_numeric(eval_df[str(evaluation.id)]) return eval_df - def _etudids(self): - """L'index du dataframe est la liste de tous les étudiants inscrits au semestre - (incluant les DEM et DEF) - """ - return [ - inscr.etudid - for inscr in db.session.get( - ModuleImpl, self.moduleimpl_id - ).formsemestre.inscriptions - ] - def get_evaluations_coefs(self, modimpl: ModuleImpl) -> np.array: """Coefficients des évaluations. Les coefs des évals incomplètes, rattrapage, session 2, bonus sont forcés à zéro. diff --git a/app/comp/moy_ue.py b/app/comp/moy_ue.py index 6f8004188..7e9e83cbb 100644 --- a/app/comp/moy_ue.py +++ b/app/comp/moy_ue.py @@ -99,9 +99,11 @@ def df_load_module_coefs(formation_id: int, semestre_idx: int = None) -> pd.Data # 0 pour modules normaux, 1. pour bonus (car par défaut, on veut qu'un bonus agisse # sur toutes les UE) default_poids = { - mod.id: 1.0 - if (mod.module_type == ModuleType.STANDARD) and (mod.ue.type == UE_SPORT) - else 0.0 + mod.id: ( + 1.0 + if (mod.module_type == ModuleType.STANDARD) and (mod.ue.type == UE_SPORT) + else 0.0 + ) for mod in modules } @@ -148,10 +150,12 @@ def df_load_modimpl_coefs( # 0 pour modules normaux, 1. pour bonus (car par défaut, on veut qu'un bonus agisse # sur toutes les UE) default_poids = { - modimpl.id: 1.0 - if (modimpl.module.module_type == ModuleType.STANDARD) - and (modimpl.module.ue.type == UE_SPORT) - else 0.0 + modimpl.id: ( + 1.0 + if (modimpl.module.module_type == ModuleType.STANDARD) + and (modimpl.module.ue.type == UE_SPORT) + else 0.0 + ) for modimpl in formsemestre.modimpls_sorted } @@ -200,8 +204,9 @@ def notes_sem_load_cube(formsemestre: FormSemestre) -> tuple: modimpls_results = {} modimpls_evals_poids = {} modimpls_notes = [] + etudids, etudids_actifs = formsemestre.etudids_actifs() for modimpl in formsemestre.modimpls_sorted: - mod_results = moy_mod.ModuleImplResultsAPC(modimpl) + mod_results = moy_mod.ModuleImplResultsAPC(modimpl, etudids, etudids_actifs) evals_poids, _ = moy_mod.load_evaluations_poids(modimpl.id) etuds_moy_module = mod_results.compute_module_moy(evals_poids) modimpls_results[modimpl.id] = mod_results diff --git a/app/comp/res_classic.py b/app/comp/res_classic.py index 673668b99..113faa0b2 100644 --- a/app/comp/res_classic.py +++ b/app/comp/res_classic.py @@ -256,8 +256,9 @@ def notes_sem_load_matrix(formsemestre: FormSemestre) -> tuple[np.ndarray, dict] """ modimpls_results = {} modimpls_notes = [] + etudids, etudids_actifs = formsemestre.etudids_actifs() for modimpl in formsemestre.modimpls_sorted: - mod_results = moy_mod.ModuleImplResultsClassic(modimpl) + mod_results = moy_mod.ModuleImplResultsClassic(modimpl, etudids, etudids_actifs) etuds_moy_module = mod_results.compute_module_moy() modimpls_results[modimpl.id] = mod_results modimpls_notes.append(etuds_moy_module) diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 84ba4c373..98ecc093f 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -936,10 +936,14 @@ class FormSemestre(models.ScoDocModel): partitions += [p for p in self.partitions if p.partition_name is None] return partitions - @cached_property - def etudids_actifs(self) -> set: - "Set des etudids inscrits non démissionnaires et non défaillants" - return {ins.etudid for ins in self.inscriptions if ins.etat == scu.INSCRIT} + def etudids_actifs(self) -> tuple[list[int], set[int]]: + """Liste les etudids inscrits (incluant DEM et DEF), + qui ser al'index des dataframes de notes + et donne l'ensemble des inscrits non DEM ni DEF. + """ + return [inscr.etudid for inscr in self.inscriptions], { + ins.etudid for ins in self.inscriptions if ins.etat == scu.INSCRIT + } @cached_property def etuds_inscriptions(self) -> dict: diff --git a/app/scodoc/sco_liste_notes.py b/app/scodoc/sco_liste_notes.py index 9e668c902..f2ca4f571 100644 --- a/app/scodoc/sco_liste_notes.py +++ b/app/scodoc/sco_liste_notes.py @@ -705,7 +705,8 @@ def _add_eval_columns( nb_att = 0 sum_notes = 0 notes = [] # liste des notes numeriques, pour calcul histogramme uniquement - inscrits = evaluation.moduleimpl.formsemestre.etudids_actifs # set d'etudids + # actifs == inscrit au semestre, non DEM ni DEF: + _, etudids_actifs = evaluation.moduleimpl.formsemestre.etudids_actifs() notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id) if evaluation.date_debut: @@ -734,7 +735,7 @@ def _add_eval_columns( nb_att += 1 # calcul moyenne SANS LES ABSENTS ni les DEMISSIONNAIRES if ( - (etudid in inscrits) + (etudid in etudids_actifs) and val is not None and val != scu.NOTES_NEUTRALISE and val != scu.NOTES_ATTENTE @@ -758,7 +759,7 @@ def _add_eval_columns( comment, ) else: - if (etudid in inscrits) and evaluation.publish_incomplete: + if (etudid in etudids_actifs) and evaluation.publish_incomplete: # Note manquante mais prise en compte immédiate: affiche ATT val = scu.NOTES_ATTENTE val_fmt = "ATT" @@ -875,8 +876,7 @@ def _add_moymod_column( col_id = "moymod" formsemestre = FormSemestre.get_formsemestre(formsemestre_id) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - inscrits = formsemestre.etudids_actifs - + _, etudids_actifs = formsemestre.etudids_actifs() nb_notes = 0 sum_notes = 0 notes = [] # liste des notes numeriques, pour calcul histogramme uniquement @@ -885,7 +885,7 @@ def _add_moymod_column( val = nt.get_etud_mod_moy(moduleimpl_id, etudid) # note sur 20, ou 'NA','NI' row[col_id] = scu.fmt_note(val, keep_numeric=keep_numeric) row["_" + col_id + "_td_attrs"] = ' class="moyenne" ' - if etudid in inscrits and not isinstance(val, str): + if etudid in etudids_actifs and not isinstance(val, str): notes.append(val) if not np.isnan(val): nb_notes = nb_notes + 1 @@ -928,7 +928,7 @@ def _add_apc_columns( # on va y ajouter une clé par UE du semestre nt: ResultatsSemestreBUT = res_sem.load_formsemestre_results(modimpl.formsemestre) modimpl_results: ModuleImplResults = nt.modimpls_results[modimpl.id] - inscrits = modimpl.formsemestre.etudids_actifs + _, etudids_actifs = modimpl.formsemestre.etudids_actifs() # les UE dans lesquelles ce module a un coef non nul: ues_with_coef = nt.modimpl_coefs_df[modimpl.id][ nt.modimpl_coefs_df[modimpl.id] > 0 @@ -946,7 +946,7 @@ def _add_apc_columns( if ( isinstance(moy_ue, float) and not np.isnan(moy_ue) - and row["etudid"] in inscrits + and row["etudid"] in etudids_actifs ): sum_by_ue[ue.id] += moy_ue nb_notes_by_ue[ue.id] += 1 diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index d298570a6..fd5b51bfd 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -541,7 +541,7 @@ def notes_add( ) } # Les étudiants inscrits au semestre ni DEM ni DEF - etudids_actifs = evaluation.moduleimpl.formsemestre.etudids_actifs + _, etudids_actifs = evaluation.moduleimpl.formsemestre.etudids_actifs() for etudid, value in notes: if check_inscription and ( (etudid not in inscrits) or (etudid not in etudids_actifs) diff --git a/tests/api/test_api_etudiants.py b/tests/api/test_api_etudiants.py index 4ee134b40..29df9e96a 100644 --- a/tests/api/test_api_etudiants.py +++ b/tests/api/test_api_etudiants.py @@ -931,6 +931,9 @@ def test_etudiant_bulletin_semestre(api_headers): # /ScoDoc/api/etudiant/nip/12345/formsemestre/123/bulletin/long/pdf/nosi # TODO voir forme utilisée par ScoDoc en interne: # formsemestre_bulletinetud?formsemestre_id=1263&etudid=16387 + formsemestre = POST_JSON( + f"/formsemestre/{1}/edit", {"bul_hide_xml": False}, headers=admin_header + ) def test_etudiant_groups(api_headers): diff --git a/tests/unit/test_but_modules.py b/tests/unit/test_but_modules.py index fd1bc4306..e12e6009f 100644 --- a/tests/unit/test_but_modules.py +++ b/tests/unit/test_but_modules.py @@ -2,6 +2,7 @@ Test modèles évaluations avec poids BUT et calcul moyennes modules """ + import datetime import numpy as np import pandas as pd @@ -215,7 +216,8 @@ def test_module_moy(test_client): etud = G.create_etud(nom="test") G.inscrit_etudiant(formsemestre_id, etud) etudid = etud["etudid"] - evaluation1 = db.session.get(Evaluation, evaluation1_ids[0]) + evaluation1: Evaluation = db.session.get(Evaluation, evaluation1_ids[0]) + formsemestre = evaluation1.moduleimpl.formsemestre # Crée une deuxième évaluation dans le même moduleimpl: evaluation2_id = G.create_evaluation( moduleimpl_id=evaluation1.moduleimpl_id, @@ -245,10 +247,10 @@ def test_module_moy(test_client): _ = sco_saisie_notes.notes_add(G.default_user, evaluation1.id, [(etudid, n1)]) _ = sco_saisie_notes.notes_add(G.default_user, evaluation2.id, [(etudid, n2)]) # Calcul de la moyenne du module - evals_poids, ues = moy_mod.load_evaluations_poids(moduleimpl_id) + evals_poids, _ = moy_mod.load_evaluations_poids(moduleimpl_id) assert evals_poids.shape == (nb_evals, nb_ues) - - mod_results = moy_mod.ModuleImplResultsAPC(modimpl) + etudids, etudids_actifs = formsemestre.etudids_actifs() + mod_results = moy_mod.ModuleImplResultsAPC(modimpl, etudids, etudids_actifs) evals_notes = mod_results.evals_notes assert evals_notes[evaluation1.id].dtype == np.float64