Réécrit le tableau de synthèse du jury PE par tag avec des DataFrame + limite l'affichage aux aggrégats significatifs (avec notes)

This commit is contained in:
Cléo Baras 2024-02-03 15:26:58 +01:00
parent 0bf0311f2f
commit c9af2345fb
3 changed files with 157 additions and 47 deletions

View File

@ -321,51 +321,131 @@ class JuryPE(object):
""" """
etudids = list(self.diplomes_ids) etudids = list(self.diplomes_ids)
aggregats = pe_comp.TOUS_LES_PARCOURS
donnees = {}
# Les données des étudiants
donnees_etudiants = {}
for etudid in etudids: for etudid in etudids:
etudiant = self.etudiants.identites[etudid] etudiant = self.etudiants.identites[etudid]
donnees[etudid] = { donnees_etudiants[etudid] = {
("Identité", "", "Civilite"): etudiant.civilite_str, ("Identité", "", "Civilite"): etudiant.civilite_str,
("Identité", "", "Nom"): etudiant.nom, ("Identité", "", "Nom"): etudiant.nom,
("Identité", "", "Prenom"): etudiant.prenom, ("Identité", "", "Prenom"): etudiant.prenom,
} }
df_synthese = pd.DataFrame.from_dict(donnees_etudiants, orient="index")
for aggregat in aggregats: # Ajout des aggrégats
# Le dictionnaire par défaut des moyennes aggregats = pe_comp.TOUS_LES_PARCOURS
donnees[etudid] |= get_defaut_dict_synthese_aggregat(
aggregat, self.diplome
)
# 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] trajectoire = self.trajectoires.suivi[etudid][aggregat]
if trajectoire: if trajectoire:
trajectoire_tagguee = self.trajectoires_tagguees[ tid = trajectoire.trajectoire_id
trajectoire.trajectoire_id trajectoire_tagguee = self.trajectoires_tagguees[tid]
] if (
if tag in trajectoire_tagguee.moyennes_tags: tag in trajectoire_tagguee.moyennes_tags
# La trajectoire tagguée and trajectoire_tagguee not in trajectoires_tagguees
moy_trajectoire_tag = trajectoire_tagguee.moyennes_tags[tag] ):
if moy_trajectoire_tag.is_significatif(): trajectoires_tagguees.append(trajectoire_tagguee)
# L'interclassement
interclass = self.interclassements_taggues[aggregat]
# Injection des données dans un dictionnaire # Ajout des notes
donnees[etudid] |= get_dict_synthese_aggregat( notes = pd.DataFrame(index=etudids, columns=[ [descr], [""], ["note"] ])
aggregat, trajectoire_tagguee, interclass, etudid, tag, self.diplome
)
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 # Fin de l'aggrégat
# Construction du dataFrame
df = pd.DataFrame.from_dict(donnees, orient="index")
# Tri par nom/prénom # Tri par nom/prénom
df.sort_values( df_synthese.sort_values(
by=[("Identité", "", "Nom"), ("Identité", "", "Prenom")], inplace=True by=[("Identité", "", "Nom"), ("Identité", "", "Prenom")], inplace=True
) )
return df return df_synthese
def synthetise_jury_par_etudiants(self) -> dict[pd.DataFrame]: def synthetise_jury_par_etudiants(self) -> dict[pd.DataFrame]:
"""Synthétise tous les résultats du jury PE dans des dataframes, """Synthétise tous les résultats du jury PE dans des dataframes,
@ -420,10 +500,14 @@ class JuryPE(object):
# L'interclassement # L'interclassement
interclass = self.interclassements_taggues[aggregat] interclass = self.interclassements_taggues[aggregat]
# Injection des données dans un dictionnaire # Injection des données dans un dictionnaire
donnees[tag] |= get_dict_synthese_aggregat( 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 # Fin de l'aggrégat
@ -597,12 +681,12 @@ def get_dict_synthese_aggregat(
if not pd.isna(note) and note != np.nan: if not pd.isna(note) and note != np.nan:
# Les moyennes de cette trajectoire # Les moyennes de cette trajectoire
donnees |= { donnees |= {
(descr, "", "note"): note, (descr, "", "note"): note,
(descr, NOM_STAT_GROUPE, "class."): classement, (descr, NOM_STAT_GROUPE, "class."): classement,
(descr, NOM_STAT_GROUPE, "min"): nmin, (descr, NOM_STAT_GROUPE, "min"): nmin,
(descr, NOM_STAT_GROUPE, "moy"): nmoy, (descr, NOM_STAT_GROUPE, "moy"): nmoy,
(descr, NOM_STAT_GROUPE, "max"): nmax, (descr, NOM_STAT_GROUPE, "max"): nmax,
} }
# L'interclassement # L'interclassement
moy_tag = interclassement_taggue.moyennes_tags[tag] moy_tag = interclassement_taggue.moyennes_tags[tag]

View File

@ -43,14 +43,15 @@ import numpy as np
from app import ScoValueError from app import ScoValueError
from app.comp.moy_sem import comp_ranks_series from app.comp.moy_sem import comp_ranks_series
from app.pe import pe_affichage from app.pe import pe_affichage
from app.pe.pe_affichage import SANS_NOTE
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
import pandas as pd import pandas as pd
TAGS_RESERVES = ["but"] TAGS_RESERVES = ["but"]
class MoyenneTag():
class MoyenneTag:
def __init__(self, tag: str, notes: pd.Series): def __init__(self, tag: str, notes: pd.Series):
"""Classe centralisant la synthèse des moyennes/classements d'une série """Classe centralisant la synthèse des moyennes/classements d'une série
d'étudiants à un tag donné, en stockant un dictionnaire : 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) (_, class_gen_ue_non_nul) = comp_ranks_series(notes_non_nulles)
# Les classements (toutes notes confondues, avec NaN si pas de notes) # 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] class_gen_ue[indices] = class_gen_ue_non_nul[indices]
synthese = { synthese = {
@ -117,6 +118,27 @@ class MoyenneTag():
} }
return synthese 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): def get_note_for_df(self, etudid: int):
"""Note d'un étudiant donné par son etudid""" """Note d'un étudiant donné par son etudid"""
return round(self.synthese["notes"].loc[etudid], 2) return round(self.synthese["notes"].loc[etudid], 2)
@ -136,8 +158,8 @@ class MoyenneTag():
def get_class_for_df(self, etudid: int) -> str: def get_class_for_df(self, etudid: int) -> str:
"""Classement ramené au nombre d'inscrits, """Classement ramené au nombre d'inscrits,
pour un étudiant donné par son etudid""" pour un étudiant donné par son etudid"""
classement = self.synthese['classements'].loc[etudid] classement = self.synthese["classements"].loc[etudid]
nb_inscrit = self.synthese['nb_inscrits'] nb_inscrit = self.synthese["nb_inscrits"]
if not pd.isna(classement): if not pd.isna(classement):
classement = int(classement) classement = int(classement)
return f"{classement}/{nb_inscrit}" return f"{classement}/{nb_inscrit}"
@ -148,6 +170,7 @@ class MoyenneTag():
"""Indique si la moyenne est significative (c'est-à-dire à des notes)""" """Indique si la moyenne est significative (c'est-à-dire à des notes)"""
return self.synthese["nb_inscrits"] > 0 return self.synthese["nb_inscrits"] > 0
class TableTag(object): class TableTag(object):
def __init__(self): def __init__(self):
"""Classe centralisant différentes méthodes communes aux """Classe centralisant différentes méthodes communes aux
@ -203,7 +226,3 @@ class TableTag(object):
dict_series[tag] = moy_tag.synthese["notes"] dict_series[tag] = moy_tag.synthese["notes"]
df = pd.DataFrame(dict_series) df = pd.DataFrame(dict_series)
return df return df

View File

@ -64,12 +64,15 @@ class TrajectoireTag(TableTag):
""" """
TableTag.__init__(self) 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() self.nom = self.get_repr()
"""Représentation textuelle de la trajectoire tagguée"""
self.formsemestre_terminal = trajectoire.formsemestre_final self.formsemestre_terminal = trajectoire.formsemestre_final
"""Le formsemestre terminal""" """Le formsemestre terminal"""
@ -110,6 +113,10 @@ class TrajectoireTag(TableTag):
moy_gen_tag = self.notes[tag] moy_gen_tag = self.notes[tag]
self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_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: def get_repr(self, verbose=False) -> str:
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle """Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
est basée)""" est basée)"""