diff --git a/app/but/jury_but.py b/app/but/jury_but.py index 4a12e1290..cae9e46a9 100644 --- a/app/but/jury_but.py +++ b/app/but/jury_but.py @@ -126,7 +126,7 @@ class DecisionsProposees: """Une décision de jury proposé, constituée d'une liste de codes et d'une explication. Super-classe, spécialisée pour les UE, les RCUE, les années et le diplôme. - validation : None ou une instance de d'une classe avec un champ code + validation : None ou une instance d'une classe avec un champ code ApcValidationRCUE, ApcValidationAnnee ou ScolarFormSemestreValidation """ diff --git a/app/but/jury_but_view.py b/app/but/jury_but_view.py index 664867498..c8efb357d 100644 --- a/app/but/jury_but_view.py +++ b/app/but/jury_but_view.py @@ -74,7 +74,7 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str: H.append( f""" -
Niveaux de compétences et unités d'enseignement
+
Niveaux de compétences et unités d'enseignement du BUT{deca.annee_but}
S{deca.formsemestre_impair.semestre_id @@ -93,7 +93,10 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str: ) dec_rcue = deca.decisions_rcue_by_niveau.get(niveau.id) if dec_rcue is None: - break + H.append( + """
""" + ) + continue # Semestre impair H.append( _gen_but_niveau_ue( diff --git a/app/models/modules.py b/app/models/modules.py index c73d82fd4..77c062749 100644 --- a/app/models/modules.py +++ b/app/models/modules.py @@ -220,25 +220,31 @@ class Module(db.Model): # à redéfinir les relationships... return sorted(self.ue_coefs, key=lambda x: x.ue.numero) - def ue_coefs_list(self, include_zeros=True): + def ue_coefs_list( + self, include_zeros=True, ues: list["UniteEns"] = None + ) -> list[tuple["UniteEns", float]]: """Liste des coefs vers les UE (pour les modules APC). - Si include_zeros, liste aussi les UE sans coef (donc nul) de ce semestre, + Si ues est spécifié, restreint aux UE indiquées. + Sinon si include_zeros, liste aussi les UE sans coef (donc nul) de ce semestre, sauf UE bonus sport. Result: List of tuples [ (ue, coef) ] """ if not self.is_apc(): return [] - if include_zeros: + if include_zeros and ues is None: # Toutes les UE du même semestre: - ues_semestre = ( + ues = ( self.formation.ues.filter_by(semestre_idx=self.ue.semestre_idx) .filter(UniteEns.type != UE_SPORT) .order_by(UniteEns.numero) .all() ) + if not ues: + return [] + if ues: coefs_dict = self.get_ue_coef_dict() coefs_list = [] - for ue in ues_semestre: + for ue in ues: coefs_list.append((ue, coefs_dict.get(ue.id, 0.0))) return coefs_list # Liste seulement les coefs définis: diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index ae7f29bdb..85eac7241 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -1105,7 +1105,7 @@ def formsemestre_status(formsemestre_id=None): {formsemestre_tableau_modules( - ressources, nt, formsemestre_id, can_edit=can_edit, show_ues=False + ressources, nt, formsemestre, can_edit=can_edit, show_ues=False )} @@ -1113,7 +1113,7 @@ def formsemestre_status(formsemestre_id=None): """, formsemestre_tableau_modules( - saes, nt, formsemestre_id, can_edit=can_edit, show_ues=False + saes, nt, formsemestre, can_edit=can_edit, show_ues=False ), ] if autres: @@ -1123,7 +1123,7 @@ def formsemestre_status(formsemestre_id=None): Autres modules """, formsemestre_tableau_modules( - autres, nt, formsemestre_id, can_edit=can_edit, show_ues=False + autres, nt, formsemestre, can_edit=can_edit, show_ues=False ), ] H += [_TABLEAU_MODULES_FOOT, "
"] @@ -1135,7 +1135,7 @@ def formsemestre_status(formsemestre_id=None): formsemestre_tableau_modules( modimpls, nt, - formsemestre_id, + formsemestre, can_edit=can_edit, use_ue_coefs=use_ue_coefs, ), @@ -1182,7 +1182,7 @@ _TABLEAU_MODULES_FOOT = """""" def formsemestre_tableau_modules( modimpls: list[dict], nt, - formsemestre_id: int, + formsemestre: FormSemestre, can_edit=True, show_ues=True, use_ue_coefs=False, @@ -1226,13 +1226,13 @@ def formsemestre_tableau_modules( ) expr = sco_compute_moy.get_ue_expression( - formsemestre_id, ue["ue_id"], html_quote=True + formsemestre.id, ue["ue_id"], html_quote=True ) if expr: H.append( f""" {expr} formule inutilisée en 9.2: supprimer""" ) @@ -1279,7 +1279,7 @@ def formsemestre_tableau_modules( """ ) if mod.module_type in (ModuleType.RESSOURCE, ModuleType.SAE): - coefs = mod.ue_coefs_list() + coefs = mod.ue_coefs_list(ues=formsemestre.query_ues().all()) H.append(f'') for coef in coefs: if coef[1] > 0: diff --git a/app/static/css/jury_but.css b/app/static/css/jury_but.css index b28f174c3..1829e73b9 100644 --- a/app/static/css/jury_but.css +++ b/app/static/css/jury_but.css @@ -33,6 +33,10 @@ padding: 8px !important; } +.niveau_vide { + background-color: rgb(195, 195, 195) !important; +} + .but_annee>* { display: flex; align-items: center; diff --git a/tests/unit/setup_test_yaml.py b/tests/unit/setup_test_yaml.py new file mode 100644 index 000000000..835b74a55 --- /dev/null +++ b/tests/unit/setup_test_yaml.py @@ -0,0 +1,331 @@ +""" +Met en place une base pour les tests, à partir d'une description YAML +qui peut donner la formation, son ref. compétences, les formsemestres, +les étudiants et leurs notes. +""" + +import os +from pathlib import Path +import re + +import yaml +from flask import current_app, g + +from app import db + +from app.auth.models import User +from app.but.import_refcomp import orebut_import_refcomp +from app.models import ( + ApcNiveau, + ApcParcours, + ApcReferentielCompetences, + Evaluation, + Formation, + FormSemestre, + Identite, + Module, + ModuleImpl, + ModuleUECoef, + UniteEns, +) + +from app.scodoc import sco_formations +from app.scodoc import sco_formsemestre_inscriptions +from app.scodoc import sco_groups +from app.scodoc import sco_saisie_notes +from app.scodoc import sco_utils as scu + +from tests.conftest import RESOURCES_DIR + + +def setup_but_formation(formation_infos: dict) -> Formation: + """Importe la formation, qui est lue à partir du fichier XML + formation_infos["filename"]. + La formation peut être quelconque, on vérifie juste qu'elle est bien créée. + """ + # Lecture fichier XML local: + with open( + os.path.join(RESOURCES_DIR, "formations", formation_infos["filename"]), + encoding="utf-8", + ) as f: + doc = f.read() + + # --- Création de la formation + formation_id, _, _ = sco_formations.formation_import_xml(doc) + formation: Formation = Formation.query.get(formation_id) + assert formation + return formation + + +def setup_formation_referentiel(formation: Formation, refcomp_infos: dict): + """Associe la formation au référentiel de compétences""" + if not refcomp_infos: + return + refcomp_filename = refcomp_infos["filename"] + refcomp_specialite = refcomp_infos["specialite"] + # --- Chargement Référentiel + if ( + ApcReferentielCompetences.query.filter_by( + scodoc_orig_filename=refcomp_filename, dept_id=g.scodoc_dept_id + ).first() + is None + ): + # pas déjà chargé + filename = ( + Path(current_app.config["SCODOC_DIR"]) + / "ressources/referentiels/but2022/competences" + / refcomp_filename + ) + with open(filename, encoding="utf-8") as f: + xml_data = f.read() + referentiel_competence = orebut_import_refcomp( + xml_data, dept_id=g.scodoc_dept_id, orig_filename=Path(filename).name + ) + assert referentiel_competence + + # --- Association au référentiel de compétences + referentiel_competence = ApcReferentielCompetences.query.filter_by( + specialite=refcomp_specialite + ).first() # le recherche à nouveau (test) + assert referentiel_competence + formation.referentiel_competence_id = referentiel_competence.id + db.session.add(formation) + + +def associe_ues_et_parcours(formation: Formation, formation_infos: dict): + """Associe les UE et modules de la formation aux parcours du ref. comp.""" + referentiel_competence = formation.referentiel_competence + if not referentiel_competence: + return + # --- Association des UEs aux parcours niveaux de compétences + for ue_acronyme, ue_infos in formation_infos["ues"].items(): + ue: UniteEns = formation.ues.filter_by(acronyme=ue_acronyme).first() + assert ue is not None # l'UE doit exister dans la formation avec cet acronyme + # Parcours: + if ue_infos.get("parcours", False): + parcour = referentiel_competence.parcours.filter_by( + code=ue_infos["parcours"] + ).first() + assert parcour is not None # le parcours indiqué pour cette UE doit exister + ue.set_parcour(parcour) + + # Niveaux compétences: + competence = referentiel_competence.competences.filter_by( + titre=ue_infos["competence"] + ).first() + assert competence is not None # La compétence de titre indiqué doit exister + niveau: ApcNiveau = competence.niveaux.filter_by( + annee=ue_infos["annee"] + ).first() + assert niveau is not None # le niveau de l'année indiquée doit exister + ue.set_niveau_competence(niveau) + + db.session.commit() + associe_modules_et_parcours(formation, formation_infos) + + +def associe_modules_et_parcours(formation: Formation, formation_infos: dict): + """Associe les modules à des parcours, grâce au champ modules_parcours""" + for code_parcours, codes_modules in formation_infos.get( + "modules_parcours", {} + ).items(): + parcour = formation.referentiel_competence.parcours.filter_by( + code=code_parcours + ).first() + assert parcour is not None # code parcours doit exister dans le ref. comp. + for code_module in codes_modules: + for module in [ + module + for module in formation.modules + if re.match(code_module, module.code) + ]: + module.parcours.append(parcour) + db.session.add(module) + db.session.commit() + + +def _un_semestre( + formation: Formation, + parcours: list[ApcParcours], + semestre_id: int, + titre: str, + date_debut: str, + date_fin: str, +) -> FormSemestre: + "Création d'un formsemetre" + formsemestre = FormSemestre( + formation=formation, + parcours=parcours, + dept_id=g.scodoc_dept_id, + titre=titre, + semestre_id=semestre_id, + date_debut=date_debut, + date_fin=date_fin, + ) + # set responsable (list) + a_user = User.query.first() + formsemestre.responsables = [a_user] + db.session.add(formsemestre) + # Ajoute pour chaque UE une ressource avec un coef vers cette UE + added_ressources = set() + for parcour in parcours + [None]: + for ue in formation.query_ues_parcour(parcour): + ressource = ( + Module.query.filter_by( + formation=formation, + semestre_id=1, + module_type=scu.ModuleType.RESSOURCE, + ) + .join(ModuleUECoef) + .filter_by(ue=ue) + .first() + ) + if ressource is not None: + if ressource.id not in added_ressources: + modimpl = ModuleImpl(module=ressource, responsable_id=a_user.id) + db.session.add(modimpl) + formsemestre.modimpls.append(modimpl) + added_ressources.add(ressource.id) + + # Ajoute la première SAE + sae = formation.modules.filter_by( + semestre_id=1, module_type=scu.ModuleType.SAE + ).first() + modimpl = ModuleImpl(module=sae, responsable_id=a_user.id) + formsemestre.modimpls.append(modimpl) + # Crée une évaluation dans chaque module + create_evaluations(formsemestre) + + # Partition par défaut: + db.session.commit() + partition_id = sco_groups.partition_create( + formsemestre.id, default=True, redirect=False + ) + _ = sco_groups.create_group(partition_id, default=True) + # Partition de parcours: + formsemestre.setup_parcours_groups() + + return formsemestre + + +def create_evaluations(formsemestre: FormSemestre): + """Crée une évaluation dans chaque module du semestre""" + for modimpl in formsemestre.modimpls: + evaluation = Evaluation( + moduleimpl=modimpl, + jour=formsemestre.date_debut, + description=f"Exam {modimpl.module.titre}", + coefficient=1.0, + note_max=20.0, + numero=1, + ) + db.session.add(evaluation) + # Affecte les mêmes poids que les coefs APC: + ue_coef_dict = modimpl.module.get_ue_coef_dict() # { ue_id : coef } + evaluation.set_ue_poids_dict(ue_coef_dict) + + +def note_les_modules(doc: dict): + """Saisie les notes des étudiants""" + a_user = User.query.first() + for nom, infos in doc["Etudiants"].items(): + etud: Identite = Identite.query.filter_by(nom=nom).first() + assert etud is not None + for titre, sem_infos in infos["formsemestres"].items(): + formsemestre: FormSemestre = FormSemestre.query.filter_by( + titre=titre + ).first() + assert formsemestre is not None + for code_module, note in sem_infos.get("notes_modules", {}).items(): + modimpl = ( + formsemestre.modimpls.join(Module) + .filter_by(code=code_module) + .first() + ) + # le sem. doit avoir un module du code indiqué: + assert modimpl is not None + for evaluation in modimpl.evaluations: + # s'il y a plusieurs evals, affecte la même note à chacune + sco_saisie_notes.notes_add( + a_user, + evaluation.id, + [(etud.id, float(note))], + comment="note_les_modules", + ) + + +def setup_formsemestres(formation: Formation, doc: str): + """Création des formsemestres pour tester les parcours BUT""" + for titre, infos in doc["FormSemestres"].items(): + parcours = [] + for code_parcour in infos["codes_parcours"]: + parcour = formation.referentiel_competence.parcours.filter_by( + code=code_parcour + ).first() + assert parcour is not None + parcours.append(parcour) + _ = _un_semestre( + formation, + parcours, + infos["idx"], + titre, + infos["date_debut"], + infos["date_fin"], + ) + + db.session.flush() + assert FormSemestre.query.count() == len(doc["FormSemestres"]) + + +def inscrit_les_etudiants(formation: Formation, doc: dict): + """Inscrit les étudiants dans chacun de leurs formsemestres""" + for nom, infos in doc["Etudiants"].items(): + etud = Identite.create_etud( + dept_id=g.scodoc_dept_id, + nom=nom, + prenom=infos["prenom"], + civilite=infos["civilite"], + ) + db.session.add(etud) + db.session.commit() + # L'inscrire à ses formsemestres + for titre, sem_infos in infos["formsemestres"].items(): + formsemestre: FormSemestre = FormSemestre.query.filter_by( + titre=titre + ).first() + assert formsemestre is not None + partition_parcours = formsemestre.partitions.filter_by( + partition_name=scu.PARTITION_PARCOURS + ).first() + if partition_parcours is None: + group_ids = [] + else: + group = partition_parcours.groups.filter_by( + group_name=sem_infos["parcours"] + ).first() + assert group is not None # le groupe de parcours doit exister + group_ids = [group.id] + sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules( + formsemestre.id, + etud.id, + group_ids=group_ids, + etat=scu.INSCRIT, + method="tests/unit/inscrit_les_etudiants", + ) + # Met à jour les inscriptions: + for formsemestre in formation.formsemestres: + formsemestre.update_inscriptions_parcours_from_groups() + + +def setup_from_yaml(filename: str) -> dict: + """Lit le fichier yaml et construit l'ensemble des objets""" + with open(filename, encoding="utf-8") as f: + doc = yaml.safe_load(f.read()) + + formation = setup_but_formation(doc["Formation"]) + setup_formation_referentiel(formation, doc.get("ReferentielCompetences", {})) + associe_ues_et_parcours(formation, doc["Formation"]) + setup_formsemestres(formation, doc) + inscrit_les_etudiants(formation, doc) + note_les_modules(doc) + return doc diff --git a/tests/unit/test_but_jury.py b/tests/unit/test_but_jury.py index 03757db7d..8b46050e0 100644 --- a/tests/unit/test_but_jury.py +++ b/tests/unit/test_but_jury.py @@ -1,320 +1,54 @@ """ Test jury BUT avec parcours """ -import os -from pathlib import Path -import re - -from flask import current_app, g import pytest import yaml - +from tests.unit import setup_test_yaml as sty import app -from app import db - -from app.auth.models import User -from app.but.import_refcomp import orebut_import_refcomp +from app.but.jury_but import DecisionsProposeesAnnee from app.models import ( - ApcNiveau, - ApcParcours, - ApcReferentielCompetences, - Evaluation, Formation, FormSemestre, Identite, - Module, - ModuleImpl, - ModuleUECoef, UniteEns, ) - -from app.scodoc import sco_formations -from app.scodoc import sco_formsemestre_inscriptions -from app.scodoc import sco_groups -from app.scodoc import sco_saisie_notes from app.scodoc import sco_utils as scu from config import TestConfig -from tests.conftest import RESOURCES_DIR - DEPT = TestConfig.DEPT_TEST -def setup_but_formation(doc: dict) -> Formation: - """Importe la formation BUT, l'associe au référentiel de compétences. - Après cette fonction, on a une formation chargée, et associée à son ref. comp. +def test_but_jury_S1(test_client): + """Test jurys BUT1 avec un seul parcours. + Vérifie aussi les champs de DecisionsProposeesAnnee """ app.set_sco_dept(DEPT) - refcomp_infos = doc["ReferentielCompetences"] - formation_infos = doc["Formation"] - refcomp_filename = refcomp_infos["filename"] - refcomp_specialite = refcomp_infos["specialite"] - # Lecture fichier XML local: - with open( - os.path.join(RESOURCES_DIR, "formations", formation_infos["filename"]), - encoding="utf-8", - ) as f: - doc = f.read() - - # --- Création de la formation - formation_id, _, _ = sco_formations.formation_import_xml(doc) - formation: Formation = Formation.query.get(formation_id) - assert formation - # --- Chargement Référentiel - if ( - ApcReferentielCompetences.query.filter_by( - scodoc_orig_filename=refcomp_filename, dept_id=g.scodoc_dept_id - ).first() - is None - ): - # pas déjà chargé - filename = ( - Path(current_app.config["SCODOC_DIR"]) - / "ressources/referentiels/but2022/competences" - / refcomp_filename - ) - with open(filename, encoding="utf-8") as f: - xml_data = f.read() - referentiel_competence = orebut_import_refcomp( - xml_data, dept_id=g.scodoc_dept_id, orig_filename=Path(filename).name - ) - assert referentiel_competence - - # --- Association au référentiel de compétences - referentiel_competence = ApcReferentielCompetences.query.filter_by( - specialite=refcomp_specialite - ).first() # le recherche à nouveau (test) - assert referentiel_competence - formation.referentiel_competence_id = referentiel_competence.id - db.session.add(formation) - # --- Association des UEs aux parcours niveaux de compétences - for ue_acronyme, ue_infos in formation_infos["ues"].items(): - ue: UniteEns = formation.ues.filter_by(acronyme=ue_acronyme).first() - assert ue is not None # l'UE doit exister dans la formation avec cet acronyme - # Parcours: - if ue_infos.get("parcours", False): - parcour = referentiel_competence.parcours.filter_by( - code=ue_infos["parcours"] - ).first() - assert parcour is not None # le parcours indiqué pour cette UE doit exister - ue.set_parcour(parcour) - - # Niveaux compétences: - competence = referentiel_competence.competences.filter_by( - titre=ue_infos["competence"] - ).first() - assert competence is not None # La compétence de titre indiqué doit exister - niveau: ApcNiveau = competence.niveaux.filter_by( - annee=ue_infos["annee"] - ).first() - assert niveau is not None # le niveau de l'année indiquée doit exister - ue.set_niveau_competence(niveau) - - db.session.commit() - associe_modules_et_parcours(formation, formation_infos) - return formation + doc = sty.setup_from_yaml("tests/unit/cursus_but_gb.yaml") + formsemestre: FormSemestre = FormSemestre.query.filter_by(titre="S1_SEE").first() + etud: Identite = formsemestre.etuds.first() + deca = DecisionsProposeesAnnee(etud, formsemestre) + assert deca.validation is None # pas encore de validation enregistrée + assert False is deca.recorded + assert deca.code_valide is None + assert deca.formsemestre_impair == formsemestre + assert deca.formsemestre_pair is None # jury de S1, pas de S2 + assert deca.rcues_annee == [] # S1, pas de RCUEs + assert deca.inscription_etat == scu.INSCRIT + assert deca.inscription_etat_impair is None + assert deca.parcour == formsemestre.parcours[0] # un seul parcours dans ce sem. + assert formsemestre.query_ues().all() == deca.ues_impair + nb_ues = formsemestre.query_ues().count() + assert len(deca.decisions_ues) == nb_ues + assert len(deca.niveaux_competences) == nb_ues + assert deca.nb_competences == nb_ues + # XXX à compléter avec le jury quand on aura décidé des notes -def associe_modules_et_parcours(formation: Formation, formation_infos: dict): - """Associe les modules à des parcours, grâce au champ modules_parcours""" - for code_parcours, codes_modules in formation_infos.get( - "modules_parcours", {} - ).items(): - parcour = formation.referentiel_competence.parcours.filter_by( - code=code_parcours - ).first() - assert parcour is not None # code parcours doit exister dans le ref. comp. - for code_module in codes_modules: - for module in [ - module - for module in formation.modules - if re.match(code_module, module.code) - ]: - module.parcours.append(parcour) - db.session.add(module) - db.session.commit() - - -def _un_semestre( - formation: Formation, - parcours: list[ApcParcours], - semestre_id: int, - titre: str, - date_debut: str, - date_fin: str, -) -> FormSemestre: - "Création d'un formsemetre" - formsemestre = FormSemestre( - formation=formation, - parcours=parcours, - dept_id=g.scodoc_dept_id, - titre=titre, - semestre_id=semestre_id, - date_debut=date_debut, - date_fin=date_fin, - ) - # set responsable (list) - a_user = User.query.first() - formsemestre.responsables = [a_user] - db.session.add(formsemestre) - # Ajoute pour chaque UE une ressource avec un coef vers cette UE - added_ressources = set() - for parcour in parcours + [None]: - for ue in formation.query_ues_parcour(parcour): - ressource = ( - Module.query.filter_by( - formation=formation, - semestre_id=1, - module_type=scu.ModuleType.RESSOURCE, - ) - .join(ModuleUECoef) - .filter_by(ue=ue) - .first() - ) - if ressource is not None: - if ressource.id not in added_ressources: - modimpl = ModuleImpl(module=ressource, responsable_id=a_user.id) - db.session.add(modimpl) - formsemestre.modimpls.append(modimpl) - added_ressources.add(ressource.id) - - # Ajoute la première SAE - sae = formation.modules.filter_by( - semestre_id=1, module_type=scu.ModuleType.SAE - ).first() - modimpl = ModuleImpl(module=sae, responsable_id=a_user.id) - formsemestre.modimpls.append(modimpl) - # Crée une évaluation dans chaque module - create_evaluations(formsemestre) - - # Partition par défaut: - db.session.commit() - partition_id = sco_groups.partition_create( - formsemestre.id, default=True, redirect=False - ) - _ = sco_groups.create_group(partition_id, default=True) - # Partition de parcours: - formsemestre.setup_parcours_groups() - - return formsemestre - - -def create_evaluations(formsemestre: FormSemestre): - """Crée une évaluation dans chaque module du semestre""" - for modimpl in formsemestre.modimpls: - evaluation = Evaluation( - moduleimpl=modimpl, - jour=formsemestre.date_debut, - description=f"Exam {modimpl.module.titre}", - coefficient=1.0, - note_max=20.0, - numero=1, - ) - db.session.add(evaluation) - # Affecte les mêmes poids que les coefs APC: - ue_coef_dict = modimpl.module.get_ue_coef_dict() # { ue_id : coef } - evaluation.set_ue_poids_dict(ue_coef_dict) - - -def note_les_modules(doc: dict): - """Saisie les notes des étudiants""" - a_user = User.query.first() - for nom, infos in doc["Etudiants"].items(): - etud: Identite = Identite.query.filter_by(nom=nom).first() - assert etud is not None - for titre, sem_infos in infos["formsemestres"].items(): - formsemestre: FormSemestre = FormSemestre.query.filter_by( - titre=titre - ).first() - assert formsemestre is not None - for code_module, note in sem_infos.get("notes_modules", {}).items(): - modimpl = ( - formsemestre.modimpls.join(Module) - .filter_by(code=code_module) - .first() - ) - # le sem. doit avoir un module du code indiqué: - assert modimpl is not None - for evaluation in modimpl.evaluations: - # s'il y a plusieurs evals, affecte la même note à chacune - sco_saisie_notes.notes_add( - a_user, - evaluation.id, - [(etud.id, float(note))], - comment="note_les_modules", - ) - - -def setup_but_formsemestres(formation: Formation, doc: str): - """Création des formsemestres pour tester les parcours BUT""" - for titre, infos in doc["FormSemestres"].items(): - parcours = [] - for code_parcour in infos["codes_parcours"]: - parcour = formation.referentiel_competence.parcours.filter_by( - code=code_parcour - ).first() - assert parcour is not None - parcours.append(parcour) - _ = _un_semestre( - formation, - parcours, - infos["idx"], - titre, - infos["date_debut"], - infos["date_fin"], - ) - - db.session.flush() - assert FormSemestre.query.count() == len(doc["FormSemestres"]) - - -def inscrit_les_etudiants(formation: Formation, doc: dict): - """Inscrit les étudiants dans chacun de leurs formsemestres""" - for nom, infos in doc["Etudiants"].items(): - etud = Identite.create_etud( - dept_id=g.scodoc_dept_id, - nom=nom, - prenom=infos["prenom"], - civilite=infos["civilite"], - ) - db.session.add(etud) - db.session.commit() - # L'inscrire à ses formsemestres - for titre, sem_infos in infos["formsemestres"].items(): - formsemestre: FormSemestre = FormSemestre.query.filter_by( - titre=titre - ).first() - assert formsemestre is not None - partition_parcours = formsemestre.partitions.filter_by( - partition_name=scu.PARTITION_PARCOURS - ).first() - if partition_parcours is None: - group_ids = [] - else: - group = partition_parcours.groups.filter_by( - group_name=sem_infos["parcours"] - ).first() - assert group is not None # le groupe de parcours doit exister - group_ids = [group.id] - sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules( - formsemestre.id, - etud.id, - group_ids=group_ids, - etat=scu.INSCRIT, - method="tests/unit/inscrit_les_etudiants", - ) - # Met à jour les inscriptions: - for formsemestre in formation.formsemestres: - formsemestre.update_inscriptions_parcours_from_groups() - - -def test_but_jury(test_client): - "Test jurys BUT1, BUT2 avec parcours" - with open("tests/unit/cursus_but_gb.yaml", encoding="utf-8") as f: - doc = yaml.safe_load(f.read()) - - formation = setup_but_formation(doc) - setup_but_formsemestres(formation, doc) - - inscrit_les_etudiants(formation, doc) - - note_les_modules(doc) +def test_but_jury_S3(test_client): + """Test jurys BUT2/S3 avec 2 parcours dans le sem.""" + app.set_sco_dept(DEPT) + doc = sty.setup_from_yaml("tests/unit/cursus_but_gb.yaml") + formsemestre: FormSemestre = FormSemestre.query.filter_by(titre="S3").first() + etud: Identite = formsemestre.etuds.filter_by(nom="See1").first() # du parcours SEE + assert etud + deca = DecisionsProposeesAnnee(etud, formsemestre) + assert len(deca.niveaux_competences) == 5 # 5 compétences dans ce parcours