Ménage + Ajout du détail des calculs (sem, trajectoires, interclassements taggués) au zip final dans un répertoire details (pouvant servir au debug)

This commit is contained in:
Cléo Baras 2024-01-27 09:15:17 +01:00
parent cb5df2fffd
commit 9c6d988fc3
7 changed files with 97 additions and 108 deletions

View File

@ -372,3 +372,5 @@ def get_cosemestres_diplomants(
cosemestres[fid] = cosem cosemestres[fid] = cosem
return cosemestres return cosemestres

View File

@ -10,14 +10,6 @@ import numpy as np
class AggregatInterclasseTag(TableTag): class AggregatInterclasseTag(TableTag):
"""Interclasse l'ensemble des étudiants diplômés à une année
donnée (celle du jury), pour un aggrégat donné (par ex: 'S2', '3S')
en reportant :
* les moyennes obtenues sur la trajectoire qu'il ont suivi pour atteindre le numéro de semestre de fin de l'aggrégat (indépendamment de son
formsemestres)
* calculant le classement sur les étudiants diplômes
"""
# ------------------------------------------------------------------------------------------------------------------- # -------------------------------------------------------------------------------------------------------------------
def __init__( def __init__(
@ -27,12 +19,26 @@ class AggregatInterclasseTag(TableTag):
trajectoires_jury_pe: TrajectoiresJuryPE, trajectoires_jury_pe: TrajectoiresJuryPE,
trajectoires_taggues: dict[tuple, TrajectoireTag], trajectoires_taggues: dict[tuple, TrajectoireTag],
): ):
# Table nommée au nom de l'aggrégat (par ex: 3S) """
TableTag.__init__(self, nom_aggregat) Interclasse l'ensemble des étudiants diplômés à une année
donnée (celle du jury), pour un aggrégat donné (par ex: 'S2', '3S')
en reportant :
* les moyennes obtenues sur la trajectoire qu'il ont suivi pour atteindre le numéro de semestre de fin de l'aggrégat (indépendamment de son
formsemestres)
* calculant le classement sur les étudiants diplômes
"""
TableTag.__init__(self)
# Le nom
self.aggregat = nom_aggregat
self.nom = self.get_repr()
"""Les étudiants diplômés et leurs trajectoires (cf. trajectoires.suivis)""" # TODO """Les étudiants diplômés et leurs trajectoires (cf. trajectoires.suivis)""" # TODO
self.diplomes_ids = etudiants.etudiants_diplomes self.diplomes_ids = etudiants.etudiants_diplomes
self.etudiants_diplomes = {etudid for etudid in self.diplomes_ids} self.etudiants_diplomes = {etudid for etudid in self.diplomes_ids}
# pour les exports sous forme de dataFrame
self.etudiants = {etudid: etudiants.identites[etudid].etat_civil for etudid in self.diplomes_ids}
# Les trajectoires (et leur version tagguées), en ne gardant que celles associées à l'aggrégat # Les trajectoires (et leur version tagguées), en ne gardant que celles associées à l'aggrégat
self.trajectoires: dict[int, Trajectoire] = {} self.trajectoires: dict[int, Trajectoire] = {}
@ -73,9 +79,13 @@ class AggregatInterclasseTag(TableTag):
"nb_inscrits": len(moy_gen_tag), "nb_inscrits": len(moy_gen_tag),
} }
# Est significatif ? (aka a-t-il des tags et des notes)
self.significatif = len(self.tags_sorted) > 0
def get_repr(self) -> str: def get_repr(self) -> str:
"""Une représentation textuelle""" """Une représentation textuelle"""
return f"Aggrégat {self.nom}" return f"Aggrégat {self.aggregat}"
def do_taglist(self): def do_taglist(self):
"""Synthétise les tags à partir des trajectoires_tagguées """Synthétise les tags à partir des trajectoires_tagguées

View File

@ -121,7 +121,7 @@ class JuryPE(object):
with pd.ExcelWriter(output, engine="openpyxl") as writer: with pd.ExcelWriter(output, engine="openpyxl") as writer:
for formsemestretag in self.semestres_taggues.values(): for formsemestretag in self.semestres_taggues.values():
onglet = formsemestretag.nom onglet = formsemestretag.nom
df = formsemestretag.df_semtag() df = formsemestretag.df_moyennes_et_classements()
# écriture dans l'onglet # écriture dans l'onglet
df.to_excel(writer, onglet, index=True, header=True) df.to_excel(writer, onglet, index=True, header=True)
output.seek(0) output.seek(0)
@ -149,17 +149,22 @@ class JuryPE(object):
self.trajectoires, self.etudiants, self.semestres_taggues self.trajectoires, self.etudiants, self.semestres_taggues
) )
if pe_affichage.PE_DEBUG: # Intègre le bilan des trajectoires tagguées au zip final
# Intègre le bilan des trajectoires tagguées au zip final output = io.BytesIO()
with pd.ExcelWriter(output, engine="openpyxl") as writer:
for trajectoire_tagguee in self.trajectoires_tagguees.values(): for trajectoire_tagguee in self.trajectoires_tagguees.values():
filename = trajectoire_tagguee.get_repr().replace(" ", "_") + ".csv" onglet = trajectoire_tagguee.get_repr()
# pe_affichage.pe_print(f" - Export csv de {filename} ") df = trajectoire_tagguee.df_moyennes_et_classements()
self.add_file_to_zip( # écriture dans l'onglet
zipfile, df.to_excel(writer, onglet, index=True, header=True)
filename, output.seek(0)
trajectoire_tagguee.str_tagtable(),
path="details_semestres", self.add_file_to_zip(
) zipfile,
f"trajectoires_taggues_{self.diplome}.xlsx",
output.read(),
path="details",
)
# Génère les interclassements (par promo et) par (nom d') aggrégat # Génère les interclassements (par promo et) par (nom d') aggrégat
pe_affichage.pe_print("*** Génère les interclassements par aggrégat") pe_affichage.pe_print("*** Génère les interclassements par aggrégat")
@ -167,17 +172,23 @@ class JuryPE(object):
self.etudiants, self.trajectoires, self.trajectoires_tagguees self.etudiants, self.trajectoires, self.trajectoires_tagguees
) )
if pe_affichage.PE_DEBUG: # Intègre le bilan des aggrégats (interclassé par promo) au zip final
# Intègre le bilan des aggrégats (par promo) au zip final output = io.BytesIO()
with pd.ExcelWriter(output, engine="openpyxl") as writer:
for interclass_tag in self.interclassements_taggues.values(): for interclass_tag in self.interclassements_taggues.values():
filename = interclass_tag.get_repr().replace(" ", "_") + ".csv" if interclass_tag.significatif: # Avec des notes
# pe_affichage.pe_print(f" - Export csv de {filename} ") onglet = interclass_tag.get_repr()
self.add_file_to_zip( df = interclass_tag.df_moyennes_et_classements()
zipfile, # écriture dans l'onglet
filename, df.to_excel(writer, onglet, index=True, header=True)
interclass_tag.str_tagtable(), output.seek(0)
path="details_semestres",
) self.add_file_to_zip(
zipfile,
f"interclassements_taggues_{self.diplome}.xlsx",
output.read(),
path="details",
)
# Synthèse des éléments du jury PE # Synthèse des éléments du jury PE
self.synthese = self.synthetise_juryPE() self.synthese = self.synthetise_juryPE()
@ -437,7 +448,7 @@ def compute_trajectoires_tag(
nom = trajectoire.get_repr() nom = trajectoire.get_repr()
pe_affichage.pe_print(f" --> Aggrégat {nom}") pe_affichage.pe_print(f" --> Aggrégat {nom}")
# Trajectoire_tagguee associée # Trajectoire_tagguee associée
trajectoire_tagguee = TrajectoireTag(nom, trajectoire, semestres_taggues) trajectoire_tagguee = TrajectoireTag(trajectoire, semestres_taggues)
# Mémorise le résultat # Mémorise le résultat
trajectoires_tagguees[trajectoire_id] = trajectoire_tagguee trajectoires_tagguees[trajectoire_id] = trajectoire_tagguee

View File

@ -61,7 +61,7 @@ class SemestreTag(TableTag):
nom: Nom à donner au SemestreTag nom: Nom à donner au SemestreTag
formsemestre_id: Identifiant du ``FormSemestre`` sur lequel il se base formsemestre_id: Identifiant du ``FormSemestre`` sur lequel il se base
""" """
# TableTag.__init__(self, nom=nom) TableTag.__init__(self)
# Le semestre # Le semestre
self.formsemestre_id = formsemestre_id self.formsemestre_id = formsemestre_id
@ -126,7 +126,7 @@ class SemestreTag(TableTag):
# Synthétise l'ensemble des moyennes dans un dataframe # Synthétise l'ensemble des moyennes dans un dataframe
self.tags_sorted = sorted(self.moyennes_tags) # les tags par ordre alphabétique self.tags_sorted = sorted(self.moyennes_tags) # les tags par ordre alphabétique
self.notes = ( self.notes = (
self.df_tagtable() self.df_notes()
) # Le dataframe synthétique des notes (=moyennes par tag) ) # Le dataframe synthétique des notes (=moyennes par tag)
pe_affichage.pe_print(f" => Traitement des tags {', '.join(self.tags_sorted)}") pe_affichage.pe_print(f" => Traitement des tags {', '.join(self.tags_sorted)}")
@ -282,28 +282,7 @@ class SemestreTag(TableTag):
# Sinon - pas de notes à prendre en compte # Sinon - pas de notes à prendre en compte
return (note, coeff_norm) return (note, coeff_norm)
def df_semtag(self):
"""Renvoie un dataframe listant toutes les moyennes,
les rangs des étudiants pour tous les tags dans le semestre taggué"""
etudiants = self.etudiants
df = pd.DataFrame.from_dict(etudiants, orient="index", columns=["nom"])
for tag in self.get_all_tags():
df = df.join(self.moyennes_tags[tag]["notes"].rename(f"Moy {tag}"))
df = df.join(self.moyennes_tags[tag]["classements"].rename(f"Class {tag}"))
return df
# ************************************************************************
# Fonctions diverses
# ************************************************************************
# *********************************************
# -----------------------------------------------------------------------------
def get_moduleimpl(modimpl_id) -> dict: def get_moduleimpl(modimpl_id) -> dict:
"""Renvoie l'objet modimpl dont l'id est modimpl_id""" """Renvoie l'objet modimpl dont l'id est modimpl_id"""
modimpl = db.session.get(ModuleImpl, modimpl_id) modimpl = db.session.get(ModuleImpl, modimpl_id)
@ -317,7 +296,6 @@ def get_moduleimpl(modimpl_id) -> dict:
return None return None
# **********************************************
def get_moy_ue_from_nt(nt, etudid, modimpl_id) -> float: def get_moy_ue_from_nt(nt, etudid, modimpl_id) -> float:
"""Renvoie la moyenne de l'UE d'un etudid dans laquelle se trouve """Renvoie la moyenne de l'UE d'un etudid dans laquelle se trouve
le module de modimpl_id le module de modimpl_id

View File

@ -48,38 +48,13 @@ TAGS_RESERVES = ["but"]
class TableTag(object): class TableTag(object):
"""
Classe mémorisant les moyennes des étudiants à différents tags et permettant de
calculer des rangs et des statistiques.
Ses attributs sont:
* nom : Nom représentatif des données de la Table def __init__(self):
* inscrlist : Les étudiants inscrits dans le TagTag avec leur information de la forme : """Classe centralisant différentes méthodes communes aux
{ etudid : dictionnaire d'info extrait de Scodoc, ...} SemestreTag, TrajectoireTag, AggregatInterclassTag
* taglist : Liste triée des noms des tags
* resultats : Dictionnaire donnant les notes-moyennes de chaque étudiant par tag et la somme commulée
des coeff utilisées dans le calcul de la moyenne pondérée, sous la forme :
{ tag : { etudid: (note_moy, somme_coeff_norm),
...}
* rangs : Dictionnaire donnant les rang par tag de chaque étudiant de la forme :
{ tag : {etudid: rang, ...} }
* nbinscrits : Nombre d'inscrits dans le semestre (pas de distinction entre les tags)
* statistiques : Dictionnaire donnant les statistiques (moyenne, min, max) des résultats par tag de la forme :
{ tag : (moy, min, max), ...}
"""
def __init__(self, nom: str):
"""Les attributs basiques des TagTable, qui seront initialisés
dans les classes dérivées
""" """
self.nom = nom pass
"""Les étudiants"""
self.etudiants = {}
"""Les moyennes par tag"""
self.moyennes_tags = {}
# ----------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------
def get_all_tags(self): def get_all_tags(self):
@ -90,8 +65,21 @@ class TableTag(object):
""" """
return sorted(self.moyennes_tags.keys()) return sorted(self.moyennes_tags.keys())
def df_moyennes_et_classements(self):
"""Renvoie un dataframe listant toutes les moyennes,
et les classements des étudiants pour tous les tags
"""
def df_tagtable(self): etudiants = self.etudiants
df = pd.DataFrame.from_dict(etudiants, orient="index", columns=["nom"])
for tag in self.get_all_tags():
df = df.join(self.moyennes_tags[tag]["notes"].rename(f"Moy {tag}"))
df = df.join(self.moyennes_tags[tag]["classements"].rename(f"Class {tag}"))
return df
def df_notes(self):
"""Renvoie un dataframe (etudid x tag) listant toutes les moyennes par tags """Renvoie un dataframe (etudid x tag) listant toutes les moyennes par tags
Returns: Returns:
@ -117,4 +105,3 @@ class TableTag(object):
df = df.join(self.moyennes_tags[tag]["classements"].rename(f"class {tag}")) df = df.join(self.moyennes_tags[tag]["classements"].rename(f"class {tag}"))
return df.to_csv(sep=";") return df.to_csv(sep=";")

View File

@ -46,16 +46,17 @@ class Trajectoire:
""" """
self.semestres_aggreges = self.semestres_aggreges | semestres self.semestres_aggreges = self.semestres_aggreges | semestres
def get_repr(self): def get_repr(self, verbose=True) -> str:
"""Représentation textuelle d'une trajectoire """Représentation textuelle d'une trajectoire
basée sur ses semestres aggrégés""" basée sur ses semestres aggrégés"""
noms = [] noms = []
for fid in self.semestres_aggreges: for fid in self.semestres_aggreges:
semestre = self.semestres_aggreges[fid] semestre = self.semestres_aggreges[fid]
noms.append(f"S{semestre.semestre_id}({fid})") noms.append(f"S{semestre.semestre_id}({fid})")
noms = sorted(noms) noms = sorted(noms)
repr = f"{self.nom} ({self.semestre_final.formsemestre_id}) {self.semestre_final.date_fin.year}" repr = f"{self.nom} ({self.semestre_final.formsemestre_id}) {self.semestre_final.date_fin.year}"
if noms: if verbose and noms:
repr += " - " + "+".join(noms) repr += " - " + "+".join(noms)
return repr return repr

View File

@ -47,30 +47,30 @@ from app.pe.pe_tabletags import TableTag
class TrajectoireTag(TableTag): class TrajectoireTag(TableTag):
"""Calcule les moyennes par tag d'une combinaison de semestres
(trajectoires), identifiée par un nom d'aggrégat (par ex: '3S') et
par un semestre terminal, pour extraire les classements par tag pour un
groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous
participé au semestre terminal.
Par ex: fusion d'un parcours ['S1', 'S2', 'S3'] donnant un nom_combinaison = '3S'
"""
# -------------------------------------------------------------------------------------------------------------------
def __init__( def __init__(
self, self,
nom: str,
trajectoire: Trajectoire, trajectoire: Trajectoire,
semestres_taggues: dict[int, SemestreTag] semestres_taggues: dict[int, SemestreTag]
): ):
""" """ """Calcule les moyennes par tag d'une combinaison de semestres
TableTag.__init__(self, nom=nom) (trajectoires), identifiée par un nom d'aggrégat (par ex: '3S') et
par un semestre terminal, pour extraire les classements par tag pour un
groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous
participé au semestre terminal.
"""La trajectoire associée""" Par ex: fusion d'un parcours ['S1', 'S2', 'S3'] donnant un nom_combinaison = '3S'
"""
TableTag.__init__(self)
# La trajectoire associée
self.trajectoire_id = trajectoire.trajectoire_id self.trajectoire_id = trajectoire.trajectoire_id
self.trajectoire = trajectoire self.trajectoire = trajectoire
# Le nom de la trajectoire tagguée (identique à la trajectoire)
self.nom = self.get_repr()
"""Le formsemestre terminal et les semestres aggrégés""" """Le formsemestre terminal et les semestres aggrégés"""
self.formsemestre_terminal = trajectoire.semestre_final self.formsemestre_terminal = trajectoire.semestre_final
nt = load_formsemestre_results(self.formsemestre_terminal) nt = load_formsemestre_results(self.formsemestre_terminal)
@ -114,10 +114,10 @@ class TrajectoireTag(TableTag):
"nb_inscrits": len(moy_gen_tag), "nb_inscrits": len(moy_gen_tag),
} }
def get_repr(self): 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)"""
return self.trajectoire.get_repr() return self.trajectoire.get_repr(verbose=verbose)
def compute_notes_cube(self): def compute_notes_cube(self):
"""Construit le cube de notes (etudid x tags x semestre_aggregé) """Construit le cube de notes (etudid x tags x semestre_aggregé)
@ -159,8 +159,6 @@ class TrajectoireTag(TableTag):
return etudids_x_tags_x_semestres return etudids_x_tags_x_semestres
def do_taglist(self): def do_taglist(self):
"""Synthétise les tags à partir des semestres (taggués) aggrégés """Synthétise les tags à partir des semestres (taggués) aggrégés
@ -173,6 +171,8 @@ class TrajectoireTag(TableTag):
return sorted(set(tags)) return sorted(set(tags))
def compute_tag_moy(set_cube: np.array, etudids: list, tags: list): def compute_tag_moy(set_cube: np.array, etudids: list, tags: list):
"""Calcul de la moyenne par tag sur plusieurs semestres. """Calcul de la moyenne par tag sur plusieurs semestres.
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles