diff --git a/app/comp/res_common.py b/app/comp/res_common.py index ed2232713e..112cc6493c 100644 --- a/app/comp/res_common.py +++ b/app/comp/res_common.py @@ -23,6 +23,7 @@ from app.models import ModuleImpl, ModuleImplInscription from app.models.ues import UniteEns from app.scodoc.sco_cache import ResultatsSemestreCache from app.scodoc.sco_codes_parcours import UE_SPORT, DEF, DEM +from app.scodoc import sco_evaluation_db from app.scodoc.sco_exceptions import ScoValueError from app.scodoc import sco_groups from app.scodoc import sco_users @@ -387,7 +388,7 @@ class ResultatsSemestre(ResultatsCache): # --- TABLEAU RECAP - def get_table_recap(self, convert_values=False): + def get_table_recap(self, convert_values=False, include_evaluations=False): """Result: tuple avec - rows: liste de dicts { column_id : value } - titles: { column_id : title } @@ -457,6 +458,11 @@ class ResultatsSemestre(ResultatsCache): idx = 0 # index de la colonne etud = Identite.query.get(etudid) row = {"etudid": etudid} + # --- Codes (seront cachés, mais exportés en excel) + idx = add_cell(row, "etudid", "etudid", etudid, "codes", idx) + idx = add_cell( + row, "code_nip", "code_nip", etud.code_nip or "", "codes", idx + ) # --- Rang idx = add_cell( row, "rang", "Rg", self.etud_moy_gen_ranks[etudid], "rang", idx @@ -618,11 +624,14 @@ class ResultatsSemestre(ResultatsCache): self._recap_add_partitions(rows, titles) self._recap_add_admissions(rows, titles) + # tri par rang croissant rows.sort(key=lambda e: e["_rang_order"]) # INFOS POUR FOOTER bottom_infos = self._recap_bottom_infos(ues_sans_bonus, modimpl_ids, fmt_note) + if include_evaluations: + self._recap_add_evaluations(rows, titles, bottom_infos) # Ajoute style "col_empty" aux colonnes de modules vides for col_id in titles: @@ -641,7 +650,9 @@ class ResultatsSemestre(ResultatsCache): row["moy_gen"] = row.get("moy_gen", "") row["_moy_gen_class"] = "col_moy_gen" # titre de la ligne: - row["prenom"] = row["nom_short"] = bottom_line.capitalize() + row["prenom"] = row["nom_short"] = ( + row.get(f"_title", "") or bottom_line.capitalize() + ) row["_tr_class"] = bottom_line.lower() + ( (" " + row["_tr_class"]) if "_tr_class" in row else "" ) @@ -656,53 +667,58 @@ class ResultatsSemestre(ResultatsCache): def _recap_bottom_infos(self, ues, modimpl_ids: set, fmt_note) -> dict: """Les informations à mettre en bas de la table: min, max, moy, ECTS""" - row_min, row_max, row_moy, row_coef, row_ects = ( - {"_tr_class": "bottom_info"}, + row_min, row_max, row_moy, row_coef, row_ects, row_apo = ( + {"_tr_class": "bottom_info", "_title": "Min."}, {"_tr_class": "bottom_info"}, {"_tr_class": "bottom_info"}, {"_tr_class": "bottom_info"}, {"_tr_class": "bottom_info"}, + {"_tr_class": "bottom_info", "_title": "Code Apogée"}, ) # --- ECTS for ue in ues: - row_ects[f"moy_ue_{ue.id}"] = ue.ects - row_ects[f"_moy_ue_{ue.id}_class"] = "col_ue" + colid = f"moy_ue_{ue.id}" + row_ects[colid] = ue.ects + row_ects[f"_{colid}_class"] = "col_ue" # style cases vides pour borders verticales - row_coef[f"moy_ue_{ue.id}"] = "" - row_coef[f"_moy_ue_{ue.id}_class"] = "col_ue" + row_coef[colid] = "" + row_coef[f"_{colid}_class"] = "col_ue" + # row_apo[colid] = ue.code_apogee or "" row_ects["moy_gen"] = sum([ue.ects or 0 for ue in ues if ue.type != UE_SPORT]) row_ects["_moy_gen_class"] = "col_moy_gen" - # --- MIN, MAX, MOY + # --- MIN, MAX, MOY, APO row_min["moy_gen"] = fmt_note(self.etud_moy_gen.min()) row_max["moy_gen"] = fmt_note(self.etud_moy_gen.max()) row_moy["moy_gen"] = fmt_note(self.etud_moy_gen.mean()) for ue in ues: - col_id = f"moy_ue_{ue.id}" - row_min[col_id] = fmt_note(self.etud_moy_ue[ue.id].min()) - row_max[col_id] = fmt_note(self.etud_moy_ue[ue.id].max()) - row_moy[col_id] = fmt_note(self.etud_moy_ue[ue.id].mean()) - row_min[f"_{col_id}_class"] = "col_ue" - row_max[f"_{col_id}_class"] = "col_ue" - row_moy[f"_{col_id}_class"] = "col_ue" + colid = f"moy_ue_{ue.id}" + row_min[colid] = fmt_note(self.etud_moy_ue[ue.id].min()) + row_max[colid] = fmt_note(self.etud_moy_ue[ue.id].max()) + row_moy[colid] = fmt_note(self.etud_moy_ue[ue.id].mean()) + row_min[f"_{colid}_class"] = "col_ue" + row_max[f"_{colid}_class"] = "col_ue" + row_moy[f"_{colid}_class"] = "col_ue" + row_apo[colid] = ue.code_apogee or "" for modimpl in self.formsemestre.modimpls_sorted: if modimpl.id in modimpl_ids: - col_id = f"moy_{modimpl.module.type_abbrv()}_{modimpl.id}_{ue.id}" + colid = f"moy_{modimpl.module.type_abbrv()}_{modimpl.id}_{ue.id}" if self.is_apc: coef = self.modimpl_coefs_df[modimpl.id][ue.id] else: coef = modimpl.module.coefficient or 0 - row_coef[col_id] = fmt_note(coef) + row_coef[colid] = fmt_note(coef) notes = self.modimpl_notes(modimpl.id, ue.id) - row_min[col_id] = fmt_note(np.nanmin(notes)) - row_max[col_id] = fmt_note(np.nanmax(notes)) + row_min[colid] = fmt_note(np.nanmin(notes)) + row_max[colid] = fmt_note(np.nanmax(notes)) moy = np.nanmean(notes) - row_moy[col_id] = fmt_note(moy) + row_moy[colid] = fmt_note(moy) if np.isnan(moy): # aucune note dans ce module - row_moy[f"_{col_id}_class"] = "col_empty" + row_moy[f"_{colid}_class"] = "col_empty" + row_apo[colid] = modimpl.module.code_apogee or "" return { # { key : row } avec key = min, max, moy, coef "min": row_min, @@ -710,6 +726,7 @@ class ResultatsSemestre(ResultatsCache): "moy": row_moy, "coef": row_coef, "ects": row_ects, + "apo": row_apo, } def _recap_etud_groups_infos(self, etudid: int, row: dict, titles: dict): @@ -803,3 +820,48 @@ class ResultatsSemestre(ResultatsCache): row[f"{cid}"] = gr_name row[f"_{cid}_class"] = klass first_partition = False + + def _recap_add_evaluations( + self, rows: list[dict], titles: dict, bottom_infos: dict + ): + """Ajoute les colonnes avec les notes aux évaluations + rows est une liste de dict avec une clé "etudid" + Les colonnes ont la classe css "evaluation" + """ + # nouvelle ligne pour description évaluations: + bottom_infos["descr_evaluation"] = { + "_tr_class": "bottom_info", + "_title": "Description évaluation", + } + first = True + for modimpl in self.formsemestre.modimpls_sorted: + evals = self.modimpls_results[modimpl.id].get_evaluations_completes(modimpl) + eval_index = len(evals) - 1 + inscrits = {i.etudid for i in modimpl.inscriptions} + klass = "evaluation first" if first else "evaluation" + first = False + for i, e in enumerate(evals): + cid = f"eval_{e.id}" + titles[ + cid + ] = f'{modimpl.module.code} {eval_index} {e.jour.isoformat() if e.jour else ""}' + titles[f"_{cid}_class"] = klass + titles[f"_{cid}_col_order"] = 9000 + i # à droite + eval_index -= 1 + notes_db = sco_evaluation_db.do_evaluation_get_all_notes( + e.evaluation_id + ) + for row in rows: + etudid = row["etudid"] + if etudid in inscrits: + if etudid in notes_db: + val = notes_db[etudid]["value"] + else: + # Note manquante mais prise en compte immédiate: affiche ATT + val = scu.NOTES_ATTENTE + row[cid] = scu.fmt_note(val) + row[f"_{cid}_class"] = klass + bottom_infos["coef"][cid] = e.coefficient + bottom_infos["min"][cid] = "0" + bottom_infos["max"][cid] = scu.fmt_note(e.note_max) + bottom_infos["descr_evaluation"][cid] = e.description or "" diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 685707b993..7d903f6cfe 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -1314,7 +1314,7 @@ def _reassociate_moduleimpls(cnx, formsemestre_id, ues_old2new, modules_old2new) def formsemestre_delete(formsemestre_id): """Delete a formsemestre (affiche avertissements)""" - sem = sco_formsemestre.get_formsemestre(formsemestre_id) + sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True) F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] H = [ html_sco_header.html_sem_header("Suppression du semestre"), diff --git a/app/scodoc/sco_recapcomplet.py b/app/scodoc/sco_recapcomplet.py index a00e7df39f..f7b365ed01 100644 --- a/app/scodoc/sco_recapcomplet.py +++ b/app/scodoc/sco_recapcomplet.py @@ -130,12 +130,10 @@ def formsemestre_recapcomplet( '