forked from ScoDoc/ScoDoc
Nouvelle page de visu/saisie des décisions RCUEs: validation_rcues
This commit is contained in:
parent
24bb78bdfd
commit
f08a4130dd
141
app/api/jury.py
141
app/api/jury.py
@ -8,25 +8,32 @@
|
|||||||
ScoDoc 9 API : jury WIP à compléter avec enregistrement décisions
|
ScoDoc 9 API : jury WIP à compléter avec enregistrement décisions
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import g, url_for
|
import datetime
|
||||||
|
|
||||||
|
from flask import flash, g, request, url_for
|
||||||
from flask_json import as_json
|
from flask_json import as_json
|
||||||
from flask_login import current_user, login_required
|
from flask_login import current_user, login_required
|
||||||
|
|
||||||
import app
|
import app
|
||||||
from app import db, log
|
from app import db, log
|
||||||
from app.api import api_bp as bp, api_web_bp, tools
|
from app.api import api_bp as bp, api_web_bp, API_CLIENT_ERROR, tools
|
||||||
from app.decorators import scodoc, permission_required
|
from app.decorators import scodoc, permission_required
|
||||||
from app.scodoc.sco_exceptions import ScoException
|
from app.scodoc.sco_exceptions import ScoException
|
||||||
from app.but import jury_but_results
|
from app.but import jury_but_results
|
||||||
from app.models import (
|
from app.models import (
|
||||||
|
ApcParcours,
|
||||||
ApcValidationAnnee,
|
ApcValidationAnnee,
|
||||||
ApcValidationRCUE,
|
ApcValidationRCUE,
|
||||||
|
Formation,
|
||||||
FormSemestre,
|
FormSemestre,
|
||||||
Identite,
|
Identite,
|
||||||
ScolarAutorisationInscription,
|
ScolarAutorisationInscription,
|
||||||
ScolarFormSemestreValidation,
|
ScolarFormSemestreValidation,
|
||||||
ScolarNews,
|
ScolarNews,
|
||||||
|
Scolog,
|
||||||
|
UniteEns,
|
||||||
)
|
)
|
||||||
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_utils import json_error
|
from app.scodoc.sco_utils import json_error
|
||||||
@ -161,6 +168,136 @@ def autorisation_inscription_delete(etudid: int, validation_id: int):
|
|||||||
return "ok"
|
return "ok"
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route(
|
||||||
|
"/etudiant/<int:etudid>/jury/validation_rcue/record",
|
||||||
|
methods=["POST"],
|
||||||
|
)
|
||||||
|
@api_web_bp.route(
|
||||||
|
"/etudiant/<int:etudid>/jury/validation_rcue/record",
|
||||||
|
methods=["POST"],
|
||||||
|
)
|
||||||
|
@login_required
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoEtudInscrit)
|
||||||
|
@as_json
|
||||||
|
def validation_rcue_record(etudid: int):
|
||||||
|
"""Enregistre une validation de RCUE.
|
||||||
|
Si une validation existe déjà pour ce RCUE, la remplace.
|
||||||
|
The request content type should be "application/json":
|
||||||
|
{
|
||||||
|
"code" : str,
|
||||||
|
"ue1_id" : int,
|
||||||
|
"ue2_id" : int,
|
||||||
|
// Optionnel:
|
||||||
|
"formsemestre_id" : int,
|
||||||
|
"date" : date_iso, // si non spécifié, now()
|
||||||
|
"parcours_id" :int,
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
etud = tools.get_etud(etudid)
|
||||||
|
if etud is None:
|
||||||
|
return json_error(404, "étudiant inconnu")
|
||||||
|
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
|
code = data.get("code")
|
||||||
|
if code is None:
|
||||||
|
return json_error(API_CLIENT_ERROR, "missing argument: code")
|
||||||
|
if code not in codes_cursus.CODES_JURY_RCUE:
|
||||||
|
return json_error(API_CLIENT_ERROR, "invalid code value")
|
||||||
|
ue1_id = data.get("ue1_id")
|
||||||
|
if ue1_id is None:
|
||||||
|
return json_error(API_CLIENT_ERROR, "missing argument: ue1_id")
|
||||||
|
try:
|
||||||
|
ue1_id = int(ue1_id)
|
||||||
|
except ValueError:
|
||||||
|
return json_error(API_CLIENT_ERROR, "invalid value for ue1_id")
|
||||||
|
ue2_id = data.get("ue2_id")
|
||||||
|
if ue2_id is None:
|
||||||
|
return json_error(API_CLIENT_ERROR, "missing argument: ue2_id")
|
||||||
|
try:
|
||||||
|
ue2_id = int(ue2_id)
|
||||||
|
except ValueError:
|
||||||
|
return json_error(API_CLIENT_ERROR, "invalid value for ue2_id")
|
||||||
|
formsemestre_id = data.get("formsemestre_id")
|
||||||
|
date_validation_str = data.get("date", datetime.datetime.now().isoformat())
|
||||||
|
parcours_id = data.get("parcours_id")
|
||||||
|
#
|
||||||
|
query = UniteEns.query.filter_by(id=ue1_id)
|
||||||
|
if g.scodoc_dept:
|
||||||
|
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
ue1: UniteEns = query.first_or_404()
|
||||||
|
query = UniteEns.query.filter_by(id=ue2_id)
|
||||||
|
if g.scodoc_dept:
|
||||||
|
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
ue2: UniteEns = query.first_or_404()
|
||||||
|
if ue1.niveau_competence_id != ue2.niveau_competence_id:
|
||||||
|
return json_error(
|
||||||
|
API_CLIENT_ERROR, "UEs non associees au meme niveau de competence"
|
||||||
|
)
|
||||||
|
if formsemestre_id is not None:
|
||||||
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
|
if g.scodoc_dept:
|
||||||
|
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
formsemestre: FormSemestre = query.first_or_404()
|
||||||
|
if (formsemestre.formation_id != ue1.formation_id) or (
|
||||||
|
formsemestre.formation_id != ue2.formation_id
|
||||||
|
):
|
||||||
|
return json_error(
|
||||||
|
API_CLIENT_ERROR, "ues et semestre ne sont pas de la meme formation"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
formsemestre = None
|
||||||
|
try:
|
||||||
|
date_validation = datetime.datetime.fromisoformat(date_validation_str)
|
||||||
|
except ValueError:
|
||||||
|
return json_error(API_CLIENT_ERROR, "invalid date string")
|
||||||
|
if parcours_id is not None:
|
||||||
|
parcours: ApcParcours = ApcParcours.query.get_or_404(parcours_id)
|
||||||
|
if parcours.referentiel_id != ue1.niveau_competence.competence.referentiel_id:
|
||||||
|
return json_error(API_CLIENT_ERROR, "niveau et parcours incompatibles")
|
||||||
|
|
||||||
|
# Une validation pour ce niveau de compétence existe-elle ?
|
||||||
|
validation = (
|
||||||
|
ApcValidationRCUE.query.filter_by(etudid=etudid)
|
||||||
|
.join(UniteEns, UniteEns.id == ApcValidationRCUE.ue2_id)
|
||||||
|
.filter_by(niveau_competence_id=ue2.niveau_competence_id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if validation:
|
||||||
|
validation.code = code
|
||||||
|
validation.date = date_validation
|
||||||
|
validation.formsemestre_id = formsemestre_id
|
||||||
|
validation.parcours_id = parcours_id
|
||||||
|
validation.ue1_id = ue1_id
|
||||||
|
validation.ue2_id = ue2_id
|
||||||
|
log(f"updating {validation}")
|
||||||
|
Scolog.logdb(
|
||||||
|
method="validation_rcue_record",
|
||||||
|
etudid=etudid,
|
||||||
|
msg=f"Mise à jour {validation}",
|
||||||
|
commit=False,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
validation = ApcValidationRCUE(
|
||||||
|
code=code,
|
||||||
|
date=date_validation,
|
||||||
|
etudid=etudid,
|
||||||
|
formsemestre_id=formsemestre_id,
|
||||||
|
parcours_id=parcours_id,
|
||||||
|
ue1_id=ue1_id,
|
||||||
|
ue2_id=ue2_id,
|
||||||
|
)
|
||||||
|
log(f"recording {validation}")
|
||||||
|
Scolog.logdb(
|
||||||
|
method="validation_rcue_record",
|
||||||
|
etudid=etudid,
|
||||||
|
msg=f"Enregistrement {validation}",
|
||||||
|
commit=False,
|
||||||
|
)
|
||||||
|
db.session.add(validation)
|
||||||
|
db.session.commit()
|
||||||
|
return validation.to_dict()
|
||||||
|
|
||||||
|
|
||||||
@bp.route(
|
@bp.route(
|
||||||
"/etudiant/<int:etudid>/jury/validation_rcue/<int:validation_id>/delete",
|
"/etudiant/<int:etudid>/jury/validation_rcue/<int:validation_id>/delete",
|
||||||
methods=["POST"],
|
methods=["POST"],
|
||||||
|
@ -282,7 +282,7 @@ def partition_remove_etud(partition_id: int, etudid: int):
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoEtudChangeGroups)
|
@permission_required(Permission.ScoEtudChangeGroups)
|
||||||
@as_json
|
@as_json
|
||||||
def group_create(partition_id: int):
|
def group_create(partition_id: int): # partition-group-create
|
||||||
"""Création d'un groupe dans une partition
|
"""Création d'un groupe dans une partition
|
||||||
|
|
||||||
The request content type should be "application/json":
|
The request content type should be "application/json":
|
||||||
|
@ -18,7 +18,7 @@ from operator import attrgetter
|
|||||||
|
|
||||||
from flask import g, url_for
|
from flask import g, url_for
|
||||||
|
|
||||||
from app import db
|
from app import db, log
|
||||||
from app.comp.res_but import ResultatsSemestreBUT
|
from app.comp.res_but import ResultatsSemestreBUT
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
|
|
||||||
@ -41,7 +41,8 @@ from app.models.formsemestre import FormSemestre, FormSemestreInscription
|
|||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
from app.models.validations import ScolarFormSemestreValidation
|
from app.models.validations import ScolarFormSemestreValidation
|
||||||
from app.scodoc import codes_cursus as sco_codes
|
from app.scodoc import codes_cursus as sco_codes
|
||||||
from app.scodoc.codes_cursus import code_ue_validant, CODES_UE_VALIDES
|
from app.scodoc.codes_cursus import code_ue_validant, CODES_UE_VALIDES, UE_STANDARD
|
||||||
|
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.scodoc.sco_exceptions import ScoNoReferentielCompetences, ScoValueError
|
from app.scodoc.sco_exceptions import ScoNoReferentielCompetences, ScoValueError
|
||||||
|
|
||||||
@ -479,3 +480,122 @@ def formsemestre_warning_apc_setup(
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def ue_associee_au_niveau_du_parcours(
|
||||||
|
ues_possibles: list[UniteEns], niveau: ApcNiveau, sem_name: str = "S"
|
||||||
|
) -> UniteEns:
|
||||||
|
"L'UE associée à ce niveau, ou None"
|
||||||
|
ues = [ue for ue in ues_possibles if ue.niveau_competence_id == niveau.id]
|
||||||
|
if len(ues) > 1:
|
||||||
|
# plusieurs UEs associées à ce niveau: élimine celles sans parcours
|
||||||
|
ues_pair_avec_parcours = [ue for ue in ues if ue.parcours]
|
||||||
|
if ues_pair_avec_parcours:
|
||||||
|
ues = ues_pair_avec_parcours
|
||||||
|
if len(ues) > 1:
|
||||||
|
log(f"_niveau_ues: {len(ues)} associées au niveau {niveau} / {sem_name}")
|
||||||
|
return ues[0] if ues else None
|
||||||
|
|
||||||
|
|
||||||
|
def parcour_formation_competences(parcour: ApcParcours, formation: Formation) -> list:
|
||||||
|
"""
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'competence' : ApcCompetence,
|
||||||
|
'niveaux' : {
|
||||||
|
1 : { ... },
|
||||||
|
2 : { ... },
|
||||||
|
3 : {
|
||||||
|
'niveau' : ApcNiveau,
|
||||||
|
'ue_impair' : UniteEns, # actuellement associée
|
||||||
|
'ues_impair' : list[UniteEns], # choix possibles
|
||||||
|
'ue_pair' : UniteEns,
|
||||||
|
'ues_pair' : list[UniteEns],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
refcomp: ApcReferentielCompetences = formation.referentiel_competence
|
||||||
|
|
||||||
|
def _niveau_ues(competence: ApcCompetence, annee: int) -> dict:
|
||||||
|
"""niveau et ues pour cette compétence de cette année du parcours.
|
||||||
|
Si parcour est None, les niveaux du tronc commun
|
||||||
|
"""
|
||||||
|
if parcour is not None:
|
||||||
|
# L'étudiant est inscrit à un parcours: cherche les niveaux
|
||||||
|
niveaux = ApcNiveau.niveaux_annee_de_parcours(
|
||||||
|
parcour, annee, competence=competence
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# sans parcours, on cherche les niveaux du Tronc Commun de cette année
|
||||||
|
niveaux = [
|
||||||
|
niveau
|
||||||
|
for niveau in refcomp.get_niveaux_by_parcours(annee)[1]["TC"]
|
||||||
|
if niveau.competence_id == competence.id
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(niveaux) > 0:
|
||||||
|
if len(niveaux) > 1:
|
||||||
|
log(
|
||||||
|
f"""_niveau_ues: plus d'un niveau pour {competence}
|
||||||
|
annee {annee} {("parcours " + parcour.code) if parcour else ""}"""
|
||||||
|
)
|
||||||
|
niveau = niveaux[0]
|
||||||
|
elif len(niveaux) == 0:
|
||||||
|
return {
|
||||||
|
"niveau": None,
|
||||||
|
"ue_pair": None,
|
||||||
|
"ue_impair": None,
|
||||||
|
"ues_pair": [],
|
||||||
|
"ues_impair": [],
|
||||||
|
}
|
||||||
|
# Toutes les UEs de la formation dans ce parcours ou tronc commun
|
||||||
|
ues = [
|
||||||
|
ue
|
||||||
|
for ue in formation.ues
|
||||||
|
if (
|
||||||
|
(not ue.parcours)
|
||||||
|
or (parcour is not None and (parcour.id in (p.id for p in ue.parcours)))
|
||||||
|
)
|
||||||
|
and ue.type == UE_STANDARD
|
||||||
|
]
|
||||||
|
ues_pair_possibles = [ue for ue in ues if ue.semestre_idx == (2 * annee)]
|
||||||
|
ues_impair_possibles = [ue for ue in ues if ue.semestre_idx == (2 * annee - 1)]
|
||||||
|
|
||||||
|
# UE associée au niveau dans ce parcours
|
||||||
|
ue_pair = ue_associee_au_niveau_du_parcours(
|
||||||
|
ues_pair_possibles, niveau, f"S{2*annee}"
|
||||||
|
)
|
||||||
|
ue_impair = ue_associee_au_niveau_du_parcours(
|
||||||
|
ues_impair_possibles, niveau, f"S{2*annee-1}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"niveau": niveau,
|
||||||
|
"ue_pair": ue_pair,
|
||||||
|
"ues_pair": [
|
||||||
|
ue
|
||||||
|
for ue in ues_pair_possibles
|
||||||
|
if (not ue.niveau_competence) or ue.niveau_competence.id == niveau.id
|
||||||
|
],
|
||||||
|
"ue_impair": ue_impair,
|
||||||
|
"ues_impair": [
|
||||||
|
ue
|
||||||
|
for ue in ues_impair_possibles
|
||||||
|
if (not ue.niveau_competence) or ue.niveau_competence.id == niveau.id
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
competences = [
|
||||||
|
{
|
||||||
|
"competence": competence,
|
||||||
|
"niveaux": {annee: _niveau_ues(competence, annee) for annee in (1, 2, 3)},
|
||||||
|
}
|
||||||
|
for competence in (
|
||||||
|
parcour.query_competences()
|
||||||
|
if parcour
|
||||||
|
else refcomp.competences.order_by(ApcCompetence.numero)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
return competences
|
||||||
|
115
app/but/validations_view.py
Normal file
115
app/but/validations_view.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
##############################################################################
|
||||||
|
# ScoDoc
|
||||||
|
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
|
||||||
|
# See LICENSE
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""Jury édition manuelle des décisions (correction d'erreurs, parcours hors normes)
|
||||||
|
|
||||||
|
Non spécifique au BUT.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import render_template
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from app import log
|
||||||
|
from app.but import cursus_but
|
||||||
|
from app.models import (
|
||||||
|
ApcCompetence,
|
||||||
|
ApcNiveau,
|
||||||
|
ApcReferentielCompetences,
|
||||||
|
# ApcValidationAnnee, # TODO
|
||||||
|
ApcValidationRCUE,
|
||||||
|
Formation,
|
||||||
|
FormSemestre,
|
||||||
|
Identite,
|
||||||
|
UniteEns,
|
||||||
|
# ScolarAutorisationInscription,
|
||||||
|
ScolarFormSemestreValidation,
|
||||||
|
)
|
||||||
|
from app.scodoc import codes_cursus
|
||||||
|
from app.scodoc.sco_exceptions import ScoNoReferentielCompetences
|
||||||
|
from app.views import ScoData
|
||||||
|
|
||||||
|
|
||||||
|
def validation_rcues(etud: Identite, formsemestre: FormSemestre, edit: bool = False):
|
||||||
|
"""Page de saisie des décisions de RCUEs "antérieures"
|
||||||
|
On peut l'utiliser pour saisir la validation de n'importe quel RCUE
|
||||||
|
d'une année antérieure et de la formation du formsemestre indiqué.
|
||||||
|
"""
|
||||||
|
formation: Formation = formsemestre.formation
|
||||||
|
refcomp = formation.referentiel_competence
|
||||||
|
if refcomp is None:
|
||||||
|
raise ScoNoReferentielCompetences(formation=formation)
|
||||||
|
parcour = formsemestre.etuds_inscriptions[etud.id].parcour
|
||||||
|
# Si non inscrit à un parcours, prend toutes les compétences
|
||||||
|
competences_parcour = cursus_but.parcour_formation_competences(parcour, formation)
|
||||||
|
|
||||||
|
ue_validation_by_niveau = get_ue_validation_by_niveau(refcomp, etud)
|
||||||
|
rcue_validation_by_niveau = get_rcue_validation_by_niveau(refcomp, etud)
|
||||||
|
return render_template(
|
||||||
|
"but/validation_rcues.j2",
|
||||||
|
competences_parcour=competences_parcour,
|
||||||
|
edit=edit,
|
||||||
|
formation=formation,
|
||||||
|
parcour=parcour,
|
||||||
|
rcue_validation_by_niveau=rcue_validation_by_niveau,
|
||||||
|
rcue_codes=sorted(codes_cursus.CODES_JURY_RCUE),
|
||||||
|
sco=ScoData(formsemestre=formsemestre, etud=etud),
|
||||||
|
title=f"{formation.acronyme} - Niveaux et UEs",
|
||||||
|
ue_validation_by_niveau=ue_validation_by_niveau,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_ue_validation_by_niveau(
|
||||||
|
refcomp: ApcReferentielCompetences, etud: Identite
|
||||||
|
) -> dict[tuple[int, str], ScolarFormSemestreValidation]:
|
||||||
|
"""Les validations d'UEs de cet étudiant liées à ce référentiel de compétences.
|
||||||
|
Pour chaque niveau / pair ou impair, choisi la "meilleure" validation
|
||||||
|
"""
|
||||||
|
validations: list[ScolarFormSemestreValidation] = (
|
||||||
|
ScolarFormSemestreValidation.query.filter_by(etudid=etud.id)
|
||||||
|
.join(UniteEns)
|
||||||
|
.join(ApcNiveau)
|
||||||
|
.join(ApcCompetence)
|
||||||
|
.filter_by(referentiel_id=refcomp.id)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
# La meilleure validation pour chaque UE
|
||||||
|
ue_validation_by_niveau = {} # { (niveau_id, pair|impair) : validation }
|
||||||
|
for validation in validations:
|
||||||
|
if validation.ue.niveau_competence is None:
|
||||||
|
log(
|
||||||
|
f"""validation_rcues: ignore validation d'UE {
|
||||||
|
validation.ue.id} pas de niveau de competence"""
|
||||||
|
)
|
||||||
|
key = (
|
||||||
|
validation.ue.niveau_competence.id,
|
||||||
|
"impair" if validation.ue.semestre_idx % 2 else "pair",
|
||||||
|
)
|
||||||
|
existing = ue_validation_by_niveau.get(key, None)
|
||||||
|
if (not existing) or (
|
||||||
|
codes_cursus.BUT_CODES_ORDER[existing.code]
|
||||||
|
< codes_cursus.BUT_CODES_ORDER[validation.code]
|
||||||
|
):
|
||||||
|
ue_validation_by_niveau[key] = validation
|
||||||
|
return ue_validation_by_niveau
|
||||||
|
|
||||||
|
|
||||||
|
def get_rcue_validation_by_niveau(
|
||||||
|
refcomp: ApcReferentielCompetences, etud: Identite
|
||||||
|
) -> dict[int, ApcValidationRCUE]:
|
||||||
|
"""Les validations d'UEs de cet étudiant liées à ce référentiel de compétences.
|
||||||
|
Pour chaque niveau / pair ou impair, choisi la "meilleure" validation
|
||||||
|
"""
|
||||||
|
validations: list[ApcValidationRCUE] = (
|
||||||
|
ApcValidationRCUE.query.filter_by(etudid=etud.id)
|
||||||
|
.join(UniteEns, UniteEns.id == ApcValidationRCUE.ue2_id)
|
||||||
|
.join(ApcNiveau, UniteEns.niveau_competence_id == ApcNiveau.id)
|
||||||
|
.join(ApcCompetence)
|
||||||
|
.filter_by(referentiel_id=refcomp.id)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
validation.ue2.niveau_competence.id: validation for validation in validations
|
||||||
|
}
|
@ -204,6 +204,7 @@ CODES_UE_VALIDES = CODES_UE_VALIDES_DE_DROIT | {ADJ, ADJR, ADSUP}
|
|||||||
CODES_UE_CAPITALISANTS = {ADM}
|
CODES_UE_CAPITALISANTS = {ADM}
|
||||||
"UE capitalisée"
|
"UE capitalisée"
|
||||||
|
|
||||||
|
CODES_JURY_RCUE = CODES_JURY_UE # tous les codes d'UEs sont utilisables pour les RCUEs
|
||||||
CODES_RCUE_VALIDES_DE_DROIT = {ADM, CMP}
|
CODES_RCUE_VALIDES_DE_DROIT = {ADM, CMP}
|
||||||
CODES_RCUE_VALIDES = CODES_RCUE_VALIDES_DE_DROIT | {ADJ, ADSUP}
|
CODES_RCUE_VALIDES = CODES_RCUE_VALIDES_DE_DROIT | {ADJ, ADSUP}
|
||||||
"Niveau RCUE validé"
|
"Niveau RCUE validé"
|
||||||
|
@ -313,9 +313,26 @@ def ficheEtud(etudid=None):
|
|||||||
)
|
)
|
||||||
info[
|
info[
|
||||||
"link_bul_pdf"
|
"link_bul_pdf"
|
||||||
] = f"""<span class="link_bul_pdf"><a class="stdlink" href="{
|
] = f"""
|
||||||
|
<span class="link_bul_pdf">
|
||||||
|
<a class="stdlink" href="{
|
||||||
url_for("notes.etud_bulletins_pdf", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
url_for("notes.etud_bulletins_pdf", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||||
}">tous les bulletins</a></span>"""
|
}">tous les bulletins</a>
|
||||||
|
</span>
|
||||||
|
"""
|
||||||
|
last_formsemestre: FormSemestre = db.session.get(
|
||||||
|
FormSemestre, info["sems"][0]["formsemestre_id"]
|
||||||
|
)
|
||||||
|
if last_formsemestre.formation.is_apc() and last_formsemestre.semestre_id > 2:
|
||||||
|
info[
|
||||||
|
"link_bul_pdf"
|
||||||
|
] += f"""
|
||||||
|
<span class="link_bul_pdf">
|
||||||
|
<a class="stdlink" href="{
|
||||||
|
url_for("notes.validation_rcues", scodoc_dept=g.scodoc_dept, etudid=etudid, formsemestre_id=last_formsemestre.id)
|
||||||
|
}">Visualiser les compétences BUT</a>
|
||||||
|
</span>
|
||||||
|
"""
|
||||||
if authuser.has_permission(Permission.ScoEtudInscrit):
|
if authuser.has_permission(Permission.ScoEtudInscrit):
|
||||||
info[
|
info[
|
||||||
"link_inscrire_ailleurs"
|
"link_inscrire_ailleurs"
|
||||||
|
@ -144,6 +144,10 @@ div.ue.pair {
|
|||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.rcue {
|
||||||
|
grid-column: 1 / span 2;
|
||||||
|
}
|
||||||
|
|
||||||
/* ne fonctionne pas
|
/* ne fonctionne pas
|
||||||
option.non_associe {
|
option.non_associe {
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
@ -154,3 +158,8 @@ option.non_associe {
|
|||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select.validation_rcue {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 32px;
|
||||||
|
}
|
||||||
|
218
app/templates/but/validation_rcues.j2
Normal file
218
app/templates/but/validation_rcues.j2
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
{% extends "sco_page.j2" %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
{{super()}}
|
||||||
|
<link href="{{scu.STATIC_DIR}}/css/refcomp_parcours_niveaux.css" rel="stylesheet" type="text/css" />
|
||||||
|
<link href="{{scu.STATIC_DIR}}/css/parcour_formation.css" rel="stylesheet" type="text/css" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% macro show_ue(niv, sem="pair", sem_idx=0) -%}
|
||||||
|
{% if niv['niveau'] %}
|
||||||
|
{# Affiche l'UE et sa décision de jury #}
|
||||||
|
{% if niv['ue_'+sem] %}
|
||||||
|
{{ niv['ue_'+sem].acronyme }}
|
||||||
|
{% set validation = ue_validation_by_niveau.get((niv['niveau'].id, sem)) %}
|
||||||
|
<div class="ue_validation_code with_scoplement">
|
||||||
|
<div>
|
||||||
|
{% if validation %}
|
||||||
|
<b>{{validation.code}}</b>
|
||||||
|
{% else %}
|
||||||
|
-
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="scoplement">
|
||||||
|
{% if validation %}
|
||||||
|
<div>Validation de {{niv['ue_'+sem].acronyme}}</div>
|
||||||
|
<div>Jury de {{validation.formsemestre.titre_annee()
|
||||||
|
if validation.formsemestre else "-"}}</div>
|
||||||
|
<div>enregistrée le {{
|
||||||
|
validation.event_date.strftime("%d/%m/%Y à %H:%M")
|
||||||
|
if validation.event_date else "-"
|
||||||
|
}}</div>
|
||||||
|
{% else %}
|
||||||
|
pas de décision de jury enregistrée pour cette UE
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<span class="fontred" title="Pas d'UE associée à ce niveau !">{{scu.EMO_WARNING|safe}} non associé</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% block app_content %}
|
||||||
|
|
||||||
|
{# Résultats dans le parcours #}
|
||||||
|
<div class="parcour_formation">
|
||||||
|
|
||||||
|
<div class="titre_parcours">
|
||||||
|
Validations de {{sco.etud.html_link_fiche()|safe}}
|
||||||
|
{% if parcour %}
|
||||||
|
parcours {{parcour.code}} « {{parcour.libelle}} »
|
||||||
|
{% else %}
|
||||||
|
non inscrit à un parcours de la spécialité
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% for comp in competences_parcour %}
|
||||||
|
{% set color_idx = 1 + loop.index0 % 6 %}
|
||||||
|
<div class="competence comp-c{{color_idx}}">
|
||||||
|
<div class="titre_competence tc">
|
||||||
|
Compétence {{comp['competence'].numero}} : {{comp['competence'].titre}}
|
||||||
|
</div>
|
||||||
|
<div class="niveaux">
|
||||||
|
{% for annee, niv in comp['niveaux'].items() %}
|
||||||
|
<div class="niveau comp-c{{color_idx}}-{{annee}}"
|
||||||
|
style="--color: var(--col-c{{color_idx}}-{{annee}});">
|
||||||
|
<div class="titre_niveau n{{annee}}">
|
||||||
|
<span class="parcs">
|
||||||
|
{% if niv['niveau'].is_tronc_commun %}
|
||||||
|
<span class="parc">TC</span>
|
||||||
|
{% elif niv['niveau'].parcours|length > 1 %}
|
||||||
|
<span class="parc">
|
||||||
|
{% set virg = joiner(", ") %}
|
||||||
|
{% for p in niv['niveau'].parcours %}
|
||||||
|
{{ virg() }}{{p.code}}
|
||||||
|
{% endfor %}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
{{niv['niveau'].libelle if niv['niveau'] else ''}}
|
||||||
|
</div>
|
||||||
|
<div class="rcue">
|
||||||
|
<div class="rcue_validation_code with_scoplement">
|
||||||
|
{% set validation = rcue_validation_by_niveau.get(niv['niveau'].id) %}
|
||||||
|
{% if validation %}
|
||||||
|
<div>
|
||||||
|
RCUE enregistré <b>{{validation.code}}</b>
|
||||||
|
{% if niv['niveau'] and edit %}
|
||||||
|
{% if not (niv['ue_pair'] and niv['ue_impair']) %}
|
||||||
|
<span title="UEs manquantes">⛔</span>
|
||||||
|
{% else %}
|
||||||
|
<select class="validation_rcue" name="ue_niv_{{niv['niveau'].id}}" id="ue_niv_{{niv['niveau'].id}}"
|
||||||
|
onchange="record_rcue_validation(event,
|
||||||
|
{{niv['niveau'].id}},
|
||||||
|
);"
|
||||||
|
|
||||||
|
data-ue1_id="{{niv['ue_impair'].id}}"
|
||||||
|
data-ue2_id="{{niv['ue_pair'].id}}"
|
||||||
|
>
|
||||||
|
{% for code in rcue_codes %}
|
||||||
|
<option value="{{code}}"
|
||||||
|
{% if validation and validation.code == code -%}
|
||||||
|
selected
|
||||||
|
{%- endif %}
|
||||||
|
>{{code}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="scoplement">
|
||||||
|
<div>Validation du RCUE</div>
|
||||||
|
<div>enregistrée le {{
|
||||||
|
validation.date.strftime("%d/%m/%Y à %H:%M")
|
||||||
|
if validation.date else "-"
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div>par le jury de {{validation.formsemestre.titre_annee()
|
||||||
|
if validation.formsemestre else "-"}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ue impair u{{annee}}1">
|
||||||
|
{{ show_ue(niv, "impair", 2*annee-1) }}
|
||||||
|
</div>
|
||||||
|
<div class="ue pair u{{annee}}1">
|
||||||
|
{{ show_ue(niv, "pair", 2*annee) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if sco.formsemestre.can_edit_jury() %}
|
||||||
|
<div style="padding-bottom: 16px;">
|
||||||
|
{% if edit %}
|
||||||
|
<a class="stdlink" href="{{url_for('notes.validation_rcues',
|
||||||
|
scodoc_dept=g.scodoc_dept, formsemestre_id=sco.formsemestre.id, etudid=sco.etud.id
|
||||||
|
)}}">quitter le mode édition des RCUEs</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="stdlink" href="{{url_for('notes.validation_rcues_edit',
|
||||||
|
scodoc_dept=g.scodoc_dept, formsemestre_id=sco.formsemestre.id, etudid=sco.etud.id
|
||||||
|
)}}">éditer les décisions d'RCUE antérieurs</a>
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="help">
|
||||||
|
|
||||||
|
<p>Cette page montre les validations d'UEs et de niveaux de compétences (RCUEs)
|
||||||
|
de {{sco.etud.html_link_fiche()|safe}}
|
||||||
|
dans le
|
||||||
|
{%if parcour %}
|
||||||
|
parcours <span class="parc">{{parcour.code}}</span>
|
||||||
|
{% else %}
|
||||||
|
tronc commun
|
||||||
|
{% endif %}
|
||||||
|
du référentiel de compétence {{formation.referentiel_competence.specialite}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Seuls les UEs et niveaux de ce référentiel sont montrés. Si le référentiel a
|
||||||
|
changé, enregistrer des validations "antérieures".
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Le symbole <span class="parc">TC</span> désigne un niveau du tronc commun
|
||||||
|
(c'est à dire présent dans tous les parcours de la spécialité). </p>
|
||||||
|
|
||||||
|
{% if edit %}
|
||||||
|
<p>Les validations sont enregistrées au fur et à mesure.</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function record_rcue_validation(event, niveau_id) {
|
||||||
|
let code = event.target.value;
|
||||||
|
let ue1_id = event.target.dataset.ue1_id;
|
||||||
|
let ue2_id = event.target.dataset.ue2_id;
|
||||||
|
const record_url = '{{
|
||||||
|
url_for(
|
||||||
|
"apiweb.validation_rcue_record",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
etudid=sco.etud.id
|
||||||
|
)
|
||||||
|
}}';
|
||||||
|
|
||||||
|
fetch(record_url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify( {
|
||||||
|
code : code,
|
||||||
|
ue1_id : ue1_id,
|
||||||
|
ue2_id : ue2_id,
|
||||||
|
// Optionnel:
|
||||||
|
formsemestre_id : {{sco.formsemestre.id}},
|
||||||
|
parcours_id : {{parcour.id if parcour else "null"}},
|
||||||
|
} )
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status) {
|
||||||
|
/* sco_message(data.message); */
|
||||||
|
/* revert menu to initial state */
|
||||||
|
event.target.value = event.target.dataset.ue_id;
|
||||||
|
}
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -50,7 +50,7 @@ def close_dept_db_connection(arg):
|
|||||||
class ScoData:
|
class ScoData:
|
||||||
"""Classe utilisée pour passer des valeurs aux vues (templates)"""
|
"""Classe utilisée pour passer des valeurs aux vues (templates)"""
|
||||||
|
|
||||||
def __init__(self, etud=None, formsemestre=None):
|
def __init__(self, etud: Identite = None, formsemestre: FormSemestre = None):
|
||||||
# Champs utilisés par toutes les pages ScoDoc (sidebar, en-tête)
|
# Champs utilisés par toutes les pages ScoDoc (sidebar, en-tête)
|
||||||
self.Permission = Permission
|
self.Permission = Permission
|
||||||
self.scu = scu
|
self.scu = scu
|
||||||
@ -96,6 +96,7 @@ class ScoData:
|
|||||||
self.sem_menu_bar = sco_formsemestre_status.formsemestre_status_menubar(
|
self.sem_menu_bar = sco_formsemestre_status.formsemestre_status_menubar(
|
||||||
self.sem
|
self.sem
|
||||||
)
|
)
|
||||||
|
self.formsemestre = formsemestre
|
||||||
# --- Préférences
|
# --- Préférences
|
||||||
# prefs fallback to global pref if sem is None:
|
# prefs fallback to global pref if sem is None:
|
||||||
if formsemestre:
|
if formsemestre:
|
||||||
|
@ -30,7 +30,8 @@ Emmanuel Viennet, 2023
|
|||||||
|
|
||||||
from flask import flash, g, redirect, render_template, request, url_for
|
from flask import flash, g, redirect, render_template, request, url_for
|
||||||
|
|
||||||
from app import db, log
|
from app import db
|
||||||
|
from app.but import cursus_but, validations_view
|
||||||
from app.decorators import (
|
from app.decorators import (
|
||||||
scodoc,
|
scodoc,
|
||||||
permission_required,
|
permission_required,
|
||||||
@ -39,16 +40,16 @@ from app.decorators import (
|
|||||||
from app.forms.formation.ue_parcours_ects import UEParcoursECTSForm
|
from app.forms.formation.ue_parcours_ects import UEParcoursECTSForm
|
||||||
|
|
||||||
from app.models import (
|
from app.models import (
|
||||||
ApcCompetence,
|
|
||||||
ApcNiveau,
|
|
||||||
ApcParcours,
|
ApcParcours,
|
||||||
ApcReferentielCompetences,
|
ApcReferentielCompetences,
|
||||||
Formation,
|
Formation,
|
||||||
|
FormSemestre,
|
||||||
|
Identite,
|
||||||
UniteEns,
|
UniteEns,
|
||||||
)
|
)
|
||||||
from app.scodoc.codes_cursus import UE_STANDARD
|
from app.scodoc.codes_cursus import UE_STANDARD
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
from app.scodoc.sco_exceptions import ScoPermissionDenied, ScoValueError
|
||||||
from app.views import notes_bp as bp
|
from app.views import notes_bp as bp
|
||||||
from app.views import ScoData
|
from app.views import ScoData
|
||||||
|
|
||||||
@ -74,7 +75,9 @@ def parcour_formation(formation_id: int, parcour_id: int = None) -> str:
|
|||||||
raise ScoValueError("parcours invalide ou hors référentiel de formation")
|
raise ScoValueError("parcours invalide ou hors référentiel de formation")
|
||||||
|
|
||||||
competences_parcour = (
|
competences_parcour = (
|
||||||
parcour_formation_competences(parcour, formation) if parcour else None
|
cursus_but.parcour_formation_competences(parcour, formation)
|
||||||
|
if parcour
|
||||||
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
@ -87,103 +90,32 @@ def parcour_formation(formation_id: int, parcour_id: int = None) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ue_associee_au_niveau_du_parcours(
|
@bp.route(
|
||||||
ues_possibles: list[UniteEns], niveau: ApcNiveau, sem_name: str = "S"
|
"/validation_rcues/<int:formsemestre_id>/<int:etudid>/edit",
|
||||||
) -> UniteEns:
|
defaults={"edit": True},
|
||||||
"L'UE associée à ce niveau, ou None"
|
endpoint="validation_rcues_edit",
|
||||||
ues = [ue for ue in ues_possibles if ue.niveau_competence_id == niveau.id]
|
)
|
||||||
if len(ues) > 1:
|
@bp.route("/validation_rcues/<int:formsemestre_id>/<int:etudid>")
|
||||||
# plusieurs UEs associées à ce niveau: élimine celles sans parcours
|
@scodoc
|
||||||
ues_pair_avec_parcours = [ue for ue in ues if ue.parcours]
|
@permission_required(Permission.ScoView)
|
||||||
if ues_pair_avec_parcours:
|
def validation_rcues(
|
||||||
ues = ues_pair_avec_parcours
|
formsemestre_id: int, etudid: int = None, edit: bool = False
|
||||||
if len(ues) > 1:
|
) -> str:
|
||||||
log(f"_niveau_ues: {len(ues)} associées au niveau {niveau} / {sem_name}")
|
"""Visualisation des résultats UEs et RCUEs d'un étudiant
|
||||||
return ues[0] if ues else None
|
et saisie des validation de RCUE antérieures.
|
||||||
|
|
||||||
|
|
||||||
def parcour_formation_competences(parcour: ApcParcours, formation: Formation) -> list:
|
|
||||||
"""
|
"""
|
||||||
[
|
etud: Identite = Identite.query.get_or_404(etudid)
|
||||||
{
|
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
'competence' : ApcCompetence,
|
if edit: # check permission
|
||||||
'niveaux' : {
|
if not formsemestre.can_edit_jury():
|
||||||
1 : { ... },
|
raise ScoPermissionDenied(
|
||||||
2 : { ... },
|
dest_url=url_for(
|
||||||
3 : {
|
"notes.formsemestre_status",
|
||||||
'niveau' : ApcNiveau,
|
scodoc_dept=g.scodoc_dept,
|
||||||
'ue_impair' : UniteEns, # actuellement associée
|
formsemestre_id=formsemestre_id,
|
||||||
'ues_impair' : list[UniteEns], # choix possibles
|
|
||||||
'ue_pair' : UniteEns,
|
|
||||||
'ues_pair' : list[UniteEns],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _niveau_ues(competence: ApcCompetence, annee: int) -> dict:
|
|
||||||
"niveau et ues pour cette compétence de cette année du parcours"
|
|
||||||
niveaux = ApcNiveau.niveaux_annee_de_parcours(
|
|
||||||
parcour, annee, competence=competence
|
|
||||||
)
|
)
|
||||||
if len(niveaux) > 0:
|
|
||||||
if len(niveaux) > 1:
|
|
||||||
log(
|
|
||||||
f"""_niveau_ues: plus d'un niveau pour {competence}
|
|
||||||
annee {annee} parcours {parcour.code}"""
|
|
||||||
)
|
)
|
||||||
niveau = niveaux[0]
|
return validations_view.validation_rcues(etud, formsemestre, edit)
|
||||||
elif len(niveaux) == 0:
|
|
||||||
return {
|
|
||||||
"niveau": None,
|
|
||||||
"ue_pair": None,
|
|
||||||
"ue_impair": None,
|
|
||||||
"ues_pair": [],
|
|
||||||
"ues_impair": [],
|
|
||||||
}
|
|
||||||
# Toutes les UEs de la formation dans ce parcours ou tronc commun
|
|
||||||
ues = [
|
|
||||||
ue
|
|
||||||
for ue in formation.ues
|
|
||||||
if ((not ue.parcours) or (parcour.id in (p.id for p in ue.parcours)))
|
|
||||||
and ue.type == UE_STANDARD
|
|
||||||
]
|
|
||||||
ues_pair_possibles = [ue for ue in ues if ue.semestre_idx == (2 * annee)]
|
|
||||||
ues_impair_possibles = [ue for ue in ues if ue.semestre_idx == (2 * annee - 1)]
|
|
||||||
|
|
||||||
# UE associée au niveau dans ce parcours
|
|
||||||
ue_pair = ue_associee_au_niveau_du_parcours(
|
|
||||||
ues_pair_possibles, niveau, f"S{2*annee}"
|
|
||||||
)
|
|
||||||
ue_impair = ue_associee_au_niveau_du_parcours(
|
|
||||||
ues_impair_possibles, niveau, f"S{2*annee-1}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"niveau": niveau,
|
|
||||||
"ue_pair": ue_pair,
|
|
||||||
"ues_pair": [
|
|
||||||
ue
|
|
||||||
for ue in ues_pair_possibles
|
|
||||||
if (not ue.niveau_competence) or ue.niveau_competence.id == niveau.id
|
|
||||||
],
|
|
||||||
"ue_impair": ue_impair,
|
|
||||||
"ues_impair": [
|
|
||||||
ue
|
|
||||||
for ue in ues_impair_possibles
|
|
||||||
if (not ue.niveau_competence) or ue.niveau_competence.id == niveau.id
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
competences = [
|
|
||||||
{
|
|
||||||
"competence": competence,
|
|
||||||
"niveaux": {annee: _niveau_ues(competence, annee) for annee in (1, 2, 3)},
|
|
||||||
}
|
|
||||||
for competence in parcour.query_competences()
|
|
||||||
]
|
|
||||||
return competences
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/ue_parcours_ects/<int:ue_id>", methods=["GET", "POST"])
|
@bp.route("/ue_parcours_ects/<int:ue_id>", methods=["GET", "POST"])
|
||||||
|
@ -16,8 +16,9 @@ Utilisation :
|
|||||||
Lancer :
|
Lancer :
|
||||||
pytest tests/api/test_api_jury.py
|
pytest tests/api/test_api_jury.py
|
||||||
"""
|
"""
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from app.scodoc import sco_utils as scu
|
||||||
from tests.api.setup_test_api import (
|
from tests.api.setup_test_api import (
|
||||||
API_URL,
|
API_URL,
|
||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
@ -37,4 +38,29 @@ def test_jury_decisions(api_headers):
|
|||||||
decisions_jury = GET(
|
decisions_jury = GET(
|
||||||
f"/formsemestre/{formsemestre_id}/decisions_jury", headers=api_headers
|
f"/formsemestre/{formsemestre_id}/decisions_jury", headers=api_headers
|
||||||
)
|
)
|
||||||
|
assert len(etudiants) > 0
|
||||||
assert len(etudiants) == len(decisions_jury)
|
assert len(etudiants) == len(decisions_jury)
|
||||||
|
# TODO La suite de ce test est a compléter: il faut modifier le formation test RT
|
||||||
|
# pour avoir au moins le S2 et le S2: actuellement seulement le S1
|
||||||
|
# # Récupère la formation de ce semestre pour avoir les UEs
|
||||||
|
# r = requests.get(
|
||||||
|
# API_URL + "/formation/1/export",
|
||||||
|
# headers=api_headers,
|
||||||
|
# verify=CHECK_CERTIFICATE,
|
||||||
|
# timeout=scu.SCO_TEST_API_TIMEOUT,
|
||||||
|
# )
|
||||||
|
# assert r.status_code == 200
|
||||||
|
# export_formation = r.json()
|
||||||
|
# ues = export_formation["ue"]
|
||||||
|
# # Enregistre une validation d'RCUE
|
||||||
|
# etudid = etudiants[0]["id"]
|
||||||
|
# validation = POST_JSON(
|
||||||
|
# f"/etudiant/{etudid}/jury/validation_rcue/record",
|
||||||
|
# data={
|
||||||
|
# "code": "ADM",
|
||||||
|
# "ue1_id": XXX,
|
||||||
|
# "ue2_id": XXX,
|
||||||
|
# },
|
||||||
|
# headers=api_headers,
|
||||||
|
# )
|
||||||
|
# assert validation
|
||||||
|
Loading…
Reference in New Issue
Block a user