forked from ScoDoc/ScoDoc
Merge branch 'pe-BUT-v2' of https://scodoc.org/git/cleo/ScoDoc-PE
This commit is contained in:
commit
9e13b51669
@ -349,8 +349,8 @@ class EtudiantsJuryPE:
|
|||||||
trajectoire = trajectoire_aggr[aggregat]
|
trajectoire = trajectoire_aggr[aggregat]
|
||||||
if trajectoire:
|
if trajectoire:
|
||||||
# Le semestre terminal de l'étudiant de l'aggrégat
|
# Le semestre terminal de l'étudiant de l'aggrégat
|
||||||
fid = trajectoire.semestre_final.formsemestre_id
|
fid = trajectoire.formsemestre_final.formsemestre_id
|
||||||
formsemestres_terminaux[fid] = trajectoire.semestre_final
|
formsemestres_terminaux[fid] = trajectoire.formsemestre_final
|
||||||
return formsemestres_terminaux
|
return formsemestres_terminaux
|
||||||
|
|
||||||
def get_formsemestres(self, semestres_recherches=None):
|
def get_formsemestres(self, semestres_recherches=None):
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from app.comp import moy_sem
|
from app.comp import moy_sem
|
||||||
from app.pe.pe_tabletags import TableTag
|
from app.pe.pe_tabletags import TableTag, MoyenneTag
|
||||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
from app.pe.pe_etudiant import EtudiantsJuryPE
|
||||||
from app.pe.pe_trajectoire import Trajectoire, TrajectoiresJuryPE
|
from app.pe.pe_trajectoire import Trajectoire, TrajectoiresJuryPE
|
||||||
from app.pe.pe_trajectoiretag import TrajectoireTag
|
from app.pe.pe_trajectoiretag import TrajectoireTag
|
||||||
@ -10,7 +10,6 @@ import numpy as np
|
|||||||
|
|
||||||
|
|
||||||
class AggregatInterclasseTag(TableTag):
|
class AggregatInterclasseTag(TableTag):
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------------------------------------
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -30,24 +29,30 @@ class AggregatInterclasseTag(TableTag):
|
|||||||
"""
|
"""
|
||||||
TableTag.__init__(self)
|
TableTag.__init__(self)
|
||||||
|
|
||||||
# Le nom
|
|
||||||
self.aggregat = nom_aggregat
|
self.aggregat = nom_aggregat
|
||||||
|
"""Aggrégat de l'interclassement"""
|
||||||
|
|
||||||
self.nom = self.get_repr()
|
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
|
# pour les exports sous forme de dataFrame
|
||||||
self.etudiants = {etudid: etudiants.identites[etudid].etat_civil for etudid in self.diplomes_ids}
|
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] = {}
|
||||||
|
"""Ensemble des trajectoires associées à l'aggrégat"""
|
||||||
for trajectoire_id in trajectoires_jury_pe.trajectoires:
|
for trajectoire_id in trajectoires_jury_pe.trajectoires:
|
||||||
trajectoire = trajectoires_jury_pe.trajectoires[trajectoire_id]
|
trajectoire = trajectoires_jury_pe.trajectoires[trajectoire_id]
|
||||||
if trajectoire_id[0] == nom_aggregat:
|
if trajectoire_id[0] == nom_aggregat:
|
||||||
self.trajectoires[trajectoire_id] = trajectoire
|
self.trajectoires[trajectoire_id] = trajectoire
|
||||||
|
|
||||||
self.trajectoires_taggues: dict[int, Trajectoire] = {}
|
self.trajectoires_taggues: dict[int, Trajectoire] = {}
|
||||||
|
"""Ensemble des trajectoires tagguées associées à l'aggrégat"""
|
||||||
for trajectoire_id in self.trajectoires:
|
for trajectoire_id in self.trajectoires:
|
||||||
self.trajectoires_taggues[trajectoire_id] = trajectoires_taggues[
|
self.trajectoires_taggues[trajectoire_id] = trajectoires_taggues[
|
||||||
trajectoire_id
|
trajectoire_id
|
||||||
@ -56,26 +61,27 @@ class AggregatInterclasseTag(TableTag):
|
|||||||
# Les trajectoires suivies par les étudiants du jury, en ne gardant que
|
# Les trajectoires suivies par les étudiants du jury, en ne gardant que
|
||||||
# celles associées aux diplomés
|
# celles associées aux diplomés
|
||||||
self.suivi: dict[int, Trajectoire] = {}
|
self.suivi: dict[int, Trajectoire] = {}
|
||||||
|
"""Association entre chaque étudiant et la trajectoire tagguée à prendre en
|
||||||
|
compte pour l'aggrégat"""
|
||||||
for etudid in self.diplomes_ids:
|
for etudid in self.diplomes_ids:
|
||||||
self.suivi[etudid] = trajectoires_jury_pe.suivi[etudid][nom_aggregat]
|
self.suivi[etudid] = trajectoires_jury_pe.suivi[etudid][nom_aggregat]
|
||||||
|
|
||||||
|
|
||||||
self.tags_sorted = self.do_taglist()
|
self.tags_sorted = self.do_taglist()
|
||||||
"""Liste des tags (triés par ordre alphabétique)"""
|
"""Liste des tags (triés par ordre alphabétique)"""
|
||||||
|
|
||||||
# Construit la matrice de notes
|
# Construit la matrice de notes
|
||||||
self.notes = self.compute_notes_matrice()
|
self.notes = self.compute_notes_matrice()
|
||||||
|
"""Matrice des notes de l'aggrégat"""
|
||||||
|
|
||||||
# Synthétise les moyennes/classements par tag
|
# Synthétise les moyennes/classements par tag
|
||||||
self.moyennes_tags = {}
|
self.moyennes_tags: dict[str, MoyenneTag] = {}
|
||||||
for tag in self.tags_sorted:
|
for tag in self.tags_sorted:
|
||||||
moy_gen_tag = self.notes[tag]
|
moy_gen_tag = self.notes[tag]
|
||||||
self.moyennes_tags[tag] = self.comp_moy_et_stat(moy_gen_tag)
|
self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_tag)
|
||||||
|
|
||||||
# Est significatif ? (aka a-t-il des tags et des notes)
|
# Est significatif ? (aka a-t-il des tags et des notes)
|
||||||
self.significatif = len(self.tags_sorted) > 0
|
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.aggregat}"
|
return f"Aggrégat {self.aggregat}"
|
||||||
@ -118,7 +124,4 @@ class AggregatInterclasseTag(TableTag):
|
|||||||
etudids_communs, tags_communs
|
etudids_communs, tags_communs
|
||||||
]
|
]
|
||||||
|
|
||||||
# Force les nan
|
|
||||||
df.fillna(np.nan)
|
|
||||||
|
|
||||||
return df
|
return df
|
||||||
|
@ -65,35 +65,15 @@ import pandas as pd
|
|||||||
|
|
||||||
|
|
||||||
class JuryPE(object):
|
class JuryPE(object):
|
||||||
"""Classe mémorisant toutes les informations nécessaires pour établir un jury de PE.
|
|
||||||
Modèle basé sur NotesTable.
|
|
||||||
|
|
||||||
Attributs :
|
|
||||||
|
|
||||||
* diplome : l'année d'obtention du diplome BUT et du jury de PE (généralement février XXXX)
|
|
||||||
* juryEtudDict : dictionnaire récapitulant les étudiants participant au jury PE (données administratives +
|
|
||||||
celles des semestres valides à prendre en compte permettant le calcul des moyennes ...
|
|
||||||
``{'etudid : { 'nom', 'prenom', 'civilite', 'diplome', '', }}``
|
|
||||||
a
|
|
||||||
Rq: il contient à la fois les étudiants qui vont être diplomés à la date prévue
|
|
||||||
et ceux qui sont éliminés (abandon, redoublement, ...) pour affichage alternatif
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Variables de classe décrivant les aggrégats, leur ordre d'apparition temporelle et
|
|
||||||
# leur affichage dans les avis latex
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------------------------------------------
|
|
||||||
def __init__(self, diplome):
|
def __init__(self, diplome):
|
||||||
"""
|
"""
|
||||||
Création d'une table PE sur la base d'un semestre selectionné. De ce semestre est déduit :
|
Classe mémorisant toutes les informations nécessaires pour établir un jury de PE, sur la base
|
||||||
|
d'une année de diplôme. De ce semestre est déduit :
|
||||||
1. l'année d'obtention du DUT,
|
1. l'année d'obtention du DUT,
|
||||||
2. tous les étudiants susceptibles à ce stade (au regard de leur parcours) d'être diplomés.
|
2. tous les étudiants susceptibles à ce stade (au regard de leur parcours) d'être diplomés.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sem_base: le FormSemestre donnant le semestre à la base du jury PE
|
diplome : l'année d'obtention du diplome BUT et du jury de PE (généralement février XXXX)
|
||||||
semBase: le dictionnaire sem donnant la base du jury (CB: TODO: A supprimer à long term)
|
|
||||||
meme_programme: si True, impose un même programme pour tous les étudiants participant au jury,
|
|
||||||
si False, permet des programmes differents
|
|
||||||
"""
|
"""
|
||||||
self.diplome = diplome
|
self.diplome = diplome
|
||||||
"L'année du diplome"
|
"L'année du diplome"
|
||||||
@ -101,7 +81,7 @@ class JuryPE(object):
|
|||||||
self.nom_export_zip = f"Jury_PE_{self.diplome}"
|
self.nom_export_zip = f"Jury_PE_{self.diplome}"
|
||||||
"Nom du zip où ranger les fichiers générés"
|
"Nom du zip où ranger les fichiers générés"
|
||||||
|
|
||||||
# Chargement des étudiants à prendre en compte dans le jury
|
# Chargement des étudiants à prendre en compte Sydans le jury
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
f"""*** Recherche et chargement des étudiants diplômés en {
|
f"""*** Recherche et chargement des étudiants diplômés en {
|
||||||
self.diplome}"""
|
self.diplome}"""
|
||||||
@ -122,7 +102,6 @@ class JuryPE(object):
|
|||||||
self._gen_xls_synthese_jury_par_tag(zipfile)
|
self._gen_xls_synthese_jury_par_tag(zipfile)
|
||||||
self._gen_xls_synthese_par_etudiant(zipfile)
|
self._gen_xls_synthese_par_etudiant(zipfile)
|
||||||
|
|
||||||
|
|
||||||
# Fin !!!! Tada :)
|
# Fin !!!! Tada :)
|
||||||
|
|
||||||
def _gen_xls_diplomes(self, zipfile: ZipFile):
|
def _gen_xls_diplomes(self, zipfile: ZipFile):
|
||||||
@ -342,48 +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 (
|
||||||
else:
|
tag in trajectoire_tagguee.moyennes_tags
|
||||||
trajectoire_tagguee = None
|
and trajectoire_tagguee not in trajectoires_tagguees
|
||||||
|
):
|
||||||
|
trajectoires_tagguees.append(trajectoire_tagguee)
|
||||||
|
|
||||||
# L'interclassement
|
# 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]
|
interclass = self.interclassements_taggues[aggregat]
|
||||||
|
moy_traj = interclass.moyennes_tags[tag]
|
||||||
|
|
||||||
# Injection des données dans un dictionnaire
|
nom_stat_promo = f"{NOM_STAT_PROMO} {self.diplome}"
|
||||||
donnees[etudid] |= get_dict_synthese_aggregat(aggregat, trajectoire_tagguee, interclass, etudid, tag, 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,
|
||||||
@ -424,7 +486,9 @@ class JuryPE(object):
|
|||||||
|
|
||||||
for aggregat in aggregats:
|
for aggregat in aggregats:
|
||||||
# Le dictionnaire par défaut des moyennes
|
# Le dictionnaire par défaut des moyennes
|
||||||
donnees[tag] |= get_defaut_dict_synthese_aggregat(aggregat, self.diplome)
|
donnees[tag] |= get_defaut_dict_synthese_aggregat(
|
||||||
|
aggregat, self.diplome
|
||||||
|
)
|
||||||
|
|
||||||
# La trajectoire de l'étudiant sur l'aggrégat
|
# La trajectoire de l'étudiant sur l'aggrégat
|
||||||
trajectoire = self.trajectoires.suivi[etudid][aggregat]
|
trajectoire = self.trajectoires.suivi[etudid][aggregat]
|
||||||
@ -432,26 +496,29 @@ class JuryPE(object):
|
|||||||
trajectoire_tagguee = self.trajectoires_tagguees[
|
trajectoire_tagguee = self.trajectoires_tagguees[
|
||||||
trajectoire.trajectoire_id
|
trajectoire.trajectoire_id
|
||||||
]
|
]
|
||||||
else:
|
if tag in trajectoire_tagguee.moyennes_tags:
|
||||||
trajectoire_tagguee = None
|
# L'interclassement
|
||||||
|
interclass = self.interclassements_taggues[aggregat]
|
||||||
# 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)
|
|
||||||
|
|
||||||
|
# Injection des données dans un dictionnaire
|
||||||
|
donnees[tag] |= get_dict_synthese_aggregat(
|
||||||
|
aggregat,
|
||||||
|
trajectoire_tagguee,
|
||||||
|
interclass,
|
||||||
|
etudid,
|
||||||
|
tag,
|
||||||
|
self.diplome,
|
||||||
|
)
|
||||||
|
|
||||||
# Fin de l'aggrégat
|
# Fin de l'aggrégat
|
||||||
# Construction du dataFrame
|
# Construction du dataFrame
|
||||||
df = pd.DataFrame.from_dict(donnees, orient="index")
|
df = pd.DataFrame.from_dict(donnees, orient="index")
|
||||||
|
|
||||||
# Tri par nom/prénom
|
# Tri par nom/prénom
|
||||||
df.sort_values(
|
df.sort_values(by=[("", "", "tag")], inplace=True)
|
||||||
by=[("", "", "tag")], inplace=True
|
|
||||||
)
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
|
def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
|
||||||
"""Créé les semestres taggués, de type 'S1', 'S2', ..., pour un groupe d'étudiants donnés.
|
"""Créé les semestres taggués, de type 'S1', 'S2', ..., pour un groupe d'étudiants donnés.
|
||||||
Chaque semestre taggué est rattaché à l'un des FormSemestre faisant partie du cursus scolaire
|
Chaque semestre taggué est rattaché à l'un des FormSemestre faisant partie du cursus scolaire
|
||||||
@ -587,7 +654,7 @@ def get_dict_synthese_aggregat(
|
|||||||
interclassement_taggue: AggregatInterclasseTag,
|
interclassement_taggue: AggregatInterclasseTag,
|
||||||
etudid: int,
|
etudid: int,
|
||||||
tag: str,
|
tag: str,
|
||||||
diplome: int
|
diplome: int,
|
||||||
):
|
):
|
||||||
"""Renvoie le dictionnaire (à intégrer au tableur excel de synthese)
|
"""Renvoie le dictionnaire (à intégrer au tableur excel de synthese)
|
||||||
traduisant les résultats (moy/class) d'un étudiant à une trajectoire tagguée associée
|
traduisant les résultats (moy/class) d'un étudiant à une trajectoire tagguée associée
|
||||||
@ -600,66 +667,43 @@ def get_dict_synthese_aggregat(
|
|||||||
note = np.nan
|
note = np.nan
|
||||||
|
|
||||||
# Les données de la trajectoire tagguée pour le tag considéré
|
# Les données de la trajectoire tagguée pour le tag considéré
|
||||||
if trajectoire_tagguee and tag in trajectoire_tagguee.moyennes_tags:
|
moy_tag = trajectoire_tagguee.moyennes_tags[tag]
|
||||||
bilan = trajectoire_tagguee.moyennes_tags[tag]
|
|
||||||
|
|
||||||
# La moyenne de l'étudiant
|
# Les données de l'étudiant
|
||||||
note = TableTag.get_note_for_df(bilan, etudid)
|
note = moy_tag.get_note_for_df(etudid)
|
||||||
|
|
||||||
# Statistiques sur le groupe
|
classement = moy_tag.get_class_for_df(etudid)
|
||||||
if not pd.isna(note) and note != np.nan:
|
nmin = moy_tag.get_min_for_df()
|
||||||
# Les moyennes de cette trajectoire
|
nmax = moy_tag.get_max_for_df()
|
||||||
donnees |= {
|
nmoy = moy_tag.get_moy_for_df()
|
||||||
(descr, "", "note"): note,
|
|
||||||
(
|
# Statistiques sur le groupe
|
||||||
descr,
|
if not pd.isna(note) and note != np.nan:
|
||||||
NOM_STAT_GROUPE,
|
# Les moyennes de cette trajectoire
|
||||||
"class.",
|
donnees |= {
|
||||||
): TableTag.get_class_for_df(bilan, etudid),
|
(descr, "", "note"): note,
|
||||||
(
|
(descr, NOM_STAT_GROUPE, "class."): classement,
|
||||||
descr,
|
(descr, NOM_STAT_GROUPE, "min"): nmin,
|
||||||
NOM_STAT_GROUPE,
|
(descr, NOM_STAT_GROUPE, "moy"): nmoy,
|
||||||
"min",
|
(descr, NOM_STAT_GROUPE, "max"): nmax,
|
||||||
): TableTag.get_min_for_df(bilan),
|
}
|
||||||
(
|
|
||||||
descr,
|
|
||||||
NOM_STAT_GROUPE,
|
|
||||||
"moy",
|
|
||||||
): TableTag.get_moy_for_df(bilan),
|
|
||||||
(
|
|
||||||
descr,
|
|
||||||
NOM_STAT_GROUPE,
|
|
||||||
"max",
|
|
||||||
): TableTag.get_max_for_df(bilan),
|
|
||||||
}
|
|
||||||
|
|
||||||
# L'interclassement
|
# L'interclassement
|
||||||
if tag in interclassement_taggue.moyennes_tags:
|
moy_tag = interclassement_taggue.moyennes_tags[tag]
|
||||||
bilan = interclassement_taggue.moyennes_tags[tag]
|
|
||||||
|
|
||||||
if not pd.isna(note) and note != np.nan:
|
classement = moy_tag.get_class_for_df(etudid)
|
||||||
nom_stat_promo = f"{NOM_STAT_PROMO} {diplome}"
|
nmin = moy_tag.get_min_for_df()
|
||||||
|
nmax = moy_tag.get_max_for_df()
|
||||||
|
nmoy = moy_tag.get_moy_for_df()
|
||||||
|
|
||||||
|
if not pd.isna(note) and note != np.nan:
|
||||||
|
nom_stat_promo = f"{NOM_STAT_PROMO} {diplome}"
|
||||||
|
|
||||||
|
donnees |= {
|
||||||
|
(descr, nom_stat_promo, "class."): classement,
|
||||||
|
(descr, nom_stat_promo, "min"): nmin,
|
||||||
|
(descr, nom_stat_promo, "moy"): nmoy,
|
||||||
|
(descr, nom_stat_promo, "max"): nmax,
|
||||||
|
}
|
||||||
|
|
||||||
donnees |= {
|
|
||||||
(
|
|
||||||
descr,
|
|
||||||
nom_stat_promo,
|
|
||||||
"class.",
|
|
||||||
): TableTag.get_class_for_df(bilan, etudid),
|
|
||||||
(
|
|
||||||
descr,
|
|
||||||
nom_stat_promo,
|
|
||||||
"min",
|
|
||||||
): TableTag.get_min_for_df(bilan),
|
|
||||||
(
|
|
||||||
descr,
|
|
||||||
nom_stat_promo,
|
|
||||||
"moy",
|
|
||||||
): TableTag.get_moy_for_df(bilan),
|
|
||||||
(
|
|
||||||
descr,
|
|
||||||
nom_stat_promo,
|
|
||||||
"max",
|
|
||||||
): TableTag.get_max_for_df(bilan),
|
|
||||||
}
|
|
||||||
return donnees
|
return donnees
|
||||||
|
@ -38,7 +38,7 @@ Created on Fri Sep 9 09:15:05 2016
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
import app.pe.pe_etudiant
|
import app.pe.pe_etudiant
|
||||||
from app import db, log
|
from app import db, log, ScoValueError
|
||||||
from app.comp import res_sem, moy_ue, moy_sem
|
from app.comp import res_sem, moy_ue, moy_sem
|
||||||
from app.comp.moy_sem import comp_ranks_series
|
from app.comp.moy_sem import comp_ranks_series
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
@ -49,7 +49,7 @@ from app.models.moduleimpls import ModuleImpl
|
|||||||
from app.scodoc import sco_tag_module
|
from app.scodoc import sco_tag_module
|
||||||
from app.scodoc.codes_cursus import UE_SPORT
|
from app.scodoc.codes_cursus import UE_SPORT
|
||||||
import app.pe.pe_affichage as pe_affichage
|
import app.pe.pe_affichage as pe_affichage
|
||||||
from app.pe.pe_tabletags import TableTag, TAGS_RESERVES
|
from app.pe.pe_tabletags import TableTag, TAGS_RESERVES, MoyenneTag
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
|
|
||||||
@ -94,44 +94,51 @@ class SemestreTag(TableTag):
|
|||||||
|
|
||||||
# Les tags :
|
# Les tags :
|
||||||
## Saisis par l'utilisateur
|
## Saisis par l'utilisateur
|
||||||
self.tags_personnalises = get_synthese_tags_personnalises_semestre(
|
tags_personnalises = get_synthese_tags_personnalises_semestre(
|
||||||
self.nt.formsemestre
|
self.nt.formsemestre
|
||||||
)
|
)
|
||||||
## Déduit des compétences
|
## Déduit des compétences
|
||||||
self.tags_competences = get_noms_competences_from_ues(self.nt.formsemestre)
|
dict_ues_competences = get_noms_competences_from_ues(self.nt.formsemestre)
|
||||||
|
|
||||||
# Supprime les doublons dans les tags
|
self.tags = (
|
||||||
tags_reserves = TAGS_RESERVES + list(self.tags_competences.values())
|
list(tags_personnalises.keys())
|
||||||
for tag in self.tags_personnalises:
|
+ list(dict_ues_competences.values())
|
||||||
if tag in tags_reserves:
|
+ ["but"]
|
||||||
del self.tags_personnalises[tag]
|
)
|
||||||
pe_affichage.pe_print(f"Supprime le tag {tag}")
|
"""Tags du semestre taggué"""
|
||||||
|
|
||||||
|
## Vérifie l'unicité des tags
|
||||||
|
if len(set(self.tags)) != len(self.tags):
|
||||||
|
raise ScoValueError(
|
||||||
|
f"""Erreur dans le module PE : L'un des tags saisis dans le programme
|
||||||
|
fait parti des tags réservés (par ex. "comp. <titre_compétence>"). Modifiez les
|
||||||
|
tags de votre programme"""
|
||||||
|
)
|
||||||
|
|
||||||
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
|
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
|
||||||
self.moyennes_tags = {}
|
self.moyennes_tags = {}
|
||||||
|
|
||||||
for tag in self.tags_personnalises:
|
for tag in tags_personnalises:
|
||||||
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
||||||
moy_gen_tag = self.compute_moyenne_tag(tag)
|
moy_gen_tag = self.compute_moyenne_tag(tag, tags_personnalises)
|
||||||
self.moyennes_tags[tag] = self.comp_moy_et_stat(moy_gen_tag)
|
self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_tag)
|
||||||
|
|
||||||
# Ajoute les moyennes générales de BUT pour le semestre considéré
|
# Ajoute les moyennes générales de BUT pour le semestre considéré
|
||||||
moy_gen_but = self.nt.etud_moy_gen
|
moy_gen_but = self.nt.etud_moy_gen
|
||||||
moy_gen_but = pd.to_numeric(moy_gen_but, errors="coerce")
|
self.moyennes_tags["but"] = MoyenneTag("but", moy_gen_but)
|
||||||
self.moyennes_tags["but"] = self.comp_moy_et_stat(moy_gen_but)
|
|
||||||
|
|
||||||
# Ajoute les moyennes par compétence
|
# Ajoute les moyennes par compétence
|
||||||
for ue_id, competence in self.tags_competences.items():
|
for ue_id, competence in dict_ues_competences.items():
|
||||||
moy_ue = self.nt.etud_moy_ue[ue_id]
|
moy_ue = self.nt.etud_moy_ue[ue_id]
|
||||||
self.moyennes_tags[competence] = self.comp_moy_et_stat(moy_ue)
|
self.moyennes_tags[competence] = MoyenneTag(competence, moy_ue)
|
||||||
|
|
||||||
|
self.tags_sorted = self.get_all_tags()
|
||||||
|
"""Tags (personnalisés+compétences) par ordre alphabétique"""
|
||||||
|
|
||||||
# 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
|
self.notes = self.df_notes()
|
||||||
) # les tags (personnalisés+compétences) par ordre alphabétique
|
"""Dataframe synthétique des notes par tag"""
|
||||||
self.notes = (
|
|
||||||
self.df_notes()
|
|
||||||
) # Le dataframe synthétique des notes (=moyennes par tag)
|
|
||||||
|
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
f" => Traitement des tags {', '.join(self.tags_sorted)}"
|
f" => Traitement des tags {', '.join(self.tags_sorted)}"
|
||||||
@ -141,9 +148,10 @@ class SemestreTag(TableTag):
|
|||||||
"""Nom affiché pour le semestre taggué"""
|
"""Nom affiché pour le semestre taggué"""
|
||||||
return app.pe.pe_etudiant.nom_semestre_etape(self.formsemestre, avec_fid=True)
|
return app.pe.pe_etudiant.nom_semestre_etape(self.formsemestre, avec_fid=True)
|
||||||
|
|
||||||
def compute_moyenne_tag(self, tag: str) -> list:
|
def compute_moyenne_tag(self, tag: str, tags_infos: dict) -> pd.Series:
|
||||||
"""Calcule la moyenne des étudiants pour le tag indiqué,
|
"""Calcule la moyenne des étudiants pour le tag indiqué,
|
||||||
pour ce SemestreTag.
|
pour ce SemestreTag, en ayant connaissance des informations sur
|
||||||
|
les tags (dictionnaire donnant les coeff de repondération)
|
||||||
|
|
||||||
Sont pris en compte les modules implémentés associés au tag,
|
Sont pris en compte les modules implémentés associés au tag,
|
||||||
avec leur éventuel coefficient de **repondération**, en utilisant les notes
|
avec leur éventuel coefficient de **repondération**, en utilisant les notes
|
||||||
@ -151,8 +159,8 @@ class SemestreTag(TableTag):
|
|||||||
|
|
||||||
Force ou non le calcul de la moyenne lorsque des notes sont manquantes.
|
Force ou non le calcul de la moyenne lorsque des notes sont manquantes.
|
||||||
|
|
||||||
Renvoie les informations sous la forme d'une liste
|
Returns:
|
||||||
[ (moy, somme_coeff_normalise, etudid), ...]
|
La série des moyennes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
"""Adaptation du mask de calcul des moyennes au tag visé"""
|
"""Adaptation du mask de calcul des moyennes au tag visé"""
|
||||||
@ -163,13 +171,13 @@ class SemestreTag(TableTag):
|
|||||||
|
|
||||||
"""Désactive tous les modules qui ne sont pas pris en compte pour ce tag"""
|
"""Désactive tous les modules qui ne sont pas pris en compte pour ce tag"""
|
||||||
for i, modimpl in enumerate(self.formsemestre.modimpls_sorted):
|
for i, modimpl in enumerate(self.formsemestre.modimpls_sorted):
|
||||||
if modimpl.moduleimpl_id not in self.tags_personnalises[tag]:
|
if modimpl.moduleimpl_id not in tags_infos[tag]:
|
||||||
modimpls_mask[i] = False
|
modimpls_mask[i] = False
|
||||||
|
|
||||||
"""Applique la pondération des coefficients"""
|
"""Applique la pondération des coefficients"""
|
||||||
modimpl_coefs_ponderes_df = self.modimpl_coefs_df.copy()
|
modimpl_coefs_ponderes_df = self.modimpl_coefs_df.copy()
|
||||||
for modimpl_id in self.tags_personnalises[tag]:
|
for modimpl_id in tags_infos[tag]:
|
||||||
ponderation = self.tags_personnalises[tag][modimpl_id]["ponderation"]
|
ponderation = tags_infos[tag][modimpl_id]["ponderation"]
|
||||||
modimpl_coefs_ponderes_df[modimpl_id] *= ponderation
|
modimpl_coefs_ponderes_df[modimpl_id] *= ponderation
|
||||||
|
|
||||||
"""Calcule les moyennes pour le tag visé dans chaque UE (dataframe etudid x ues)"""
|
"""Calcule les moyennes pour le tag visé dans chaque UE (dataframe etudid x ues)"""
|
||||||
|
@ -40,8 +40,10 @@ Created on Thu Sep 8 09:36:33 2016
|
|||||||
import datetime
|
import datetime
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
@ -49,62 +51,34 @@ import pandas as pd
|
|||||||
TAGS_RESERVES = ["but"]
|
TAGS_RESERVES = ["but"]
|
||||||
|
|
||||||
|
|
||||||
class TableTag(object):
|
class MoyenneTag:
|
||||||
def __init__(self):
|
def __init__(self, tag: str, notes: pd.Series):
|
||||||
"""Classe centralisant différentes méthodes communes aux
|
"""Classe centralisant la synthèse des moyennes/classements d'une série
|
||||||
SemestreTag, TrajectoireTag, AggregatInterclassTag
|
d'étudiants à un tag donné, en stockant un dictionnaire :
|
||||||
|
|
||||||
|
``
|
||||||
|
{
|
||||||
|
"notes": la Serie pandas des notes (float),
|
||||||
|
"classements": la Serie pandas des classements (float),
|
||||||
|
"min": la note minimum,
|
||||||
|
"max": la note maximum,
|
||||||
|
"moy": la moyenne,
|
||||||
|
"nb_inscrits": le nombre d'étudiants ayant une note,
|
||||||
|
}
|
||||||
|
``
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tag: Un tag
|
||||||
|
note: Une série de notes (moyenne) sous forme d'un pd.Series()
|
||||||
"""
|
"""
|
||||||
pass
|
self.tag = tag
|
||||||
|
"""Le tag associé à la moyenne"""
|
||||||
|
self.synthese = self.comp_moy_et_stat(notes)
|
||||||
|
"""La synthèse des notes/classements/statistiques"""
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------
|
def __eq__(self, other):
|
||||||
def get_all_tags(self):
|
"""Egalité de deux MoyenneTag lorsque leur tag sont identiques"""
|
||||||
"""Liste des tags de la table, triée par ordre alphabétique
|
return self.tag == other.tag
|
||||||
|
|
||||||
Returns:
|
|
||||||
Liste de tags triés par ordre alphabétique
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Un dataframe etudids x tag (avec tag par ordre alphabétique)
|
|
||||||
"""
|
|
||||||
tags = self.get_all_tags()
|
|
||||||
if tags:
|
|
||||||
dict_series = {tag: self.moyennes_tags[tag]["notes"] for tag in tags}
|
|
||||||
df = pd.DataFrame(dict_series)
|
|
||||||
return df
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def str_tagtable(self):
|
|
||||||
"""Renvoie une chaine de caractère listant toutes les moyennes,
|
|
||||||
les rangs des étudiants pour tous les tags."""
|
|
||||||
|
|
||||||
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.to_csv(sep=";")
|
|
||||||
|
|
||||||
def comp_moy_et_stat(self, notes: pd.Series) -> dict:
|
def comp_moy_et_stat(self, notes: pd.Series) -> dict:
|
||||||
"""Calcule et structure les données nécessaires au PE pour une série
|
"""Calcule et structure les données nécessaires au PE pour une série
|
||||||
@ -131,7 +105,7 @@ class TableTag(object):
|
|||||||
(_, 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 = {
|
||||||
@ -140,42 +114,115 @@ class TableTag(object):
|
|||||||
"min": notes.min(),
|
"min": notes.min(),
|
||||||
"max": notes.max(),
|
"max": notes.max(),
|
||||||
"moy": notes.mean(),
|
"moy": notes.mean(),
|
||||||
"nb_inscrits": len(indices),
|
"nb_inscrits": sum(indices),
|
||||||
}
|
}
|
||||||
return synthese
|
return synthese
|
||||||
|
|
||||||
@classmethod
|
def get_df_notes(self, arrondi=False):
|
||||||
def get_min_for_df(cls, bilan: dict) -> float:
|
"""Série des notes, arrondies à 2 chiffres après la virgule"""
|
||||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
if arrondi:
|
||||||
revoie le min renseigné pour affichage dans un df"""
|
serie = self.synthese["notes"].round(2)
|
||||||
return round(bilan["min"], 2)
|
else:
|
||||||
|
serie = self.synthese["notes"]
|
||||||
|
df = serie.to_frame("notes")
|
||||||
|
return df
|
||||||
|
|
||||||
@classmethod
|
def get_df_rangs_pertinents(self) -> pd.Series:
|
||||||
def get_max_for_df(cls, bilan: dict) -> float:
|
"""Série des rangs classement/nbre_inscrit"""
|
||||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
classement = self.synthese["classements"]
|
||||||
renvoie le max renseigné pour affichage dans un df"""
|
indices = classement[classement.notnull()].index.to_list()
|
||||||
return round(bilan["max"], 2)
|
classement_non_nul = classement.loc[indices].to_frame("classements")
|
||||||
|
classement_non_nul.insert(1, "rangs", np.nan)
|
||||||
|
|
||||||
@classmethod
|
nb_inscrit = self.synthese["nb_inscrits"]
|
||||||
def get_moy_for_df(cls, bilan: dict) -> float:
|
|
||||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
|
||||||
renvoie la moyenne renseignée pour affichage dans un df"""
|
|
||||||
return round(bilan["moy"], 2)
|
|
||||||
|
|
||||||
@classmethod
|
classement_non_nul["rangs"] = classement_non_nul["classements"].astype(int).astype(str) + "/" + str(nb_inscrit)
|
||||||
def get_class_for_df(cls, bilan: dict, etudid: int) -> str:
|
return classement_non_nul["rangs"].to_frame("rangs")
|
||||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
|
||||||
renvoie le classement ramené au nombre d'inscrits,
|
def get_note_for_df(self, etudid: int):
|
||||||
|
"""Note d'un étudiant donné par son etudid"""
|
||||||
|
return round(self.synthese["notes"].loc[etudid], 2)
|
||||||
|
|
||||||
|
def get_min_for_df(self) -> float:
|
||||||
|
"""Min renseigné pour affichage dans un df"""
|
||||||
|
return round(self.synthese["min"], 2)
|
||||||
|
|
||||||
|
def get_max_for_df(self) -> float:
|
||||||
|
"""Max renseigné pour affichage dans un df"""
|
||||||
|
return round(self.synthese["max"], 2)
|
||||||
|
|
||||||
|
def get_moy_for_df(self) -> float:
|
||||||
|
"""Moyenne renseignée pour affichage dans un df"""
|
||||||
|
return round(self.synthese["moy"], 2)
|
||||||
|
|
||||||
|
def get_class_for_df(self, etudid: int) -> str:
|
||||||
|
"""Classement ramené au nombre d'inscrits,
|
||||||
pour un étudiant donné par son etudid"""
|
pour un étudiant donné par son etudid"""
|
||||||
classement = bilan['classements'].loc[etudid]
|
classement = self.synthese["classements"].loc[etudid]
|
||||||
|
nb_inscrit = self.synthese["nb_inscrits"]
|
||||||
if not pd.isna(classement):
|
if not pd.isna(classement):
|
||||||
return f"{classement}/{bilan['nb_inscrits']}"
|
classement = int(classement)
|
||||||
|
return f"{classement}/{nb_inscrit}"
|
||||||
else:
|
else:
|
||||||
return pe_affichage.SANS_NOTE
|
return pe_affichage.SANS_NOTE
|
||||||
|
|
||||||
@classmethod
|
def is_significatif(self) -> bool:
|
||||||
def get_note_for_df(cls, bilan: dict, etudid: int):
|
"""Indique si la moyenne est significative (c'est-à-dire à des notes)"""
|
||||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
return self.synthese["nb_inscrits"] > 0
|
||||||
renvoie la note (moyenne)
|
|
||||||
pour un étudiant donné par son etudid"""
|
|
||||||
return round(bilan["notes"].loc[etudid], 2)
|
class TableTag(object):
|
||||||
|
def __init__(self):
|
||||||
|
"""Classe centralisant différentes méthodes communes aux
|
||||||
|
SemestreTag, TrajectoireTag, AggregatInterclassTag
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------
|
||||||
|
def get_all_tags(self):
|
||||||
|
"""Liste des tags de la table, triée par ordre alphabétique,
|
||||||
|
extraite des clés du dictionnaire ``moyennes_tags`` connues (tags en doublon
|
||||||
|
possible).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Liste de tags triés par ordre alphabétique
|
||||||
|
"""
|
||||||
|
return sorted(list(self.moyennes_tags.keys()))
|
||||||
|
|
||||||
|
def df_moyennes_et_classements(self) -> pd.DataFrame:
|
||||||
|
"""Renvoie un dataframe listant toutes les moyennes,
|
||||||
|
et les classements des étudiants pour tous les tags.
|
||||||
|
|
||||||
|
Est utilisé pour afficher le détail d'un tableau taggué
|
||||||
|
(semestres, trajectoires ou aggrégat)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Le dataframe des notes et des classements
|
||||||
|
"""
|
||||||
|
|
||||||
|
etudiants = self.etudiants
|
||||||
|
df = pd.DataFrame.from_dict(etudiants, orient="index", columns=["nom"])
|
||||||
|
|
||||||
|
tags_tries = self.get_all_tags()
|
||||||
|
for tag in tags_tries:
|
||||||
|
moy_tag = self.moyennes_tags[tag]
|
||||||
|
df = df.join(moy_tag.synthese["notes"].rename(f"Moy {tag}"))
|
||||||
|
df = df.join(moy_tag.synthese["classements"].rename(f"Class {tag}"))
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
def df_notes(self) -> pd.DataFrame | None:
|
||||||
|
"""Renvoie un dataframe (etudid x tag) listant toutes les moyennes par tags
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Un dataframe etudids x tag (avec tag par ordre alphabétique)
|
||||||
|
"""
|
||||||
|
tags_tries = self.get_all_tags()
|
||||||
|
if tags_tries:
|
||||||
|
dict_series = {}
|
||||||
|
for tag in tags_tries:
|
||||||
|
# Les moyennes associés au tag
|
||||||
|
moy_tag = self.moyennes_tags[tag]
|
||||||
|
dict_series[tag] = moy_tag.synthese["notes"]
|
||||||
|
df = pd.DataFrame(dict_series)
|
||||||
|
return df
|
||||||
|
@ -6,43 +6,44 @@ from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
|
|||||||
|
|
||||||
|
|
||||||
class Trajectoire:
|
class Trajectoire:
|
||||||
"""Modélise, pour un aggrégat visé (par ex: 'S2', '3S', '2A')
|
|
||||||
et un ensemble d'étudiants donnés,
|
|
||||||
la combinaison des formsemestres des étudiants amenant à un semestre
|
|
||||||
terminal visé.
|
|
||||||
|
|
||||||
Si l'aggrégat est un semestre de type Si, elle stocke le (ou les)
|
|
||||||
formsemestres de numéro i qu'ont suivis l'étudiant pour atteindre le Si
|
|
||||||
(en général 1 si personnes n'a redoublé, mais 2 s'il y a des redoublants)
|
|
||||||
|
|
||||||
Pour des aggrégats de type iS ou iA (par ex, 3A=S1+S2+S3), elle identifie
|
|
||||||
les semestres que les étudiants ont suivis pour les amener jusqu'au semestre
|
|
||||||
terminal de la trajectoire (par ex: ici un S3).
|
|
||||||
Ces semestres peuvent être :
|
|
||||||
* des S1+S2+S1+S2+S3 si redoublement de la 1ère année
|
|
||||||
* des S1+S2+(année de césure)+S3 si césure, ...
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, nom_aggregat: str, semestre_final: FormSemestre):
|
def __init__(self, nom_aggregat: str, semestre_final: FormSemestre):
|
||||||
"""Modélise un ensemble de formsemestres d'étudiants
|
"""Modélise un ensemble de formsemestres d'étudiants
|
||||||
amenant à un semestre terminal
|
amenant à un semestre terminal, au sens d'un aggrégat (par ex: 'S2', '3S', '2A').
|
||||||
|
|
||||||
|
Si l'aggrégat est un semestre de type Si, elle stocke le (ou les)
|
||||||
|
formsemestres de numéro i qu'ont suivi l'étudiant pour atteindre le Si
|
||||||
|
(en général 1 si personnes n'a redoublé, mais 2 s'il y a des redoublants)
|
||||||
|
|
||||||
|
Pour des aggrégats de type iS ou iA (par ex, 3A=S1+S2+S3), elle identifie
|
||||||
|
les semestres que les étudiants ont suivis pour les amener jusqu'au semestre
|
||||||
|
terminal de la trajectoire (par ex: ici un S3).
|
||||||
|
|
||||||
|
Ces semestres peuvent être :
|
||||||
|
|
||||||
|
* des S1+S2+S1+S2+S3 si redoublement de la 1ère année
|
||||||
|
* des S1+S2+(année de césure)+S3 si césure, ...
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
nom_aggregat: Un nom d'aggrégat (par ex: '5S')
|
nom_aggregat: Un nom d'aggrégat (par ex: '5S')
|
||||||
semestre_final: Le semestre final de l'aggrégat
|
semestre_final: Le semestre final de l'aggrégat
|
||||||
"""
|
"""
|
||||||
self.nom = nom_aggregat
|
self.nom = nom_aggregat
|
||||||
self.semestre_final = semestre_final
|
"""Nom de l'aggrégat"""
|
||||||
self.trajectoire_id = (nom_aggregat, semestre_final.formsemestre_id)
|
|
||||||
|
self.formsemestre_final = semestre_final
|
||||||
|
"""FormSemestre terminal de la trajectoire"""
|
||||||
|
|
||||||
|
self.trajectoire_id = (nom_aggregat, semestre_final.formsemestre_id)
|
||||||
|
"""Identifiant de la trajectoire"""
|
||||||
|
|
||||||
"""Les semestres à aggréger"""
|
|
||||||
self.semestres_aggreges = {}
|
self.semestres_aggreges = {}
|
||||||
|
"""Semestres aggrégés"""
|
||||||
|
|
||||||
def add_semestres_a_aggreger(self, semestres: dict[int:FormSemestre]):
|
def add_semestres_a_aggreger(self, semestres: dict[int:FormSemestre]):
|
||||||
"""Ajoute des semestres au semestre à aggréger
|
"""Ajout de semestres aux semestres à aggréger
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
semestres: Dictionnaire ``{fid: FormSemestre(fid)} à ajouter``
|
semestres: Dictionnaire ``{fid: FormSemestre(fid)}`` à ajouter
|
||||||
"""
|
"""
|
||||||
self.semestres_aggreges = self.semestres_aggreges | semestres
|
self.semestres_aggreges = self.semestres_aggreges | semestres
|
||||||
|
|
||||||
@ -55,27 +56,30 @@ class Trajectoire:
|
|||||||
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.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"
|
||||||
if verbose and noms:
|
if verbose and noms:
|
||||||
repr += " - " + "+".join(noms)
|
repr += " - " + "+".join(noms)
|
||||||
return repr
|
return repr
|
||||||
|
|
||||||
|
|
||||||
class TrajectoiresJuryPE:
|
class TrajectoiresJuryPE:
|
||||||
"""Centralise toutes les trajectoires du jury PE"""
|
|
||||||
|
|
||||||
def __init__(self, annee_diplome: int):
|
def __init__(self, annee_diplome: int):
|
||||||
"""
|
"""Classe centralisant toutes les trajectoires des étudiants à prendre
|
||||||
|
en compte dans un jury PE
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
annee_diplome: L'année de diplomation
|
annee_diplome: L'année de diplomation
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.annee_diplome = annee_diplome
|
self.annee_diplome = annee_diplome
|
||||||
"""Toutes les trajectoires possibles"""
|
"""Toutes les trajectoires possibles"""
|
||||||
|
|
||||||
self.trajectoires: dict[tuple:Trajectoire] = {}
|
self.trajectoires: dict[tuple:Trajectoire] = {}
|
||||||
"""Quelle trajectoires pour quel étudiant :
|
"""Ensemble des trajectoires recensées : {(aggregat, fid_terminal): Trajectoire}"""
|
||||||
dictionnaire {etudid: {nom_aggregat: Trajectoire}}"""
|
|
||||||
self.suivi: dict[int:str] = {}
|
self.suivi: dict[int:str] = {}
|
||||||
|
"""Dictionnaire associant, pour chaque étudiant et pour chaque aggrégat,
|
||||||
|
sa trajectoire : {etudid: {nom_aggregat: Trajectoire}}"""
|
||||||
|
|
||||||
def cree_trajectoires(self, etudiants: EtudiantsJuryPE):
|
def cree_trajectoires(self, etudiants: EtudiantsJuryPE):
|
||||||
"""Créé toutes les trajectoires, au regard du cursus des étudiants
|
"""Créé toutes les trajectoires, au regard du cursus des étudiants
|
||||||
@ -122,9 +126,6 @@ class TrajectoiresJuryPE:
|
|||||||
"""Mémoire la trajectoire suivie par l'étudiant"""
|
"""Mémoire la trajectoire suivie par l'étudiant"""
|
||||||
self.suivi[etudid][nom_aggregat] = trajectoire
|
self.suivi[etudid][nom_aggregat] = trajectoire
|
||||||
|
|
||||||
"""Vérifications"""
|
|
||||||
# dernier_semestre_aggregat = get_dernier_semestre_en_date(semestres_aggreges)
|
|
||||||
# assert dernier_semestre_aggregat == formsemestre_terminal
|
|
||||||
|
|
||||||
|
|
||||||
def get_trajectoires_etudid(trajectoires, etudid):
|
def get_trajectoires_etudid(trajectoires, etudid):
|
||||||
@ -142,26 +143,3 @@ def get_trajectoires_etudid(trajectoires, etudid):
|
|||||||
return liste
|
return liste
|
||||||
|
|
||||||
|
|
||||||
def get_semestres_a_aggreger(self, aggregat: str, formsemestre_id_terminal: int):
|
|
||||||
"""Pour un nom d'aggrégat donné (par ex: 'S3') et un semestre terminal cible
|
|
||||||
identifié par son formsemestre_id (par ex: 'S3 2022-2023'),
|
|
||||||
renvoie l'ensemble des semestres à prendre en compte dans
|
|
||||||
l'aggrégat sous la forme d'un dictionnaire {fid: FormSemestre(fid)}.
|
|
||||||
|
|
||||||
Fusionne les cursus individuels des étudiants, dont le cursus correspond
|
|
||||||
à l'aggrégat visé.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
aggregat: Un aggrégat (par ex. 1A, 2A, 3S, 6S)
|
|
||||||
formsemestre_id_terminal: L'identifiant du formsemestre terminal de l'aggrégat, devant correspondre au
|
|
||||||
dernier semestre de l'aggrégat
|
|
||||||
"""
|
|
||||||
noms_semestres_aggreges = pe_comp.PARCOURS[aggregat]["aggregat"]
|
|
||||||
|
|
||||||
formsemestres = {}
|
|
||||||
for etudid in self.cursus:
|
|
||||||
cursus_etudiant = self.cursus[etudid][aggregat]
|
|
||||||
if formsemestre_id_terminal in cursus_etudiant:
|
|
||||||
formsemestres_etudiant = cursus_etudiant[formsemestre_id_terminal]
|
|
||||||
formsemestres = formsemestres | formsemestres_etudiant
|
|
||||||
return formsemestres
|
|
||||||
|
@ -43,7 +43,7 @@ import pandas as pd
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from app.pe.pe_trajectoire import Trajectoire
|
from app.pe.pe_trajectoire import Trajectoire
|
||||||
|
|
||||||
from app.pe.pe_tabletags import TableTag
|
from app.pe.pe_tabletags import TableTag, MoyenneTag
|
||||||
|
|
||||||
|
|
||||||
class TrajectoireTag(TableTag):
|
class TrajectoireTag(TableTag):
|
||||||
@ -58,18 +58,25 @@ 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'
|
||||||
|
|
||||||
|
Args:
|
||||||
|
trajectoire: Une trajectoire (aggrégat+semestre terminal)
|
||||||
|
semestres_taggues: Les données sur les semestres taggués
|
||||||
"""
|
"""
|
||||||
TableTag.__init__(self)
|
TableTag.__init__(self)
|
||||||
|
|
||||||
# La trajectoire associée
|
|
||||||
self.trajectoire_id = trajectoire.trajectoire_id
|
self.trajectoire_id = trajectoire.trajectoire_id
|
||||||
|
"""Identifiant de la trajectoire tagguée"""
|
||||||
|
|
||||||
self.trajectoire = trajectoire
|
self.trajectoire = trajectoire
|
||||||
|
"""Trajectoire associée à la trajectoire tagguée"""
|
||||||
|
|
||||||
# Le nom de la trajectoire tagguée (identique à la trajectoire)
|
|
||||||
self.nom = self.get_repr()
|
self.nom = self.get_repr()
|
||||||
|
"""Représentation textuelle de la trajectoire tagguée"""
|
||||||
|
|
||||||
self.formsemestre_terminal = trajectoire.semestre_final
|
self.formsemestre_terminal = trajectoire.formsemestre_final
|
||||||
"""Le formsemestre terminal"""
|
"""Le formsemestre terminal"""
|
||||||
|
|
||||||
# Les résultats du formsemestre terminal
|
# Les résultats du formsemestre terminal
|
||||||
nt = load_formsemestre_results(self.formsemestre_terminal)
|
nt = load_formsemestre_results(self.formsemestre_terminal)
|
||||||
|
|
||||||
@ -100,11 +107,15 @@ class TrajectoireTag(TableTag):
|
|||||||
self.notes = compute_tag_moy(self.notes_cube, etudids, self.tags_sorted)
|
self.notes = compute_tag_moy(self.notes_cube, etudids, self.tags_sorted)
|
||||||
"""Calcul les moyennes par tag sous forme d'un dataframe"""
|
"""Calcul les moyennes par tag sous forme d'un dataframe"""
|
||||||
|
|
||||||
self.moyennes_tags = {}
|
self.moyennes_tags: dict[str, MoyenneTag] = {}
|
||||||
"""Synthétise les moyennes/classements par tag (qu'ils soient personnalisé ou de compétences)"""
|
"""Synthétise les moyennes/classements par tag (qu'ils soient personnalisé ou de compétences)"""
|
||||||
for tag in self.tags_sorted:
|
for tag in self.tags_sorted:
|
||||||
moy_gen_tag = self.notes[tag]
|
moy_gen_tag = self.notes[tag]
|
||||||
self.moyennes_tags[tag] = self.comp_moy_et_stat(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
|
||||||
|
Loading…
Reference in New Issue
Block a user