Mise en place des RCRCF + de l'agrégation des coeff pour les moyennes de RCSTag
This commit is contained in:
parent
1716daafde
commit
68bd20f8de
@ -680,18 +680,3 @@ def nom_semestre_etape(semestre: FormSemestre, avec_fid=False) -> str:
|
||||
|
||||
return " ".join(description)
|
||||
|
||||
|
||||
def convert_trajectoire_to_sxtag_id(trajectoire: dict[int:FormSemestre]) -> (int, int):
|
||||
"""Partant d'une trajectoire (dictionnaire de la forme {fid: FormSemestre}),
|
||||
renvoie l'identifiant (rang_sem, fid du semestre_terminal) associé"""
|
||||
if not trajectoire:
|
||||
return None
|
||||
rangs = [formsemestre.semestre_id for formsemestre in trajectoire.values()]
|
||||
assert len(set(rangs)) == 1, "Les trajectoires doivent être de même rang"
|
||||
rang = min(rangs)
|
||||
fid_terminal = list(trajectoire.values())[0].formsemestre_id
|
||||
for fid, formsemestre in trajectoire.items():
|
||||
if trajectoire[fid_terminal].date_fin <= formsemestre.date_fin:
|
||||
fid_terminal = fid
|
||||
return (rang, fid_terminal)
|
||||
|
||||
|
@ -98,7 +98,7 @@ class RCSInterclasseTag(TableTag):
|
||||
"""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] = rcss_jury_pe.suivi[etudid][nom_rcs]
|
||||
self.suivi[etudid] = rcss_jury_pe.rcss_suivis[etudid][nom_rcs]
|
||||
|
||||
self.tags_sorted = self.do_taglist()
|
||||
"""Liste des tags (triés par ordre alphabétique)"""
|
||||
|
@ -94,6 +94,9 @@ class JuryPE(object):
|
||||
self.etudiants.find_etudiants()
|
||||
self.diplomes_ids = self.etudiants.diplomes_ids
|
||||
|
||||
self.rcss_jury = pe_rcs.RCSsJuryPE(self.diplome)
|
||||
"""Les informations sur les regroupements de semestres"""
|
||||
|
||||
self.zipdata = io.BytesIO()
|
||||
with ZipFile(self.zipdata, "w") as zipfile:
|
||||
if not self.diplomes_ids:
|
||||
@ -102,8 +105,10 @@ class JuryPE(object):
|
||||
self._gen_xls_diplomes(zipfile)
|
||||
|
||||
self._gen_xls_ressembuttags(zipfile)
|
||||
self._gen_rcss()
|
||||
self._gen_xls_sxtags(zipfile)
|
||||
# self._gen_xls_rcss_tags(zipfile)
|
||||
# self._gen_rcrcfs()
|
||||
# self._gen_xls_rcss_tags(zipfile)
|
||||
# self._gen_xls_interclassements_rcss(zipfile)
|
||||
# self._gen_xls_synthese_jury_par_tag(zipfile)
|
||||
# self._gen_xls_synthese_par_etudiant(zipfile)
|
||||
@ -173,51 +178,46 @@ class JuryPE(object):
|
||||
)
|
||||
|
||||
def _gen_rcss(self):
|
||||
"""Génère les RCS (attribut `rcss_jury`), combinaisons de semestres suivis par les étudiants au sens
|
||||
d'un nom de RCS (par ex: 'S2' ou '3S').
|
||||
"""Génère les RCS (attribut `rcss_jury`), combinaisons de semestres
|
||||
suivis par les étudiants au sens d'un nom de RCS (par ex: 'S2' ou '3S').
|
||||
"""
|
||||
pe_affichage.pe_print(
|
||||
"*** Génère les RCS (différentes combinaisons de semestres) des étudiants"
|
||||
)
|
||||
self.rcss_jury = pe_rcs.RCSsJuryPE(self.diplome)
|
||||
|
||||
self.rcss_jury.cree_rcss(self.etudiants)
|
||||
self.rcss_jury.cree_rcfs(self.etudiants)
|
||||
|
||||
def _gen_xls_sxtags(self, zipfile: ZipFile):
|
||||
"""Génère les semestres taggués en s'appuyant sur les RCS de type Sx (pour
|
||||
identifier les redoublements impactant les semestres taggués).
|
||||
"""
|
||||
# Génère les regroupements de semestres de type Sx
|
||||
pe_affichage.pe_print("*** Génère les RCF (RCS de type Sx)***")
|
||||
self.rcss_jury.cree_rcfs(self.etudiants)
|
||||
|
||||
# Génère les moyennes des RCS de type Sx
|
||||
pe_affichage.pe_print("*** Calcule les moyennes des SxTag")
|
||||
|
||||
# Les regroupements de Sx
|
||||
self.regroupements = {}
|
||||
for rang in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT + 1):
|
||||
self.regroupements[rang] = {}
|
||||
for etudid in self.etudiants.etudiants_ids:
|
||||
trajectoire = self.etudiants.trajectoires[etudid][f"S{rang}"]
|
||||
if trajectoire:
|
||||
sxtag_id = pe_etudiant.convert_trajectoire_to_sxtag_id(trajectoire)
|
||||
if sxtag_id not in self.regroupements[rang]:
|
||||
self.regroupements[rang][sxtag_id] = {}
|
||||
else:
|
||||
self.regroupements[rang][sxtag_id] |= trajectoire
|
||||
|
||||
# Les SxTag
|
||||
# Les SxTag (moyenne de Sx par UE)
|
||||
self.sxtags = {}
|
||||
for rang in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT + 1):
|
||||
for sxtag_id in self.regroupements[rang]:
|
||||
trajectoires = self.regroupements[rang][sxtag_id]
|
||||
ressemstags = {fid: self.ressembuttags[fid] for fid in trajectoires}
|
||||
self.sxtags[sxtag_id] = pe_sxtag.SxTag(sxtag_id, ressemstags)
|
||||
for rcf_id, rcf in self.rcss_jury.rcfs.items():
|
||||
# SxTag traduisant le RCF
|
||||
sxtag_id = rcf_id
|
||||
# Les resultats des semestres taggués à prendre en compte dans le RCF
|
||||
ressemstags = {
|
||||
fid: self.ressembuttags[fid] for fid in rcf.semestres_aggreges
|
||||
}
|
||||
self.sxtags[sxtag_id] = pe_sxtag.SxTag(sxtag_id, ressemstags)
|
||||
|
||||
# Intègre le bilan des semestres taggués au zip final
|
||||
output = io.BytesIO()
|
||||
with pd.ExcelWriter( # pylint: disable=abstract-class-instantiated
|
||||
output, engine="openpyxl"
|
||||
) as writer:
|
||||
for sem_tag in self.sxtags.values():
|
||||
onglet = sem_tag.get_repr(verbose=False)
|
||||
df = sem_tag.df_moyennes_et_classements()
|
||||
for sxtag in self.sxtags.values():
|
||||
onglet = sxtag.get_repr(verbose=False)
|
||||
df = sxtag.df_moyennes_et_classements()
|
||||
# écriture dans l'onglet
|
||||
df.to_excel(writer, onglet, index=True, header=True)
|
||||
output.seek(0)
|
||||
@ -229,9 +229,16 @@ class JuryPE(object):
|
||||
path="details",
|
||||
)
|
||||
|
||||
def _gen_xls_rcss_tags(self, zipfile: ZipFile):
|
||||
"""Génère les RCS taggués (autres que ceux de type Sx), etc...
|
||||
en calculant les moyennes et les classements par tag pour chaque RCS.
|
||||
def _gen_rcrcfs(self):
|
||||
"""Génère les regroupements cohérents de RCFs qu'ont suivi chaque étudiant"""
|
||||
|
||||
pe_affichage.pe_print("*** Génère les RCRCF (regroupements de RCF de type Sx) amenant du S1 à un semestre final***")
|
||||
self.rcss_jury.cree_rcrcfs(self.etudiants)
|
||||
|
||||
|
||||
def _gen_xls_rcrcss_tags(self, zipfile: ZipFile):
|
||||
"""Génère les RCS taggués traduisant les moyennes (orientées compétences)
|
||||
de regroupements de semestre de type Sx, xA ou xS.
|
||||
|
||||
Stocke le résultat dans self.rccs_tag, un dictionnaire de
|
||||
la forme ``{nom_aggregat: {fid_terminal: RCSTag(fid_terminal)} }``
|
||||
@ -249,12 +256,11 @@ class JuryPE(object):
|
||||
"""
|
||||
|
||||
# Génère les moyennes des RCS de type Sx
|
||||
pe_affichage.pe_print("*** Calcule les moyennes des RCS")
|
||||
pe_affichage.pe_print("*** Calcule les moyennes des RC de RCFS")
|
||||
|
||||
self.rcss_tags = {}
|
||||
for rcs_id, rcs in self.rcss_jury.rcss.items():
|
||||
# nom = rcs.get_repr()
|
||||
self.rcss_tags[rcs_id] = RCSTag(rcs, self.res_sems_tags)
|
||||
for rcs_id, rcrcf in self.rcss_jury.rcrcfs.items():
|
||||
self.rcss_tags[rcs_id] = RCSTag(rcrcf, self.sxtags)
|
||||
|
||||
# Intègre le bilan des trajectoires tagguées au zip final
|
||||
output = io.BytesIO()
|
||||
@ -431,7 +437,7 @@ class JuryPE(object):
|
||||
# considéré
|
||||
trajectoires_tagguees = []
|
||||
for etudid in etudids:
|
||||
trajectoire = self.rcss_jury.suivi[etudid][aggregat]
|
||||
trajectoire = self.rcss_jury.rcss_suivis[etudid][aggregat]
|
||||
if trajectoire:
|
||||
tid = trajectoire.sxtag_id
|
||||
trajectoire_tagguee = self.rcss_tags[tid]
|
||||
@ -577,7 +583,7 @@ class JuryPE(object):
|
||||
)
|
||||
|
||||
# La trajectoire de l'étudiant sur l'aggrégat
|
||||
trajectoire = self.rcss_jury.suivi[etudid][aggregat]
|
||||
trajectoire = self.rcss_jury.rcss_suivis[etudid][aggregat]
|
||||
if trajectoire:
|
||||
trajectoire_tagguee = self.rcss_tags[trajectoire.sxtag_id]
|
||||
if tag in trajectoire_tagguee.moyennes_tags:
|
||||
|
@ -155,101 +155,74 @@ class Moyenne:
|
||||
return self.synthese["nb_inscrits"] > 0
|
||||
|
||||
|
||||
|
||||
class MoyennesTag:
|
||||
def __init__(
|
||||
self,
|
||||
tag: str,
|
||||
ues: dict[int, UniteEns],
|
||||
notes_ues: pd.DataFrame,
|
||||
ues_inscr_parcours_df: pd.DataFrame
|
||||
# notes_gen: pd.Series,
|
||||
matrice_notes: pd.DataFrame, # etudids x colonnes
|
||||
matrice_coeffs: pd.DataFrame # etudids x colonnes
|
||||
):
|
||||
"""Classe centralisant la synthèse des moyennes/classements d'une série
|
||||
d'étudiants à un tag donné, en différenciant les notes
|
||||
obtenues aux UE et au général (toutes UEs confondues)
|
||||
|
||||
|
||||
Args:
|
||||
tag: Un tag
|
||||
ues: La liste des UEs ayant servie au calcul de la moyenne
|
||||
notes_ues: Les moyennes (etudid x acronymes_ues) aux différentes UEs et pour le tag
|
||||
ues_inscr_parcours_df: Les inscriptions des etudid au UE
|
||||
matrice_notes: Les moyennes (etudid x acronymes_ues ou etudid x compétences) aux différentes UEs ou compétences
|
||||
matrice_coeffs: Les coeff à appliquer pour le calcul de la moyenne générale
|
||||
# notes_gen: Une série de notes (moyenne) sous forme d'un pd.Series() (toutes UEs confondues)
|
||||
"""
|
||||
self.tag = tag
|
||||
"""Le tag associé aux moyennes"""
|
||||
|
||||
# Les UE
|
||||
self.ues: dict[int, UniteEns] = ues
|
||||
"""Les UEs sur lesquelles sont calculées les moyennes"""
|
||||
|
||||
colonnes = list(notes_ues.columns)
|
||||
acronymes: list[str] = [self.ues[ue_id].acronyme for ue_id in self.ues]
|
||||
assert len(set(acronymes)) == len(
|
||||
colonnes
|
||||
), "Deux UEs ne peuvent pas avoir le même acronyme"
|
||||
|
||||
# Les inscriptions des etudids aux UEs
|
||||
self.ues_inscr_parcours_df: pd.DataFrame = ues_inscr_parcours_df
|
||||
"""Les inscriptions des etudids au UE en fonction de leur parcours"""
|
||||
|
||||
# Les coefficients à appliquer aux UEs pour la moyenne générale = ECTS
|
||||
self.ects = self.ues_inscr_parcours_df.fillna(0.0) * [
|
||||
ue.ects
|
||||
for ue in self.ues.values() # if ue.type != UE_SPORT <= déjà supprimé
|
||||
]
|
||||
# Les profils d'ects (pour debug)
|
||||
profils_ects = []
|
||||
for val in list(self.ects.values):
|
||||
if tuple(val) not in profils_ects:
|
||||
profils_ects.append(tuple(val))
|
||||
|
||||
# Les moyennes par UE
|
||||
self.notes_ues: pd.DataFrame = notes_ues
|
||||
"""Les notes aux UEs (dataframe)"""
|
||||
self.notes_ues.columns = acronymes # remplace les ue.id par leur acronyme
|
||||
self.moys_ues: dict[int, pd.DataFrame] = {}
|
||||
self.matrice_notes: pd.DataFrame = matrice_notes
|
||||
"""Les notes aux UEs ou aux compétences (DataFrame)"""
|
||||
|
||||
self.matrice_coeffs: pd.DataFrame = matrice_coeffs
|
||||
"""Les coeffs à appliquer pour le calcul des moyennes générales
|
||||
(toutes UE ou compétences confondues). NaN si étudiant non inscrit"""
|
||||
|
||||
self.moyennes: dict[int, pd.DataFrame] = {}
|
||||
"""Les dataframes retraçant les moyennes/classements/statistiques des étudiants aux UEs"""
|
||||
for ue in self.ues.values(): # if ue.type != UE_SPORT:
|
||||
notes = notes_ues[ue.acronyme]
|
||||
self.moys_ues[ue.acronyme] = Moyenne(notes)
|
||||
colonnes = self.matrice_notes.columns
|
||||
for col in colonnes: # if ue.type != UE_SPORT:
|
||||
notes = matrice_notes[col]
|
||||
self.moyennes[col] = Moyenne(notes)
|
||||
|
||||
# Les moyennes générales
|
||||
notes_gen = self.compute_moy_gen(self.notes_ues, self.ects)
|
||||
notes_gen = self.compute_moy_gen(self.matrice_notes, self.matrice_coeffs)
|
||||
self.notes_gen = notes_gen
|
||||
"""Les notes générales (moyenne toutes UEs confonudes)"""
|
||||
self.moy_gen = Moyenne(notes_gen)
|
||||
self.moyenne_gen = Moyenne(notes_gen)
|
||||
"""Le dataframe retraçant les moyennes/classements/statistiques général"""
|
||||
|
||||
pe_affichage.pe_print(f"> MoyTag pour {tag} avec")
|
||||
pe_affichage.pe_print(f" - ues={acronymes}")
|
||||
pe_affichage.pe_print(f" - ects={profils_ects}")
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Egalité de deux MoyenneTag lorsque leur tag sont identiques"""
|
||||
return self.tag == other.tag
|
||||
|
||||
def compute_moy_gen(
|
||||
self, moy_ues: pd.DataFrame, coeff_ues: pd.DataFrame
|
||||
) -> pd.Series:
|
||||
"""Calcule la moyenne générale (toutes UE confondus)
|
||||
def compute_moy_gen(self, moys: pd.DataFrame, coeffs: pd.DataFrame) -> pd.Series:
|
||||
"""Calcule la moyenne générale (toutes UE/compétences confondus)
|
||||
pour le tag considéré, en pondérant les notes obtenues au UE
|
||||
par les crédits ECTS.
|
||||
par les coeff (généralement les crédits ECTS).
|
||||
|
||||
Args:
|
||||
moy_ues: Les moyennes etudids x acronymes_ues
|
||||
coeff_ues: Les coeff etudids x ueids
|
||||
moys: Les moyennes etudids x acronymes_ues/compétences
|
||||
coeff: Les coeff etudids x ueids/compétences
|
||||
"""
|
||||
|
||||
# Calcule la moyenne générale dans le semestre (pondérée par le ECTS)
|
||||
try:
|
||||
moy_gen_tag = comp.moy_sem.compute_sem_moys_apc_using_ects(
|
||||
moy_ues,
|
||||
coeff_ues,
|
||||
moys,
|
||||
coeffs.fillna(0.0),
|
||||
# formation_id=self.formsemestre.formation_id,
|
||||
skip_empty_ues=True,
|
||||
)
|
||||
except TypeError as e:
|
||||
raise TypeError("Pb dans le calcul de la moyenne toutes UEs confondues")
|
||||
raise TypeError(
|
||||
"Pb dans le calcul de la moyenne toutes UEs/compétences confondues"
|
||||
)
|
||||
|
||||
return moy_gen_tag
|
||||
|
||||
|
338
app/pe/pe_rcs.py
338
app/pe/pe_rcs.py
@ -12,7 +12,7 @@ Created on 01-2024
|
||||
import app.pe.pe_comp as pe_comp
|
||||
|
||||
from app.models import FormSemestre
|
||||
from app.pe import pe_sxtag
|
||||
from app.pe import pe_sxtag, pe_affichage
|
||||
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
|
||||
|
||||
TYPES_RCS = {
|
||||
@ -80,28 +80,14 @@ TOUS_LES_RCS = list(TYPES_RCS.keys())
|
||||
TOUS_LES_SEMESTRES = [cle for cle in TYPES_RCS if cle.startswith("S")]
|
||||
|
||||
|
||||
def get_descr_rcs(nom_rcs: str) -> str:
|
||||
"""Renvoie la description pour les tableurs de synthèse
|
||||
Excel d'un nom de RCS"""
|
||||
return TYPES_RCS[nom_rcs]["descr"]
|
||||
|
||||
|
||||
class RCS:
|
||||
"""Modélise un ensemble de semestres finals d'étudiants
|
||||
associé à un type de regroupement cohérent de semestres
|
||||
donné (par ex: 'S2', '3S', '2A').
|
||||
|
||||
Si le RCS est un semestre de type Si, stocke le
|
||||
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 le RCS 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_rcs: Un nom du RCS (par ex: '5S')
|
||||
semestre_final: Le semestre final du RCS
|
||||
"""
|
||||
"""Modélise un regroupement cohérent de semestres (formsemestre ou de Sx)"""
|
||||
|
||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||
self.nom: str = nom_rcs
|
||||
@ -110,42 +96,155 @@ class RCS:
|
||||
self.formsemestre_final: FormSemestre = semestre_final
|
||||
"""FormSemestre terminal du RCS"""
|
||||
|
||||
self.rang_final = self.formsemestre_final.semestre_id
|
||||
"""Le rang du formsemestre final"""
|
||||
|
||||
self.rcs_id: (str, int) = (nom_rcs, semestre_final.formsemestre_id)
|
||||
"""Identifiant du RCS sous forme (nom_rcs, id du semestre_terminal)"""
|
||||
|
||||
# self.semestres_aggreges: dict[int:FormSemestre] = {}
|
||||
# """Semestres regroupés dans le RCS"""
|
||||
def get_formsemestre_id_final(self) -> int:
|
||||
"""Renvoie l'identifiant du formsemestre final du RCS
|
||||
|
||||
self.sxtags_aggreges: dict[(str, int): pe_sxtag.SxTag] = {}
|
||||
"""Les SxTag aggrégés"""
|
||||
|
||||
def get_formsemestre_id_final(self):
|
||||
"""Renvoie l'identifiant du formsemestre final du RCS"""
|
||||
Returns:
|
||||
L'id du formsemestre final (marquant la fin) du RCS
|
||||
"""
|
||||
return self.formsemestre_final.formsemestre_id
|
||||
|
||||
def add_sxtags_a_aggreger(self, sxtags: dict[(str,int): pe_sxtag.SxTag]):
|
||||
"""Ajout des SxTag aux semestres à regrouper
|
||||
def __repr__(self):
|
||||
"""Représentation textuelle d'un RCS"""
|
||||
return f"{self.nom} ({self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"
|
||||
|
||||
def get_repr(self):
|
||||
return self.__repr__()
|
||||
|
||||
def __eq(self, other):
|
||||
"""Egalité de RCS"""
|
||||
return (
|
||||
self.nom == other.nom
|
||||
and self.formsemestre_final == other.formsemestre_final
|
||||
)
|
||||
|
||||
|
||||
class RCF(RCS):
|
||||
"""Modélise un ensemble de (form)semestres d'étudiants
|
||||
associé à un type de regroupement cohérent de semestres
|
||||
donné (par ex: 'S2', '3S', '2A').
|
||||
|
||||
Si le RCF est un semestre de type Si, stocke 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 le RCF de type iS ou iA (par ex, 3A=S1+S2+S3), identifie
|
||||
les semestres que les étudiants ont suivis pour les amener jusqu'au semestre
|
||||
terminal du RCS (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_rcs: Un nom du RCS (par ex: '5S')
|
||||
semestre_final: Le formsemestre final du RCS
|
||||
"""
|
||||
|
||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||
RCS.__init__(self, nom_rcs, semestre_final)
|
||||
|
||||
self.semestres_aggreges: dict[int:FormSemestre] = {}
|
||||
"""Formsemestres regroupés dans le RCS"""
|
||||
|
||||
def add_semestres_a_aggreger(self, semestres: dict[int:FormSemestre]):
|
||||
"""Ajout de semestres aux semestres à regrouper
|
||||
|
||||
Args:
|
||||
sxtags: Dictionnaire ``{(str,fid): SxTag}`` à ajouter
|
||||
semestres: Dictionnaire ``{fid: Formsemestre)``
|
||||
"""
|
||||
self.sxtags_aggreges = self.sxtags_aggreges | sxtags
|
||||
self.semestres_aggreges = self.semestres_aggreges | semestres
|
||||
|
||||
def get_repr(self, verbose=True) -> str:
|
||||
"""Représentation textuelle d'un RCS
|
||||
basé sur ses sxtags aggrégés"""
|
||||
basé sur ses semestres aggrégés"""
|
||||
|
||||
noms = []
|
||||
for sxtag_id, sxtag in self.sxtags_aggreges.items():
|
||||
noms.append(f"S{sxtag.semestre_id}")
|
||||
for fid in self.semestres_aggreges:
|
||||
semestre = self.semestres_aggreges[fid]
|
||||
noms.append(f"S{semestre.semestre_id}({fid})")
|
||||
noms = sorted(noms)
|
||||
title = f"""{self.nom} ({
|
||||
self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"""
|
||||
title = f"""{str(self)}"""
|
||||
if verbose and noms:
|
||||
title += " - " + "+".join(noms)
|
||||
return title
|
||||
|
||||
|
||||
|
||||
def get_rcf_from_semestres_aggreges(
|
||||
rcfs: dict[(str, int):RCF], semestres_a_aggreges: list[FormSemestre]
|
||||
) -> (str, int):
|
||||
"""Partant d'un dictionnaire de RCFs (de la forme
|
||||
``{ (nom_rcs, fid): RCF }, et connaissant une liste
|
||||
de (form)semestres à aggréger, renvoie l'identifiant
|
||||
(nom_rcs, fid) du RCFs qui lui correspond (c'est à dire celui dont
|
||||
les semestres_aggregés par le RCF sont les même que les
|
||||
semestres_a_aggreger.
|
||||
|
||||
Returns:
|
||||
rcf_id: L'identifiant du RCF trouvé
|
||||
"""
|
||||
fids_semestres_a_aggreger = set(
|
||||
[frms.formsemestre_id for frms in semestres_a_aggreges]
|
||||
)
|
||||
for rcf_id, rcf in rcfs.items():
|
||||
fids_rcf = set(rcf.semestres_aggreges)
|
||||
if fids_rcf == fids_semestres_a_aggreger:
|
||||
return rcf_id
|
||||
return None
|
||||
|
||||
|
||||
class RCRCF:
|
||||
"""Modélise les RCF d'étudiants suivis par un étudiant dans
|
||||
le cadre d'un RCS donné (par ex: 3S=S1+S2+S3).
|
||||
|
||||
Pour rappel : un RCF (par ex. S1) combine les semestres 1 qu'a suivi
|
||||
l'étudiant pour valider son S1 (1 si étudiant standard, 2 si redoublant).
|
||||
|
||||
Le RCRCF 3S est donc le regroupement du RCF S1 + RCF S2 + RCF S3.
|
||||
|
||||
Il est identifié par le formsemestre de S3 marquant la fin du regroupement.
|
||||
|
||||
|
||||
Args:
|
||||
nom_rcs: Un nom du RCS (par ex: '5S')
|
||||
semestre_final: Le semestre final du RCS
|
||||
"""
|
||||
|
||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||
RCS.__init__(self, nom_rcs, semestre_final)
|
||||
|
||||
self.rcfs_aggreges: dict[(str, int) : pe_sxtag.SxTag] = {}
|
||||
"""Les RCFs à aggréger"""
|
||||
|
||||
def add_rcfs_a_aggreger(self, rcfs: dict[(str, int):RCF]):
|
||||
"""Ajout des RCFs aux RCFS à regrouper
|
||||
|
||||
Args:
|
||||
rcfs: Dictionnaire ``{(str,fid): RCF}`` à ajouter
|
||||
"""
|
||||
self.rcfs_aggreges = self.rcfs_aggreges | rcfs
|
||||
|
||||
def get_repr(self, verbose=True) -> str:
|
||||
"""Représentation textuelle d'un RCSF
|
||||
basé sur ses RCF aggrégés"""
|
||||
|
||||
noms = []
|
||||
for rcf_id, rcf in self.rcfs_aggreges.items():
|
||||
noms.append(rcf.get_repr())
|
||||
title = f"""{str(self)}"""
|
||||
if verbose and noms:
|
||||
title += " : " + "+".join(noms)
|
||||
return title
|
||||
|
||||
|
||||
class RCSsJuryPE:
|
||||
"""Classe centralisant tous les regroupements cohérents de
|
||||
semestres (RCS) des étudiants à prendre en compte dans un jury PE
|
||||
@ -158,78 +257,151 @@ class RCSsJuryPE:
|
||||
self.annee_diplome = annee_diplome
|
||||
"""Année de diplômation"""
|
||||
|
||||
self.rcss: dict[tuple:RCS] = {}
|
||||
"""Ensemble des RCS recensés : {(nom_RCS, fid_terminal): RCS}"""
|
||||
self.rcss: dict[tuple(int, str) : RCF] = {}
|
||||
"""Ensemble des RCS recensés"""
|
||||
|
||||
self.suivi: dict[int:str] = {}
|
||||
self.rcss_suivis: dict[int:dict] = {}
|
||||
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
||||
son RCS : {etudid: {nom_RCS: RCS}}"""
|
||||
son RCS : {etudid: {nom_RCS: RCS}}"""
|
||||
|
||||
def cree_rcss(self, etudiants: EtudiantsJuryPE, sxtags: dict[(str, int), pe_sxtag.SxTag]):
|
||||
self.rcfs: dict[tuple(int, str) : RCF] = {}
|
||||
"""Ensemble des RCF recensés : {(nom_RCS, fid_terminal): RCF}"""
|
||||
|
||||
self.rcfs_suivis: dict[int:dict] = {}
|
||||
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
||||
son RCS : {etudid: {nom_RCS: RCF}}"""
|
||||
|
||||
self.rcrcfs: dict[tuple(int, str) : RCRCF] = {}
|
||||
"""Ensemble des RCS recensés : {(nom_RCS, fid_terminal): RCRCF}"""
|
||||
|
||||
self.rcrcfs_suivis: dict[int:str] = {}
|
||||
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
||||
son RCRCF : {etudid: {nom_RCS: RCSx}}"""
|
||||
|
||||
def cree_rcss(self, etudiants: EtudiantsJuryPE):
|
||||
"""Créé tous les RCS, au regard du cursus des étudiants
|
||||
analysés et des SxTag calculés.
|
||||
|
||||
Les mémorise dans les données de chaque étudiant.
|
||||
analysés + les mémorise dans les données de l'étudiant
|
||||
|
||||
Args:
|
||||
etudiants: Les étudiants à prendre en compte dans le Jury PE
|
||||
pe_sxtag: Les Sx taggués
|
||||
"""
|
||||
|
||||
for etudid in etudiants.trajectoires:
|
||||
self.rcss_suivis[etudid] = {
|
||||
aggregat: None
|
||||
for aggregat in pe_comp.TOUS_LES_SEMESTRES
|
||||
+ TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
|
||||
}
|
||||
|
||||
for nom_rcs in pe_comp.TOUS_LES_SEMESTRES + TOUS_LES_RCS_AVEC_PLUSIEURS_SEM:
|
||||
# L'aggrégat considéré (par ex: 3S=S1+S2+S3), son nom de son semestre
|
||||
# terminal (par ex: S3) et son numéro (par ex: 3)
|
||||
noms_semestre_de_aggregat = TYPES_RCS[nom_rcs]["aggregat"] # ["S1", "S2", "S3"]
|
||||
nom_semestre_terminal = noms_semestre_de_aggregat[-1] # "S3"
|
||||
noms_semestre_de_aggregat = TYPES_RCS[nom_rcs]["aggregat"]
|
||||
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
|
||||
|
||||
for etudid in etudiants.trajectoires:
|
||||
if etudid not in self.suivi:
|
||||
self.suivi[etudid] = {
|
||||
aggregat: None
|
||||
for aggregat in pe_comp.TOUS_LES_SEMESTRES
|
||||
+ TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
|
||||
}
|
||||
|
||||
# Le formsemestre terminal (dernier en date) associé au
|
||||
# semestre marquant la fin de l'aggrégat
|
||||
# (par ex: son dernier S3 en date)
|
||||
semestres = etudiants.trajectoires[etudid][nom_semestre_terminal]
|
||||
if semestres:
|
||||
formsemestre_final = get_dernier_semestre_en_date(semestres)
|
||||
trajectoire = etudiants.trajectoires[etudid][nom_semestre_terminal]
|
||||
if trajectoire:
|
||||
formsemestre_final = get_dernier_semestre_en_date(trajectoire)
|
||||
|
||||
# Ajout ou récupération de la trajectoire
|
||||
# Ajout ou récupération du RCS associé
|
||||
rcs_id = (nom_rcs, formsemestre_final.formsemestre_id)
|
||||
if rcs_id not in self.rcss:
|
||||
rcs = RCS(nom_rcs, formsemestre_final)
|
||||
self.rcss[rcs_id] = rcs
|
||||
else:
|
||||
rcs = self.rcss[rcs_id]
|
||||
self.rcss[rcs_id] = RCF(nom_rcs, formsemestre_final)
|
||||
rcs = self.rcss[rcs_id]
|
||||
|
||||
# La liste des semestres de l'étudiant à prendre en compte
|
||||
# pour cette trajectoire
|
||||
semestres_a_aggreger = get_trajectoire_etudiant(
|
||||
semestres_a_aggreger = get_rcs_etudiant(
|
||||
etudiants.trajectoires[etudid], formsemestre_final, nom_rcs
|
||||
)
|
||||
|
||||
# Extrait les sxtags correspondants aux semestres à aggréger
|
||||
# (par ex. des 2 semestres S1(18)+S1(26) récupère le sxtag S1(26)
|
||||
sxtags_a_aggreger = {}
|
||||
semestres_tries = pe_comp.tri_semestres_par_rang(semestres_a_aggreger)
|
||||
for rang in semestres_tries:
|
||||
sems = semestres_tries[rang] # les 1 ou 2 semestres de même rang suivi
|
||||
sxtag_id = pe_sxtag.get_sxtag_from_semestres(sems, sxtags)
|
||||
if not sxtag_id:
|
||||
raise ValueError(f"Il manque un sxtag pour {sems}")
|
||||
sxtags_a_aggreger[sxtag_id] = sxtags[sxtag_id]
|
||||
# Ajout des semestres au RCS
|
||||
rcs.add_semestres_a_aggreger(semestres_a_aggreger)
|
||||
|
||||
# Ajout des semestres à la trajectoire
|
||||
rcs.add_sxtags_a_aggreger(sxtags_a_aggreger)
|
||||
# Mémorise le RCS suivi par l'étudiant
|
||||
self.rcss_suivis[etudid][nom_rcs] = rcs
|
||||
|
||||
# Mémoire la trajectoire suivie par l'étudiant
|
||||
self.suivi[etudid][nom_rcs] = rcs
|
||||
# Affichage pour debug
|
||||
jeunes = list(enumerate(self.rcss_suivis))
|
||||
for no_etud, etudid in jeunes[:20]:
|
||||
pe_affichage.pe_print(f"-> {etudiants.identites[etudid].nomprenom} :")
|
||||
for nom_rcs, rcs in self.rcss_suivis[etudid].items():
|
||||
if rcs:
|
||||
pe_affichage.pe_print(f" > RCS {nom_rcs}: {rcs.get_repr()}")
|
||||
|
||||
def cree_rcfs(self, etudiants: EtudiantsJuryPE):
|
||||
"""Créé les RCFs en ne conservant dans les RCS que les regroupements
|
||||
de type Sx"""
|
||||
self.rcfs = {}
|
||||
for rcs_id, rcs in self.rcss.items():
|
||||
if rcs and rcs.nom in pe_comp.TOUS_LES_SEMESTRES:
|
||||
self.rcfs[rcs_id] = rcs
|
||||
print(self.rcfs)
|
||||
|
||||
for etudid in self.rcss_suivis:
|
||||
for nom_rcs, rcs in self.rcss_suivis[etudid].items():
|
||||
if rcs and nom_rcs in pe_comp.TOUS_LES_SEMESTRES:
|
||||
if etudid not in self.rcfs_suivis:
|
||||
self.rcfs_suivis[etudid] = {}
|
||||
self.rcfs_suivis[etudid][nom_rcs] = rcs
|
||||
|
||||
# Affichage pour debug
|
||||
jeunes = list(enumerate(self.rcfs_suivis))
|
||||
for no_etud, etudid in jeunes[:20]:
|
||||
pe_affichage.pe_print(f"-> {etudiants.identites[etudid].nomprenom} :")
|
||||
for nom_rcs, rcs in self.rcfs_suivis[etudid].items():
|
||||
if rcs:
|
||||
pe_affichage.pe_print(f" > RCF {nom_rcs}: {rcs.get_repr()}")
|
||||
else:
|
||||
pe_affichage.pe_print(f" > RCF {nom_rcs}: <vide> !!! ")
|
||||
|
||||
def cree_rcrcfs(self, etudiants: EtudiantsJuryPE):
|
||||
"""Créé tous les RCRCF, au regard du cursus des étudiants
|
||||
analysés (trajectoires traduisant son parcours dans les
|
||||
différents semestres) + les mémorise dans les données de l'étudiant
|
||||
"""
|
||||
|
||||
# Pour tous les étudiants du jury
|
||||
for etudid in self.rcss_suivis:
|
||||
self.rcrcfs_suivis[etudid] = {}
|
||||
|
||||
for rcf_id, rcf in self.rcfs_suivis[etudid].items(): # Pour chaque RCS
|
||||
semestres_a_aggreger = rcf.semestres_aggreges
|
||||
|
||||
# Tri des semestres par rang
|
||||
semestres_tries = pe_comp.tri_semestres_par_rang(semestres_a_aggreger)
|
||||
|
||||
# Récupére les RCFs de type Sx traduisant sa trajectoire
|
||||
rcfs_a_aggreger = {}
|
||||
for semestres_du_rang in semestres_tries.values():
|
||||
if semestres_du_rang:
|
||||
rcf_id = get_rcf_from_semestres_aggreges(
|
||||
self.rcfs, semestres_du_rang
|
||||
)
|
||||
if rcf_id:
|
||||
raise ValueError(
|
||||
"Il manque un RCF pour créer les RCRCFs dans cree_rcrcfs"
|
||||
)
|
||||
rcfs_a_aggreger[rcf_id] = self.rcfs[rcf_id]
|
||||
|
||||
# Ajout du RCRCF
|
||||
if rcf_id not in self.rcrcfs:
|
||||
self.rcrfs[rcf_id] = RCRCF(rcf_id, rcf.formsemestre_final)
|
||||
rcrcf = self.rcrcfs[rcf_id]
|
||||
|
||||
# Ajout des RCFs au RCRCF
|
||||
rcrcf.add_rcfs_a_aggreger(rcfs_a_aggreger)
|
||||
|
||||
# Mémoire la trajectoire RCRCF suivie par l'étudiant
|
||||
nom_rcs = rcrcf.nom
|
||||
self.rcrcfs_suivis[etudid][nom_rcs] = rcrcf
|
||||
|
||||
|
||||
def get_trajectoire_etudiant(
|
||||
def get_rcs_etudiant(
|
||||
semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str
|
||||
) -> dict[int, FormSemestre]:
|
||||
"""Ensemble des semestres parcourus (trajectoire)
|
||||
@ -273,9 +445,3 @@ def get_trajectoire_etudiant(
|
||||
):
|
||||
semestres_aggreges[fid] = semestre
|
||||
return semestres_aggreges
|
||||
|
||||
|
||||
def get_descr_rcs(nom_rcs: str) -> str:
|
||||
"""Renvoie la description pour les tableurs de synthèse
|
||||
Excel d'un nom de RCS"""
|
||||
return TYPES_RCS[nom_rcs]["descr"]
|
||||
|
@ -41,76 +41,81 @@ from app.pe import pe_affichage
|
||||
from app.pe.pe_ressemtag import ResSemBUTTag
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from app.pe.pe_rcs import RCS
|
||||
from app.pe.pe_rcs import RCS, RCRCF
|
||||
from app.pe.pe_sxtag import SxTag
|
||||
|
||||
from app.pe.pe_tabletags import TableTag
|
||||
from app.pe.pe_moytag import MoyennesTag
|
||||
|
||||
|
||||
class RCSTag(TableTag):
|
||||
|
||||
def __init__(
|
||||
self, rcs: RCS, semestres_taggues: dict[int, ResSemBUTTag]
|
||||
):
|
||||
"""Calcule les moyennes par tag d'une combinaison de semestres
|
||||
(RCS), pour extraire les classements par tag pour un
|
||||
def __init__(self, rcrcf: RCS, sxstags: dict[(str, int): SxTag]):
|
||||
"""Calcule les moyennes par tag (orientées compétences)
|
||||
d'un regroupement de SxTag
|
||||
(RCRCF), pour extraire les classements par tag pour un
|
||||
groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous
|
||||
participé au semestre terminal.
|
||||
participé au même semestre terminal.
|
||||
|
||||
Args:
|
||||
rcs: Un RCS (identifié par un nom et l'id de son semestre terminal)
|
||||
semestres_taggues: Les données sur les semestres taggués
|
||||
sxstags: Les données sur les RCF taggués
|
||||
"""
|
||||
TableTag.__init__(self)
|
||||
|
||||
self.rcs_id: tuple(str, int) = rcs.rcs_id
|
||||
self.rcs_id: tuple(str, int) = rcrcf.rcs_id
|
||||
"""Identifiant du RCS taggué (identique au RCS sur lequel il s'appuie)"""
|
||||
|
||||
self.rcs: RCS = rcs
|
||||
"""RCS associé au RCS taggué"""
|
||||
self.rcrcf: RCRCF = rcrcf
|
||||
"""RCRCF associé au RCS taggué"""
|
||||
|
||||
self.nom = self.get_repr()
|
||||
"""Représentation textuelle du RCS taggué"""
|
||||
|
||||
self.formsemestre_terminal = rcs.formsemestre_final
|
||||
self.formsemestre_terminal = rcrcf.formsemestre_final
|
||||
"""Le formsemestre terminal"""
|
||||
|
||||
# Les résultats du formsemestre terminal
|
||||
nt = load_formsemestre_results(self.formsemestre_terminal)
|
||||
|
||||
self.semestres_aggreges = rcs.semestres_aggreges
|
||||
"""Les semestres aggrégés"""
|
||||
self.rcfs_aggreges = rcrcf.rcfs_aggreges
|
||||
"""Les RCFs aggrégés"""
|
||||
|
||||
self.res_sems_tags = {}
|
||||
"""Les semestres tags associés aux semestres aggrégés"""
|
||||
self.sxstags = {}
|
||||
"""Les SxTag associés aux RCF aggrégés"""
|
||||
try:
|
||||
for frmsem_id in self.semestres_aggreges:
|
||||
self.res_sems_tags[frmsem_id] = semestres_taggues[frmsem_id]
|
||||
for rcf_id in self.rcfs_aggreges:
|
||||
self.sxstags[rcf_id] = sxstags[rcf_id]
|
||||
except:
|
||||
raise ValueError("Semestres taggués manquants")
|
||||
raise ValueError("Semestres SxTag manquants")
|
||||
|
||||
# Les étudiants (etuds, états civils & etudis)
|
||||
self.etuds = nt.etuds
|
||||
self.add_etuds(nt.etuds)
|
||||
|
||||
# Les compétences (extraites des ues de tous les semestres)
|
||||
self.ues = self.comp_ues(tag="but")
|
||||
# Les compétences (extraites de tous les Sxtags)
|
||||
self.competences_sorted = self.do_complist()
|
||||
"""Compétences extraites de tous les SxTag aggrégés"""
|
||||
|
||||
# Les tags
|
||||
self.tags_sorted = self.do_taglist()
|
||||
"""Tags extraits de tous les semestres"""
|
||||
|
||||
self.notes_cube = self.compute_notes_cube()
|
||||
"""Cube de notes"""
|
||||
|
||||
etudids = list(self.etats_civils.keys())
|
||||
self.notes = compute_tag_moy(self.notes_cube, etudids, self.tags_sorted)
|
||||
"""Calcul les moyennes par tag sous forme d'un dataframe"""
|
||||
"""Tags extraits de tous les SxTag aggrégés"""
|
||||
|
||||
# Les moyennes
|
||||
self.moyennes_tags: dict[str, MoyennesTag] = {}
|
||||
"""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] = MoyennesTag(tag, moy_gen_tag)
|
||||
# Cube de note
|
||||
notes_cube, coeffs_cube = self.compute_notes_comps_cube(tag)
|
||||
|
||||
# Calcule des moyennes/coeffs sous forme d'un dataframe"""
|
||||
moys_competences, coeffs_competences = compute_notes_competences(
|
||||
notes_cube, coeffs_cube, self.etudids, self.competences_sorted
|
||||
)
|
||||
|
||||
# Les moyennes
|
||||
self.moyennes_tags[tag] = MoyennesTag(tag, moys_competences,
|
||||
coeffs_competences)
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Egalité de 2 RCS taggués sur la base de leur identifiant"""
|
||||
@ -119,11 +124,14 @@ class RCSTag(TableTag):
|
||||
def get_repr(self, verbose=False) -> str:
|
||||
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
||||
est basée)"""
|
||||
return self.rcs.get_repr(verbose=verbose)
|
||||
return self.rcrcf.get_repr(verbose=verbose)
|
||||
|
||||
def compute_notes_cube(self):
|
||||
"""Construit le cube de notes (etudid x tags x semestre_aggregé)
|
||||
nécessaire au calcul des moyennes de l'aggrégat
|
||||
def compute_notes_comps_cube(self, tag):
|
||||
"""Pour un tag donné, construit :
|
||||
* le cube de notes (etudid x competences x SxTag) nécessaire au calcul des moyennes,
|
||||
en remplaçant les données d'UE (obtenus du SxTag) par les compétences
|
||||
* le cube de coeffs (etudid x competences x SxTag) (traduisant les inscriptions)
|
||||
appliqué au calcul des différents SxTag
|
||||
"""
|
||||
# nb_tags = len(self.tags_sorted)
|
||||
# nb_etudiants = len(self.etuds)
|
||||
@ -131,55 +139,90 @@ class RCSTag(TableTag):
|
||||
|
||||
# Index du cube (etudids -> dim 0, tags -> dim 1)
|
||||
etudids = [etud.etudid for etud in self.etuds]
|
||||
tags = self.tags_sorted
|
||||
semestres_id = list(self.res_sems_tags.keys())
|
||||
competences_sorted = self.competences_sorted
|
||||
sxstags_ids = list(self.sxstags.keys())
|
||||
|
||||
dfs = {}
|
||||
notes_dfs = {}
|
||||
coeffs_dfs = {}
|
||||
|
||||
for frmsem_id in semestres_id:
|
||||
for sxtag_id, sxtag in sxstags_ids.item():
|
||||
# Partant d'un dataframe vierge
|
||||
df = pd.DataFrame(np.nan, index=etudids, columns=tags)
|
||||
notes_df = pd.DataFrame(np.nan, index=etudids, columns=competences_sorted)
|
||||
coeffs_df = pd.DataFrame(np.nan, index=etudids, columns=competences_sorted)
|
||||
|
||||
# Charge les notes du semestre tag
|
||||
notes = self.res_sems_tags[frmsem_id].notes
|
||||
moys_tag = sxtag.moyennes_tags[tag]
|
||||
|
||||
# Les étudiants & les tags commun au dataframe final et aux notes du semestre)
|
||||
etudids_communs = df.index.intersection(notes.index)
|
||||
tags_communs = df.columns.intersection(notes.columns)
|
||||
# Charge les notes et les coeffs du semestre tag
|
||||
notes = moys_tag.matrice_notes.copy() # avec une copie
|
||||
coeffs = moys_tag.matrice_coeffs.copy() # les coeffs
|
||||
|
||||
# Injecte les notes par tag
|
||||
df.loc[etudids_communs, tags_communs] = notes.loc[
|
||||
etudids_communs, tags_communs
|
||||
# Traduction des UE en compétences
|
||||
association_ues_comp = moys_tag.competences
|
||||
ues_columns_df = notes.columns
|
||||
comp_associes_aux_ues = [association_ues_comp[ue] for ue in ues_columns_df]
|
||||
notes.columns = comp_associes_aux_ues
|
||||
coeffs.columns = comp_associes_aux_ues
|
||||
|
||||
# Compétences communes
|
||||
comp_communes = list(set(competences_sorted) & set(comp_associes_aux_ues))
|
||||
|
||||
# Etudiants communs
|
||||
etudids_communs = notes_df.index.intersection(notes.index)
|
||||
|
||||
# Recopie des notes et des coeffs
|
||||
notes_df.loc[etudids_communs, comp_communes] = notes.loc[
|
||||
etudids_communs, comp_communes
|
||||
]
|
||||
coeffs_df.loc[etudids_communs, comp_communes] = coeffs.loc[
|
||||
etudids_communs, comp_communes
|
||||
]
|
||||
|
||||
# Supprime tout ce qui n'est pas numérique
|
||||
for col in df.columns:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
for col in notes_df.columns:
|
||||
notes_df[col] = pd.to_numeric(notes_df[col], errors="coerce")
|
||||
|
||||
# Stocke le df
|
||||
dfs[frmsem_id] = df
|
||||
# Stocke les dfs
|
||||
notes_dfs[sxtag_id] = notes_df
|
||||
coeffs_dfs[sxtag_id] = coeffs_df
|
||||
|
||||
"""Réunit les notes sous forme d'un cube etdids x tags x semestres"""
|
||||
semestres_x_etudids_x_tags = [dfs[fid].values for fid in dfs]
|
||||
etudids_x_tags_x_semestres = np.stack(semestres_x_etudids_x_tags, axis=-1)
|
||||
"""Réunit les notes sous forme d'un cube etudids x competences x semestres"""
|
||||
sxtag_x_etudids_x_comps = [notes_dfs[fid].values for fid in notes_dfs]
|
||||
notes_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
|
||||
|
||||
return etudids_x_tags_x_semestres
|
||||
"""Réunit les coeffs sous forme d'un cube etudids x competences x semestres"""
|
||||
sxtag_x_etudids_x_comps = [coeffs_dfs[fid].values for fid in notes_dfs]
|
||||
coeffs_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
|
||||
|
||||
return notes_etudids_x_comps_x_sxtag, coeffs_etudids_x_comps_x_sxtag
|
||||
|
||||
def do_taglist(self):
|
||||
"""Synthétise les tags à partir des semestres (taggués) aggrégés
|
||||
"""Synthétise les tags à partir des Sxtags aggrégés
|
||||
|
||||
Returns:
|
||||
Une liste de tags triés par ordre alphabétique
|
||||
"""
|
||||
tags = []
|
||||
for frmsem_id in self.res_sems_tags:
|
||||
tags.extend(self.res_sems_tags[frmsem_id].tags_sorted)
|
||||
for frmsem_id in self.sxstags:
|
||||
tags.extend(self.sxstags[frmsem_id].tags_sorted)
|
||||
pe_affichage.pe_print(f"* Tags : {', '.join(tags)}")
|
||||
return sorted(set(tags))
|
||||
|
||||
def do_complist(self):
|
||||
"""Synthétise les compétences à partir des Sxtags aggrégés"""
|
||||
competences = []
|
||||
for sxtag_id, sxtag in self.sxstags:
|
||||
comp = sxtag.moyennes_tags["but"].competences
|
||||
competences.extend(comp)
|
||||
return sorted(set(competences))
|
||||
|
||||
|
||||
def compute_notes_competences(
|
||||
set_cube: np.array, coeff_cube: np.array, etudids: list, competences: list
|
||||
):
|
||||
"""Calcule:
|
||||
* la moyenne par compétences à un tag donné sur plusieurs semestres (partant du set_cube).
|
||||
* la somme des coeffs à utiliser pour la moyenne générale.
|
||||
|
||||
def compute_tag_moy(set_cube: np.array, etudids: list, tags: list):
|
||||
"""Calcul de la moyenne par tag sur plusieurs semestres.
|
||||
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles
|
||||
|
||||
*Remarque* : Adaptation de moy_ue.compute_ue_moys_apc au cas des moyennes de tag
|
||||
@ -187,34 +230,41 @@ def compute_tag_moy(set_cube: np.array, etudids: list, tags: list):
|
||||
|
||||
Args:
|
||||
set_cube: notes moyennes aux modules ndarray
|
||||
(etuds x modimpls x UEs), des floats avec des NaN
|
||||
(etuds x UEs|compétences x sxtags), des floats avec des NaN
|
||||
coeffs_cube: somme des coeffs impliqués dans la moyennes
|
||||
etudids: liste des étudiants (dim. 0 du cube)
|
||||
competences: list
|
||||
tags: liste des tags (dim. 1 du cube)
|
||||
Returns:
|
||||
Un DataFrame avec pour columns les moyennes par tags,
|
||||
et pour rows les etudid
|
||||
"""
|
||||
nb_etuds, nb_tags, nb_semestres = set_cube.shape
|
||||
nb_etuds, nb_comps, nb_semestres = set_cube.shape
|
||||
assert nb_etuds == len(etudids)
|
||||
assert nb_tags == len(tags)
|
||||
assert nb_comps == len(competences)
|
||||
|
||||
# Quelles entrées du cube contiennent des notes ?
|
||||
mask = ~np.isnan(set_cube)
|
||||
|
||||
# Enlève les NaN du cube pour les entrées manquantes
|
||||
# Enlève les NaN du cube de notes pour les entrées manquantes
|
||||
set_cube_no_nan = np.nan_to_num(set_cube, nan=0.0)
|
||||
coeffs_cube_no_nan = no.nan_to_num(coeff_cube, nan=0.0)
|
||||
|
||||
# Les moyennes par tag
|
||||
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
||||
etud_moy_tag = np.sum(set_cube_no_nan, axis=2) / np.sum(mask, axis=2)
|
||||
# La somme des coeffs
|
||||
coeff_tag = np.sum(coeffs_cube_no_nan, axis=2)
|
||||
|
||||
# Le dataFrame
|
||||
# Le dataFrame des notes moyennes
|
||||
etud_moy_tag_df = pd.DataFrame(
|
||||
etud_moy_tag,
|
||||
index=etudids, # les etudids
|
||||
columns=tags, # les tags
|
||||
columns=competences, # les competences
|
||||
)
|
||||
|
||||
etud_moy_tag_df.fillna(np.nan)
|
||||
|
||||
return etud_moy_tag_df
|
||||
coeffs_df = pd.DataFrame(coeff_tag, index=etudids, columns=competences)
|
||||
coeffs_df.fillna(np.nan)
|
||||
|
||||
return etud_moy_tag_df, coeffs_df
|
||||
|
@ -85,11 +85,25 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||
ues_hors_sport = [ue for ue in self.ues if ue.type != UE_SPORT]
|
||||
self.ues_inscr_parcours_df = self.load_ues_inscr_parcours()
|
||||
|
||||
# Les compétences associées aux UEs
|
||||
self.competences = {}
|
||||
for ue in self.ues:
|
||||
if ue.type != UE_SPORT:
|
||||
assert ue.niveau_competence, ScoValueError("Des UEs ne sont pas rattachées à des compétences")
|
||||
nom = ue.niveau_competence.competence.titre
|
||||
self.competences[ue.ue_id] = nom
|
||||
|
||||
# Les tags personnalisés et auto:
|
||||
tags_dict = self._get_tags_dict()
|
||||
self._check_tags(tags_dict)
|
||||
# self.tags = [tag for cat in dict_tags for tag in dict_tags[cat]]
|
||||
|
||||
# Les coefficients
|
||||
matrice_coeffs = self.ues_inscr_parcours_df * [
|
||||
ue.ects
|
||||
for ue in ues_hors_sport # if ue.type != UE_SPORT <= déjà supprimé
|
||||
]
|
||||
|
||||
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
|
||||
self.moyennes_tags = {}
|
||||
|
||||
@ -99,13 +113,10 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||
moy_ues_tag = self.compute_moy_ues_tag(infos_tag)
|
||||
# moy_gen_tag = self.compute_moy_gen_tag(moy_ues_tag)
|
||||
|
||||
ues_dict = {ue.id: ue for ue in ues_hors_sport}
|
||||
self.moyennes_tags[tag] = MoyennesTag(
|
||||
tag,
|
||||
ues_dict,
|
||||
moy_ues_tag,
|
||||
self.ues_inscr_parcours_df
|
||||
# moy_gen_tag
|
||||
matrice_coeffs
|
||||
)
|
||||
|
||||
# Ajoute les d'UE moyennes générales de BUT pour le semestre considéré
|
||||
@ -117,12 +128,8 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||
{ue.id: self.etud_moy_ue[ue.id] for ue in ues_hors_sport},
|
||||
index=self.etudids,
|
||||
)
|
||||
# moy_ues = self.nt.etud_moy_ue[ue_id]
|
||||
# moy_gen_but = self.nt.etud_moy_gen
|
||||
ues_dict = {ue.id: ue for ue in ues_hors_sport}
|
||||
|
||||
self.moyennes_tags["but"] = MoyennesTag(
|
||||
"but", ues_dict, df_ues, self.ues_inscr_parcours_df # , moy_gen_but
|
||||
"but", df_ues, matrice_coeffs # , moy_gen_but
|
||||
)
|
||||
|
||||
self.tags_sorted = self.get_all_tags()
|
||||
@ -183,6 +190,13 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||
self.dispense_ues,
|
||||
block=self.formsemestre.block_moyennes,
|
||||
)
|
||||
|
||||
# Transforme les UEs en acronyme
|
||||
colonnes = moyennes_ues_tag.columns
|
||||
ue_to_acro = {ue.id: ue.acronyme for ue in self.ues}
|
||||
acronymes = [ue_to_acro[col] for col in colonnes]
|
||||
moyennes_ues_tag.columns = acronymes
|
||||
|
||||
return moyennes_ues_tag
|
||||
|
||||
def _get_tags_dict(self):
|
||||
@ -299,27 +313,3 @@ def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
|
||||
|
||||
return synthese_tags
|
||||
|
||||
|
||||
def get_noms_competences_from_ues(formsemestre: FormSemestre) -> dict[int, str]:
|
||||
"""Partant d'un formsemestre, extrait le nom des compétences associés
|
||||
à (ou aux) parcours des étudiants du formsemestre.
|
||||
|
||||
Ignore les UEs non associées à un niveau de compétence.
|
||||
|
||||
Args:
|
||||
formsemestre: Un FormSemestre
|
||||
|
||||
Returns:
|
||||
Dictionnaire {ue_id: nom_competence} lisant tous les noms des compétences
|
||||
en les raccrochant à leur ue
|
||||
"""
|
||||
# Les résultats du semestre
|
||||
nt = load_formsemestre_results(formsemestre)
|
||||
|
||||
noms_competences = {}
|
||||
for ue in nt.ues:
|
||||
if ue.niveau_competence and ue.type != UE_SPORT:
|
||||
# ?? inutilisé ordre = ue.niveau_competence.ordre
|
||||
nom = ue.niveau_competence.competence.titre
|
||||
noms_competences[ue.ue_id] = f"comp. {nom}"
|
||||
return noms_competences
|
||||
|
@ -50,7 +50,7 @@ from app.pe.pe_moytag import MoyennesTag
|
||||
class SxTag(TableTag):
|
||||
def __init__(self, sxtag_id: (int, int), ressembuttags: dict[int, ResSemBUTTag]):
|
||||
"""Calcule les moyennes/classements par tag d'un semestre de type 'Sx'
|
||||
(par ex. 'S1', 'S2', ...) :
|
||||
(par ex. 'S1', 'S2', ...) avec une orientation par UE :
|
||||
|
||||
* pour les étudiants non redoublants, ce sont les moyennes/classements
|
||||
du semestre suivi
|
||||
@ -91,6 +91,7 @@ class SxTag(TableTag):
|
||||
self.etuds = ressembuttags[self.fid_final].etuds
|
||||
self.add_etuds(self.etuds)
|
||||
|
||||
# Affichage
|
||||
pe_affichage.pe_print(f"--> {self.get_repr()}")
|
||||
|
||||
# Les tags
|
||||
@ -99,38 +100,53 @@ class SxTag(TableTag):
|
||||
pe_affichage.pe_print(f"* Tags : {', '.join(self.tags_sorted)}")
|
||||
|
||||
# Les UE
|
||||
self.ues = self.ressembuttag_final.moyennes_tags["but"].ues
|
||||
moy_sem_final = self.ressembuttag_final.moyennes_tags["but"]
|
||||
self.ues = list(moy_sem_final.matrice_notes.columns)
|
||||
|
||||
# Les acronymes des UE
|
||||
self.acronymes_ues_sorted = sorted([ue.acronyme for ue in self.ues.values()])
|
||||
self.acronymes_ues_sorted = sorted(self.ues)
|
||||
|
||||
# Les inscriptions des étudiants aux UEs
|
||||
# => ne conserve que les UEs du semestre final (pour les redoublants)
|
||||
self.ues_inscr_parcours_df = self.ressembuttag_final.moyennes_tags[
|
||||
self.matrice_coeffs = self.ressembuttag_final.moyennes_tags[
|
||||
"but"
|
||||
].ues_inscr_parcours_df
|
||||
].matrice_coeffs
|
||||
self.ues_inscr_parcours = ~np.isnan(self.matrice_coeffs.to_numpy())
|
||||
|
||||
|
||||
# Les moyennes par tag
|
||||
self.moyennes_tags: dict[str, pd.DataFrame] = {}
|
||||
"""Les notes aux UEs dans différents tags"""
|
||||
|
||||
# Masque des inscriptions
|
||||
inscr_mask = self.ues_inscr_parcours_df.to_numpy()
|
||||
inscr_mask = self.ues_inscr_parcours
|
||||
for tag in self.tags_sorted:
|
||||
# Cube de note
|
||||
# Cube de note etudids x UEs
|
||||
notes_cube = self.compute_notes_ues_cube(tag, self.acronymes_ues_sorted)
|
||||
|
||||
# Calcule des moyennes sous forme d'un dataframe"""
|
||||
moys_ues = compute_notes_ues(
|
||||
matrice_moys_ues = compute_notes_ues(
|
||||
notes_cube,
|
||||
self.etudids,
|
||||
self.acronymes_ues_sorted,
|
||||
inscr_mask,
|
||||
)
|
||||
|
||||
# Les profils d'ects (pour debug)
|
||||
profils_ects = []
|
||||
for i in self.matrice_coeffs.index:
|
||||
val = tuple(self.matrice_coeffs.loc[i].fillna("x"))
|
||||
if tuple(val) not in profils_ects:
|
||||
profils_ects.append(tuple(val))
|
||||
|
||||
# Les moyennes
|
||||
self.moyennes_tags[tag] = MoyennesTag(
|
||||
tag, self.ues, moys_ues, self.ues_inscr_parcours_df
|
||||
)
|
||||
self.moyennes_tags[tag] = MoyennesTag(tag,
|
||||
matrice_moys_ues,
|
||||
self.matrice_coeffs)
|
||||
|
||||
pe_affichage.pe_print(f"> MoyTag pour {tag} avec")
|
||||
pe_affichage.pe_print(f" - ues={self.acronymes_ues_sorted}")
|
||||
pe_affichage.pe_print(f" - ects={profils_ects}")
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Egalité de 2 SxTag sur la base de leur identifiant"""
|
||||
@ -140,7 +156,7 @@ class SxTag(TableTag):
|
||||
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
||||
est basée)"""
|
||||
affichage = [str(fid) for fid in self.ressembuttags]
|
||||
return f"S{self.sxtag_id[0]}Tag ({'+'.join(affichage)})"
|
||||
return f"{self.sxtag_id[0]}Tag ({'+'.join(affichage)})"
|
||||
|
||||
def compute_notes_ues_cube(self, tag, acronymes_ues_sorted):
|
||||
"""Construit le cube de notes des UEs (etudid x accronyme_ue x semestre_aggregé)
|
||||
@ -160,7 +176,7 @@ class SxTag(TableTag):
|
||||
# Charge les notes du semestre tag
|
||||
sem_tag = self.ressembuttags[frmsem_id]
|
||||
moys_tag = sem_tag.moyennes_tags[tag]
|
||||
notes = moys_tag.notes_ues # dataframe etudids x ues
|
||||
notes = moys_tag.matrice_notes # dataframe etudids x ues
|
||||
acronymes_ues_sem = list(
|
||||
notes.columns
|
||||
) # les acronymes des UEs du semestre tag
|
||||
@ -183,7 +199,7 @@ class SxTag(TableTag):
|
||||
# Stocke le df
|
||||
dfs[frmsem_id] = df
|
||||
|
||||
"""Réunit les notes sous forme d'un cube semestres x etdids x ues"""
|
||||
"""Réunit les notes sous forme d'un cube etudids x ues x semestres"""
|
||||
semestres_x_etudids_x_ues = [dfs[fid].values for fid in dfs]
|
||||
etudids_x_ues_x_semestres = np.stack(semestres_x_etudids_x_ues, axis=-1)
|
||||
return etudids_x_ues_x_semestres
|
||||
|
@ -94,13 +94,13 @@ class TableTag(object):
|
||||
tags_tries = self.get_all_tags()
|
||||
for tag in tags_tries:
|
||||
moy_tag = self.moyennes_tags[tag]
|
||||
for acronyme in moy_tag.moys_ues:
|
||||
moy = moy_tag.moys_ues[acronyme] # une moyenne
|
||||
for acronyme in moy_tag.moyennes:
|
||||
moy = moy_tag.moyennes[acronyme] # une moyenne
|
||||
df = df.join(moy.synthese["notes"].rename(f"Moy {tag}-{acronyme}"))
|
||||
df = df.join(
|
||||
moy.synthese["classements"].rename(f"Class {tag}-{acronyme}")
|
||||
)
|
||||
moy_gen = moy_tag.moy_gen
|
||||
moy_gen = moy_tag.moyenne_gen
|
||||
df = df.join(moy_gen.synthese["notes"].rename(f"Moy {tag} (gen)"))
|
||||
df = df.join(moy_gen.synthese["classements"].rename(f"Class {tag} (gen)"))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user