diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py index 009a6b1b9..5cf7d9cd0 100644 --- a/app/but/bulletin_but.py +++ b/app/but/bulletin_but.py @@ -120,8 +120,13 @@ class ResultatsSemestreBUT: "total": ue.ects, }, "competence": None, # XXX TODO lien avec référentiel - "moyenne": fmt_note(self.etud_moy_ue[ue.id].mean()), - "bonus": None, # XXX TODO + "moyenne": { + "value": fmt_note(self.etud_moy_ue[ue.id][etud.id]), + "min": fmt_note(self.etud_moy_ue[ue.id].min()), + "max": fmt_note(self.etud_moy_ue[ue.id].max()), + "moy": fmt_note(self.etud_moy_ue[ue.id].mean()), + }, + "bonus": 17.8, # None, # XXX TODO "malus": None, # XXX TODO voir ce qui est ici "capitalise": None, # "AAAA-MM-JJ" TODO "ressources": self.etud_ue_mod_results(etud, ue, self.ressources), diff --git a/app/comp/moy_ue.py b/app/comp/moy_ue.py index f609060ea..9a722f255 100644 --- a/app/comp/moy_ue.py +++ b/app/comp/moy_ue.py @@ -186,25 +186,25 @@ def compute_ue_moys( modimpl_coefs = modimpl_coefs_df.values # Duplique les inscriptions sur les UEs: modimpl_inscr_stacked = np.stack([modimpl_inscr] * nb_ues, axis=2) - # Enlève les NaN du numérateur: - # si on veut prendre en compte les module avec notes neutralisées ? - # sem_cube_no_nan = np.nan_to_num(sem_cube, nan=0.0) + # si on veut prendre en compte les modules avec notes neutralisées ? + sem_cube_no_nan = np.nan_to_num(sem_cube, nan=0.0) # Ne prend pas en compte les notes des étudiants non inscrits au module: # Annule les notes: - sem_cube_inscrits = np.where(modimpl_inscr_stacked, sem_cube, 0.0) + sem_cube_inscrits = np.where(modimpl_inscr_stacked, sem_cube_no_nan, 0.0) # Annule les coefs des modules où l'étudiant n'est pas inscrit: modimpl_coefs_etuds = np.where( modimpl_inscr_stacked, np.stack([modimpl_coefs.T] * nb_etuds), 0.0 ) - + # Annule les coefs des modules NaN + modimpl_coefs_etuds_no_nan = np.where(np.isnan(sem_cube), 0.0, modimpl_coefs_etuds) # # Version vectorisée # - etud_moy_ue = np.sum(modimpl_coefs_etuds * sem_cube_inscrits, axis=1) / np.sum( - modimpl_coefs_etuds, axis=1 - ) + etud_moy_ue = np.sum( + modimpl_coefs_etuds_no_nan * sem_cube_inscrits, axis=1 + ) / np.sum(modimpl_coefs_etuds_no_nan, axis=1) return pd.DataFrame( etud_moy_ue, index=modimpl_inscr_df.index, columns=modimpl_coefs_df.index ) diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 72932b45e..7bf09fced 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -528,21 +528,18 @@ def module_edit(module_id=None): ("formation_id", {"input_type": "hidden"}), ("ue_id", {"input_type": "hidden"}), ("module_id", {"input_type": "hidden"}), + ( + "ue_matiere_id", + { + "input_type": "menu" if not is_apc else "hidden", + "title": "Matière", + "explanation": "un module appartient à une seule matière.", + "labels": mat_names, + "allowed_values": ue_mat_ids, + "enabled": unlocked and not is_apc, # pas d'édition des matieres en BUT + }, + ), ] - if not is_apc: - descr += [ - ( - "ue_matiere_id", - { - "input_type": "menu", - "title": "Matière", - "explanation": "un module appartient à une seule matière.", - "labels": mat_names, - "allowed_values": ue_mat_ids, - "enabled": unlocked, - }, - ), - ] if is_apc: # le semestre du module est toujours celui de son UE descr += [ diff --git a/app/scodoc/sco_liste_notes.py b/app/scodoc/sco_liste_notes.py index 3fbdf0505..a7c85ef59 100644 --- a/app/scodoc/sco_liste_notes.py +++ b/app/scodoc/sco_liste_notes.py @@ -816,7 +816,7 @@ def _add_apc_columns( etuds_moy_module = moy_mod.compute_module_moy( evals_notes, evals_poids, evaluations, evaluations_completes ) - + ue_coefs = models.ModuleImpl.query.get(moduleimpl_id).module.ue_coefs for row in rows: for ue in ues: moy_ue = etuds_moy_module[ue.id].get(row["etudid"], "?") @@ -826,4 +826,7 @@ def _add_apc_columns( col_id = f"moy_ue_{ue.id}" titles[col_id] = ue.acronyme columns_ids.append(col_id) - row_coefs[f"moy_ue_{ue.id}"] = "m" + row_coefs[f"moy_ue_{ue.id}"] = [uc for uc in ue_coefs if uc.ue_id == ue.id][ + 0 + ].coef + row_coefs[f"_moy_ue_{ue.id}_td_attrs"] = ' class="coef_mod_ue" ' diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index 015f41c5f..e92e4ca2e 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -1066,6 +1066,12 @@ table.notes_evaluation td.moy_ue { color:rgb(1, 116, 96); } +td.coef_mod_ue { + font-style: normal; + font-weight: bold; + color: rgb(1, 116, 96); +} + h2.formsemestre, #gtrcontent h2 { margin-top: 2px; font-size: 130%; diff --git a/app/static/js/bulletin-but.js b/app/static/js/bulletin-but.js index b44002638..da73ea41c 100644 --- a/app/static/js/bulletin-but.js +++ b/app/static/js/bulletin-but.js @@ -1,59 +1,59 @@ /* Il manque : - - rangs - - Synthèse : moyenne UE - - Synthèse : min, max, moy classe - - Synthèse : absences - - Eval : absences + - rangs + - Synthèse : moyenne UE + - Synthèse : min, max, moy classe + - Synthèse : absences + - Eval : absences Moi : - "show_codemodules" :true, - "show_minmax": true, - "show_minmax_eval": true, - "show_minmax_mod": false, - "show_mod_rangs": false, - "show_moypromo": true, - "show_rangs": true, - "show_ue_cap_current": true, - "show_ue_cap_details": true, - "show_ue_rangs": true, - "show_uevalid": true, + "show_codemodules" :true, + "show_minmax": true, + "show_minmax_eval": true, + "show_minmax_mod": false, + "show_mod_rangs": false, + "show_moypromo": true, + "show_rangs": true, + "show_ue_cap_current": true, + "show_ue_cap_details": true, + "show_ue_rangs": true, + "show_uevalid": true, */ /*****************************/ /* Gestionnaire d'événements */ /*****************************/ document.querySelectorAll(".CTA_Liste").forEach(e => { - e.addEventListener("click", listeOnOff) + e.addEventListener("click", listeOnOff) }) function listeOnOff() { - this.parentElement.parentElement.classList.toggle("listeOff") + this.parentElement.parentElement.classList.toggle("listeOff") } /*****************************/ /* Recupération et affichage */ /*****************************/ fetch(dataSrc) - .then(r => { return r.json() }) - .then(json => showData(json)) + .then(r => { return r.json() }) + .then(json => showData(json)) function showData(data) { - showInformations(data); - showSemestre(data); - showSynthese(data); - showEvaluations(data); + showInformations(data); + showSemestre(data); + showSynthese(data); + showEvaluations(data); - setOptions(data.options); + setOptions(data.options); - document.body.classList.add("ready"); + document.body.classList.add("ready"); } /********************************/ /* Informations sur l'étudiant */ /********************************/ function showInformations(data) { - document.querySelector(".studentPic").src = data.etudiant.photo_url || "default_Student.svg"; + document.querySelector(".studentPic").src = data.etudiant.photo_url || "default_Student.svg"; - let output = ` + let output = `
${civilite(data.etudiant.civilite)} @@ -70,16 +70,16 @@ function showInformations(data) {
`; - document.querySelector(".infoEtudiant").innerHTML = output; + document.querySelector(".infoEtudiant").innerHTML = output; } /*******************************/ /* Information sur le semestre */ /*******************************/ function showSemestre(data) { - document.querySelector("h2").innerHTML += data.semestre.numero; - document.querySelector(".dateInscription").innerHTML += ISOToDate(data.semestre.inscription); - let output = ` + document.querySelector("h2").innerHTML += data.semestre.numero; + document.querySelector(".dateInscription").innerHTML += ISOToDate(data.semestre.inscription); + let output = `
Moyenne
${data.semestre.notes.value}
Rang :
${data.semestre.rang.value} / ${data.semestre.rang.total}
@@ -88,7 +88,7 @@ function showSemestre(data) {
Min. promo. :
${data.semestre.notes.min}
${data.semestre.groupes.map(groupe => { - return ` + return `
Groupe
${groupe.nom}
Rang :
${groupe.rang.value} / ${groupe.rang.total}
@@ -97,25 +97,25 @@ function showSemestre(data) {
Min. groupe :
${groupe.notes.min}
`; - }).join("") - } + }).join("") + } `; - document.querySelector(".infoSemestre").innerHTML = output; + document.querySelector(".infoSemestre").innerHTML = output; } /*******************************/ /* Synthèse */ /*******************************/ function showSynthese(data) { - let output = ``; - Object.entries(data.ues).forEach(([ue, dataUE]) => { - output += ` + let output = ``; + Object.entries(data.ues).forEach(([ue, dataUE]) => { + output += `

${(dataUE.competence) ? dataUE.competence + " - " : ""}${ue}

-
Moyenne : ${dataUE.moyenne?.value || "-"}
+
Moyenn : ${dataUE.moyenne?.value || "-"}
Bonus : ${dataUE.bonus || 0} - Malus : ${dataUE.malus || 0} @@ -132,15 +132,15 @@ function showSynthese(data) { ${synthese(dataUE.ressources)} ${synthese(dataUE.saes)} `; - }); - document.querySelector(".synthese").innerHTML = output; + }); + document.querySelector(".synthese").innerHTML = output; - function synthese(modules) { - let output = ""; - Object.entries(modules).forEach(([module, dataModule]) => { - let titre = data.ressources[module]?.titre || data.saes[module]?.titre; + function synthese(modules) { + let output = ""; + Object.entries(modules).forEach(([module, dataModule]) => { + let titre = data.ressources[module]?.titre || data.saes[module]?.titre; let url = data.ressources[module]?.url || data.saes[module]?.url; - output += ` + output += `
@@ -149,21 +149,21 @@ function showSynthese(data) {
`; - }) - return output; - } + }) + return output; + } } /*******************************/ /* Evaluations */ /*******************************/ function showEvaluations(data) { - document.querySelector(".evaluations").innerHTML = module(data.ressources); - document.querySelector(".sae").innerHTML += module(data.saes); + document.querySelector(".evaluations").innerHTML = module(data.ressources); + document.querySelector(".sae").innerHTML += module(data.saes); - function module(module) { - let output = ""; - Object.entries(module).forEach(([numero, content]) => { - output += ` + function module(module) { + let output = ""; + Object.entries(module).forEach(([numero, content]) => { + output += `

${numero} - ${content.titre}

@@ -183,14 +183,14 @@ function showEvaluations(data) { ${evaluation(content.evaluations)}
`; - }) - return output; - } + }) + return output; + } - function evaluation(evaluations) { - let output = ""; - evaluations.forEach(eval => { - output += ` + function evaluation(evaluations) { + let output = ""; + evaluations.forEach(eval => { + output += `
${eval.description}
@@ -203,28 +203,28 @@ function showEvaluations(data) {
Moy. promo.
${eval.note.moy}
Min. promo.
${eval.note.min}
${Object.entries(eval.poids).map(([UE, poids]) => { - return ` + return `
Poids ${UE}
${poids}
`; - }).join("")} + }).join("")}
`; - }) - return output; - } + }) + return output; + } } /********************/ /* Options */ /********************/ function setOptions(options) { - Object.entries(options).forEach(([option, value]) => { - if (value === false) { - document.body.classList.add(option.replace("show", "hide")) - } - }) + Object.entries(options).forEach(([option, value]) => { + if (value === false) { + document.body.classList.add(option.replace("show", "hide")) + } + }) } @@ -232,13 +232,13 @@ function setOptions(options) { /* Fonctions d'aide */ /********************/ function civilite(txt) { - switch (txt) { - case "M": return "M."; - case "F": return "Mme"; - default: return ""; - } + switch (txt) { + case "M": return "M."; + case "F": return "Mme"; + default: return ""; + } } function ISOToDate(ISO) { - return ISO.split("-").reverse().join("/"); + return ISO.split("-").reverse().join("/"); } diff --git a/tests/unit/test_but_ues.py b/tests/unit/test_but_ues.py index 3dcb21d2f..2bf03fca4 100644 --- a/tests/unit/test_but_ues.py +++ b/tests/unit/test_but_ues.py @@ -94,9 +94,7 @@ def test_ue_moy(test_client): # EXC à un module n1, n2 = 5.0, NOTES_NEUTRALISE etud_moy_ue = change_notes(n1, n2) - # Pour le moment, une note NEUTRALISE va entrainer le non-calcul - # des moyennes. - assert np.isnan(etud_moy_ue.values).all() + assert (etud_moy_ue.values == n1).all() # Désinscrit l'étudiant du module 2: inscr = ModuleImplInscription.query.filter_by( moduleimpl_id=evaluation2.moduleimpl.id, etudid=etudid