1
0
forked from ScoDoc/ScoDoc

Notes rattrapage et 2eme session en BUT et DUT (pas affichées sur buls)

This commit is contained in:
Emmanuel Viennet 2022-02-02 09:36:12 +01:00
parent 7a4e0206a3
commit e3c9f187dd
3 changed files with 114 additions and 8 deletions

View File

@ -41,7 +41,6 @@ from app import db
from app.models import ModuleImpl, Evaluation, EvaluationUEPoids from app.models import ModuleImpl, Evaluation, EvaluationUEPoids
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
from app.scodoc.sco_codes_parcours import UE_SPORT from app.scodoc.sco_codes_parcours import UE_SPORT
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
@ -92,6 +91,10 @@ class ModuleImplResults:
ne donnent pas de coef vers cette UE. ne donnent pas de coef vers cette UE.
""" """
self.load_notes() self.load_notes()
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): # 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.
@ -135,8 +138,11 @@ class ModuleImplResults:
eval_df = self._load_evaluation_notes(evaluation) eval_df = self._load_evaluation_notes(evaluation)
# is_complete ssi tous les inscrits (non dem) au semestre ont une note # is_complete ssi tous les inscrits (non dem) au semestre ont une note
# ou évaluation déclarée "à prise en compte immédiate" # ou évaluation déclarée "à prise en compte immédiate"
is_complete = evaluation.publish_incomplete or ( # Les évaluations de rattrapage et 2eme session sont toujours incomplètes
not (inscrits_module - set(eval_df.index)) # car on calcule leur moyenne à part.
is_complete = (evaluation.evaluation_type == scu.EVALUATION_NORMALE) and (
evaluation.publish_incomplete
or (not (inscrits_module - set(eval_df.index)))
) )
self.evaluations_completes.append(is_complete) self.evaluations_completes.append(is_complete)
self.evaluations_completes_dict[evaluation.id] = is_complete self.evaluations_completes_dict[evaluation.id] = is_complete
@ -212,6 +218,33 @@ class ModuleImplResults:
self.evals_notes.values > scu.NOTES_ABSENCE, self.evals_notes.values, 0.0 self.evals_notes.values > scu.NOTES_ABSENCE, self.evals_notes.values, 0.0
) / [e.note_max / 20.0 for e in moduleimpl.evaluations] ) / [e.note_max / 20.0 for e in moduleimpl.evaluations]
def get_evaluation_rattrapage(self, moduleimpl: ModuleImpl):
"""L'évaluation de rattrapage de ce module, ou None s'il n'en a pas.
Rattrapage: la moyenne du module est la meilleure note entre moyenne
des autres évals et la note eval rattrapage.
"""
eval_list = [
e
for e in moduleimpl.evaluations
if e.evaluation_type == scu.EVALUATION_RATTRAPAGE
]
if eval_list:
return eval_list[0]
return None
def get_evaluation_session2(self, moduleimpl: ModuleImpl):
"""L'évaluation de deuxième session de ce module, ou None s'il n'en a pas.
Session 2: remplace la note de moyenne des autres évals.
"""
eval_list = [
e
for e in moduleimpl.evaluations
if e.evaluation_type == scu.EVALUATION_SESSION2
]
if eval_list:
return eval_list[0]
return None
class ModuleImplResultsAPC(ModuleImplResults): class ModuleImplResultsAPC(ModuleImplResults):
"Calcul des moyennes de modules à la mode BUT" "Calcul des moyennes de modules à la mode BUT"
@ -229,7 +262,7 @@ class ModuleImplResultsAPC(ModuleImplResults):
ou NaN si les évaluations (dans lesquelles l'étudiant a des notes) ou NaN si les évaluations (dans lesquelles l'étudiant a des notes)
ne donnent pas de coef vers cette UE. ne donnent pas de coef vers cette UE.
""" """
moduleimpl = ModuleImpl.query.get(self.moduleimpl_id) modimpl = ModuleImpl.query.get(self.moduleimpl_id)
nb_etuds, nb_evals = self.evals_notes.shape nb_etuds, nb_evals = self.evals_notes.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
@ -237,11 +270,11 @@ class ModuleImplResultsAPC(ModuleImplResults):
return pd.DataFrame(index=[], columns=evals_poids_df.columns) return pd.DataFrame(index=[], columns=evals_poids_df.columns)
if nb_ues == 0: if nb_ues == 0:
return pd.DataFrame(index=self.evals_notes.index, columns=[]) return pd.DataFrame(index=self.evals_notes.index, columns=[])
evals_coefs = self.get_evaluations_coefs(moduleimpl) evals_coefs = self.get_evaluations_coefs(modimpl)
evals_poids = evals_poids_df.values * evals_coefs evals_poids = evals_poids_df.values * evals_coefs
# -> evals_poids 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)
evals_notes_20 = self.get_eval_notes_sur_20(moduleimpl) evals_notes_20 = self.get_eval_notes_sur_20(modimpl)
# Les poids des évals pour chaque étudiant: là où il a des notes # Les poids des évals pour chaque étudiant: là où il a des notes
# non neutralisées # non neutralisées
@ -262,6 +295,45 @@ class ModuleImplResultsAPC(ModuleImplResults):
etuds_moy_module = np.sum( etuds_moy_module = np.sum(
evals_poids_etuds * evals_notes_stacked, axis=1 evals_poids_etuds * evals_notes_stacked, axis=1
) / np.sum(evals_poids_etuds, axis=1) ) / np.sum(evals_poids_etuds, axis=1)
# Session2 : quand elle existe, remplace la note de module
eval_session2 = self.get_evaluation_session2(modimpl)
if eval_session2:
notes_session2 = self.evals_notes[eval_session2.id].values
# n'utilise que les notes valides (pas ATT, EXC, ABS, NaN)
etuds_use_session2 = notes_session2 > scu.NOTES_ABSENCE
etuds_moy_module = np.where(
etuds_use_session2[:, np.newaxis],
np.tile(
(notes_session2 / (eval_session2.note_max / 20.0))[:, np.newaxis],
nb_ues,
),
etuds_moy_module,
)
self.etuds_use_session2 = pd.Series(
etuds_use_session2, index=self.evals_notes.index
)
else:
# Rattrapage: remplace la note de module ssi elle est supérieure
eval_rat = self.get_evaluation_rattrapage(modimpl)
if eval_rat:
notes_rat = self.evals_notes[eval_rat.id].values
# remplace les notes invalides (ATT, EXC...) par des NaN
notes_rat = np.where(
notes_rat > scu.NOTES_ABSENCE,
notes_rat / (eval_rat.note_max / 20.0),
np.nan,
)
# prend le max
etuds_use_rattrapage = notes_rat > etuds_moy_module
etuds_moy_module = np.where(
etuds_use_rattrapage[:, np.newaxis],
np.tile(notes_rat[:, np.newaxis], nb_ues),
etuds_moy_module,
)
self.etuds_use_rattrapage = pd.Series(
etuds_use_rattrapage, index=self.evals_notes.index
)
self.etuds_moy_module = pd.DataFrame( self.etuds_moy_module = pd.DataFrame(
etuds_moy_module, etuds_moy_module,
index=self.evals_notes.index, index=self.evals_notes.index,
@ -371,8 +443,42 @@ class ModuleImplResultsClassic(ModuleImplResults):
evals_coefs_etuds * evals_notes_20, axis=1 evals_coefs_etuds * evals_notes_20, axis=1
) / np.sum(evals_coefs_etuds, axis=1) ) / np.sum(evals_coefs_etuds, axis=1)
# Session2 : quand elle existe, remplace la note de module
eval_session2 = self.get_evaluation_session2(modimpl)
if eval_session2:
notes_session2 = self.evals_notes[eval_session2.id].values
# n'utilise que les notes valides (pas ATT, EXC, ABS, NaN)
etuds_use_session2 = notes_session2 > scu.NOTES_ABSENCE
etuds_moy_module = np.where(
etuds_use_session2,
notes_session2 / (eval_session2.note_max / 20.0),
etuds_moy_module,
)
self.etuds_use_session2 = pd.Series(
etuds_use_session2, index=self.evals_notes.index
)
else:
# Rattrapage: remplace la note de module ssi elle est supérieure
eval_rat = self.get_evaluation_rattrapage(modimpl)
if eval_rat:
notes_rat = self.evals_notes[eval_rat.id].values
# remplace les notes invalides (ATT, EXC...) par des NaN
notes_rat = np.where(
notes_rat > scu.NOTES_ABSENCE,
notes_rat / (eval_rat.note_max / 20.0),
np.nan,
)
# prend le max
etuds_use_rattrapage = notes_rat > etuds_moy_module
etuds_moy_module = np.where(
etuds_use_rattrapage, notes_rat, etuds_moy_module
)
self.etuds_use_rattrapage = pd.Series(
etuds_use_rattrapage, index=self.evals_notes.index
)
self.etuds_moy_module = pd.Series( self.etuds_moy_module = pd.Series(
etuds_moy_module, etuds_moy_module,
index=self.evals_notes.index, index=self.evals_notes.index,
) )
return self.etuds_moy_module return self.etuds_moy_module

View File

@ -619,7 +619,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
prefs=prefs, prefs=prefs,
) )
if nbeval: # boite autour des evaluations (en pdf) if nbeval: # boite autour des évaluations (en pdf)
P[-1]["_pdf_style"].append( P[-1]["_pdf_style"].append(
("BOX", (1, 1 - nbeval), (-1, 0), 0.2, self.PDF_LIGHT_GRAY) ("BOX", (1, 1 - nbeval), (-1, 0), 0.2, self.PDF_LIGHT_GRAY)
) )

View File

@ -207,7 +207,7 @@ def evaluation_create_form(
{ {
"size": 6, "size": 6,
"type": "float", "type": "float",
"explanation": "coef. dans le module (choisi librement par l'enseignant)", "explanation": "coef. dans le module (choisi librement par l'enseignant, non utilisé pour rattrapage et 2ème session)",
"allow_null": False, "allow_null": False,
}, },
) )