diff --git a/app/models/formations.py b/app/models/formations.py index 3d63b1b7..db154e50 100644 --- a/app/models/formations.py +++ b/app/models/formations.py @@ -7,6 +7,7 @@ from app.models import APO_CODE_STR_LEN from app.models import SHORT_STR_LEN from app.scodoc import sco_utils as scu from app.scodoc.sco_utils import ModuleType +from app.scodoc import sco_codes_parcours class Formation(db.Model): @@ -39,6 +40,10 @@ class Formation(db.Model): def __repr__(self): return f"<{self.__class__.__name__}(id={self.id}, dept_id={self.dept_id}, acronyme='{self.acronyme}')>" + def get_parcours(self): + """get l'instance de TypeParcours de cette formation""" + return sco_codes_parcours.get_parcours_from_code(self.type_parcours) + class UniteEns(db.Model): """Unité d'Enseignement (UE)""" @@ -51,6 +56,11 @@ class UniteEns(db.Model): acronyme = db.Column(db.Text(), nullable=False) numero = db.Column(db.Integer) # ordre de présentation titre = db.Column(db.Text()) + # Le semestre_idx n'est pas un id mais le numéro du semestre: 1, 2, ... + # En ScoDoc7 et pour les formations classiques, il est NULL + # (le numéro du semestre étant alors déterminé par celui des modules de l'UE) + # Pour les formations APC, il est obligatoire (de 1 à 6 pour le BUT): + semestre_idx = db.Column(db.Integer, nullable=True, index=True) # Type d'UE: 0 normal ("fondamentale"), 1 "sport", 2 "projet et stage (LP)", # 4 "élective" type = db.Column(db.Integer, default=0, server_default="0") diff --git a/app/scodoc/TrivialFormulator.py b/app/scodoc/TrivialFormulator.py index e324742c..7e8f9bd8 100644 --- a/app/scodoc/TrivialFormulator.py +++ b/app/scodoc/TrivialFormulator.py @@ -51,7 +51,7 @@ def TrivialFormulator( allow_null : if true, field can be left empty (default true) type : 'string', 'int', 'float' (default to string), 'list' (only for hidden) readonly : default False. if True, no form element, display current value. - convert_numbers: covert int and float values (from string) + convert_numbers: convert int and float values (from string) allowed_values : list of possible values (default: any value) validator : function validating the field (called with (value,field)). min_value : minimum value (for floats and ints) diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 0591fd08..1a46be44 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -32,7 +32,7 @@ import flask from flask import g, url_for, request from flask_login import current_user -from app.models.formations import UniteEns +from app.models.formations import Formation, UniteEns import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu from app.scodoc.sco_utils import ModuleType @@ -66,6 +66,7 @@ _ueEditor = ndb.EditableTable( "acronyme", "numero", "titre", + "semestre_idx", "type", "ue_code", "ects", @@ -82,6 +83,7 @@ _ueEditor = ndb.EditableTable( "numero": ndb.int_null_is_zero, "ects": ndb.float_null_is_null, "coefficient": ndb.float_null_is_zero, + "semestre_idx": ndb.int_null_is_null, }, ) @@ -195,7 +197,7 @@ def ue_create(formation_id=None): def ue_edit(ue_id=None, create=False, formation_id=None): - """Modification ou creation d'une UE""" + """Modification ou création d'une UE""" from app.scodoc import sco_formations create = int(create) @@ -212,25 +214,25 @@ def ue_edit(ue_id=None, create=False, formation_id=None): title = "Création d'une UE" initvalues = {} submitlabel = "Créer cette UE" - Fol = sco_formations.formation_list(args={"formation_id": formation_id}) - if not Fol: - raise ScoValueError( - "Formation %s inexistante ! (si vous avez suivi un lien valide, merci de signaler le problème)" - % formation_id - ) - Fo = Fol[0] - parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"]) - + formation = Formation.query.get(formation_id) + if not formation: + raise ScoValueError(f"Formation inexistante ! (id={formation_id})") + parcours = formation.get_parcours() + is_apc = parcours.APC_SAE + semestres_indices = list(range(1, parcours.NB_SEM + 1)) H = [ html_sco_header.sco_header(page_title=title, javascripts=["js/edit_ue.js"]), "

" + title, - " (formation %(acronyme)s, version %(version)s)

" % Fo, + f" (formation {formation.acronyme}, version {formation.version})", """ -

Les UE sont des groupes de modules dans une formation donnée, utilisés pour l'évaluation (on calcule des moyennes par UE et applique des seuils ("barres")). -

+

Les UE sont des groupes de modules dans une formation donnée, + utilisés pour la validation (on calcule des moyennes par UE et applique des + seuils ("barres")). +

-

Note: L'UE n'a pas de coefficient associé. Seuls les modules ont des coefficients. -

""", +

Note: sauf exception, l'UE n'a pas de coefficient associé. + Seuls les modules ont des coefficients. +

""", ] ue_types = parcours.ALLOWED_UE_TYPES @@ -252,6 +254,18 @@ def ue_edit(ue_id=None, create=False, formation_id=None): "type": "int", }, ), + ( + "semestre_idx", + { + "input_type": "menu", + "type": "int", + "allow_null": True, + "title": parcours.SESSION_NAME.capitalize(), + "explanation": "%s de l'UE dans la formation" % parcours.SESSION_NAME, + "labels": ["non spécifié"] + [str(x) for x in semestres_indices], + "allowed_values": [""] + semestres_indices, + }, + ), ( "type", { @@ -276,11 +290,12 @@ def ue_edit(ue_id=None, create=False, formation_id=None): "size": 4, "type": "float", "title": "Coefficient", - "explanation": """les coefficients d'UE ne sont utilisés que lorsque - l'option Utiliser les coefficients d'UE pour calculer la moyenne générale - est activée. Par défaut, le coefficient d'une UE est simplement la somme des - coefficients des modules dans lesquels l'étudiant a des notes. - """, + "explanation": """les coefficients d'UE ne sont utilisés que + lorsque l'option Utiliser les coefficients d'UE pour calculer + la moyenne générale est activée. Par défaut, le coefficient + d'une UE est simplement la somme des coefficients des modules dans + lesquels l'étudiant a des notes. + """, }, ), ( @@ -308,30 +323,13 @@ def ue_edit(ue_id=None, create=False, formation_id=None): }, ), ] - if parcours.UE_IS_MODULE: - # demande le semestre pour creer le module immediatement: - semestres_indices = list(range(1, parcours.NB_SEM + 1)) - fw.append( - ( - "semestre_id", - { - "input_type": "menu", - "type": "int", - "title": parcours.SESSION_NAME.capitalize(), - "explanation": "%s de début du module dans la formation" - % parcours.SESSION_NAME, - "labels": [str(x) for x in semestres_indices], - "allowed_values": semestres_indices, - }, - ) - ) if create and not parcours.UE_IS_MODULE: fw.append( ( "create_matiere", { "input_type": "boolcheckbox", - "default": False, + "default": True, "title": "Créer matière identique", "explanation": "créer immédiatement une matière dans cette UE (utile si on n'utilise pas de matières)", }, @@ -353,13 +351,10 @@ def ue_edit(ue_id=None, create=False, formation_id=None): if not tf[2]["ue_code"]: del tf[2]["ue_code"] if not tf[2]["numero"]: - if not "semestre_id" in tf[2]: - tf[2]["semestre_id"] = 0 # numero regroupant par semestre ou année: tf[2]["numero"] = next_ue_numero( - formation_id, int(tf[2]["semestre_id"] or 0) + formation_id, int(tf[2]["semestre_idx"]) ) - ue_id = do_ue_create(tf[2]) if parcours.UE_IS_MODULE or tf[2]["create_matiere"]: matiere_id = sco_edit_matiere.do_matiere_create( @@ -375,7 +370,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None): "ue_id": ue_id, "matiere_id": matiere_id, "formation_id": formation_id, - "semestre_id": tf[2]["semestre_id"], + "semestre_id": tf[2]["semestre_idx"], }, ) else: @@ -388,16 +383,20 @@ def ue_edit(ue_id=None, create=False, formation_id=None): def _add_ue_semestre_id(ues): - """ajoute semestre_id dans les ue, en regardant le premier module de chacune. + """ajoute semestre_id dans les ue, en regardant + semestre_idx ou à défaut le premier module de chacune. Les UE sans modules se voient attribuer le numero UE_SEM_DEFAULT (1000000), qui les place à la fin de la liste. """ for ue in ues: - Modlist = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]}) - if Modlist: - ue["semestre_id"] = Modlist[0]["semestre_id"] + if ue["semestre_idx"] is not None: + ue["semestre_id"] = ue["semestre_idx"] else: - ue["semestre_id"] = 1000000 + modules = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]}) + if modules: + ue["semestre_id"] = modules[0]["semestre_id"] + else: + ue["semestre_id"] = 1000000 def next_ue_numero(formation_id, semestre_id=None): diff --git a/migrations/versions/c8efc54586d8_ue_semestre_idx.py b/migrations/versions/c8efc54586d8_ue_semestre_idx.py new file mode 100644 index 00000000..7eeb0e7e --- /dev/null +++ b/migrations/versions/c8efc54586d8_ue_semestre_idx.py @@ -0,0 +1,30 @@ +"""UE.semestre_idx + +Revision ID: c8efc54586d8 +Revises: 6cfc21a7ae1b +Create Date: 2021-11-14 17:35:39.602613 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'c8efc54586d8' +down_revision = '6cfc21a7ae1b' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('notes_ue', sa.Column('semestre_idx', sa.Integer(), nullable=True)) + op.create_index(op.f('ix_notes_ue_semestre_idx'), 'notes_ue', ['semestre_idx'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_notes_ue_semestre_idx'), table_name='notes_ue') + op.drop_column('notes_ue', 'semestre_idx') + # ### end Alembic commands ###