forked from ScoDoc/ScoDoc
Bonus sport multiplicatifs ou additifs sur bulletins DUT et BUT
This commit is contained in:
parent
7e4459a15e
commit
23672bebde
@ -28,10 +28,12 @@ class BulletinBUT(ResultatsSemestreBUT):
|
|||||||
"dict synthèse résultats dans l'UE pour les modules indiqués"
|
"dict synthèse résultats dans l'UE pour les modules indiqués"
|
||||||
d = {}
|
d = {}
|
||||||
etud_idx = self.etud_index[etud.id]
|
etud_idx = self.etud_index[etud.id]
|
||||||
|
if ue.type != UE_SPORT:
|
||||||
ue_idx = self.modimpl_coefs_df.index.get_loc(ue.id)
|
ue_idx = self.modimpl_coefs_df.index.get_loc(ue.id)
|
||||||
etud_moy_module = self.sem_cube[etud_idx] # module x UE
|
etud_moy_module = self.sem_cube[etud_idx] # module x UE
|
||||||
for modimpl in modimpls:
|
for modimpl in modimpls:
|
||||||
if self.modimpl_inscr_df[str(modimpl.id)][etud.id]: # si inscrit
|
if self.modimpl_inscr_df[str(modimpl.id)][etud.id]: # si inscrit
|
||||||
|
if ue.type != UE_SPORT:
|
||||||
coef = self.modimpl_coefs_df[modimpl.id][ue.id]
|
coef = self.modimpl_coefs_df[modimpl.id][ue.id]
|
||||||
if coef > 0:
|
if coef > 0:
|
||||||
d[modimpl.module.code] = {
|
d[modimpl.module.code] = {
|
||||||
@ -43,6 +45,12 @@ class BulletinBUT(ResultatsSemestreBUT):
|
|||||||
][ue_idx]
|
][ue_idx]
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
# else: # modules dans UE bonus sport
|
||||||
|
# d[modimpl.module.code] = {
|
||||||
|
# "id": modimpl.id,
|
||||||
|
# "coef": "",
|
||||||
|
# "moyenne": "?x?",
|
||||||
|
# }
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def etud_ue_results(self, etud, ue):
|
def etud_ue_results(self, etud, ue):
|
||||||
|
@ -38,7 +38,7 @@ class BonusSport:
|
|||||||
notes moyennes aux modules (tous les étuds x tous les modimpls)
|
notes moyennes aux modules (tous les étuds x tous les modimpls)
|
||||||
floats avec des NaN.
|
floats avec des NaN.
|
||||||
En classique: sem_matrix, ndarray (etuds x modimpls)
|
En classique: sem_matrix, ndarray (etuds x modimpls)
|
||||||
En APC: sem_cube, ndarray (etuds x modimpls x UEs)
|
En APC: sem_cube, ndarray (etuds x modimpls x UEs non bonus)
|
||||||
- ues: les ues du semestre (incluant le bonus sport)
|
- ues: les ues du semestre (incluant le bonus sport)
|
||||||
- modimpl_inscr_df: matrice d'inscription aux modules du semestre (etud x modimpl)
|
- modimpl_inscr_df: matrice d'inscription aux modules du semestre (etud x modimpl)
|
||||||
- modimpl_coefs: les coefs des modules
|
- modimpl_coefs: les coefs des modules
|
||||||
@ -50,10 +50,9 @@ class BonusSport:
|
|||||||
etud_moy_gen et etud_moy_ue ne sont PAS modifiés (mais utilisés par certains bonus non additifs).
|
etud_moy_gen et etud_moy_ue ne sont PAS modifiés (mais utilisés par certains bonus non additifs).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Si vrai, en APC, si le bonus UE est None, reporte le bonus moy gen:
|
# En classique, active un bonus sur les UEs: (dans ce cas bonus_moy_gen reste None)
|
||||||
apc_apply_bonus_mg_to_ues = True
|
classic_use_bonus_ues = False
|
||||||
# Si True, reporte toujours le bonus moy gen sur les UE (même en formations classiques)
|
|
||||||
apply_bonus_mg_to_ues = False
|
|
||||||
# Attributs virtuels:
|
# Attributs virtuels:
|
||||||
seuil_moy_gen = None
|
seuil_moy_gen = None
|
||||||
proportion_point = None
|
proportion_point = None
|
||||||
@ -77,7 +76,7 @@ class BonusSport:
|
|||||||
self.etud_moy_ue = etud_moy_ue
|
self.etud_moy_ue = etud_moy_ue
|
||||||
self.etuds_idx = modimpl_inscr_df.index # les étudiants inscrits au semestre
|
self.etuds_idx = modimpl_inscr_df.index # les étudiants inscrits au semestre
|
||||||
self.bonus_ues: pd.DataFrame = None # virtual
|
self.bonus_ues: pd.DataFrame = None # virtual
|
||||||
self.bonus_moy_gen: pd.Series = None # virtual
|
self.bonus_moy_gen: pd.Series = None # virtual (pour formations non apc slt)
|
||||||
# Restreint aux modules standards des UE de type "sport":
|
# Restreint aux modules standards des UE de type "sport":
|
||||||
modimpl_mask = np.array(
|
modimpl_mask = np.array(
|
||||||
[
|
[
|
||||||
@ -94,13 +93,14 @@ class BonusSport:
|
|||||||
"liste des modimpls sport"
|
"liste des modimpls sport"
|
||||||
|
|
||||||
# Les moyennes des modules "sport": (une par UE en APC)
|
# Les moyennes des modules "sport": (une par UE en APC)
|
||||||
|
# donc (nb_etuds, nb_mod_sport, nb_ues_non_bonus)
|
||||||
sem_modimpl_moys_spo = sem_modimpl_moys[:, modimpl_mask]
|
sem_modimpl_moys_spo = sem_modimpl_moys[:, modimpl_mask]
|
||||||
# Les inscriptions aux modules sport:
|
# Les inscriptions aux modules sport:
|
||||||
modimpl_inscr_spo = modimpl_inscr_df.values[:, modimpl_mask]
|
modimpl_inscr_spo = modimpl_inscr_df.values[:, modimpl_mask]
|
||||||
# Les coefficients des modules sport (en apc: nb_mod_sport x nb_ue)
|
# Les coefficients des modules sport (en apc: nb_mod_sport x nb_ue) (toutes ues)
|
||||||
modimpl_coefs_spo = modimpl_coefs[modimpl_mask]
|
modimpl_coefs_spo = modimpl_coefs[modimpl_mask]
|
||||||
# sem_modimpl_moys_spo est (nb_etuds, nb_mod_sport)
|
# sem_modimpl_moys_spo est (nb_etuds, nb_mod_sport)
|
||||||
# ou (nb_etuds, nb_mod_sport, nb_ues)
|
# ou (nb_etuds, nb_mod_sport, nb_ues_non_bonus)
|
||||||
nb_etuds, nb_mod_sport = sem_modimpl_moys_spo.shape[:2]
|
nb_etuds, nb_mod_sport = sem_modimpl_moys_spo.shape[:2]
|
||||||
nb_ues = len(ues)
|
nb_ues = len(ues)
|
||||||
# Enlève les NaN du numérateur:
|
# Enlève les NaN du numérateur:
|
||||||
@ -115,7 +115,7 @@ class BonusSport:
|
|||||||
[modimpl_inscr_spo] * nb_ues_no_bonus, axis=2
|
[modimpl_inscr_spo] * nb_ues_no_bonus, axis=2
|
||||||
)
|
)
|
||||||
# Ne prend pas en compte les notes des étudiants non inscrits au module:
|
# Ne prend pas en compte les notes des étudiants non inscrits au module:
|
||||||
# Annule les notes:
|
# Annule les notes: (nb_etuds, nb_mod_bonus, nb_ues_non_bonus)
|
||||||
sem_modimpl_moys_inscrits = np.where(
|
sem_modimpl_moys_inscrits = np.where(
|
||||||
modimpl_inscr_spo_stacked, sem_modimpl_moys_no_nan, 0.0
|
modimpl_inscr_spo_stacked, sem_modimpl_moys_no_nan, 0.0
|
||||||
)
|
)
|
||||||
@ -151,7 +151,7 @@ class BonusSport:
|
|||||||
"""Calcul des bonus: méthode virtuelle à écraser.
|
"""Calcul des bonus: méthode virtuelle à écraser.
|
||||||
Arguments:
|
Arguments:
|
||||||
- sem_modimpl_moys_inscrits:
|
- sem_modimpl_moys_inscrits:
|
||||||
ndarray (nb_etuds, mod_sport) ou en APC (nb_etuds, mods_sport, nb_ue)
|
ndarray (nb_etuds, mod_sport) ou en APC (nb_etuds, mods_sport, nb_ue_non_bonus)
|
||||||
les notes aux modules sports auxquel l'étudiant est inscrit, 0 sinon. Pas de nans.
|
les notes aux modules sports auxquel l'étudiant est inscrit, 0 sinon. Pas de nans.
|
||||||
- modimpl_coefs_etuds_no_nan:
|
- modimpl_coefs_etuds_no_nan:
|
||||||
les coefficients: float ndarray
|
les coefficients: float ndarray
|
||||||
@ -164,24 +164,16 @@ class BonusSport:
|
|||||||
"""Les bonus à appliquer aux UE
|
"""Les bonus à appliquer aux UE
|
||||||
Résultat: DataFrame de float, index etudid, columns: ue.id
|
Résultat: DataFrame de float, index etudid, columns: ue.id
|
||||||
"""
|
"""
|
||||||
if self.bonus_ues is None and (
|
if self.classic_use_bonus_ues or self.formsemestre.formation.is_apc():
|
||||||
(self.apc_apply_bonus_mg_to_ues and self.formsemestre.formation.is_apc())
|
|
||||||
or self.apply_bonus_mg_to_ues
|
|
||||||
):
|
|
||||||
# reporte uniformément le bonus moyenne générale sur les UEs
|
|
||||||
# (assure la compatibilité de la plupart des anciens bonus avec le BUT)
|
|
||||||
# ues = self.formsemestre.query_ues(with_sport=False)
|
|
||||||
ues_idx = [ue.id for ue in self.formsemestre.query_ues(with_sport=False)]
|
|
||||||
bonus_moy_gen = self.get_bonus_moy_gen()
|
|
||||||
bonus_ues = np.stack([bonus_moy_gen.values] * len(ues_idx), axis=1)
|
|
||||||
return pd.DataFrame(bonus_ues, index=self.etuds_idx, columns=ues_idx)
|
|
||||||
|
|
||||||
return self.bonus_ues
|
return self.bonus_ues
|
||||||
|
return None
|
||||||
|
|
||||||
def get_bonus_moy_gen(self):
|
def get_bonus_moy_gen(self):
|
||||||
"""Le bonus à appliquer à la moyenne générale.
|
"""Le bonus à appliquer à la moyenne générale.
|
||||||
Résultat: Series de float, index etudid
|
Résultat: Series de float, index etudid
|
||||||
"""
|
"""
|
||||||
|
if self.formsemestre.formation.is_apc():
|
||||||
|
return None # garde-fou
|
||||||
return self.bonus_moy_gen
|
return self.bonus_moy_gen
|
||||||
|
|
||||||
|
|
||||||
@ -200,8 +192,12 @@ class BonusSportAdditif(BonusSport):
|
|||||||
proportion_point = 0.05 # multiplie les points au dessus du seuil
|
proportion_point = 0.05 # multiplie les points au dessus du seuil
|
||||||
|
|
||||||
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||||
"""calcul du bonus"""
|
"""calcul du bonus
|
||||||
bonus_moy_gen_arr = np.sum(
|
sem_modimpl_moys_inscrits: les notes de sport
|
||||||
|
En APC: ndarray (nb_etuds, nb_mod_sport, nb_ues_non_bonus)
|
||||||
|
modimpl_coefs_etuds_no_nan:
|
||||||
|
"""
|
||||||
|
bonus_moy_arr = np.sum(
|
||||||
np.where(
|
np.where(
|
||||||
sem_modimpl_moys_inscrits > self.seuil_moy_gen,
|
sem_modimpl_moys_inscrits > self.seuil_moy_gen,
|
||||||
(sem_modimpl_moys_inscrits - self.seuil_moy_gen)
|
(sem_modimpl_moys_inscrits - self.seuil_moy_gen)
|
||||||
@ -210,18 +206,28 @@ class BonusSportAdditif(BonusSport):
|
|||||||
),
|
),
|
||||||
axis=1,
|
axis=1,
|
||||||
)
|
)
|
||||||
# en APC, applati la moyenne gen. XXX pourrait être fait en amont
|
|
||||||
if len(bonus_moy_gen_arr.shape) > 1:
|
|
||||||
bonus_moy_gen_arr = bonus_moy_gen_arr.sum(axis=1)
|
|
||||||
# Bonus moyenne générale, et 0 sur les UE
|
|
||||||
self.bonus_moy_gen = pd.Series(
|
|
||||||
bonus_moy_gen_arr, index=self.etuds_idx, dtype=float
|
|
||||||
)
|
|
||||||
if self.bonus_max is not None:
|
if self.bonus_max is not None:
|
||||||
# Seuil: bonus (sur moy. gen.) limité à bonus_max points
|
# Seuil: bonus limité à bonus_max points (et >= 0)
|
||||||
self.bonus_moy_gen = self.bonus_moy_gen.clip(upper=self.bonus_max)
|
bonus_moy_arr = np.clip(
|
||||||
|
bonus_moy_arr, 0.0, self.bonus_max, out=bonus_moy_arr
|
||||||
|
)
|
||||||
|
|
||||||
# Laisse bonus_ues à None, en APC le bonus moy. gen. sera réparti sur les UEs.
|
# en APC, bonus_moy_arr est (nb_etuds, nb_ues_non_bonus)
|
||||||
|
if self.formsemestre.formation.is_apc() or self.classic_use_bonus_ues:
|
||||||
|
# Bonus sur les UE et None sur moyenne générale
|
||||||
|
ues_idx = [ue.id for ue in self.formsemestre.query_ues(with_sport=False)]
|
||||||
|
self.bonus_ues = pd.DataFrame(
|
||||||
|
bonus_moy_arr, index=self.etuds_idx, columns=ues_idx, dtype=float
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Bonus sur la moyenne générale seulement
|
||||||
|
self.bonus_moy_gen = pd.Series(
|
||||||
|
bonus_moy_arr, index=self.etuds_idx, dtype=float
|
||||||
|
)
|
||||||
|
|
||||||
|
# if len(bonus_moy_arr.shape) > 1:
|
||||||
|
# bonus_moy_arr = bonus_moy_arr.sum(axis=1)
|
||||||
|
# Laisse bonus_moy_gen à None, en APC le bonus moy. gen. sera réparti sur les UEs.
|
||||||
|
|
||||||
|
|
||||||
class BonusSportMultiplicatif(BonusSport):
|
class BonusSportMultiplicatif(BonusSport):
|
||||||
@ -229,6 +235,8 @@ class BonusSportMultiplicatif(BonusSport):
|
|||||||
|
|
||||||
seuil_moy_gen = 10.0 # seuls les points au dessus du seuil sont comptés
|
seuil_moy_gen = 10.0 # seuls les points au dessus du seuil sont comptés
|
||||||
amplitude = 0.005 # multiplie les points au dessus du seuil
|
amplitude = 0.005 # multiplie les points au dessus du seuil
|
||||||
|
# En classique, les bonus multiplicatifs agissent par défaut sur les UE:
|
||||||
|
classic_use_bonus_ues = True
|
||||||
|
|
||||||
# C'est un bonus "multiplicatif": on l'exprime en additif,
|
# C'est un bonus "multiplicatif": on l'exprime en additif,
|
||||||
# sur chaque moyenne d'UE m_0
|
# sur chaque moyenne d'UE m_0
|
||||||
@ -243,11 +251,12 @@ class BonusSportMultiplicatif(BonusSport):
|
|||||||
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
|
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
|
||||||
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
|
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
|
||||||
notes = np.nan_to_num(notes, copy=False)
|
notes = np.nan_to_num(notes, copy=False)
|
||||||
|
|
||||||
factor = (notes - self.seuil_moy_gen) * self.amplitude # 5% si note=20
|
factor = (notes - self.seuil_moy_gen) * self.amplitude # 5% si note=20
|
||||||
factor[factor <= 0] = 0.0 # note < seuil_moy_gen, pas de bonus
|
factor[factor <= 0] = 0.0 # note < seuil_moy_gen, pas de bonus
|
||||||
|
|
||||||
# Ne s'applique qu'aux moyennes d'UE
|
# Ne s'applique qu'aux moyennes d'UE
|
||||||
|
if len(factor.shape) == 1: # classic
|
||||||
|
factor = factor[:, np.newaxis]
|
||||||
bonus = self.etud_moy_ue * factor
|
bonus = self.etud_moy_ue * factor
|
||||||
if self.bonus_max is not None:
|
if self.bonus_max is not None:
|
||||||
# Seuil: bonus limité à bonus_max points
|
# Seuil: bonus limité à bonus_max points
|
||||||
@ -255,9 +264,8 @@ class BonusSportMultiplicatif(BonusSport):
|
|||||||
|
|
||||||
self.bonus_ues = bonus # DataFrame
|
self.bonus_ues = bonus # DataFrame
|
||||||
|
|
||||||
if not self.formsemestre.formation.is_apc():
|
# Les bonus multiplicatifs ne s'appliquent pas à la moyenne générale
|
||||||
# s'applique à la moyenne générale
|
self.bonus_moy_gen = None
|
||||||
self.bonus_moy_gen = bonus
|
|
||||||
|
|
||||||
|
|
||||||
class BonusDirect(BonusSportAdditif):
|
class BonusDirect(BonusSportAdditif):
|
||||||
@ -323,6 +331,7 @@ class BonusBordeaux1(BonusSportMultiplicatif):
|
|||||||
|
|
||||||
name = "bonus_iutBordeaux1"
|
name = "bonus_iutBordeaux1"
|
||||||
displayed_name = "IUT de Bordeaux 1"
|
displayed_name = "IUT de Bordeaux 1"
|
||||||
|
classic_use_bonus_ues = True # s'applique aux UEs en DUT et LP
|
||||||
seuil_moy_gen = 10.0
|
seuil_moy_gen = 10.0
|
||||||
amplitude = 0.005
|
amplitude = 0.005
|
||||||
|
|
||||||
@ -576,17 +585,15 @@ class BonusVilleAvray(BonusSport):
|
|||||||
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||||
"""calcul du bonus"""
|
"""calcul du bonus"""
|
||||||
# Calcule moyenne pondérée des notes de sport:
|
# Calcule moyenne pondérée des notes de sport:
|
||||||
bonus_moy_gen_arr = np.sum(
|
bonus_moy_arr = np.sum(
|
||||||
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
|
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
|
||||||
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
|
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
|
||||||
bonus_moy_gen_arr[bonus_moy_gen_arr >= 10.0] = 0.1
|
bonus_moy_arr[bonus_moy_arr >= 10.0] = 0.1
|
||||||
bonus_moy_gen_arr[bonus_moy_gen_arr >= 12.0] = 0.2
|
bonus_moy_arr[bonus_moy_arr >= 12.0] = 0.2
|
||||||
bonus_moy_gen_arr[bonus_moy_gen_arr >= 16.0] = 0.3
|
bonus_moy_arr[bonus_moy_arr >= 16.0] = 0.3
|
||||||
|
|
||||||
# Bonus moyenne générale, et 0 sur les UE
|
# Bonus moyenne générale, et 0 sur les UE
|
||||||
self.bonus_moy_gen = pd.Series(
|
self.bonus_moy_gen = pd.Series(bonus_moy_arr, index=self.etuds_idx, dtype=float)
|
||||||
bonus_moy_gen_arr, index=self.etuds_idx, dtype=float
|
|
||||||
)
|
|
||||||
if self.bonus_max is not None:
|
if self.bonus_max is not None:
|
||||||
# Seuil: bonus (sur moy. gen.) limité à bonus_max points
|
# Seuil: bonus (sur moy. gen.) limité à bonus_max points
|
||||||
self.bonus_moy_gen = self.bonus_moy_gen.clip(upper=self.bonus_max)
|
self.bonus_moy_gen = self.bonus_moy_gen.clip(upper=self.bonus_max)
|
||||||
|
@ -40,6 +40,7 @@ import pandas as pd
|
|||||||
from app import db
|
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_exceptions import ScoValueError
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
|
|
||||||
|
|
||||||
@ -269,7 +270,8 @@ def load_evaluations_poids(moduleimpl_id: int) -> tuple[pd.DataFrame, list]:
|
|||||||
"""Charge poids des évaluations d'un module et retourne un dataframe
|
"""Charge poids des évaluations d'un module et retourne un dataframe
|
||||||
rows = evaluations, columns = UE, value = poids (float).
|
rows = evaluations, columns = UE, value = poids (float).
|
||||||
Les valeurs manquantes (évaluations sans coef vers des UE) sont
|
Les valeurs manquantes (évaluations sans coef vers des UE) sont
|
||||||
remplies: 1 si le coef de ce module dans l'UE est non nul, zéro sinon.
|
remplies: 1 si le coef de ce module dans l'UE est non nul, zéro sinon
|
||||||
|
(sauf pour module bonus, defaut à 1)
|
||||||
Résultat: (evals_poids, liste de UEs du semestre sauf le sport)
|
Résultat: (evals_poids, liste de UEs du semestre sauf le sport)
|
||||||
"""
|
"""
|
||||||
modimpl: ModuleImpl = ModuleImpl.query.get(moduleimpl_id)
|
modimpl: ModuleImpl = ModuleImpl.query.get(moduleimpl_id)
|
||||||
@ -287,11 +289,13 @@ 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...
|
pass # poids vers des UE qui n'existent plus ou sont dans un autre semestre...
|
||||||
|
|
||||||
# Initialise poids non enregistrés:
|
# Initialise poids non enregistrés:
|
||||||
|
default_poids = 1.0 if modimpl.module.ue.type == UE_SPORT else 0.0
|
||||||
|
|
||||||
if np.isnan(evals_poids.values.flat).any():
|
if np.isnan(evals_poids.values.flat).any():
|
||||||
ue_coefs = modimpl.module.get_ue_coef_dict()
|
ue_coefs = modimpl.module.get_ue_coef_dict()
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
evals_poids[ue.id][evals_poids[ue.id].isna()] = (
|
evals_poids[ue.id][evals_poids[ue.id].isna()] = (
|
||||||
1 if ue_coefs.get(ue.id, 0.0) > 0 else 0
|
1 if ue_coefs.get(ue.id, default_poids) > 0 else 0
|
||||||
)
|
)
|
||||||
|
|
||||||
return evals_poids, ues
|
return evals_poids, ues
|
||||||
|
@ -36,6 +36,7 @@ from app.models import UniteEns, Module, ModuleImpl, ModuleUECoef
|
|||||||
from app.comp import moy_mod
|
from app.comp import moy_mod
|
||||||
from app.models.formsemestre import FormSemestre
|
from app.models.formsemestre import FormSemestre
|
||||||
from app.scodoc import sco_codes_parcours
|
from app.scodoc import sco_codes_parcours
|
||||||
|
from app.scodoc.sco_codes_parcours import UE_SPORT
|
||||||
from app.scodoc.sco_utils import ModuleType
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
|
|
||||||
@ -44,10 +45,10 @@ def df_load_module_coefs(formation_id: int, semestre_idx: int = None) -> pd.Data
|
|||||||
|
|
||||||
En APC, ces coefs lient les modules à chaque UE.
|
En APC, ces coefs lient les modules à chaque UE.
|
||||||
|
|
||||||
Résultat: (module_coefs_df, ues, modules)
|
Résultat: (module_coefs_df, ues_no_bonus, modules)
|
||||||
DataFrame rows = UEs, columns = modules, value = coef.
|
DataFrame rows = UEs, columns = modules, value = coef.
|
||||||
|
|
||||||
Considère toutes les UE (sauf sport) et modules du semestre.
|
Considère toutes les UE sauf bonus et tous les modules du semestre.
|
||||||
Les coefs non définis (pas en base) sont mis à zéro.
|
Les coefs non définis (pas en base) sont mis à zéro.
|
||||||
|
|
||||||
Si semestre_idx None, prend toutes les UE de la formation.
|
Si semestre_idx None, prend toutes les UE de la formation.
|
||||||
@ -92,7 +93,17 @@ def df_load_module_coefs(formation_id: int, semestre_idx: int = None) -> pd.Data
|
|||||||
module_coefs_df[mod_coef.module_id][mod_coef.ue_id] = mod_coef.coef
|
module_coefs_df[mod_coef.module_id][mod_coef.ue_id] = mod_coef.coef
|
||||||
# silently ignore coefs associated to other modules (ie when module_type is changed)
|
# silently ignore coefs associated to other modules (ie when module_type is changed)
|
||||||
|
|
||||||
module_coefs_df.fillna(value=0, inplace=True)
|
# Initialisation des poids non fixés:
|
||||||
|
# 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
|
||||||
|
if (mod.module_type == ModuleType.STANDARD) and (mod.ue.type == UE_SPORT)
|
||||||
|
else 0.0
|
||||||
|
for mod in modules
|
||||||
|
}
|
||||||
|
|
||||||
|
module_coefs_df.fillna(value=default_poids, inplace=True)
|
||||||
|
|
||||||
return module_coefs_df, ues, modules
|
return module_coefs_df, ues, modules
|
||||||
|
|
||||||
@ -104,9 +115,9 @@ def df_load_modimpl_coefs(
|
|||||||
|
|
||||||
Comme df_load_module_coefs mais prend seulement les UE
|
Comme df_load_module_coefs mais prend seulement les UE
|
||||||
et modules du formsemestre.
|
et modules du formsemestre.
|
||||||
Si ues et modimpls sont None, prend tous ceux du formsemestre.
|
Si ues et modimpls sont None, prend tous ceux du formsemestre (sauf ue bonus).
|
||||||
Résultat: (module_coefs_df, ues, modules)
|
Résultat: (module_coefs_df, ues, modules)
|
||||||
DataFrame rows = UEs (avec bonus), columns = modimpl, value = coef.
|
DataFrame rows = UEs (sans bonus), columns = modimpl, value = coef.
|
||||||
"""
|
"""
|
||||||
if ues is None:
|
if ues is None:
|
||||||
ues = formsemestre.query_ues().all()
|
ues = formsemestre.query_ues().all()
|
||||||
@ -124,7 +135,19 @@ def df_load_modimpl_coefs(
|
|||||||
|
|
||||||
for mod_coef in mod_coefs:
|
for mod_coef in mod_coefs:
|
||||||
modimpl_coefs_df[mod2impl[mod_coef.module_id]][mod_coef.ue_id] = mod_coef.coef
|
modimpl_coefs_df[mod2impl[mod_coef.module_id]][mod_coef.ue_id] = mod_coef.coef
|
||||||
modimpl_coefs_df.fillna(value=0, inplace=True)
|
|
||||||
|
# Initialisation des poids non fixés:
|
||||||
|
# 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
|
||||||
|
if (modimpl.module.module_type == ModuleType.STANDARD)
|
||||||
|
and (modimpl.module.ue.type == UE_SPORT)
|
||||||
|
else 0.0
|
||||||
|
for modimpl in formsemestre.modimpls_sorted
|
||||||
|
}
|
||||||
|
|
||||||
|
modimpl_coefs_df.fillna(value=default_poids, inplace=True)
|
||||||
return modimpl_coefs_df, ues, modimpls
|
return modimpl_coefs_df, ues, modimpls
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,22 +40,22 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
|||||||
) = moy_ue.notes_sem_load_cube(self.formsemestre)
|
) = moy_ue.notes_sem_load_cube(self.formsemestre)
|
||||||
self.modimpl_inscr_df = inscr_mod.df_load_modimpl_inscr(self.formsemestre)
|
self.modimpl_inscr_df = inscr_mod.df_load_modimpl_inscr(self.formsemestre)
|
||||||
self.modimpl_coefs_df, _, _ = moy_ue.df_load_modimpl_coefs(
|
self.modimpl_coefs_df, _, _ = moy_ue.df_load_modimpl_coefs(
|
||||||
self.formsemestre, ues=self.ues, modimpls=self.formsemestre.modimpls_sorted
|
self.formsemestre, modimpls=self.formsemestre.modimpls_sorted
|
||||||
)
|
)
|
||||||
# l'idx de la colonne du mod modimpl.id est
|
# l'idx de la colonne du mod modimpl.id est
|
||||||
# modimpl_coefs_df.columns.get_loc(modimpl.id)
|
# modimpl_coefs_df.columns.get_loc(modimpl.id)
|
||||||
# idx de l'UE: modimpl_coefs_df.index.get_loc(ue.id)
|
# idx de l'UE: modimpl_coefs_df.index.get_loc(ue.id)
|
||||||
|
|
||||||
# Elimine les coefs des UE bonus sports
|
# Elimine les coefs des UE bonus sports XXX inutile car df_load_modimpl_coefs sans bonus
|
||||||
no_bonus = [ue.type != UE_SPORT for ue in self.ues]
|
# no_bonus = [ue.type != UE_SPORT for ue in self.ues]
|
||||||
modimpl_coefs_no_bonus_df = self.modimpl_coefs_df[no_bonus]
|
# modimpl_coefs_no_bonus_df = self.modimpl_coefs_df[no_bonus]
|
||||||
self.etud_moy_ue = moy_ue.compute_ue_moys_apc(
|
self.etud_moy_ue = moy_ue.compute_ue_moys_apc(
|
||||||
self.sem_cube,
|
self.sem_cube,
|
||||||
self.etuds,
|
self.etuds,
|
||||||
self.formsemestre.modimpls_sorted,
|
self.formsemestre.modimpls_sorted,
|
||||||
self.ues,
|
self.ues,
|
||||||
self.modimpl_inscr_df,
|
self.modimpl_inscr_df,
|
||||||
modimpl_coefs_no_bonus_df,
|
self.modimpl_coefs_df,
|
||||||
)
|
)
|
||||||
# Les coefficients d'UE ne sont pas utilisés en APC
|
# Les coefficients d'UE ne sont pas utilisés en APC
|
||||||
self.etud_coef_ue_df = pd.DataFrame(
|
self.etud_coef_ue_df = pd.DataFrame(
|
||||||
@ -63,6 +63,12 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# --- Bonus Sport & Culture
|
# --- Bonus Sport & Culture
|
||||||
|
modimpl_sport = [
|
||||||
|
modimpl
|
||||||
|
for modimpl in self.formsemestre.modimpls_sorted
|
||||||
|
if modimpl.module.ue.type == UE_SPORT
|
||||||
|
]
|
||||||
|
if len(modimpl_sport) > 0:
|
||||||
bonus_class = ScoDocSiteConfig.get_bonus_sport_class()
|
bonus_class = ScoDocSiteConfig.get_bonus_sport_class()
|
||||||
if bonus_class is not None:
|
if bonus_class is not None:
|
||||||
bonus: BonusSport = bonus_class(
|
bonus: BonusSport = bonus_class(
|
||||||
@ -80,8 +86,10 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
|||||||
self.etud_moy_ue.clip(lower=0.0, upper=20.0, inplace=True)
|
self.etud_moy_ue.clip(lower=0.0, upper=20.0, inplace=True)
|
||||||
|
|
||||||
# Moyenne générale indicative:
|
# Moyenne générale indicative:
|
||||||
|
# (note: le bonus sport a déjà été appliqué aux moyenens d'UE, et impacte
|
||||||
|
# donc la moyenne indicative)
|
||||||
self.etud_moy_gen = moy_sem.compute_sem_moys_apc(
|
self.etud_moy_gen = moy_sem.compute_sem_moys_apc(
|
||||||
self.etud_moy_ue, modimpl_coefs_no_bonus_df
|
self.etud_moy_ue, self.modimpl_coefs_df
|
||||||
)
|
)
|
||||||
self.etud_moy_gen_ranks = moy_sem.comp_ranks_series(self.etud_moy_gen)
|
self.etud_moy_gen_ranks = moy_sem.comp_ranks_series(self.etud_moy_gen)
|
||||||
|
|
||||||
|
@ -219,18 +219,18 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
ues.append(d)
|
ues.append(d)
|
||||||
return ues
|
return ues
|
||||||
|
|
||||||
def get_modimpls_dict(self, ue_id=None):
|
def get_modimpls_dict(self, ue_id=None) -> list[dict]:
|
||||||
"""Liste des modules pour une UE (ou toutes si ue_id==None),
|
"""Liste des modules pour une UE (ou toutes si ue_id==None),
|
||||||
triés par numéros (selon le type de formation)
|
triés par numéros (selon le type de formation)
|
||||||
"""
|
"""
|
||||||
if ue_id is None:
|
modimpls_dict = []
|
||||||
return [m.to_dict() for m in self.formsemestre.modimpls_sorted]
|
for modimpl in self.formsemestre.modimpls_sorted:
|
||||||
else:
|
if ue_id == None or modimpl.module.ue.id == ue_id:
|
||||||
return [
|
d = modimpl.to_dict()
|
||||||
m.to_dict()
|
# compat ScoDoc < 9.2: ajoute matières
|
||||||
for m in self.formsemestre.modimpls_sorted
|
d["mat"] = modimpl.module.matiere.to_dict()
|
||||||
if m.module.ue.id == ue_id
|
modimpls_dict.append(d)
|
||||||
]
|
return modimpls_dict
|
||||||
|
|
||||||
def get_etud_decision_sem(self, etudid: int) -> dict:
|
def get_etud_decision_sem(self, etudid: int) -> dict:
|
||||||
"""Decision du jury prise pour cet etudiant, ou None s'il n'y en pas eu.
|
"""Decision du jury prise pour cet etudiant, ou None s'il n'y en pas eu.
|
||||||
@ -259,13 +259,10 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
return ""
|
return ""
|
||||||
return ins.etat
|
return ins.etat
|
||||||
|
|
||||||
def get_etud_moy_gen(self, etudid): # -> float | str
|
def get_etud_mat_moy(self, matiere_id, etudid):
|
||||||
"""Moyenne générale de cet etudiant dans ce semestre.
|
"""moyenne d'un étudiant dans une matière (ou NA si pas de notes)"""
|
||||||
Prend(ra) en compte les UE capitalisées. (TODO) XXX
|
# non supporté en 9.2
|
||||||
Si apc, moyenne indicative.
|
return "na"
|
||||||
Si pas de notes: 'NA'
|
|
||||||
"""
|
|
||||||
return self.etud_moy_gen[etudid]
|
|
||||||
|
|
||||||
def get_etud_mod_moy(self, moduleimpl_id: int, etudid: int) -> float:
|
def get_etud_mod_moy(self, moduleimpl_id: int, etudid: int) -> float:
|
||||||
"""La moyenne de l'étudiant dans le moduleimpl
|
"""La moyenne de l'étudiant dans le moduleimpl
|
||||||
@ -274,6 +271,14 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError() # virtual method
|
raise NotImplementedError() # virtual method
|
||||||
|
|
||||||
|
def get_etud_moy_gen(self, etudid): # -> float | str
|
||||||
|
"""Moyenne générale de cet etudiant dans ce semestre.
|
||||||
|
Prend(ra) en compte les UE capitalisées. (TODO) XXX
|
||||||
|
Si apc, moyenne indicative.
|
||||||
|
Si pas de notes: 'NA'
|
||||||
|
"""
|
||||||
|
return self.etud_moy_gen[etudid]
|
||||||
|
|
||||||
def get_etud_ue_status(self, etudid: int, ue_id: int):
|
def get_etud_ue_status(self, etudid: int, ue_id: int):
|
||||||
coef_ue = self.etud_coef_ue_df[ue_id][etudid]
|
coef_ue = self.etud_coef_ue_df[ue_id][etudid]
|
||||||
return {
|
return {
|
||||||
|
@ -161,3 +161,16 @@ class Matiere(db.Model):
|
|||||||
numero = db.Column(db.Integer) # ordre de présentation
|
numero = db.Column(db.Integer) # ordre de présentation
|
||||||
|
|
||||||
modules = db.relationship("Module", lazy="dynamic", backref="matiere")
|
modules = db.relationship("Module", lazy="dynamic", backref="matiere")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"""<{self.__class__.__name__}(id={self.id}, ue_id={
|
||||||
|
self.ue_id}, titre='{self.titre}')>"""
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
"""as a dict, with the same conversions as in ScoDoc7"""
|
||||||
|
e = dict(self.__dict__)
|
||||||
|
e.pop("_sa_instance_state", None)
|
||||||
|
# ScoDoc7 output_formators
|
||||||
|
e["ue_id"] = self.id
|
||||||
|
e["numero"] = e["numero"] if e["numero"] else 0
|
||||||
|
return e
|
||||||
|
@ -310,7 +310,10 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
else:
|
else:
|
||||||
x = ""
|
x = ""
|
||||||
if isinstance(x, str):
|
if isinstance(x, str):
|
||||||
|
if nt.bonus_ues is None:
|
||||||
u["cur_moy_ue_txt"] = "pas de bonus"
|
u["cur_moy_ue_txt"] = "pas de bonus"
|
||||||
|
else:
|
||||||
|
u["cur_moy_ue_txt"] = "bonus appliqué sur les UEs"
|
||||||
else:
|
else:
|
||||||
u["cur_moy_ue_txt"] = "bonus de %.3g points" % x
|
u["cur_moy_ue_txt"] = "bonus de %.3g points" % x
|
||||||
u["moy_ue_txt"] = scu.fmt_note(ue_status["moy"])
|
u["moy_ue_txt"] = scu.fmt_note(ue_status["moy"])
|
||||||
@ -380,7 +383,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if prefs["bul_show_ue_rangs"] and ue["type"] != sco_codes_parcours.UE_SPORT:
|
if prefs["bul_show_ue_rangs"] and ue["type"] != sco_codes_parcours.UE_SPORT:
|
||||||
if ue_attente: # nt.get_moduleimpls_attente():
|
if ue_attente or nt.ue_rangs[ue["ue_id"]][0] is None:
|
||||||
u["ue_descr_txt"] = "%s/%s" % (
|
u["ue_descr_txt"] = "%s/%s" % (
|
||||||
scu.RANG_ATTENTE_STR,
|
scu.RANG_ATTENTE_STR,
|
||||||
nt.ue_rangs[ue["ue_id"]][1],
|
nt.ue_rangs[ue["ue_id"]][1],
|
||||||
@ -398,8 +401,8 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
I["ues"].append(u) # ne montre pas les UE si non inscrit
|
I["ues"].append(u) # ne montre pas les UE si non inscrit
|
||||||
|
|
||||||
# Accès par matieres
|
# Accès par matieres
|
||||||
# voir si on supporte encore cela en #sco92 XXX
|
# En #sco92, pas d'information
|
||||||
# I["matieres_modules"].update(_sort_mod_by_matiere(modules, nt, etudid))
|
I["matieres_modules"].update(_sort_mod_by_matiere(modules, nt, etudid))
|
||||||
|
|
||||||
#
|
#
|
||||||
C = make_context_dict(I["sem"], I["etud"])
|
C = make_context_dict(I["sem"], I["etud"])
|
||||||
@ -616,6 +619,9 @@ def _ue_mod_bulletin(etudid, formsemestre_id, ue_id, modimpls, nt, version):
|
|||||||
# Classement
|
# Classement
|
||||||
if bul_show_mod_rangs and mod["mod_moy_txt"] != "-" and not is_malus:
|
if bul_show_mod_rangs and mod["mod_moy_txt"] != "-" and not is_malus:
|
||||||
rg = nt.mod_rangs[modimpl["moduleimpl_id"]]
|
rg = nt.mod_rangs[modimpl["moduleimpl_id"]]
|
||||||
|
if rg[0] is None:
|
||||||
|
mod["mod_rang_txt"] = ""
|
||||||
|
else:
|
||||||
if mod_attente: # nt.get_moduleimpls_attente():
|
if mod_attente: # nt.get_moduleimpls_attente():
|
||||||
mod["mod_rang"] = scu.RANG_ATTENTE_STR
|
mod["mod_rang"] = scu.RANG_ATTENTE_STR
|
||||||
else:
|
else:
|
||||||
|
Loading…
Reference in New Issue
Block a user