BUT: jury: validation des niveaux inférieurs. WIP

This commit is contained in:
Emmanuel Viennet 2023-06-15 08:49:05 +02:00
parent 73023b7806
commit e46ae76399
11 changed files with 414 additions and 94 deletions

View File

@ -104,7 +104,7 @@ class EtudCursusBUT:
self.parcour: ApcParcours = self.inscriptions[-1].parcour
"Le parcours à valider: celui du DERNIER semestre suivi (peut être None)"
self.niveaux_by_annee = {}
"{ annee : liste des niveaux à valider }"
"{ annee:int : liste des niveaux à valider }"
self.niveaux: dict[int, ApcNiveau] = {}
"cache les niveaux"
for annee in (1, 2, 3):
@ -118,21 +118,6 @@ class EtudCursusBUT:
self.niveaux.update(
{niveau.id: niveau for niveau in self.niveaux_by_annee[annee]}
)
# Probablement inutile:
# # Cherche les validations de jury enregistrées pour chaque niveau
# self.validations_by_niveau = collections.defaultdict(lambda: [])
# " { niveau_id : [ ApcValidationRCUE ] }"
# for validation_rcue in ApcValidationRCUE.query.filter_by(etud=etud):
# self.validations_by_niveau[validation_rcue.niveau().id].append(
# validation_rcue
# )
# self.validation_by_niveau = {
# niveau_id: sorted(
# validations, key=lambda v: sco_codes.BUT_CODES_ORDERED[v.code]
# )[0]
# for niveau_id, validations in self.validations_by_niveau.items()
# }
# "{ niveau_id : meilleure validation pour ce niveau }"
self.validation_par_competence_et_annee = {}
"""{ competence_id : { 'BUT1' : validation_rcue (la "meilleure"), ... } }"""
@ -146,7 +131,7 @@ class EtudCursusBUT:
# prend la "meilleure" validation
if (not previous_validation) or (
sco_codes.BUT_CODES_ORDERED[validation_rcue.code]
> sco_codes.BUT_CODES_ORDERED[previous_validation["code"]]
> sco_codes.BUT_CODES_ORDERED[previous_validation.code]
):
self.validation_par_competence_et_annee[niveau.competence.id][
niveau.annee
@ -206,6 +191,23 @@ class EtudCursusBUT:
)
return d
def load_validation_by_niveau(self) -> dict[int, list[ApcValidationRCUE]]:
"""Cherche les validations de jury enregistrées pour chaque niveau
Résultat: { niveau_id : [ ApcValidationRCUE ] }
meilleure validation pour ce niveau
"""
validations_by_niveau = collections.defaultdict(lambda: [])
for validation_rcue in ApcValidationRCUE.query.filter_by(etud=self.etud):
validations_by_niveau[validation_rcue.niveau().id].append(validation_rcue)
validation_by_niveau = {
niveau_id: sorted(
validations, key=lambda v: sco_codes.BUT_CODES_ORDERED[v.code]
)[0]
for niveau_id, validations in validations_by_niveau.items()
if validations
}
return validation_by_niveau
class FormSemestreCursusBUT:
"""L'état des étudiants d'un formsemestre dans leur cursus BUT

View File

@ -58,6 +58,7 @@ DecisionsProposeesUE: décisions de jury sur une UE du BUT
DecisionsProposeesRCUE appelera .set_compensable()
si on a la possibilité de la compenser dans le RCUE.
"""
from datetime import datetime
import html
from operator import attrgetter
import re
@ -68,6 +69,7 @@ from flask import flash, g, url_for
from app import db
from app import log
from app.but.cursus_but import EtudCursusBUT
from app.comp.res_but import ResultatsSemestreBUT
from app.comp import res_sem
@ -92,6 +94,7 @@ from app.models.validations import ScolarFormSemestreValidation
from app.scodoc import sco_cache
from app.scodoc import codes_cursus as sco_codes
from app.scodoc.codes_cursus import (
code_rcue_validant,
BUT_CODES_ORDERED,
CODES_RCUE_VALIDES,
CODES_UE_CAPITALISANTS,
@ -275,6 +278,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
if self.formsemestre_impair is not None:
self.validation = ApcValidationAnnee.query.filter_by(
etudid=self.etud.id,
formation_id=self.formsemestre.formation_id,
formsemestre_id=formsemestre_impair.id,
ordre=self.annee_but,
).first()
@ -755,6 +759,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
self.validation = ApcValidationAnnee(
etudid=self.etud.id,
formsemestre=self.formsemestre_impair,
formation_id=self.formsemestre.formation_id,
ordre=self.annee_but,
annee_scolaire=self.annee_scolaire(),
code=code,
@ -900,6 +905,9 @@ class DecisionsProposeesAnnee(DecisionsProposees):
)
validations = ApcValidationAnnee.query.filter_by(
etudid=self.etud.id,
# XXX efface les validations émise depuis ce semestre
# et pas toutes celles concernant cette l'année...
# (utiliser formation_id pour changer cette politique)
formsemestre_id=self.formsemestre_impair.id,
ordre=self.annee_but,
)
@ -1035,6 +1043,9 @@ class DecisionsProposeesRCUE(DecisionsProposees):
):
super().__init__(etud=dec_prop_annee.etud)
self.deca = dec_prop_annee
self.referentiel_competence_id = (
self.deca.formsemestre.formation.referentiel_competence_id
)
self.rcue = rcue
if rcue is None: # RCUE non dispo, eg un seul semestre
self.codes = []
@ -1139,7 +1150,8 @@ class DecisionsProposeesRCUE(DecisionsProposees):
dec_ue.record(sco_codes.ADJR)
# Valide les niveaux inférieurs de la compétence (code ADSUP)
# TODO
if code in CODES_RCUE_VALIDES:
self.valide_niveau_inferieur()
if self.rcue.formsemestre_1 is not None:
sco_cache.invalidate_formsemestre(
@ -1177,6 +1189,189 @@ class DecisionsProposeesRCUE(DecisionsProposees):
return f"{niveau_titre}-{ordre}"
return ""
def valide_niveau_inferieur(self) -> None:
"""Appelé juste après la validation d'un RCUE.
*La validation des deux UE du niveau dune compétence emporte la validation de
lensemble des UEs du niveau inférieur de cette même compétence.*
"""
if not self.rcue or not self.rcue.ue_1 or not self.rcue.ue_1.niveau_competence:
return
competence: ApcCompetence = self.rcue.ue_1.niveau_competence.competence
ordre_inferieur = self.rcue.ue_1.niveau_competence.ordre - 1
if ordre_inferieur < 1:
return # pas de niveau inferieur
# --- Si le RCUE inférieur est déjà validé, ne fait rien
validations_rcue = (
ApcValidationRCUE.query.filter_by(etudid=self.etud.id)
.join(UniteEns, UniteEns.id == ApcValidationRCUE.ue1_id)
.join(ApcNiveau)
.filter_by(ordre=ordre_inferieur)
.join(ApcCompetence)
.filter_by(id=competence.id)
.all()
)
if [v for v in validations_rcue if code_rcue_validant(v.code)]:
return # déjà validé
# --- Validations des UEs
ues, ue1, ue2 = self._get_ues_inferieures(competence, ordre_inferieur)
# Pour chaque UE inférieure non validée, valide:
for ue in ues:
validations_ue = ScolarFormSemestreValidation.query.filter_by(
etudid=self.etud.id, ue_id=ue.id
).all()
if [
validation
for validation in validations_ue
if sco_codes.code_ue_validant(validation.code)
]:
continue # on a déjà une validation
# aucune validation validante
validation_ue = validations_ue[0] if validations_ue else None
if validation_ue:
# Modifie validation existante
validation_ue.code = sco_codes.ADSUP
validation_ue.event_date = datetime.now()
if validation_ue.formsemestre_id is not None:
sco_cache.invalidate_formsemestre(
formsemestre_id=validation_ue.formsemestre_id
)
log(f"updating {validation_ue}")
else:
# Ajoute une validation,
# pas de formsemestre ni de note car pas une capitalisation
validation_ue = ScolarFormSemestreValidation(
etudid=self.etud.id,
code=sco_codes.ADSUP,
ue_id=ue.id,
is_external=True, # pas rattachée à un formsemestre
)
log(f"recording {validation_ue}")
db.session.add(validation_ue)
# Valide le RCUE inférieur
if validations_rcue:
# Met à jour validation existante
validation_rcue = validations_rcue[0]
validation_rcue.code = sco_codes.ADSUP
validation_rcue.date = datetime.now()
log(f"updating {validation_rcue}")
if validation_rcue.formsemestre_id is not None:
sco_cache.invalidate_formsemestre(
formsemestre_id=validation_rcue.formsemestre_id
)
else:
# Crée nouvelle validation
validation_rcue = ApcValidationRCUE(
etudid=self.etud.id, ue1_id=ue1.id, ue2_id=ue2.id, code=sco_codes.ADSUP
)
log(f"recording {validation_rcue}")
db.session.add(validation_rcue)
db.session.commit()
self.valide_annee_inferieure()
def valide_annee_inferieure(self) -> None:
"""Si tous les RCUEs de l'année inférieure sont validés, la valide"""
# Indice de l'année inférieure:
annee_courante = self.rcue.ue_1.niveau_competence.annee # "BUT2"
if not re.match(r"^BUT\d$", annee_courante):
log("Warning: valide_annee_inferieure invalid annee_courante")
return
annee_inferieure = int(annee_courante[3]) - 1
if annee_inferieure < 1:
return
# Garde-fou: Année déjà validée ?
validations_annee: ApcValidationAnnee = ApcValidationAnnee.query.filter_by(
etudid=self.etud.id,
ordre=annee_inferieure,
formation_id=self.rcue.formsemestre_1.formation_id,
).all()
if len(validations_annee) > 1:
log(
f"warning: {len(validations_annee)} validations d'année\n{validations_annee}"
)
if [
validation_annee
for validation_annee in validations_annee
if sco_codes.code_annee_validant(validation_annee.code)
]:
return # déja valide
validation_annee = validations_annee[0] if validations_annee else None
# Liste des niveaux à valider:
# ici on sort l'artillerie lourde
cursus: EtudCursusBUT = EtudCursusBUT(
self.etud, self.rcue.formsemestre_1.formation
)
niveaux_a_valider = cursus.niveaux_by_annee[annee_inferieure]
# Pour chaque niveau, cherche validation RCUE
validations_by_niveau = cursus.load_validation_by_niveau()
ok = True
for niveau in niveaux_a_valider:
validation_niveau: ApcValidationRCUE = validations_by_niveau.get(niveau.id)
if not validation_niveau or not sco_codes.code_rcue_validant(
validation_niveau.code
):
ok = False
# Si tous OK, émet validation année
if validation_annee: # Modifie la validation antérieure (non validante)
validation_annee.code = sco_codes.ADSUP
validation_annee.date = datetime.now()
log(f"updating {validation_annee}")
else:
validation_annee = ApcValidationAnnee(
etudid=self.etud.id,
ordre=annee_inferieure,
code=sco_codes.ADSUP,
formation_id=self.rcue.formsemestre_1.formation_id,
# met cette validation sur l'année scolaire actuelle, pas la précédente (??)
annee_scolaire=self.rcue.formsemestre_1.annee_scolaire(),
)
log(f"recording {validation_annee}")
db.session.add(validation_annee)
db.session.commit()
def _get_ues_inferieures(
self, competence: ApcCompetence, ordre_inferieur: int
) -> tuple[list[UniteEns], UniteEns, UniteEns]:
"""Les UEs de cette formation associées au niveau de compétence inférieur ?
Note: on ne cherche que dans la formation courante, pas les UEs de
même code d'autres formations.
"""
formation: Formation = self.rcue.formsemestre_1.formation
ues: list[UniteEns] = (
UniteEns.query.filter_by(formation_id=formation.id)
.filter(UniteEns.semestre_idx != None)
.join(ApcNiveau)
.filter_by(ordre=ordre_inferieur)
.join(ApcCompetence)
.filter_by(id=competence.id)
.all()
)
log(f"valide_niveau_inferieur: {competence} UEs inférieures: {ues}")
if len(ues) != 2: # on n'a pas 2 UE associées au niveau inférieur !
flash(
"Impossible de valider le niveau de compétence inférieur: pas 2 UEs associées'",
"warning",
)
return
ues_impaires = [ue for ue in ues if ue.semestre_idx % 2]
if len(ues_impaires) != 1:
flash(
"Impossible de valider le niveau de compétence inférieur: pas d'UE impaire associée"
)
return
ue1 = ues_impaires[0]
ues_paires = [ue for ue in ues if not ue.semestre_idx % 2]
if len(ues_paires) != 1:
flash(
"Impossible de valider le niveau de compétence inférieur: pas d'UE paire associée"
)
return
ue2 = ues_paires[0]
return ues, ue1, ue2
class DecisionsProposeesUE(DecisionsProposees):
"""Décisions de jury sur une UE du BUT
@ -1383,23 +1578,29 @@ class BUTCursusEtud: # WIP TODO
for competence in self.competences_du_parcours()
)
def est_diplome(self) -> bool:
"""Vrai si BUT déjà validé"""
# vrai si la troisième année est validée
# On cherche les validations de 3ieme annee (ordre=3) avec le même référentiel
# de formation que nous.
def est_annee_validee(self, ordre: int) -> bool:
"""Vrai si l'année BUT ordre est validée"""
# On cherche les validations d'annee avec le même
# code formation que nous.
return (
ApcValidationAnnee.query.filter_by(etudid=self.etud.id, ordre=3)
.join(FormSemestre, FormSemestre.id == ApcValidationAnnee.formsemestre_id)
.join(Formation, FormSemestre.formation_id == Formation.id)
ApcValidationAnnee.query.filter_by(
etudid=self.etud.id,
ordre=ordre,
formation_id=self.formsemestre.formation_id,
)
.join(Formation)
.filter(
Formation.referentiel_competence_id
== self.formsemestre.formation.referentiel_competence_id
Formation.formation_code == self.formsemestre.formation.formation_code
)
.count()
> 0
)
def est_diplome(self) -> bool:
"""Vrai si BUT déjà validé"""
# vrai si la troisième année est validée
return self.est_annee_validee(3)
def competences_du_parcours(self) -> list[ApcCompetence]:
"""Construit liste des compétences du parcours, qui doivent être
validées pour obtenir le diplôme.

View File

@ -307,9 +307,10 @@ class ResultatsSemestreBUT(NotesTableCompat):
return ues_ids
def etud_has_decision(self, etudid) -> bool:
"""True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre.
"""True s'il y a une décision (quelconque) de jury
émanant de ce formsemestre pour cet étudiant.
prend aussi en compte les autorisations de passage.
Sous-classée en BUT pour les RCUEs et années.
Ici sous-classée (BUT) pour les RCUEs et années.
"""
return bool(
super().etud_has_decision(etudid)

View File

@ -320,7 +320,12 @@ class ApcValidationAnnee(db.Model):
db.Integer, db.ForeignKey("notes_formsemestre.id"), nullable=True
)
"le semestre IMPAIR (le 1er) de l'année"
annee_scolaire = db.Column(db.Integer, nullable=False) # 2021
formation_id = db.Column(
db.Integer,
db.ForeignKey("notes_formations.id"),
nullable=False,
)
annee_scolaire = db.Column(db.Integer, nullable=False) # eg 2021
date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
code = db.Column(db.String(CODE_STR_LEN), nullable=False, index=True)
@ -348,7 +353,7 @@ def dict_decision_jury(etud: Identite, formsemestre: FormSemestre) -> dict:
"""
Un dict avec les décisions de jury BUT enregistrées:
- decision_rcue : list[dict]
- decision_annee : dict
- decision_annee : dict (décision issue de ce semestre seulement (à confirmer ?))
Ne reprend pas les décisions d'UE, non spécifiques au BUT.
"""
decisions = {}
@ -383,8 +388,7 @@ def dict_decision_jury(etud: Identite, formsemestre: FormSemestre) -> dict:
etudid=etud.id,
annee_scolaire=formsemestre.annee_scolaire(),
)
.join(ApcValidationAnnee.formsemestre)
.join(FormSemestre.formation)
.join(Formation)
.filter(Formation.formation_code == formsemestre.formation.formation_code)
.first()
)

View File

@ -859,7 +859,7 @@ class FormSemestre(db.Model):
.order_by(UniteEns.numero)
.all()
)
vals_annee = (
vals_annee = ( # issues de ce formsemestre seulement
ApcValidationAnnee.query.filter_by(
etudid=etudid,
annee_scolaire=self.annee_scolaire(),

View File

@ -122,6 +122,7 @@ ABAN = "ABAN"
ABL = "ABL"
ADM = "ADM" # moyenne gen., barres UE, assiduité: sem. validé
ADC = "ADC" # admis par compensation (eg moy(S1, S2) > 10)
ADSUP = "ADSUP" # BUT: UE ou RCUE validé par niveau supérieur
ADJ = "ADJ" # admis par le jury
ADJR = "ADJR" # UE admise car son RCUE est ADJ
ATT = "ATT" #
@ -162,6 +163,7 @@ CODES_EXPL = {
ADJ: "Validé par le Jury",
ADJR: "UE validée car son RCUE est validé ADJ par le jury",
ADM: "Validé",
ADSUP: "UE ou RCUE validé car le niveau supérieur est validé",
AJ: "Ajourné (ou UE/BC de BUT en attente pour problème de moyenne)",
ATB: "Décision en attente d'un autre semestre (au moins une UE sous la barre)",
ATJ: "Décision en attente d'un autre semestre (assiduité insuffisante)",
@ -195,17 +197,18 @@ CODES_SEM_ATTENTES = {ATT, ATB, ATJ} # semestre en attente
CODES_SEM_REO = {NAR} # reorientation
CODES_UE_VALIDES_DE_DROIT = {ADM, CMP} # validation "de droit"
CODES_UE_VALIDES = CODES_UE_VALIDES_DE_DROIT | {ADJ, ADJR}
CODES_UE_VALIDES = CODES_UE_VALIDES_DE_DROIT | {ADJ, ADJR, ADSUP}
"UE validée"
CODES_UE_CAPITALISANTS = {ADM}
"UE capitalisée"
CODES_RCUE_VALIDES_DE_DROIT = {ADM, CMP}
CODES_RCUE_VALIDES = CODES_RCUE_VALIDES_DE_DROIT | {ADJ}
CODES_RCUE_VALIDES = CODES_RCUE_VALIDES_DE_DROIT | {ADJ, ADSUP}
"Niveau RCUE validé"
# Pour le BUT:
CODES_ANNEE_BUT_VALIDES_DE_DROIT = {ADM, PASD}
CODES_ANNEE_BUT_VALIDES_DE_DROIT = {ADM} # PASD était ici mais retiré en juin 23
CODES_ANNEE_BUT_VALIDES = {ADM, ADSUP}
CODES_ANNEE_ARRET = {DEF, DEM, ABAN, ABL}
BUT_BARRE_UE8 = 8.0 - NOTES_TOLERANCE
BUT_BARRE_UE = BUT_BARRE_RCUE = 10.0 - NOTES_TOLERANCE
@ -229,6 +232,7 @@ BUT_CODES_ORDERED = {
PASD: 50,
PAS1NCI: 60,
ADJR: 90,
ADSUP: 90,
ADJ: 100,
ADM: 100,
}
@ -249,6 +253,16 @@ def code_ue_validant(code: str) -> bool:
return code in CODES_UE_VALIDES
def code_rcue_validant(code: str) -> bool:
"Vrai si ce code d'RCUE est validant"
return code in CODES_RCUE_VALIDES
def code_annee_validant(code: str) -> bool:
"Vrai si code d'année BUT validant"
return code in CODES_ANNEE_BUT_VALIDES
DEVENIR_EXPL = {
NEXT: "Passage au semestre suivant",
REDOANNEE: "Redoublement année",

View File

@ -473,7 +473,10 @@ class ApoEtud(dict):
)
def _but_load_validation_annuelle(self):
"charge la validation de jury BUT annuelle"
"""charge la validation de jury BUT annuelle.
Ici impose qu'elle soit issue d'un semestre de l'année en cours
(pas forcément nécessaire, voir selon les retours des équipes ?)
"""
# le semestre impair de l'année scolaire
if self.cur_res.formsemestre.semestre_id % 2:
formsemestre = self.cur_res.formsemestre
@ -490,7 +493,9 @@ class ApoEtud(dict):
return
self.validation_annee_but: ApcValidationAnnee = (
ApcValidationAnnee.query.filter_by(
formsemestre_id=formsemestre.id, etudid=self.etud["etudid"]
formsemestre_id=formsemestre.id,
etudid=self.etud["etudid"],
formation_id=self.cur_sem["formation_id"],
).first()
)
self.is_nar = (

View File

@ -66,6 +66,7 @@ from app.scodoc import sco_photos
from app.scodoc import sco_preferences
from app.scodoc import sco_pv_dict
# ------------------------------------------------------------------------------------
def formsemestre_validation_etud_form(
formsemestre_id=None, # required
@ -1063,8 +1064,6 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid):
"""Form. saisie UE validée hors ScoDoc
(pour étudiants arrivant avec un UE antérieurement validée).
"""
from app.scodoc import sco_formations
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
formation: Formation = Formation.query.get_or_404(sem["formation_id"])
@ -1087,8 +1086,8 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid):
<em>dans un semestre hors ScoDoc</em>.</p>
<p><b>Les UE validées dans ScoDoc sont déjà
automatiquement prises en compte</b>. Cette page n'est utile que pour les étudiants ayant
suivi un début de cursus dans <b>un autre établissement</b>, ou bien dans un semestre géré <b>sans
ScoDoc</b> et qui <b>redouble</b> ce semestre
suivi un début de cursus dans <b>un autre établissement</b>, ou bien dans un semestre géré
<b>sans ScoDoc</b> et qui <b>redouble</b> ce semestre
(<em>ne pas utiliser pour les semestres précédents !</em>).
</p>
<p>Notez que l'UE est validée, avec enregistrement immédiat de la décision et

View File

@ -0,0 +1,63 @@
"""validation niveaux inferieurs
Revision ID: c701224fa255
Revises: d84bc592584e
Create Date: 2023-06-11 11:08:05.553898
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.orm import sessionmaker # added by ev
# revision identifiers, used by Alembic.
revision = "c701224fa255"
down_revision = "d84bc592584e"
branch_labels = None
depends_on = None
Session = sessionmaker()
def upgrade():
# Ajoute la colonne formation_id, nullable, la peuple puis la rend non nullable
op.add_column(
"apc_validation_annee", sa.Column("formation_id", sa.Integer(), nullable=True)
)
op.create_foreign_key(
"apc_validation_annee_formation_id_fkey",
"apc_validation_annee",
"notes_formations",
["formation_id"],
["id"],
)
# Affecte la formation des anciennes validations
bind = op.get_bind()
session = Session(bind=bind)
session.execute(
sa.text(
"""
UPDATE apc_validation_annee AS a
SET formation_id = (
SELECT f.id
FROM notes_formations f
JOIN notes_formsemestre s ON f.id = s.formation_id
WHERE s.id = a.formsemestre_id
)
WHERE a.formsemestre_id IS NOT NULL;
"""
)
)
op.alter_column(
"apc_validation_annee",
"formation_id",
nullable=False,
)
def downgrade():
with op.batch_alter_table("apc_validation_annee", schema=None) as batch_op:
batch_op.drop_constraint(
"apc_validation_annee_formation_id_fkey", type_="foreignkey"
)
batch_op.drop_column("formation_id")

View File

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

View File

@ -1,122 +1,122 @@
# Tests unitaires jury BUT
# Essais avec un BUT GCCD (GC-CD) et un parcours de S1 à S6
# Le GCCD est un programme à 5 compétences, dont certaines
# Le GCCD est un programme à 5 compétences, dont certaines
# terminent en S4 ou en S6 selon les parcours.
ReferentielCompetences:
filename: but-GCCD-05012022-081630.xml
filename: but-GCCD-05012022-081630.xml
specialite: GCCD
Formation:
filename: scodoc_formation_BUT_GC-CD_v2.xml
# Association des UEs aux compétences:
ues:
# S1 tronc commun:
'UE1.1':
# S1 tronc commun:
"UE1.1":
annee: BUT1
competence: "Solutions Bâtiment"
'UE1.2':
"UE1.2":
annee: BUT1
competence: "Solutions TP"
'UE1.3':
"UE1.3":
annee: BUT1
competence: "Dimensionner"
'UE1.4':
"UE1.4":
annee: BUT1
competence: Organiser
'UE1.5':
"UE1.5":
annee: BUT1
competence: Piloter
# S2 tronc commun:
'UE2.1':
# S2 tronc commun:
"UE2.1":
annee: BUT1
competence: "Solutions Bâtiment"
'UE2.2':
"UE2.2":
annee: BUT1
competence: "Solutions TP"
'UE2.3':
"UE2.3":
annee: BUT1
competence: "Dimensionner"
'UE2.4':
"UE2.4":
annee: BUT1
competence: Organiser
'UE2.5':
"UE2.5":
annee: BUT1
competence: Piloter
# S3 : Tronc commun
'UE3.1':
"UE3.1":
annee: BUT2
competence: "Solutions Bâtiment"
'UE3.2':
"UE3.2":
annee: BUT2
competence: "Solutions TP"
'UE3.3':
"UE3.3":
annee: BUT2
competence: "Dimensionner"
'UE3.4':
"UE3.4":
annee: BUT2
competence: Organiser
'UE3.5':
"UE3.5":
annee: BUT2
competence: Piloter
# S4 Tronc commun
'UE4.1':
"UE4.1":
annee: BUT2
competence: "Solutions Bâtiment"
'UE4.2':
"UE4.2":
annee: BUT2
competence: "Solutions TP"
'UE4.3':
"UE4.3":
annee: BUT2
competence: "Dimensionner"
'UE4.4':
"UE4.4":
annee: BUT2
competence: Organiser
'UE4.5':
"UE4.5":
annee: BUT2
competence: Piloter
# S5 Parcours BAT + TP
'UE5.1': # Parcours BAT seulement
"UE5.1": # Parcours BAT seulement
annee: BUT3
parcours: BAT # + RAPEB, BEC
competence: "Solutions Bâtiment"
'UE5.2': # Parcours TP seulement
"UE5.2": # Parcours TP seulement
annee: BUT3
parcours: TP # + BEC
competence: "Solutions TP"
'UE5.3':
"UE5.3":
annee: BUT3
parcours: [RAPEB, BEC]
competence: "Dimensionner"
'UE5.4':
"UE5.4":
annee: BUT3
parcours: [BAT, TP]
competence: Organiser
'UE5.5':
"UE5.5":
annee: BUT3
parcours: [BAT, TP]
competence: Piloter
# S6 Parcours BAT + TP
'UE6.1': # Parcours BAT seulement
"UE6.1": # Parcours BAT seulement
annee: BUT3
parcours: BAT # + RAPEB, BEC
competence: "Solutions Bâtiment"
'UE6.2': # Parcours TP seulement
"UE6.2": # Parcours TP seulement
annee: BUT3
parcours: [TP,BEC]
parcours: [TP, BEC]
competence: "Solutions TP"
'UE6.3':
"UE6.3":
annee: BUT3
parcours: [RAPEB,BEC]
parcours: [RAPEB, BEC]
competence: "Dimensionner"
'UE6.4':
"UE6.4":
annee: BUT3
parcours: [BAT, TP]
competence: Organiser
'UE6.5':
"UE6.5":
annee: BUT3
parcours: [BAT,TP]
parcours: [BAT, TP]
competence: Piloter
modules_parcours:
@ -126,8 +126,8 @@ Formation:
# - tous les module de S1 à S4 dans tous les parcours
# - SAE communes en S1 et S2 mais différenciées par parcours ensuite
# - en S5, ressources différenciées: on ne les mentionne pas toutes ici
BAT: [ "R[1-4].*", "SAÉ [1-2]", "SAÉ *.BAT.*", "R5.0[1-7]", "R5.14" ]
TP: [ "R[1-4].*", "SAÉ [1-2]", "SAÉ *.TP.*", "R5.0[1-4]", "R5.0[89]" ]
BAT: ["R[1-4].*", "SAÉ [1-2]", "SAÉ *.BAT.*", "R5.0[1-7]", "R5.14"]
TP: ["R[1-4].*", "SAÉ [1-2]", "SAÉ *.TP.*", "R5.0[1-4]", "R5.0[89]"]
FormSemestres:
# S1 et S2 avec les parcours BAT et TP:
@ -135,32 +135,32 @@ FormSemestres:
idx: 1
date_debut: 2021-09-01
date_fin: 2022-01-15
codes_parcours: ['BAT', 'TP']
S2:
codes_parcours: ["BAT", "TP"]
S2:
idx: 2
date_debut: 2022-01-15
date_fin: 2022-06-30
codes_parcours: ['BAT', 'TP']
codes_parcours: ["BAT", "TP"]
S3:
idx: 3
date_debut: 2022-09-01
date_fin: 2023-01-15
codes_parcours: ['BAT', 'TP']
codes_parcours: ["BAT", "TP"]
S4:
idx: 4
date_debut: 2023-01-16
date_fin: 2023-06-30
codes_parcours: ['BAT', 'TP']
codes_parcours: ["BAT", "TP"]
S5:
idx: 5
date_debut: 2023-09-01
date_fin: 2024-01-15
codes_parcours: ['BAT', 'TP']
codes_parcours: ["BAT", "TP"]
S6:
idx: 6
date_debut: 2024-01-16
date_fin: 2024-06-30
codes_parcours: ['BAT', 'TP']
codes_parcours: ["BAT", "TP"]
Etudiants:
A_ok: # Etudiant parcours BAT qui va tout valider directement
@ -171,10 +171,18 @@ Etudiants:
parcours: BAT
notes_modules:
"R1.01": 11 # toutes UEs
"SAÉ 1-2": EXC
S2:
parcours: BAT
notes_modules:
"R2.01": 12 # toutes UEs
attendu: # les codes jury que l'on doit vérifier
deca:
passage_de_droit: True
autorisations_inscription: [3]
code_valide:
nb_competences: 5
nb_rcue_annee: 4
S3:
parcours: BAT
notes_modules:
@ -186,7 +194,7 @@ Etudiants:
S5:
parcours: BAT
dispense_ues: ['UE5.2', 'UE5.3']
dispense_ues: ["UE5.2", "UE5.3"]
notes_modules:
"R5.01": 15 # toutes UE
"SAÉ 5.BAT.01": 10 # UE5.1
@ -202,6 +210,7 @@ Etudiants:
parcours: TP
notes_modules:
"R1.01": 11 # toutes UEs
"SAÉ 1-2": EXC
S2:
parcours: TP
notes_modules:
@ -217,10 +226,32 @@ Etudiants:
S5:
parcours: TP
dispense_ues: ['UE5.1', 'UE5.3']
dispense_ues: ["UE5.1", "UE5.3"]
notes_modules:
"R5.01": 15 # toutes UE
"SAÉ 5.BAT.01": 10 # UE5.1
"SAÉ 5.BAT.02": 11 # UE5.4
S6:
parcours: TP
C: # Etudiant qui passe sans un RCUE et valide en BUT2
prenom: Étudiant_TP_but2
civilite: M
formsemestres:
S1:
parcours: TP
notes_modules:
"R1.01": 11 # toutes UEs
"SAÉ 1-2": 8 # plombe l'UE 2
S2:
parcours: TP
notes_modules:
"R2.01": 11 # toutes UEs
S3:
parcours: TP
notes_modules:
"R3.01": 12 # toutes UEs
S4:
parcours: TP
notes_modules:
"R4.01": 14 # toutes UE