From 96248ccfd3270f34ea3bac1808456f164fe64304 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 2 May 2022 14:39:06 +0200 Subject: [PATCH] BUT: assoc. modules <-> App. Critiques --- app/models/but_refcomp.py | 58 ++++++++++++----- app/models/modules.py | 10 ++- app/scodoc/sco_edit_apc.py | 4 +- app/scodoc/sco_edit_module.py | 65 +++++++++++++++++-- app/static/css/scodoc.css | 4 ++ .../versions/6002d7d366e5_assoc_ue_niveau.py | 33 ++++++++++ 6 files changed, 145 insertions(+), 29 deletions(-) diff --git a/app/models/but_refcomp.py b/app/models/but_refcomp.py index 3b28676514..b585019753 100644 --- a/app/models/but_refcomp.py +++ b/app/models/but_refcomp.py @@ -286,6 +286,21 @@ class ApcNiveau(db.Model, XMLModel): ) +app_critiques_modules = db.Table( + "apc_modules_acs", + db.Column( + "module_id", + db.ForeignKey("notes_modules.id", ondelete="CASCADE"), + primary_key=True, + ), + db.Column( + "app_crit_id", + db.ForeignKey("apc_app_critique.id"), + primary_key=True, + ), +) + + class ApcAppCritique(db.Model, XMLModel): "Apprentissage Critique BUT" id = db.Column(db.Integer, primary_key=True) @@ -293,12 +308,31 @@ class ApcAppCritique(db.Model, XMLModel): code = db.Column(db.Text(), nullable=False, index=True) libelle = db.Column(db.Text()) - modules = db.relationship( - "Module", - secondary="apc_modules_acs", - lazy="dynamic", - backref=db.backref("app_critiques", lazy="dynamic"), - ) + # modules = db.relationship( + # "Module", + # secondary="apc_modules_acs", + # lazy="dynamic", + # backref=db.backref("app_critiques", lazy="dynamic"), + # ) + + @classmethod + def app_critiques_ref_comp( + cls, + ref_comp: ApcReferentielCompetences, + annee: str, + competence: ApcCompetence = None, + ) -> flask_sqlalchemy.BaseQuery: + "Liste les AC de tous les parcours de ref_comp pour l'année indiquée" + assert annee in {"BUT1", "BUT2", "BUT3"} + query = cls.query.filter( + ApcAppCritique.niveau_id == ApcNiveau.id, + ApcNiveau.competence_id == ApcCompetence.id, + ApcNiveau.annee == annee, + ApcCompetence.referentiel_id == ref_comp.id, + ) + if competence is not None: + query = query.filter(ApcNiveau.competence == competence) + return query def to_dict(self) -> dict: return {"libelle": self.libelle} @@ -314,13 +348,6 @@ class ApcAppCritique(db.Model, XMLModel): return [m for m in self.modules if m.module_type == ModuleType.SAE] -ApcAppCritiqueModules = db.Table( - "apc_modules_acs", - db.Column("module_id", db.ForeignKey("notes_modules.id")), - db.Column("app_crit_id", db.ForeignKey("apc_app_critique.id")), -) - - parcours_modules = db.Table( "parcours_modules", db.Column( @@ -350,11 +377,6 @@ class ApcParcours(db.Model, XMLModel): lazy="dynamic", cascade="all, delete-orphan", ) - # modules = db.relationship( - # "Module", - # secondary=parcours_modules, - # back_populates="parcours", - # ) def __repr__(self): return f"<{self.__class__.__name__} {self.code}>" diff --git a/app/models/modules.py b/app/models/modules.py index 5ffac90435..28ef949b88 100644 --- a/app/models/modules.py +++ b/app/models/modules.py @@ -3,7 +3,7 @@ from app import db from app.models import APO_CODE_STR_LEN -from app.models.but_refcomp import parcours_modules +from app.models.but_refcomp import app_critiques_modules, parcours_modules from app.scodoc import sco_utils as scu from app.scodoc.sco_codes_parcours import UE_SPORT from app.scodoc.sco_utils import ModuleType @@ -50,7 +50,13 @@ class Module(db.Model): "ApcParcours", secondary=parcours_modules, lazy="subquery", - # cascade="all, delete", + backref=db.backref("modules", lazy=True), + ) + + app_critiques = db.relationship( + "ApcAppCritique", + secondary=app_critiques_modules, + lazy="subquery", backref=db.backref("modules", lazy=True), ) diff --git a/app/scodoc/sco_edit_apc.py b/app/scodoc/sco_edit_apc.py index 11bb53af19..8cb516a5a2 100644 --- a/app/scodoc/sco_edit_apc.py +++ b/app/scodoc/sco_edit_apc.py @@ -127,7 +127,7 @@ def html_edit_formation_apc( formation=formation, titre=f"Ressources du S{semestre_idx}", create_element_msg="créer une nouvelle ressource", - matiere_parent=matiere_parent, + # matiere_parent=matiere_parent, modules=ressources_in_sem, module_type=ModuleType.RESSOURCE, editable=editable, @@ -143,7 +143,7 @@ def html_edit_formation_apc( formation=formation, titre=f"Situations d'Apprentissage et d'Évaluation (SAÉs) S{semestre_idx}", create_element_msg="créer une nouvelle SAÉ", - matiere_parent=matiere_parent, + # matiere_parent=matiere_parent, modules=saes_in_sem, module_type=ModuleType.SAE, editable=editable, diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 2317d8b4ce..d986c60e06 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -39,7 +39,7 @@ from app.models import APO_CODE_STR_LEN from app.models import Formation, Matiere, Module, UniteEns from app.models import FormSemestre, ModuleImpl from app.models import ScolarNews -from app.models.but_refcomp import ApcParcours +from app.models.but_refcomp import ApcAppCritique, ApcParcours import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu @@ -272,6 +272,7 @@ def module_edit( # --- Détermination de la formation orig_semestre_idx = None + ue = None if create: if matiere_id: matiere = Matiere.query.get_or_404(matiere_id) @@ -286,6 +287,7 @@ def module_edit( if not module_id: raise ValueError("missing module_id !") module = models.Module.query.get_or_404(module_id) + ue = module.ue module_dict = module.to_dict() formation = module.formation unlocked = not module_is_locked(module_id) @@ -633,8 +635,9 @@ def module_edit( }, ), ] - # Choix des parcours + if is_apc: + # Choix des parcours ref_comp = formation.referentiel_competence if ref_comp: descr += [ @@ -656,13 +659,54 @@ def module_edit( module_dict["parcours"] = [ str(parcour.id) for parcour in module.parcours ] + module_dict["app_critiques"] = [ + str(app_crit.id) for app_crit in module.app_critiques + ] + # Choix des Apprentissages Critiques + if ue is not None: + annee = f"BUT{orig_semestre_idx//2 + 1}" + app_critiques = ApcAppCritique.app_critiques_ref_comp(ref_comp, annee) + descr += ( + [ + ( + "app_critiques", + { + "title": "Apprentissages Critiques", + "input_type": "checkbox", + "vertical": True, + "dom_id": "tf_module_app_critiques", + "labels": [ + app_crit.libelle for app_crit in app_critiques + ], + "allowed_values": [ + str(app_crit.id) for app_crit in app_critiques + ], + "explanation": "apprentissages critiques liés à ce module.", + }, + ) + ] + if (ue.niveau_competence is not None) + else [ + ( + "app_critiques", + { + "input_type": "separator", + "title": f"""{scu.EMO_WARNING } + L'UE {ue.acronyme} {ue.titre} + n'est pas associée à un niveau de compétences + """, + }, + ) + ] + ) else: descr += [ ( "parcours", { "input_type": "separator", - "title": f"""Pas de parcours: + "title": f"""{scu.EMO_WARNING } + Pas de parcours: associer un référentiel de compétence @@ -748,10 +792,17 @@ def module_edit( # do_module_edit(tf[2]) # Modifie les parcours - module.parcours = [ - ApcParcours.query.get(int(parcour_id_str)) - for parcour_id_str in tf[2]["parcours"] - ] + if "parcours" in tf[2]: + module.parcours = [ + ApcParcours.query.get(int(parcour_id_str)) + for parcour_id_str in tf[2]["parcours"] + ] + # Modifie les AC + if "app_critiques" in tf[2]: + module.app_critiques = [ + ApcAppCritique.query.get(int(ac_id_str)) + for ac_id_str in tf[2]["app_critiques"] + ] db.session.add(module) db.session.commit() return flask.redirect( diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index 170d191744..c9933ffb58 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -2271,6 +2271,10 @@ tr#tf_module_parcours>td { background-color: rgb(229, 229, 229); } +tr#tf_module_app_critiques>td { + background-color: rgb(194, 209, 228); +} + /* tableau recap notes */ table.notes_recapcomplet { border: 2px solid blue; diff --git a/migrations/versions/6002d7d366e5_assoc_ue_niveau.py b/migrations/versions/6002d7d366e5_assoc_ue_niveau.py index cf6c5932e2..7448ab60fa 100644 --- a/migrations/versions/6002d7d366e5_assoc_ue_niveau.py +++ b/migrations/versions/6002d7d366e5_assoc_ue_niveau.py @@ -47,6 +47,23 @@ def upgrade(): ), sa.PrimaryKeyConstraint("parcours_id", "module_id"), ) + op.alter_column( + "apc_modules_acs", "module_id", existing_type=sa.INTEGER(), nullable=False + ) + op.alter_column( + "apc_modules_acs", "app_crit_id", existing_type=sa.INTEGER(), nullable=False + ) + op.drop_constraint( + "apc_modules_acs_module_id_fkey", "apc_modules_acs", type_="foreignkey" + ) + op.create_foreign_key( + "apc_modules_acs_module_id_fkey", + "apc_modules_acs", + "notes_modules", + ["module_id"], + ["id"], + ondelete="CASCADE", + ) # ### end Alembic commands ### @@ -57,4 +74,20 @@ def downgrade(): ) op.drop_column("notes_ue", "niveau_competence_id") op.drop_table("parcours_modules") + op.drop_constraint( + "apc_modules_acs_module_id_fkey", "apc_modules_acs", type_="foreignkey" + ) + op.create_foreign_key( + "apc_modules_acs_module_id_fkey", + "apc_modules_acs", + "notes_modules", + ["module_id"], + ["id"], + ) + op.alter_column( + "apc_modules_acs", "app_crit_id", existing_type=sa.INTEGER(), nullable=True + ) + op.alter_column( + "apc_modules_acs", "module_id", existing_type=sa.INTEGER(), nullable=True + ) # ### end Alembic commands ###