BUT ref. Comp.: fix calcul niveaux/parcours, ajoute tests unitaires GCCD et MLT.

This commit is contained in:
Emmanuel Viennet 2023-03-26 10:08:50 +02:00
parent 8af5c3ffa0
commit 65b87049ca
7 changed files with 159 additions and 35 deletions

View File

@ -85,7 +85,11 @@ class ApcReferentielCompetences(db.Model, XMLModel):
lazy="dynamic",
cascade="all, delete-orphan",
)
formations = db.relationship("Formation", backref="referentiel_competence")
formations = db.relationship(
"Formation",
backref="referentiel_competence",
order_by="Formation.acronyme, Formation.version",
)
def __repr__(self):
return f"<ApcReferentielCompetences {self.id} {self.specialite!r} {self.departement!r}>"
@ -360,32 +364,36 @@ class ApcNiveau(db.Model, XMLModel):
parcour: "ApcParcours",
annee: int,
referentiel_competence: ApcReferentielCompetences = None,
) -> flask_sqlalchemy.BaseQuery:
) -> list["ApcNiveau"]:
"""Les niveaux de l'année du parcours
Si le parcour est None, tous les niveaux de l'année
"""
if annee not in {1, 2, 3}:
raise ValueError("annee invalide pour un parcours BUT")
referentiel_competence = (
parcour.referentiel if parcour else referentiel_competence
)
if referentiel_competence is None:
raise ScoNoReferentielCompetences()
if not parcour:
annee_formation = f"BUT{annee}"
if parcour is None:
return ApcNiveau.query.filter(
ApcNiveau.annee == annee_formation,
ApcCompetence.id == ApcNiveau.competence_id,
ApcCompetence.referentiel_id == referentiel_competence.id,
)
else:
return ApcNiveau.query.filter(
ApcParcoursNiveauCompetence.annee_parcours_id == ApcAnneeParcours.id,
ApcParcours.id == ApcAnneeParcours.parcours_id,
ApcParcours.referentiel == parcour.referentiel,
ApcParcoursNiveauCompetence.competence_id == ApcCompetence.id,
ApcCompetence.id == ApcNiveau.competence_id,
ApcAnneeParcours.parcours == parcour,
ApcNiveau.annee == annee_formation,
)
annee_parcour = parcour.annees.filter_by(ordre=annee).first()
if not annee_parcour:
return []
parcour_niveaux: list[
ApcParcoursNiveauCompetence
] = annee_parcour.niveaux_competences
niveaux: list[ApcNiveau] = [
pn.competence.niveaux.filter_by(ordre=pn.niveau).first()
for pn in parcour_niveaux
]
return niveaux
app_critiques_modules = db.Table(

View File

@ -554,7 +554,7 @@ def formation_list_table() -> GenTable:
row["_buttons_html"] = but_locked + but_suppr + but_edit
rows.append(row)
# Tri par annee_dernier_sem, type, acronyme, titre, version décroissante
# donc plus récemment utilsiée en tête
# donc plus récemment utilisée en tête
rows.sort(
key=lambda row: (
-row["annee_dernier_sem"],

View File

@ -19,7 +19,20 @@
<div class="part2">
<a class="stdlink" href="{{url_for('notes.refcomp_table', scodoc_dept=g.scodoc_dept)}}">liste des référentiels</a>
<ul>
<li>Formations se référant à ce référentiel:
<ul>
{% for formation in ref.formations %}
<li><a class="stdlink" href="{{
url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=formation.id )
}}">{{ formation.get_titre_version() }}</a></li>
{% endfor %}
</ul>
</li>
<li><a class="stdlink" href="{{url_for('notes.refcomp_table', scodoc_dept=g.scodoc_dept)}}">Liste des référentiels</a>
</li>
</ul>
</div>
{% endblock %}

View File

@ -33,31 +33,21 @@ Emmanuel Viennet, 2021
from flask import url_for
from flask import jsonify
from flask import current_app, g, request
from flask import g, request
from flask.templating import render_template
from flask_login import current_user
from werkzeug.utils import redirect
from app.scodoc.codes_cursus import UE_SPORT
from config import Config
from app import db
from app import models
from app.auth.models import User
from app import db, models
from app.comp import moy_ue
from app.decorators import scodoc, permission_required
from app.models import ApcParcours, Formation, Module
from app.views import notes_bp as bp
# ---------------
from app.scodoc import sco_utils as scu
from app.scodoc import sco_formations
from app import log
from app.models import ApcParcours, Formation, UniteEns, Module
from app.scodoc import html_sco_header
from app.scodoc import sco_utils as scu
from app.scodoc.sco_permissions import Permission

View File

@ -44,17 +44,17 @@ def refcomp(refcomp_id):
@permission_required(Permission.ScoView)
def refcomp_show(refcomp_id):
"""Affichage du référentiel de compétences."""
ref = ApcReferentielCompetences.query.get_or_404(refcomp_id)
referentiel_competence = ApcReferentielCompetences.query.get_or_404(refcomp_id)
return render_template(
"but/refcomp_show.j2",
ref=ref,
ref=referentiel_competence,
title="Référentiel de compétences",
sco=ScoData(),
data_source=url_for(
"notes.refcomp",
scodoc_dept=g.scodoc_dept,
refcomp_id=refcomp_id,
),
sco=ScoData(),
)

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.4.70"
SCOVERSION = "9.4.71"
SCONAME = "ScoDoc"

View File

@ -24,6 +24,12 @@ from tests.unit import setup
REF_RT_XML = open(
"ressources/referentiels/but2022/competences/but-RT-05012022-081735.xml"
).read()
REF_MLT_XML = open(
"ressources/referentiels/but2022/competences/but-MLT-05012022-081603.xml"
).read()
REF_GCCD_XML = open(
"ressources/referentiels/but2022/competences/but-GCCD-05012022-081630.xml"
).read()
def test_but_refcomp(test_client):
@ -84,3 +90,110 @@ def test_but_assoc_refcomp(test_client):
db.session.commit()
formation.refcomp_desassoc()
assert ue.niveau_competence_id is None
def test_refcomp_niveaux_mlt(test_client):
"""Test calcul niveaux / parcours pour un BUT MLT
avec en parcours "Mobilité et Supply Chain Connectées"
une compétence "Transporter" sur BUT1, BUT2
et une compétence "Digitaliser" sur BUT2, BUT3
"""
# Charger le ref. comp. MLT
dept_id = models.Departement.query.first().id
ref_comp: ApcReferentielCompetences = orebut_import_refcomp(REF_MLT_XML, dept_id)
# Vérifier qu'on a 2 parcours et 4 compétences dans chaque
nb_parcours = ref_comp.parcours.count()
assert nb_parcours == 2
assert [len(p.query_competences().all()) for p in ref_comp.parcours] == [
4
] * nb_parcours
# 3 niveaux en 1ere année (dans chaque parcours):
assert [
len(ApcNiveau.niveaux_annee_de_parcours(p, 1, ref_comp))
for p in ref_comp.parcours
] == [3, 3]
# 2 niveaux en 2ème année (dans chaque parcours):
assert [
len(ApcNiveau.niveaux_annee_de_parcours(p, 2, ref_comp))
for p in ref_comp.parcours
] == [4, 4]
# 3 niveaux en 3ème année (dans chaque parcours):
assert [
len(ApcNiveau.niveaux_annee_de_parcours(p, 3, ref_comp))
for p in ref_comp.parcours
] == [3, 3]
# Vérifier les niveaux_by_parcours
parcour = ref_comp.parcours.first()
# BUT 1
parcours, niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(1, parcour)
assert parcours == [parcour] # le parcours indiqué
assert (tuple(niveaux_by_parcours.keys())) == (parcour.id, "TC")
assert niveaux_by_parcours[parcour.id] == [] # tout en tronc commun en BUT1 MLT
assert niveaux_by_parcours["TC"][0].competence.titre == "Transporter"
assert len(niveaux_by_parcours["TC"]) == 3
# BUT 2
parcours, niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(2, parcour)
assert parcours == [parcour] # le parcours indiqué
assert (tuple(niveaux_by_parcours.keys())) == (parcour.id, "TC")
assert len(niveaux_by_parcours[parcour.id]) == 1
assert len(niveaux_by_parcours["TC"]) == 3
# BUT 3
parcours, niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(3, parcour)
assert parcours == [parcour] # le parcours indiqué
assert (tuple(niveaux_by_parcours.keys())) == (parcour.id, "TC")
assert len(niveaux_by_parcours[parcour.id]) == 1
assert len(niveaux_by_parcours["TC"]) == 2
# Vérifier les niveaux de chaque année
assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 1)) == 3
assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 2)) == 4
assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 3)) == 3
def test_refcomp_niveaux_gccd(test_client):
"""Test calcul niveaux / parcours pour un BUT GCCD
avec en parcours "Travaux Bâtiment" 5 compétences
dont la première "Solutions Bâtiment" sur BUT1, BUT2, BUT3
et en parcours "Travaux Publics" la même sur BUT1, BUT2 seulement.
"""
# Charger le ref. comp. GCCD
dept_id = models.Departement.query.first().id
ref_comp: ApcReferentielCompetences = orebut_import_refcomp(REF_GCCD_XML, dept_id)
# Vérifier qu'on a 4 parcours et 5 compétences dans chaque
nb_parcours = ref_comp.parcours.count()
assert nb_parcours == 4
assert [len(p.query_competences().all()) for p in ref_comp.parcours] == [
5
] * nb_parcours
# 5 niveaux en 1ere année (dans chaque parcours):
assert [
len(ApcNiveau.niveaux_annee_de_parcours(p, 1, ref_comp))
for p in ref_comp.parcours
] == [5] * nb_parcours
# 5 niveaux en 2ème année (dans chaque parcours):
assert [
len(ApcNiveau.niveaux_annee_de_parcours(p, 2, ref_comp))
for p in ref_comp.parcours
] == [5] * nb_parcours
# 3 niveaux en 3ème année (dans chaque parcours):
assert [
len(ApcNiveau.niveaux_annee_de_parcours(p, 3, ref_comp))
for p in ref_comp.parcours
] == [3] * nb_parcours
# Vérifier les niveaux_by_parcours
parcour = ref_comp.parcours.first()
# BUT 1
parcours, niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(1, parcour)
assert parcours == [parcour] # le parcours indiqué
assert (tuple(niveaux_by_parcours.keys())) == (parcour.id, "TC")
assert len(niveaux_by_parcours[parcour.id]) == 0
assert len(niveaux_by_parcours["TC"]) == 5
# BUT 3
parcours, niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(3, parcour)
assert parcours == [parcour] # le parcours indiqué
assert (tuple(niveaux_by_parcours.keys())) == (parcour.id, "TC")
assert len(niveaux_by_parcours[parcour.id]) == 3
assert len(niveaux_by_parcours["TC"]) == 0
# Vérifier les niveaux de chaque année
assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 1)) == 5
assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 2)) == 5
assert len(ApcNiveau.niveaux_annee_de_parcours(parcour, 3)) == 3