forked from ScoDoc/ScoDoc
Calcul moyennes modules avec inscriptions partielles, ATT, EXC, ABS, mode immédiat. (+ tests unit.)
This commit is contained in:
parent
9927368680
commit
36b432839a
@ -166,19 +166,19 @@ class ResultatsSemestreBUT:
|
|||||||
|
|
||||||
def etud_eval_results(self, etud, e) -> dict:
|
def etud_eval_results(self, etud, e) -> dict:
|
||||||
"dict resultats d'un étudiant à une évaluation"
|
"dict resultats d'un étudiant à une évaluation"
|
||||||
eval_notes = self.modimpls_evals_notes[e.moduleimpl_id][str(e.id)] # pd.Series
|
eval_notes = self.modimpls_evals_notes[e.moduleimpl_id][e.id] # pd.Series
|
||||||
notes_ok = eval_notes.where(eval_notes > -1000).dropna()
|
notes_ok = eval_notes.where(eval_notes > -1000).dropna()
|
||||||
d = {
|
d = {
|
||||||
"id": e.id,
|
"id": e.id,
|
||||||
"description": e.description,
|
"description": e.description,
|
||||||
"date": e.jour.isoformat(),
|
"date": e.jour.isoformat() if e.jour else None,
|
||||||
"heure_debut": e.heure_debut.strftime("%H:%M") if e.heure_debut else None,
|
"heure_debut": e.heure_debut.strftime("%H:%M") if e.heure_debut else None,
|
||||||
"heure_fin": e.heure_fin.strftime("%H:%M") if e.heure_debut else None,
|
"heure_fin": e.heure_fin.strftime("%H:%M") if e.heure_debut else None,
|
||||||
"coef": e.coefficient,
|
"coef": e.coefficient,
|
||||||
"poids": {p.ue.acronyme: p.poids for p in e.ue_poids},
|
"poids": {p.ue.acronyme: p.poids for p in e.ue_poids},
|
||||||
"note": {
|
"note": {
|
||||||
"value": fmt_note(
|
"value": fmt_note(
|
||||||
self.modimpls_evals_notes[e.moduleimpl_id][str(e.id)][etud.id]
|
self.modimpls_evals_notes[e.moduleimpl_id][e.id][etud.id]
|
||||||
),
|
),
|
||||||
"min": fmt_note(notes_ok.min()),
|
"min": fmt_note(notes_ok.min()),
|
||||||
"max": fmt_note(notes_ok.max()),
|
"max": fmt_note(notes_ok.max()),
|
||||||
|
@ -81,63 +81,92 @@ def check_moduleimpl_conformity(
|
|||||||
return check
|
return check
|
||||||
|
|
||||||
|
|
||||||
def df_load_modimpl_notes(moduleimpl_id: int) -> pd.DataFrame:
|
def df_load_modimpl_notes(moduleimpl_id: int) -> tuple:
|
||||||
"""Construit un dataframe avec toutes les notes des évaluations du module.
|
"""Construit un dataframe avec toutes les notes des évaluations du module.
|
||||||
colonnes: evaluation_id (le nom de la colonne est l'evaluation_id en str)
|
colonnes: le nom de la colonne est l'evaluation_id (int)
|
||||||
index (lignes): etudid
|
index (lignes): etudid (int)
|
||||||
|
|
||||||
Résultat: (evals_notes, liste de évaluations du moduleimpl)
|
Résultat: (evals_notes, liste de évaluations du moduleimpl,
|
||||||
|
liste de booleens indiquant si l'évaluation est "complete")
|
||||||
|
|
||||||
L'ensemble des étudiants est celui des inscrits au SEMESTRE.
|
L'ensemble des étudiants est celui des inscrits au SEMESTRE.
|
||||||
|
|
||||||
Les notes renvoyées sont "brutes" (séries de floats) et peuvent prendre les valeurs:
|
Les notes renvoyées sont "brutes" (séries de floats) et peuvent prendre les valeurs:
|
||||||
note : float (valeur enregistrée brute, non normalisée sur 20)
|
note : float (valeur enregistrée brute, non normalisée sur 20)
|
||||||
pas de note: NaN
|
pas de note: NaN (rien en bd, ou étudiant non inscrit au module)
|
||||||
absent: NaN
|
absent: NOTES_ABSENCE (NULL en bd)
|
||||||
excusé: NOTES_NEUTRALISE (voir sco_utils)
|
excusé: NOTES_NEUTRALISE (voir sco_utils)
|
||||||
attente: NOTES_ATTENTE
|
attente: NOTES_ATTENTE
|
||||||
|
|
||||||
|
L'évaluation "complete" (prise en compte dans les calculs) si:
|
||||||
|
- soit tous les étudiants inscrits au module ont des notes
|
||||||
|
- soit elle a été déclarée "à prise ne compte immédiate" (publish_incomplete)
|
||||||
|
|
||||||
N'utilise pas de cache ScoDoc.
|
N'utilise pas de cache ScoDoc.
|
||||||
"""
|
"""
|
||||||
# L'index du dataframe est la liste des étudiants inscrits au semestre:
|
# L'index du dataframe est la liste des étudiants inscrits au semestre:
|
||||||
etudids = [
|
etudids = [
|
||||||
e.etudid for e in ModuleImpl.query.get(moduleimpl_id).formsemestre.inscriptions
|
e.etudid for e in ModuleImpl.query.get(moduleimpl_id).formsemestre.inscriptions
|
||||||
]
|
]
|
||||||
evaluations = Evaluation.query.filter_by(moduleimpl_id=moduleimpl_id)
|
evaluations = Evaluation.query.filter_by(moduleimpl_id=moduleimpl_id).all()
|
||||||
|
if evaluations:
|
||||||
|
nb_inscrits_module = len(evaluations[0].moduleimpl.inscriptions)
|
||||||
|
else:
|
||||||
|
nb_inscrits_module = 0
|
||||||
evals_notes = pd.DataFrame(index=etudids, dtype=float) # empty df with all students
|
evals_notes = pd.DataFrame(index=etudids, dtype=float) # empty df with all students
|
||||||
|
evaluations_completes = []
|
||||||
for evaluation in evaluations:
|
for evaluation in evaluations:
|
||||||
eval_df = pd.read_sql_query(
|
eval_df = pd.read_sql_query(
|
||||||
"""SELECT etudid, value AS "%(evaluation_id)s"
|
"""SELECT n.etudid, n.value AS "%(evaluation_id)s"
|
||||||
FROM notes_notes
|
FROM notes_notes n, notes_moduleimpl_inscription i
|
||||||
WHERE evaluation_id=%(evaluation_id)s""",
|
WHERE evaluation_id=%(evaluation_id)s
|
||||||
|
AND n.etudid = i.etudid
|
||||||
|
AND i.moduleimpl_id = %(moduleimpl_id)s
|
||||||
|
""",
|
||||||
db.engine,
|
db.engine,
|
||||||
params={"evaluation_id": evaluation.id},
|
params={
|
||||||
|
"evaluation_id": evaluation.id,
|
||||||
|
"moduleimpl_id": evaluation.moduleimpl.id,
|
||||||
|
},
|
||||||
index_col="etudid",
|
index_col="etudid",
|
||||||
dtype=np.float64,
|
dtype=np.float64,
|
||||||
)
|
)
|
||||||
|
evaluations_completes.append(
|
||||||
|
len(eval_df) == nb_inscrits_module or evaluation.publish_incomplete
|
||||||
|
)
|
||||||
|
# NULL en base => ABS
|
||||||
|
eval_df.fillna(scu.NOTES_ABSENCE, inplace=True)
|
||||||
|
# Ce merge met à NULL les élements non présents
|
||||||
|
# (notes non saisies ou etuds non inscrits au module):
|
||||||
evals_notes = evals_notes.merge(
|
evals_notes = evals_notes.merge(
|
||||||
eval_df, how="outer", left_index=True, right_index=True
|
eval_df, how="outer", left_index=True, right_index=True
|
||||||
)
|
)
|
||||||
|
# Force columns names to integers (evaluation ids)
|
||||||
return evals_notes, evaluations
|
evals_notes.columns = pd.Int64Index(
|
||||||
|
[int(x) for x in evals_notes.columns], dtype="int64"
|
||||||
|
)
|
||||||
|
return evals_notes, evaluations, evaluations_completes
|
||||||
|
|
||||||
|
|
||||||
def compute_module_moy(
|
def compute_module_moy(
|
||||||
evals_notes_df: pd.DataFrame,
|
evals_notes_df: pd.DataFrame,
|
||||||
evals_poids_df: pd.DataFrame,
|
evals_poids_df: pd.DataFrame,
|
||||||
evaluations: list,
|
evaluations: list,
|
||||||
|
evaluations_completes: list,
|
||||||
) -> pd.DataFrame:
|
) -> pd.DataFrame:
|
||||||
"""Calcule les moyennes des étudiants dans ce module
|
"""Calcule les moyennes des étudiants dans ce module
|
||||||
|
|
||||||
- evals_notes : DataFrame, colonnes: EVALS, Lignes: etudid
|
- evals_notes : DataFrame, colonnes: EVALS, Lignes: etudid
|
||||||
valeur: notes brutes, float ou NOTES_ATTENTE ou NOTES_NEUTRALISE
|
valeur: notes brutes, float ou NOTES_ATTENTE, NOTES_NEUTRALISE, NOTES_ABSENCE
|
||||||
Les NaN désignent les ABS.
|
Les NaN désignent les notes manquantes (non saisies).
|
||||||
|
|
||||||
- evals_poids: DataFrame, colonnes: UEs, Lignes: EVALs
|
- evals_poids: DataFrame, colonnes: UEs, Lignes: EVALs
|
||||||
|
|
||||||
- evaluations: séquence d'évaluations (utilisées pour le coef et le barème)
|
- evaluations: séquence d'évaluations (utilisées pour le coef et le barème)
|
||||||
|
|
||||||
|
- evaluations_completes: séquence de booléens indiquaant les évals à prendre
|
||||||
|
en compte.
|
||||||
|
|
||||||
Résultat: DataFrame, colonnes UE, lignes etud
|
Résultat: DataFrame, colonnes UE, lignes etud
|
||||||
= la note de l'étudiant dans chaque UE pour ce module.
|
= la note de l'étudiant dans chaque UE pour ce module.
|
||||||
ou NaN si les évaluations (dans lesquelles l'étudiant à des notes)
|
ou NaN si les évaluations (dans lesquelles l'étudiant à des notes)
|
||||||
@ -146,26 +175,34 @@ def compute_module_moy(
|
|||||||
nb_etuds, nb_evals = evals_notes_df.shape
|
nb_etuds, nb_evals = evals_notes_df.shape
|
||||||
nb_ues = evals_poids_df.shape[1]
|
nb_ues = evals_poids_df.shape[1]
|
||||||
assert evals_poids_df.shape[0] == nb_evals # compat notes/poids
|
assert evals_poids_df.shape[0] == nb_evals # compat notes/poids
|
||||||
evals_coefs = np.array([e.coefficient for e in evaluations], dtype=float).reshape(
|
# Coefficients des évaluations, met à zéro ceux des évals incomplètes:
|
||||||
-1, 1
|
evals_coefs = (
|
||||||
)
|
np.array(
|
||||||
|
[e.coefficient for e in evaluations],
|
||||||
|
dtype=float,
|
||||||
|
)
|
||||||
|
* evaluations_completes
|
||||||
|
).reshape(-1, 1)
|
||||||
evals_poids = evals_poids_df.values * evals_coefs
|
evals_poids = evals_poids_df.values * evals_coefs
|
||||||
# -> evals_poids_arr shape : (nb_evals, nb_ues)
|
# -> evals_poids shape : (nb_evals, nb_ues)
|
||||||
assert evals_poids.shape == (nb_evals, nb_ues)
|
assert evals_poids.shape == (nb_evals, nb_ues)
|
||||||
# Remet les notes sur 20 (sauf notes spéciales <= -1000):
|
# Remplace les notes ATT, EXC, ABS, NaN par zéro et mets les notes sur 20:
|
||||||
evals_notes = np.where(
|
evals_notes = np.where(
|
||||||
evals_notes_df.values > -1000, evals_notes_df.values, 0.0
|
evals_notes_df.values > scu.NOTES_ABSENCE, evals_notes_df.values, 0.0
|
||||||
) / [e.note_max / 20.0 for e in evaluations]
|
) / [e.note_max / 20.0 for e in evaluations]
|
||||||
# Les poids des évals pour les étudiant: là où il a des notes non neutralisées
|
# Les poids des évals pour les étudiant: là où il a des notes non neutralisées
|
||||||
# Attention: les NaN (codant les absents) sont remplacés par des 0 dans
|
# (ABS n'est pas neutralisée, mais ATTENTE et NEUTRALISE oui)
|
||||||
# evals_notes_arr mais pas dans evals_poids_etuds_arr
|
# Note: les NaN sont remplacés par des 0 dans evals_notes
|
||||||
# (la comparaison est toujours false face à un NaN)
|
# et dans dans evals_poids_etuds
|
||||||
|
# (rappel: la comparaison est toujours false face à un NaN)
|
||||||
# shape: (nb_etuds, nb_evals, nb_ues)
|
# shape: (nb_etuds, nb_evals, nb_ues)
|
||||||
poids_stacked = np.stack([evals_poids] * nb_etuds)
|
poids_stacked = np.stack([evals_poids] * nb_etuds)
|
||||||
evals_poids_etuds = np.where(
|
evals_poids_etuds = np.where(
|
||||||
np.stack([evals_notes_df.values] * nb_ues, axis=2) <= -1000.0, 0, poids_stacked
|
np.stack([evals_notes_df.values] * nb_ues, axis=2) > scu.NOTES_NEUTRALISE,
|
||||||
|
poids_stacked,
|
||||||
|
0,
|
||||||
)
|
)
|
||||||
# Calcule la moyenne pondérée sur les notes disponibles
|
# Calcule la moyenne pondérée sur les notes disponibles:
|
||||||
evals_notes_stacked = np.stack([evals_notes] * nb_ues, axis=2)
|
evals_notes_stacked = np.stack([evals_notes] * nb_ues, axis=2)
|
||||||
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
||||||
etuds_moy_module = np.sum(
|
etuds_moy_module = np.sum(
|
||||||
|
@ -130,10 +130,12 @@ def notes_sem_load_cube(formsemestre):
|
|||||||
modimpls_evaluations = {} # modimpl.id : liste des évaluations
|
modimpls_evaluations = {} # modimpl.id : liste des évaluations
|
||||||
modimpls_notes = []
|
modimpls_notes = []
|
||||||
for modimpl in formsemestre.modimpls:
|
for modimpl in formsemestre.modimpls:
|
||||||
evals_notes, evaluations = moy_mod.df_load_modimpl_notes(modimpl.id)
|
evals_notes, evaluations, evaluations_completes = moy_mod.df_load_modimpl_notes(
|
||||||
|
modimpl.id
|
||||||
|
)
|
||||||
evals_poids, ues = moy_mod.df_load_evaluations_poids(modimpl.id)
|
evals_poids, ues = moy_mod.df_load_evaluations_poids(modimpl.id)
|
||||||
etuds_moy_module = moy_mod.compute_module_moy(
|
etuds_moy_module = moy_mod.compute_module_moy(
|
||||||
evals_notes, evals_poids, evaluations
|
evals_notes, evals_poids, evaluations, evaluations_completes
|
||||||
)
|
)
|
||||||
modimpls_evals_poids[modimpl.id] = evals_poids
|
modimpls_evals_poids[modimpl.id] = evals_poids
|
||||||
modimpls_evals_notes[modimpl.id] = evals_notes
|
modimpls_evals_notes[modimpl.id] = evals_notes
|
||||||
|
@ -528,18 +528,21 @@ def module_edit(module_id=None):
|
|||||||
("formation_id", {"input_type": "hidden"}),
|
("formation_id", {"input_type": "hidden"}),
|
||||||
("ue_id", {"input_type": "hidden"}),
|
("ue_id", {"input_type": "hidden"}),
|
||||||
("module_id", {"input_type": "hidden"}),
|
("module_id", {"input_type": "hidden"}),
|
||||||
(
|
|
||||||
"ue_matiere_id",
|
|
||||||
{
|
|
||||||
"input_type": "menu",
|
|
||||||
"title": "Matière",
|
|
||||||
"explanation": "un module appartient à une seule matière.",
|
|
||||||
"labels": mat_names,
|
|
||||||
"allowed_values": ue_mat_ids,
|
|
||||||
"enabled": unlocked,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
if not is_apc:
|
||||||
|
descr += [
|
||||||
|
(
|
||||||
|
"ue_matiere_id",
|
||||||
|
{
|
||||||
|
"input_type": "menu",
|
||||||
|
"title": "Matière",
|
||||||
|
"explanation": "un module appartient à une seule matière.",
|
||||||
|
"labels": mat_names,
|
||||||
|
"allowed_values": ue_mat_ids,
|
||||||
|
"enabled": unlocked,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
if is_apc:
|
if is_apc:
|
||||||
# le semestre du module est toujours celui de son UE
|
# le semestre du module est toujours celui de son UE
|
||||||
descr += [
|
descr += [
|
||||||
|
@ -810,8 +810,12 @@ def _add_apc_columns(
|
|||||||
# rows est une liste de dict avec une clé "etudid"
|
# rows est une liste de dict avec une clé "etudid"
|
||||||
# on va y ajouter une clé par UE du semestre
|
# on va y ajouter une clé par UE du semestre
|
||||||
|
|
||||||
evals_notes, evaluations = moy_mod.df_load_modimpl_notes(moduleimpl_id)
|
evals_notes, evaluations, evaluations_completes = moy_mod.df_load_modimpl_notes(
|
||||||
etuds_moy_module = moy_mod.compute_module_moy(evals_notes, evals_poids, evaluations)
|
moduleimpl_id
|
||||||
|
)
|
||||||
|
etuds_moy_module = moy_mod.compute_module_moy(
|
||||||
|
evals_notes, evals_poids, evaluations, evaluations_completes
|
||||||
|
)
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
@ -822,3 +826,4 @@ def _add_apc_columns(
|
|||||||
col_id = f"moy_ue_{ue.id}"
|
col_id = f"moy_ue_{ue.id}"
|
||||||
titles[col_id] = ue.acronyme
|
titles[col_id] = ue.acronyme
|
||||||
columns_ids.append(col_id)
|
columns_ids.append(col_id)
|
||||||
|
row_coefs[f"moy_ue_{ue.id}"] = "m"
|
||||||
|
@ -66,11 +66,11 @@ import sco_version
|
|||||||
NOTES_PRECISION = 1e-4 # evite eventuelles erreurs d'arrondis
|
NOTES_PRECISION = 1e-4 # evite eventuelles erreurs d'arrondis
|
||||||
NOTES_MIN = 0.0 # valeur minimale admise pour une note (sauf malus, dans [-20, 20])
|
NOTES_MIN = 0.0 # valeur minimale admise pour une note (sauf malus, dans [-20, 20])
|
||||||
NOTES_MAX = 1000.0
|
NOTES_MAX = 1000.0
|
||||||
|
NOTES_ABSENCE = -999.0 # absences dans les DataFrames, NULL en base
|
||||||
NOTES_NEUTRALISE = -1000.0 # notes non prises en comptes dans moyennes
|
NOTES_NEUTRALISE = -1000.0 # notes non prises en comptes dans moyennes
|
||||||
NOTES_SUPPRESS = -1001.0 # note a supprimer
|
NOTES_SUPPRESS = -1001.0 # note a supprimer
|
||||||
NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutralisee)
|
NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutralisee)
|
||||||
|
|
||||||
|
|
||||||
# Types de modules
|
# Types de modules
|
||||||
class ModuleType(IntEnum):
|
class ModuleType(IntEnum):
|
||||||
"""Code des types de module."""
|
"""Code des types de module."""
|
||||||
|
@ -12,7 +12,12 @@ from app.comp import moy_mod
|
|||||||
from app.comp import moy_ue
|
from app.comp import moy_ue
|
||||||
from app.models import Evaluation
|
from app.models import Evaluation
|
||||||
from app.scodoc import sco_saisie_notes
|
from app.scodoc import sco_saisie_notes
|
||||||
from app.scodoc.sco_utils import NOTES_ATTENTE, NOTES_NEUTRALISE
|
from app.scodoc.sco_utils import (
|
||||||
|
NOTES_ATTENTE,
|
||||||
|
NOTES_NEUTRALISE,
|
||||||
|
NOTES_SUPPRESS,
|
||||||
|
NOTES_PRECISION,
|
||||||
|
)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
mapp.set_sco_dept("RT")
|
mapp.set_sco_dept("RT")
|
||||||
@ -23,6 +28,10 @@ login_user(admin_user)
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def same_note(x, y):
|
||||||
|
return abs(x - y) < NOTES_PRECISION
|
||||||
|
|
||||||
|
|
||||||
def test_evaluation_poids(test_client):
|
def test_evaluation_poids(test_client):
|
||||||
"""Association de poids vers les UE"""
|
"""Association de poids vers les UE"""
|
||||||
G, formation_id, ue1_id, ue2_id, ue3_id, module_ids = setup.build_formation_test()
|
G, formation_id, ue1_id, ue2_id, ue3_id, module_ids = setup.build_formation_test()
|
||||||
@ -140,27 +149,33 @@ def test_module_moy_elem(test_client):
|
|||||||
"""Vérification calcul moyenne d'un module
|
"""Vérification calcul moyenne d'un module
|
||||||
(notes entrées dans un DataFrame sans passer par ScoDoc)
|
(notes entrées dans un DataFrame sans passer par ScoDoc)
|
||||||
"""
|
"""
|
||||||
|
# Création de deux évaluations:
|
||||||
|
e1 = Evaluation(note_max=20.0, coefficient=1.0)
|
||||||
|
e2 = Evaluation(note_max=20.0, coefficient=1.0)
|
||||||
|
db.session.add(e1)
|
||||||
|
db.session.add(e2)
|
||||||
|
db.session.commit()
|
||||||
# Repris du notebook CalculNotesBUT.ipynb
|
# Repris du notebook CalculNotesBUT.ipynb
|
||||||
data = [ # Les notes de chaque étudiant dans les 2 evals:
|
data = [ # Les notes de chaque étudiant dans les 2 evals:
|
||||||
{
|
{
|
||||||
"EVAL1": 11.0,
|
e1.id: 11.0,
|
||||||
"EVAL2": 16.0,
|
e2.id: 16.0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"EVAL1": np.NaN, # une absence (NaN)
|
e1.id: None, # une absence
|
||||||
"EVAL2": 17.0,
|
e2.id: 17.0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"EVAL1": 13.0,
|
e1.id: 13.0,
|
||||||
"EVAL2": NOTES_NEUTRALISE, # une abs EXC
|
e2.id: NOTES_NEUTRALISE, # une abs EXC
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"EVAL1": 14.0,
|
e1.id: 14.0,
|
||||||
"EVAL2": 19.0,
|
e2.id: 19.0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"EVAL1": NOTES_ATTENTE, # une ATT (traitée comme EXC)
|
e1.id: NOTES_ATTENTE, # une ATT (traitée comme EXC)
|
||||||
"EVAL2": np.NaN, # et une ABS
|
e2.id: None, # et une ABS
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
evals_notes_df = pd.DataFrame(
|
evals_notes_df = pd.DataFrame(
|
||||||
@ -171,13 +186,10 @@ def test_module_moy_elem(test_client):
|
|||||||
{"UE1": 1, "UE2": 0, "UE3": 0},
|
{"UE1": 1, "UE2": 0, "UE3": 0},
|
||||||
{"UE1": 2, "UE2": 5, "UE3": 0},
|
{"UE1": 2, "UE2": 5, "UE3": 0},
|
||||||
]
|
]
|
||||||
evals_poids_df = pd.DataFrame(data, index=["EVAL1", "EVAL2"], dtype=float)
|
evals_poids_df = pd.DataFrame(data, index=[e1.id, e2.id], dtype=float)
|
||||||
evaluations = [
|
evaluations = [e1, e2]
|
||||||
Evaluation(note_max=20.0, coefficient=1.0),
|
|
||||||
Evaluation(note_max=20.0, coefficient=1.0),
|
|
||||||
]
|
|
||||||
etuds_moy_module_df = moy_mod.compute_module_moy(
|
etuds_moy_module_df = moy_mod.compute_module_moy(
|
||||||
evals_notes_df.fillna(0.0), evals_poids_df, evaluations
|
evals_notes_df.fillna(0.0), evals_poids_df, evaluations, [True, True]
|
||||||
)
|
)
|
||||||
NAN = 666.0 # pour pouvoir comparer NaN et NaN (car NaN != NaN)
|
NAN = 666.0 # pour pouvoir comparer NaN et NaN (car NaN != NaN)
|
||||||
r = etuds_moy_module_df.fillna(NAN)
|
r = etuds_moy_module_df.fillna(NAN)
|
||||||
@ -235,10 +247,14 @@ def test_module_moy(test_client):
|
|||||||
# Calcul de la moyenne du module
|
# Calcul de la moyenne du module
|
||||||
evals_poids, ues = moy_mod.df_load_evaluations_poids(moduleimpl_id)
|
evals_poids, ues = moy_mod.df_load_evaluations_poids(moduleimpl_id)
|
||||||
assert evals_poids.shape == (nb_evals, nb_ues)
|
assert evals_poids.shape == (nb_evals, nb_ues)
|
||||||
evals_notes, evaluations = moy_mod.df_load_modimpl_notes(moduleimpl_id)
|
evals_notes, evaluations, evaluations_completes = moy_mod.df_load_modimpl_notes(
|
||||||
assert evals_notes[str(evaluations[0].id)].dtype == np.float64
|
moduleimpl_id
|
||||||
|
)
|
||||||
|
assert evals_notes[evaluations[0].id].dtype == np.float64
|
||||||
|
assert evaluation1.id == evaluations[0].id
|
||||||
|
assert evaluation2.id == evaluations[1].id
|
||||||
etuds_moy_module = moy_mod.compute_module_moy(
|
etuds_moy_module = moy_mod.compute_module_moy(
|
||||||
evals_notes, evals_poids, evaluations
|
evals_notes, evals_poids, evaluations, evaluations_completes
|
||||||
)
|
)
|
||||||
return etuds_moy_module
|
return etuds_moy_module
|
||||||
|
|
||||||
@ -254,7 +270,7 @@ def test_module_moy(test_client):
|
|||||||
moy_ue3 = etuds_moy_module[ue3.id][etudid]
|
moy_ue3 = etuds_moy_module[ue3.id][etudid]
|
||||||
assert np.isnan(moy_ue3) # car les poids vers UE3 sont nuls
|
assert np.isnan(moy_ue3) # car les poids vers UE3 sont nuls
|
||||||
|
|
||||||
# --- Une Note ABS (comptée comme zéro)
|
# --- Une note ABS (comptée comme zéro)
|
||||||
etuds_moy_module = change_notes(None, note2)
|
etuds_moy_module = change_notes(None, note2)
|
||||||
assert etuds_moy_module[ue1.id][etudid] == (note2 * e2p1 * coef_e2) / sum_copo1
|
assert etuds_moy_module[ue1.id][etudid] == (note2 * e2p1 * coef_e2) / sum_copo1
|
||||||
assert etuds_moy_module[ue2.id][etudid] == (note2 * e2p2 * coef_e2) / sum_copo2
|
assert etuds_moy_module[ue2.id][etudid] == (note2 * e2p2 * coef_e2) / sum_copo2
|
||||||
@ -287,3 +303,11 @@ def test_module_moy(test_client):
|
|||||||
assert moy_ue2 == ((note1 * e1p2 * coef_e1) + (note2 * e2p2 * coef_e2)) / sum_copo2
|
assert moy_ue2 == ((note1 * e1p2 * coef_e1) + (note2 * e2p2 * coef_e2)) / sum_copo2
|
||||||
moy_ue3 = etuds_moy_module[ue3.id][etudid]
|
moy_ue3 = etuds_moy_module[ue3.id][etudid]
|
||||||
assert np.isnan(moy_ue3) # car les poids vers UE3 sont nuls
|
assert np.isnan(moy_ue3) # car les poids vers UE3 sont nuls
|
||||||
|
# --- Note manquante à l'éval. 1
|
||||||
|
note_2_37 = note2 / 20 * 37
|
||||||
|
etuds_moy_module = change_notes(NOTES_SUPPRESS, note_2_37)
|
||||||
|
assert same_note(etuds_moy_module[ue2.id][etudid], note2)
|
||||||
|
# --- Prise en compte immédiate:
|
||||||
|
evaluation1.publish_incomplete = True
|
||||||
|
etuds_moy_module = change_notes(NOTES_SUPPRESS, note_2_37)
|
||||||
|
assert same_note(etuds_moy_module[ue2.id][etudid], note2)
|
||||||
|
@ -94,7 +94,7 @@ def test_ue_moy(test_client):
|
|||||||
# EXC à un module
|
# EXC à un module
|
||||||
n1, n2 = 5.0, NOTES_NEUTRALISE
|
n1, n2 = 5.0, NOTES_NEUTRALISE
|
||||||
etud_moy_ue = change_notes(n1, n2)
|
etud_moy_ue = change_notes(n1, n2)
|
||||||
# Pour le moment, une note NEUTRALISE var entrainer le non calcul
|
# Pour le moment, une note NEUTRALISE va entrainer le non-calcul
|
||||||
# des moyennes.
|
# des moyennes.
|
||||||
assert np.isnan(etud_moy_ue.values).all()
|
assert np.isnan(etud_moy_ue.values).all()
|
||||||
# Désinscrit l'étudiant du module 2:
|
# Désinscrit l'étudiant du module 2:
|
||||||
|
Loading…
Reference in New Issue
Block a user