Modifie FormSemestre.etudids_actifs: retire @cached_property. Tests OK.

This commit is contained in:
Emmanuel Viennet 2024-03-22 11:49:51 +01:00
parent 4aa30a40bd
commit 635269ff36
8 changed files with 61 additions and 42 deletions

View File

@ -72,7 +72,15 @@ class ModuleImplResults:
les caches sont gérés par ResultatsSemestre. 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.moduleimpl_id = moduleimpl.id
self.module_id = moduleimpl.module.id self.module_id = moduleimpl.module.id
self.etudids = None self.etudids = None
@ -105,14 +113,21 @@ class ModuleImplResults:
""" """
self.evals_etudids_sans_note = {} self.evals_etudids_sans_note = {}
"""dict: evaluation_id : set des etudids non notés dans cette eval, sans les démissions.""" """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) 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""" """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) 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""" """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. """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 Dataframe evals_notes
colonnes: le nom de la colonne est l'evaluation_id (int) colonnes: le nom de la colonne est l'evaluation_id (int)
index (lignes): etudid (int) index (lignes): etudid (int)
@ -135,12 +150,12 @@ class ModuleImplResults:
qui ont des notes ATT. qui ont des notes ATT.
""" """
moduleimpl = db.session.get(ModuleImpl, self.moduleimpl_id) 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": # --- Calcul nombre d'inscrits pour déterminer les évaluations "completes":
# on prend les inscrits au module ET au semestre (donc sans démissionnaires) # on prend les inscrits au module ET au semestre (donc sans démissionnaires)
inscrits_module = {ins.etud.id for ins in moduleimpl.inscriptions}.intersection( inscrits_module = {ins.etud.id for ins in moduleimpl.inscriptions}.intersection(
moduleimpl.formsemestre.etudids_actifs etudids_actifs
) )
self.nb_inscrits_module = len(inscrits_module) 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)]) eval_df[str(evaluation.id)] = pd.to_numeric(eval_df[str(evaluation.id)])
return eval_df 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: def get_evaluations_coefs(self, modimpl: ModuleImpl) -> np.array:
"""Coefficients des évaluations. """Coefficients des évaluations.
Les coefs des évals incomplètes, rattrapage, session 2, bonus sont forcés à zéro. Les coefs des évals incomplètes, rattrapage, session 2, bonus sont forcés à zéro.

View File

@ -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 # 0 pour modules normaux, 1. pour bonus (car par défaut, on veut qu'un bonus agisse
# sur toutes les UE) # sur toutes les UE)
default_poids = { default_poids = {
mod.id: 1.0 mod.id: (
if (mod.module_type == ModuleType.STANDARD) and (mod.ue.type == UE_SPORT) 1.0
else 0.0 if (mod.module_type == ModuleType.STANDARD) and (mod.ue.type == UE_SPORT)
else 0.0
)
for mod in modules 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 # 0 pour modules normaux, 1. pour bonus (car par défaut, on veut qu'un bonus agisse
# sur toutes les UE) # sur toutes les UE)
default_poids = { default_poids = {
modimpl.id: 1.0 modimpl.id: (
if (modimpl.module.module_type == ModuleType.STANDARD) 1.0
and (modimpl.module.ue.type == UE_SPORT) if (modimpl.module.module_type == ModuleType.STANDARD)
else 0.0 and (modimpl.module.ue.type == UE_SPORT)
else 0.0
)
for modimpl in formsemestre.modimpls_sorted for modimpl in formsemestre.modimpls_sorted
} }
@ -200,8 +204,9 @@ def notes_sem_load_cube(formsemestre: FormSemestre) -> tuple:
modimpls_results = {} modimpls_results = {}
modimpls_evals_poids = {} modimpls_evals_poids = {}
modimpls_notes = [] modimpls_notes = []
etudids, etudids_actifs = formsemestre.etudids_actifs()
for modimpl in formsemestre.modimpls_sorted: 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) evals_poids, _ = moy_mod.load_evaluations_poids(modimpl.id)
etuds_moy_module = mod_results.compute_module_moy(evals_poids) etuds_moy_module = mod_results.compute_module_moy(evals_poids)
modimpls_results[modimpl.id] = mod_results modimpls_results[modimpl.id] = mod_results

View File

@ -256,8 +256,9 @@ def notes_sem_load_matrix(formsemestre: FormSemestre) -> tuple[np.ndarray, dict]
""" """
modimpls_results = {} modimpls_results = {}
modimpls_notes = [] modimpls_notes = []
etudids, etudids_actifs = formsemestre.etudids_actifs()
for modimpl in formsemestre.modimpls_sorted: 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() etuds_moy_module = mod_results.compute_module_moy()
modimpls_results[modimpl.id] = mod_results modimpls_results[modimpl.id] = mod_results
modimpls_notes.append(etuds_moy_module) modimpls_notes.append(etuds_moy_module)

View File

@ -936,10 +936,14 @@ class FormSemestre(models.ScoDocModel):
partitions += [p for p in self.partitions if p.partition_name is None] partitions += [p for p in self.partitions if p.partition_name is None]
return partitions return partitions
@cached_property def etudids_actifs(self) -> tuple[list[int], set[int]]:
def etudids_actifs(self) -> set: """Liste les etudids inscrits (incluant DEM et DEF),
"Set des etudids inscrits non démissionnaires et non défaillants" qui ser al'index des dataframes de notes
return {ins.etudid for ins in self.inscriptions if ins.etat == scu.INSCRIT} 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 @cached_property
def etuds_inscriptions(self) -> dict: def etuds_inscriptions(self) -> dict:

View File

@ -705,7 +705,8 @@ def _add_eval_columns(
nb_att = 0 nb_att = 0
sum_notes = 0 sum_notes = 0
notes = [] # liste des notes numeriques, pour calcul histogramme uniquement 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) notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id)
if evaluation.date_debut: if evaluation.date_debut:
@ -734,7 +735,7 @@ def _add_eval_columns(
nb_att += 1 nb_att += 1
# calcul moyenne SANS LES ABSENTS ni les DEMISSIONNAIRES # calcul moyenne SANS LES ABSENTS ni les DEMISSIONNAIRES
if ( if (
(etudid in inscrits) (etudid in etudids_actifs)
and val is not None and val is not None
and val != scu.NOTES_NEUTRALISE and val != scu.NOTES_NEUTRALISE
and val != scu.NOTES_ATTENTE and val != scu.NOTES_ATTENTE
@ -758,7 +759,7 @@ def _add_eval_columns(
comment, comment,
) )
else: 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 # Note manquante mais prise en compte immédiate: affiche ATT
val = scu.NOTES_ATTENTE val = scu.NOTES_ATTENTE
val_fmt = "ATT" val_fmt = "ATT"
@ -875,8 +876,7 @@ def _add_moymod_column(
col_id = "moymod" col_id = "moymod"
formsemestre = FormSemestre.get_formsemestre(formsemestre_id) formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
inscrits = formsemestre.etudids_actifs _, etudids_actifs = formsemestre.etudids_actifs()
nb_notes = 0 nb_notes = 0
sum_notes = 0 sum_notes = 0
notes = [] # liste des notes numeriques, pour calcul histogramme uniquement 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' 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] = scu.fmt_note(val, keep_numeric=keep_numeric)
row["_" + col_id + "_td_attrs"] = ' class="moyenne" ' 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) notes.append(val)
if not np.isnan(val): if not np.isnan(val):
nb_notes = nb_notes + 1 nb_notes = nb_notes + 1
@ -928,7 +928,7 @@ def _add_apc_columns(
# on va y ajouter une clé par UE du semestre # on va y ajouter une clé par UE du semestre
nt: ResultatsSemestreBUT = res_sem.load_formsemestre_results(modimpl.formsemestre) nt: ResultatsSemestreBUT = res_sem.load_formsemestre_results(modimpl.formsemestre)
modimpl_results: ModuleImplResults = nt.modimpls_results[modimpl.id] 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: # les UE dans lesquelles ce module a un coef non nul:
ues_with_coef = nt.modimpl_coefs_df[modimpl.id][ ues_with_coef = nt.modimpl_coefs_df[modimpl.id][
nt.modimpl_coefs_df[modimpl.id] > 0 nt.modimpl_coefs_df[modimpl.id] > 0
@ -946,7 +946,7 @@ def _add_apc_columns(
if ( if (
isinstance(moy_ue, float) isinstance(moy_ue, float)
and not np.isnan(moy_ue) and not np.isnan(moy_ue)
and row["etudid"] in inscrits and row["etudid"] in etudids_actifs
): ):
sum_by_ue[ue.id] += moy_ue sum_by_ue[ue.id] += moy_ue
nb_notes_by_ue[ue.id] += 1 nb_notes_by_ue[ue.id] += 1

View File

@ -541,7 +541,7 @@ def notes_add(
) )
} }
# Les étudiants inscrits au semestre ni DEM ni DEF # 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: for etudid, value in notes:
if check_inscription and ( if check_inscription and (
(etudid not in inscrits) or (etudid not in etudids_actifs) (etudid not in inscrits) or (etudid not in etudids_actifs)

View File

@ -931,6 +931,9 @@ def test_etudiant_bulletin_semestre(api_headers):
# /ScoDoc/api/etudiant/nip/12345/formsemestre/123/bulletin/long/pdf/nosi # /ScoDoc/api/etudiant/nip/12345/formsemestre/123/bulletin/long/pdf/nosi
# TODO voir forme utilisée par ScoDoc en interne: # TODO voir forme utilisée par ScoDoc en interne:
# formsemestre_bulletinetud?formsemestre_id=1263&etudid=16387 # 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): def test_etudiant_groups(api_headers):

View File

@ -2,6 +2,7 @@
Test modèles évaluations avec poids BUT Test modèles évaluations avec poids BUT
et calcul moyennes modules et calcul moyennes modules
""" """
import datetime import datetime
import numpy as np import numpy as np
import pandas as pd import pandas as pd
@ -215,7 +216,8 @@ def test_module_moy(test_client):
etud = G.create_etud(nom="test") etud = G.create_etud(nom="test")
G.inscrit_etudiant(formsemestre_id, etud) G.inscrit_etudiant(formsemestre_id, etud)
etudid = etud["etudid"] 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: # Crée une deuxième évaluation dans le même moduleimpl:
evaluation2_id = G.create_evaluation( evaluation2_id = G.create_evaluation(
moduleimpl_id=evaluation1.moduleimpl_id, 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, evaluation1.id, [(etudid, n1)])
_ = sco_saisie_notes.notes_add(G.default_user, evaluation2.id, [(etudid, n2)]) _ = sco_saisie_notes.notes_add(G.default_user, evaluation2.id, [(etudid, n2)])
# Calcul de la moyenne du module # 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) assert evals_poids.shape == (nb_evals, nb_ues)
etudids, etudids_actifs = formsemestre.etudids_actifs()
mod_results = moy_mod.ModuleImplResultsAPC(modimpl) mod_results = moy_mod.ModuleImplResultsAPC(modimpl, etudids, etudids_actifs)
evals_notes = mod_results.evals_notes evals_notes = mod_results.evals_notes
assert evals_notes[evaluation1.id].dtype == np.float64 assert evals_notes[evaluation1.id].dtype == np.float64