forked from ScoDoc/ScoDoc
BUT: corrige affichage coefs UE tableau sem., et niveaux sur fiche etud. + unit tests
This commit is contained in:
parent
00ff4aaa6d
commit
8390ec393b
@ -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
|
||||
"""
|
||||
|
||||
|
@ -74,7 +74,7 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
|
||||
|
||||
H.append(
|
||||
f"""
|
||||
<div class="titre_niveaux"><b>Niveaux de compétences et unités d'enseignement</b></div>
|
||||
<div class="titre_niveaux"><b>Niveaux de compétences et unités d'enseignement du BUT{deca.annee_but}</b></div>
|
||||
<div class="but_annee">
|
||||
<div class="titre"></div>
|
||||
<div class="titre">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(
|
||||
"""<div class="niveau_vide"></div><div class="niveau_vide"></div><div class="niveau_vide"></div>"""
|
||||
)
|
||||
continue
|
||||
# Semestre impair
|
||||
H.append(
|
||||
_gen_but_niveau_ue(
|
||||
|
@ -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:
|
||||
|
@ -1105,7 +1105,7 @@ def formsemestre_status(formsemestre_id=None):
|
||||
</td>
|
||||
</tr>
|
||||
{formsemestre_tableau_modules(
|
||||
ressources, nt, formsemestre_id, can_edit=can_edit, show_ues=False
|
||||
ressources, nt, formsemestre, can_edit=can_edit, show_ues=False
|
||||
)}
|
||||
<tr class="formsemestre_status_cat">
|
||||
<td colspan="5">
|
||||
@ -1113,7 +1113,7 @@ def formsemestre_status(formsemestre_id=None):
|
||||
</td>
|
||||
</tr>""",
|
||||
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):
|
||||
<span class="status_module_cat">Autres modules</span>
|
||||
</td></tr>""",
|
||||
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, "</div>"]
|
||||
@ -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 = """</table>"""
|
||||
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""" <span class="formula" title="mode de calcul de la moyenne d'UE">{expr}</span>
|
||||
<span class="warning">formule inutilisée en 9.2: <a href="{
|
||||
url_for("notes.delete_ue_expr", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, ue_id=ue["ue_id"] )
|
||||
url_for("notes.delete_ue_expr", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id, ue_id=ue["ue_id"] )
|
||||
}
|
||||
">supprimer</a></span>"""
|
||||
)
|
||||
@ -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'<a class="invisible_link" href="#" title="{mod_descr}">')
|
||||
for coef in coefs:
|
||||
if coef[1] > 0:
|
||||
|
@ -33,6 +33,10 @@
|
||||
padding: 8px !important;
|
||||
}
|
||||
|
||||
.niveau_vide {
|
||||
background-color: rgb(195, 195, 195) !important;
|
||||
}
|
||||
|
||||
.but_annee>* {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
331
tests/unit/setup_test_yaml.py
Normal file
331
tests/unit/setup_test_yaml.py
Normal file
@ -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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user