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,16 +149,21 @@ 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()
# écriture dans l'onglet
df.to_excel(writer, onglet, index=True, header=True)
output.seek(0)
self.add_file_to_zip( self.add_file_to_zip(
zipfile, zipfile,
filename, f"trajectoires_taggues_{self.diplome}.xlsx",
trajectoire_tagguee.str_tagtable(), output.read(),
path="details_semestres", 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
@ -167,16 +172,22 @@ 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()
df = interclass_tag.df_moyennes_et_classements()
# écriture dans l'onglet
df.to_excel(writer, onglet, index=True, header=True)
output.seek(0)
self.add_file_to_zip( self.add_file_to_zip(
zipfile, zipfile,
filename, f"interclassements_taggues_{self.diplome}.xlsx",
interclass_tag.str_tagtable(), output.read(),
path="details_semestres", path="details",
) )
# Synthèse des éléments du jury PE # Synthèse des éléments du jury PE
@ -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):
def __init__(self):
"""Classe centralisant différentes méthodes communes aux
SemestreTag, TrajectoireTag, AggregatInterclassTag
""" """
Classe mémorisant les moyennes des étudiants à différents tags et permettant de pass
calculer des rangs et des statistiques.
Ses attributs sont:
* nom : Nom représentatif des données de la Table
* inscrlist : Les étudiants inscrits dans le TagTag avec leur information de la forme :
{ etudid : dictionnaire d'info extrait de Scodoc, ...}
* 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
"""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,6 +47,12 @@ from app.pe.pe_tabletags import TableTag
class TrajectoireTag(TableTag): class TrajectoireTag(TableTag):
def __init__(
self,
trajectoire: Trajectoire,
semestres_taggues: dict[int, SemestreTag]
):
"""Calcule les moyennes par tag d'une combinaison de semestres """Calcule les moyennes par tag d'une combinaison de semestres
(trajectoires), identifiée par un nom d'aggrégat (par ex: '3S') et (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 par un semestre terminal, pour extraire les classements par tag pour un
@ -56,21 +62,15 @@ class TrajectoireTag(TableTag):
Par ex: fusion d'un parcours ['S1', 'S2', 'S3'] donnant un nom_combinaison = '3S' Par ex: fusion d'un parcours ['S1', 'S2', 'S3'] donnant un nom_combinaison = '3S'
""" """
TableTag.__init__(self)
# ------------------------------------------------------------------------------------------------------------------- # La trajectoire associée
def __init__(
self,
nom: str,
trajectoire: Trajectoire,
semestres_taggues: dict[int, SemestreTag]
):
""" """
TableTag.__init__(self, nom=nom)
"""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