forked from ScoDoc/ScoDoc
WIP: jury BUT: validations annee, RCUE, UE
This commit is contained in:
parent
cd2de51bcc
commit
a37a2c08e2
@ -117,15 +117,15 @@ class DecisionsProposees:
|
||||
self.codes = []
|
||||
"Les codes attribuables par ce jury"
|
||||
if include_communs:
|
||||
self.codes = self.codes_communs
|
||||
self.codes = self.codes_communs.copy()
|
||||
if isinstance(code, list):
|
||||
self.codes = code + self.codes_communs
|
||||
self.codes = code + self.codes
|
||||
elif code is not None:
|
||||
self.codes = [code] + self.codes_communs
|
||||
self.codes = [code] + self.codes
|
||||
self.code_valide: str = code_valide
|
||||
"La décision actuelle enregistrée"
|
||||
self.explanation: str = explanation
|
||||
"Explication en à afficher à côté de la décision"
|
||||
"Explication à afficher à côté de la décision"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"""<{self.__class__.__name__} valid={self.code_valide
|
||||
@ -193,48 +193,72 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
self.parcour = None
|
||||
"Le parcours considéré (celui du semestre pair, ou à défaut impair)"
|
||||
self.ues_impair, self.ues_pair = self.compute_ues_annee() # pylint: disable=all
|
||||
self.decisions_ues = {
|
||||
ue.id: DecisionsProposeesUE(etud, formsemestre_impair, ue)
|
||||
for ue in self.ues_impair
|
||||
}
|
||||
"{ue_id : DecisionsProposeesUE} pour toutes les UE de l'année"
|
||||
self.decisions_ues.update(
|
||||
{
|
||||
ue.id: DecisionsProposeesUE(etud, formsemestre_pair, ue)
|
||||
for ue in self.ues_pair
|
||||
}
|
||||
)
|
||||
assert self.parcour is not None
|
||||
self.rcues_annee = self.compute_rcues_annee()
|
||||
"RCUEs de l'année"
|
||||
|
||||
self.nb_competences = len(
|
||||
ApcNiveau.niveaux_annee_de_parcours(self.parcour, self.annee_but).all()
|
||||
) # note that .count() won't give the same res
|
||||
self.niveaux_competences = ApcNiveau.niveaux_annee_de_parcours(
|
||||
self.parcour, self.annee_but
|
||||
).all() # XXX à trier, selon l'ordre des UE associées ?
|
||||
"liste des niveaux de compétences associés à cette année"
|
||||
self.decisions_rcue_by_niveau = self.compute_decisions_niveaux()
|
||||
"les décisions rcue associées aux niveau_id"
|
||||
self.nb_competences = len(self.niveaux_competences)
|
||||
"le nombre de niveaux de compétences à valider cette année"
|
||||
self.nb_validables = len(
|
||||
[rcue for rcue in self.rcues_annee if rcue.est_validable()]
|
||||
)
|
||||
"le nombre de comp. validables (éventuellement par compensation)"
|
||||
self.nb_rcues_under_8 = len(
|
||||
[rcue for rcue in self.rcues_annee if not rcue.est_suffisant()]
|
||||
)
|
||||
"le nb de comp. sous la barre de 8/20"
|
||||
# année ADM si toutes RCUE validées (sinon PASD)
|
||||
admis = self.nb_validables == self.nb_competences
|
||||
valide_moitie_rcue = self.nb_validables > self.nb_competences // 2
|
||||
self.valide_moitie_rcue = self.nb_validables > (self.nb_competences // 2)
|
||||
# Peut passer si plus de la moitié validables et tous > 8
|
||||
passage_de_droit = valide_moitie_rcue and (self.nb_rcues_under_8 == 0)
|
||||
self.passage_de_droit = self.valide_moitie_rcue and (self.nb_rcues_under_8 == 0)
|
||||
# XXX TODO ajouter condition pour passage en S5
|
||||
|
||||
# Enfin calcule les codes des UE:
|
||||
for dec_ue in self.decisions_ues.values():
|
||||
dec_ue.compute_codes()
|
||||
|
||||
# Reste à attribuer ADM, ADJ, PASD, PAS1NCI, RED, NAR
|
||||
expl_rcues = f"{self.nb_validables} validables sur {self.nb_competences}"
|
||||
expl_rcues = (
|
||||
f"{self.nb_validables} niveau validable(s) sur {self.nb_competences}"
|
||||
)
|
||||
if admis:
|
||||
self.codes = [sco_codes.ADM] + self.codes
|
||||
self.explanation = expl_rcues
|
||||
elif passage_de_droit:
|
||||
elif self.passage_de_droit:
|
||||
self.codes = [sco_codes.PASD, sco_codes.ADJ] + self.codes
|
||||
self.explanation = expl_rcues
|
||||
elif valide_moitie_rcue: # mais au moins 1 rcue insuffisante
|
||||
elif self.valide_moitie_rcue: # mais au moins 1 rcue insuffisante
|
||||
self.codes = [sco_codes.PAS1NCI, sco_codes.ADJ] + self.codes
|
||||
self.explanation = expl_rcues + f" et {self.nb_rcues_under_8} < 8"
|
||||
else:
|
||||
self.codes = [sco_codes.RED, sco_codes.NAR, sco_codes.ADJ] + self.codes
|
||||
self.explanation = expl_rcues + f" et {self.nb_rcues_under_8} < 8"
|
||||
self.explanation = expl_rcues + f" et {self.nb_rcues_under_8} niveau < 8"
|
||||
#
|
||||
|
||||
def infos(self) -> str:
|
||||
"informations, for debugging purpose"
|
||||
return f"""DecisionsProposeesAnnee
|
||||
etud: {self.etud}
|
||||
formsemestre_pair: {self.formsemestre_pair}
|
||||
formsemestre_impair: {self.formsemestre_impair}
|
||||
formsemestre_pair: {self.formsemestre_pair}
|
||||
RCUEs: {self.rcues_annee}
|
||||
nb_competences: {self.nb_competences}
|
||||
nb_nb_validables: {self.nb_validables}
|
||||
@ -286,7 +310,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
# Parcour dans lequel l'étudiant est inscrit, et liste des UEs
|
||||
if res.etuds_parcour_id[etudid] is None:
|
||||
# pas de parcour: prend toutes les UEs (non bonus)
|
||||
ues = res.etud_ues(etudid)
|
||||
ues = list(res.etud_ues(etudid))
|
||||
else:
|
||||
parcour = ApcParcours.query.get(res.etuds_parcour_id[etudid])
|
||||
if parcour is not None:
|
||||
@ -343,10 +367,34 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
raise ScoValueError(f"pas de RCUE pour l'UE {ue_pair.acronyme}")
|
||||
rcues_annee.append(rcue)
|
||||
if len(ues_impair_sans_rcue) > 0:
|
||||
ue = ues_impair_sans_rcue.pop()
|
||||
ue = UniteEns.query.get(ues_impair_sans_rcue.pop())
|
||||
raise ScoValueError(f"pas de RCUE pour l'UE {ue.acronyme}")
|
||||
return rcues_annee
|
||||
|
||||
def compute_decisions_niveaux(self) -> dict[int, "DecisionsProposeesRCUE"]:
|
||||
"""Pour chaque niveau de compétence de cette année, donne le DecisionsProposeesRCUE
|
||||
ou None s'il n'y en a pas (ne devrait pas arriver car
|
||||
compute_rcues_annee vérifie déjà cela).
|
||||
Return: { niveau_id : DecisionsProposeesRCUE }
|
||||
"""
|
||||
# Retrouve le RCUE associé à chaque niveau
|
||||
rc_niveaux = []
|
||||
for niveau in self.niveaux_competences:
|
||||
rcue = None
|
||||
for rc in self.rcues_annee:
|
||||
if rc.ue_1.niveau_competence_id == niveau.id:
|
||||
rcue = rc
|
||||
break
|
||||
dec_rcue = DecisionsProposeesRCUE(self, rcue)
|
||||
rc_niveaux.append((dec_rcue, niveau.id))
|
||||
# prévient les UE concernées :-)
|
||||
self.decisions_ues[dec_rcue.rcue.ue_1.id].set_rcue(dec_rcue.rcue)
|
||||
self.decisions_ues[dec_rcue.rcue.ue_2.id].set_rcue(dec_rcue.rcue)
|
||||
# Ordonne par numéro d'UE
|
||||
rc_niveaux.sort(key=lambda x: x[0].rcue.ue_1.numero)
|
||||
decisions_rcue_by_niveau = {x[1]: x[0] for x in rc_niveaux}
|
||||
return decisions_rcue_by_niveau
|
||||
|
||||
|
||||
class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
"""Liste des codes de décisions que l'on peut proposer pour
|
||||
@ -371,7 +419,7 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
validation = rcue.query_validations().first()
|
||||
if validation is not None:
|
||||
self.code_valide = validation.code
|
||||
if rcue.est_compense():
|
||||
if rcue.est_compensable():
|
||||
self.codes.insert(0, sco_codes.CMP)
|
||||
elif rcue.est_validable():
|
||||
self.codes.insert(0, sco_codes.ADM)
|
||||
@ -412,6 +460,7 @@ class DecisionsProposeesUE(DecisionsProposees):
|
||||
super().__init__(etud=etud)
|
||||
self.ue: UniteEns = ue
|
||||
self.rcue: RegroupementCoherentUE = None
|
||||
"Le rcu auquel est rattaché cette UE, ou None"
|
||||
# Une UE peut être validée plusieurs fois en cas de redoublement (qu'elle soit capitalisée ou non)
|
||||
# mais ici on a restreint au formsemestre donc une seule (prend la première)
|
||||
self.validation = ScolarFormSemestreValidation.query.filter_by(
|
||||
@ -423,17 +472,7 @@ class DecisionsProposeesUE(DecisionsProposees):
|
||||
self.explanation = "UE bonus, pas de décision de jury"
|
||||
self.codes = [] # aucun code proposé
|
||||
return
|
||||
# Code sur année ?
|
||||
decision_annee = ApcValidationAnnee.query.filter_by(
|
||||
etudid=etud.id, annee_scolaire=formsemestre.annee_scolaire()
|
||||
).first()
|
||||
if (
|
||||
decision_annee is not None
|
||||
and decision_annee.code in sco_codes.CODES_ANNEE_ARRET
|
||||
): # DEF, DEM, ABAN, ABL
|
||||
self.explanation = f"l'année a le code {decision_annee.code}"
|
||||
self.codes = [decision_annee.code] # sans les codes communs
|
||||
return
|
||||
|
||||
# Moyenne de l'UE ?
|
||||
res: ResultatsSemestreBUT = res_sem.load_formsemestre_results(formsemestre)
|
||||
if not ue.id in res.etud_moy_ue:
|
||||
@ -442,23 +481,25 @@ class DecisionsProposeesUE(DecisionsProposees):
|
||||
if not etud.id in res.etud_moy_ue[ue.id]:
|
||||
self.explanation = "Étudiant sans résultat dans cette UE"
|
||||
return
|
||||
moy_ue = res.etud_moy_ue[ue.id][etud.id]
|
||||
if moy_ue > (sco_codes.ParcoursBUT.BARRE_MOY - sco_codes.NOTES_TOLERANCE):
|
||||
self.moy_ue = res.etud_moy_ue[ue.id][etud.id]
|
||||
|
||||
def set_rcue(self, rcue: RegroupementCoherentUE):
|
||||
"""Rattache cette UE à un RCUE. Cela peut modifier les codes
|
||||
proposés (si compensation)"""
|
||||
self.rcue = rcue
|
||||
|
||||
def compute_codes(self):
|
||||
"""Calcul des .codes attribuables et de l'explanation associée"""
|
||||
if self.moy_ue > (sco_codes.ParcoursBUT.BARRE_MOY - sco_codes.NOTES_TOLERANCE):
|
||||
self.codes.insert(0, sco_codes.ADM)
|
||||
self.explanation = (f"Moyenne >= {sco_codes.ParcoursBUT.BARRE_MOY}/20",)
|
||||
|
||||
# Compensation dans un RCUE ?
|
||||
rcues = but_validations.find_rcues(formsemestre, ue, etud)
|
||||
for rcue in rcues:
|
||||
if rcue.est_validable():
|
||||
self.codes.insert(0, sco_codes.CMP)
|
||||
self.explanation = f"Compensée par {rcue.other_ue(ue)} (moyenne RCUE={scu.fmt_note(rcue.moy_rcue)}/20"
|
||||
self.rcue = rcue
|
||||
return # s'arrête au 1er RCU validable
|
||||
|
||||
# Échec à valider cette UE
|
||||
self.codes = [sco_codes.AJ, sco_codes.ADJ] + self.codes
|
||||
self.explanation = "notes insuffisantes"
|
||||
elif self.rcue and self.rcue.est_compensable():
|
||||
self.codes.insert(0, sco_codes.CMP)
|
||||
self.explanation = "compensable dans le RCUE"
|
||||
else:
|
||||
# Échec à valider cette UE
|
||||
self.codes = [sco_codes.AJ, sco_codes.ADJ] + self.codes
|
||||
self.explanation = "notes insuffisantes"
|
||||
|
||||
|
||||
class BUTCursusEtud: # WIP TODO
|
||||
|
@ -67,7 +67,7 @@ class ApcValidationRCUE(db.Model):
|
||||
# Attention: ce n'est pas un modèle mais une classe ordinaire:
|
||||
class RegroupementCoherentUE:
|
||||
"""Le regroupement cohérent d'UE, dans la terminologie du BUT, est le couple d'UEs
|
||||
de la même année (BUT1,2,3) liées au même niveau de compétence.
|
||||
de la même année (BUT1,2,3) liées au *même niveau de compétence*.
|
||||
|
||||
La moyenne (10/20) au RCU déclenche la compensation des UE.
|
||||
"""
|
||||
@ -139,7 +139,7 @@ class RegroupementCoherentUE:
|
||||
etudid=self.etud.id,
|
||||
)
|
||||
.join(UniteEns, UniteEns.id == ApcValidationRCUE.ue2_id)
|
||||
.join(ApcNiveau, UniteEns.niveau_id == ApcNiveau.id)
|
||||
.join(ApcNiveau, UniteEns.niveau_competence_id == ApcNiveau.id)
|
||||
.filter(ApcNiveau.id == niveau.id)
|
||||
)
|
||||
|
||||
@ -157,7 +157,7 @@ class RegroupementCoherentUE:
|
||||
"""
|
||||
return self.query_validations().count() > 0
|
||||
|
||||
def est_compense(self):
|
||||
def est_compensable(self):
|
||||
"""Vrai si ce RCUE est validable par compensation
|
||||
c'est à dire que sa moyenne est > 10 avec une UE < 10
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user