diff --git a/app/pe/pe_jury.py b/app/pe/pe_jury.py index f256073a2..30f6da83b 100644 --- a/app/pe/pe_jury.py +++ b/app/pe/pe_jury.py @@ -321,51 +321,131 @@ class JuryPE(object): """ etudids = list(self.diplomes_ids) - aggregats = pe_comp.TOUS_LES_PARCOURS - - donnees = {} + # Les données des étudiants + donnees_etudiants = {} for etudid in etudids: etudiant = self.etudiants.identites[etudid] - donnees[etudid] = { + donnees_etudiants[etudid] = { ("Identité", "", "Civilite"): etudiant.civilite_str, ("Identité", "", "Nom"): etudiant.nom, ("Identité", "", "Prenom"): etudiant.prenom, } + df_synthese = pd.DataFrame.from_dict(donnees_etudiants, orient="index") - for aggregat in aggregats: - # Le dictionnaire par défaut des moyennes - donnees[etudid] |= get_defaut_dict_synthese_aggregat( - aggregat, self.diplome - ) + # Ajout des aggrégats + aggregats = pe_comp.TOUS_LES_PARCOURS - # La trajectoire de l'étudiant sur l'aggrégat + for aggregat in aggregats: + descr = pe_comp.PARCOURS[aggregat]["descr"] + + # Les trajectoires (tagguées) suivies par les étudiants pour l'aggrégat et le tag + # considéré + trajectoires_tagguees = [] + for etudid in etudids: trajectoire = self.trajectoires.suivi[etudid][aggregat] if trajectoire: - trajectoire_tagguee = self.trajectoires_tagguees[ - trajectoire.trajectoire_id - ] - if tag in trajectoire_tagguee.moyennes_tags: - # La trajectoire tagguée - moy_trajectoire_tag = trajectoire_tagguee.moyennes_tags[tag] - if moy_trajectoire_tag.is_significatif(): - # L'interclassement - interclass = self.interclassements_taggues[aggregat] + tid = trajectoire.trajectoire_id + trajectoire_tagguee = self.trajectoires_tagguees[tid] + if ( + tag in trajectoire_tagguee.moyennes_tags + and trajectoire_tagguee not in trajectoires_tagguees + ): + trajectoires_tagguees.append(trajectoire_tagguee) - # Injection des données dans un dictionnaire - donnees[etudid] |= get_dict_synthese_aggregat( - aggregat, trajectoire_tagguee, interclass, etudid, tag, self.diplome - ) + # Ajout des notes + notes = pd.DataFrame(index=etudids, columns=[ [descr], [""], ["note"] ]) + nbre_notes_injectees = 0 + for traj in trajectoires_tagguees: + moy_traj = traj.moyennes_tags[tag] + notes_traj = moy_traj.get_df_notes(arrondi=True) + etudids_communs = notes_traj.index.intersection(etudids) + nbre_notes_injectees += len(etudids_communs) + notes.loc[etudids_communs, (descr, "", "note")] = notes_traj.loc[etudids_communs, "notes"] + + # Si l'aggrégat est significatif (aka il y a des notes) + if nbre_notes_injectees > 0: + df_synthese = df_synthese.join(notes) + + # Ajout des classements & statistiques + donnees = pd.DataFrame( + index=etudids, + columns=[ [descr]*4, [NOM_STAT_GROUPE]*4, ["class.", "min", "moy", "max"] ], + ) + # donnees[(descr, NOM_STAT_GROUPE, "class.")] = donnees[ + # (descr, NOM_STAT_GROUPE, "class.") + # ].astype(str) + # donnees[(descr, NOM_STAT_GROUPE, "class.")] = np.nan + + for traj in trajectoires_tagguees: + moy_traj = traj.moyennes_tags[tag] + + # Les classements + rangs = moy_traj.get_df_rangs_pertinents() + + # Les etudids communs pour la trajectoire + etudids_communs = rangs.index.intersection(etudids) + + donnees.loc[ + etudids_communs, (descr, NOM_STAT_GROUPE, "class.") + ] = rangs.loc[etudids_communs, "rangs"] + + # Le min + donnees.loc[ + etudids_communs, (descr, NOM_STAT_GROUPE, "min") + ] = moy_traj.get_min_for_df() + # Le max + donnees.loc[ + etudids_communs, (descr, NOM_STAT_GROUPE, "max") + ] = moy_traj.get_max_for_df() + # La moyenne + donnees.loc[ + etudids_communs, (descr, NOM_STAT_GROUPE, "moy") + ] = moy_traj.get_moy_for_df() + + df_synthese = df_synthese.join(donnees) + + # Ajoute les données d'interclassement + interclass = self.interclassements_taggues[aggregat] + moy_traj = interclass.moyennes_tags[tag] + + nom_stat_promo = f"{NOM_STAT_PROMO} {self.diplome}" + donnees = pd.DataFrame( + index=etudids, + columns=[ [descr]*4, [nom_stat_promo]*4, ["class.", "min", "moy", "max"] ], + ) + + # Les classements + rangs = moy_traj.get_df_rangs_pertinents() + + etudids_communs = rangs.index.intersection(etudids) + + donnees.loc[ + etudids_communs, (descr, nom_stat_promo, "class.") + ] = rangs.loc[etudids_communs, "rangs"] + + # Le min + donnees.loc[ + etudids_communs, (descr, nom_stat_promo, "min") + ] = moy_traj.get_min_for_df() + # Le max + donnees.loc[ + etudids_communs, (descr, nom_stat_promo, "max") + ] = moy_traj.get_max_for_df() + # La moyenne + donnees.loc[ + etudids_communs, (descr, nom_stat_promo, "moy") + ] = moy_traj.get_moy_for_df() + + df_synthese = df_synthese.join(donnees) # Fin de l'aggrégat - # Construction du dataFrame - df = pd.DataFrame.from_dict(donnees, orient="index") # Tri par nom/prénom - df.sort_values( + df_synthese.sort_values( by=[("Identité", "", "Nom"), ("Identité", "", "Prenom")], inplace=True ) - return df + return df_synthese def synthetise_jury_par_etudiants(self) -> dict[pd.DataFrame]: """Synthétise tous les résultats du jury PE dans des dataframes, @@ -420,10 +500,14 @@ class JuryPE(object): # L'interclassement interclass = self.interclassements_taggues[aggregat] - # Injection des données dans un dictionnaire donnees[tag] |= get_dict_synthese_aggregat( - aggregat, trajectoire_tagguee, interclass, etudid, tag, self.diplome + aggregat, + trajectoire_tagguee, + interclass, + etudid, + tag, + self.diplome, ) # Fin de l'aggrégat @@ -597,12 +681,12 @@ def get_dict_synthese_aggregat( if not pd.isna(note) and note != np.nan: # Les moyennes de cette trajectoire donnees |= { - (descr, "", "note"): note, - (descr, NOM_STAT_GROUPE, "class."): classement, - (descr, NOM_STAT_GROUPE, "min"): nmin, - (descr, NOM_STAT_GROUPE, "moy"): nmoy, - (descr, NOM_STAT_GROUPE, "max"): nmax, - } + (descr, "", "note"): note, + (descr, NOM_STAT_GROUPE, "class."): classement, + (descr, NOM_STAT_GROUPE, "min"): nmin, + (descr, NOM_STAT_GROUPE, "moy"): nmoy, + (descr, NOM_STAT_GROUPE, "max"): nmax, + } # L'interclassement moy_tag = interclassement_taggue.moyennes_tags[tag] diff --git a/app/pe/pe_tabletags.py b/app/pe/pe_tabletags.py index 4f0618610..b85bb5545 100644 --- a/app/pe/pe_tabletags.py +++ b/app/pe/pe_tabletags.py @@ -43,14 +43,15 @@ import numpy as np from app import ScoValueError from app.comp.moy_sem import comp_ranks_series from app.pe import pe_affichage +from app.pe.pe_affichage import SANS_NOTE from app.scodoc import sco_utils as scu import pandas as pd TAGS_RESERVES = ["but"] -class MoyenneTag(): +class MoyenneTag: def __init__(self, tag: str, notes: pd.Series): """Classe centralisant la synthèse des moyennes/classements d'une série d'étudiants à un tag donné, en stockant un dictionnaire : @@ -104,7 +105,7 @@ class MoyenneTag(): (_, class_gen_ue_non_nul) = comp_ranks_series(notes_non_nulles) # Les classements (toutes notes confondues, avec NaN si pas de notes) - class_gen_ue = pd.Series(np.nan, index=notes.index) #, dtype="Int64") + class_gen_ue = pd.Series(np.nan, index=notes.index) # , dtype="Int64") class_gen_ue[indices] = class_gen_ue_non_nul[indices] synthese = { @@ -117,6 +118,27 @@ class MoyenneTag(): } return synthese + def get_df_notes(self, arrondi=False): + """Série des notes, arrondies à 2 chiffres après la virgule""" + if arrondi: + serie = self.synthese["notes"].round(2) + else: + serie = self.synthese["notes"] + df = serie.to_frame("notes") + return df + + def get_df_rangs_pertinents(self) -> pd.Series: + """Série des rangs classement/nbre_inscrit""" + classement = self.synthese["classements"] + indices = classement[classement.notnull()].index.to_list() + classement_non_nul = classement.loc[indices].to_frame("classements") + classement_non_nul.insert(1, "rangs", np.nan) + + nb_inscrit = self.synthese["nb_inscrits"] + + classement_non_nul["rangs"] = classement_non_nul["classements"].astype(int).astype(str) + "/" + str(nb_inscrit) + return classement_non_nul["rangs"].to_frame("rangs") + def get_note_for_df(self, etudid: int): """Note d'un étudiant donné par son etudid""" return round(self.synthese["notes"].loc[etudid], 2) @@ -136,8 +158,8 @@ class MoyenneTag(): def get_class_for_df(self, etudid: int) -> str: """Classement ramené au nombre d'inscrits, pour un étudiant donné par son etudid""" - classement = self.synthese['classements'].loc[etudid] - nb_inscrit = self.synthese['nb_inscrits'] + classement = self.synthese["classements"].loc[etudid] + nb_inscrit = self.synthese["nb_inscrits"] if not pd.isna(classement): classement = int(classement) return f"{classement}/{nb_inscrit}" @@ -148,6 +170,7 @@ class MoyenneTag(): """Indique si la moyenne est significative (c'est-à-dire à des notes)""" return self.synthese["nb_inscrits"] > 0 + class TableTag(object): def __init__(self): """Classe centralisant différentes méthodes communes aux @@ -203,7 +226,3 @@ class TableTag(object): dict_series[tag] = moy_tag.synthese["notes"] df = pd.DataFrame(dict_series) return df - - - - diff --git a/app/pe/pe_trajectoiretag.py b/app/pe/pe_trajectoiretag.py index d29e01e3f..3150daff6 100644 --- a/app/pe/pe_trajectoiretag.py +++ b/app/pe/pe_trajectoiretag.py @@ -64,12 +64,15 @@ class TrajectoireTag(TableTag): """ TableTag.__init__(self) - # La trajectoire associée - self.trajectoire_id = trajectoire.trajectoire_id - self.trajectoire = trajectoire - # Le nom de la trajectoire tagguée (identique à la trajectoire) + self.trajectoire_id = trajectoire.trajectoire_id + """Identifiant de la trajectoire tagguée""" + + self.trajectoire = trajectoire + """Trajectoire associée à la trajectoire tagguée""" + self.nom = self.get_repr() + """Représentation textuelle de la trajectoire tagguée""" self.formsemestre_terminal = trajectoire.formsemestre_final """Le formsemestre terminal""" @@ -110,6 +113,10 @@ class TrajectoireTag(TableTag): moy_gen_tag = self.notes[tag] self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_tag) + def __eq__(self, other): + """Egalité de 2 trajectoires tagguées sur la base de leur identifiant""" + return self.trajectoire_id == other.trajectoire_id + def get_repr(self, verbose=False) -> str: """Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle est basée)"""