diff --git a/app/comp/bonus_spo.py b/app/comp/bonus_spo.py index 0e63adaf..d442647a 100644 --- a/app/comp/bonus_spo.py +++ b/app/comp/bonus_spo.py @@ -16,12 +16,7 @@ import datetime import numpy as np import pandas as pd -from app import db -from app import models -from app.models import UniteEns, Module, ModuleImpl, ModuleUECoef -from app.comp import moy_mod from app.models.formsemestre import FormSemestre -from app.scodoc import bonus_sport from app.scodoc.sco_codes_parcours import UE_SPORT from app.scodoc.sco_utils import ModuleType @@ -57,6 +52,8 @@ class BonusSport: # Si vrai, en APC, si le bonus UE est None, reporte le bonus moy gen: apc_apply_bonus_mg_to_ues = True + # Si True, reporte toujours le bonus moy gen sur les UE (même en formations classiques) + apply_bonus_mg_to_ues = False # Attributs virtuels: seuil_moy_gen = None proportion_point = None @@ -167,10 +164,9 @@ class BonusSport: """Les bonus à appliquer aux UE Résultat: DataFrame de float, index etudid, columns: ue.id """ - if ( - self.formsemestre.formation.is_apc() - and self.apc_apply_bonus_mg_to_ues - and self.bonus_ues is None + if self.bonus_ues is None and ( + (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) @@ -189,10 +185,10 @@ class BonusSport: return self.bonus_moy_gen -class BonusSportSimples(BonusSport): - """Les bonus sport simples calcule un bonus à partir des notes moyennes de modules - de l'UE sport, et ce bonus est soit appliqué sur la moyenne générale (formations classiques), - soit réparti sur les UE (formations APC). +class BonusSportAdditif(BonusSport): + """Bonus sport simples calcule un bonus à partir des notes moyennes de modules + de l'UE sport, et ce bonus est soit ajouté à la moyenne générale (formations classiques), + soit ajouté à chaque UE (formations APC). Le bonus est par défaut calculé comme: Les points au-dessus du seuil (par défaut) 10 sur 20 obtenus dans chacun des @@ -231,7 +227,7 @@ class BonusSportSimples(BonusSport): # bonus_ue = np.stack([modimpl_coefs_spo.T] * nb_ues) -class BonusIUTV(BonusSportSimples): +class BonusIUTV(BonusSportAdditif): """Calcul bonus modules optionels (sport, culture), règle IUT Villetaneuse Les étudiants de l'IUT peuvent suivre des enseignements optionnels @@ -246,7 +242,7 @@ class BonusIUTV(BonusSportSimples): pass # oui, c'ets le bonus par défaut -class BonusDirect(BonusSportSimples): +class BonusDirect(BonusSportAdditif): """Bonus direct: les points sont directement ajoutés à la moyenne générale. Les coefficients sont ignorés: tous les points de bonus sont sommés. (rappel: la note est ramenée sur 20 avant application). @@ -264,7 +260,7 @@ class BonusStDenis(BonusIUTV): bonus_moy_gen_limit = 0.5 -class BonusColmar(BonusSportSimples): +class BonusColmar(BonusSportAdditif): """Calcul bonus modules optionels (sport, culture), règle IUT Colmar. Les étudiants de l'IUT peuvent suivre des enseignements optionnels @@ -298,11 +294,8 @@ class BonusTours(BonusDirect): proportion_point = 1.0 / 40.0 -# ---- Un peu moins simples (mais pas trop compliqué) - - # Bonus simple, mais avec changement de paramètres en 2010 ! -class BonusLille(BonusSportSimples): +class BonusLille(BonusSportAdditif): """Calcul bonus modules optionels (sport, culture), règle IUT Villeneuve d'Ascq Les étudiants de l'IUT peuvent suivre des enseignements optionnels @@ -328,25 +321,39 @@ class BonusLille(BonusSportSimples): ) -def bonus_iut1grenoble_2017(notes_sport, coefs, infos=None): - """Calcul bonus sport IUT Grenoble sur la moyenne générale (version 2017) +class BonusSportMultiplicatif(BonusSport): + """Bonus sport qui multiplie les moyennes d'UE par un facteur""" - La note de sport de nos étudiants va de 0 à 5 points. - Chaque point correspond à un % qui augmente la moyenne de chaque UE et la moyenne générale. - Par exemple : note de sport 2/5 : la moyenne générale sera augmentée de 2%. + 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 - Calcul ici du bonus sur moyenne générale - """ - # les coefs sont ignorés - # notes de 0 à 5/20 - points = sum([x for x in notes_sport]) - factor = (points / 4.0) / 100.0 - bonus = infos["moy"] * factor + # C'est un bonus "multiplicatif": on l'exprime en additif, + # sur chaque moyenne d'UE m_0 + # Augmenter de 5% correspond à multiplier par a=1.05 + # m_1 = a . m_0 + # m_1 = m_0 + bonus + # bonus = m_0 (a - 1) + def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan): + """calcul du bonus""" + # Calcule moyenne pondérée des notes de sport: + notes = np.sum( + sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1 + ) / np.sum(modimpl_coefs_etuds_no_nan, axis=1) + notes = np.nan_to_num(notes, copy=False) - return bonus + factor = (notes - self.seuil_moy_gen) * self.amplitude # 5% si note=20 + factor[factor <= 0] = 0.0 # note < seuil_moy_gen, pas de bonus + + # S'applique qu'aux moyennes d'UE + bonus = self.etud_moy_ue * factor + self.bonus_ues = bonus # DataFrame + + if not self.formsemestre.formation.is_apc(): + # s'applique à la moyenne générale + self.bonus_moy_gen = bonus -class BonusGrenobleIUT1(BonusSport): +class BonusGrenobleIUT1(BonusSportMultiplicatif): """Bonus IUT1 de Grenoble À compter de sept. 2021: @@ -372,27 +379,63 @@ class BonusGrenobleIUT1(BonusSport): # m_1 = m_0 + bonus # bonus = m_0 (a - 1) def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan): - """calcul du bonus""" - # Calcule moyenne pondérée des notes de sport: - notes = np.sum( - sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1 - ) / np.sum(modimpl_coefs_etuds_no_nan, axis=1) - notes = np.nan_to_num(notes, copy=False) + """calcul du bonus, avec réglage différent suivant la date""" if self.formsemestre.date_debut > datetime.date(2021, 7, 15): - factor = (notes - 10.0) * 0.005 # 5% si note=20 - factor[factor <= 0] = 0.0 # note < 10, pas de bonus + self.seuil_moy_gen = 10.0 + self.amplitude = 0.005 else: # anciens semestres - factor = notes / 400.0 - factor[factor <= 0] = 0.0 # facteur 1 si bonus nul + self.seuil_moy_gen = 0.0 + self.amplitude = 1 / 400.0 - # S'applique qu'aux moyennes d'UE - bonus = self.etud_moy_ue * factor - self.bonus_ues = bonus # DataFrame + super().compute_bonus(sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan) - if not self.formsemestre.formation.is_apc(): - # s'applique à la moyenne générale - self.bonus_moy_gen = bonus + +class BonusLeHavre(BonusSportMultiplicatif): + """Bonus sport IUT du Havre sur moyenne générale et UE + + Les points des modules bonus au dessus de 10/20 sont ajoutés, + et les moyennes d'UE augmentées de 5% de ces points. + """ + + name = "bonus_iutlh" + 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 + + +class BonusNantes(BonusSportAdditif): + """IUT de Nantes (Septembre 2018) + + Nous avons différents types de bonification + (sport, culture, engagement citoyen). + + Nous ajoutons aux moyennes une bonification de 0,2 pour chaque item + la bonification totale ne doit pas excéder les 0,5 point. + Sur le bulletin nous ne mettons pas une note sur 20 mais directement les bonifications. + + Dans ScoDoc: on a déclarera une UE "sport&culture" dans laquelle on aura des modules + pour chaque activité (Sport, Associations, ...) + avec à chaque fois une note (ScoDoc l'affichera comme une note sur 20, mais en fait ce sera la + valeur de la bonification: entrer 0,1/20 signifiera un bonus de 0,1 point la moyenne générale) + """ + + name = "bonus_nantes" + seuil_moy_gen = 0.0 # seuls les points au dessus du seuil sont comptés + proportion_point = 1 # multiplie les points au dessus du seuil + bonus_moy_gen_limit = 0.5 # plafonnement à 0.5 points + + +class BonusRoanne(BonusSportAdditif): + """IUT de Roanne. + + Le bonus est compris entre 0 et 0.35 point + et est toujours appliqué aux UEs. + """ + + name = "bonus_iutr" + seuil_moy_gen = 0.0 + bonus_moy_gen_limit = 0.35 # plafonnement à 0.35 points + apply_bonus_mg_to_ues = True # sur les UE, même en DUT et LP class BonusVilleAvray(BonusSport):