""" Test modèles évaluations avec poids BUT et calcul moyennes modules """ import numpy as np import pandas as pd from app.models.modules import Module from app.models.moduleimpls import ModuleImpl from tests.unit import setup from app import db from app import models from app.comp import moy_mod from app.comp import moy_ue from app.models import Evaluation from app.scodoc import sco_saisie_notes from app.scodoc.sco_utils import ( NOTES_ATTENTE, NOTES_NEUTRALISE, NOTES_SUPPRESS, NOTES_PRECISION, ) """ mapp.set_sco_dept("RT") from app.auth.models import get_super_admin admin_user = get_super_admin() ctx.push() login_user(admin_user) """ def same_note(x, y): return abs(x - y) < NOTES_PRECISION def test_evaluation_poids(test_client): """Association de poids vers les UE""" G, formation_id, ue1_id, ue2_id, ue3_id, module_ids = setup.build_formation_test() sem = G.create_formsemestre( formation_id=formation_id, semestre_id=1, date_debut="01/01/2021", date_fin="30/06/2021", ) # formsemestre_id=716 mi = G.create_moduleimpl( module_id=module_ids[0], formsemestre_id=sem["formsemestre_id"], ) moduleimpl_id = mi["id"] _e1 = G.create_evaluation( moduleimpl_id=moduleimpl_id, jour="01/01/2021", description="evaluation 1", coefficient=0, ) evaluation_id = _e1["evaluation_id"] # evaluation_id=25246 # ue1_id=1684 # formation_id=199 # e1 = models.Evaluation.query.get(evaluation_id) ue1 = models.UniteEns.query.get(ue1_id) assert e1.ue_poids == [] p1 = 3.14 e1.set_ue_poids(ue1, p1) db.session.commit() assert e1.get_ue_poids_dict()[ue1_id] == p1 ues = models.UniteEns.query.filter_by( formation_id=formation_id, semestre_idx=2 ).all() poids = [1.0, 2.0, 3.0] for (ue, p) in zip(ues, poids): e1.set_ue_poids(ue, p) assert len(e1.ue_poids) == len(ues) assert e1.get_ue_poids_dict()[ues[1].id] == poids[1] e1.set_ue_poids(ue1, p1) db.session.commit() poids2 = [10, 20] e1.update_ue_poids_dict({ue.id: p for (ue, p) in zip(ues[:-1], poids2)}) assert e1.get_ue_poids_dict()[ues[0].id] == poids2[0] assert e1.get_ue_poids_dict()[ues[1].id] == poids2[1] assert e1.get_ue_poids_dict()[ues[2].id] == poids[2] # Delete UE db.session.delete(ues[2]) db.session.commit() # Delete eval db.session.delete(e1) db.session.commit() assert len(models.EvaluationUEPoids.query.all()) == 0 def test_modules_coefs(test_client): """Coefs vers les UE (BUT)""" G, formation_id, ue1_id, ue2_id, ue3_id, module_ids = setup.build_formation_test() ue1 = models.UniteEns.query.get(ue1_id) ue2 = models.UniteEns.query.get(ue2_id) mod = models.Module.query.get(module_ids[0]) coef = 2.5 mod.set_ue_coef(ue1, coef) db.session.commit() assert mod.ue_coefs[0].coef == coef mod.set_ue_coef(ue2, 2 * coef) db.session.commit() assert set(mod.get_ue_coef_dict().values()) == {coef, 2 * coef} assert set(mod.get_ue_coef_dict().keys()) == {ue1_id, ue2_id} mod.delete_ue_coef(ue1) db.session.commit() assert len(mod.ue_coefs) == 1 # Gestion des coefs nuls: mod.set_ue_coef(ue2, 0.0) db.session.commit() assert len(mod.ue_coefs) == 0 def test_module_conformity(test_client): """Vérification coefficients module<->UE vs poids des évaluations""" ( _, formation_id, _, evaluation_ids, ue1, ue2, ue3, ) = setup.build_modules_with_evaluations() semestre_idx = 2 nb_ues = 3 # 3 UEs dans ce test nb_mods = 1 # 1 seul module nb_evals = 1 # 1 seule evaluation pour l'instant p1, p2, p3 = 1.0, 2.0, 0.0 # poids de l'éval vers les UE 1, 2 et 3 evaluation = models.Evaluation.query.get(evaluation_ids[0]) evaluation.set_ue_poids_dict({ue1.id: p1, ue2.id: p2, ue3.id: p3}) assert evaluation.get_ue_poids_dict() == {ue1.id: p1, ue2.id: p2, ue3.id: p3} # On n'est pas conforme car p3 est nul alors que c3 est non nul modules_coefficients, _ues, _modules = moy_ue.df_load_module_coefs( formation_id, semestre_idx ) assert isinstance(modules_coefficients, pd.DataFrame) assert modules_coefficients.shape == (nb_ues, nb_mods) evals_poids, ues = moy_mod.load_evaluations_poids(evaluation.moduleimpl_id) assert isinstance(evals_poids, pd.DataFrame) assert len(ues) == nb_ues assert all(evals_poids.dtypes == np.float64) assert evals_poids.shape == (nb_evals, nb_ues) assert not moy_mod.moduleimpl_is_conforme( evaluation.moduleimpl, evals_poids, modules_coefficients ) # En ScoDoc 9.2 test ne peut plus exister car compute_module_moy # est maintenant incorporé dans la classe ModuleImplResultsAPC # def test_module_moy_elem(test_client): # """Vérification calcul moyenne d'un module # (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.flush() # # Repris du notebook CalculNotesBUT.ipynb # data = [ # Les notes de chaque étudiant dans les 2 evals: # { # e1.id: 11.0, # e2.id: 16.0, # }, # { # e1.id: None, # une absence # e2.id: 17.0, # }, # { # e1.id: 13.0, # e2.id: NOTES_NEUTRALISE, # une abs EXC # }, # { # e1.id: 14.0, # e2.id: 19.0, # }, # { # e1.id: NOTES_ATTENTE, # une ATT (traitée comme EXC) # e2.id: None, # et une ABS # }, # ] # evals_notes_df = pd.DataFrame( # data, index=["etud1", "etud2", "etud3", "etud4", "etud5"] # ) # # Poids des évaluations (1 ligne / évaluation) # data = [ # {"UE1": 1, "UE2": 0, "UE3": 0}, # {"UE1": 2, "UE2": 5, "UE3": 0}, # ] # evals_poids_df = pd.DataFrame(data, index=[e1.id, e2.id], dtype=float) # evaluations = [e1, e2] # etuds_moy_module_df = moy_mod.compute_module_moy( # evals_notes_df.fillna(0.0), evals_poids_df, evaluations, [True, True] # ) # NAN = 666.0 # pour pouvoir comparer NaN et NaN (car NaN != NaN) # r = etuds_moy_module_df.fillna(NAN) # assert tuple(r.loc["etud1"]) == (14 + 1 / 3, 16.0, NAN) # assert tuple(r.loc["etud2"]) == (11 + 1 / 3, 17.0, NAN) # assert tuple(r.loc["etud3"]) == (13, NAN, NAN) # assert tuple(r.loc["etud4"]) == (17 + 1 / 3, 19, NAN) # assert tuple(r.loc["etud5"]) == (0.0, 0.0, NAN) # # note: les notes UE3 sont toutes NAN car les poids vers l'UE3 sont nuls def test_module_moy(test_client): """Test calcul moyenne module avec saisie des notes via ScoDoc""" coef_e1, coef_e2 = 7.0, 11.0 # coefficients des évaluations ( G, formation_id, sem, evaluation1_ids, ue1, ue2, ue3, ) = setup.build_modules_with_evaluations() etud = G.create_etud(nom="test") G.inscrit_etudiant(sem, etud) etudid = etud["etudid"] evaluation1 = models.Evaluation.query.get(evaluation1_ids[0]) # Crée une deuxième évaluation dans le même moduleimpl: evaluation2_id = G.create_evaluation( moduleimpl_id=evaluation1.moduleimpl_id, jour="02/01/2021", description="evaluation 2", coefficient=coef_e2, )["evaluation_id"] evaluation2 = models.Evaluation.query.get(evaluation2_id) # Coefficients de l'eval 1 evaluation1.coefficient = coef_e1 # Poids des évaluations: e1p1, e1p2, e1p3 = 1.0, 2.0, 0.0 # poids de l'éval 1 vers les UE 1, 2 et 3 e2p1, e2p2, e2p3 = 0.0, 1.0, 0.0 # poids de l'éval 2 vers les UE evaluation1.set_ue_poids_dict({ue1.id: e1p1, ue2.id: e1p2, ue3.id: e1p3}) evaluation2.set_ue_poids_dict({ue1.id: e2p1, ue2.id: e2p2, ue3.id: e2p3}) # Vérifications moduleimpl_id = evaluation1.moduleimpl_id nb_evals = models.Evaluation.query.filter_by(moduleimpl_id=moduleimpl_id).count() assert nb_evals == 2 nb_ues = 3 modimpl = ModuleImpl.query.get(moduleimpl_id) # --- Change les notes et recalcule les moyennes du module # (rappel: on a deux évaluations: evaluation1, evaluation2, et un seul étudiant) def change_notes(n1, n2): # Saisie d'une note dans chaque éval _ = 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) assert evals_poids.shape == (nb_evals, nb_ues) mod_results = moy_mod.ModuleImplResultsAPC(modimpl) evals_notes = mod_results.evals_notes assert evals_notes[evaluation1.id].dtype == np.float64 etuds_moy_module = mod_results.compute_module_moy(evals_poids) return etuds_moy_module # --- Notes ordinaires: note1, note2 = 11.0, 12.0 sum_copo1 = e1p1 * coef_e1 + e2p1 * coef_e2 # coefs vers UE1 sum_copo2 = e1p2 * coef_e1 + e2p2 * coef_e2 # etuds_moy_module = change_notes(note1, note2) moy_ue1 = etuds_moy_module[ue1.id][etudid] assert moy_ue1 == ((note1 * e1p1 * coef_e1) + (note2 * e2p1 * coef_e2)) / sum_copo1 moy_ue2 = etuds_moy_module[ue2.id][etudid] assert moy_ue2 == ((note1 * e1p2 * coef_e1) + (note2 * e2p2 * coef_e2)) / sum_copo2 moy_ue3 = etuds_moy_module[ue3.id][etudid] assert np.isnan(moy_ue3) # car les poids vers UE3 sont nuls # --- Une note ABS (comptée comme zéro) etuds_moy_module = change_notes(None, note2) 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 np.isnan(etuds_moy_module[ue3.id][etudid]) # --- Deux notes ABS etuds_moy_module = change_notes(None, None) assert etuds_moy_module[ue1.id][etudid] == 0.0 assert etuds_moy_module[ue2.id][etudid] == 0.0 assert np.isnan(etuds_moy_module[ue3.id][etudid]) # --- Note EXC etuds_moy_module = change_notes(NOTES_ATTENTE, note2) assert np.isnan( etuds_moy_module[ue1.id][etudid] ) # car l'eval 2 ne touche que l'UE2 assert etuds_moy_module[ue2.id][etudid] == note2 assert np.isnan(etuds_moy_module[ue3.id][etudid]) # --- Toutes notes ATT (ATT se traite comme EXC) etuds_moy_module = change_notes(NOTES_NEUTRALISE, NOTES_NEUTRALISE) assert np.isnan(etuds_moy_module[ue1.id][etudid]) assert np.isnan(etuds_moy_module[ue2.id][etudid]) assert np.isnan(etuds_moy_module[ue3.id][etudid]) # --- Barème sur 37 evaluation2.note_max = 37.0 note1, note2 = 11.0, 12.0 note_2_37 = note2 / 20 * 37 etuds_moy_module = change_notes(note1, note_2_37) moy_ue1 = etuds_moy_module[ue1.id][etudid] assert moy_ue1 == ((note1 * e1p1 * coef_e1) + (note2 * e2p1 * coef_e2)) / sum_copo1 moy_ue2 = etuds_moy_module[ue2.id][etudid] assert moy_ue2 == ((note1 * e1p2 * coef_e1) + (note2 * e2p2 * coef_e2)) / sum_copo2 moy_ue3 = etuds_moy_module[ue3.id][etudid] 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)