Merge branch 'refactor_nt' of https://scodoc.org/git/ScoDoc/ScoDoc into entreprises

This commit is contained in:
Arthur ZHU 2022-02-01 14:14:39 +01:00
commit e85149f4a6
19 changed files with 213 additions and 49 deletions

View File

@ -71,7 +71,7 @@ class BulletinBUT(ResultatsSemestreBUT):
"bonus": fmt_note(self.bonus_ues[ue.id][etud.id]) "bonus": fmt_note(self.bonus_ues[ue.id][etud.id])
if self.bonus_ues is not None and ue.id in self.bonus_ues if self.bonus_ues is not None and ue.id in self.bonus_ues
else fmt_note(0.0), 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 "capitalise": None, # "AAAA-MM-JJ" TODO
"ressources": self.etud_ue_mod_results(etud, ue, self.ressources), "ressources": self.etud_ue_mod_results(etud, ue, self.ressources),
"saes": self.etud_ue_mod_results(etud, ue, self.saes), "saes": self.etud_ue_mod_results(etud, ue, self.saes),

View File

@ -104,7 +104,6 @@ class BonusSport:
# 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_non_bonus) # 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)
# Enlève les NaN du numérateur: # Enlève les NaN du numérateur:
sem_modimpl_moys_no_nan = np.nan_to_num(sem_modimpl_moys_spo, nan=0.0) sem_modimpl_moys_no_nan = np.nan_to_num(sem_modimpl_moys_spo, nan=0.0)
@ -124,7 +123,7 @@ class BonusSport:
# Annule les coefs des modules où l'étudiant n'est pas inscrit: # Annule les coefs des modules où l'étudiant n'est pas inscrit:
modimpl_coefs_etuds = np.where( modimpl_coefs_etuds = np.where(
modimpl_inscr_spo_stacked, modimpl_inscr_spo_stacked,
np.stack([modimpl_coefs_spo.T] * nb_etuds), np.stack([modimpl_coefs_spo] * nb_etuds),
0.0, 0.0,
) )
else: else:
@ -162,7 +161,7 @@ class BonusSport:
""" """
raise NotImplementedError("méthode virtuelle") raise NotImplementedError("méthode virtuelle")
def get_bonus_ues(self) -> pd.Series: def get_bonus_ues(self) -> pd.DataFrame:
"""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
""" """

View File

@ -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_codes_parcours import UE_SPORT
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_utils import ModuleType
@dataclass @dataclass
class EvaluationEtat: class EvaluationEtat:
@ -233,6 +235,8 @@ class ModuleImplResultsAPC(ModuleImplResults):
assert evals_poids_df.shape[0] == nb_evals # compat notes/poids assert evals_poids_df.shape[0] == nb_evals # compat notes/poids
if nb_etuds == 0: if nb_etuds == 0:
return pd.DataFrame(index=[], columns=evals_poids_df.columns) return pd.DataFrame(index=[], columns=evals_poids_df.columns)
if nb_ues == 0:
return pd.DataFrame(index=self.evals_notes.index, columns=[])
evals_coefs = self.get_evaluations_coefs(moduleimpl) evals_coefs = self.get_evaluations_coefs(moduleimpl)
evals_poids = evals_poids_df.values * evals_coefs evals_poids = evals_poids_df.values * evals_coefs
# -> evals_poids shape : (nb_evals, nb_ues) # -> evals_poids shape : (nb_evals, nb_ues)
@ -289,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... 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 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(): if np.isnan(evals_poids.values.flat).any():
ue_coefs = modimpl.module.get_ue_coef_dict() ue_coefs = modimpl.module.get_ue_coef_dict()

View File

@ -27,6 +27,7 @@
"""Fonctions de calcul des moyennes d'UE (classiques ou BUT) """Fonctions de calcul des moyennes d'UE (classiques ou BUT)
""" """
from re import X
import numpy as np import numpy as np
import pandas as pd import pandas as pd
@ -263,9 +264,10 @@ def compute_ue_moys_apc(
# #
# Version vectorisée # Version vectorisée
# #
etud_moy_ue = np.sum( with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
modimpl_coefs_etuds_no_nan * sem_cube_inscrits, axis=1 etud_moy_ue = np.sum(
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1) modimpl_coefs_etuds_no_nan * sem_cube_inscrits, axis=1
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
return pd.DataFrame( return pd.DataFrame(
etud_moy_ue, etud_moy_ue,
index=modimpl_inscr_df.index, # les etudids index=modimpl_inscr_df.index, # les etudids
@ -379,3 +381,42 @@ def compute_ue_moys_classic(
etud_moy_gen_s = pd.Series(etud_moy_gen, index=modimpl_inscr_df.index) 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 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

View File

@ -68,6 +68,12 @@ class ResultatsSemestreBUT(NotesTableCompat):
1.0, index=self.etud_moy_ue.index, columns=self.etud_moy_ue.columns 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 # --- Bonus Sport & Culture
if len(modimpls_sport) > 0: if len(modimpls_sport) > 0:
bonus_class = ScoDocSiteConfig.get_bonus_sport_class() bonus_class = ScoDocSiteConfig.get_bonus_sport_class()

View File

@ -71,6 +71,16 @@ class ResultatsSemestreClassic(NotesTableCompat):
self.modimpl_coefs, self.modimpl_coefs,
modimpl_standards_mask, 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 Sport & Culture
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:

View File

@ -70,7 +70,7 @@ class CodesDecisionsForm(FlaskForm):
ATT = _build_code_field("ATT") ATT = _build_code_field("ATT")
CMP = _build_code_field("CMP") CMP = _build_code_field("CMP")
DEF = _build_code_field("DEF") DEF = _build_code_field("DEF")
DEM = _build_code_field("DEF") DEM = _build_code_field("DEM")
NAR = _build_code_field("NAR") NAR = _build_code_field("NAR")
RAT = _build_code_field("RAT") RAT = _build_code_field("RAT")
submit = SubmitField("Valider") submit = SubmitField("Valider")

View File

@ -178,7 +178,7 @@ class ScoDocSiteConfig(db.Model):
return getattr(bonus_sport, func_name) return getattr(bonus_sport, func_name)
except AttributeError: except AttributeError:
raise ScoValueError( raise ScoValueError(
f"""Fonction de calcul maison inexistante: {func_name}. f"""Fonction de calcul de l'UE bonus inexistante: "{func_name}".
(contacter votre administrateur local).""" (contacter votre administrateur local)."""
) )

View File

@ -276,14 +276,24 @@ class TF(object):
) )
ok = 0 ok = 0
if typ[:3] == "int" or typ == "float" or typ == "real": if typ[:3] == "int" or typ == "float" or typ == "real":
if "min_value" in descr and val < descr["min_value"]: if (
val != ""
and val != None
and "min_value" in descr
and val < descr["min_value"]
):
msg.append( msg.append(
"La valeur (%d) du champ '%s' est trop petite (min=%s)" "La valeur (%d) du champ '%s' est trop petite (min=%s)"
% (val, field, descr["min_value"]) % (val, field, descr["min_value"])
) )
ok = 0 ok = 0
if "max_value" in descr and val > descr["max_value"]: if (
val != ""
and val != None
and "max_value" in descr
and val > descr["max_value"]
):
msg.append( msg.append(
"La valeur (%s) du champ '%s' est trop grande (max=%s)" "La valeur (%s) du champ '%s' est trop grande (max=%s)"
% (val, field, descr["max_value"]) % (val, field, descr["max_value"])

View File

@ -456,6 +456,11 @@ def bonus_iutbeziers(notes_sport, coefs, infos=None):
return bonus return bonus
def bonus_iutlemans(notes_sport, coefs, infos=None):
"fake: formule inutilisée en ScoDoc 9.2 mais doiut être présente"
return 0.0
def bonus_iutlr(notes_sport, coefs, infos=None): def bonus_iutlr(notes_sport, coefs, infos=None):
"""Calcul bonus modules optionels (sport, culture), règle IUT La Rochelle """Calcul bonus modules optionels (sport, culture), règle IUT 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 0 et 10 : pas d'ajout de point

View File

@ -125,8 +125,8 @@ APO_NEWLINE = "\r\n"
def _apo_fmt_note(note): def _apo_fmt_note(note):
"Formatte une note pour Apogée (séparateur décimal: ',')" "Formatte une note pour Apogée (séparateur décimal: ',')"
if not note and isinstance(note, float): # if not note and isinstance(note, float): changé le 31/1/2022, étrange ?
return "" # return ""
try: try:
val = float(note) val = float(note)
except ValueError: except ValueError:

View File

@ -141,7 +141,7 @@ BUG = "BUG"
ALL = "ALL" ALL = "ALL"
# Explication des codes (de demestre ou d'UE) # Explication des codes (de semestre ou d'UE)
CODES_EXPL = { CODES_EXPL = {
ADC: "Validé par compensation", ADC: "Validé par compensation",
ADJ: "Validé par le Jury", ADJ: "Validé par le Jury",
@ -154,6 +154,7 @@ CODES_EXPL = {
DEF: "Défaillant", DEF: "Défaillant",
NAR: "Échec, non autorisé à redoubler", NAR: "Échec, non autorisé à redoubler",
RAT: "En attente d'un rattrapage", RAT: "En attente d'un rattrapage",
DEM: "Démission",
} }
# Nota: ces explications sont personnalisables via le fichier # Nota: ces explications sont personnalisables via le fichier
# de config locale /opt/scodoc/var/scodoc/config/scodoc_local.py # de config locale /opt/scodoc/var/scodoc/config/scodoc_local.py

View File

@ -115,22 +115,30 @@ def do_module_create(args) -> int:
return r return r
def module_create(matiere_id=None, module_type=None, semestre_id=None): def module_create(
"""Création d'un module""" 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_formations
from app.scodoc import sco_edit_ue from app.scodoc import sco_edit_ue
matiere = Matiere.query.get_or_404(matiere_id) if matiere_id:
if matiere is None: matiere = Matiere.query.get_or_404(matiere_id)
raise ScoValueError("invalid matiere !") ue = matiere.ue
ue = matiere.ue formation = ue.formation
parcours = ue.formation.get_parcours() else:
formation = Formation.query.get_or_404(formation_id)
parcours = formation.get_parcours()
is_apc = parcours.APC_SAE is_apc = parcours.APC_SAE
ues = ue.formation.ues.order_by( ues = formation.ues.order_by(
UniteEns.semestre_idx, UniteEns.numero, UniteEns.acronyme UniteEns.semestre_idx, UniteEns.numero, UniteEns.acronyme
).all() ).all()
# cherche le numero adéquat (pour placer le module en fin de liste) # 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: if modules:
default_num = max([m.numero or 0 for m in modules]) + 10 default_num = max([m.numero or 0 for m in modules]) + 10
else: else:
@ -143,9 +151,11 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
H = [ H = [
html_sco_header.sco_header(page_title=f"Création {object_name}"), html_sco_header.sco_header(page_title=f"Création {object_name}"),
] ]
if is_apc: if not matiere_id:
H += [ 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: else:
H += [ H += [
@ -158,7 +168,6 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
render_template( render_template(
"scodoc/help/modules.html", "scodoc/help/modules.html",
is_apc=is_apc, is_apc=is_apc,
ue=ue,
semestre_id=semestre_id, semestre_id=semestre_id,
) )
] ]
@ -170,7 +179,7 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
"size": 10, "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.", "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, "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 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)) 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 += [ descr += [
( (
"module_type", "module_type",
@ -199,8 +217,8 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
"input_type": "menu", "input_type": "menu",
"title": "Type", "title": "Type",
"explanation": "", "explanation": "",
"labels": [x.name.capitalize() for x in scu.ModuleType], "labels": [x.name.capitalize() for x in module_types],
"allowed_values": [str(int(x)) for x in scu.ModuleType], "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 += [ descr += [
# ('ects', { 'size' : 4, 'type' : 'float', 'title' : 'ECTS', 'explanation' : 'nombre de crédits ECTS (inutilisés: les crédits sont associés aux UE)' }), # ('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"}), ("formation_id", {"default": formation.id, "input_type": "hidden"}),
("ue_id", {"default": ue.id, "input_type": "hidden"}),
("matiere_id", {"default": matiere.id, "input_type": "hidden"}),
( (
"code_apogee", "code_apogee",
{ {
@ -290,6 +327,20 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
if tf[0] == 0: if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer() return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
else: 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 tf[2]["semestre_id"] = ue.semestre_idx
_ = do_module_create(tf[2]) _ = do_module_create(tf[2])
@ -298,7 +349,7 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
url_for( url_for(
"notes.ue_table", "notes.ue_table",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
formation_id=ue.formation_id, formation_id=formation.id,
semestre_idx=tf[2]["semestre_id"], semestre_idx=tf[2]["semestre_id"],
) )
) )
@ -493,6 +544,13 @@ def module_edit(module_id=None):
soyez prudents ! soyez prudents !
</span></div>""" </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 = [ descr = [
( (
@ -514,8 +572,8 @@ def module_edit(module_id=None):
"input_type": "menu", "input_type": "menu",
"title": "Type", "title": "Type",
"explanation": "", "explanation": "",
"labels": [x.name.capitalize() for x in scu.ModuleType], "labels": [x.name.capitalize() for x in module_types],
"allowed_values": [str(int(x)) for x in scu.ModuleType], "allowed_values": [str(int(x)) for x in module_types],
"enabled": unlocked, "enabled": unlocked,
}, },
), ),

View File

@ -998,6 +998,7 @@ def _ue_table_matieres(
H.append( H.append(
_ue_table_modules( _ue_table_modules(
parcours, parcours,
ue,
mat, mat,
modules, modules,
editable, editable,
@ -1031,6 +1032,7 @@ def _ue_table_matieres(
def _ue_table_modules( def _ue_table_modules(
parcours, parcours,
ue,
mat, mat,
modules, modules,
editable, editable,
@ -1121,8 +1123,12 @@ def _ue_table_modules(
tag_cls, tag_cls,
",".join(sco_tag_module.module_tag_list(mod["module_id"])), ",".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( H.append(
" %s %s" % (parcours.SESSION_NAME, mod["semestre_id"]) " %s %s%s" % (parcours.SESSION_NAME, mod["semestre_id"], warning_semestre)
+ " (%s)" % heurescoef + " (%s)" % heurescoef
+ tag_edit + tag_edit
) )

View File

@ -546,7 +546,7 @@ def do_formsemestre_createwithmodules(edit=False):
for mod in mods: for mod in mods:
if mod["semestre_id"] == semestre_id and ( if mod["semestre_id"] == semestre_id and (
(not edit) # creation => tous modules (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 (semestre_id == formsemestre.semestre_id) # module du semestre
or (mod["module_id"] in module_ids_set) # module déjà présent or (mod["module_id"] in module_ids_set) # module déjà présent
): ):

View File

@ -219,7 +219,9 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
page_title=f"{mod_type_name} {Mod['code']} {Mod['titre']}" page_title=f"{mod_type_name} {Mod['code']} {Mod['titre']}"
), ),
f"""<h2 class="formsemestre">{mod_type_name} 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_{ <div class="moduleimpl_tableaubord moduleimpl_type_{
scu.ModuleType(Mod['module_type']).name.lower()}"> scu.ModuleType(Mod['module_type']).name.lower()}">
<table> <table>

View File

@ -71,14 +71,22 @@
</li> </li>
{% endfor %} {% endfor %}
{% if editable and matiere_parent %} {% if editable %}
<li><a class="stdlink" href="{{ <li><a class="stdlink" href=
{% if matiere_parent %}"{{
url_for("notes.module_create", url_for("notes.module_create",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
module_type=module_type|int, module_type=module_type|int,
matiere_id=matiere_parent.id matiere_id=matiere_parent.id
)}}" )}}"
>{{create_element_msg}}</a> {% 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> </li>
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@ -191,7 +191,7 @@ def get_etud_dept():
# le choix a peu d'importance... # le choix a peu d'importance...
last_etud = etuds[-1] last_etud = etuds[-1]
return Departement.query.get(last_etud.dept_id).acronym return Departement.query.get_or_404(last_etud.dept_id).acronym
# Bricolage pour le portail IUTV avec ScoDoc 7: (DEPRECATED: NE PAS UTILISER !) # Bricolage pour le portail IUTV avec ScoDoc 7: (DEPRECATED: NE PAS UTILISER !)

View File

@ -149,7 +149,7 @@ def user_info(user_name, format="json"):
@scodoc @scodoc
@permission_required(Permission.ScoUsersAdmin) @permission_required(Permission.ScoUsersAdmin)
@scodoc7func @scodoc7func
def create_user_form(user_name=None, edit=0, all_roles=1): def create_user_form(user_name=None, edit=0, all_roles=False):
"form. création ou édition utilisateur" "form. création ou édition utilisateur"
if user_name is not None: # scodoc7func converti en int ! if user_name is not None: # scodoc7func converti en int !
user_name = str(user_name) user_name = str(user_name)
@ -218,9 +218,11 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
} }
if current_user.is_administrator(): if current_user.is_administrator():
editable_roles_set |= { editable_roles_set |= {
(Role.get_named_role(r), "") (Role.get_named_role(r), None)
for r in sco_roles_default.ROLES_ATTRIBUABLES_SCODOC for r in sco_roles_default.ROLES_ATTRIBUABLES_SCODOC
} }
# Un super-admin peut nommer d'autres super-admin:
editable_roles_set |= {(Role.get_named_role("SuperAdmin"), None)}
# #
if not edit: if not edit:
submitlabel = "Créer utilisateur" submitlabel = "Créer utilisateur"
@ -251,16 +253,23 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
orig_roles_strings = {r.name + "_" + (dept or "") for (r, dept) in orig_roles} orig_roles_strings = {r.name + "_" + (dept or "") for (r, dept) in orig_roles}
# add existing user roles # add existing user roles
displayed_roles = list(editable_roles_set.union(orig_roles)) displayed_roles = list(editable_roles_set.union(orig_roles))
displayed_roles.sort(key=lambda x: (x[1] or "", x[0].name or "")) displayed_roles.sort(
key=lambda x: (
x[1] or "",
(x[0].name or "") if x[0].name != "SuperAdmin" else "A",
)
)
displayed_roles_strings = [ displayed_roles_strings = [
r.name + "_" + (dept or "") for (r, dept) in displayed_roles r.name + "_" + (dept or "") for (r, dept) in displayed_roles
] ]
displayed_roles_labels = [f"{dept}: {r.name}" for (r, dept) in displayed_roles] displayed_roles_labels = [
f"{dept or '<em>tout dépt.</em>'}: {r.name}" for (r, dept) in displayed_roles
]
disabled_roles = {} # pour désactiver les roles que l'on ne peut pas éditer disabled_roles = {} # pour désactiver les roles que l'on ne peut pas éditer
for i in range(len(displayed_roles_strings)): for i in range(len(displayed_roles_strings)):
if displayed_roles_strings[i] not in editable_roles_strings: if displayed_roles_strings[i] not in editable_roles_strings:
disabled_roles[i] = True disabled_roles[i] = True
breakpoint()
descr = [ descr = [
("edit", {"input_type": "hidden", "default": edit}), ("edit", {"input_type": "hidden", "default": edit}),
("nom", {"title": "Nom", "size": 20, "allow_null": False}), ("nom", {"title": "Nom", "size": 20, "allow_null": False}),