Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
S
SCODOC_R6A06
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Paul Milleville
SCODOC_R6A06
Commits
e11b7fa0
Commit
e11b7fa0
authored
2 years ago
by
Emmanuel Viennet
Browse files
Options
Downloads
Patches
Plain Diff
Tests YAML: séparation fct spécifiques BUT
parent
b0897737
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
sco_version.py
+1
-1
1 addition, 1 deletion
sco_version.py
tests/unit/test_but_jury.py
+9
-9
9 additions, 9 deletions
tests/unit/test_but_jury.py
tests/unit/yaml_setup.py
+32
-298
32 additions, 298 deletions
tests/unit/yaml_setup.py
tests/unit/yaml_setup_but.py
+304
-0
304 additions, 0 deletions
tests/unit/yaml_setup_but.py
with
346 additions
and
308 deletions
sco_version.py
+
1
−
1
View file @
e11b7fa0
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION
=
"
9.4.4
0
"
SCOVERSION
=
"
9.4.4
1
"
SCONAME
=
"
ScoDoc
"
...
...
This diff is collapsed.
Click to expand it.
tests/unit/test_but_jury.py
+
9
−
9
View file @
e11b7fa0
...
...
@@ -19,7 +19,7 @@ pytest --pdb -m lyon tests/unit/test_but_jury.py
"""
import
pytest
from
tests.unit
import
yaml_setup
from
tests.unit
import
yaml_setup
,
yaml_setup_but
import
app
from
app.but.jury_but_validation_auto
import
formsemestre_validation_auto_but
...
...
@@ -46,7 +46,7 @@ def test_but_jury_GB(test_client):
# Vérifie les deca de tous les semestres:
for
formsemestre
in
FormSemestre
.
query
:
yaml_setup
.
check_deca_fields
(
formsemestre
)
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
):
...
...
@@ -54,11 +54,11 @@ def test_but_jury_GB(test_client):
# Vérifie résultats attendus:
S1
:
FormSemestre
=
FormSemestre
.
query
.
filter_by
(
titre
=
"
S1_SEE
"
).
first
()
yaml_setup
.
test_bu
t_jury
(
S1
,
doc
)
yaml_setup
_but
.
but_tes
t_jury
(
S1
,
doc
)
S2
:
FormSemestre
=
FormSemestre
.
query
.
filter_by
(
titre
=
"
S2_SEE
"
).
first
()
yaml_setup
.
test_bu
t_jury
(
S2
,
doc
)
yaml_setup
_but
.
but_tes
t_jury
(
S2
,
doc
)
S3
:
FormSemestre
=
FormSemestre
.
query
.
filter_by
(
titre
=
"
S3
"
).
first
()
yaml_setup
.
test_bu
t_jury
(
S3
,
doc
)
yaml_setup
_but
.
but_tes
t_jury
(
S3
,
doc
)
# _test_but_jury(S1_redoublant, doc)
...
...
@@ -77,7 +77,7 @@ def test_but_jury_GMP_lm(test_client):
# Vérifie les deca de tous les semestres:
for
formsemestre
in
formsemestres
:
yaml_setup
.
check_deca_fields
(
formsemestre
)
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
:
...
...
@@ -85,7 +85,7 @@ def test_but_jury_GMP_lm(test_client):
# Vérifie résultats attendus:
for
formsemestre
in
formsemestres
:
yaml_setup
.
test_bu
t_jury
(
formsemestre
,
doc
)
yaml_setup
_but
.
but_tes
t_jury
(
formsemestre
,
doc
)
@pytest.mark.slow
...
...
@@ -102,7 +102,7 @@ def test_but_jury_GEII_lyon(test_client):
# Vérifie les champs de DecisionsProposeesAnnee de tous les semestres:
for
formsemestre
in
formsemestres
:
yaml_setup
.
check_deca_fields
(
formsemestre
)
yaml_setup
_but
.
check_deca_fields
(
formsemestre
)
# Saisie de toutes les décisions de jury "automatiques"
# et vérification des résultats attendus:
...
...
@@ -110,4 +110,4 @@ def test_but_jury_GEII_lyon(test_client):
formsemestre_validation_auto_but
(
formsemestre
,
only_adm
=
False
,
no_overwrite
=
False
)
yaml_setup
.
test_bu
t_jury
(
formsemestre
,
doc
)
yaml_setup
_but
.
but_tes
t_jury
(
formsemestre
,
doc
)
This diff is collapsed.
Click to expand it.
tests/unit/yaml_setup.py
+
32
−
298
View file @
e11b7fa0
...
...
@@ -13,7 +13,7 @@ Le traitement est effectué dans l'ordre suivant:
setup_from_yaml()
- setup_
but_
formation():
- setup_formation():
- 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
...
...
@@ -43,33 +43,22 @@ et donc marqués par `@pytest.mark.slow`.
"""
import
os
from
pathlib
import
Path
import
re
import
yaml
from
flask
import
current_app
,
g
from
flask
import
g
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
,
DecisionsProposeesRCUE
,
DecisionsProposeesUE
,
)
from
app.models
import
(
ApcNiveau
,
ApcParcours
,
ApcReferentielCompetences
,
Evaluation
,
Formation
,
FormSemestre
,
Identite
,
Module
,
ModuleImpl
,
ModuleUECoef
,
UniteEns
,
)
from
app.scodoc
import
sco_formations
...
...
@@ -79,9 +68,10 @@ from app.scodoc import sco_saisie_notes
from
app.scodoc
import
sco_utils
as
scu
from
tests.conftest
import
RESOURCES_DIR
from
tests.unit
import
yaml_setup_but
def
setup_
but_
formation
(
formation_infos
:
dict
)
->
Formation
:
def
setup_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.
...
...
@@ -100,94 +90,7 @@ def setup_but_formation(formation_infos: dict) -> 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
(
def
create_formsemestre
(
formation
:
Formation
,
parcours
:
list
[
ApcParcours
],
semestre_id
:
int
,
...
...
@@ -195,7 +98,8 @@ def _un_semestre(
date_debut
:
str
,
date_fin
:
str
,
)
->
FormSemestre
:
"
Création d
'
un formsemestre
"
"
Création d
'
un formsemestre, avec ses modimpls et évaluations
"
assert
formation
.
is_apc
()
or
not
parcours
# parcours seulement si APC
formsemestre
=
FormSemestre
(
formation
=
formation
,
parcours
=
parcours
,
...
...
@@ -230,7 +134,9 @@ def _un_semestre(
formsemestre
.
id
,
default
=
True
,
redirect
=
False
)
_
=
sco_groups
.
create_group
(
partition_id
,
default
=
True
)
# Partition de parcours:
if
formsemestre
.
formation
.
is_apc
():
formsemestre
.
setup_parcours_groups
()
return
formsemestre
...
...
@@ -248,13 +154,17 @@ def create_evaluations(formsemestre: FormSemestre):
numero
=
1
,
)
db
.
session
.
add
(
evaluation
)
if
formsemestre
.
formation
.
is_apc
():
# 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
"""
"""
Saisie les notes des étudiants
doc : données YAML
"""
a_user
=
User
.
query
.
first
()
for
nom
,
infos
in
doc
[
"
Etudiants
"
].
items
():
etud
:
Identite
=
Identite
.
query
.
filter_by
(
nom
=
nom
).
first
()
...
...
@@ -295,16 +205,21 @@ def note_les_modules(doc: dict):
def
setup_formsemestres
(
formation
:
Formation
,
doc
:
str
):
"""
Création des formsemestres pour tester les parcours BUT
"""
"""
Création des formsemestres
Le cas échéant associés à leur(s) parcours.
"""
for
titre
,
infos
in
doc
[
"
FormSemestres
"
].
items
():
codes_parcours
=
infos
.
get
(
"
codes_parcours
"
,
[])
assert
formation
.
is_apc
()
or
not
codes_parcours
# parcours seulement en APC
parcours
=
[]
for
code_parcour
in
infos
.
get
(
"
codes_parcours
"
,
[])
:
for
code_parcour
in
codes_parcours
:
parcour
=
formation
.
referentiel_competence
.
parcours
.
filter_by
(
code
=
code_parcour
).
first
()
assert
parcour
is
not
None
parcours
.
append
(
parcour
)
_
=
_un_semestre
(
_
=
create_formsemestre
(
formation
,
parcours
,
infos
[
"
idx
"
],
...
...
@@ -362,193 +277,12 @@ def setup_from_yaml(filename: str) -> dict:
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
"
])
formation
=
setup_formation
(
doc
[
"
Formation
"
])
yaml_setup_but
.
setup_formation_referentiel
(
formation
,
doc
.
get
(
"
ReferentielCompetences
"
,
{})
)
yaml_setup_but
.
associe_ues_et_parcours
(
formation
,
doc
[
"
Formation
"
])
setup_formsemestres
(
formation
,
doc
)
inscrit_les_etudiants
(
formation
,
doc
)
note_les_modules
(
doc
)
return
doc
def
_check_codes_jury
(
codes
:
list
[
str
],
codes_att
:
list
[
str
]):
"""
Vérifie (assert) la liste des codes
l
'
ordre n
'
a pas d
'
importance ici.
Si codes_att contient un
"
...
"
, on se contente de vérifier que
les codes de codes_att sont tous présents dans codes.
"""
codes_set
=
set
(
codes
)
codes_att_set
=
set
(
codes_att
)
if
"
...
"
in
codes_att_set
:
codes_att_set
.
remove
(
"
...
"
)
assert
codes_att_set
.
issubset
(
codes_set
)
else
:
assert
codes_att_set
==
codes_set
def
_check_decisions_ues
(
decisions_ues
:
dict
[
int
,
DecisionsProposeesUE
],
decisions_ues_att
:
dict
[
str
:
dict
]
):
"""
Vérifie les décisions d
'
UE
puis enregistre décision manuelle si indiquée dans le YAML.
"""
for
acronyme
,
dec_ue_att
in
decisions_ues_att
.
items
():
# retrouve l'UE
ues_d
=
[
dec_ue
for
dec_ue
in
decisions_ues
.
values
()
if
dec_ue
.
ue
.
acronyme
==
acronyme
]
assert
len
(
ues_d
)
==
1
# une et une seule UE avec l'acronyme indiqué
dec_ue
=
ues_d
[
0
]
if
"
codes
"
in
dec_ue_att
:
_check_codes_jury
(
dec_ue
.
codes
,
dec_ue_att
[
"
codes
"
])
for
attr
in
(
"
explanation
"
,
"
code_valide
"
):
if
attr
in
dec_ue_att
:
if
getattr
(
dec_ue
,
attr
)
!=
dec_ue_att
[
attr
]:
raise
ValueError
(
f
"""
Erreur: décision d
'
UE:
{
dec_ue
.
ue
.
acronyme
}
: champs
{
attr
}
=
{
getattr
(
dec_ue
,
attr
)
}
!= attendu
{
dec_ue_att
[
attr
]
}
"""
)
for
attr
in
(
"
moy_ue
"
,
"
moy_ue_with_cap
"
):
if
attr
in
dec_ue_att
:
assert
(
abs
(
getattr
(
dec_ue
,
attr
)
-
dec_ue_att
[
attr
])
<
scu
.
NOTES_PRECISION
)
# Force décision de jury:
code_manuel
=
dec_ue_att
.
get
(
"
decision_jury
"
)
if
code_manuel
is
not
None
:
assert
code_manuel
in
dec_ue
.
codes
dec_ue
.
record
(
code_manuel
)
def
_check_decisions_rcues
(
decisions_rcues
:
list
[
DecisionsProposeesRCUE
],
decisions_rcues_att
:
dict
):
"
Vérifie les décisions d
'
RCUEs
"
for
acronyme
,
dec_rcue_att
in
decisions_rcues_att
.
items
():
# retrouve la décision RCUE à partir de l'acronyme de la 1ère UE
rcues_d
=
[
dec_rcue
for
dec_rcue
in
decisions_rcues
if
dec_rcue
.
rcue
.
ue_1
.
acronyme
==
acronyme
]
assert
len
(
rcues_d
)
==
1
# un et un seul RCUE avec l'UE d'acronyme indiqué
dec_rcue
=
rcues_d
[
0
]
if
"
codes
"
in
dec_rcue_att
:
_check_codes_jury
(
dec_rcue
.
codes
,
dec_rcue_att
[
"
codes
"
])
for
attr
in
(
"
explanation
"
,
"
code_valide
"
):
if
attr
in
dec_rcue_att
:
assert
getattr
(
dec_rcue
,
attr
)
==
dec_rcue_att
[
attr
]
# Descend dans le RCUE:
if
"
rcue
"
in
dec_rcue_att
:
if
"
moy_rcue
"
in
dec_rcue_att
[
"
rcue
"
]:
assert
(
abs
(
dec_rcue
.
rcue
.
moy_rcue
-
dec_rcue_att
[
"
rcue
"
][
"
moy_rcue
"
])
<
scu
.
NOTES_PRECISION
)
if
"
est_compensable
"
in
dec_rcue_att
[
"
rcue
"
]:
assert
(
dec_rcue
.
rcue
.
est_compensable
()
==
dec_rcue_att
[
"
rcue
"
][
"
est_compensable
"
]
)
# Force décision de jury:
code_manuel
=
dec_rcue_att
.
get
(
"
decision_jury
"
)
if
code_manuel
is
not
None
:
assert
code_manuel
in
dec_rcue
.
codes
dec_rcue
.
record
(
code_manuel
)
def
compare_decisions_annee
(
deca
:
DecisionsProposeesAnnee
,
deca_att
:
dict
):
"""
Vérifie que les résultats de jury calculés pour l
'
année, les RCUEs et les UEs
sont ceux attendus,
puis enregistre les décisions manuelles indiquées dans le YAML.
deca est le résultat calculé par ScoDoc
deca_att est un dict lu du YAML
"""
if
"
codes
"
in
deca_att
:
_check_codes_jury
(
deca
.
codes
,
deca_att
[
"
codes
"
])
for
attr
in
(
"
passage_de_droit
"
,
"
code_valide
"
,
"
nb_competences
"
):
if
attr
in
deca_att
:
assert
getattr
(
deca
,
attr
)
==
deca_att
[
attr
]
if
"
decisions_ues
"
in
deca_att
:
_check_decisions_ues
(
deca
.
decisions_ues
,
deca_att
[
"
decisions_ues
"
])
if
"
nb_rcues_annee
"
in
deca_att
:
assert
deca_att
[
"
nb_rcues_annee
"
]
==
len
(
deca
.
rcues_annee
)
if
"
decisions_rcues
"
in
deca_att
:
_check_decisions_rcues
(
deca
.
decisions_rcue_by_niveau
.
values
(),
deca_att
[
"
decisions_rcues
"
]
)
# Force décision de jury:
code_manuel
=
deca_att
.
get
(
"
decision_jury
"
)
if
code_manuel
is
not
None
:
assert
code_manuel
in
deca
.
codes
deca
.
record
(
code_manuel
)
assert
deca
.
recorded
def
check_deca_fields
(
formsemestre
:
FormSemestre
,
etud
:
Identite
=
None
):
"""
Vérifie les champs principaux (inscription, nb UE, nb compétences)
de l
'
instance de DecisionsProposeesAnnee.
Ne vérifie pas les décisions de jury proprement dites.
Si etud n
'
est pas spécifié, prend le premier inscrit trouvé dans le semestre.
"""
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
if
formsemestre
.
semestre_id
%
2
:
assert
deca
.
formsemestre_impair
==
formsemestre
assert
formsemestre
.
query_ues_parcours_etud
(
etud
.
id
).
all
()
==
deca
.
ues_impair
else
:
assert
deca
.
formsemestre_pair
==
formsemestre
assert
formsemestre
.
query_ues_parcours_etud
(
etud
.
id
).
all
()
==
deca
.
ues_pair
assert
deca
.
inscription_etat
==
scu
.
INSCRIT
assert
deca
.
inscription_etat_impair
==
scu
.
INSCRIT
assert
(
deca
.
parcour
is
None
)
or
(
deca
.
parcour
.
id
in
{
p
.
id
for
p
in
formsemestre
.
parcours
}
)
nb_ues
=
(
len
(
deca
.
formsemestre_pair
.
query_ues_parcours_etud
(
etud
.
id
).
all
())
if
deca
.
formsemestre_pair
else
0
)
nb_ues
+=
(
len
(
deca
.
formsemestre_impair
.
query_ues_parcours_etud
(
etud
.
id
).
all
())
if
deca
.
formsemestre_impair
else
0
)
assert
len
(
deca
.
decisions_ues
)
==
nb_ues
nb_ues_un_sem
=
(
len
(
deca
.
formsemestre_impair
.
query_ues_parcours_etud
(
etud
.
id
).
all
())
if
deca
.
formsemestre_impair
else
len
(
deca
.
formsemestre_pair
.
query_ues_parcours_etud
(
etud
.
id
).
all
())
)
assert
len
(
deca
.
niveaux_competences
)
==
nb_ues_un_sem
assert
deca
.
nb_competences
==
nb_ues_un_sem
def
test_but_jury
(
formsemestre
:
FormSemestre
,
doc
:
dict
):
"""
Test jurys BUT
Vérifie les champs de DecisionsProposeesAnnee et UEs
"""
for
etud
in
formsemestre
.
etuds
:
deca
=
DecisionsProposeesAnnee
(
etud
,
formsemestre
)
doc_formsemestre
=
doc
[
"
Etudiants
"
][
etud
.
nom
][
"
formsemestres
"
][
formsemestre
.
titre
]
assert
doc_formsemestre
if
"
attendu
"
in
doc_formsemestre
:
if
"
deca
"
in
doc_formsemestre
[
"
attendu
"
]:
deca_att
=
doc_formsemestre
[
"
attendu
"
][
"
deca
"
]
compare_decisions_annee
(
deca
,
deca_att
)
This diff is collapsed.
Click to expand it.
tests/unit/yaml_setup_but.py
0 → 100644
+
304
−
0
View file @
e11b7fa0
##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""
Mise en place pour tests unitaires à partir de descriptions YAML:
fonctions spécifiques au BUT
"""
from
pathlib
import
Path
import
re
from
flask
import
current_app
,
g
from
app
import
db
from
app.but.import_refcomp
import
orebut_import_refcomp
from
app.but.jury_but
import
(
DecisionsProposeesAnnee
,
DecisionsProposeesRCUE
,
DecisionsProposeesUE
,
)
from
app.models
import
(
ApcNiveau
,
ApcReferentielCompetences
,
Formation
,
FormSemestre
,
Identite
,
UniteEns
,
)
from
app.scodoc
import
sco_utils
as
scu
def
setup_formation_referentiel
(
formation
:
Formation
,
refcomp_infos
:
dict
):
"""
Si il y a un référentiel de compétences, indiqué dans le YAML,
le charge au besoin et l
'
associe à la formation.
"""
if
not
refcomp_infos
:
return
assert
formation
.
is_apc
()
# si ref; comp., doit être APC
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
_check_codes_jury
(
codes
:
list
[
str
],
codes_att
:
list
[
str
]):
"""
Vérifie (assert) la liste des codes
l
'
ordre n
'
a pas d
'
importance ici.
Si codes_att contient un
"
...
"
, on se contente de vérifier que
les codes de codes_att sont tous présents dans codes.
"""
codes_set
=
set
(
codes
)
codes_att_set
=
set
(
codes_att
)
if
"
...
"
in
codes_att_set
:
codes_att_set
.
remove
(
"
...
"
)
assert
codes_att_set
.
issubset
(
codes_set
)
else
:
assert
codes_att_set
==
codes_set
def
but_check_decisions_ues
(
decisions_ues
:
dict
[
int
,
DecisionsProposeesUE
],
decisions_ues_att
:
dict
[
str
:
dict
]
):
"""
Vérifie les décisions d
'
UE
puis enregistre décision manuelle si indiquée dans le YAML.
"""
for
acronyme
,
dec_ue_att
in
decisions_ues_att
.
items
():
# retrouve l'UE
ues_d
=
[
dec_ue
for
dec_ue
in
decisions_ues
.
values
()
if
dec_ue
.
ue
.
acronyme
==
acronyme
]
assert
len
(
ues_d
)
==
1
# une et une seule UE avec l'acronyme indiqué
dec_ue
=
ues_d
[
0
]
if
"
codes
"
in
dec_ue_att
:
_check_codes_jury
(
dec_ue
.
codes
,
dec_ue_att
[
"
codes
"
])
for
attr
in
(
"
explanation
"
,
"
code_valide
"
):
if
attr
in
dec_ue_att
:
if
getattr
(
dec_ue
,
attr
)
!=
dec_ue_att
[
attr
]:
raise
ValueError
(
f
"""
Erreur: décision d
'
UE:
{
dec_ue
.
ue
.
acronyme
}
: champs
{
attr
}
=
{
getattr
(
dec_ue
,
attr
)
}
!= attendu
{
dec_ue_att
[
attr
]
}
"""
)
for
attr
in
(
"
moy_ue
"
,
"
moy_ue_with_cap
"
):
if
attr
in
dec_ue_att
:
assert
(
abs
(
getattr
(
dec_ue
,
attr
)
-
dec_ue_att
[
attr
])
<
scu
.
NOTES_PRECISION
)
# Force décision de jury:
code_manuel
=
dec_ue_att
.
get
(
"
decision_jury
"
)
if
code_manuel
is
not
None
:
assert
code_manuel
in
dec_ue
.
codes
dec_ue
.
record
(
code_manuel
)
def
but_check_decisions_rcues
(
decisions_rcues
:
list
[
DecisionsProposeesRCUE
],
decisions_rcues_att
:
dict
):
"
Vérifie les décisions d
'
RCUEs
"
for
acronyme
,
dec_rcue_att
in
decisions_rcues_att
.
items
():
# retrouve la décision RCUE à partir de l'acronyme de la 1ère UE
rcues_d
=
[
dec_rcue
for
dec_rcue
in
decisions_rcues
if
dec_rcue
.
rcue
.
ue_1
.
acronyme
==
acronyme
]
assert
len
(
rcues_d
)
==
1
# un et un seul RCUE avec l'UE d'acronyme indiqué
dec_rcue
=
rcues_d
[
0
]
if
"
codes
"
in
dec_rcue_att
:
_check_codes_jury
(
dec_rcue
.
codes
,
dec_rcue_att
[
"
codes
"
])
for
attr
in
(
"
explanation
"
,
"
code_valide
"
):
if
attr
in
dec_rcue_att
:
assert
getattr
(
dec_rcue
,
attr
)
==
dec_rcue_att
[
attr
]
# Descend dans le RCUE:
if
"
rcue
"
in
dec_rcue_att
:
if
"
moy_rcue
"
in
dec_rcue_att
[
"
rcue
"
]:
assert
(
abs
(
dec_rcue
.
rcue
.
moy_rcue
-
dec_rcue_att
[
"
rcue
"
][
"
moy_rcue
"
])
<
scu
.
NOTES_PRECISION
)
if
"
est_compensable
"
in
dec_rcue_att
[
"
rcue
"
]:
assert
(
dec_rcue
.
rcue
.
est_compensable
()
==
dec_rcue_att
[
"
rcue
"
][
"
est_compensable
"
]
)
# Force décision de jury:
code_manuel
=
dec_rcue_att
.
get
(
"
decision_jury
"
)
if
code_manuel
is
not
None
:
assert
code_manuel
in
dec_rcue
.
codes
dec_rcue
.
record
(
code_manuel
)
def
but_compare_decisions_annee
(
deca
:
DecisionsProposeesAnnee
,
deca_att
:
dict
):
"""
Vérifie que les résultats de jury calculés pour l
'
année, les RCUEs et les UEs
sont ceux attendus,
puis enregistre les décisions manuelles indiquées dans le YAML.
deca est le résultat calculé par ScoDoc
deca_att est un dict lu du YAML
"""
if
"
codes
"
in
deca_att
:
_check_codes_jury
(
deca
.
codes
,
deca_att
[
"
codes
"
])
for
attr
in
(
"
passage_de_droit
"
,
"
code_valide
"
,
"
nb_competences
"
):
if
attr
in
deca_att
:
assert
getattr
(
deca
,
attr
)
==
deca_att
[
attr
]
if
"
decisions_ues
"
in
deca_att
:
but_check_decisions_ues
(
deca
.
decisions_ues
,
deca_att
[
"
decisions_ues
"
])
if
"
nb_rcues_annee
"
in
deca_att
:
assert
deca_att
[
"
nb_rcues_annee
"
]
==
len
(
deca
.
rcues_annee
)
if
"
decisions_rcues
"
in
deca_att
:
but_check_decisions_rcues
(
deca
.
decisions_rcue_by_niveau
.
values
(),
deca_att
[
"
decisions_rcues
"
]
)
# Force décision de jury:
code_manuel
=
deca_att
.
get
(
"
decision_jury
"
)
if
code_manuel
is
not
None
:
assert
code_manuel
in
deca
.
codes
deca
.
record
(
code_manuel
)
assert
deca
.
recorded
def
check_deca_fields
(
formsemestre
:
FormSemestre
,
etud
:
Identite
=
None
):
"""
Vérifie les champs principaux (inscription, nb UE, nb compétences)
de l
'
instance de DecisionsProposeesAnnee.
Ne vérifie pas les décisions de jury proprement dites.
Si etud n
'
est pas spécifié, prend le premier inscrit trouvé dans le semestre.
"""
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
if
formsemestre
.
semestre_id
%
2
:
assert
deca
.
formsemestre_impair
==
formsemestre
assert
formsemestre
.
query_ues_parcours_etud
(
etud
.
id
).
all
()
==
deca
.
ues_impair
else
:
assert
deca
.
formsemestre_pair
==
formsemestre
assert
formsemestre
.
query_ues_parcours_etud
(
etud
.
id
).
all
()
==
deca
.
ues_pair
assert
deca
.
inscription_etat
==
scu
.
INSCRIT
assert
deca
.
inscription_etat_impair
==
scu
.
INSCRIT
assert
(
deca
.
parcour
is
None
)
or
(
deca
.
parcour
.
id
in
{
p
.
id
for
p
in
formsemestre
.
parcours
}
)
nb_ues
=
(
len
(
deca
.
formsemestre_pair
.
query_ues_parcours_etud
(
etud
.
id
).
all
())
if
deca
.
formsemestre_pair
else
0
)
nb_ues
+=
(
len
(
deca
.
formsemestre_impair
.
query_ues_parcours_etud
(
etud
.
id
).
all
())
if
deca
.
formsemestre_impair
else
0
)
assert
len
(
deca
.
decisions_ues
)
==
nb_ues
nb_ues_un_sem
=
(
len
(
deca
.
formsemestre_impair
.
query_ues_parcours_etud
(
etud
.
id
).
all
())
if
deca
.
formsemestre_impair
else
len
(
deca
.
formsemestre_pair
.
query_ues_parcours_etud
(
etud
.
id
).
all
())
)
assert
len
(
deca
.
niveaux_competences
)
==
nb_ues_un_sem
assert
deca
.
nb_competences
==
nb_ues_un_sem
def
but_test_jury
(
formsemestre
:
FormSemestre
,
doc
:
dict
):
"""
Test jurys BUT
Vérifie les champs de DecisionsProposeesAnnee et UEs
"""
for
etud
in
formsemestre
.
etuds
:
deca
=
DecisionsProposeesAnnee
(
etud
,
formsemestre
)
doc_formsemestre
=
doc
[
"
Etudiants
"
][
etud
.
nom
][
"
formsemestres
"
][
formsemestre
.
titre
]
assert
doc_formsemestre
if
"
attendu
"
in
doc_formsemestre
:
if
"
deca
"
in
doc_formsemestre
[
"
attendu
"
]:
deca_att
=
doc_formsemestre
[
"
attendu
"
][
"
deca
"
]
but_compare_decisions_annee
(
deca
,
deca_att
)
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment