1
0
forked from ScoDoc/ScoDoc

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

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
# sur toutes les UE)
default_poids = {
mod.id: 1.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
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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