BUT: corrige affichage coefs UE tableau sem., et niveaux sur fiche etud. + unit tests

This commit is contained in:
Emmanuel Viennet 2022-12-09 04:24:32 +01:00 committed by iziram
parent f63fa43862
commit bec4cd7978
7 changed files with 393 additions and 315 deletions

View File

@ -126,7 +126,7 @@ class DecisionsProposees:
"""Une décision de jury proposé, constituée d'une liste de codes et d'une explication. """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. 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 ApcValidationRCUE, ApcValidationAnnee ou ScolarFormSemestreValidation
""" """

View File

@ -74,7 +74,7 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
H.append( H.append(
f""" 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="but_annee">
<div class="titre"></div> <div class="titre"></div>
<div class="titre">S{deca.formsemestre_impair.semestre_id <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) dec_rcue = deca.decisions_rcue_by_niveau.get(niveau.id)
if dec_rcue is None: 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 # Semestre impair
H.append( H.append(
_gen_but_niveau_ue( _gen_but_niveau_ue(

View File

@ -220,25 +220,31 @@ class Module(db.Model):
# à redéfinir les relationships... # à redéfinir les relationships...
return sorted(self.ue_coefs, key=lambda x: x.ue.numero) 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). """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. sauf UE bonus sport.
Result: List of tuples [ (ue, coef) ] Result: List of tuples [ (ue, coef) ]
""" """
if not self.is_apc(): if not self.is_apc():
return [] return []
if include_zeros: if include_zeros and ues is None:
# Toutes les UE du même semestre: # Toutes les UE du même semestre:
ues_semestre = ( ues = (
self.formation.ues.filter_by(semestre_idx=self.ue.semestre_idx) self.formation.ues.filter_by(semestre_idx=self.ue.semestre_idx)
.filter(UniteEns.type != UE_SPORT) .filter(UniteEns.type != UE_SPORT)
.order_by(UniteEns.numero) .order_by(UniteEns.numero)
.all() .all()
) )
if not ues:
return []
if ues:
coefs_dict = self.get_ue_coef_dict() coefs_dict = self.get_ue_coef_dict()
coefs_list = [] coefs_list = []
for ue in ues_semestre: for ue in ues:
coefs_list.append((ue, coefs_dict.get(ue.id, 0.0))) coefs_list.append((ue, coefs_dict.get(ue.id, 0.0)))
return coefs_list return coefs_list
# Liste seulement les coefs définis: # Liste seulement les coefs définis:

View File

@ -1105,7 +1105,7 @@ def formsemestre_status(formsemestre_id=None):
</td> </td>
</tr> </tr>
{formsemestre_tableau_modules( {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"> <tr class="formsemestre_status_cat">
<td colspan="5"> <td colspan="5">
@ -1113,7 +1113,7 @@ def formsemestre_status(formsemestre_id=None):
</td> </td>
</tr>""", </tr>""",
formsemestre_tableau_modules( 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: if autres:
@ -1123,7 +1123,7 @@ def formsemestre_status(formsemestre_id=None):
<span class="status_module_cat">Autres modules</span> <span class="status_module_cat">Autres modules</span>
</td></tr>""", </td></tr>""",
formsemestre_tableau_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, "</div>"] H += [_TABLEAU_MODULES_FOOT, "</div>"]
@ -1135,7 +1135,7 @@ def formsemestre_status(formsemestre_id=None):
formsemestre_tableau_modules( formsemestre_tableau_modules(
modimpls, modimpls,
nt, nt,
formsemestre_id, formsemestre,
can_edit=can_edit, can_edit=can_edit,
use_ue_coefs=use_ue_coefs, use_ue_coefs=use_ue_coefs,
), ),
@ -1182,7 +1182,7 @@ _TABLEAU_MODULES_FOOT = """</table>"""
def formsemestre_tableau_modules( def formsemestre_tableau_modules(
modimpls: list[dict], modimpls: list[dict],
nt, nt,
formsemestre_id: int, formsemestre: FormSemestre,
can_edit=True, can_edit=True,
show_ues=True, show_ues=True,
use_ue_coefs=False, use_ue_coefs=False,
@ -1226,13 +1226,13 @@ def formsemestre_tableau_modules(
) )
expr = sco_compute_moy.get_ue_expression( 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: if expr:
H.append( H.append(
f""" <span class="formula" title="mode de calcul de la moyenne d'UE">{expr}</span> 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="{ <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>""" ">supprimer</a></span>"""
) )
@ -1279,7 +1279,7 @@ def formsemestre_tableau_modules(
""" """
) )
if mod.module_type in (ModuleType.RESSOURCE, ModuleType.SAE): 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}">') H.append(f'<a class="invisible_link" href="#" title="{mod_descr}">')
for coef in coefs: for coef in coefs:
if coef[1] > 0: if coef[1] > 0:

View File

@ -33,6 +33,10 @@
padding: 8px !important; padding: 8px !important;
} }
.niveau_vide {
background-color: rgb(195, 195, 195) !important;
}
.but_annee>* { .but_annee>* {
display: flex; display: flex;
align-items: center; align-items: center;

View 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

View File

@ -1,320 +1,54 @@
""" Test jury BUT avec parcours """ Test jury BUT avec parcours
""" """
import os
from pathlib import Path
import re
from flask import current_app, g
import pytest import pytest
import yaml import yaml
from tests.unit import setup_test_yaml as sty
import app import app
from app import db from app.but.jury_but import DecisionsProposeesAnnee
from app.auth.models import User
from app.but.import_refcomp import orebut_import_refcomp
from app.models import ( from app.models import (
ApcNiveau,
ApcParcours,
ApcReferentielCompetences,
Evaluation,
Formation, Formation,
FormSemestre, FormSemestre,
Identite, Identite,
Module,
ModuleImpl,
ModuleUECoef,
UniteEns, 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 app.scodoc import sco_utils as scu
from config import TestConfig from config import TestConfig
from tests.conftest import RESOURCES_DIR
DEPT = TestConfig.DEPT_TEST DEPT = TestConfig.DEPT_TEST
def setup_but_formation(doc: dict) -> Formation: def test_but_jury_S1(test_client):
"""Importe la formation BUT, l'associe au référentiel de compétences. """Test jurys BUT1 avec un seul parcours.
Après cette fonction, on a une formation chargée, et associée à son ref. comp. Vérifie aussi les champs de DecisionsProposeesAnnee
""" """
app.set_sco_dept(DEPT) app.set_sco_dept(DEPT)
refcomp_infos = doc["ReferentielCompetences"] doc = sty.setup_from_yaml("tests/unit/cursus_but_gb.yaml")
formation_infos = doc["Formation"] formsemestre: FormSemestre = FormSemestre.query.filter_by(titre="S1_SEE").first()
refcomp_filename = refcomp_infos["filename"] etud: Identite = formsemestre.etuds.first()
refcomp_specialite = refcomp_infos["specialite"] deca = DecisionsProposeesAnnee(etud, formsemestre)
# Lecture fichier XML local: assert deca.validation is None # pas encore de validation enregistrée
with open( assert False is deca.recorded
os.path.join(RESOURCES_DIR, "formations", formation_infos["filename"]), assert deca.code_valide is None
encoding="utf-8", assert deca.formsemestre_impair == formsemestre
) as f: assert deca.formsemestre_pair is None # jury de S1, pas de S2
doc = f.read() assert deca.rcues_annee == [] # S1, pas de RCUEs
assert deca.inscription_etat == scu.INSCRIT
# --- Création de la formation assert deca.inscription_etat_impair is None
formation_id, _, _ = sco_formations.formation_import_xml(doc) assert deca.parcour == formsemestre.parcours[0] # un seul parcours dans ce sem.
formation: Formation = Formation.query.get(formation_id) assert formsemestre.query_ues().all() == deca.ues_impair
assert formation nb_ues = formsemestre.query_ues().count()
# --- Chargement Référentiel assert len(deca.decisions_ues) == nb_ues
if ( assert len(deca.niveaux_competences) == nb_ues
ApcReferentielCompetences.query.filter_by( assert deca.nb_competences == nb_ues
scodoc_orig_filename=refcomp_filename, dept_id=g.scodoc_dept_id # XXX à compléter avec le jury quand on aura décidé des notes
).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
def associe_modules_et_parcours(formation: Formation, formation_infos: dict): def test_but_jury_S3(test_client):
"""Associe les modules à des parcours, grâce au champ modules_parcours""" """Test jurys BUT2/S3 avec 2 parcours dans le sem."""
for code_parcours, codes_modules in formation_infos.get( app.set_sco_dept(DEPT)
"modules_parcours", {} doc = sty.setup_from_yaml("tests/unit/cursus_but_gb.yaml")
).items(): formsemestre: FormSemestre = FormSemestre.query.filter_by(titre="S3").first()
parcour = formation.referentiel_competence.parcours.filter_by( etud: Identite = formsemestre.etuds.filter_by(nom="See1").first() # du parcours SEE
code=code_parcours assert etud
).first() deca = DecisionsProposeesAnnee(etud, formsemestre)
assert parcour is not None # code parcours doit exister dans le ref. comp. assert len(deca.niveaux_competences) == 5 # 5 compétences dans ce parcours
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)