Jury BUT: amélioration gestion redoublants + #547 (WIP)

This commit is contained in:
Emmanuel Viennet 2023-01-08 15:36:05 -03:00 committed by iziram
parent cda20c27b2
commit cf18520e9c
7 changed files with 99 additions and 52 deletions

View File

@ -350,8 +350,9 @@ class DecisionsProposeesAnnee(DecisionsProposees):
)
"vrai si l'année est réussie, tous niveaux validables ou validés par le jury"
self.valide_moitie_rcue = self.nb_validables > (self.nb_competences // 2)
"Peut passer si plus de la moitié validables et tous > 8"
"Vrai si plus de la moitié des RCUE validables"
self.passage_de_droit = self.valide_moitie_rcue and (self.nb_rcues_under_8 == 0)
"Vrai si peut passer dans l'année BUT suivante: plus de la moitié validables et tous > 8"
# XXX TODO ajouter condition pour passage en S5
# Enfin calcule les codes des UE:
@ -752,8 +753,9 @@ class DecisionsProposeesAnnee(DecisionsProposees):
pour cette année: décisions d'UE, de RCUE, d'année,
et autorisations d'inscription émises.
Efface même si étudiant DEM ou DEF.
Si à cheval, n'efface que pour le semestre d'origine du deca.
"""
if only_one_sem:
if only_one_sem or self.a_cheval:
# N'efface que les autorisations venant de ce semestre,
# et les validations de ses UEs
ScolarAutorisationInscription.delete_autorisation_etud(
@ -906,6 +908,19 @@ class DecisionsProposeesRCUE(DecisionsProposees):
or dec_prop_annee.formsemestre_pair.modalite == "EXT"
):
self.codes.insert(0, sco_codes.ADM)
# S'il y a une décision enregistrée: si elle est plus favorable que celle que l'on
# proposerait, la place en tête.
# Sinon, la place en seconde place
if self.code_valide and self.code_valide != self.codes[0]:
code_default = self.codes[0]
if self.code_valide in self.codes:
self.codes.remove(self.code_valide)
if sco_codes.BUT_CODES_ORDERED.get(
self.code_valide, 0
) > sco_codes.BUT_CODES_ORDERED.get(code_default, 0):
self.codes.insert(0, self.code_valide)
else:
self.codes.insert(1, self.code_valide)
def record(self, code: str, no_overwrite=False):
"""Enregistre le code"""

View File

@ -41,16 +41,8 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
Si pas read_only, menus sélection codes jury.
"""
H = []
if deca.code_valide and not read_only:
erase_span = f"""<a href="{
url_for("notes.formsemestre_jury_but_erase",
scodoc_dept=g.scodoc_dept, formsemestre_id=deca.formsemestre_id,
etudid=deca.etud.id)}" class="stdlink">effacer décisions</a>"""
else:
erase_span = ""
H.append("""<div class="but_section_annee">""")
if deca.jury_annuel:
H.append(
f"""
<div>
@ -59,14 +51,11 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
disabled=True, klass="manual")
}
<span>({'non ' if deca.code_valide is None else ''}enregistrée)</span>
<span>{erase_span}</span>
</div>
"""
)
div_explanation = f"""<div class="but_explanation">{deca.explanation}</div>"""
else:
H.append("""<div><em>Pas de décision annuelle (sem. impair).</em></div>""")
div_explanation = ""
H.append("""</div>""")
formsemestre_1 = deca.formsemestre_impair
@ -245,9 +234,16 @@ def _gen_but_rcue(dec_rcue: DecisionsProposeesRCUE, niveau: ApcNiveau) -> str:
else ""
)
# Déjà enregistré ?
niveau_rcue_class = ""
if dec_rcue.code_valide is not None and dec_rcue.codes:
if dec_rcue.code_valide == dec_rcue.codes[0]:
niveau_rcue_class = "recorded"
else:
niveau_rcue_class = "recorded_different"
return f"""
<div class="but_niveau_rcue
{'recorded' if dec_rcue.code_valide is not None else ''}
<div class="but_niveau_rcue {niveau_rcue_class}
">
<div class="but_note with_scoplement">
<div>{scu.fmt_note(dec_rcue.rcue.moy_rcue)}</div>
@ -351,7 +347,7 @@ def jury_but_semestriel(
warning = ""
H = [
html_sco_header.sco_header(
page_title="Validation BUT",
page_title=f"Validation BUT S{formsemestre.semestre_id}",
formsemestre_id=formsemestre.id,
etudid=etud.id,
cssstyles=("css/jury_but.css",),
@ -376,21 +372,21 @@ def jury_but_semestriel(
{warning}
</div>
<form method="POST">
<form method="post" id="jury_but">
""",
]
if (not read_only) and any([dec.code_valide for dec in decisions_ues.values()]):
erase_span = f"""<a href="{
url_for("notes.formsemestre_jury_but_erase",
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id,
etudid=etud.id, only_one_sem=1)}" class="stdlink">effacer les décisions enregistrées</a>"""
etudid=etud.id, only_one_sem=1)
}" class="stdlink">effacer les décisions enregistrées</a>"""
else:
erase_span = "Cet étudiant n'a aucune décision enregistrée pour ce semestre."
H.append(
f"""
<div class="but_section_annee">
<span>{erase_span}</span>
</div>
<div><b>Unités d'enseignement de S{formsemestre.semestre_id}:</b></div>
"""
@ -447,9 +443,10 @@ def jury_but_semestriel(
else:
H.append("""<div class="help">dernier semestre de la formation.</div>""")
H.append(
"""
f"""
<div class="but_buttons">
<input type="submit" value="Enregistrer ces décisions">
<span><input type="submit" value="Enregistrer ces décisions"></span>
<span>{erase_span}</span>
</div>
"""
)

View File

@ -180,8 +180,9 @@ class RegroupementCoherentUE:
return self.query_validations().count() > 0
def est_compensable(self):
"""Vrai si ce RCUE est validable par compensation
c'est à dire que sa moyenne est > 10 avec une UE < 10
"""Vrai si ce RCUE est validable (uniquement) par compensation
c'est à dire que sa moyenne est > 10 avec une UE < 10.
Note: si ADM, est_compensable est faux.
"""
return (
(self.moy_rcue is not None)

View File

@ -205,6 +205,20 @@ BUT_CODES_PASSAGE = {
PAS1NCI,
ATJ,
}
# les codes, du plus "défavorable" à l'étudiant au plus favorable:
# (valeur par défaut 0)
BUT_CODES_ORDERED = {
"NAR": 0,
"DEF": 0,
"AJ": 10,
"ATJ": 20,
"CMP": 50,
"ADC": 50,
"PASD": 50,
"PAS1NCI": 60,
"ADJ": 100,
"ADM": 100,
}
def code_semestre_validant(code: str) -> bool:

View File

@ -22,7 +22,7 @@
margin-left: 32px;
display: inline-grid;
grid-template-columns: repeat(4, auto);
gap: 4px;
gap: 8px;
}
.but_annee_caption {
@ -143,8 +143,14 @@ div.but_code {
div.but_niveau_ue.recorded,
div.but_niveau_rcue.recorded {
border-color: rgb(136, 252, 136);
border-width: 2px;
border-color: rgb(0, 169, 0);
border-width: 3px;
}
div.but_niveau_ue.recorded_different,
div.but_niveau_rcue.recorded_different {
box-shadow: 0 0 0 3px red;
outline: dashed 3px rgb(0, 169, 0);
}
div.but_niveau_ue.annee_prec {
@ -160,6 +166,7 @@ div.but_buttons {
}
div.but_buttons span {
margin-left: 16px;
margin-right: 16px;
}

View File

@ -63,6 +63,8 @@ $(function () {
// ----- Etat du formulaire jury pour éviter sortie sans enregistrer
let FORM_STATE = "";
let IS_SUBMITTING = false;
// Une chaine décrivant l'état du form
function get_form_state() {
let codes = [];
@ -73,13 +75,19 @@ function get_form_state() {
$('document').ready(function () {
FORM_STATE = get_form_state();
document.querySelector("form#jury_but").addEventListener('submit', jury_form_submit);
});
function is_modified() {
return FORM_STATE != get_form_state();
}
function jury_form_submit(event) {
IS_SUBMITTING = true;
}
window.addEventListener("beforeunload", function (e) {
if (is_modified()) {
if ((!IS_SUBMITTING) && is_modified()) {
var confirmationMessage = 'Changements non enregistrés !';
(e || window.event).returnValue = confirmationMessage;
return confirmationMessage;

View File

@ -2327,17 +2327,17 @@ def formsemestre_validation_but(
# provisoires avec NEXT et PREV
try:
etudid = int(etudid)
except:
except ValueError:
abort(404, "invalid etudid")
read_only = not sco_permissions_check.can_validate_sem(formsemestre_id)
# --- Navigation
prev = f"""{scu.EMO_PREV_ARROW}&nbsp;<a href="{url_for(
prev_lnk = f"""{scu.EMO_PREV_ARROW}&nbsp;<a href="{url_for(
"notes.formsemestre_validation_but", scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id, etudid="PREV"
)}" class="stdlink"">précédent</a>
"""
next = f"""<a href="{url_for(
next_lnk = f"""<a href="{url_for(
"notes.formsemestre_validation_but", scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id, etudid="NEXT"
)}" class="stdlink"">suivant</a>&nbsp;{scu.EMO_NEXT_ARROW}
@ -2345,7 +2345,7 @@ def formsemestre_validation_but(
navigation_div = f"""
<div class="but_navigation">
<div class="prev">
{prev}
{prev_lnk}
</div>
<div class="back_list">
<a href="{url_for(
@ -2354,21 +2354,20 @@ def formsemestre_validation_but(
)}" class="stdlink">retour à la liste</a>
</div>
<div class="next">
{next}
{next_lnk}
</div>
</div>
"""
H = [
html_sco_header.sco_header(
page_title="Validation BUT",
page_title=f"Validation BUT S{formsemestre.semestre_id}",
formsemestre_id=formsemestre_id,
etudid=etudid,
cssstyles=("css/jury_but.css",),
javascripts=("js/jury_but.js",),
),
f"""
<div class="jury_but">
"""<div class="jury_but">
""",
]
@ -2401,7 +2400,6 @@ def formsemestre_validation_but(
deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre)
if len(deca.rcues_annee) == 0:
# raise ScoValueError("année incomplète: pas de jury BUT annuel possible")
return jury_but_view.jury_but_semestriel(
formsemestre, etud, read_only, navigation_div=navigation_div
)
@ -2421,7 +2419,7 @@ def formsemestre_validation_but(
warning = ""
if len(deca.niveaux_competences) != len(deca.decisions_rcue_by_niveau):
if deca.a_cheval:
warning += f"""<div class="warning">Attention: regroupements RCUE
warning += """<div class="warning">Attention: regroupements RCUE
entre années (redoublement).</div>"""
else:
warning += f"""<div class="warning">Attention: {len(deca.niveaux_competences)}
@ -2454,7 +2452,7 @@ def formsemestre_validation_but(
{warning}
</div>
<form method="POST">
<form method="post" id="jury_but">
"""
)
@ -2467,6 +2465,10 @@ def formsemestre_validation_but(
Les champs entourés en vert sont enregistrés.</div>"""
)
else:
erase_span = f"""<a href="{
url_for("notes.formsemestre_jury_but_erase",
scodoc_dept=g.scodoc_dept, formsemestre_id=deca.formsemestre_id,
etudid=deca.etud.id)}" class="stdlink">effacer décisions</a>"""
H.append(
f"""<div class="but_settings">
<input type="checkbox" onchange="enable_manual_codes(this)">
@ -2477,7 +2479,8 @@ def formsemestre_validation_but(
</div>
<div class="but_buttons">
<input type="submit" value="Enregistrer ces décisions">
<span><input type="submit" value="Enregistrer ces décisions"></span>
<span>{erase_span}</span>
</div>
"""
)
@ -2790,7 +2793,9 @@ def formsemestre_jury_but_erase(
explanation=f"""Les validations d'UE et autorisations de passage
du semestre S{formsemestre.semestre_id} seront effacées."""
if only_one_sem
else """Les validations de toutes les UE, RCUE (compétences) et année seront effacées.""",
else """Les validations de toutes les UE, RCUE (compétences) et année seront effacées.
Les décisions de l'année scolaire précédente ne seront pas modifiées.
""",
cancel_url=dest_url,
)