Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
5 changed files with 122 additions and 122 deletions
Showing only changes of commit ee95a6178a - Show all commits

View File

@ -776,8 +776,10 @@ class DecisionsProposeesAnnee(DecisionsProposees):
# UEs
for dec_ue in self.decisions_ues.values():
if (
not dec_ue.recorded
) and dec_ue.formsemestre.annee_scolaire() == annee_scolaire:
dec_ue.formsemestre
and (not dec_ue.recorded)
and dec_ue.formsemestre.annee_scolaire() == annee_scolaire
):
# rappel: le code par défaut est en tête
code = dec_ue.codes[0] if dec_ue.codes else None
if (not only_validantes) or code in sco_codes.CODES_UE_VALIDES_DE_DROIT:
@ -1457,7 +1459,12 @@ class DecisionsProposeesUE(DecisionsProposees):
) or self.formsemestre.modalite == "EXT":
self.codes.insert(0, sco_codes.ADM)
self.explanation = f"Moyenne >= {sco_codes.CursusBUT.BARRE_MOY}/20"
elif self.rcue and self.rcue.est_compensable():
elif (
self.rcue
and self.rcue.est_compensable()
and self.ue_status
and not self.ue_status["is_capitalized"]
):
self.codes.insert(0, sco_codes.CMP)
self.explanation = "compensable dans le RCUE"
else:

View File

@ -1090,7 +1090,7 @@ Etudiants:
"S1.2": 12.0000
attendu: # les codes jury que l'on doit vérifier
deca:
passage_de_droit: False
passage_de_droit: True
nb_competences: 2
nb_rcue_annee: 0
decisions_ues:

View File

@ -30,6 +30,26 @@ from config import TestConfig
DEPT = TestConfig.DEPT_TEST
def setup_and_test_jurys(yaml_filename: str):
"Charge YAML et lance test jury BUT"
app.set_sco_dept(DEPT)
# Construit la base de test GB une seule fois
# puis lance les tests de jury
doc, formation, formsemestre_titres = yaml_setup.setup_from_yaml(yaml_filename)
for formsemestre_titre in formsemestre_titres:
formsemestre = yaml_setup.create_formsemestre_with_etuds(
doc, formation, formsemestre_titre
)
# Vérifie les champs de DecisionsProposeesAnnee de ce semestre
yaml_setup_but.check_deca_fields(formsemestre)
# Saisie de toutes les décisions de jury "automatiques"
# et vérification des résultats attendus:
formsemestre_validation_auto_but(formsemestre, only_adm=False)
yaml_setup_but.but_test_jury(formsemestre, doc)
@pytest.mark.slow
@pytest.mark.but_gb
def test_but_jury_GB(test_client):
@ -40,76 +60,21 @@ def test_but_jury_GB(test_client):
- vérification jury de S3
- vérification jury de S1 avec redoublants et capitalisations
"""
app.set_sco_dept(DEPT)
# Construit la base de test GB une seule fois
# puis lance les tests de jury
doc = yaml_setup.setup_from_yaml("tests/ressources/yaml/cursus_but_gb.yaml")
# Vérifie les deca de tous les semestres:
for formsemestre in FormSemestre.query:
yaml_setup_but.check_deca_fields(formsemestre)
# Saisie de toutes les décisions de jury
for formsemestre in FormSemestre.query.order_by(FormSemestre.semestre_id):
formsemestre_validation_auto_but(formsemestre, only_adm=False)
# Vérifie résultats attendus:
S1: FormSemestre = FormSemestre.query.filter_by(titre="S1_SEE").first()
yaml_setup_but.but_test_jury(S1, doc)
S2: FormSemestre = FormSemestre.query.filter_by(titre="S2_SEE").first()
yaml_setup_but.but_test_jury(S2, doc)
S3: FormSemestre = FormSemestre.query.filter_by(titre="S3").first()
yaml_setup_but.but_test_jury(S3, doc)
# _test_but_jury(S1_redoublant, doc)
setup_and_test_jurys("tests/ressources/yaml/cursus_but_gb.yaml")
@pytest.mark.slow
@pytest.mark.lemans
def test_but_jury_GMP_lm(test_client):
"""Tests sur un cursus GMP fourni par Le Mans"""
app.set_sco_dept(DEPT)
# Construit la base de test GB une seule fois
# puis lance les tests de jury
doc = yaml_setup.setup_from_yaml("tests/ressources/yaml/cursus_but_gmp_iutlm.yaml")
formsemestres = FormSemestre.query.order_by(
FormSemestre.date_debut, FormSemestre.semestre_id
).all()
# Vérifie les deca de tous les semestres:
for formsemestre in formsemestres:
yaml_setup_but.check_deca_fields(formsemestre)
# Saisie de toutes les décisions de jury qui ne le seraient pas déjà
for formsemestre in formsemestres:
formsemestre_validation_auto_but(formsemestre, only_adm=False)
# Vérifie résultats attendus:
for formsemestre in formsemestres:
yaml_setup_but.but_test_jury(formsemestre, doc)
setup_and_test_jurys("tests/ressources/yaml/cursus_but_gmp_iutlm.yaml")
@pytest.mark.slow
@pytest.mark.lyon
def test_but_jury_GEII_lyon(test_client):
"""Tests sur un cursus GEII fourni par Lyon"""
app.set_sco_dept(DEPT)
# Construit la base de test GB une seule fois
# puis lance les tests de jury
doc = yaml_setup.setup_from_yaml("tests/ressources/yaml/cursus_but_geii_lyon.yaml")
formsemestres = FormSemestre.query.order_by(
FormSemestre.date_debut, FormSemestre.semestre_id
).all()
# Vérifie les champs de DecisionsProposeesAnnee de tous les semestres:
for formsemestre in formsemestres:
yaml_setup_but.check_deca_fields(formsemestre)
# Saisie de toutes les décisions de jury "automatiques"
# et vérification des résultats attendus:
for formsemestre in formsemestres:
formsemestre_validation_auto_but(formsemestre, only_adm=False)
yaml_setup_but.but_test_jury(formsemestre, doc)
setup_and_test_jurys("tests/ressources/yaml/cursus_but_geii_lyon.yaml")
@pytest.mark.slow
@ -118,7 +83,14 @@ def test_but_jury_GCCD_CY(test_client):
"""Tests sur un cursus BUT GCCD de S1 à S6"""
# WIP
app.set_sco_dept(DEPT)
doc = yaml_setup.setup_from_yaml("tests/ressources/yaml/cursus_but_gccd_cy.yaml")
doc, formation, formsemestre_titres = yaml_setup.setup_from_yaml(
"tests/ressources/yaml/cursus_but_gccd_cy.yaml"
)
for formsemestre_titre in formsemestre_titres:
_ = yaml_setup.create_formsemestre_with_etuds(
doc, formation, formsemestre_titre
)
formsemestres = FormSemestre.query.order_by(
FormSemestre.date_debut, FormSemestre.semestre_id
).all()

View File

@ -17,7 +17,7 @@ setup_from_yaml()
- import de la formation (le test utilise une seule formation)
- associe_ues_et_parcours():
- crée les associations formation <-> référentiel de compétence
- setup_formsemestres()
- setup_formsemestre()
- crée les formsemestres décrits dans le YAML
avec tous les modules du semestre ou des parcours si indiqués
et une évaluation dans chaque moduleimpl.
@ -166,19 +166,20 @@ def create_evaluations(formsemestre: FormSemestre, publish_incomplete=True):
evaluation.set_ue_poids_dict(ue_coef_dict)
def note_les_modules(doc: dict):
"""Saisie les notes des étudiants
def note_les_modules(doc: dict, formsemestre_titre: str = ""):
"""Saisie les notes des étudiants pour ce formsemestre
doc : données YAML
"""
a_user = User.query.first()
formsemestre: FormSemestre = FormSemestre.query.filter_by(
titre=formsemestre_titre
).first()
assert formsemestre is not None
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
sem_infos = infos["formsemestres"].get(formsemestre_titre)
if sem_infos:
for code_module, note in sem_infos.get("notes_modules", {}).items():
modimpl = (
formsemestre.modimpls.join(Module)
@ -209,11 +210,14 @@ def note_les_modules(doc: dict):
)
def setup_formsemestres(formation: Formation, doc: str):
"""Création des formsemestres
Le cas échéant associés à leur(s) parcours.
def setup_formsemestre(
formation: Formation, doc: str, formsemestre_titre: str = ""
) -> FormSemestre:
"""Création du formsemestre de titre indiqué.
Le cas échéant associé à son parcours.
"""
for titre, infos in doc["FormSemestres"].items():
infos = doc["FormSemestres"][formsemestre_titre]
codes_parcours = infos.get("codes_parcours", [])
assert formation.is_apc() or not codes_parcours # parcours seulement en APC
parcours = []
@ -224,42 +228,51 @@ def setup_formsemestres(formation: Formation, doc: str):
assert parcour is not None
parcours.append(parcour)
_ = create_formsemestre(
formsemestre = create_formsemestre(
formation,
parcours,
infos["idx"],
titre,
formsemestre_titre,
infos["date_debut"],
infos["date_fin"],
)
db.session.flush()
assert FormSemestre.query.count() == len(doc["FormSemestres"])
return formsemestre
def inscrit_les_etudiants(formation: Formation, doc: dict):
def inscrit_les_etudiants(doc: dict, formsemestre_titre: str = ""):
"""Inscrit les étudiants dans chacun de leurs formsemestres"""
etudiants = doc.get("Etudiants")
if not etudiants:
return
for nom, infos in etudiants.items():
etud = Identite.create_etud(
dept_id=g.scodoc_dept_id,
nom=nom,
prenom=infos.get("prenom", "prénom"),
civilite=infos.get("civilite", "X"),
)
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
titre=formsemestre_titre
).first()
assert formsemestre is not None
partition_parcours = formsemestre.partitions.filter_by(
partition_name=scu.PARTITION_PARCOURS
).first()
for nom, infos in etudiants.items():
# Création des étudiants (sauf si déjà existants)
prenom = infos.get("prenom", "prénom")
civilite = infos.get("civilite", "X")
etud = Identite.query.filter_by(
nom=nom, prenom=prenom, civilite=civilite
).first()
if etud is None:
etud = Identite.create_etud(
dept_id=g.scodoc_dept_id,
nom=nom,
prenom=prenom,
civilite=civilite,
)
db.session.add(etud)
db.session.commit()
# L'inscrire au formsemestre
sem_infos = infos["formsemestres"].get(formsemestre_titre)
if sem_infos:
if partition_parcours is not None and "parcours" in sem_infos:
group = partition_parcours.groups.filter_by(
group_name=sem_infos["parcours"]
@ -279,7 +292,6 @@ def inscrit_les_etudiants(formation: Formation, doc: dict):
method="tests/unit/inscrit_les_etudiants",
)
# Met à jour les inscriptions:
for formsemestre in formation.formsemestres:
formsemestre.update_inscriptions_parcours_from_groups()
@ -296,7 +308,7 @@ def etud_dispense_ues(
db.session.add(disp)
def setup_from_yaml(filename: str) -> dict:
def setup_from_yaml(filename: str) -> tuple[dict, Formation, list[str]]:
"""Lit le fichier yaml et construit l'ensemble des objets"""
with open(filename, encoding="utf-8") as f:
doc = yaml.safe_load(f.read())
@ -307,9 +319,20 @@ def setup_from_yaml(filename: str) -> dict:
formation = setup_formation(doc["Formation"])
yaml_setup_but.associe_ues_et_parcours(formation, doc["Formation"])
setup_formsemestres(formation, doc)
formsemestre_titres = list(doc["FormSemestres"].keys())
return doc, formation, formsemestre_titres
def create_formsemestre_with_etuds(
doc: dict, formation: Formation, formsemestre_titre: str
) -> FormSemestre:
"""Création formsemestre de titre indiqué, puis inscrit ses étudianst et les note"""
formsemestre = setup_formsemestre(
formation, doc, formsemestre_titre=formsemestre_titre
)
etudiants = doc.get("Etudiants")
if etudiants:
inscrit_les_etudiants(formation, doc)
note_les_modules(doc)
return doc
inscrit_les_etudiants(doc, formsemestre_titre=formsemestre_titre)
note_les_modules(doc, formsemestre_titre=formsemestre_titre)
return formsemestre

View File

@ -272,9 +272,7 @@ def check_deca_fields(formsemestre: FormSemestre, etud: Identite = None):
etud = etud or formsemestre.etuds.first()
assert etud # il faut au moins un étudiant dans le semestre
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
parcour = deca.parcour
formation: Formation = formsemestre.formation
ues = (
@ -310,7 +308,7 @@ def check_deca_fields(formsemestre: FormSemestre, etud: Identite = None):
if deca.formsemestre_impair
else 0
)
assert len(deca.decisions_ues) == nb_ues
assert nb_ues > 0
assert len(deca.niveaux_competences) == len(ues)
assert deca.nb_competences == len(ues)