From a67515d560aa910484d8c6978fc82e61698a3deb Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 18 Feb 2022 19:34:40 +0100 Subject: [PATCH 01/13] =?UTF-8?q?Ignore=20les=20poids=20en=20BD=20des=20?= =?UTF-8?q?=C3=A9vals=20des=20modules=20non=20APC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/comp/moy_mod.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/comp/moy_mod.py b/app/comp/moy_mod.py index f30f04912..2ec6c35f2 100644 --- a/app/comp/moy_mod.py +++ b/app/comp/moy_mod.py @@ -359,6 +359,10 @@ def load_evaluations_poids(moduleimpl_id: int) -> tuple[pd.DataFrame, list]: 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 (sauf pour module bonus, defaut à 1) + + Si le module n'est pas une ressource ou une SAE, ne charge pas de poids + et renvoie toujours les poids par défaut. + Résultat: (evals_poids, liste de UEs du semestre sauf le sport) """ modimpl: ModuleImpl = ModuleImpl.query.get(moduleimpl_id) @@ -367,13 +371,17 @@ def load_evaluations_poids(moduleimpl_id: int) -> tuple[pd.DataFrame, list]: ue_ids = [ue.id for ue in ues] evaluation_ids = [evaluation.id for evaluation in evaluations] evals_poids = pd.DataFrame(columns=ue_ids, index=evaluation_ids, dtype=float) - for ue_poids in EvaluationUEPoids.query.join( - EvaluationUEPoids.evaluation - ).filter_by(moduleimpl_id=moduleimpl_id): - try: - evals_poids[ue_poids.ue_id][ue_poids.evaluation_id] = ue_poids.poids - except KeyError as exc: - pass # poids vers des UE qui n'existent plus ou sont dans un autre semestre... + if ( + modimpl.module.module_type == ModuleType.RESSOURCE + or modimpl.module.module_type == ModuleType.SAE + ): + for ue_poids in EvaluationUEPoids.query.join( + EvaluationUEPoids.evaluation + ).filter_by(moduleimpl_id=moduleimpl_id): + try: + evals_poids[ue_poids.ue_id][ue_poids.evaluation_id] = ue_poids.poids + except KeyError as exc: + pass # poids vers des UE qui n'existent plus ou sont dans un autre semestre... # Initialise poids non enregistrés: default_poids = ( From ab212a5b2b5e1d4777dab5fcda33d4aa550483dd Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 18 Feb 2022 19:35:57 +0100 Subject: [PATCH 02/13] Edition programme: avertissements --- app/templates/pn/form_mods.html | 7 +++++++ app/templates/pn/form_ues.html | 3 +++ 2 files changed, 10 insertions(+) diff --git a/app/templates/pn/form_mods.html b/app/templates/pn/form_mods.html index 2be3cfe24..10927bc88 100644 --- a/app/templates/pn/form_mods.html +++ b/app/templates/pn/form_mods.html @@ -65,6 +65,13 @@ {% endfor %} + {% if mod.ue.type != 0 and mod.module_type != 0 %} + + type incompatible avec son UE de rattachement ! + + {% endif %} +
diff --git a/app/templates/pn/form_ues.html b/app/templates/pn/form_ues.html index 350d12d82..0cd3e333b 100644 --- a/app/templates/pn/form_ues.html +++ b/app/templates/pn/form_ues.html @@ -48,6 +48,9 @@ }}">modifier {% endif %} + {% if ue.type == 1 and ue.modules.count() == 0 %} + aucun module rattaché ! + {% endif %} {% endfor %} From 091a49cb0df07eb9949fe3112ecbcf2f466ae109 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 18 Feb 2022 19:36:51 +0100 Subject: [PATCH 03/13] Edition module: simplifie liste rattachement --- app/scodoc/sco_edit_module.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 41a25d090..2bedc0c11 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -500,6 +500,13 @@ def module_edit(module_id=None): matieres = matieres.filter(UniteEns.semestre_idx == a_module.ue.semestre_idx) if is_apc: + # ne conserve que la 1ere matière de chaque UE, + # et celle à laquelle ce module est rattaché + matieres = [ + mat + for mat in matieres + if a_module.matiere.id == mat.id or mat.id == mat.ue.matieres.first().id + ] mat_names = [ "S%s / %s" % (mat.ue.semestre_idx, mat.ue.acronyme) for mat in matieres ] From 7a85ec7466419db036418dc3869870877d6c70cb Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 18 Feb 2022 21:18:08 +0100 Subject: [PATCH 04/13] Ajout bonus Cachan1, et suppression bonus Annecy --- app/comp/bonus_spo.py | 231 ++++++++++++++++++++++++++++-------------- 1 file changed, 154 insertions(+), 77 deletions(-) diff --git a/app/comp/bonus_spo.py b/app/comp/bonus_spo.py index be2a87306..ed4bf5db3 100644 --- a/app/comp/bonus_spo.py +++ b/app/comp/bonus_spo.py @@ -284,6 +284,7 @@ class BonusSportMultiplicatif(BonusSport): 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). """ @@ -294,47 +295,49 @@ class BonusDirect(BonusSportAdditif): proportion_point = 1.0 -class BonusAnnecy(BonusSport): - """Calcul bonus modules optionnels (sport), règle IUT d'Annecy. - Il peut y avoir plusieurs modules de bonus. - Prend pour chaque étudiant la meilleure de ses notes bonus et - ajoute à chaque UE : - 0.05 point si >=10, - 0.1 point si >=12, - 0.15 point si >=14, - 0.2 point si >=16, - 0.25 point si >=18. - """ +# Finalement ils n'en veulent pas. +# class BonusAnnecy(BonusSport): +# """Calcul bonus modules optionnels (sport), règle IUT d'Annecy. - name = "bonus_iut_annecy" - displayed_name = "IUT d'Annecy" +# Il peut y avoir plusieurs modules de bonus. +# Prend pour chaque étudiant la meilleure de ses notes bonus et +# ajoute à chaque UE :
+# 0.05 point si >=10,
+# 0.1 point si >=12,
+# 0.15 point si >=14,
+# 0.2 point si >=16,
+# 0.25 point si >=18. +# """ - def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan): - """calcul du bonus""" - # if math.prod(sem_modimpl_moys_inscrits.shape) == 0: - # return # no etuds or no mod sport - # 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 - bonus = np.zeros(note_bonus_max.shape) - bonus[note_bonus_max >= 18.0] = 0.25 - bonus[note_bonus_max >= 16.0] = 0.20 - bonus[note_bonus_max >= 14.0] = 0.15 - bonus[note_bonus_max >= 12.0] = 0.10 - bonus[note_bonus_max >= 10.0] = 0.05 +# name = "bonus_iut_annecy" +# displayed_name = "IUT d'Annecy" - # Bonus moyenne générale et sur les UE - self.bonus_moy_gen = pd.Series(bonus, index=self.etuds_idx, dtype=float) - ues_idx = [ue.id for ue in self.formsemestre.query_ues(with_sport=False)] - nb_ues_no_bonus = len(ues_idx) - self.bonus_ues = pd.DataFrame( - np.stack([bonus] * nb_ues_no_bonus, axis=1), - columns=ues_idx, - index=self.etuds_idx, - dtype=float, - ) +# def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan): +# """calcul du bonus""" +# # if math.prod(sem_modimpl_moys_inscrits.shape) == 0: +# # return # no etuds or no mod sport +# # 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 +# bonus = np.zeros(note_bonus_max.shape) +# bonus[note_bonus_max >= 10.0] = 0.05 +# bonus[note_bonus_max >= 12.0] = 0.10 +# bonus[note_bonus_max >= 14.0] = 0.15 +# bonus[note_bonus_max >= 16.0] = 0.20 +# bonus[note_bonus_max >= 18.0] = 0.25 + +# # Bonus moyenne générale et sur les UE +# self.bonus_moy_gen = pd.Series(bonus, index=self.etuds_idx, dtype=float) +# ues_idx = [ue.id for ue in self.formsemestre.query_ues(with_sport=False)] +# nb_ues_no_bonus = len(ues_idx) +# self.bonus_ues = pd.DataFrame( +# np.stack([bonus] * nb_ues_no_bonus, axis=1), +# columns=ues_idx, +# index=self.etuds_idx, +# dtype=float, +# ) class BonusBethune(BonusSportMultiplicatif): @@ -375,15 +378,16 @@ class BonusBezier(BonusSportAdditif): class BonusBordeaux1(BonusSportMultiplicatif): """Calcul bonus modules optionnels (sport, culture), règle IUT Bordeaux 1, sur moyenne générale et UE. - +

Les étudiants de l'IUT peuvent suivre des enseignements optionnels de l'Université Bordeaux 1 (sport, théâtre) non rattachés à une unité d'enseignement. - +

Chaque point au-dessus de 10 sur 20 obtenus dans cet enseignement correspond à un % - qui augmente la moyenne de chaque UE et la moyenne générale. - Formule : le % = points>moyenne / 2 + qui augmente la moyenne de chaque UE et la moyenne générale.
+ Formule : pourcentage = (points au dessus de 10) / 2 +

Par exemple : sport 13/20 : chaque UE sera multipliée par 1+0,015, ainsi que la moyenne générale. - +

""" name = "bonus_iutBordeaux1" @@ -393,6 +397,68 @@ class BonusBordeaux1(BonusSportMultiplicatif): amplitude = 0.005 +class BonusCachan1(BonusSportAdditif): + """Calcul bonus optionnels (sport, culture), règle IUT de Cachan 1. + +
    +
  • DUT/LP : la meilleure note d'option, si elle est supérieure à 10, + bonifie les moyennes d'UE (sauf l'UE41 dont le code est UE41_E) à raison + de bonus = (option - 10)/10. +
  • + +
  • BUT : la meilleure note d'option, si elle est supérieure à 10, bonifie + les moyennes d'UE à raison de bonus = (option - 10)*5%.
  • +
+ """ + + name = "bonus_cachan1" + displayed_name = "IUT de Cachan 1" + seuil_moy_gen = 10.0 # tous les points sont comptés + proportion_point = 0.05 + classic_use_bonus_ues = True + + def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan): + """calcul du bonus, avec réglage différent suivant le type de formation""" + # 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, + ) + else: # --- DUT + # pareil mais proportion différente et exclusion d'une UE + proportion_point = 0.1 + bonus_moy_arr = np.where( + note_bonus_max > self.seuil_moy_gen, + (note_bonus_max - self.seuil_moy_gen) * 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, + ) + # Pas de bonus sur la ou les ue de code "UE41_E" + ue_exclues = [ue for ue in ues if ue.ue_code == "UE41_E"] + for ue in ue_exclues: + self.bonus_ues[ue.id] = 0.0 + + class BonusColmar(BonusSportAdditif): """Calcul bonus modules optionnels (sport, culture), règle IUT Colmar. @@ -417,19 +483,21 @@ class BonusColmar(BonusSportAdditif): class BonusGrenobleIUT1(BonusSportMultiplicatif): """Bonus IUT1 de Grenoble +

À compter de sept. 2021: La note de sport est sur 20, et on calcule une bonification (en %) qui va s'appliquer à la moyenne de chaque UE du semestre en appliquant la formule : bonification (en %) = (note-10)*0,5. - - Bonification qui ne s'applique que si la note est >10. - - (Une note de 10 donne donc 0% de bonif ; note de 20 : 5% de bonif) - +

+ La bonification ne s'applique que si la note est supérieure à 10. +

+ (Une note de 10 donne donc 0% de bonif, et une note de 20 : 5% de bonif) +

Avant sept. 2021, la note de sport allait de 0 à 5 points (sur 20). Chaque point correspondait à 0.25% d'augmentation de la moyenne générale. Par exemple : note de sport 2/5 : la moyenne générale était augmentée de 0.5%. +

""" name = "bonus_iut1grenoble_2017" @@ -456,9 +524,11 @@ class BonusGrenobleIUT1(BonusSportMultiplicatif): class BonusLaRochelle(BonusSportAdditif): """Calcul bonus modules optionnels (sport, culture), règle IUT de La Rochelle. - Si la note de sport est comprise entre 0 et 10 : pas d'ajout de point. - 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). +
    +
  • Si la note de sport est comprise entre 0 et 10 : pas d'ajout de point.
  • +
  • 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).
  • +
""" name = "bonus_iutlr" @@ -483,16 +553,17 @@ class BonusLeHavre(BonusSportMultiplicatif): class BonusLeMans(BonusSportAdditif): """Calcul bonus modules optionnels (sport, culture), règle IUT Le Mans. - Les points au-dessus de 10 sur 20 obtenus dans chacune des matières +

Les points au-dessus de 10 sur 20 obtenus dans chacune des matières optionnelles sont cumulés. +

+
    +
  • En BUT: la moyenne de chacune des UE du semestre est augmentée de + 2% du cumul des points de bonus;
  • - - En BUT: la moyenne de chacune des UE du semestre est augmentée de - 2% du cumul des points de bonus, - - En DUT/LP: la moyenne générale est augmentée de 5% du cumul des points bonus. - - Dans tous les cas, le bonus est dans la limite de 0,5 point. +
  • En DUT/LP: la moyenne générale est augmentée de 5% du cumul des points bonus. +
  • +
+

Dans tous les cas, le bonus est dans la limite de 0,5 point.

""" name = "bonus_iutlemans" @@ -516,12 +587,13 @@ class BonusLeMans(BonusSportAdditif): class BonusLille(BonusSportAdditif): """Calcul bonus modules optionnels (sport, culture), règle IUT Villeneuve d'Ascq - Les étudiants de l'IUT peuvent suivre des enseignements optionnels +

Les étudiants de l'IUT peuvent suivre des enseignements optionnels de l'Université Lille (sports, etc) non rattachés à une unité d'enseignement. - +

Les points au-dessus de 10 sur 20 obtenus dans chacune des matières optionnelles sont cumulés et 4% (2% avant août 2010) de ces points cumulés s'ajoutent à la moyenne générale du semestre déjà obtenue par l'étudiant. +

""" name = "bonus_lille" @@ -573,17 +645,19 @@ class BonusMulhouse(BonusSportAdditif): class BonusNantes(BonusSportAdditif): """IUT de Nantes (Septembre 2018) - Nous avons différents types de bonification +

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) +

+ 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" @@ -627,13 +701,14 @@ class BonusStDenis(BonusSportAdditif): class BonusTours(BonusDirect): """Calcul bonus sport & culture IUT Tours. - Les notes des UE bonus (ramenées sur 20) sont sommées +

Les notes des UE bonus (ramenées sur 20) sont sommées et 1/40 (2,5%) est ajouté aux moyennes: soit à la moyenne générale, soit pour le BUT à chaque moyenne d'UE. - - Attention: en GEII, facteur 1/40, ailleurs facteur 1. - +

+ Attention: en GEII, facteur 1/40, ailleurs facteur 1. +

Le bonus total est limité à 1 point. +

""" name = "bonus_tours" @@ -658,11 +733,13 @@ class BonusVilleAvray(BonusSport): Les étudiants de l'IUT peuvent suivre des enseignements optionnels de l'Université Paris 10 (C2I) non rattachés à une unité d'enseignement. - Si la note est >= 10 et < 12, bonus de 0.1 point - Si la note est >= 12 et < 16, bonus de 0.2 point - Si la note est >= 16, bonus de 0.3 point - Ce bonus s'ajoute à la moyenne générale du semestre déjà obtenue par - l'étudiant. +
    +
  • Si la note est >= 10 et < 12, bonus de 0.1 point
  • +
  • Si la note est >= 12 et < 16, bonus de 0.2 point
  • +
  • Si la note est >= 16, bonus de 0.3 point
  • +
+

Ce bonus s'ajoute à la moyenne générale du semestre déjà obtenue par + l'étudiant.

""" name = "bonus_iutva" From fae11d82ced18f65e91c82a2716b5e014a15498e Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 18 Feb 2022 21:54:30 +0100 Subject: [PATCH 05/13] =?UTF-8?q?Fix:=20prise=20en=20compte=20=C3=A9val=20?= =?UTF-8?q?de=20rattrapage=20dans=20les=20modules=20BUT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/comp/moy_mod.py | 12 +++++++----- sco_version.py | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/comp/moy_mod.py b/app/comp/moy_mod.py index 2ec6c35f2..eea357e8f 100644 --- a/app/comp/moy_mod.py +++ b/app/comp/moy_mod.py @@ -335,15 +335,17 @@ class ModuleImplResultsAPC(ModuleImplResults): notes_rat / (eval_rat.note_max / 20.0), np.nan, ) + # "Étend" le rattrapage sur les UE: la note de rattrapage est la même + # pour toutes les UE mais ne remplace que là où elle est supérieure + notes_rat_ues = np.stack([notes_rat] * nb_ues, axis=1) # prend le max - etuds_use_rattrapage = notes_rat > etuds_moy_module + etuds_use_rattrapage = notes_rat_ues > etuds_moy_module etuds_moy_module = np.where( - etuds_use_rattrapage[:, np.newaxis], - np.tile(notes_rat[:, np.newaxis], nb_ues), - etuds_moy_module, + etuds_use_rattrapage, notes_rat_ues, etuds_moy_module ) + # Serie indiquant que l'étudiant utilise une note de rattarage sur l'une des UE: self.etuds_use_rattrapage = pd.Series( - etuds_use_rattrapage, index=self.evals_notes.index + etuds_use_rattrapage.any(axis=1), index=self.evals_notes.index ) self.etuds_moy_module = pd.DataFrame( etuds_moy_module, diff --git a/sco_version.py b/sco_version.py index e7456ecab..04070622d 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.1.58" +SCOVERSION = "9.1.59" SCONAME = "ScoDoc" From 58a8bcb83da72c122825fbaec2f5fd586da8ca7e Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 18 Feb 2022 22:14:12 +0100 Subject: [PATCH 06/13] =?UTF-8?q?Ajoute=20lien=20'inscrire=20des=20=C3=A9t?= =?UTF-8?q?udiants'.=20Closes=20#319.=20Auteur:=20PB.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_moduleimpl_inscriptions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index e3c232305..453f63f1f 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -305,7 +305,10 @@ def moduleimpl_inscriptions_stats(formsemestre_id): if can_change: c_link = ( '%s' - % (mod["moduleimpl_id"], mod["descri"]) + % ( + mod["moduleimpl_id"], + mod["descri"] or "(inscrire des étudiants)", + ) ) else: c_link = mod["descri"] From d8776485465574009876ea5022f09c75324eceb2 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 18 Feb 2022 22:19:23 +0100 Subject: [PATCH 07/13] =?UTF-8?q?empeche=20cr=C3=A9ation=20de=20d=C3=A9par?= =?UTF-8?q?tements=20avec=20m=C3=AAme=20acronyme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/departements.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/departements.py b/app/models/departements.py index ebe5cc145..44a963ca0 100644 --- a/app/models/departements.py +++ b/app/models/departements.py @@ -55,6 +55,9 @@ def create_dept(acronym: str, visible=True) -> Departement: "Create new departement" from app.models import ScoPreference + existing = Departement.query.filter_by(acronym=acronym).count() + if existing: + raise ValueError(f"acronyme {acronym} déjà existant") departement = Departement(acronym=acronym, visible=visible) p1 = ScoPreference(name="DeptName", value=acronym, departement=departement) db.session.add(p1) From c81c4efb406f2f0059daaab7aa03183769f28c59 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 18 Feb 2022 22:59:05 +0100 Subject: [PATCH 08/13] Fix: formsemestre_evaluations_cal --- app/scodoc/sco_etape_apogee_view.py | 4 ++-- app/scodoc/sco_evaluations.py | 27 +++++++++++---------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/app/scodoc/sco_etape_apogee_view.py b/app/scodoc/sco_etape_apogee_view.py index b784e5f0f..21fc2410f 100644 --- a/app/scodoc/sco_etape_apogee_view.py +++ b/app/scodoc/sco_etape_apogee_view.py @@ -53,7 +53,7 @@ from app.scodoc.sco_exceptions import ScoValueError def apo_semset_maq_status( - semset_id="", + semset_id: int, allow_missing_apo=False, allow_missing_decisions=False, allow_missing_csv=False, @@ -65,7 +65,7 @@ def apo_semset_maq_status( ): """Page statut / tableau de bord""" if not semset_id: - raise ValueError("invalid null semset_id") + raise ScoValueError("invalid null semset_id") semset = sco_semset.SemSet(semset_id=semset_id) semset.fill_formsemestres() # autorise export meme si etudiants Apo manquants: diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py index 09f2179b7..d895a8a37 100644 --- a/app/scodoc/sco_evaluations.py +++ b/app/scodoc/sco_evaluations.py @@ -405,7 +405,6 @@ def formsemestre_evaluations_cal(formsemestre_id): """Page avec calendrier de toutes les evaluations de ce semestre""" formsemestre = FormSemestre.query.get_or_404(formsemestre_id) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - sem = formsemestre.to_dict() evals = nt.get_evaluations_etats() nb_evals = len(evals) @@ -416,8 +415,8 @@ def formsemestre_evaluations_cal(formsemestre_id): today = time.strftime("%Y-%m-%d") - year = int(sem["annee_debut"]) - if sem["mois_debut_ord"] < 8: + year = formsemestre.date_debut.year + if formsemestre.date_debut.month < 8: year -= 1 # calendrier septembre a septembre events = {} # (day, halfday) : event for e in evals: @@ -537,11 +536,10 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"): """Experimental: un tableau indiquant pour chaque évaluation le nombre de jours avant la publication des notes. - N'indique pas les évaluations de ratrapage ni celles des modules de bonus/malus. + N'indique pas les évaluations de rattrapage ni celles des modules de bonus/malus. """ formsemestre = FormSemestre.query.get_or_404(formsemestre_id) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - sem = formsemestre.to_dict() evals = nt.get_evaluations_etats() T = [] @@ -607,7 +605,7 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"): origin="Généré par %s le " % sco_version.SCONAME + scu.timedate_human_repr() + "", - filename=scu.make_filename("evaluations_delais_" + sem["titreannee"]), + filename=scu.make_filename("evaluations_delais_" + formsemestre.titre_annee()), ) return tab.make_page(format=format) @@ -635,16 +633,13 @@ def evaluation_describe(evaluation_id="", edit_in_place=True): 'voir toutes les notes du module' % moduleimpl_id ) - mod_descr = ( - '%s %s (resp. %s) %s' - % ( - moduleimpl_id, - Mod["code"] or "", - Mod["titre"] or "?", - nomcomplet, - resp, - link, - ) + mod_descr = '%s %s (resp. %s) %s' % ( + moduleimpl_id, + Mod["code"] or "", + Mod["titre"] or "?", + nomcomplet, + resp, + link, ) etit = E["description"] or "" From 202ce4e73e8a2ae05df7bb5f02065135110a4926 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 18 Feb 2022 23:30:45 +0100 Subject: [PATCH 09/13] nginx frontal: augmentation du timeout proxy --- tools/build_release.sh | 2 ++ tools/etc/scodoc9-nginx-timeout.conf | 5 +++++ 2 files changed, 7 insertions(+) create mode 100644 tools/etc/scodoc9-nginx-timeout.conf diff --git a/tools/build_release.sh b/tools/build_release.sh index 69a333bbd..5afefaca6 100644 --- a/tools/build_release.sh +++ b/tools/build_release.sh @@ -93,6 +93,8 @@ fi # nginx: mkdir -p "$slash"/etc/nginx/sites-available || die "can't mkdir nginx config" cp -p "$SCODOC_DIR"/tools/etc/scodoc9.nginx "$slash"/etc/nginx/sites-available/scodoc9.nginx.distrib || die "can't copy nginx config" +mkdir -p "$slash"/etc/nginx/conf.d || die "can't mkdir nginx conf.d" +cp -p "$SCODOC_DIR"/tools/etc/scodoc9-nginx-timeout.conf "$slash"/etc/nginx/conf.d/ || die "can't copy nginx timeout config" # systemd mkdir -p "$slash"/etc/systemd/system/ || die "can't mkdir systemd config" diff --git a/tools/etc/scodoc9-nginx-timeout.conf b/tools/etc/scodoc9-nginx-timeout.conf new file mode 100644 index 000000000..98f367161 --- /dev/null +++ b/tools/etc/scodoc9-nginx-timeout.conf @@ -0,0 +1,5 @@ +# Reglage des timeout du frontal nginx pour ScoDoc 9 (>= 9.1.59) + +proxy_read_timeout 400; +proxy_connect_timeout 400; +proxy_send_timeout 400; From 5c951d58e790f37fed0e9993b57af460accc1480 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 18 Feb 2022 23:43:13 +0100 Subject: [PATCH 10/13] =?UTF-8?q?Fix=20lien=20chargement=20ref.=20comp?= =?UTF-8?q?=C3=A9tences?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/templates/but/refcomp_load.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/templates/but/refcomp_load.html b/app/templates/but/refcomp_load.html index 290de937b..50d73d80c 100644 --- a/app/templates/but/refcomp_load.html +++ b/app/templates/but/refcomp_load.html @@ -18,10 +18,12 @@ Liste des référentiels de compétences chargés + {% if formation is not none %}
  • Association à la formation {{ formation.acronyme }}
  • + {% endif %} From cca72dfed27089f9308cf3d6ce13df994110a4cc Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 19 Feb 2022 00:28:24 +0100 Subject: [PATCH 11/13] Bonus IUT Amiens --- app/comp/bonus_spo.py | 15 +++++++++++++++ sco_version.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/comp/bonus_spo.py b/app/comp/bonus_spo.py index ed4bf5db3..65f6c03b9 100644 --- a/app/comp/bonus_spo.py +++ b/app/comp/bonus_spo.py @@ -295,6 +295,21 @@ class BonusDirect(BonusSportAdditif): proportion_point = 1.0 +class BonusAmiens(BonusSportAdditif): + """Bonus IUT Amiens pour les modules optionnels (sport, culture, ...). + + Toute note non nulle, peu importe sa valeur, entraine un bonus de 0,1 point + sur toutes les moyennes d'UE. + """ + + name = "bonus_amiens" + displayed_name = "IUT d'Amiens" + seuil_moy_gen = 0.0 # tous les points sont comptés + proportion_point = 1e10 + bonus_max = 0.1 + classic_use_bonus_ues = True # s'applique aux UEs en DUT et LP + + # Finalement ils n'en veulent pas. # class BonusAnnecy(BonusSport): # """Calcul bonus modules optionnels (sport), règle IUT d'Annecy. diff --git a/sco_version.py b/sco_version.py index 04070622d..96ef7d1da 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.1.59" +SCOVERSION = "9.1.60" SCONAME = "ScoDoc" From 63784e341ad1d33f1bdb7060b88e25e0b3ad4d9e Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 19 Feb 2022 01:00:40 +0100 Subject: [PATCH 12/13] Correction pour Amiens, Roanne --- app/comp/bonus_spo.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app/comp/bonus_spo.py b/app/comp/bonus_spo.py index 65f6c03b9..0a95621e8 100644 --- a/app/comp/bonus_spo.py +++ b/app/comp/bonus_spo.py @@ -205,7 +205,8 @@ class BonusSportAdditif(BonusSport): """calcul du bonus 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: + En classic: ndarray (nb_etuds, nb_mod_sport) + modimpl_coefs_etuds_no_nan: même shape, les coefs. """ if 0 in sem_modimpl_moys_inscrits.shape: # pas d'étudiants ou pas d'UE ou pas de module... @@ -228,12 +229,22 @@ class BonusSportAdditif(BonusSport): bonus_moy_arr = np.clip(bonus_moy_arr, 0.0, 20.0, out=bonus_moy_arr) # en APC, bonus_moy_arr est (nb_etuds, nb_ues_non_bonus) - if self.formsemestre.formation.is_apc() or self.classic_use_bonus_ues: + if self.formsemestre.formation.is_apc(): # 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 ) + elif self.classic_use_bonus_ues: + # Formations classiques apppliquant le bonus sur les UEs + # ici bonus_moy_arr = ndarray 1d nb_etuds + ues_idx = [ue.id for ue in self.formsemestre.query_ues(with_sport=False)] + self.bonus_ues = pd.DataFrame( + np.stack([bonus_moy_arr] * len(ues_idx)).T, + index=self.etuds_idx, + columns=ues_idx, + dtype=float, + ) else: # Bonus sur la moyenne générale seulement self.bonus_moy_gen = pd.Series( @@ -693,7 +704,7 @@ class BonusRoanne(BonusSportAdditif): displayed_name = "IUT de Roanne" seuil_moy_gen = 0.0 bonus_max = 0.6 # plafonnement à 0.6 points - apply_bonus_mg_to_ues = True # sur les UE, même en DUT et LP + classic_use_bonus_ues = True # sur les UE, même en DUT et LP class BonusStDenis(BonusSportAdditif): @@ -792,7 +803,7 @@ class BonusIUTV(BonusSportAdditif): name = "bonus_iutv" displayed_name = "IUT de Villetaneuse" - pass # oui, c'ets le bonus par défaut + pass # oui, c'est le bonus par défaut def get_bonus_class_dict(start=BonusSport, d=None): From 44123c022eac799449173f8d92734bcebb75f8c1 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 19 Feb 2022 16:16:52 +0100 Subject: [PATCH 13/13] =?UTF-8?q?Am=C3=A9liore=20=C3=A9dition=20programmes?= =?UTF-8?q?=20classiques?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_edit_ue.py | 34 +++++++++++++++++------ app/scodoc/sco_formsemestre_validation.py | 16 +++++++---- app/static/css/scodoc.css | 4 +++ sco_version.py | 2 +- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 17cdc8c07..061bf43c6 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -601,7 +601,12 @@ def ue_table(formation_id=None, semestre_idx=1, msg=""): # was ue_list _add_ue_semestre_id(ues_externes, is_apc) ues.sort(key=lambda u: (u["semestre_id"], u["numero"])) ues_externes.sort(key=lambda u: (u["semestre_id"], u["numero"])) - has_duplicate_ue_codes = len(set([ue["ue_code"] for ue in ues])) != len(ues) + # Codes dupliqués (pour aider l'utilisateur) + seen = set() + duplicated_codes = { + ue["ue_code"] for ue in ues if ue["ue_code"] in seen or seen.add(ue["ue_code"]) + } + ues_with_duplicated_code = [ue for ue in ues if ue["ue_code"] in duplicated_codes] has_perm_change = current_user.has_permission(Permission.ScoChangeFormation) # editable = (not locked) and has_perm_change @@ -664,11 +669,17 @@ du programme" (menu "Semestre") si vous avez un semestre en cours); if msg: H.append('

    ' + msg + "

    ") - if has_duplicate_ue_codes: + if ues_with_duplicated_code: H.append( - """
    Attention: plusieurs UE de cette - formation ont le même code. Il faut corriger cela ci-dessous, - sinon les calculs d'ECTS seront erronés !
    """ + f"""
    Attention: plusieurs UE de cette + formation ont le même code : { + ', '.join([ + '' + ue["acronyme"] + " (code " + ue["ue_code"] + ")" + for ue in ues_with_duplicated_code ]) + }. + Il faut corriger cela, sinon les capitalisations et ECTS seront + erronés !
    """ ) # Description de la formation @@ -930,8 +941,8 @@ def _ue_table_ues( if cur_ue_semestre_id != ue["semestre_id"]: cur_ue_semestre_id = ue["semestre_id"] - if iue > 0: - H.append("") + # if iue > 0: + # H.append("") if ue["semestre_id"] == sco_codes_parcours.UE_SEM_DEFAULT: lab = "Pas d'indication de semestre:" else: @@ -953,7 +964,6 @@ def _ue_table_ues( ) else: H.append(arrow_none) - iue += 1 ue["acro_titre"] = str(ue["acronyme"]) if ue["titre"] != ue["acronyme"]: ue["acro_titre"] += " " + str(ue["titre"]) @@ -1001,6 +1011,14 @@ def _ue_table_ues( delete_disabled_icon, ) ) + if (iue >= len(ues) - 1) or ue["semestre_id"] != ues[iue + 1]["semestre_id"]: + H.append( + f"""""" + ) + iue += 1 + return "\n".join(H) diff --git a/app/scodoc/sco_formsemestre_validation.py b/app/scodoc/sco_formsemestre_validation.py index f755bb118..987dc962f 100644 --- a/app/scodoc/sco_formsemestre_validation.py +++ b/app/scodoc/sco_formsemestre_validation.py @@ -1250,7 +1250,7 @@ def check_formation_ues(formation_id): for ue in ues: # formsemestres utilisant cette ue ? sems = ndb.SimpleDictFetch( - """SELECT DISTINCT sem.id AS formsemestre_id, sem.* + """SELECT DISTINCT sem.id AS formsemestre_id, sem.* FROM notes_formsemestre sem, notes_modules mod, notes_moduleimpl mi WHERE sem.formation_id = %(formation_id)s AND mod.id = mi.module_id @@ -1269,11 +1269,11 @@ def check_formation_ues(formation_id): return "", {} # Genere message HTML: H = [ - """
    Attention: les UE suivantes de cette formation + """
    Attention: les UE suivantes de cette formation sont utilisées dans des - semestres de rangs différents (eg S1 et S3).
    Cela peut engendrer des problèmes pour - la capitalisation des UE. Il serait préférable d'essayer de rectifier cette situation: - soit modifier le programme de la formation (définir des UE dans chaque semestre), + semestres de rangs différents (eg S1 et S3).
    Cela peut engendrer des problèmes pour + la capitalisation des UE. Il serait préférable d'essayer de rectifier cette situation: + soit modifier le programme de la formation (définir des UE dans chaque semestre), soit veiller à saisir le bon indice de semestre dans le menu lors de la validation d'une UE extérieure.
      @@ -1286,7 +1286,11 @@ def check_formation_ues(formation_id): for x in ue_multiples[ue["ue_id"]] ] slist = ", ".join( - ["%(titreannee)s (semestre %(semestre_id)s)" % s for s in sems] + [ + """%(titreannee)s (semestre %(semestre_id)s)""" + % s + for s in sems + ] ) H.append("
    • %s : %s
    • " % (ue["acronyme"], slist)) H.append("
    ") diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index e88930857..ecd8b0ab0 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -1707,6 +1707,9 @@ ul.notes_ue_list { li.notes_ue_list { margin-top: 9px; list-style-type: none; + border: 1px solid maroon; + border-radius: 10px; + padding-bottom: 5px; } span.ue_type_1 { color: green; @@ -1749,6 +1752,7 @@ ul.notes_matiere_list { background-color: rgb(220,220,220); font-weight: normal; font-style: italic; + border-top: 1px solid maroon; } ul.notes_module_list { diff --git a/sco_version.py b/sco_version.py index 96ef7d1da..9e02cffd1 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.1.60" +SCOVERSION = "9.1.61" SCONAME = "ScoDoc"