diff --git a/app/forms/formsemestre/edit_modimpls_codes_apo.py b/app/forms/formsemestre/edit_modimpls_codes_apo.py new file mode 100644 index 000000000..bdd84a18d --- /dev/null +++ b/app/forms/formsemestre/edit_modimpls_codes_apo.py @@ -0,0 +1,52 @@ +""" +Formulaire configuration des codes Apo et EDT des modimps d'un formsemestre +""" + +from flask_wtf import FlaskForm +from wtforms import validators +from wtforms.fields.simple import BooleanField, StringField, SubmitField + +from app.models import FormSemestre, ModuleImpl + + +class _EditModimplsCodesForm(FlaskForm): + "form. définition des liens personnalisés" + # construit dynamiquement ci-dessous + + +def EditModimplsCodesForm(formsemestre: FormSemestre) -> _EditModimplsCodesForm: + "Création d'un formulaire pour éditer les codes" + + # Formulaire dynamique, on créé une classe ad-hoc + class F(_EditModimplsCodesForm): + pass + + def _gen_mod_form(modimpl: ModuleImpl): + field = StringField( + modimpl.module.code, + validators=[ + validators.Optional(), + validators.Length(min=1, max=80), + ], + default="", + render_kw={"size": 32}, + ) + setattr(F, f"modimpl_apo_{modimpl.id}", field) + field = StringField( + "", + validators=[ + validators.Optional(), + validators.Length(min=1, max=80), + ], + default="", + render_kw={"size": 12}, + ) + setattr(F, f"modimpl_edt_{modimpl.id}", field) + + for modimpl in formsemestre.modimpls_sorted: + _gen_mod_form(modimpl) + + F.submit = SubmitField("Valider") + F.cancel = SubmitField("Annuler", render_kw={"formnovalidate": True}) + + return F() diff --git a/app/forms/main/config_personalized_links.py b/app/forms/main/config_personalized_links.py index b2293a2c2..2a261e766 100644 --- a/app/forms/main/config_personalized_links.py +++ b/app/forms/main/config_personalized_links.py @@ -2,9 +2,8 @@ Formulaire configuration liens personalisés (menu "Liens") """ -from flask import g, url_for from flask_wtf import FlaskForm -from wtforms import FieldList, Form, validators +from wtforms import validators from wtforms.fields.simple import BooleanField, StringField, SubmitField from app.models import ScoDocSiteConfig diff --git a/app/models/moduleimpls.py b/app/models/moduleimpls.py index 741abda1b..468888e6a 100644 --- a/app/models/moduleimpls.py +++ b/app/models/moduleimpls.py @@ -7,6 +7,7 @@ from flask_sqlalchemy.query import Query from app import db from app.auth.models import User from app.comp import df_cache +from app.models import APO_CODE_STR_LEN from app.models.etudiants import Identite from app.models.modules import Module from app.scodoc.sco_exceptions import AccessDenied, ScoLockedSemError @@ -21,6 +22,10 @@ class ModuleImpl(db.Model): __table_args__ = (db.UniqueConstraint("formsemestre_id", "module_id"),) id = db.Column(db.Integer, primary_key=True) + code_apogee = db.Column(db.String(APO_CODE_STR_LEN), index=True, nullable=True) + "id de l'element pedagogique Apogee correspondant" + edt_id: str | None = db.Column(db.Text(), index=True, nullable=True) + "identifiant emplois du temps (unicité non imposée)" moduleimpl_id = db.synonym("id") module_id = db.Column(db.Integer, db.ForeignKey("notes_modules.id"), nullable=False) formsemestre_id = db.Column( @@ -45,11 +50,21 @@ class ModuleImpl(db.Model): def __repr__(self): return f"<{self.__class__.__name__} {self.id} module={repr(self.module)}>" + def get_codes_apogee(self) -> set[str]: + """Les codes Apogée (codés en base comme "VRT1,VRT2"). + (si non renseigné, ceux du module) + """ + if self.code_apogee: + return {x.strip() for x in self.code_apogee.split(",") if x} + return self.module.get_codes_apogee() + def get_edt_id(self) -> str: - "l'id pour l'emploi du temps: actuellement celui du module" + "l'id pour l'emploi du temps: à défaut, le 1er code Apogée" return ( - self.module.get_edt_id() - ) # TODO à décliner pour autoriser des codes différents ? + self.edt_id + or (self.code_apogee.split(",")[0] if self.code_apogee else "") + or self.module.get_edt_id() + ) def get_evaluations_poids(self) -> pd.DataFrame: """Les poids des évaluations vers les UE (accès via cache)""" @@ -102,6 +117,7 @@ class ModuleImpl(db.Model): d["module"] = self.module.to_dict(convert_objects=convert_objects) else: d.pop("module", None) + d["code_apogee"] = d["code_apogee"] or "" # pas de None return d def can_edit_evaluation(self, user) -> bool: diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 4f5ae61de..bff18edb3 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -464,6 +464,10 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id) }">Modifier les coefficients des UE capitalisées
+Modifier les codes Apogée et emploi du temps des modules +
On ne peut pas changer la formation d'un semestre existant car -elle défini son organisation (modules, ...), SAUF si la nouvelle formation a -exactement le même contenu que l'existante. +elle définit son organisation (modules, ...), SAUF si la nouvelle formation a +exactement le même contenu que l'existante. Cela peut arriver par exemple lorsqu'on crée une nouvelle version (pas encore modifiée) et que l'on a oublié d'y rattacher un semestre. -
+ {% if formations %}Les codes élément Apogée sont utilisés pour les exports des +résultats et peuvent aussi l'être pour connecter l'emploi du temps. Si votre +logiciel d'emploi du temps utilise des codes différents, vous pouvez aussi +indiquer un code EDT spécifique. +
+ +Les codes Apogée modules rappelés à gauche sont ceux définis +dans la formation: il sont utilisés sauf si on spécifie un code ici. +Pour les modifier, aller dans l'édition de la formation. +
+ + +