forked from ScoDoc/DocScoDoc
Saisie automatique des décisions de jury BUT pour semestres pairs ou impairs.
This commit is contained in:
parent
165dac0496
commit
d3248a37ad
@ -693,20 +693,20 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
|
||||
db.session.commit()
|
||||
|
||||
def record(self, code: str, no_overwrite=False):
|
||||
def record(self, code: str, no_overwrite=False) -> bool:
|
||||
"""Enregistre le code de l'année, et au besoin l'autorisation d'inscription.
|
||||
Si no_overwrite, ne fait rien si un code est déjà enregistré.
|
||||
Si l'étudiant est DEM ou DEF, ne fait rien.
|
||||
"""
|
||||
if self.inscription_etat != scu.INSCRIT:
|
||||
return
|
||||
return False
|
||||
if code and not code in self.codes:
|
||||
raise ScoValueError(
|
||||
f"code annee <tt>{html.escape(code)}</tt> invalide pour formsemestre {html.escape(self.formsemestre)}"
|
||||
)
|
||||
if code == self.code_valide or (self.code_valide is not None and no_overwrite):
|
||||
self.recorded = True
|
||||
return # no change
|
||||
return False # no change
|
||||
if self.validation:
|
||||
db.session.delete(self.validation)
|
||||
db.session.commit()
|
||||
@ -746,9 +746,10 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
next_semestre_id,
|
||||
)
|
||||
|
||||
self.recorded = True
|
||||
db.session.commit()
|
||||
self.recorded = True
|
||||
self.invalidate_formsemestre_cache()
|
||||
return True
|
||||
|
||||
def invalidate_formsemestre_cache(self):
|
||||
"invalide le résultats des deux formsemestres"
|
||||
@ -759,13 +760,20 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
if self.formsemestre_pair is not None:
|
||||
sco_cache.invalidate_formsemestre(formsemestre_id=self.formsemestre_pair.id)
|
||||
|
||||
def record_all(self, no_overwrite: bool = True):
|
||||
def record_all(
|
||||
self, no_overwrite: bool = True, only_validantes: bool = False
|
||||
) -> bool:
|
||||
"""Enregistre les codes qui n'ont pas été spécifiés par le formulaire,
|
||||
et sont donc en mode "automatique".
|
||||
- Si "à cheval", ne modifie pas les codes UE de l'année scolaire précédente.
|
||||
- Pour les RCUE: n'enregistre que si la nouvelle décision est plus favorable que l'ancienne.
|
||||
|
||||
Si only_validantes, n'enregistre que des décisions "validantes" de droit: ADM ou CMP.
|
||||
|
||||
Return: True si au moins un code modifié et enregistré.
|
||||
"""
|
||||
# Toujours valider dans l'ordre UE, RCUE, Année:
|
||||
modif = False
|
||||
# Toujours valider dans l'ordre UE, RCUE, Année
|
||||
annee_scolaire = self.formsemestre.annee_scolaire()
|
||||
# UEs
|
||||
for dec_ue in self.decisions_ues.values():
|
||||
@ -774,25 +782,40 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
) and dec_ue.formsemestre.annee_scolaire() == annee_scolaire:
|
||||
# rappel: le code par défaut est en tête
|
||||
code = dec_ue.codes[0] if dec_ue.codes else None
|
||||
if (not only_validantes) or code in sco_codes.CODES_UE_VALIDES_DE_DROIT:
|
||||
# enregistre le code jury seulement s'il n'y a pas déjà de code
|
||||
# (no_overwrite=True) sauf en mode test yaml
|
||||
dec_ue.record(code, no_overwrite=no_overwrite)
|
||||
# RCUE : enregistre seulement si pas déjà validé "mieux"
|
||||
modif |= dec_ue.record(code, no_overwrite=no_overwrite)
|
||||
# RCUE :
|
||||
for dec_rcue in self.decisions_rcue_by_niveau.values():
|
||||
code = dec_rcue.codes[0] if dec_rcue.codes else None
|
||||
if (not dec_rcue.recorded) and (
|
||||
if (
|
||||
(not dec_rcue.recorded)
|
||||
and ( # enregistre seulement si pas déjà validé "mieux"
|
||||
(not dec_rcue.validation)
|
||||
or BUT_CODES_ORDERED.get(dec_rcue.validation.code, 0)
|
||||
< BUT_CODES_ORDERED.get(code, 0)
|
||||
)
|
||||
and ( # décision validante de droit ?
|
||||
(
|
||||
(not only_validantes)
|
||||
or code in sco_codes.CODES_RCUE_VALIDES_DE_DROIT
|
||||
)
|
||||
)
|
||||
):
|
||||
dec_rcue.record(code, no_overwrite=no_overwrite)
|
||||
modif |= dec_rcue.record(code, no_overwrite=no_overwrite)
|
||||
# Année:
|
||||
if not self.recorded:
|
||||
# rappel: le code par défaut est en tête
|
||||
code = self.codes[0] if self.codes else None
|
||||
# enregistre le code jury seulement s'il n'y a pas déjà de code
|
||||
# (no_overwrite=True) sauf en mode test yaml
|
||||
self.record(code, no_overwrite=no_overwrite)
|
||||
if (
|
||||
not only_validantes
|
||||
) or code in sco_codes.CODES_ANNEE_BUT_VALIDES_DE_DROIT:
|
||||
modif |= self.record(code, no_overwrite=no_overwrite)
|
||||
|
||||
return modif
|
||||
|
||||
def erase(self, only_one_sem=False):
|
||||
"""Efface les décisions de jury de cet étudiant
|
||||
@ -1005,23 +1028,23 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
return f"""<{self.__class__.__name__} rcue={self.rcue} valid={self.code_valide
|
||||
} codes={self.codes} explanation={self.explanation}"""
|
||||
|
||||
def record(self, code: str, no_overwrite=False):
|
||||
def record(self, code: str, no_overwrite=False) -> bool:
|
||||
"""Enregistre le code RCUE.
|
||||
Note:
|
||||
- si le RCUE est ADJ, les UE non validées sont passées à ADJ
|
||||
XXX on pourra imposer ici d'autres règles de cohérence
|
||||
"""
|
||||
if self.rcue is None:
|
||||
return # pas de RCUE a enregistrer
|
||||
return False # pas de RCUE a enregistrer
|
||||
if self.inscription_etat != scu.INSCRIT:
|
||||
return
|
||||
return False
|
||||
if code and not code in self.codes:
|
||||
raise ScoValueError(
|
||||
f"code UE invalide pour ue_id={self.ue.id}: {html.escape(code)}"
|
||||
)
|
||||
if code == self.code_valide or (self.code_valide is not None and no_overwrite):
|
||||
self.recorded = True
|
||||
return # no change
|
||||
return False # no change
|
||||
parcours_id = self.parcour.id if self.parcour is not None else None
|
||||
if self.validation:
|
||||
db.session.delete(self.validation)
|
||||
@ -1072,6 +1095,7 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
)
|
||||
self.code_valide = code # mise à jour état
|
||||
self.recorded = True
|
||||
return True
|
||||
|
||||
def erase(self):
|
||||
"""Efface la décision de jury de cet étudiant pour cet RCUE"""
|
||||
@ -1203,9 +1227,10 @@ class DecisionsProposeesUE(DecisionsProposees):
|
||||
self.codes = [sco_codes.AJ, sco_codes.ADJ] + self.codes
|
||||
self.explanation = "notes insuffisantes"
|
||||
|
||||
def record(self, code: str, no_overwrite=False):
|
||||
def record(self, code: str, no_overwrite=False) -> bool:
|
||||
"""Enregistre le code jury pour cette UE.
|
||||
Si no_overwrite, n'enregistre pas s'il y a déjà un code.
|
||||
Return: True si code enregistré (modifié)
|
||||
"""
|
||||
if code and not code in self.codes:
|
||||
raise ScoValueError(
|
||||
@ -1213,7 +1238,7 @@ class DecisionsProposeesUE(DecisionsProposees):
|
||||
)
|
||||
if code == self.code_valide or (self.code_valide is not None and no_overwrite):
|
||||
self.recorded = True
|
||||
return # no change
|
||||
return False # no change
|
||||
self.erase()
|
||||
if code is None:
|
||||
self.validation = None
|
||||
@ -1244,6 +1269,7 @@ class DecisionsProposeesUE(DecisionsProposees):
|
||||
sco_cache.invalidate_formsemestre(formsemestre_id=self.formsemestre.id)
|
||||
self.code_valide = code # mise à jour
|
||||
self.recorded = True
|
||||
return True
|
||||
|
||||
def erase(self):
|
||||
"""Efface la décision de jury de cet étudiant pour cette UE"""
|
||||
|
@ -18,29 +18,29 @@ from app.scodoc.sco_exceptions import ScoValueError
|
||||
def formsemestre_validation_auto_but(
|
||||
formsemestre: FormSemestre, only_adm: bool = True, no_overwrite: bool = True
|
||||
) -> int:
|
||||
"""Calcul automatique des décisions de jury sur une année BUT.
|
||||
Ne modifie jamais de décisions de l'année scolaire précédente, même
|
||||
"""Calcul automatique des décisions de jury sur une "année" BUT.
|
||||
|
||||
- N'enregistre jamais de décisions de l'année scolaire précédente, même
|
||||
si on a des RCUE "à cheval".
|
||||
Normalement, only_adm est True et on n'enregistre que les décisions ADM (de droit).
|
||||
Si only_adm est faux, on enregistre la première décision proposée par ScoDoc
|
||||
(mode à n'utiliser que pour les tests)
|
||||
- Ne modifie jamais de décisions déjà enregistrées (sauf si no_overwrite est faux,
|
||||
ce qui est utilisé pour certains tests unitaires).
|
||||
- Normalement, only_adm est True et on n'enregistre que les décisions validantes
|
||||
de droit: ADM ou CMP.
|
||||
En revanche, si only_adm est faux, on enregistre la première décision proposée par ScoDoc
|
||||
(mode à n'utiliser que pour les tests unitaires vérifiant la saisie des jurys)
|
||||
|
||||
Si no_overwrite est vrai (défaut), ne ré-écrit jamais les codes déjà enregistrés
|
||||
(utiliser faux pour certains tests)
|
||||
|
||||
Returns: nombre d'étudiants "admis"
|
||||
Returns: nombre d'étudiants pour lesquels on a enregistré au moins un code.
|
||||
"""
|
||||
if not formsemestre.formation.is_apc():
|
||||
raise ScoValueError("fonction réservée aux formations BUT")
|
||||
nb_admis = 0
|
||||
nb_etud_modif = 0
|
||||
with sco_cache.DeferredSemCacheManager():
|
||||
for etudid in formsemestre.etuds_inscriptions:
|
||||
etud: Identite = Identite.query.get(etudid)
|
||||
deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre)
|
||||
if deca.admis: # année réussie
|
||||
nb_admis += 1
|
||||
if deca.admis or not only_adm:
|
||||
deca.record_all(no_overwrite=no_overwrite)
|
||||
nb_etud_modif += deca.record_all(
|
||||
no_overwrite=no_overwrite, only_validantes=only_adm
|
||||
)
|
||||
|
||||
db.session.commit()
|
||||
return nb_admis
|
||||
return nb_etud_modif
|
||||
|
@ -563,9 +563,8 @@ class JuryPE(object):
|
||||
dec = nt.get_etud_decision_sem(
|
||||
etudid
|
||||
) # quelle est la décision du jury ?
|
||||
if dec and dec["code"] in list(
|
||||
sco_codes_parcours.CODES_SEM_VALIDES.keys()
|
||||
): # isinstance( sesMoyennes[i+1], float) and
|
||||
if dec and (dec["code"] in sco_codes_parcours.CODES_SEM_VALIDES):
|
||||
# isinstance( sesMoyennes[i+1], float) and
|
||||
# mT = sesMoyennes[i+1] # substitue la moyenne si le semestre suivant est "valide"
|
||||
leFid = sem["formsemestre_id"]
|
||||
else:
|
||||
|
@ -187,20 +187,23 @@ CODES_EXPL = {
|
||||
|
||||
# Les codes de semestres:
|
||||
CODES_JURY_SEM = {ADC, ADJ, ADM, AJ, ATB, ATJ, ATT, DEF, NAR, RAT}
|
||||
CODES_SEM_VALIDES = {ADM: True, ADC: True, ADJ: True} # semestre validé
|
||||
CODES_SEM_ATTENTES = {ATT: True, ATB: True, ATJ: True} # semestre en attente
|
||||
CODES_SEM_VALIDES_DE_DROIT = {ADM, ADC}
|
||||
CODES_SEM_VALIDES = CODES_SEM_VALIDES_DE_DROIT | {ADJ} # semestre validé
|
||||
CODES_SEM_ATTENTES = {ATT, ATB, ATJ} # semestre en attente
|
||||
|
||||
CODES_SEM_REO = {NAR: 1} # reorientation
|
||||
CODES_SEM_REO = {NAR} # reorientation
|
||||
|
||||
CODES_UE_VALIDES = {ADM: True, CMP: True, ADJ: True, ADJR: True}
|
||||
CODES_UE_VALIDES_DE_DROIT = {ADM, CMP} # validation "de droit"
|
||||
CODES_UE_VALIDES = CODES_UE_VALIDES_DE_DROIT | {ADJ, ADJR}
|
||||
"UE validée"
|
||||
|
||||
CODES_RCUE_VALIDES = {ADM, CMP, ADJ}
|
||||
CODES_RCUE_VALIDES_DE_DROIT = {ADM, CMP}
|
||||
CODES_RCUE_VALIDES = CODES_RCUE_VALIDES_DE_DROIT | {ADJ}
|
||||
"Niveau RCUE validé"
|
||||
|
||||
# Pour le BUT:
|
||||
CODES_ANNEE_BUT_VALIDES_DE_DROIT = {ADM, PASD}
|
||||
CODES_ANNEE_ARRET = {DEF, DEM, ABAN, ABL}
|
||||
CODES_RCUE = {ADM, AJ, CMP}
|
||||
BUT_BARRE_UE8 = 8.0 - NOTES_TOLERANCE
|
||||
BUT_BARRE_UE = BUT_BARRE_RCUE = 10.0 - NOTES_TOLERANCE
|
||||
BUT_RCUE_SUFFISANT = 8.0 - NOTES_TOLERANCE
|
||||
@ -230,17 +233,17 @@ BUT_CODES_ORDERED = {
|
||||
|
||||
def code_semestre_validant(code: str) -> bool:
|
||||
"Vrai si ce CODE entraine la validation du semestre"
|
||||
return CODES_SEM_VALIDES.get(code, False)
|
||||
return code in CODES_SEM_VALIDES
|
||||
|
||||
|
||||
def code_semestre_attente(code: str) -> bool:
|
||||
"Vrai si ce CODE est un code d'attente (semestre validable plus tard par jury ou compensation)"
|
||||
return CODES_SEM_ATTENTES.get(code, False)
|
||||
return code in CODES_SEM_ATTENTES
|
||||
|
||||
|
||||
def code_ue_validant(code: str) -> bool:
|
||||
"Vrai si ce code d'UE est validant (ie attribue les ECTS)"
|
||||
return CODES_UE_VALIDES.get(code, False)
|
||||
return code in CODES_UE_VALIDES
|
||||
|
||||
|
||||
DEVENIR_EXPL = {
|
||||
|
@ -890,7 +890,7 @@ def formsemestre_validate_ues(formsemestre_id, etudid, code_etat_sem, assiduite)
|
||||
car ils ne dépendent que de la note d'UE et de la validation ou non du semestre.
|
||||
Les UE des semestres NON ASSIDUS ne sont jamais validées (code AJ).
|
||||
"""
|
||||
valid_semestre = CODES_SEM_VALIDES.get(code_etat_sem, False)
|
||||
valid_semestre = code_etat_sem in CODES_SEM_VALIDES
|
||||
cnx = ndb.GetDBConnexion()
|
||||
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||
|
@ -781,8 +781,8 @@ def form_decision_manuelle(Se, formsemestre_id, etudid, desturl="", sortcol=None
|
||||
)
|
||||
|
||||
# Choix code semestre:
|
||||
codes = list(sco_codes_parcours.CODES_JURY_SEM)
|
||||
codes.sort() # fortuitement, cet ordre convient bien !
|
||||
codes = sorted(sco_codes_parcours.CODES_JURY_SEM)
|
||||
# fortuitement, cet ordre convient bien !
|
||||
|
||||
H.append(
|
||||
'<tr><td>Code semestre: </td><td><select name="code_etat"><option value="" selected>Choisir...</option>'
|
||||
|
@ -8,14 +8,22 @@
|
||||
|
||||
{% block app_content %}
|
||||
|
||||
<h2>Calcul automatique des décisions de jury annuelle BUT</h2>
|
||||
<h2>Calcul automatique des décisions de jury du BUT</h2>
|
||||
<ul>
|
||||
<li>Seuls les étudiants qui valident l'année seront affectés:
|
||||
tous les niveaux de compétences (RCUE) validables
|
||||
(moyenne annuelle au dessus de 10);
|
||||
<li>N'enregistre jamais de décisions de l'année scolaire précédente, même
|
||||
si on a des RCUE "à cheval" sur deux années.
|
||||
</li>
|
||||
<li>Ne modifie jamais de décisions déjà enregistrées.
|
||||
</li>
|
||||
<li>N'enregistre que les décisions <b>validantes de droit: ADM ou CMP</b>.
|
||||
</li>
|
||||
<li>L'assiduité n'est <b>pas</b> prise en compte.
|
||||
</li>
|
||||
<li>l'assiduité n'est <b>pas</b> prise en compte;</li>
|
||||
</ul>
|
||||
<p>
|
||||
En conséquence, saisir ensuite <b>manuellement les décisions manquantes</b>,
|
||||
notamment sur les UEs en dessous de 10.
|
||||
</p>
|
||||
<p class="warning">
|
||||
Il est nécessaire de relire soigneusement les décisions à l'issue de cette procédure !
|
||||
</p>
|
||||
|
@ -2548,10 +2548,10 @@ def formsemestre_validation_auto_but(formsemestre_id: int = None):
|
||||
form = jury_but_forms.FormSemestreValidationAutoBUTForm()
|
||||
if request.method == "POST":
|
||||
if not form.cancel.data:
|
||||
nb_admis = jury_but_validation_auto.formsemestre_validation_auto_but(
|
||||
nb_etud_modif = jury_but_validation_auto.formsemestre_validation_auto_but(
|
||||
formsemestre
|
||||
)
|
||||
flash(f"Décisions enregistrées ({nb_admis} admis)")
|
||||
flash(f"Décisions enregistrées ({nb_etud_modif} étudiants modifiés)")
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_saisie_jury",
|
||||
@ -2563,7 +2563,7 @@ def formsemestre_validation_auto_but(formsemestre_id: int = None):
|
||||
"but/formsemestre_validation_auto_but.html",
|
||||
form=form,
|
||||
sco=ScoData(formsemestre=formsemestre),
|
||||
title=f"Calcul automatique jury BUT",
|
||||
title="Calcul automatique jury BUT",
|
||||
)
|
||||
|
||||
|
||||
@ -2641,7 +2641,17 @@ def formsemestre_validation_auto(formsemestre_id):
|
||||
message="<p>Opération non autorisée pour %s</h2>" % current_user,
|
||||
dest_url=scu.ScoURL(),
|
||||
)
|
||||
|
||||
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
||||
).first_or_404()
|
||||
if formsemestre.formation.is_apc():
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_validation_auto_but",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
)
|
||||
)
|
||||
return sco_formsemestre_validation.formsemestre_validation_auto(formsemestre_id)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user