diff --git a/app/models/formations.py b/app/models/formations.py index fd1f01895..3d63b1b75 100644 --- a/app/models/formations.py +++ b/app/models/formations.py @@ -143,11 +143,15 @@ class Module(db.Model): def set_ue_coef_dict(self, ue_coef_dict: dict) -> None: """set coefs vers les UE (remplace existants) ue_coef_dict = { ue_id : coef } + Les coefs nuls (zéro) ne sont pas stockés: la relation est supprimée. """ ue_coefs = [] for ue_id, coef in ue_coef_dict.items(): ue = UniteEns.query.get(ue_id) - ue_coefs.append(ModuleUECoef(module=self, ue=ue, coef=coef)) + if coef == 0.0: + self.delete_ue_coef(ue) + else: + ue_coefs.append(ModuleUECoef(module=self, ue=ue, coef=coef)) self.ue_coefs = ue_coefs def update_ue_coef_dict(self, ue_coef_dict: dict): @@ -163,7 +167,12 @@ class Module(db.Model): def delete_ue_coef(self, ue): """delete coef""" ue_coef = ModuleUECoef.query.get((self.id, ue.id)) - db.session.delete(ue_coef) + if ue_coef: + db.session.delete(ue_coef) + + def ue_coefs_descr(self): + """List of tuples [ (ue_acronyme, coef) ]""" + return [(c.ue.acronyme, c.coef) for c in self.ue_coefs] class ModuleUECoef(db.Model): diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 9fcb75a48..979ed3af7 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -37,6 +37,7 @@ import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu from app.scodoc.sco_utils import ModuleType from app import log +from app import models from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc.sco_permissions import Permission from app.scodoc.sco_exceptions import ScoValueError, ScoLockedFormError, ScoGenError @@ -344,9 +345,8 @@ def module_edit(module_id=None): raise ScoValueError("invalid module !") module = modules[0] unlocked = not module_is_locked(module_id) - formation = sco_formations.formation_list( - args={"formation_id": module["formation_id"]} - )[0] + formation_id = module["formation_id"] + formation = sco_formations.formation_list(args={"formation_id": formation_id})[0] parcours = sco_codes_parcours.get_parcours_from_code(formation["type_parcours"]) is_apc = parcours.APC_SAE ues_matieres = ndb.SimpleDictFetch( @@ -356,7 +356,7 @@ def module_edit(module_id=None): AND ue.formation_id = %(formation_id)s ORDER BY ue.numero, mat.numero """, - {"formation_id": module["formation_id"]}, + {"formation_id": formation_id}, ) mat_names = ["%s / %s" % (x["acronyme"], x["titre"]) for x in ues_matieres] ue_mat_ids = ["%s!%s" % (x["ue_id"], x["matiere_id"]) for x in ues_matieres] @@ -366,7 +366,7 @@ def module_edit(module_id=None): dest_url = url_for( "notes.ue_table", scodoc_dept=g.scodoc_dept, - formation_id=str(module["formation_id"]), + formation_id=str(formation_id), ) H = [ html_sco_header.sco_header( @@ -387,56 +387,72 @@ def module_edit(module_id=None): """
Formation verrouillée, seuls certains éléments peuvent être modifiés
""" ) - tf = TrivialFormulator( - request.base_url, - scu.get_request_args(), + descr = [ ( + "code", + { + "size": 10, + "explanation": "code du module (doit être unique dans la formation)", + "allow_null": False, + "validator": lambda val, field, formation_id=formation_id: check_module_code_unicity( + val, field, formation_id, module_id=module_id + ), + }, + ), + ("titre", {"size": 30, "explanation": "nom du module"}), + ("abbrev", {"size": 20, "explanation": "nom abrégé (pour bulletins)"}), + ( + "module_type", + { + "input_type": "menu", + "title": "Type", + "explanation": "", + "labels": [x.name.capitalize() for x in scu.ModuleType], + "allowed_values": [str(int(x)) for x in scu.ModuleType], + "enabled": unlocked, + }, + ), + ( + "heures_cours", + {"size": 4, "type": "float", "explanation": "nombre d'heures de cours"}, + ), + ( + "heures_td", + { + "size": 4, + "type": "float", + "explanation": "nombre d'heures de Travaux Dirigés", + }, + ), + ( + "heures_tp", + { + "size": 4, + "type": "float", + "explanation": "nombre d'heures de Travaux Pratiques", + }, + ), + ] + if is_apc: + a_module = models.Module.query.get(module_id) + coefs_descr = a_module.ue_coefs_descr() + if coefs_descr: + coefs_descr_txt = ", ".join(["%s: %s" % x for x in coefs_descr]) + else: + coefs_descr_txt = """non définis""" + descr += [ ( - "code", + "ue_coefs", { - "size": 10, - "explanation": "code du module (doit être unique dans la formation)", - "allow_null": False, - "validator": lambda val, field, formation_id=module[ - "formation_id" - ]: check_module_code_unicity( - val, field, formation_id, module_id=module_id - ), + "readonly": True, + "title": "Coefficients vers les UE", + "default": coefs_descr_txt, + "explanation": "passer par la page d'édition de la formation pour modifier les coefficients", }, - ), - ("titre", {"size": 30, "explanation": "nom du module"}), - ("abbrev", {"size": 20, "explanation": "nom abrégé (pour bulletins)"}), - ( - "module_type", - { - "input_type": "menu", - "title": "Type", - "explanation": "", - "labels": [x.name.capitalize() for x in scu.ModuleType], - "allowed_values": [str(int(x)) for x in scu.ModuleType], - "enabled": unlocked, - }, - ), - ( - "heures_cours", - {"size": 4, "type": "float", "explanation": "nombre d'heures de cours"}, - ), - ( - "heures_td", - { - "size": 4, - "type": "float", - "explanation": "nombre d'heures de Travaux Dirigés", - }, - ), - ( - "heures_tp", - { - "size": 4, - "type": "float", - "explanation": "nombre d'heures de Travaux Pratiques", - }, - ), + ) + ] + else: # Module classique avec coef scalaire: + descr += [ ( "coefficient", { @@ -447,51 +463,57 @@ def module_edit(module_id=None): "enabled": unlocked, }, ), - # ('ects', { 'size' : 4, 'type' : 'float', 'title' : 'ECTS', 'explanation' : 'nombre de crédits ECTS', 'enabled' : unlocked }), - ("formation_id", {"input_type": "hidden"}), - ("ue_id", {"input_type": "hidden"}), - ("module_id", {"input_type": "hidden"}), - ( - "ue_matiere_id", - { - "input_type": "menu", - "title": "Matière", - "explanation": "un module appartient à une seule matière.", - "labels": mat_names, - "allowed_values": ue_mat_ids, - "enabled": unlocked, - }, - ), - ( - "semestre_id", - { - "input_type": "menu", - "type": "int", - "title": parcours.SESSION_NAME.capitalize(), - "explanation": "%s de début du module dans la formation standard" - % parcours.SESSION_NAME, - "labels": [str(x) for x in semestres_indices], - "allowed_values": semestres_indices, - "enabled": unlocked, - }, - ), - ( - "code_apogee", - { - "title": "Code Apogée", - "size": 25, - "explanation": "(optionnel) code élément pédagogique Apogée ou liste de codes ELP séparés par des virgules", - }, - ), - ( - "numero", - { - "size": 2, - "explanation": "numéro (1,2,3,4...) pour ordre d'affichage", - "type": "int", - }, - ), + ] + descr += [ + ("formation_id", {"input_type": "hidden"}), + ("ue_id", {"input_type": "hidden"}), + ("module_id", {"input_type": "hidden"}), + ( + "ue_matiere_id", + { + "input_type": "menu", + "title": "Matière", + "explanation": "un module appartient à une seule matière.", + "labels": mat_names, + "allowed_values": ue_mat_ids, + "enabled": unlocked, + }, ), + ( + "semestre_id", + { + "input_type": "menu", + "type": "int", + "title": parcours.SESSION_NAME.capitalize(), + "explanation": "%s de début du module dans la formation standard" + % parcours.SESSION_NAME, + "labels": [str(x) for x in semestres_indices], + "allowed_values": semestres_indices, + "enabled": unlocked, + }, + ), + ( + "code_apogee", + { + "title": "Code Apogée", + "size": 25, + "explanation": "(optionnel) code élément pédagogique Apogée ou liste de codes ELP séparés par des virgules", + }, + ), + ( + "numero", + { + "size": 2, + "explanation": "numéro (1,2,3,4...) pour ordre d'affichage", + "type": "int", + }, + ), + ] + + tf = TrivialFormulator( + request.base_url, + scu.get_request_args(), + descr, html_foot_markup="""
""".format( module_id, ",".join(sco_tag_module.module_tag_list(module_id)) ), diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 232e3cb87..0591fd080 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -453,6 +453,7 @@ def ue_table(formation_id=None, msg=""): # was ue_list raise ScoValueError("invalid formation_id") F = F[0] parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"]) + is_apc = parcours.APC_SAE locked = sco_formations.formation_has_locked_sems(formation_id) ues = ue_list(args={"formation_id": formation_id, "is_external": False}) @@ -568,7 +569,18 @@ du programme" (menu "Semestre") si vous avez un semestre en cours); ) H.append("") - + # Formation APC (BUT) ? + if is_apc: + H.append( + f"""
+
Formation par compétences (BUT)
+ +
""" + ) # Description des UE/matières/modules H.append('
') H.append('
Programme pédagogique:
') diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index f897375da..e091b90a8 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -1606,6 +1606,11 @@ div.ue_warning span { font-weight: bold; } +span.missing_value { + font-weight: bold; + color: red; +} + /* tableau recap notes */ table.notes_recapcomplet { border: 2px solid blue; diff --git a/tests/unit/test_but_modules.py b/tests/unit/test_but_modules.py index 1b8aac936..74899a0ae 100644 --- a/tests/unit/test_but_modules.py +++ b/tests/unit/test_but_modules.py @@ -105,3 +105,7 @@ def test_modules_coefs(test_client): mod.delete_ue_coef(ue1) db.session.commit() assert len(mod.ue_coefs) == 1 + # Gestion des coefs nuls: + mod.set_ue_coef(ue2, 0.0) + db.session.commit() + assert len(mod.ue_coefs) == 0