forked from ScoDoc/ScoDoc
Implémentation des bonus malus
This commit is contained in:
parent
e3450ebc82
commit
bb40532ca6
@ -71,7 +71,7 @@ class BulletinBUT(ResultatsSemestreBUT):
|
||||
"bonus": fmt_note(self.bonus_ues[ue.id][etud.id])
|
||||
if self.bonus_ues is not None and ue.id in self.bonus_ues
|
||||
else fmt_note(0.0),
|
||||
"malus": None, # XXX TODO voir ce qui est ici
|
||||
"malus": self.malus[ue.id][etud.id],
|
||||
"capitalise": None, # "AAAA-MM-JJ" TODO
|
||||
"ressources": self.etud_ue_mod_results(etud, ue, self.ressources),
|
||||
"saes": self.etud_ue_mod_results(etud, ue, self.saes),
|
||||
|
@ -104,7 +104,6 @@ class BonusSport:
|
||||
# sem_modimpl_moys_spo est (nb_etuds, nb_mod_sport)
|
||||
# ou (nb_etuds, nb_mod_sport, nb_ues_non_bonus)
|
||||
nb_etuds, nb_mod_sport = sem_modimpl_moys_spo.shape[:2]
|
||||
nb_ues = len(ues)
|
||||
# Enlève les NaN du numérateur:
|
||||
sem_modimpl_moys_no_nan = np.nan_to_num(sem_modimpl_moys_spo, nan=0.0)
|
||||
|
||||
@ -162,7 +161,7 @@ class BonusSport:
|
||||
"""
|
||||
raise NotImplementedError("méthode virtuelle")
|
||||
|
||||
def get_bonus_ues(self) -> pd.Series:
|
||||
def get_bonus_ues(self) -> pd.DataFrame:
|
||||
"""Les bonus à appliquer aux UE
|
||||
Résultat: DataFrame de float, index etudid, columns: ue.id
|
||||
"""
|
||||
|
@ -43,6 +43,8 @@ from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_codes_parcours import UE_SPORT
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
|
||||
|
||||
@dataclass
|
||||
class EvaluationEtat:
|
||||
@ -291,7 +293,12 @@ def load_evaluations_poids(moduleimpl_id: int) -> tuple[pd.DataFrame, list]:
|
||||
pass # poids vers des UE qui n'existent plus ou sont dans un autre semestre...
|
||||
|
||||
# Initialise poids non enregistrés:
|
||||
default_poids = 1.0 if modimpl.module.ue.type == UE_SPORT else 0.0
|
||||
default_poids = (
|
||||
1.0
|
||||
if modimpl.module.ue.type == UE_SPORT
|
||||
or modimpl.module.module_type == ModuleType.MALUS
|
||||
else 0.0
|
||||
)
|
||||
|
||||
if np.isnan(evals_poids.values.flat).any():
|
||||
ue_coefs = modimpl.module.get_ue_coef_dict()
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
"""Fonctions de calcul des moyennes d'UE (classiques ou BUT)
|
||||
"""
|
||||
from re import X
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
@ -380,3 +381,42 @@ def compute_ue_moys_classic(
|
||||
etud_moy_gen_s = pd.Series(etud_moy_gen, index=modimpl_inscr_df.index)
|
||||
|
||||
return etud_moy_gen_s, etud_moy_ue_df, etud_coef_ue_df
|
||||
|
||||
|
||||
def compute_malus(
|
||||
formsemestre: FormSemestre,
|
||||
sem_modimpl_moys: np.array,
|
||||
ues: list[UniteEns],
|
||||
modimpl_inscr_df: pd.DataFrame,
|
||||
) -> pd.DataFrame:
|
||||
"""Calcul le malus sur les UE
|
||||
Dans chaque UE, on peut avoir un ou plusieurs modules de MALUS.
|
||||
Leurs notes sont positives ou négatives. leur somme sera _soustraite_ à la moyenne
|
||||
de chaque UE.
|
||||
Arguments:
|
||||
- sem_modimpl_moys :
|
||||
notes moyennes aux modules (tous les étuds x tous les modimpls)
|
||||
floats avec des NaN.
|
||||
En classique: sem_matrix, ndarray (etuds x modimpls)
|
||||
En APC: sem_cube, ndarray (etuds x modimpls x UEs non bonus)
|
||||
- ues: les ues du semestre (incluant le bonus sport)
|
||||
- modimpl_inscr_df: matrice d'inscription aux modules du semestre (etud x modimpl)
|
||||
|
||||
Résultat: DataFrame de float, index etudid, columns: ue.id (sans NaN)
|
||||
"""
|
||||
ues_idx = [ue.id for ue in ues]
|
||||
malus = pd.DataFrame(index=modimpl_inscr_df.index, columns=ues_idx, dtype=float)
|
||||
for ue in ues:
|
||||
if ue.type != UE_SPORT:
|
||||
modimpl_mask = np.array(
|
||||
[
|
||||
(m.module.module_type == ModuleType.MALUS)
|
||||
and (m.module.ue.id == ue.id)
|
||||
for m in formsemestre.modimpls_sorted
|
||||
]
|
||||
)
|
||||
malus_moys = sem_modimpl_moys[:, modimpl_mask].sum(axis=1)
|
||||
malus[ue.id] = malus_moys
|
||||
|
||||
malus.fillna(0.0, inplace=True)
|
||||
return malus
|
||||
|
@ -68,6 +68,12 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
||||
1.0, index=self.etud_moy_ue.index, columns=self.etud_moy_ue.columns
|
||||
)
|
||||
|
||||
# --- Modules de MALUS sur les UEs
|
||||
self.malus = moy_ue.compute_malus(
|
||||
self.formsemestre, self.sem_cube, self.ues, self.modimpl_inscr_df
|
||||
)
|
||||
self.etud_moy_ue -= self.malus
|
||||
|
||||
# --- Bonus Sport & Culture
|
||||
if len(modimpls_sport) > 0:
|
||||
bonus_class = ScoDocSiteConfig.get_bonus_sport_class()
|
||||
|
@ -71,6 +71,16 @@ class ResultatsSemestreClassic(NotesTableCompat):
|
||||
self.modimpl_coefs,
|
||||
modimpl_standards_mask,
|
||||
)
|
||||
# --- Modules de MALUS sur les UEs et la moyenne générale
|
||||
self.malus = moy_ue.compute_malus(
|
||||
self.formsemestre, self.sem_matrix, self.ues, self.modimpl_inscr_df
|
||||
)
|
||||
self.etud_moy_ue -= self.malus
|
||||
# ajuste la moyenne générale (à l'aide des coefs d'UE)
|
||||
self.etud_moy_gen -= (self.etud_coef_ue_df * self.malus).sum(
|
||||
axis=1
|
||||
) / self.etud_coef_ue_df.sum(axis=1)
|
||||
|
||||
# --- Bonus Sport & Culture
|
||||
bonus_class = ScoDocSiteConfig.get_bonus_sport_class()
|
||||
if bonus_class is not None:
|
||||
|
@ -115,22 +115,30 @@ def do_module_create(args) -> int:
|
||||
return r
|
||||
|
||||
|
||||
def module_create(matiere_id=None, module_type=None, semestre_id=None):
|
||||
"""Création d'un module"""
|
||||
def module_create(
|
||||
matiere_id=None, module_type=None, semestre_id=None, formation_id=None
|
||||
):
|
||||
"""Formulaire de création d'un module
|
||||
Si matiere_id est spécifié, le module sera créé dans cette matière (cas normal).
|
||||
Sinon, donne le choix de l'UE de rattachement et utilise la première
|
||||
matière de cette UE (si elle n'existe pas, la crée).
|
||||
"""
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_edit_ue
|
||||
|
||||
if matiere_id:
|
||||
matiere = Matiere.query.get_or_404(matiere_id)
|
||||
if matiere is None:
|
||||
raise ScoValueError("invalid matiere !")
|
||||
ue = matiere.ue
|
||||
parcours = ue.formation.get_parcours()
|
||||
formation = ue.formation
|
||||
else:
|
||||
formation = Formation.query.get_or_404(formation_id)
|
||||
parcours = formation.get_parcours()
|
||||
is_apc = parcours.APC_SAE
|
||||
ues = ue.formation.ues.order_by(
|
||||
ues = formation.ues.order_by(
|
||||
UniteEns.semestre_idx, UniteEns.numero, UniteEns.acronyme
|
||||
).all()
|
||||
# cherche le numero adéquat (pour placer le module en fin de liste)
|
||||
modules = matiere.ue.formation.modules.all()
|
||||
modules = formation.modules.all()
|
||||
if modules:
|
||||
default_num = max([m.numero or 0 for m in modules]) + 10
|
||||
else:
|
||||
@ -143,9 +151,11 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title=f"Création {object_name}"),
|
||||
]
|
||||
if is_apc:
|
||||
if not matiere_id:
|
||||
H += [
|
||||
f"""<h2>Création {object_name} dans la formation {ue.formation.acronyme}, Semestre {ue.semestre_idx}, {ue.acronyme}</h2>"""
|
||||
f"""<h2>Création {object_name} dans la formation {formation.acronyme}
|
||||
</h2>
|
||||
"""
|
||||
]
|
||||
else:
|
||||
H += [
|
||||
@ -158,7 +168,6 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
|
||||
render_template(
|
||||
"scodoc/help/modules.html",
|
||||
is_apc=is_apc,
|
||||
ue=ue,
|
||||
semestre_id=semestre_id,
|
||||
)
|
||||
]
|
||||
@ -170,7 +179,7 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
|
||||
"size": 10,
|
||||
"explanation": "code du module, ressource ou SAÉ. Exemple M1203, R2.01, ou SAÉ 3.4. Ce code doit être unique dans la formation.",
|
||||
"allow_null": False,
|
||||
"validator": lambda val, field, formation_id=ue.formation_id: check_module_code_unicity(
|
||||
"validator": lambda val, field, formation_id=formation_id: check_module_code_unicity(
|
||||
val, field, formation_id
|
||||
),
|
||||
},
|
||||
@ -192,6 +201,15 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
|
||||
]
|
||||
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
||||
|
||||
if is_apc:
|
||||
module_types = scu.ModuleType # tous les types
|
||||
else:
|
||||
# ne propose pas SAE et Ressources:
|
||||
module_types = set(scu.ModuleType) - {
|
||||
scu.ModuleType.RESSOURCE,
|
||||
scu.ModuleType.SAE,
|
||||
}
|
||||
|
||||
descr += [
|
||||
(
|
||||
"module_type",
|
||||
@ -199,8 +217,8 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
|
||||
"input_type": "menu",
|
||||
"title": "Type",
|
||||
"explanation": "",
|
||||
"labels": [x.name.capitalize() for x in scu.ModuleType],
|
||||
"allowed_values": [str(int(x)) for x in scu.ModuleType],
|
||||
"labels": [x.name.capitalize() for x in module_types],
|
||||
"allowed_values": [str(int(x)) for x in module_types],
|
||||
},
|
||||
),
|
||||
(
|
||||
@ -256,11 +274,30 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
|
||||
),
|
||||
]
|
||||
|
||||
if matiere_id:
|
||||
descr += [
|
||||
("ue_id", {"default": ue.id, "input_type": "hidden"}),
|
||||
("matiere_id", {"default": matiere_id, "input_type": "hidden"}),
|
||||
]
|
||||
else:
|
||||
# choix de l'UE de rattachement
|
||||
descr += [
|
||||
(
|
||||
"ue_id",
|
||||
{
|
||||
"input_type": "menu",
|
||||
"type": "int",
|
||||
"title": "UE de rattachement",
|
||||
"explanation": "utilisée notamment pour les malus",
|
||||
"labels": [f"{u.acronyme} {u.titre}" for u in ues],
|
||||
"allowed_values": [u.id for u in ues],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
descr += [
|
||||
# ('ects', { 'size' : 4, 'type' : 'float', 'title' : 'ECTS', 'explanation' : 'nombre de crédits ECTS (inutilisés: les crédits sont associés aux UE)' }),
|
||||
("formation_id", {"default": ue.formation_id, "input_type": "hidden"}),
|
||||
("ue_id", {"default": ue.id, "input_type": "hidden"}),
|
||||
("matiere_id", {"default": matiere.id, "input_type": "hidden"}),
|
||||
("formation_id", {"default": formation.id, "input_type": "hidden"}),
|
||||
(
|
||||
"code_apogee",
|
||||
{
|
||||
@ -290,6 +327,20 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
|
||||
if tf[0] == 0:
|
||||
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
|
||||
else:
|
||||
if not matiere_id:
|
||||
# formulaire avec choix UE de rattachement
|
||||
ue = UniteEns.query.get(tf[2]["ue_id"])
|
||||
if ue is None:
|
||||
raise ValueError("UE invalide")
|
||||
matiere = ue.matieres.first()
|
||||
if matiere:
|
||||
tf[2]["matiere_id"] = matiere.id
|
||||
else:
|
||||
matiere_id = sco_edit_matiere.do_matiere_create(
|
||||
{"ue_id": ue.id, "titre": ue.titre, "numero": 1},
|
||||
)
|
||||
tf[2]["matiere_id"] = matiere_id
|
||||
|
||||
tf[2]["semestre_id"] = ue.semestre_idx
|
||||
|
||||
_ = do_module_create(tf[2])
|
||||
@ -298,7 +349,7 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
|
||||
url_for(
|
||||
"notes.ue_table",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formation_id=ue.formation_id,
|
||||
formation_id=formation.id,
|
||||
semestre_idx=tf[2]["semestre_id"],
|
||||
)
|
||||
)
|
||||
@ -493,6 +544,13 @@ def module_edit(module_id=None):
|
||||
soyez prudents !
|
||||
</span></div>"""
|
||||
)
|
||||
if is_apc:
|
||||
module_types = scu.ModuleType # tous les types
|
||||
else:
|
||||
# ne propose pas SAE et Ressources, sauf si déjà de ce type...
|
||||
module_types = (
|
||||
set(scu.ModuleType) - {scu.ModuleType.RESSOURCE, scu.ModuleType.SAE}
|
||||
) | {a_module.module_type}
|
||||
|
||||
descr = [
|
||||
(
|
||||
@ -514,8 +572,8 @@ def module_edit(module_id=None):
|
||||
"input_type": "menu",
|
||||
"title": "Type",
|
||||
"explanation": "",
|
||||
"labels": [x.name.capitalize() for x in scu.ModuleType],
|
||||
"allowed_values": [str(int(x)) for x in scu.ModuleType],
|
||||
"labels": [x.name.capitalize() for x in module_types],
|
||||
"allowed_values": [str(int(x)) for x in module_types],
|
||||
"enabled": unlocked,
|
||||
},
|
||||
),
|
||||
|
@ -998,6 +998,7 @@ def _ue_table_matieres(
|
||||
H.append(
|
||||
_ue_table_modules(
|
||||
parcours,
|
||||
ue,
|
||||
mat,
|
||||
modules,
|
||||
editable,
|
||||
@ -1031,6 +1032,7 @@ def _ue_table_matieres(
|
||||
|
||||
def _ue_table_modules(
|
||||
parcours,
|
||||
ue,
|
||||
mat,
|
||||
modules,
|
||||
editable,
|
||||
@ -1121,8 +1123,12 @@ def _ue_table_modules(
|
||||
tag_cls,
|
||||
",".join(sco_tag_module.module_tag_list(mod["module_id"])),
|
||||
)
|
||||
if ue["semestre_idx"] is not None and mod["semestre_id"] != ue["semestre_idx"]:
|
||||
warning_semestre = ' <span class="red">incohérent ?</span>'
|
||||
else:
|
||||
warning_semestre = ""
|
||||
H.append(
|
||||
" %s %s" % (parcours.SESSION_NAME, mod["semestre_id"])
|
||||
" %s %s%s" % (parcours.SESSION_NAME, mod["semestre_id"], warning_semestre)
|
||||
+ " (%s)" % heurescoef
|
||||
+ tag_edit
|
||||
)
|
||||
|
@ -546,7 +546,7 @@ def do_formsemestre_createwithmodules(edit=False):
|
||||
for mod in mods:
|
||||
if mod["semestre_id"] == semestre_id and (
|
||||
(not edit) # creation => tous modules
|
||||
or (not formation.is_apc()) # pas BUT, on peux mixer les semestres
|
||||
or (not formation.is_apc()) # pas BUT, on peut mixer les semestres
|
||||
or (semestre_id == formsemestre.semestre_id) # module du semestre
|
||||
or (mod["module_id"] in module_ids_set) # module déjà présent
|
||||
):
|
||||
|
@ -219,7 +219,9 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
page_title=f"{mod_type_name} {Mod['code']} {Mod['titre']}"
|
||||
),
|
||||
f"""<h2 class="formsemestre">{mod_type_name}
|
||||
<tt>{Mod['code']}</tt> {Mod['titre']}</h2>
|
||||
<tt>{Mod['code']}</tt> {Mod['titre']}
|
||||
{"dans l'UE " + modimpl.module.ue.acronyme if modimpl.module.module_type == scu.ModuleType.MALUS else ""}
|
||||
</h2>
|
||||
<div class="moduleimpl_tableaubord moduleimpl_type_{
|
||||
scu.ModuleType(Mod['module_type']).name.lower()}">
|
||||
<table>
|
||||
|
@ -71,13 +71,21 @@
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
{% if editable and matiere_parent %}
|
||||
<li><a class="stdlink" href="{{
|
||||
{% if editable %}
|
||||
<li><a class="stdlink" href=
|
||||
{% if matiere_parent %}"{{
|
||||
url_for("notes.module_create",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
module_type=module_type|int,
|
||||
matiere_id=matiere_parent.id
|
||||
)}}"
|
||||
{% else %}"{{
|
||||
url_for("notes.module_create",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
module_type=module_type|int,
|
||||
formation_id=formation.id
|
||||
)}}"
|
||||
{% endif %}
|
||||
>{{create_element_msg}}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
Loading…
Reference in New Issue
Block a user