forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -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
|
||||
|
@ -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 d’une compétence emporte la validation de
|
||||
l’ensemble 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.
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
)
|
||||
|
@ -865,7 +865,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(),
|
||||
|
@ -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",
|
||||
|
@ -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 = (
|
||||
|
@ -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
|
||||
|
@ -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")
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.4.83"
|
||||
SCOVERSION = "9.4.84"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user