From ffa5190a498b8b2fe86b84f9e92977cbfa4df599 Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet
Date: Sat, 3 Sep 2022 20:48:56 +0200
Subject: [PATCH] =?UTF-8?q?Am=C3=A9liore=20saisie=20des=20UE=20ext=C3=A9ri?=
=?UTF-8?q?eures.=20Pas=20de=20coefs=20en=20BUT.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/comp/res_common.py | 6 +-
app/scodoc/TrivialFormulator.py | 18 +-
app/scodoc/sco_formsemestre_exterieurs.py | 293 ++++++++++--------
.../formsemestre_ext_edit_ue_validations.js | 70 +++--
4 files changed, 218 insertions(+), 169 deletions(-)
diff --git a/app/comp/res_common.py b/app/comp/res_common.py
index 52d5cd902..6df763593 100644
--- a/app/comp/res_common.py
+++ b/app/comp/res_common.py
@@ -228,7 +228,7 @@ class ResultatsSemestre(ResultatsCache):
UE capitalisées.
"""
# Supposant qu'il y a peu d'UE capitalisées,
- # on va soustraire la moyenne d'UE et ajouter celle de l'UE capitalisée.
+ # on recalcule les moyennes gen des etuds ayant des UE capitalisée.
if not self.validations:
self.validations = res_sem.load_formsemestre_validations(self.formsemestre)
ue_capitalisees = self.validations.ue_capitalisees
@@ -396,10 +396,10 @@ class ResultatsSemestre(ResultatsCache):
def compute_etud_ue_coef(self, etudid: int, ue: UniteEns) -> float:
"Détermine le coefficient de l'UE pour cet étudiant."
- # calcul différent en classqiue et BUT
+ # calcul différent en classique et BUT
raise NotImplementedError()
- def get_etud_ue_cap_coef(self, etudid, ue, ue_cap):
+ def get_etud_ue_cap_coef(self, etudid, ue, ue_cap): # UNUSED in ScoDoc 9
"""Calcule le coefficient d'une UE capitalisée, pour cet étudiant,
injectée dans le semestre courant.
diff --git a/app/scodoc/TrivialFormulator.py b/app/scodoc/TrivialFormulator.py
index 9dcd9edac..522709151 100644
--- a/app/scodoc/TrivialFormulator.py
+++ b/app/scodoc/TrivialFormulator.py
@@ -28,6 +28,7 @@ def TrivialFormulator(
submitlabel="OK",
name=None,
formid="tf",
+ form_attrs="",
cssclass="",
cancelbutton=None,
submitbutton=True,
@@ -100,7 +101,8 @@ def TrivialFormulator(
submitlabel,
name,
formid,
- cssclass,
+ form_attrs=form_attrs,
+ cssclass=cssclass,
cancelbutton=cancelbutton,
submitbutton=submitbutton,
submitbuttonattributes=submitbuttonattributes,
@@ -132,6 +134,7 @@ class TF(object):
submitlabel="OK",
name=None,
formid="tf",
+ form_attrs="",
cssclass="",
cancelbutton=None,
submitbutton=True,
@@ -154,6 +157,7 @@ class TF(object):
else:
self.name = formid # 'tf'
self.formid = formid
+ self.form_attrs = form_attrs
self.cssclass = cssclass
self.cancelbutton = cancelbutton
self.submitbutton = submitbutton
@@ -408,8 +412,16 @@ class TF(object):
klass = ""
name = self.name
R.append(
- '
+ }" class="stdlink">Étudiant {etud.nomprenom}
""",
]
F = html_sco_header.sco_footer()
@@ -228,66 +231,82 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid):
"""Edition des validations d'UE et de semestre (jury)
pour un semestre extérieur.
On peut saisir pour chaque UE du programme de formation
- sa validation, son code jury, sa note, son coefficient.
+ sa validation, son code jury, sa note, son coefficient
+ (sauf en BUT où le coef. des UE est toujours égal aux ECTS).
- La moyenne générale du semestre est calculée et affichée,
+ La moyenne générale indicative du semestre est calculée et affichée,
mais pas enregistrée.
"""
- sem = sco_formsemestre.get_formsemestre(formsemestre_id)
- etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
- ues = _list_ue_with_coef_and_validations(sem, etudid)
- descr = _ue_form_description(ues, scu.get_request_args())
+ formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
+ etud = Identite.query.get_or_404(etudid)
+ ues = formsemestre.formation.ues.filter(UniteEns.type != UE_SPORT).order_by(
+ UniteEns.semestre_idx, UniteEns.numero
+ )
+ descr = _ue_form_description(formsemestre, etud, ues, scu.get_request_args())
+ initvalues = {}
if request.method == "GET":
- initvalues = {
- "note_" + str(ue["ue_id"]): ue["validation"].get("moy_ue", "") for ue in ues
- }
- else:
- initvalues = {}
+ for ue in ues:
+ validation = ScolarFormSemestreValidation.query.filter_by(
+ ue_id=ue.id, etudid=etud.id, formsemestre_id=formsemestre.id
+ ).first()
+ initvalues[f"note_{ue.id}"] = validation.moy_ue if validation else ""
+
tf = TrivialFormulator(
request.base_url,
scu.get_request_args(),
descr,
- cssclass="tf_ext_edit_ue_validations",
submitlabel="Enregistrer ces validations",
cancelbutton="Annuler",
initvalues=initvalues,
+ cssclass="tf_ext_edit_ue_validations ext_apc"
+ if formsemestre.formation.is_apc()
+ else "tf_ext_edit_ue_validations",
+ # En APC, stocke les coefficients pour l'affichage de la moyenne en direct
+ form_attrs=f"""data-ue_coefs='[{', '.join(str(ue.ects or 0) for ue in ues)}]'"""
+ if formsemestre.formation.is_apc()
+ else "",
)
if tf[0] == -1:
return "annulation "
else:
- H = _make_page(etud, sem, tf)
+ H = _make_page(etud, formsemestre, tf)
if tf[0] == 0: # premier affichage
return "\n".join(H)
else: # soumission
# simule erreur
- ok, message = _check_values(ues, tf[2])
+ ok, message = _check_values(formsemestre, ues, tf[2])
if not ok:
- H = _make_page(etud, sem, tf, message=message)
+ H = _make_page(etud, formsemestre, tf, message=message)
return "\n".join(H)
else:
# Submit
- _record_ue_validations_and_coefs(formsemestre_id, etudid, ues, tf[2])
+ _record_ue_validations_and_coefs(formsemestre, etud, ues, tf[2])
return flask.redirect(
- "formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s"
- % (formsemestre_id, etudid)
+ url_for(
+ "notes.formsemestre_bulletinetud",
+ scodoc_dept=g.scodoc_dept,
+ formsemestre_id=formsemestre_id,
+ etudid=etudid,
+ )
)
-def _make_page(etud: dict, sem, tf, message="") -> list:
- formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
+def _make_page(etud: Identite, formsemestre: FormSemestre, tf, message="") -> list[str]:
+ """html formulaire saisie"""
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
- moy_gen = nt.get_etud_moy_gen(etud["etudid"])
+ moy_gen = nt.get_etud_moy_gen(etud.id)
H = [
html_sco_header.sco_header(
page_title="Validation des UE d'un semestre extérieur",
javascripts=["js/formsemestre_ext_edit_ue_validations.js"],
),
tf_error_message(message),
- """%(nomprenom)s est inscrit%(ne)s à ce semestre extérieur.
- Voici les UE entregistrées avec leur notes et coefficients.
+ f"""
{etud.nomprenom} est inscrit{etud.e} à ce semestre extérieur.
+ Voici ses UE enregistrées avec leur notes
+ { "et coefficients" if not formsemestre.formation.is_apc()
+ else " (en BUT, les coefficients sont égaux aux ECTS)"}.
- """
- % etud,
+ """,
f"""La moyenne de ce semestre serait:
{moy_gen} / 20
@@ -298,7 +317,7 @@ def _make_page(etud: dict, sem, tf, message="") -> list:
f"""
""",
@@ -314,7 +333,9 @@ _UE_VALID_CODES = {
}
-def _ue_form_description(ues, values):
+def _ue_form_description(
+ formsemestre: FormSemestre, etud: Identite, ues: list[UniteEns], values
+):
"""Description du formulaire de saisie des UE / validations
Pour chaque UE, on peut saisir: son code jury, sa note, son coefficient.
"""
@@ -324,8 +345,14 @@ def _ue_form_description(ues, values):
{
"input_type": "separator",
"template": """UE
- Code jury Note/20 Coefficient UE
- """,
+ Code jury Note/20
+ """
+ + (
+ """Coefficient UE """
+ if not formsemestre.formation.is_apc()
+ else ""
+ )
+ + "",
},
),
("formsemestre_id", {"input_type": "hidden"}),
@@ -334,59 +361,80 @@ def _ue_form_description(ues, values):
for ue in ues:
# Menu pour code validation UE:
# Ne propose que ADM, CMP et "Non inscrit"
- select_name = "valid_" + str(ue["ue_id"])
- menu_code_UE = """""" % (
- select_name,
- )
- cur_value = values.get("valid_" + str(ue["ue_id"]), False)
- for code in _UE_VALID_CODES:
- if cur_value is False: # pas dans le form, cherche en base
- cur_value = ue["validation"].get("code", None)
- if str(cur_value) == str(code):
+ select_name = f"valid_{ue.id}"
+ menu_code_ue = f""""""
+ cur_code_value = values.get("valid_{ue.id}", False)
+ for (code, explanation) in _UE_VALID_CODES.items():
+ if cur_code_value is False: # pas dans le form, cherche en base
+ validation = ScolarFormSemestreValidation.query.filter_by(
+ ue_id=ue.id, etudid=etud.id, formsemestre_id=formsemestre.id
+ ).first()
+ cur_code_value = validation.code if validation else None
+ if str(cur_code_value) == str(code):
selected = "selected"
else:
selected = ""
- menu_code_UE += '%s ' % (
- code,
- selected,
- _UE_VALID_CODES[code],
+ # code jury:
+ menu_code_ue += (
+ f"""{explanation} """
)
- if cur_value is None:
- disabled = 'disabled="1"'
+ if cur_code_value is None:
+ coef_disabled = 'disabled="1"'
else:
- disabled = ""
- menu_code_UE += " "
- cur_value = values.get("coef_" + str(ue["ue_id"]), False)
- if cur_value is False: # pas dans le form, cherche en base
- cur_value = ue["uecoef"].get("coefficient", "")
+ coef_disabled = ""
+ menu_code_ue += " "
+ if formsemestre.formation.is_apc():
+ coef_disabled = 'disabled="1"'
+ cur_coef_value = ue.ects or 0
+ coef_input_class = "ext_coef_disabled"
+ else:
+ cur_coef_value = values.get(f"coef_{ue.id}", False)
+ coef_input_class = ""
+ if cur_coef_value is False: # pas dans le form, cherche en base
+ ue_coef: FormSemestreUECoef = FormSemestreUECoef.query.filter_by(
+ formsemestre_id=formsemestre.id, ue_id=ue.id
+ ).first()
+ cur_coef_value = (ue_coef.coefficient if ue_coef else "") or ""
itemtemplate = (
- """%(label)s """
- + ""
- + menu_code_UE
- + " " # code jury
- + '%(elem)s ' # note
- + """
-
- """
- % (ue["ue_id"], cur_value, disabled)
- + " "
+ f"""
+
+ %(label)s
+ { menu_code_ue }
+ %(elem)s
+ """
+ + (
+ f"""
+
+ """
+ if not formsemestre.formation.is_apc()
+ else ""
+ )
+ + """ """
)
+
descr.append(
(
- "note_" + str(ue["ue_id"]),
+ f"note_{ue.id}",
{
"input_type": "text",
"size": 4,
"template": itemtemplate,
- "title": "%(acronyme)s %(titre)s" % ue,
- "attributes": [disabled],
+ "title": ""
+ + (f"S{ue.semestre_idx} " if ue.semestre_idx is not None else "")
+ + f"{ue.acronyme} {ue.titre}"
+ + f" ({ue.ects} ECTS)"
+ if ue.ects is not None
+ else "",
+ "attributes": [coef_disabled],
},
)
)
return descr
-def _check_values(ue_list, values):
+def _check_values(formsemestre: FormSemestre, ue_list, values):
"""Check that form values are ok
for each UE:
code != None => note and coef
@@ -396,107 +444,82 @@ def _check_values(ue_list, values):
coef float >= 0
"""
for ue in ue_list:
- pu = " pour UE %s" % ue["acronyme"]
- code = values.get("valid_" + str(ue["ue_id"]), False)
+ pu = f" pour UE {ue.acronyme}"
+ code = values.get(f"valid_{ue.id}", False)
if code == "None":
code = None
- note = values.get("note_" + str(ue["ue_id"]), False)
+ note = values.get(f"note_{ue.id}", False)
try:
note = _convert_field_to_float(note)
except ValueError:
return False, "note invalide" + pu
- coef = values.get("coef_" + str(ue["ue_id"]), False)
+
+ if code is not False:
+ if code not in _UE_VALID_CODES:
+ return False, "code invalide" + pu
+ if code is not None:
+ if note is False or note == "":
+ return False, "note manquante" + pu
+ coef = values.get(f"coef_{ue.id}", False)
try:
coef = _convert_field_to_float(coef)
except ValueError:
return False, "coefficient invalide" + pu
- if code != False:
- if code not in _UE_VALID_CODES:
- return False, "code invalide" + pu
- if code != None:
- if note is False or note == "":
- return False, "note manquante" + pu
- if note != False and note != "":
- if code == None:
+ if note is not False and note != "":
+ if code is None:
return (
False,
- "code jury incohérent (code %s, note %s)" % (code, note)
- + pu
- + " (supprimer note et coef)",
+ f"""code jury incohérent (code {code}, note {note}) {pu}
+ (supprimer note)""",
)
if note < 0 or note > 20:
return False, "valeur note invalide" + pu
- if not isinstance(coef, float):
- return False, "coefficient manquant pour note %s" % note + pu
- if coef != False and coef != "":
- if coef < 0:
- return False, "valeur coefficient invalide" + pu
+ if not isinstance(coef, float) and not formsemestre.formation.is_apc():
+ return False, f"coefficient manquant pour note {note} {pu}"
+
+ # Vérifie valeur coef seulement pour formations classiques:
+ if not formsemestre.formation.is_apc():
+ if coef is not False and coef != "":
+ if coef < 0:
+ return False, "valeur coefficient invalide" + pu
+
return True, "ok"
def _convert_field_to_float(val):
- """value may be empty, False, or a float. Raise exception"""
- if val != False:
+ """val may be empty, False (left unchanged), or a float. Raise exception ValueError"""
+ if val is not False:
val = val.strip()
if val:
val = float(val)
return val
-def _list_ue_with_coef_and_validations(sem, etudid):
- """Liste des UE de la même formation que sem,
- avec leurs coefs d'UE capitalisée (si déjà saisi)
- et leur validation pour cet étudiant.
+def _record_ue_validations_and_coefs(
+ formsemestre: FormSemestre, etud: Identite, ues: list[UniteEns], values
+):
+ """Enregistre en base les validations
+ En APC, le coef est toujours NULL
"""
- cnx = ndb.GetDBConnexion()
- formsemestre_id = sem["formsemestre_id"]
- ues = sco_edit_ue.ue_list({"formation_id": sem["formation_id"]})
for ue in ues:
- # add coefficient
- uecoef = sco_formsemestre.formsemestre_uecoef_list(
- cnx, args={"formsemestre_id": formsemestre_id, "ue_id": ue["ue_id"]}
- )
- if uecoef:
- ue["uecoef"] = uecoef[0]
- else:
- ue["uecoef"] = {}
- # add validation
- validation = sco_cursus_dut.scolar_formsemestre_validation_list(
- cnx,
- args={
- "formsemestre_id": formsemestre_id,
- "etudid": etudid,
- "ue_id": ue["ue_id"],
- },
- )
- if validation:
- ue["validation"] = validation[0]
- else:
- ue["validation"] = {}
- return ues
-
-
-def _record_ue_validations_and_coefs(formsemestre_id, etudid, ues, values):
- for ue in ues:
- code = values.get("valid_" + str(ue["ue_id"]), False)
+ code = values.get(f"valid_{ue.id}", False)
if code == "None":
code = None
- note = values.get("note_" + str(ue["ue_id"]), False)
+ note = values.get(f"note_{ue.id}", False)
note = _convert_field_to_float(note)
- coef = values.get("coef_" + str(ue["ue_id"]), False)
+ coef = values.get(f"coef_{ue.id}", False)
coef = _convert_field_to_float(coef)
- if coef == "" or coef == False:
+ if coef == "" or coef is False:
coef = None
now_dmy = time.strftime("%d/%m/%Y")
log(
- "_record_ue_validations_and_coefs: %s etudid=%s ue_id=%s moy_ue=%s ue_coef=%s"
- % (formsemestre_id, etudid, ue["ue_id"], note, repr(coef))
+ f"_record_ue_validations_and_coefs: {formsemestre.id} etudid={etud.id} ue_id={ue.id} moy_ue={note} ue_coef={coef}"
)
- assert code == None or (note) # si code validant, il faut une note
+ assert code is None or (note) # si code validant, il faut une note
sco_formsemestre_validation.do_formsemestre_validate_previous_ue(
- formsemestre_id,
- etudid,
- ue["ue_id"],
+ formsemestre.id,
+ etud.id,
+ ue.id,
note,
now_dmy,
code=code,
diff --git a/app/static/js/formsemestre_ext_edit_ue_validations.js b/app/static/js/formsemestre_ext_edit_ue_validations.js
index b0e08d32d..ed9576b52 100644
--- a/app/static/js/formsemestre_ext_edit_ue_validations.js
+++ b/app/static/js/formsemestre_ext_edit_ue_validations.js
@@ -2,66 +2,80 @@
function compute_moyenne() {
- var notes = $(".tf_field_note input").map(
- function() { return parseFloat($(this).val()); }
- ).get();
- var coefs = $(".tf_field_coef input").map(
- function() { return parseFloat($(this).val()); }
+ var notes = $(".tf_field_note input").map(
+ function () { return parseFloat($(this).val()); }
).get();
+ // les coefs sont donnes (ECTS en BUT)
+ let coefs = $("form.tf_ext_edit_ue_validations").data("ue_coefs");
+ // ou saisis (formations classiques)
+ if (coefs == 'undefined') {
+ coefs = $(".tf_field_coef input").map(
+ function () { return parseFloat($(this).val()); }
+ ).get();
+ }
var N = notes.length;
var dp = 0.;
var sum_coefs = 0.;
- for (var i=0; i < N; i++) {
+ for (var i = 0; i < N; i++) {
if (!(isNaN(notes[i]) || isNaN(coefs[i]))) {
dp += notes[i] * coefs[i];
sum_coefs += coefs[i];
}
}
- return dp / sum_coefs;
+ let moy = dp / sum_coefs;
+ if (isNaN(moy)) {
+ moy = "-";
+ }
+ if (typeof moy == "number") {
+ moy = moy.toFixed(2);
+ }
+ return moy;
}
// Callback select menu (UE code)
function enable_disable_fields_cb() {
enable_disable_fields(this);
}
-function enable_disable_fields(select_elt) {
+function enable_disable_fields(select_elt) {
// input fields controled by this menu
- var input_fields = $(select_elt).parent().parent().find('input');
+ var input_fields = $(select_elt).parent().parent().find('input:not(.ext_coef_disabled)');
var disabled = false;
if ($(select_elt).val() === "None") {
disabled = true;
}
- console.log('disabled=', disabled);
- input_fields.each( function () {
- var old_state = this.disabled;
- console.log("old_state=", old_state)
- if (old_state == disabled) {
- return; /* state unchanged */
+ input_fields.each(function () {
+ if (disabled) {
+ let cur_value = $(this).val();
+ $(this).data('saved-value', cur_value);
+ $(this).val("");
+ } else {
+ let saved_value = $(this).data('saved-value');
+ if (typeof saved_value == 'undefined') {
+ saved_value = '';
+ }
+ if (saved_value) {
+ $(this).val(saved_value);
+ }
}
- var saved_value = $(this).data('saved-value');
- if (typeof saved_value == 'undefined') {
- saved_value = '';
- }
- var cur_value = $(this).val();
- // swap
- $(this).data('saved-value', cur_value);
- $(this).val(saved_value);
});
input_fields.prop('disabled', disabled);
}
function setup_text_fields() {
$(".ueext_valid_select").each(
- function() {
+ function () {
enable_disable_fields(this);
}
);
}
-$().ready(function(){
- $(".tf_ext_edit_ue_validations").change(function (){
- $(".ext_sem_moy_val")[0].innerHTML=compute_moyenne();
+$().ready(function () {
+ $(".tf_ext_edit_ue_validations").change(function () {
+ $(".ext_sem_moy_val")[0].innerHTML = compute_moyenne();
});
- $(".ueext_valid_select").change( enable_disable_fields_cb );
+ $("form.tf_ext_edit_ue_validations input").blur(function () {
+ $(".ext_sem_moy_val")[0].innerHTML = compute_moyenne();
+ });
+ $(".ueext_valid_select").change(enable_disable_fields_cb);
setup_text_fields();
});