forked from ScoDoc/ScoDoc
Jury BUT:
- Modification gestion de l'enregistrement des codes. - Signale quand un RCUE change de code. - Calcul auto du jury: peut modifier les décisions RCUE.
This commit is contained in:
parent
c45abc33cc
commit
438caf1052
@ -278,11 +278,15 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
)
|
||||
|
||||
if self.formsemestre_impair is not None:
|
||||
self.validation = ApcValidationAnnee.query.filter_by(
|
||||
self.validation = (
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
etudid=self.etud.id,
|
||||
formation_id=self.formsemestre.formation_id,
|
||||
ordre=self.annee_but,
|
||||
).first()
|
||||
)
|
||||
.join(Formation)
|
||||
.filter_by(formation_code=self.formsemestre.formation.formation_code)
|
||||
.first()
|
||||
)
|
||||
else:
|
||||
self.validation = None
|
||||
if self.validation is not None:
|
||||
@ -721,7 +725,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
et qu'il n'y en a pas déjà, enregistre ceux par défaut.
|
||||
"""
|
||||
log("jury_but.DecisionsProposeesAnnee.record_form")
|
||||
code_annee = None
|
||||
code_annee = self.codes[0] # si pas dans le form, valeur par defaut
|
||||
codes_rcues = [] # [ (dec_rcue, code), ... ]
|
||||
codes_ues = [] # [ (dec_ue, code), ... ]
|
||||
for key in form:
|
||||
@ -753,16 +757,15 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
dec_ue.record(code)
|
||||
for dec_rcue, code in codes_rcues:
|
||||
dec_rcue.record(code)
|
||||
self.record(code_annee) # XXX , mark_recorded=False)
|
||||
self.record(code_annee)
|
||||
self.record_autorisation_inscription(code_annee)
|
||||
self.record_all()
|
||||
self.recorded = True
|
||||
|
||||
db.session.commit()
|
||||
|
||||
def record(self, code: str, no_overwrite=False, mark_recorded: bool = True) -> bool:
|
||||
def record(self, code: str, mark_recorded: bool = True) -> 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.
|
||||
Si mark_recorded est vrai, positionne self.recorded
|
||||
"""
|
||||
@ -773,14 +776,15 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
f"code annee <tt>{html.escape(code)}</tt> invalide pour formsemestre {html.escape(self.formsemestre)}"
|
||||
)
|
||||
|
||||
if code != self.code_valide and (self.code_valide is None or not no_overwrite):
|
||||
if code != self.code_valide:
|
||||
# Enregistrement du code annuel BUT
|
||||
if code is None:
|
||||
if self.validation:
|
||||
db.session.delete(self.validation)
|
||||
db.session.commit()
|
||||
if code is None:
|
||||
self.validation = None
|
||||
db.session.commit()
|
||||
else:
|
||||
if self.validation is None:
|
||||
self.validation = ApcValidationAnnee(
|
||||
etudid=self.etud.id,
|
||||
formsemestre=self.formsemestre_impair,
|
||||
@ -789,7 +793,17 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
annee_scolaire=self.annee_scolaire(),
|
||||
code=code,
|
||||
)
|
||||
else: # Update validation année BUT
|
||||
self.validation.etud = self.etud
|
||||
self.validation.formsemestre = self.formsemestre_impair
|
||||
self.validation.formation_id = self.formsemestre.formation_id
|
||||
self.validation.ordre = self.annee_but
|
||||
self.validation.annee_scolaire = self.annee_scolaire()
|
||||
self.validation.code = code
|
||||
self.validation.date = datetime.now()
|
||||
|
||||
db.session.add(self.validation)
|
||||
db.session.commit()
|
||||
log(f"Recording {self}: {code}")
|
||||
Scolog.logdb(
|
||||
method="jury_but",
|
||||
@ -840,9 +854,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
)
|
||||
return res and self.etud.id in res.get_etudids_attente()
|
||||
|
||||
def record_all(
|
||||
self, no_overwrite: bool = True, only_validantes: bool = False
|
||||
) -> bool:
|
||||
def record_all(self, 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.
|
||||
@ -868,9 +880,8 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
# 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
|
||||
modif |= dec_ue.record(code, no_overwrite=no_overwrite)
|
||||
# enregistre le code jury
|
||||
modif |= dec_ue.record(code)
|
||||
# RCUE :
|
||||
for dec_rcue in self.decisions_rcue_by_niveau.values():
|
||||
code = dec_rcue.codes[0] if dec_rcue.codes else None
|
||||
@ -888,17 +899,15 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
)
|
||||
)
|
||||
):
|
||||
modif |= dec_rcue.record(code, no_overwrite=no_overwrite)
|
||||
modif |= dec_rcue.record(code)
|
||||
# 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
|
||||
if (
|
||||
not only_validantes
|
||||
) or code in sco_codes.CODES_ANNEE_BUT_VALIDES_DE_DROIT:
|
||||
modif |= self.record(code, no_overwrite=no_overwrite)
|
||||
modif |= self.record(code)
|
||||
self.record_autorisation_inscription(code)
|
||||
return modif
|
||||
|
||||
@ -1133,7 +1142,7 @@ 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) -> bool:
|
||||
def record(self, code: str) -> bool:
|
||||
"""Enregistre le code RCUE.
|
||||
Note:
|
||||
- si le RCUE est ADJ, les UE non validées sont passées à ADJ
|
||||
@ -1147,7 +1156,7 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
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):
|
||||
if code == self.code_valide:
|
||||
self.recorded = True
|
||||
return False # no change
|
||||
parcours_id = self.parcour.id if self.parcour is not None else None
|
||||
@ -1322,11 +1331,15 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
if annee_inferieure < 1:
|
||||
return
|
||||
# Garde-fou: Année déjà validée ?
|
||||
validations_annee: ApcValidationAnnee = ApcValidationAnnee.query.filter_by(
|
||||
validations_annee: ApcValidationAnnee = (
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
etudid=self.etud.id,
|
||||
ordre=annee_inferieure,
|
||||
formation_id=self.rcue.formsemestre_1.formation_id,
|
||||
).all()
|
||||
)
|
||||
.join(Formation)
|
||||
.filter_by(formation_code=self.rcue.formsemestre_1.formation.code)
|
||||
.all()
|
||||
)
|
||||
if len(validations_annee) > 1:
|
||||
log(
|
||||
f"warning: {len(validations_annee)} validations d'année\n{validations_annee}"
|
||||
@ -1519,16 +1532,15 @@ 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) -> bool:
|
||||
def record(self, code: str) -> 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(
|
||||
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):
|
||||
if code == self.code_valide:
|
||||
self.recorded = True
|
||||
return False # no change
|
||||
self.erase()
|
||||
@ -1627,7 +1639,6 @@ class BUTCursusEtud: # WIP TODO
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
etudid=self.etud.id,
|
||||
ordre=ordre,
|
||||
formation_id=self.formsemestre.formation_id,
|
||||
)
|
||||
.join(Formation)
|
||||
.filter(
|
||||
|
@ -16,14 +16,12 @@ from app.scodoc.sco_exceptions import ScoValueError
|
||||
|
||||
|
||||
def formsemestre_validation_auto_but(
|
||||
formsemestre: FormSemestre, only_adm: bool = True, no_overwrite: bool = True
|
||||
formsemestre: FormSemestre, only_adm: bool = True
|
||||
) -> int:
|
||||
"""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".
|
||||
- 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
|
||||
@ -38,9 +36,7 @@ def formsemestre_validation_auto_but(
|
||||
for etudid in formsemestre.etuds_inscriptions:
|
||||
etud = Identite.get_etud(etudid)
|
||||
deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre)
|
||||
nb_etud_modif += deca.record_all(
|
||||
no_overwrite=no_overwrite, only_validantes=only_adm
|
||||
)
|
||||
nb_etud_modif += deca.record_all(only_validantes=only_adm)
|
||||
|
||||
db.session.commit()
|
||||
return nb_etud_modif
|
||||
|
@ -155,6 +155,7 @@ def _gen_but_select(
|
||||
disabled: bool = False,
|
||||
klass: str = "",
|
||||
data: dict = {},
|
||||
code_valide_label: str = "",
|
||||
) -> str:
|
||||
"Le menu html select avec les codes"
|
||||
# if disabled: # mauvaise idée car le disabled est traité en JS
|
||||
@ -164,7 +165,10 @@ def _gen_but_select(
|
||||
f"""<option value="{code}"
|
||||
{'selected' if code == code_valide else ''}
|
||||
class="{'recorded' if code == code_valide else ''}"
|
||||
>{code}</option>"""
|
||||
>{code
|
||||
if ((code != code_valide) or not code_valide_label)
|
||||
else code_valide_label
|
||||
}</option>"""
|
||||
for code in codes
|
||||
]
|
||||
)
|
||||
@ -246,6 +250,7 @@ def _gen_but_rcue(dec_rcue: DecisionsProposeesRCUE, niveau: ApcNiveau) -> str:
|
||||
"""
|
||||
|
||||
code_propose_menu = dec_rcue.code_valide # le code enregistré
|
||||
code_valide_label = code_propose_menu
|
||||
if dec_rcue.validation:
|
||||
if dec_rcue.code_valide == dec_rcue.codes[0]:
|
||||
descr_validation = dec_rcue.validation.html()
|
||||
@ -257,6 +262,9 @@ def _gen_but_rcue(dec_rcue: DecisionsProposeesRCUE, niveau: ApcNiveau) -> str:
|
||||
> sco_codes.BUT_CODES_ORDER[dec_rcue.code_valide]
|
||||
):
|
||||
code_propose_menu = dec_rcue.codes[0]
|
||||
code_valide_label = (
|
||||
f"{dec_rcue.codes[0]} (actuel {dec_rcue.code_valide})"
|
||||
)
|
||||
scoplement = f"""<div class="scoplement">{descr_validation}</div>"""
|
||||
else:
|
||||
scoplement = "" # "pas de validation"
|
||||
@ -282,7 +290,8 @@ def _gen_but_rcue(dec_rcue: DecisionsProposeesRCUE, niveau: ApcNiveau) -> str:
|
||||
code_propose_menu,
|
||||
disabled=True,
|
||||
klass="manual code_rcue",
|
||||
data = { "niveau_id" : str(niveau.id)}
|
||||
data = { "niveau_id" : str(niveau.id)},
|
||||
code_valide_label = code_valide_label,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -223,8 +223,15 @@ BUT_CODES_PASSAGE = {
|
||||
# les codes, du plus "défavorable" à l'étudiant au plus favorable:
|
||||
# (valeur par défaut 0)
|
||||
BUT_CODES_ORDER = {
|
||||
NAR: 0,
|
||||
ABAN: 0,
|
||||
ABL: 0,
|
||||
DEM: 0,
|
||||
DEF: 0,
|
||||
EXCLU: 0,
|
||||
NAR: 0,
|
||||
UEBSL: 0,
|
||||
RAT: 5,
|
||||
RED: 6,
|
||||
AJ: 10,
|
||||
ATJ: 20,
|
||||
CMP: 50,
|
||||
@ -233,7 +240,7 @@ BUT_CODES_ORDER = {
|
||||
PASD: 60,
|
||||
ADJR: 90,
|
||||
ADSUP: 90,
|
||||
ADJ: 100,
|
||||
ADJ: 90,
|
||||
ADM: 100,
|
||||
}
|
||||
|
||||
|
@ -495,7 +495,9 @@ class ApoEtud(dict):
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
formsemestre_id=formsemestre.id,
|
||||
etudid=self.etud["etudid"],
|
||||
formation_id=self.cur_sem["formation_id"],
|
||||
formation_id=self.cur_sem[
|
||||
"formation_id"
|
||||
], # XXX utiliser formation_code
|
||||
).first()
|
||||
)
|
||||
self.is_nar = (
|
||||
|
@ -168,6 +168,7 @@ div.but_niveau_ue.recorded_different,
|
||||
div.but_niveau_rcue.recorded_different {
|
||||
box-shadow: 0 0 0 3px red;
|
||||
outline: dashed 3px var(--color-recorded);
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
div.but_niveau_ue.annee_prec {
|
||||
|
@ -1128,7 +1128,8 @@ div.sco_help {
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
font-style: italic;
|
||||
background-color: rgb(200, 200, 220);
|
||||
max-width: 800px;
|
||||
background-color: rgb(209, 255, 214);
|
||||
}
|
||||
|
||||
div.vertical_spacing_but {
|
||||
|
@ -8,6 +8,8 @@
|
||||
(transcription paramétrable par votre administrateur ScoDoc).
|
||||
</p>
|
||||
<div class="but_doc_section">Codes d'année</div>
|
||||
<em>Les codes d'année BUT sont associés à la formation et non au semestre: on ne valide
|
||||
qu'une seule fois BUT1, BUT2 puis BUT3.</em>
|
||||
<div class="but_doc">
|
||||
<table>
|
||||
<tr>
|
||||
@ -100,7 +102,8 @@
|
||||
</div>
|
||||
|
||||
<div class="but_doc_section">Codes RCUE (niveaux de compétences annuels)</div>
|
||||
|
||||
<em>Les codes de RCUE sont associés à la formation: chaque niveau de compétence
|
||||
est validé une fois au maximum. En cas de redoublement, le code RCUE peut changer.</em>
|
||||
<div class="but_doc">
|
||||
<table>
|
||||
<tr>
|
||||
@ -161,7 +164,9 @@
|
||||
</div>
|
||||
|
||||
<div class="but_doc_section">Codes des Unités d'Enseignement (UE)</div>
|
||||
|
||||
<em>Les codes d'UE sont associés aux UE d'un semestre. En cas de redoublement,
|
||||
l'UE antérieure garde son code, non écrasé par le redoublement. Chaque UE suivie a son code.
|
||||
</em>
|
||||
<div class="but_doc">
|
||||
<table>
|
||||
<tr>
|
||||
|
@ -8,19 +8,27 @@
|
||||
|
||||
{% block app_content %}
|
||||
|
||||
<div class="sco_help">
|
||||
<h2>Calcul automatique des décisions de jury du BUT</h2>
|
||||
<ul>
|
||||
<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><b>Attention: peut modifier des décisions déjà enregistrées</b>, si la
|
||||
validation de droit est calculée. Par exemple, vous aviez saisi <b>RAT</b>
|
||||
pour un étudiant dont les moyennes d'UE dépassent 10 mais qui pour une
|
||||
raison particulière ne valide pas son année. Le calcul automatique peut
|
||||
remplacer ce <b>RAT</b> par un <b>ADM</b>, ScoDoc considérant que les
|
||||
conditions sont satisfaites. On peut éviter cela en laissant une note de
|
||||
l'étudiant en ATTente.
|
||||
</li>
|
||||
|
||||
<li>N'enregistre que les décisions <b>validantes de droit: ADM ou CMP</b>.
|
||||
</li>
|
||||
<li>N'enregistre pas de décision si l'étudiant a une ou plusieurs notes en ATTente.
|
||||
</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>,
|
||||
@ -34,9 +42,10 @@
|
||||
<li>Il est nécessaire de relire soigneusement les décisions à l'issue de cette procédure !</li>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<div class="col-md-10">
|
||||
{{ wtf.quick_form(form) }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -108,9 +108,7 @@ def test_but_jury_GEII_lyon(test_client):
|
||||
# Saisie de toutes les décisions de jury "automatiques"
|
||||
# et vérification des résultats attendus:
|
||||
for formsemestre in formsemestres:
|
||||
formsemestre_validation_auto_but(
|
||||
formsemestre, only_adm=False, no_overwrite=False
|
||||
)
|
||||
formsemestre_validation_auto_but(formsemestre, only_adm=False)
|
||||
yaml_setup_but.but_test_jury(formsemestre, doc)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user