forked from ScoDoc/ScoDoc
Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc into dev93
This commit is contained in:
commit
9cc7a80fb0
@ -730,7 +730,7 @@ class BonusIUTRennes1(BonusSportAdditif):
|
||||
seuil_moy_gen = 10.0
|
||||
proportion_point = 1 / 20.0
|
||||
classic_use_bonus_ues = False
|
||||
# Adapté de BonusTarbes, mais s'applique aussi en classic
|
||||
# S'applique aussi en classic, sur la moy. gen.
|
||||
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||
"""calcul du bonus"""
|
||||
# Prend la note de chaque modimpl, sans considération d'UE
|
||||
@ -771,18 +771,59 @@ class BonusLaRochelle(BonusSportAdditif):
|
||||
"""Calcul bonus modules optionnels (sport, culture), règle IUT de La Rochelle.
|
||||
|
||||
<ul>
|
||||
<li>Si la note de sport est comprise entre 0 et 10 : pas d'ajout de point.</li>
|
||||
<li>Si la note de sport est comprise entre 10 et 20 : ajout de 1% de cette
|
||||
note sur la moyenne générale du semestre (ou sur les UE en BUT).</li>
|
||||
<li>Si la note de sport est comprise entre 0 et 10 : pas d’ajout de point.</li>
|
||||
<li>Si la note de sport est comprise entre 10 et 20 :
|
||||
<ul>
|
||||
<li>Pour le BUT, application pour chaque UE du semestre :
|
||||
<ul>
|
||||
<li>pour une note entre 18 et 20 => + 0,10 points</li>
|
||||
<li>pour une note entre 16 et 17,99 => + 0,08 points</li>
|
||||
<li>pour une note entre 14 et 15,99 => + 0,06 points</li>
|
||||
<li>pour une note entre 12 et 13,99 => + 0,04 points</li>
|
||||
<li>pour une note entre 10 et 11,99 => + 0,02 points</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Pour les DUT/LP :
|
||||
ajout de 1% de la note sur la moyenne générale du semestre
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
"""
|
||||
|
||||
name = "bonus_iutlr"
|
||||
displayed_name = "IUT de La Rochelle"
|
||||
|
||||
seuil_moy_gen = 10.0 # si bonus > 10,
|
||||
seuil_comptage = 0.0 # tous les points sont comptés
|
||||
proportion_point = 0.01 # 1%
|
||||
|
||||
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||
"""calcul du bonus"""
|
||||
# La date du semestre ?
|
||||
if self.formsemestre.formation.is_apc():
|
||||
if 0 in sem_modimpl_moys_inscrits.shape:
|
||||
# pas d'étudiants ou pas d'UE ou pas de module...
|
||||
return
|
||||
# Calcule moyenne pondérée des notes de sport:
|
||||
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
||||
bonus_moy_arr = np.sum(
|
||||
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
|
||||
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
|
||||
np.nan_to_num(bonus_moy_arr, nan=0.0, copy=False)
|
||||
bonus_moy_arr[bonus_moy_arr < 10.0] = 0.0
|
||||
bonus_moy_arr[bonus_moy_arr >= 18.0] = 0.10
|
||||
bonus_moy_arr[bonus_moy_arr >= 16.0] = 0.08
|
||||
bonus_moy_arr[bonus_moy_arr >= 14.0] = 0.06
|
||||
bonus_moy_arr[bonus_moy_arr >= 12.0] = 0.04
|
||||
bonus_moy_arr[bonus_moy_arr >= 10.0] = 0.02
|
||||
self.bonus_additif(bonus_moy_arr)
|
||||
else:
|
||||
# DUT et LP:
|
||||
return super().compute_bonus(
|
||||
sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan
|
||||
)
|
||||
|
||||
|
||||
class BonusLeHavre(BonusSportAdditif):
|
||||
"""Bonus sport IUT du Havre sur les moyennes d'UE
|
||||
@ -1067,14 +1108,15 @@ class BonusStNazaire(BonusSportMultiplicatif):
|
||||
factor_max = 0.1 # 10% max
|
||||
|
||||
|
||||
class BonusTarbes(BonusSportAdditif):
|
||||
class BonusTarbes(BonusIUTRennes1):
|
||||
"""Calcul bonus optionnels (sport, culture), règle IUT de Tarbes.
|
||||
|
||||
<ul>
|
||||
<li>Les étudiants opeuvent suivre un ou plusieurs activités optionnelles notées.
|
||||
La meilleure des notes obtenue est prise en compte, si elle est supérieure à 10/20.
|
||||
</li>
|
||||
<li>Le trentième des points au dessus de 10 est ajouté à la moyenne des UE.
|
||||
<li>Le trentième des points au dessus de 10 est ajouté à la moyenne des UE en BUT,
|
||||
ou à la moyenne générale en DUT et LP.
|
||||
</li>
|
||||
<li> Exemple: un étudiant ayant 16/20 bénéficiera d'un bonus de (16-10)/30 = 0,2 points
|
||||
sur chaque UE.
|
||||
@ -1088,29 +1130,6 @@ class BonusTarbes(BonusSportAdditif):
|
||||
proportion_point = 1 / 30.0
|
||||
classic_use_bonus_ues = True
|
||||
|
||||
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||
"""calcul du bonus"""
|
||||
# Prend la note de chaque modimpl, sans considération d'UE
|
||||
if len(sem_modimpl_moys_inscrits.shape) > 2: # apc
|
||||
sem_modimpl_moys_inscrits = sem_modimpl_moys_inscrits[:, :, 0]
|
||||
# ici sem_modimpl_moys_inscrits est nb_etuds x nb_mods_bonus, en APC et en classic
|
||||
note_bonus_max = np.max(sem_modimpl_moys_inscrits, axis=1) # 1d, nb_etuds
|
||||
ues = self.formsemestre.query_ues(with_sport=False).all()
|
||||
ues_idx = [ue.id for ue in ues]
|
||||
|
||||
if self.formsemestre.formation.is_apc(): # --- BUT
|
||||
bonus_moy_arr = np.where(
|
||||
note_bonus_max > self.seuil_moy_gen,
|
||||
(note_bonus_max - self.seuil_moy_gen) * self.proportion_point,
|
||||
0.0,
|
||||
)
|
||||
self.bonus_ues = pd.DataFrame(
|
||||
np.stack([bonus_moy_arr] * len(ues)).T,
|
||||
index=self.etuds_idx,
|
||||
columns=ues_idx,
|
||||
dtype=float,
|
||||
)
|
||||
|
||||
|
||||
class BonusTours(BonusDirect):
|
||||
"""Calcul bonus sport & culture IUT Tours.
|
||||
|
@ -41,7 +41,8 @@ from app import db
|
||||
from app.models import ModuleImpl, Evaluation, EvaluationUEPoids
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_codes_parcours import UE_SPORT
|
||||
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc.sco_exceptions import ScoBugCatcher
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
|
||||
|
||||
@ -423,7 +424,9 @@ def moduleimpl_is_conforme(
|
||||
if nb_ues == 0:
|
||||
return False # situation absurde (pas d'UE)
|
||||
if len(modules_coefficients) != nb_ues:
|
||||
raise ValueError("moduleimpl_is_conforme: nb ue incoherent")
|
||||
# il arrive (#bug) que le cache ne soit pas à jour...
|
||||
sco_cache.invalidate_formsemestre()
|
||||
raise ScoBugCatcher("moduleimpl_is_conforme: nb ue incoherent")
|
||||
module_evals_poids = evals_poids.transpose().sum(axis=1).to_numpy() != 0
|
||||
check = all(
|
||||
(modules_coefficients[moduleimpl.module_id].to_numpy() != 0)
|
||||
|
@ -79,6 +79,15 @@ class UniteEns(db.Model):
|
||||
|
||||
return sco_edit_ue.ue_is_locked(self.id)
|
||||
|
||||
def can_be_deleted(self) -> bool:
|
||||
"""True si l'UE n'est pas utilisée dans des formsemestre
|
||||
et n'a pas de module rattachés
|
||||
"""
|
||||
# "pas un seul module de cette UE n'a de modimpl...""
|
||||
return (self.modules.count() == 0) or not any(
|
||||
m.modimpls.all() for m in self.modules
|
||||
)
|
||||
|
||||
def guess_semestre_idx(self) -> None:
|
||||
"""Lorsqu'on prend une ancienne formation non APC,
|
||||
les UE n'ont pas d'indication de semestre.
|
||||
|
@ -66,8 +66,9 @@ def formation_delete(formation_id=None, dialog_confirmed=False):
|
||||
sems = sco_formsemestre.do_formsemestre_list({"formation_id": formation_id})
|
||||
if sems:
|
||||
H.append(
|
||||
"""<p class="warning">Impossible de supprimer cette formation, car les sessions suivantes l'utilisent:</p>
|
||||
<ul>"""
|
||||
"""<p class="warning">Impossible de supprimer cette formation,
|
||||
car les sessions suivantes l'utilisent:</p>
|
||||
<ul>"""
|
||||
)
|
||||
for sem in sems:
|
||||
H.append(
|
||||
|
@ -204,7 +204,7 @@ def module_delete(module_id=None):
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
||||
f"""<h2>Suppression du module {module.titre} ({module.code})</h2>""",
|
||||
f"""<h2>Suppression du module {module.titre or "<em>sans titre</em>"} ({module.code})</h2>""",
|
||||
]
|
||||
|
||||
dest_url = url_for(
|
||||
@ -917,21 +917,13 @@ def module_count_moduleimpls(module_id):
|
||||
|
||||
def formation_add_malus_modules(formation_id, titre=None, redirect=True):
|
||||
"""Création d'un module de "malus" dans chaque UE d'une formation"""
|
||||
from app.scodoc import sco_edit_ue
|
||||
|
||||
ues = sco_edit_ue.ue_list(args={"formation_id": formation_id})
|
||||
formation = Formation.query.get_or_404(formation_id)
|
||||
|
||||
for ue in ues:
|
||||
# Un seul module de malus par UE:
|
||||
nb_mod_malus = len(
|
||||
[
|
||||
mod
|
||||
for mod in module_list(args={"ue_id": ue["ue_id"]})
|
||||
if mod["module_type"] == scu.ModuleType.MALUS
|
||||
]
|
||||
)
|
||||
if nb_mod_malus == 0:
|
||||
ue_add_malus_module(ue["ue_id"], titre=titre)
|
||||
for ue in formation.ues:
|
||||
ue_add_malus_module(ue, titre=titre)
|
||||
|
||||
formation.invalidate_cached_sems()
|
||||
|
||||
if redirect:
|
||||
return flask.redirect(
|
||||
@ -941,20 +933,22 @@ def formation_add_malus_modules(formation_id, titre=None, redirect=True):
|
||||
)
|
||||
|
||||
|
||||
def ue_add_malus_module(ue_id, titre=None, code=None):
|
||||
"""Add a malus module in this ue"""
|
||||
from app.scodoc import sco_edit_ue
|
||||
def ue_add_malus_module(ue: UniteEns, titre=None, code=None) -> int:
|
||||
"""Add a malus module in this ue.
|
||||
If already exists, do nothing.
|
||||
Returns id of malus module.
|
||||
"""
|
||||
modules_malus = [m for m in ue.modules if m.module_type == scu.ModuleType.MALUS]
|
||||
if len(modules_malus) > 0:
|
||||
return modules_malus[0].id # déjà existant
|
||||
|
||||
ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
|
||||
|
||||
if titre is None:
|
||||
titre = ""
|
||||
if code is None:
|
||||
code = "MALUS%d" % ue["numero"]
|
||||
titre = titre or ""
|
||||
code = code or f"MALUS{ue.numero}"
|
||||
|
||||
# Tout module doit avoir un semestre_id (indice 1, 2, ...)
|
||||
semestre_ids = sco_edit_ue.ue_list_semestre_ids(ue)
|
||||
if semestre_ids:
|
||||
if ue.semestre_idx is None:
|
||||
semestre_ids = sorted(list(set([m.semestre_id for m in ue.modules])))
|
||||
if len(semestre_ids) > 0:
|
||||
semestre_id = semestre_ids[0]
|
||||
else:
|
||||
# c'est ennuyeux: dans ce cas, on pourrait demander à indiquer explicitement
|
||||
@ -962,25 +956,35 @@ def ue_add_malus_module(ue_id, titre=None, code=None):
|
||||
raise ScoValueError(
|
||||
"Impossible d'ajouter un malus s'il n'y a pas d'autres modules"
|
||||
)
|
||||
else:
|
||||
semestre_id = ue.semestre_idx
|
||||
|
||||
# Matiere pour placer le module malus
|
||||
Matlist = sco_edit_matiere.matiere_list(args={"ue_id": ue_id})
|
||||
numero = max([mat["numero"] for mat in Matlist]) + 10
|
||||
matiere_id = sco_edit_matiere.do_matiere_create(
|
||||
{"ue_id": ue_id, "titre": "Malus", "numero": numero}
|
||||
)
|
||||
titre_matiere_malus = "Malus"
|
||||
|
||||
module_id = do_module_create(
|
||||
{
|
||||
"titre": titre,
|
||||
"code": code,
|
||||
"coefficient": 0.0, # unused
|
||||
"ue_id": ue_id,
|
||||
"matiere_id": matiere_id,
|
||||
"formation_id": ue["formation_id"],
|
||||
"semestre_id": semestre_id,
|
||||
"module_type": scu.ModuleType.MALUS,
|
||||
},
|
||||
)
|
||||
matieres_malus = [mat for mat in ue.matieres if mat.titre == titre_matiere_malus]
|
||||
if len(matieres_malus) > 0:
|
||||
# matière Malus déjà existante, l'utilise
|
||||
matiere = matieres_malus[0]
|
||||
else:
|
||||
if ue.matieres.count() > 0:
|
||||
numero = max([mat.numero for mat in ue.matieres]) + 10
|
||||
else:
|
||||
numero = 0
|
||||
matiere = Matiere(ue_id=ue.id, titre=titre_matiere_malus, numero=numero)
|
||||
db.session.add(matiere)
|
||||
|
||||
return module_id
|
||||
module = Module(
|
||||
titre=titre,
|
||||
code=code,
|
||||
coefficient=0.0,
|
||||
ue=ue,
|
||||
matiere=matiere,
|
||||
formation=ue.formation,
|
||||
semestre_id=semestre_id,
|
||||
module_type=scu.ModuleType.MALUS,
|
||||
)
|
||||
db.session.add(module)
|
||||
db.session.commit()
|
||||
|
||||
return module.id
|
||||
|
@ -143,14 +143,6 @@ def do_ue_create(args):
|
||||
return ue_id
|
||||
|
||||
|
||||
def can_delete_ue(ue: UniteEns) -> bool:
|
||||
"""True si l'UE n'est pas utilisée dans des formsemestre
|
||||
et n'a pas de module rattachés
|
||||
"""
|
||||
# "pas un seul module de cette UE n'a de modimpl...""
|
||||
return (ue.modules.count() == 0) and not any(m.modimpls.all() for m in ue.modules)
|
||||
|
||||
|
||||
def do_ue_delete(ue_id, delete_validations=False, force=False):
|
||||
"delete UE and attached matieres (but not modules)"
|
||||
from app.scodoc import sco_formations
|
||||
@ -159,9 +151,9 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
||||
ue = UniteEns.query.get_or_404(ue_id)
|
||||
formation_id = ue.formation_id
|
||||
semestre_idx = ue.semestre_idx
|
||||
if not can_delete_ue(ue):
|
||||
if not ue.can_be_deleted():
|
||||
raise ScoNonEmptyFormationObject(
|
||||
"UE",
|
||||
f"UE (id={ue.id}, dud)",
|
||||
msg=ue.titre,
|
||||
dest_url=url_for(
|
||||
"notes.ue_table",
|
||||
@ -553,9 +545,9 @@ def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
||||
semestre_idx=ue.semestre_idx,
|
||||
),
|
||||
)
|
||||
if not can_delete_ue(ue):
|
||||
if not ue.can_be_deleted():
|
||||
raise ScoNonEmptyFormationObject(
|
||||
"UE",
|
||||
f"UE",
|
||||
msg=ue.titre,
|
||||
dest_url=url_for(
|
||||
"notes.ue_table",
|
||||
@ -1367,16 +1359,6 @@ def ue_is_locked(ue_id):
|
||||
return len(r) > 0
|
||||
|
||||
|
||||
def ue_list_semestre_ids(ue: dict):
|
||||
"""Liste triée des numeros de semestres des modules dans cette UE
|
||||
Il est recommandable que tous les modules d'une UE aient le même indice de semestre.
|
||||
Mais cela n'a pas toujours été le cas dans les programmes pédagogiques officiels,
|
||||
aussi ScoDoc laisse le choix.
|
||||
"""
|
||||
modules = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]})
|
||||
return sorted(list(set([mod["semestre_id"] for mod in modules])))
|
||||
|
||||
|
||||
UE_PALETTE = [
|
||||
"#B80004", # rouge
|
||||
"#F97B3D", # Orange Crayola
|
||||
|
Loading…
Reference in New Issue
Block a user