forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -349,8 +349,8 @@ class EtudiantsJuryPE:
|
||||
trajectoire = trajectoire_aggr[aggregat]
|
||||
if trajectoire:
|
||||
# Le semestre terminal de l'étudiant de l'aggrégat
|
||||
fid = trajectoire.semestre_final.formsemestre_id
|
||||
formsemestres_terminaux[fid] = trajectoire.semestre_final
|
||||
fid = trajectoire.formsemestre_final.formsemestre_id
|
||||
formsemestres_terminaux[fid] = trajectoire.formsemestre_final
|
||||
return formsemestres_terminaux
|
||||
|
||||
def get_formsemestres(self, semestres_recherches=None):
|
||||
|
@ -1,5 +1,5 @@
|
||||
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_trajectoire import Trajectoire, TrajectoiresJuryPE
|
||||
from app.pe.pe_trajectoiretag import TrajectoireTag
|
||||
@ -10,7 +10,6 @@ import numpy as np
|
||||
|
||||
|
||||
class AggregatInterclasseTag(TableTag):
|
||||
|
||||
# -------------------------------------------------------------------------------------------------------------------
|
||||
def __init__(
|
||||
self,
|
||||
@ -30,24 +29,30 @@ class AggregatInterclasseTag(TableTag):
|
||||
"""
|
||||
TableTag.__init__(self)
|
||||
|
||||
# Le nom
|
||||
self.aggregat = nom_aggregat
|
||||
"""Aggrégat de l'interclassement"""
|
||||
|
||||
self.nom = self.get_repr()
|
||||
|
||||
"""Les étudiants diplômés et leurs trajectoires (cf. trajectoires.suivis)""" # TODO
|
||||
self.diplomes_ids = etudiants.etudiants_diplomes
|
||||
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}
|
||||
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
|
||||
self.trajectoires: dict[int, Trajectoire] = {}
|
||||
"""Ensemble des trajectoires associées à l'aggrégat"""
|
||||
for trajectoire_id in trajectoires_jury_pe.trajectoires:
|
||||
trajectoire = trajectoires_jury_pe.trajectoires[trajectoire_id]
|
||||
if trajectoire_id[0] == nom_aggregat:
|
||||
self.trajectoires[trajectoire_id] = trajectoire
|
||||
|
||||
self.trajectoires_taggues: dict[int, Trajectoire] = {}
|
||||
"""Ensemble des trajectoires tagguées associées à l'aggrégat"""
|
||||
for trajectoire_id in self.trajectoires:
|
||||
self.trajectoires_taggues[trajectoire_id] = trajectoires_taggues[
|
||||
trajectoire_id
|
||||
@ -56,26 +61,27 @@ class AggregatInterclasseTag(TableTag):
|
||||
# Les trajectoires suivies par les étudiants du jury, en ne gardant que
|
||||
# celles associées aux diplomés
|
||||
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:
|
||||
self.suivi[etudid] = trajectoires_jury_pe.suivi[etudid][nom_aggregat]
|
||||
|
||||
|
||||
self.tags_sorted = self.do_taglist()
|
||||
"""Liste des tags (triés par ordre alphabétique)"""
|
||||
|
||||
# Construit la matrice de notes
|
||||
self.notes = self.compute_notes_matrice()
|
||||
"""Matrice des notes de l'aggrégat"""
|
||||
|
||||
# Synthétise les moyennes/classements par tag
|
||||
self.moyennes_tags = {}
|
||||
self.moyennes_tags: dict[str, MoyenneTag] = {}
|
||||
for tag in self.tags_sorted:
|
||||
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)
|
||||
self.significatif = len(self.tags_sorted) > 0
|
||||
|
||||
|
||||
def get_repr(self) -> str:
|
||||
"""Une représentation textuelle"""
|
||||
return f"Aggrégat {self.aggregat}"
|
||||
@ -118,7 +124,4 @@ class AggregatInterclasseTag(TableTag):
|
||||
etudids_communs, tags_communs
|
||||
]
|
||||
|
||||
# Force les nan
|
||||
df.fillna(np.nan)
|
||||
|
||||
return df
|
||||
|
@ -65,35 +65,15 @@ import pandas as pd
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
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,
|
||||
2. tous les étudiants susceptibles à ce stade (au regard de leur parcours) d'être diplomés.
|
||||
|
||||
Args:
|
||||
sem_base: le FormSemestre donnant le semestre à la base du jury PE
|
||||
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
|
||||
diplome : l'année d'obtention du diplome BUT et du jury de PE (généralement février XXXX)
|
||||
"""
|
||||
self.diplome = diplome
|
||||
"L'année du diplome"
|
||||
@ -101,7 +81,7 @@ class JuryPE(object):
|
||||
self.nom_export_zip = f"Jury_PE_{self.diplome}"
|
||||
"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(
|
||||
f"""*** Recherche et chargement des étudiants diplômés en {
|
||||
self.diplome}"""
|
||||
@ -122,7 +102,6 @@ class JuryPE(object):
|
||||
self._gen_xls_synthese_jury_par_tag(zipfile)
|
||||
self._gen_xls_synthese_par_etudiant(zipfile)
|
||||
|
||||
|
||||
# Fin !!!! Tada :)
|
||||
|
||||
def _gen_xls_diplomes(self, zipfile: ZipFile):
|
||||
@ -356,7 +335,9 @@ class JuryPE(object):
|
||||
|
||||
for aggregat in aggregats:
|
||||
# Le dictionnaire par défaut des moyennes
|
||||
donnees[etudid] |= get_defaut_dict_synthese_aggregat(aggregat, self.diplome)
|
||||
donnees[etudid] |= get_defaut_dict_synthese_aggregat(
|
||||
aggregat, self.diplome
|
||||
)
|
||||
|
||||
# La trajectoire de l'étudiant sur l'aggrégat
|
||||
trajectoire = self.trajectoires.suivi[etudid][aggregat]
|
||||
@ -364,16 +345,17 @@ class JuryPE(object):
|
||||
trajectoire_tagguee = self.trajectoires_tagguees[
|
||||
trajectoire.trajectoire_id
|
||||
]
|
||||
else:
|
||||
trajectoire_tagguee = None
|
||||
|
||||
# L'interclassement
|
||||
interclass = self.interclassements_taggues[aggregat]
|
||||
|
||||
# Injection des données dans un dictionnaire
|
||||
donnees[etudid] |= get_dict_synthese_aggregat(aggregat, trajectoire_tagguee, interclass, etudid, tag, self.diplome)
|
||||
|
||||
if tag in trajectoire_tagguee.moyennes_tags:
|
||||
# La trajectoire tagguée
|
||||
moy_trajectoire_tag = trajectoire_tagguee.moyennes_tags[tag]
|
||||
if moy_trajectoire_tag.is_significatif():
|
||||
# L'interclassement
|
||||
interclass = self.interclassements_taggues[aggregat]
|
||||
|
||||
# Injection des données dans un dictionnaire
|
||||
donnees[etudid] |= get_dict_synthese_aggregat(
|
||||
aggregat, trajectoire_tagguee, interclass, etudid, tag, self.diplome
|
||||
)
|
||||
|
||||
# Fin de l'aggrégat
|
||||
# Construction du dataFrame
|
||||
@ -424,7 +406,9 @@ class JuryPE(object):
|
||||
|
||||
for aggregat in aggregats:
|
||||
# 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
|
||||
trajectoire = self.trajectoires.suivi[etudid][aggregat]
|
||||
@ -432,26 +416,25 @@ class JuryPE(object):
|
||||
trajectoire_tagguee = self.trajectoires_tagguees[
|
||||
trajectoire.trajectoire_id
|
||||
]
|
||||
else:
|
||||
trajectoire_tagguee = None
|
||||
if tag in trajectoire_tagguee.moyennes_tags:
|
||||
# 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
|
||||
# Construction du dataFrame
|
||||
df = pd.DataFrame.from_dict(donnees, orient="index")
|
||||
|
||||
# Tri par nom/prénom
|
||||
df.sort_values(
|
||||
by=[("", "", "tag")], inplace=True
|
||||
)
|
||||
df.sort_values(by=[("", "", "tag")], inplace=True)
|
||||
return df
|
||||
|
||||
|
||||
def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
|
||||
"""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
|
||||
@ -587,7 +570,7 @@ def get_dict_synthese_aggregat(
|
||||
interclassement_taggue: AggregatInterclasseTag,
|
||||
etudid: int,
|
||||
tag: str,
|
||||
diplome: int
|
||||
diplome: int,
|
||||
):
|
||||
"""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
|
||||
@ -600,66 +583,43 @@ def get_dict_synthese_aggregat(
|
||||
note = np.nan
|
||||
|
||||
# Les données de la trajectoire tagguée pour le tag considéré
|
||||
if trajectoire_tagguee and tag in trajectoire_tagguee.moyennes_tags:
|
||||
bilan = trajectoire_tagguee.moyennes_tags[tag]
|
||||
moy_tag = trajectoire_tagguee.moyennes_tags[tag]
|
||||
|
||||
# La moyenne de l'étudiant
|
||||
note = TableTag.get_note_for_df(bilan, etudid)
|
||||
# Les données de l'étudiant
|
||||
note = moy_tag.get_note_for_df(etudid)
|
||||
|
||||
# Statistiques sur le groupe
|
||||
if not pd.isna(note) and note != np.nan:
|
||||
# Les moyennes de cette trajectoire
|
||||
donnees |= {
|
||||
classement = moy_tag.get_class_for_df(etudid)
|
||||
nmin = moy_tag.get_min_for_df()
|
||||
nmax = moy_tag.get_max_for_df()
|
||||
nmoy = moy_tag.get_moy_for_df()
|
||||
|
||||
# Statistiques sur le groupe
|
||||
if not pd.isna(note) and note != np.nan:
|
||||
# Les moyennes de cette trajectoire
|
||||
donnees |= {
|
||||
(descr, "", "note"): note,
|
||||
(
|
||||
descr,
|
||||
NOM_STAT_GROUPE,
|
||||
"class.",
|
||||
): TableTag.get_class_for_df(bilan, etudid),
|
||||
(
|
||||
descr,
|
||||
NOM_STAT_GROUPE,
|
||||
"min",
|
||||
): 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),
|
||||
(descr, NOM_STAT_GROUPE, "class."): classement,
|
||||
(descr, NOM_STAT_GROUPE, "min"): nmin,
|
||||
(descr, NOM_STAT_GROUPE, "moy"): nmoy,
|
||||
(descr, NOM_STAT_GROUPE, "max"): nmax,
|
||||
}
|
||||
|
||||
# L'interclassement
|
||||
if tag in interclassement_taggue.moyennes_tags:
|
||||
bilan = interclassement_taggue.moyennes_tags[tag]
|
||||
moy_tag = interclassement_taggue.moyennes_tags[tag]
|
||||
|
||||
if not pd.isna(note) and note != np.nan:
|
||||
nom_stat_promo = f"{NOM_STAT_PROMO} {diplome}"
|
||||
classement = moy_tag.get_class_for_df(etudid)
|
||||
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
|
||||
|
@ -38,7 +38,7 @@ Created on Fri Sep 9 09:15:05 2016
|
||||
import numpy as np
|
||||
|
||||
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.moy_sem import comp_ranks_series
|
||||
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.codes_cursus import UE_SPORT
|
||||
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
|
||||
|
||||
|
||||
@ -94,44 +94,51 @@ class SemestreTag(TableTag):
|
||||
|
||||
# Les tags :
|
||||
## Saisis par l'utilisateur
|
||||
self.tags_personnalises = get_synthese_tags_personnalises_semestre(
|
||||
tags_personnalises = get_synthese_tags_personnalises_semestre(
|
||||
self.nt.formsemestre
|
||||
)
|
||||
## 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
|
||||
tags_reserves = TAGS_RESERVES + list(self.tags_competences.values())
|
||||
for tag in self.tags_personnalises:
|
||||
if tag in tags_reserves:
|
||||
del self.tags_personnalises[tag]
|
||||
pe_affichage.pe_print(f"Supprime le tag {tag}")
|
||||
self.tags = (
|
||||
list(tags_personnalises.keys())
|
||||
+ list(dict_ues_competences.values())
|
||||
+ ["but"]
|
||||
)
|
||||
"""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
|
||||
self.moyennes_tags = {}
|
||||
|
||||
for tag in self.tags_personnalises:
|
||||
for tag in tags_personnalises:
|
||||
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
||||
moy_gen_tag = self.compute_moyenne_tag(tag)
|
||||
self.moyennes_tags[tag] = self.comp_moy_et_stat(moy_gen_tag)
|
||||
moy_gen_tag = self.compute_moyenne_tag(tag, tags_personnalises)
|
||||
self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_tag)
|
||||
|
||||
# Ajoute les moyennes générales de BUT pour le semestre considéré
|
||||
moy_gen_but = self.nt.etud_moy_gen
|
||||
moy_gen_but = pd.to_numeric(moy_gen_but, errors="coerce")
|
||||
self.moyennes_tags["but"] = self.comp_moy_et_stat(moy_gen_but)
|
||||
self.moyennes_tags["but"] = MoyenneTag("but", moy_gen_but)
|
||||
|
||||
# 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]
|
||||
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
|
||||
self.tags_sorted = sorted(
|
||||
self.moyennes_tags
|
||||
) # les tags (personnalisés+compétences) par ordre alphabétique
|
||||
self.notes = (
|
||||
self.df_notes()
|
||||
) # Le dataframe synthétique des notes (=moyennes par tag)
|
||||
|
||||
self.notes = self.df_notes()
|
||||
"""Dataframe synthétique des notes par tag"""
|
||||
|
||||
pe_affichage.pe_print(
|
||||
f" => Traitement des tags {', '.join(self.tags_sorted)}"
|
||||
@ -141,9 +148,10 @@ class SemestreTag(TableTag):
|
||||
"""Nom affiché pour le semestre taggué"""
|
||||
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é,
|
||||
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,
|
||||
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.
|
||||
|
||||
Renvoie les informations sous la forme d'une liste
|
||||
[ (moy, somme_coeff_normalise, etudid), ...]
|
||||
Returns:
|
||||
La série des moyennes
|
||||
"""
|
||||
|
||||
"""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"""
|
||||
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
|
||||
|
||||
"""Applique la pondération des coefficients"""
|
||||
modimpl_coefs_ponderes_df = self.modimpl_coefs_df.copy()
|
||||
for modimpl_id in self.tags_personnalises[tag]:
|
||||
ponderation = self.tags_personnalises[tag][modimpl_id]["ponderation"]
|
||||
for modimpl_id in tags_infos[tag]:
|
||||
ponderation = tags_infos[tag][modimpl_id]["ponderation"]
|
||||
modimpl_coefs_ponderes_df[modimpl_id] *= ponderation
|
||||
|
||||
"""Calcule les moyennes pour le tag visé dans chaque UE (dataframe etudid x ues)"""
|
||||
|
@ -40,6 +40,7 @@ Created on Thu Sep 8 09:36:33 2016
|
||||
import datetime
|
||||
import numpy as np
|
||||
|
||||
from app import ScoValueError
|
||||
from app.comp.moy_sem import comp_ranks_series
|
||||
from app.pe import pe_affichage
|
||||
from app.scodoc import sco_utils as scu
|
||||
@ -48,63 +49,35 @@ import pandas as pd
|
||||
|
||||
TAGS_RESERVES = ["but"]
|
||||
|
||||
class MoyenneTag():
|
||||
|
||||
class TableTag(object):
|
||||
def __init__(self):
|
||||
"""Classe centralisant différentes méthodes communes aux
|
||||
SemestreTag, TrajectoireTag, AggregatInterclassTag
|
||||
def __init__(self, tag: str, notes: pd.Series):
|
||||
"""Classe centralisant la synthèse des moyennes/classements d'une série
|
||||
d'étudiants à un tag donné, en stockant un dictionnaire :
|
||||
|
||||
``
|
||||
{
|
||||
"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 get_all_tags(self):
|
||||
"""Liste des tags de la table, triée par ordre alphabétique
|
||||
|
||||
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 __eq__(self, other):
|
||||
"""Egalité de deux MoyenneTag lorsque leur tag sont identiques"""
|
||||
return self.tag == other.tag
|
||||
|
||||
def comp_moy_et_stat(self, notes: pd.Series) -> dict:
|
||||
"""Calcule et structure les données nécessaires au PE pour une série
|
||||
@ -131,7 +104,7 @@ class TableTag(object):
|
||||
(_, class_gen_ue_non_nul) = comp_ranks_series(notes_non_nulles)
|
||||
|
||||
# Les classements (toutes notes confondues, avec NaN si pas de notes)
|
||||
class_gen_ue = pd.Series(np.nan, index=notes.index, dtype="Int64")
|
||||
class_gen_ue = pd.Series(np.nan, index=notes.index) #, dtype="Int64")
|
||||
class_gen_ue[indices] = class_gen_ue_non_nul[indices]
|
||||
|
||||
synthese = {
|
||||
@ -140,42 +113,97 @@ class TableTag(object):
|
||||
"min": notes.min(),
|
||||
"max": notes.max(),
|
||||
"moy": notes.mean(),
|
||||
"nb_inscrits": len(indices),
|
||||
"nb_inscrits": sum(indices),
|
||||
}
|
||||
return synthese
|
||||
|
||||
@classmethod
|
||||
def get_min_for_df(cls, bilan: dict) -> float:
|
||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
||||
revoie le min renseigné pour affichage dans un df"""
|
||||
return round(bilan["min"], 2)
|
||||
def get_note_for_df(self, etudid: int):
|
||||
"""Note d'un étudiant donné par son etudid"""
|
||||
return round(self.synthese["notes"].loc[etudid], 2)
|
||||
|
||||
@classmethod
|
||||
def get_max_for_df(cls, bilan: dict) -> float:
|
||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
||||
renvoie le max renseigné pour affichage dans un df"""
|
||||
return round(bilan["max"], 2)
|
||||
def get_min_for_df(self) -> float:
|
||||
"""Min renseigné pour affichage dans un df"""
|
||||
return round(self.synthese["min"], 2)
|
||||
|
||||
@classmethod
|
||||
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)
|
||||
def get_max_for_df(self) -> float:
|
||||
"""Max renseigné pour affichage dans un df"""
|
||||
return round(self.synthese["max"], 2)
|
||||
|
||||
@classmethod
|
||||
def get_class_for_df(cls, bilan: dict, etudid: int) -> str:
|
||||
"""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_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"""
|
||||
classement = bilan['classements'].loc[etudid]
|
||||
classement = self.synthese['classements'].loc[etudid]
|
||||
nb_inscrit = self.synthese['nb_inscrits']
|
||||
if not pd.isna(classement):
|
||||
return f"{classement}/{bilan['nb_inscrits']}"
|
||||
classement = int(classement)
|
||||
return f"{classement}/{nb_inscrit}"
|
||||
else:
|
||||
return pe_affichage.SANS_NOTE
|
||||
|
||||
@classmethod
|
||||
def get_note_for_df(cls, bilan: dict, etudid: int):
|
||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
||||
renvoie la note (moyenne)
|
||||
pour un étudiant donné par son etudid"""
|
||||
return round(bilan["notes"].loc[etudid], 2)
|
||||
def is_significatif(self) -> bool:
|
||||
"""Indique si la moyenne est significative (c'est-à-dire à des notes)"""
|
||||
return self.synthese["nb_inscrits"] > 0
|
||||
|
||||
class TableTag(object):
|
||||
def __init__(self):
|
||||
"""Classe centralisant différentes méthodes communes aux
|
||||
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:
|
||||
"""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):
|
||||
"""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:
|
||||
nom_aggregat: Un nom d'aggrégat (par ex: '5S')
|
||||
semestre_final: Le semestre final de l'aggrégat
|
||||
"""
|
||||
self.nom = nom_aggregat
|
||||
self.semestre_final = semestre_final
|
||||
self.trajectoire_id = (nom_aggregat, semestre_final.formsemestre_id)
|
||||
"""Nom de l'aggrégat"""
|
||||
|
||||
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 = {}
|
||||
"""Semestres aggrégés"""
|
||||
|
||||
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:
|
||||
semestres: Dictionnaire ``{fid: FormSemestre(fid)} à ajouter``
|
||||
semestres: Dictionnaire ``{fid: FormSemestre(fid)}`` à ajouter
|
||||
"""
|
||||
self.semestres_aggreges = self.semestres_aggreges | semestres
|
||||
|
||||
@ -55,27 +56,30 @@ class Trajectoire:
|
||||
semestre = self.semestres_aggreges[fid]
|
||||
noms.append(f"S{semestre.semestre_id}({fid})")
|
||||
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:
|
||||
repr += " - " + "+".join(noms)
|
||||
return repr
|
||||
|
||||
|
||||
class TrajectoiresJuryPE:
|
||||
"""Centralise toutes les trajectoires du jury PE"""
|
||||
|
||||
def __init__(self, annee_diplome: int):
|
||||
"""
|
||||
"""Classe centralisant toutes les trajectoires des étudiants à prendre
|
||||
en compte dans un jury PE
|
||||
|
||||
Args:
|
||||
annee_diplome: L'année de diplomation
|
||||
"""
|
||||
|
||||
self.annee_diplome = annee_diplome
|
||||
"""Toutes les trajectoires possibles"""
|
||||
|
||||
self.trajectoires: dict[tuple:Trajectoire] = {}
|
||||
"""Quelle trajectoires pour quel étudiant :
|
||||
dictionnaire {etudid: {nom_aggregat: Trajectoire}}"""
|
||||
"""Ensemble des trajectoires recensées : {(aggregat, fid_terminal): Trajectoire}"""
|
||||
|
||||
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):
|
||||
"""Créé toutes les trajectoires, au regard du cursus des étudiants
|
||||
@ -122,9 +126,6 @@ class TrajectoiresJuryPE:
|
||||
"""Mémoire la trajectoire suivie par l'étudiant"""
|
||||
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):
|
||||
@ -142,26 +143,3 @@ def get_trajectoires_etudid(trajectoires, etudid):
|
||||
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
|
||||
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):
|
||||
@ -58,6 +58,9 @@ class TrajectoireTag(TableTag):
|
||||
|
||||
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)
|
||||
|
||||
@ -68,8 +71,9 @@ class TrajectoireTag(TableTag):
|
||||
# Le nom de la trajectoire tagguée (identique à la trajectoire)
|
||||
self.nom = self.get_repr()
|
||||
|
||||
self.formsemestre_terminal = trajectoire.semestre_final
|
||||
self.formsemestre_terminal = trajectoire.formsemestre_final
|
||||
"""Le formsemestre terminal"""
|
||||
|
||||
# Les résultats du formsemestre terminal
|
||||
nt = load_formsemestre_results(self.formsemestre_terminal)
|
||||
|
||||
@ -100,11 +104,11 @@ class TrajectoireTag(TableTag):
|
||||
self.notes = compute_tag_moy(self.notes_cube, etudids, self.tags_sorted)
|
||||
"""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)"""
|
||||
for tag in self.tags_sorted:
|
||||
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 get_repr(self, verbose=False) -> str:
|
||||
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
||||
|
Loading…
Reference in New Issue
Block a user