From 3bb9a5cb76948a31a7cf90bfc613be0e93d9d8be Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 8 Nov 2021 19:44:25 +0100 Subject: [PATCH] WIP: distinction SAE/ressources, poids de evals --- app/models/__init__.py | 2 + app/models/but_pn.py | 39 +++++++- app/models/formations.py | 9 +- app/models/formsemestre.py | 53 ++++++++++- app/scodoc/html_sidebar.py | 2 +- app/scodoc/sco_codes_parcours.py | 84 +++++++++++++---- app/scodoc/sco_edit_module.py | 4 +- app/scodoc/sco_edit_ue.py | 89 +++++++++++++++++-- app/scodoc/sco_utils.py | 16 +++- .../ada0d1f3d84f_but_poids_evaluations_ac.py | 64 +++++++++++++ sco_version.py | 2 +- tests/unit/test_but_modules.py | 83 +++++++++++++++++ 12 files changed, 415 insertions(+), 32 deletions(-) create mode 100644 migrations/versions/ada0d1f3d84f_but_poids_evaluations_ac.py create mode 100644 tests/unit/test_but_modules.py diff --git a/app/models/__init__.py b/app/models/__init__.py index d54a8982e..3d164fe44 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -51,9 +51,11 @@ from app.models.formsemestre import ( notes_modules_enseignants, NotesModuleImplInscription, NotesEvaluation, + EvaluationUEPoids, NotesSemSet, notes_semset_formsemestre, ) +from app.models.but_pn import AppCrit from app.models.groups import Partition, GroupDescr, group_membership from app.models.notes import ( ScolarEvent, diff --git a/app/models/but_pn.py b/app/models/but_pn.py index 1e5e42315..75f83a35f 100644 --- a/app/models/but_pn.py +++ b/app/models/but_pn.py @@ -1,4 +1,41 @@ """ScoDoc 9 models : Formation BUT 2021 """ +from enum import unique +from typing import Any -# insérer ici idk +from app import db + +from app.scodoc.sco_utils import ModuleType + +Modules_ACs = db.Table( + "modules_acs", + db.Column("module_id", db.ForeignKey("notes_modules.id")), + db.Column("ac_id", db.ForeignKey("app_crit.id")), +) + + +class AppCrit(db.Model): + "Apprentissage Critique BUT" + __tablename__ = "app_crit" + id = db.Column(db.Integer, primary_key=True) + code = db.Column(db.Text(), nullable=False, info={"label": "Code"}) + titre = db.Column(db.Text(), info={"label": "Titre"}) + + modules = db.relationship( + "NotesModule", secondary=Modules_ACs, lazy="dynamic", backref="acs" + ) + + def to_dict(self): + result = dict(self.__dict__) + result.pop("_sa_instance_state", None) + return result + + def get_label(self): + return self.code + " - " + self.titre + + def __repr__(self): + return "".format(self.code) + + def get_saes(self): + """Liste des SAE associées""" + return [m for m in self.modules if m.module_type == ModuleType.SAE] diff --git a/app/models/formations.py b/app/models/formations.py index c4f81e521..046a3e737 100644 --- a/app/models/formations.py +++ b/app/models/formations.py @@ -5,6 +5,7 @@ from typing import Any from app import db from app.models import APO_CODE_STR_LEN from app.models import SHORT_STR_LEN +from app.scodoc.sco_utils import ModuleType class NotesFormation(db.Model): @@ -116,10 +117,16 @@ class NotesModule(db.Model): numero = db.Column(db.Integer) # ordre de présentation # id de l'element pedagogique Apogee correspondant: code_apogee = db.Column(db.String(APO_CODE_STR_LEN)) - module_type = db.Column(db.Integer) # NULL ou 0:defaut, 1: malus (NOTES_MALUS) + # Type: ModuleType: DEFAULT, MALUS, RESSOURCE, MODULE_SAE (enum) + module_type = db.Column(db.Integer) # Relations: modimpls = db.relationship("NotesModuleImpl", backref="module", lazy="dynamic") + def __repr__(self): + return ( + f"" + ) + class NotesTag(db.Model): """Tag sur un module""" diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 94f0188fa..b92e2241a 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -8,6 +8,7 @@ from app import db from app.models import APO_CODE_STR_LEN from app.models import SHORT_STR_LEN from app.models import CODE_STR_LEN +from app.models import NotesUE class FormSemestre(db.Model): @@ -305,7 +306,7 @@ class NotesEvaluation(db.Model): heure_fin = db.Column(db.Time) description = db.Column(db.Text) note_max = db.Column(db.Float) - coefficient = db.Column(db.Float) + coefficient = db.Column(db.Float) # non BUT visibulletin = db.Column( db.Boolean, nullable=False, default=True, server_default="true" ) @@ -319,6 +320,56 @@ class NotesEvaluation(db.Model): # ordre de presentation (par défaut, le plus petit numero # est la plus ancienne eval): numero = db.Column(db.Integer) + ues = db.relationship("NotesUE", secondary="evaluation_ue_poids", viewonly=True) + + def set_ue_poids(self, ue, poids: float): + """Set poids évaluation vers cette UE""" + self.update_ue_poids_dict({ue.id: poids}) + + def set_ue_poids_dict(self, ue_poids_dict: dict): + """set poids vers les UE (remplace existants) + ue_poids_dict = { ue_id : poids } + """ + L = [] + for ue_id, poids in ue_poids_dict.items(): + ue = NotesUE.query.get(ue_id) + L.append(EvaluationUEPoids(evaluation=self, ue=ue, poids=poids)) + self.ue_poids = L + + def update_ue_poids_dict(self, ue_poids_dict: dict): + """update poids vers UE (ajoute aux existants)""" + current = self.get_ue_poids_dict() + current.update(ue_poids_dict) + self.set_ue_poids_dict(current) + + def get_ue_poids_dict(self): + """returns { ue_id : poids }""" + return {p.ue.id: p.poids for p in self.ue_poids} + + +class EvaluationUEPoids(db.Model): + """Poids des évaluations (BUT) + association many to many + """ + + evaluation_id = db.Column( + db.Integer, db.ForeignKey("notes_evaluation.id"), primary_key=True + ) + ue_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), primary_key=True) + poids = db.Column( + db.Float, + nullable=False, + ) + evaluation = db.relationship( + NotesEvaluation, + backref=db.backref("ue_poids", cascade="all, delete-orphan"), + ) + ue = db.relationship( + NotesUE, backref=db.backref("evaluation_ue_poids", cascade="all, delete-orphan") + ) + + def __repr__(self): + return f"" class NotesSemSet(db.Model): diff --git a/app/scodoc/html_sidebar.py b/app/scodoc/html_sidebar.py index ba57c67aa..a96da1a36 100644 --- a/app/scodoc/html_sidebar.py +++ b/app/scodoc/html_sidebar.py @@ -40,7 +40,7 @@ from app.scodoc.sco_permissions import Permission def sidebar_common(): "partie commune à toutes les sidebar" H = [ - f"""ScoDoc 9 + f"""ScoDoc 9.1
= (BARRE-TOLERANCE), considere ok # (permet d'eviter d'afficher 10.00 sous barre alors que la moyenne vaut 9.999) @@ -214,6 +247,7 @@ class TypeParcours(object): ALLOWED_UE_TYPES = list( UE_TYPE_NAME.keys() ) # par defaut, autorise tous les types d'UE + APC_SAE = False # Approche par compétences avec ressources et SAÉs def check(self, formation=None): return True, "" # status, diagnostic_message @@ -262,6 +296,20 @@ def register_parcours(Parcours): TYPES_PARCOURS[Parcours.TYPE_PARCOURS] = Parcours +class ParcoursBUT(TypeParcours): + """BUT Bachelor Universitaire de Technologie""" + + TYPE_PARCOURS = 700 + NAME = "BUT" + NB_SEM = 6 + COMPENSATION_UE = False + APC_SAE = True + ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT] + + +register_parcours(ParcoursBUT()) + + class ParcoursDUT(TypeParcours): """DUT selon l'arrêté d'août 2005""" @@ -302,7 +350,7 @@ register_parcours(ParcoursDUTMono()) class ParcoursDUT2(ParcoursDUT): """DUT en deux semestres (par ex.: années spéciales semestrialisées)""" - TYPE_PARCOURS = 130 + TYPE_PARCOURS = CodesParcours.DUT2 NAME = "DUT2" NB_SEM = 2 @@ -315,7 +363,7 @@ class ParcoursLP(TypeParcours): (pour anciennes LP. Après 2014, préférer ParcoursLP2014) """ - TYPE_PARCOURS = 200 + TYPE_PARCOURS = CodesParcours.LP NAME = "LP" NB_SEM = 1 COMPENSATION_UE = False @@ -332,7 +380,7 @@ register_parcours(ParcoursLP()) class ParcoursLP2sem(ParcoursLP): """Licence Pro (en deux "semestres")""" - TYPE_PARCOURS = 210 + TYPE_PARCOURS = CodesParcours.LP2sem NAME = "LP2sem" NB_SEM = 2 COMPENSATION_UE = True @@ -345,7 +393,7 @@ register_parcours(ParcoursLP2sem()) class ParcoursLP2semEvry(ParcoursLP): """Licence Pro (en deux "semestres", U. Evry)""" - TYPE_PARCOURS = 220 + TYPE_PARCOURS = CodesParcours.LP2semEvry NAME = "LP2semEvry" NB_SEM = 2 COMPENSATION_UE = True @@ -371,7 +419,7 @@ class ParcoursLP2014(TypeParcours): # l'établissement d'un coefficient qui peut varier dans un rapport de 1 à 3. ", etc ne sont _pas_ # vérifiés par ScoDoc) - TYPE_PARCOURS = 230 + TYPE_PARCOURS = CodesParcours.LP2014 NAME = "LP2014" NB_SEM = 1 ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT, UE_STAGE_LP] @@ -415,7 +463,7 @@ register_parcours(ParcoursLP2014()) class ParcoursLP2sem2014(ParcoursLP): """Licence Pro (en deux "semestres", selon arrêté du 22/01/2014)""" - TYPE_PARCOURS = 240 + TYPE_PARCOURS = CodesParcours.LP2sem2014 NAME = "LP2014_2sem" NB_SEM = 2 @@ -427,7 +475,7 @@ register_parcours(ParcoursLP2sem2014()) class ParcoursM2(TypeParcours): """Master 2 (en deux "semestres")""" - TYPE_PARCOURS = 250 + TYPE_PARCOURS = CodesParcours.M2 NAME = "M2sem" NB_SEM = 2 COMPENSATION_UE = True @@ -440,7 +488,7 @@ register_parcours(ParcoursM2()) class ParcoursM2noncomp(ParcoursM2): """Master 2 (en deux "semestres") sans compensation""" - TYPE_PARCOURS = 251 + TYPE_PARCOURS = CodesParcours.M2noncomp NAME = "M2noncomp" COMPENSATION_UE = False UNUSED_CODES = set((ADC, ATT, ATB)) @@ -452,7 +500,7 @@ register_parcours(ParcoursM2noncomp()) class ParcoursMono(TypeParcours): """Formation générique en une session""" - TYPE_PARCOURS = 300 + TYPE_PARCOURS = CodesParcours.Mono NAME = "Mono" NB_SEM = 1 COMPENSATION_UE = False @@ -465,7 +513,7 @@ register_parcours(ParcoursMono()) class ParcoursLegacy(TypeParcours): """DUT (ancien ScoDoc, ne plus utiliser)""" - TYPE_PARCOURS = 0 + TYPE_PARCOURS = CodesParcours.Legacy NAME = "DUT" NB_SEM = 4 COMPENSATION_UE = None # backward compat: defini dans formsemestre @@ -499,7 +547,7 @@ class ParcoursBachelorISCID6(ParcoursISCID): """ISCID: Bachelor en 3 ans (6 sem.)""" NAME = "ParcoursBachelorISCID6" - TYPE_PARCOURS = 1001 + TYPE_PARCOURS = CodesParcours.ISCID6 NAME = "" NB_SEM = 6 ECTS_PROF_DIPL = 8 # crédits professionnels requis pour obtenir le diplôme @@ -510,7 +558,7 @@ register_parcours(ParcoursBachelorISCID6()) class ParcoursMasterISCID4(ParcoursISCID): "ISCID: Master en 2 ans (4 sem.)" - TYPE_PARCOURS = 1002 + TYPE_PARCOURS = CodesParcours.ISCID4 NAME = "ParcoursMasterISCID4" NB_SEM = 4 ECTS_PROF_DIPL = 15 # crédits professionnels requis pour obtenir le diplôme @@ -536,7 +584,7 @@ class ParcoursUCAC(TypeParcours): class ParcoursLicenceUCAC3(ParcoursUCAC): """UCAC: Licence en 3 sessions d'un an""" - TYPE_PARCOURS = 501 + TYPE_PARCOURS = CodesParcours.LicenceUCAC3 NAME = "Licence UCAC en 3 sessions d'un an" NB_SEM = 3 @@ -547,7 +595,7 @@ register_parcours(ParcoursLicenceUCAC3()) class ParcoursMasterUCAC2(ParcoursUCAC): """UCAC: Master en 2 sessions d'un an""" - TYPE_PARCOURS = 502 + TYPE_PARCOURS = CodesParcours.MasterUCAC2 NAME = "Master UCAC en 2 sessions d'un an" NB_SEM = 2 @@ -558,7 +606,7 @@ register_parcours(ParcoursMasterUCAC2()) class ParcoursMonoUCAC(ParcoursUCAC): """UCAC: Formation en 1 session de durée variable""" - TYPE_PARCOURS = 503 + TYPE_PARCOURS = CodesParcours.MonoUCAC NAME = "Formation UCAC en 1 session de durée variable" NB_SEM = 1 UNUSED_CODES = set((ADC, ATT, ATB)) @@ -570,7 +618,7 @@ register_parcours(ParcoursMonoUCAC()) class Parcours6Sem(TypeParcours): """Parcours générique en 6 semestres""" - TYPE_PARCOURS = 600 + TYPE_PARCOURS = CodesParcours.GEN_6_SEM NAME = "Formation en 6 semestres" NB_SEM = 6 COMPENSATION_UE = True @@ -592,7 +640,7 @@ register_parcours(Parcours6Sem()) class ParcoursMasterLMD(TypeParcours): """Master générique en 4 semestres dans le LMD""" - TYPE_PARCOURS = 402 + TYPE_PARCOURS = CodesParcours.MasterLMD NAME = "Master LMD" NB_SEM = 4 COMPENSATION_UE = True # variabale inutilisée @@ -605,7 +653,7 @@ register_parcours(ParcoursMasterLMD()) class ParcoursMasterIG(ParcoursMasterLMD): """Master de l'Institut Galilée (U. Paris 13) en 4 semestres (LMD)""" - TYPE_PARCOURS = 403 + TYPE_PARCOURS = CodesParcours.MasterIG NAME = "Master IG P13" BARRE_MOY = 10.0 NOTES_BARRE_VALID_UE_TH = 10.0 # seuil pour valider UE diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 54d7fc846..ae19b8b77 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -420,8 +420,8 @@ def module_edit(module_id=None): "input_type": "menu", "title": "Type", "explanation": "", - "labels": ("Standard", "Malus"), - "allowed_values": (str(scu.MODULE_STANDARD), str(scu.MODULE_MALUS)), + "labels": [x.name.capitalize() for x in scu.ModuleType], + "allowed_values": [str(int(x)) for x in scu.ModuleType], "enabled": unlocked, }, ), diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 66d8777d5..0911eec87 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -775,8 +775,12 @@ def _ue_table_ues( ) else: H.append('[verrouillé]') + if parcours.APC_SAE: + func_html_list = _ue_table_ressources_saes + else: + func_html_list = _ue_table_matieres H.append( - _ue_table_matieres( + func_html_list( parcours, ue, editable, @@ -837,6 +841,8 @@ def _ue_table_matieres( delete_disabled_icon, ) ) + if not parcours.UE_IS_MODULE: + H.append("") if not matieres: H.append("
  • Aucune matière dans cette UE ! ") if editable: @@ -855,6 +861,76 @@ def _ue_table_matieres( return "\n".join(H) +def _ue_table_ressources_saes( + parcours, + ue, + editable, + tag_editable, + arrow_up, + arrow_down, + arrow_none, + delete_icon, + delete_disabled_icon, +): + """Édition de programme: liste des ressources et SAÉs d'une UE. + (pour les parcours APC_SAE) + """ + matieres = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]}) + if not matieres: + # Les formations APC (BUT) n'utilisent pas de matières + # mais il doit y en avoir une par UE + # silently fix this on-the-fly to ease migration + _ = sco_edit_matiere.do_matiere_create( + {"ue_id": ue["ue_id"], "titre": "APC", "numero": 1}, + ) + matieres = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]}) + assert matieres + mat = matieres[0] + + H = [ + """ +
      + """ + ] + modules = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]}) + for titre, element_name, module_type in ( + ("Ressources", "ressource", scu.ModuleType.RESSOURCE), + ("SAÉs", "SAÉ", scu.ModuleType.SAE), + ("Autres modules", "xxx", None), + ): + H.append(f'
    • {titre}') + elements = [ + m + for m in modules + if module_type == m["module_type"] + or ( + (module_type is None) + and m["module_type"] + not in (scu.ModuleType.RESSOURCE, scu.ModuleType.SAE) + ) + ] + H.append( + _ue_table_modules( + parcours, + mat, + elements, + editable, + tag_editable, + arrow_up, + arrow_down, + arrow_none, + delete_icon, + delete_disabled_icon, + empty_list_msg="Aucune " + element_name, + create_element_msg="créer une " + element_name, + add_suppress_link=False, + ) + ) + + H.append("
    ") + return "\n".join(H) + + def _ue_table_modules( parcours, mat, @@ -866,6 +942,10 @@ def _ue_table_modules( arrow_none, delete_icon, delete_disabled_icon, + unit_name="matière", + add_suppress_link=True, # lien "supprimer cette matière" + empty_list_msg="Aucun élément dans cette matière", + create_element_msg="créer un module", ): """Édition de programme: liste des modules d'une matière d'une UE""" H = ['
    ") - H.append("
  • ") return "\n".join(H) diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 5860ef078..f5a97a970 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -32,13 +32,12 @@ import base64 import bisect import copy import datetime +from enum import IntEnum import json from hashlib import md5 import numbers import os -import pydot import re -import requests import _thread import time import unicodedata @@ -46,6 +45,8 @@ import urllib from urllib.parse import urlparse, parse_qsl, urlunparse, urlencode from PIL import Image as PILImage +import pydot +import requests from flask import g, request from flask import url_for, make_response @@ -73,6 +74,17 @@ NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutrali MODULE_STANDARD = 0 MODULE_MALUS = 1 + +class ModuleType(IntEnum): + """Code des types de module.""" + + # Stockés en BD dans NotesModule.module_type: ne pas modifier ces valeurs + STANDARD = 0 + MALUS = 1 + RESSOURCE = 2 # BUT + SAE = 3 # BUT + + MALUS_MAX = 20.0 MALUS_MIN = -20.0 diff --git a/migrations/versions/ada0d1f3d84f_but_poids_evaluations_ac.py b/migrations/versions/ada0d1f3d84f_but_poids_evaluations_ac.py new file mode 100644 index 000000000..eb23fc606 --- /dev/null +++ b/migrations/versions/ada0d1f3d84f_but_poids_evaluations_ac.py @@ -0,0 +1,64 @@ +"""BUT: poids evaluations, AC + +Revision ID: ada0d1f3d84f +Revises: 75cf18659984 +Create Date: 2021-11-07 22:49:22.697211 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "ada0d1f3d84f" +down_revision = "75cf18659984" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "app_crit", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("code", sa.Text(), nullable=False), + sa.Column("titre", sa.Text(), nullable=True), + sa.PrimaryKeyConstraint("id"), + ) + op.create_table( + "modules_acs", + sa.Column("module_id", sa.Integer(), nullable=True), + sa.Column("ac_id", sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ["ac_id"], + ["app_crit.id"], + ), + sa.ForeignKeyConstraint( + ["module_id"], + ["notes_modules.id"], + ), + ) + op.create_table( + "evaluation_ue_poids", + sa.Column("evaluation_id", sa.Integer(), nullable=False), + sa.Column("ue_id", sa.Integer(), nullable=False), + sa.Column("poids", sa.Float(), nullable=False), + sa.ForeignKeyConstraint( + ["evaluation_id"], + ["notes_evaluation.id"], + ), + sa.ForeignKeyConstraint( + ["ue_id"], + ["notes_ue.id"], + ), + sa.PrimaryKeyConstraint("evaluation_id", "ue_id"), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("evaluation_ue_poids") + op.drop_table("modules_acs") + op.drop_table("app_crit") + # ### end Alembic commands ### diff --git a/sco_version.py b/sco_version.py index 56f9121a8..380c725ed 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.61" +SCOVERSION = "9.1.0" SCONAME = "ScoDoc" diff --git a/tests/unit/test_but_modules.py b/tests/unit/test_but_modules.py new file mode 100644 index 000000000..96f68fbf7 --- /dev/null +++ b/tests/unit/test_but_modules.py @@ -0,0 +1,83 @@ +""" +Test modèles évaluations avec poids BUT +""" + +from tests.unit import sco_fake_gen +from app import db +from app import models + +""" +mapp.set_sco_dept("RT") +from app.auth.models import get_super_admin +admin_user = get_super_admin() +ctx.push() +login_user(admin_user) +""" + + +def test_evaluation_poids(test_client): + """Association de poids vers les UE""" + G = sco_fake_gen.ScoFake(verbose=False) + _f = G.create_formation( + acronyme="F3", titre="Formation 2", titre_officiel="Titre officiel 2" + ) + _ue1 = G.create_ue(formation_id=_f["formation_id"], acronyme="UE1", titre="ue 1") + _ue2 = G.create_ue(formation_id=_f["formation_id"], acronyme="UE2", titre="ue 2") + _ue3 = G.create_ue(formation_id=_f["formation_id"], acronyme="UE3", titre="ue 3") + _mat = G.create_matiere(ue_id=_ue1["ue_id"], titre="matière test") + _mod = G.create_module( + matiere_id=_mat["matiere_id"], + code="TSM1", + coefficient=1.0, + titre="module test", + ue_id=_ue1["ue_id"], + formation_id=_f["formation_id"], + ) + sem = G.create_formsemestre( + formation_id=_f["formation_id"], + semestre_id=1, + date_debut="01/01/2021", + date_fin="30/06/2021", + ) # formsemestre_id=716 + mi = G.create_moduleimpl( + module_id=_mod["module_id"], + formsemestre_id=sem["formsemestre_id"], + ) + moduleimpl_id = mi["id"] + _e1 = G.create_evaluation( + moduleimpl_id=moduleimpl_id, + jour="01/01/2021", + description="evaluation 1", + coefficient=0, + ) + evaluation_id = _e1["evaluation_id"] # evaluation_id=25246 + ue1_id = _ue1["id"] # ue1_id=1684 + formation_id = _f["id"] # formation_id=199 + # + e1 = models.NotesEvaluation.query.get(evaluation_id) + ue1 = models.NotesUE.query.get(ue1_id) + assert e1.ue_poids == [] + p1 = 3.14 + e1.set_ue_poids(ue1, p1) + db.session.commit() + assert e1.get_ue_poids_dict()[ue1_id] == p1 + ues = models.NotesUE.query.filter_by(formation_id=formation_id).all() + poids = [1.0, 2.0, 3.0] + for (ue, p) in zip(ues, poids): + e1.set_ue_poids(ue, p) + assert len(e1.ue_poids) == len(ues) + assert e1.get_ue_poids_dict()[ues[1].id] == poids[1] + e1.set_ue_poids(ue1, p1) + db.session.commit() + poids2 = [10, 20] + e1.update_ue_poids_dict({ue.id: p for (ue, p) in zip(ues[:-1], poids2)}) + assert e1.get_ue_poids_dict()[ues[0].id] == poids2[0] + assert e1.get_ue_poids_dict()[ues[1].id] == poids2[1] + assert e1.get_ue_poids_dict()[ues[2].id] == poids[2] + # Delete UE + db.session.delete(ues[2]) + db.session.commit() + # Delete eval + db.session.delete(e1) + db.session.commit() + assert len(models.EvaluationUEPoids.query.all()) == 0