diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py index c91932aeee..49c419f267 100644 --- a/app/but/bulletin_but.py +++ b/app/but/bulletin_but.py @@ -34,6 +34,7 @@ class ResultatsSemestreBUT: "modimpls_evals_poids", "modimpls_evals_notes", "etud_moy_gen", + "etud_moy_gen_ranks", ) def __init__(self, formsemestre): @@ -94,6 +95,7 @@ class ResultatsSemestreBUT: self.etud_moy_gen = moy_sem.compute_sem_moys( self.etud_moy_ue, self.modimpl_coefs_df ) + self.etud_moy_gen_ranks = moy_sem.comp_ranks_series(self.etud_moy_gen) def etud_ue_mod_results(self, etud, ue, modimpls) -> dict: "dict synthèse résultats dans l'UE pour les modules indiqués" @@ -227,8 +229,8 @@ class ResultatsSemestreBUT: "max": fmt_note(self.etud_moy_gen.max()), }, "rang": { # classement wrt moyenne général, indicatif - "value": None, # XXX TODO - "total": None, + "value": self.etud_moy_gen_ranks[etud.id], + "total": len(self.etuds), }, "absences": { # XXX TODO "injustifie": 1, diff --git a/app/comp/moy_sem.py b/app/comp/moy_sem.py index ef87f9e97a..037b4cd068 100644 --- a/app/comp/moy_sem.py +++ b/app/comp/moy_sem.py @@ -44,3 +44,36 @@ def compute_sem_moys(etud_moy_ue_df, modimpl_coefs_df): axis=1 ) / modimpl_coefs_df.values.sum() return moy_gen + + +def comp_ranks_series(notes: pd.Series): + """Calcul rangs à partir d'une séries ("vecteur") de notes (index etudid, valeur numérique) + en tenant compte des ex-aequos + Le resultat est: { etudid : rang } où rang est une chaine decrivant le rang + """ + notes = notes.sort_values(ascending=False) # Serie, tri par ordre décroissant + rangs = pd.Series(index=notes.index, dtype=str) # le rang est une chaîne + N = len(notes) + nb_ex = 0 # nb d'ex-aequo consécutifs en cours + notes_i = notes.iat + for i, etudid in enumerate(notes.index): + # test ex-aequo + if i < (N - 1): + next = notes_i[i + 1] + else: + next = None + val = notes_i[i] + if nb_ex: + srang = "%d ex" % (i + 1 - nb_ex) + if val == next: + nb_ex += 1 + else: + nb_ex = 0 + else: + if val == next: + srang = "%d ex" % (i + 1 - nb_ex) + nb_ex = 1 + else: + srang = "%d" % (i + 1) + rangs[etudid] = srang + return rangs diff --git a/app/scodoc/notes_table.py b/app/scodoc/notes_table.py index 29314e3f22..cabc373b97 100644 --- a/app/scodoc/notes_table.py +++ b/app/scodoc/notes_table.py @@ -200,9 +200,7 @@ class NotesTable(object): self.inscrlist.sort(key=itemgetter("nomp")) # { etudid : rang dans l'ordre alphabetique } - rangalpha = {} - for i in range(len(self.inscrlist)): - rangalpha[self.inscrlist[i]["etudid"]] = i + self.rang_alpha = {e["etudid"]: i for i, e in enumerate(self.inscrlist)} self.bonus = scu.DictDefault(defaultvalue=0) # Notes dans les modules { moduleimpl_id : { etudid: note_moyenne_dans_ce_module } } @@ -264,7 +262,7 @@ class NotesTable(object): self._ues.sort(key=lambda u: u["numero"]) T = [] - # XXX self.comp_ue_coefs(cnx) + self.moy_gen = {} # etudid : moy gen (avec UE capitalisées) self.moy_ue = {} # ue_id : { etudid : moy ue } (valeur numerique) self.etud_moy_infos = {} # etudid : resultats de comp_etud_moy_gen() @@ -305,21 +303,11 @@ class NotesTable(object): # t.append(etudid) T.append(t) + + self.T = T # tri par moyennes décroissantes, # en laissant les demissionnaires a la fin, par ordre alphabetique - def row_key(x): - """clé de tri par moyennes décroissantes, - en laissant les demissionnaires a la fin, par ordre alphabetique. - (moy_gen, rang_alpha) - """ - try: - moy = -float(x[0]) - except (ValueError, TypeError): - moy = 1000.0 - return (moy, rangalpha[x[-1]]) - - T.sort(key=row_key) - self.T = T + self.T.sort(key=self._row_key) if len(valid_moy): self.moy_min = min(valid_moy) @@ -349,7 +337,7 @@ class NotesTable(object): ue_eff = len( [x for x in val_ids if isinstance(x[0], float)] ) # nombre d'étudiants avec une note dans l'UE - val_ids.sort(key=row_key) + val_ids.sort(key=self._row_key) ue_rangs[ue_id] = ( comp_ranks(val_ids), ue_eff, @@ -360,13 +348,24 @@ class NotesTable(object): for modimpl in self._modimpls: vals = self._modmoys[modimpl["moduleimpl_id"]] val_ids = [(vals[etudid], etudid) for etudid in vals.keys()] - val_ids.sort(key=row_key) + val_ids.sort(key=self._row_key) self.mod_rangs[modimpl["moduleimpl_id"]] = (comp_ranks(val_ids), len(vals)) # self.compute_moy_moy() # log(f"NotesTable( formsemestre_id={formsemestre_id} ) done.") + def _row_key(self, x): + """clé de tri par moyennes décroissantes, + en laissant les demissionnaires a la fin, par ordre alphabetique. + (moy_gen, rang_alpha) + """ + try: + moy = -float(x[0]) + except (ValueError, TypeError): + moy = 1000.0 + return (moy, self.rang_alpha[x[-1]]) + def get_etudids(self, sorted=False): if sorted: # Tri par moy. generale décroissante @@ -1338,7 +1337,7 @@ class NotesTable(object): ] def apc_recompute_moyennes(self): - """recalule les moyennes en APC (BUT) + """recalcule les moyennes en APC (BUT) et modifie en place le tableau T. XXX Raccord provisoire avant refonte de cette classe. """ @@ -1355,3 +1354,7 @@ class NotesTable(object): for i, ue in enumerate(ues, start=1): if ue["type"] != UE_SPORT: t[i] = results.etud_moy_ue[ue["id"]][etudid] + # re-trie selon la nouvelle moyenne générale: + self.T.sort(key=self._row_key) + # Remplace aussi le rang: + self.rangs = results.etud_moy_gen_ranks diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index ae206a5cdb..b3d4015b12 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -1121,7 +1121,7 @@ def formsemestre_tableau_modules( [f"{ue_acro}: {co}" for ue_acro, co in mod.ue_coefs_descr()] ) if coef_descr: - mod_descr += "coefs: " + coef_descr + mod_descr += " Coefs: " + coef_descr else: mod_descr += " (pas de coefficients) " else: