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)
|
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
|
"""Association entre chaque étudiant et la trajectoire tagguée à prendre en
|
||||||
compte pour l'aggrégat"""
|
compte pour l'aggrégat"""
|
||||||
for etudid in self.diplomes_ids:
|
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()
|
self.tags_sorted = self.do_taglist()
|
||||||
"""Liste des tags (triés par ordre alphabétique)"""
|
"""Liste des tags (triés par ordre alphabétique)"""
|
||||||
|
@ -94,6 +94,9 @@ class JuryPE(object):
|
|||||||
self.etudiants.find_etudiants()
|
self.etudiants.find_etudiants()
|
||||||
self.diplomes_ids = self.etudiants.diplomes_ids
|
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()
|
self.zipdata = io.BytesIO()
|
||||||
with ZipFile(self.zipdata, "w") as zipfile:
|
with ZipFile(self.zipdata, "w") as zipfile:
|
||||||
if not self.diplomes_ids:
|
if not self.diplomes_ids:
|
||||||
@ -102,7 +105,9 @@ class JuryPE(object):
|
|||||||
self._gen_xls_diplomes(zipfile)
|
self._gen_xls_diplomes(zipfile)
|
||||||
|
|
||||||
self._gen_xls_ressembuttags(zipfile)
|
self._gen_xls_ressembuttags(zipfile)
|
||||||
|
self._gen_rcss()
|
||||||
self._gen_xls_sxtags(zipfile)
|
self._gen_xls_sxtags(zipfile)
|
||||||
|
# self._gen_rcrcfs()
|
||||||
# self._gen_xls_rcss_tags(zipfile)
|
# self._gen_xls_rcss_tags(zipfile)
|
||||||
# self._gen_xls_interclassements_rcss(zipfile)
|
# self._gen_xls_interclassements_rcss(zipfile)
|
||||||
# self._gen_xls_synthese_jury_par_tag(zipfile)
|
# self._gen_xls_synthese_jury_par_tag(zipfile)
|
||||||
@ -173,41 +178,36 @@ class JuryPE(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _gen_rcss(self):
|
def _gen_rcss(self):
|
||||||
"""Génère les RCS (attribut `rcss_jury`), combinaisons de semestres suivis par les étudiants au sens
|
"""Génère les RCS (attribut `rcss_jury`), combinaisons de semestres
|
||||||
d'un nom de RCS (par ex: 'S2' ou '3S').
|
suivis par les étudiants au sens d'un nom de RCS (par ex: 'S2' ou '3S').
|
||||||
"""
|
"""
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
"*** Génère les RCS (différentes combinaisons de semestres) des étudiants"
|
"*** 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_rcss(self.etudiants)
|
||||||
|
self.rcss_jury.cree_rcfs(self.etudiants)
|
||||||
|
|
||||||
def _gen_xls_sxtags(self, zipfile: ZipFile):
|
def _gen_xls_sxtags(self, zipfile: ZipFile):
|
||||||
"""Génère les semestres taggués en s'appuyant sur les RCS de type Sx (pour
|
"""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).
|
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
|
# Génère les moyennes des RCS de type Sx
|
||||||
pe_affichage.pe_print("*** Calcule les moyennes des SxTag")
|
pe_affichage.pe_print("*** Calcule les moyennes des SxTag")
|
||||||
|
|
||||||
# Les regroupements de Sx
|
# Les SxTag (moyenne de Sx par UE)
|
||||||
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
|
|
||||||
self.sxtags = {}
|
self.sxtags = {}
|
||||||
for rang in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT + 1):
|
for rcf_id, rcf in self.rcss_jury.rcfs.items():
|
||||||
for sxtag_id in self.regroupements[rang]:
|
# SxTag traduisant le RCF
|
||||||
trajectoires = self.regroupements[rang][sxtag_id]
|
sxtag_id = rcf_id
|
||||||
ressemstags = {fid: self.ressembuttags[fid] for fid in trajectoires}
|
# 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)
|
self.sxtags[sxtag_id] = pe_sxtag.SxTag(sxtag_id, ressemstags)
|
||||||
|
|
||||||
# Intègre le bilan des semestres taggués au zip final
|
# Intègre le bilan des semestres taggués au zip final
|
||||||
@ -215,9 +215,9 @@ class JuryPE(object):
|
|||||||
with pd.ExcelWriter( # pylint: disable=abstract-class-instantiated
|
with pd.ExcelWriter( # pylint: disable=abstract-class-instantiated
|
||||||
output, engine="openpyxl"
|
output, engine="openpyxl"
|
||||||
) as writer:
|
) as writer:
|
||||||
for sem_tag in self.sxtags.values():
|
for sxtag in self.sxtags.values():
|
||||||
onglet = sem_tag.get_repr(verbose=False)
|
onglet = sxtag.get_repr(verbose=False)
|
||||||
df = sem_tag.df_moyennes_et_classements()
|
df = sxtag.df_moyennes_et_classements()
|
||||||
# écriture dans l'onglet
|
# écriture dans l'onglet
|
||||||
df.to_excel(writer, onglet, index=True, header=True)
|
df.to_excel(writer, onglet, index=True, header=True)
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
@ -229,9 +229,16 @@ class JuryPE(object):
|
|||||||
path="details",
|
path="details",
|
||||||
)
|
)
|
||||||
|
|
||||||
def _gen_xls_rcss_tags(self, zipfile: ZipFile):
|
def _gen_rcrcfs(self):
|
||||||
"""Génère les RCS taggués (autres que ceux de type Sx), etc...
|
"""Génère les regroupements cohérents de RCFs qu'ont suivi chaque étudiant"""
|
||||||
en calculant les moyennes et les classements par tag pour chaque RCS.
|
|
||||||
|
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
|
Stocke le résultat dans self.rccs_tag, un dictionnaire de
|
||||||
la forme ``{nom_aggregat: {fid_terminal: RCSTag(fid_terminal)} }``
|
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
|
# 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 = {}
|
self.rcss_tags = {}
|
||||||
for rcs_id, rcs in self.rcss_jury.rcss.items():
|
for rcs_id, rcrcf in self.rcss_jury.rcrcfs.items():
|
||||||
# nom = rcs.get_repr()
|
self.rcss_tags[rcs_id] = RCSTag(rcrcf, self.sxtags)
|
||||||
self.rcss_tags[rcs_id] = RCSTag(rcs, self.res_sems_tags)
|
|
||||||
|
|
||||||
# Intègre le bilan des trajectoires tagguées au zip final
|
# Intègre le bilan des trajectoires tagguées au zip final
|
||||||
output = io.BytesIO()
|
output = io.BytesIO()
|
||||||
@ -431,7 +437,7 @@ class JuryPE(object):
|
|||||||
# considéré
|
# considéré
|
||||||
trajectoires_tagguees = []
|
trajectoires_tagguees = []
|
||||||
for etudid in etudids:
|
for etudid in etudids:
|
||||||
trajectoire = self.rcss_jury.suivi[etudid][aggregat]
|
trajectoire = self.rcss_jury.rcss_suivis[etudid][aggregat]
|
||||||
if trajectoire:
|
if trajectoire:
|
||||||
tid = trajectoire.sxtag_id
|
tid = trajectoire.sxtag_id
|
||||||
trajectoire_tagguee = self.rcss_tags[tid]
|
trajectoire_tagguee = self.rcss_tags[tid]
|
||||||
@ -577,7 +583,7 @@ class JuryPE(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# La trajectoire de l'étudiant sur l'aggrégat
|
# 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:
|
if trajectoire:
|
||||||
trajectoire_tagguee = self.rcss_tags[trajectoire.sxtag_id]
|
trajectoire_tagguee = self.rcss_tags[trajectoire.sxtag_id]
|
||||||
if tag in trajectoire_tagguee.moyennes_tags:
|
if tag in trajectoire_tagguee.moyennes_tags:
|
||||||
|
@ -155,101 +155,74 @@ class Moyenne:
|
|||||||
return self.synthese["nb_inscrits"] > 0
|
return self.synthese["nb_inscrits"] > 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MoyennesTag:
|
class MoyennesTag:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
tag: str,
|
tag: str,
|
||||||
ues: dict[int, UniteEns],
|
matrice_notes: pd.DataFrame, # etudids x colonnes
|
||||||
notes_ues: pd.DataFrame,
|
matrice_coeffs: pd.DataFrame # etudids x colonnes
|
||||||
ues_inscr_parcours_df: pd.DataFrame
|
|
||||||
# notes_gen: pd.Series,
|
|
||||||
):
|
):
|
||||||
"""Classe centralisant la synthèse des moyennes/classements d'une série
|
"""Classe centralisant la synthèse des moyennes/classements d'une série
|
||||||
d'étudiants à un tag donné, en différenciant les notes
|
d'étudiants à un tag donné, en différenciant les notes
|
||||||
obtenues aux UE et au général (toutes UEs confondues)
|
obtenues aux UE et au général (toutes UEs confondues)
|
||||||
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tag: Un tag
|
tag: Un tag
|
||||||
ues: La liste des UEs ayant servie au calcul de la moyenne
|
matrice_notes: Les moyennes (etudid x acronymes_ues ou etudid x compétences) aux différentes UEs ou compétences
|
||||||
notes_ues: Les moyennes (etudid x acronymes_ues) aux différentes UEs et pour le tag
|
matrice_coeffs: Les coeff à appliquer pour le calcul de la moyenne générale
|
||||||
ues_inscr_parcours_df: Les inscriptions des etudid au UE
|
|
||||||
# notes_gen: Une série de notes (moyenne) sous forme d'un pd.Series() (toutes UEs confondues)
|
# notes_gen: Une série de notes (moyenne) sous forme d'un pd.Series() (toutes UEs confondues)
|
||||||
"""
|
"""
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
"""Le tag associé aux moyennes"""
|
"""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
|
# Les moyennes par UE
|
||||||
self.notes_ues: pd.DataFrame = notes_ues
|
self.matrice_notes: pd.DataFrame = matrice_notes
|
||||||
"""Les notes aux UEs (dataframe)"""
|
"""Les notes aux UEs ou aux compétences (DataFrame)"""
|
||||||
self.notes_ues.columns = acronymes # remplace les ue.id par leur acronyme
|
|
||||||
self.moys_ues: dict[int, pd.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"""
|
"""Les dataframes retraçant les moyennes/classements/statistiques des étudiants aux UEs"""
|
||||||
for ue in self.ues.values(): # if ue.type != UE_SPORT:
|
colonnes = self.matrice_notes.columns
|
||||||
notes = notes_ues[ue.acronyme]
|
for col in colonnes: # if ue.type != UE_SPORT:
|
||||||
self.moys_ues[ue.acronyme] = Moyenne(notes)
|
notes = matrice_notes[col]
|
||||||
|
self.moyennes[col] = Moyenne(notes)
|
||||||
|
|
||||||
# Les moyennes générales
|
# 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
|
self.notes_gen = notes_gen
|
||||||
"""Les notes générales (moyenne toutes UEs confonudes)"""
|
"""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"""
|
"""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):
|
def compute_moy_gen(self, moys: pd.DataFrame, coeffs: pd.DataFrame) -> pd.Series:
|
||||||
"""Egalité de deux MoyenneTag lorsque leur tag sont identiques"""
|
"""Calcule la moyenne générale (toutes UE/compétences confondus)
|
||||||
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)
|
|
||||||
pour le tag considéré, en pondérant les notes obtenues au UE
|
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:
|
Args:
|
||||||
moy_ues: Les moyennes etudids x acronymes_ues
|
moys: Les moyennes etudids x acronymes_ues/compétences
|
||||||
coeff_ues: Les coeff etudids x ueids
|
coeff: Les coeff etudids x ueids/compétences
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Calcule la moyenne générale dans le semestre (pondérée par le ECTS)
|
# Calcule la moyenne générale dans le semestre (pondérée par le ECTS)
|
||||||
try:
|
try:
|
||||||
moy_gen_tag = comp.moy_sem.compute_sem_moys_apc_using_ects(
|
moy_gen_tag = comp.moy_sem.compute_sem_moys_apc_using_ects(
|
||||||
moy_ues,
|
moys,
|
||||||
coeff_ues,
|
coeffs.fillna(0.0),
|
||||||
# formation_id=self.formsemestre.formation_id,
|
# formation_id=self.formsemestre.formation_id,
|
||||||
skip_empty_ues=True,
|
skip_empty_ues=True,
|
||||||
)
|
)
|
||||||
except TypeError as e:
|
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
|
return moy_gen_tag
|
||||||
|
|
||||||
|
330
app/pe/pe_rcs.py
330
app/pe/pe_rcs.py
@ -12,7 +12,7 @@ Created on 01-2024
|
|||||||
import app.pe.pe_comp as pe_comp
|
import app.pe.pe_comp as pe_comp
|
||||||
|
|
||||||
from app.models import FormSemestre
|
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
|
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
|
||||||
|
|
||||||
TYPES_RCS = {
|
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")]
|
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:
|
class RCS:
|
||||||
"""Modélise un ensemble de semestres finals d'étudiants
|
"""Modélise un regroupement cohérent de semestres (formsemestre ou de Sx)"""
|
||||||
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
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||||
self.nom: str = nom_rcs
|
self.nom: str = nom_rcs
|
||||||
@ -110,42 +96,155 @@ class RCS:
|
|||||||
self.formsemestre_final: FormSemestre = semestre_final
|
self.formsemestre_final: FormSemestre = semestre_final
|
||||||
"""FormSemestre terminal du RCS"""
|
"""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)
|
self.rcs_id: (str, int) = (nom_rcs, semestre_final.formsemestre_id)
|
||||||
"""Identifiant du RCS sous forme (nom_rcs, id du semestre_terminal)"""
|
"""Identifiant du RCS sous forme (nom_rcs, id du semestre_terminal)"""
|
||||||
|
|
||||||
# self.semestres_aggreges: dict[int:FormSemestre] = {}
|
def get_formsemestre_id_final(self) -> int:
|
||||||
# """Semestres regroupés dans le RCS"""
|
"""Renvoie l'identifiant du formsemestre final du RCS
|
||||||
|
|
||||||
self.sxtags_aggreges: dict[(str, int): pe_sxtag.SxTag] = {}
|
Returns:
|
||||||
"""Les SxTag aggrégés"""
|
L'id du formsemestre final (marquant la fin) du RCS
|
||||||
|
"""
|
||||||
def get_formsemestre_id_final(self):
|
|
||||||
"""Renvoie l'identifiant du formsemestre final du RCS"""
|
|
||||||
return self.formsemestre_final.formsemestre_id
|
return self.formsemestre_final.formsemestre_id
|
||||||
|
|
||||||
def add_sxtags_a_aggreger(self, sxtags: dict[(str,int): pe_sxtag.SxTag]):
|
def __repr__(self):
|
||||||
"""Ajout des SxTag aux semestres à regrouper
|
"""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:
|
Args:
|
||||||
sxtags: Dictionnaire ``{(str,fid): SxTag}`` à ajouter
|
nom_rcs: Un nom du RCS (par ex: '5S')
|
||||||
|
semestre_final: Le formsemestre final du RCS
|
||||||
"""
|
"""
|
||||||
self.sxtags_aggreges = self.sxtags_aggreges | sxtags
|
|
||||||
|
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:
|
||||||
|
semestres: Dictionnaire ``{fid: Formsemestre)``
|
||||||
|
"""
|
||||||
|
self.semestres_aggreges = self.semestres_aggreges | semestres
|
||||||
|
|
||||||
def get_repr(self, verbose=True) -> str:
|
def get_repr(self, verbose=True) -> str:
|
||||||
"""Représentation textuelle d'un RCS
|
"""Représentation textuelle d'un RCS
|
||||||
basé sur ses sxtags aggrégés"""
|
basé sur ses semestres aggrégés"""
|
||||||
|
|
||||||
noms = []
|
noms = []
|
||||||
for sxtag_id, sxtag in self.sxtags_aggreges.items():
|
for fid in self.semestres_aggreges:
|
||||||
noms.append(f"S{sxtag.semestre_id}")
|
semestre = self.semestres_aggreges[fid]
|
||||||
|
noms.append(f"S{semestre.semestre_id}({fid})")
|
||||||
noms = sorted(noms)
|
noms = sorted(noms)
|
||||||
title = f"""{self.nom} ({
|
title = f"""{str(self)}"""
|
||||||
self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"""
|
|
||||||
if verbose and noms:
|
if verbose and noms:
|
||||||
title += " - " + "+".join(noms)
|
title += " - " + "+".join(noms)
|
||||||
return title
|
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:
|
class RCSsJuryPE:
|
||||||
"""Classe centralisant tous les regroupements cohérents de
|
"""Classe centralisant tous les regroupements cohérents de
|
||||||
semestres (RCS) des étudiants à prendre en compte dans un jury PE
|
semestres (RCS) des étudiants à prendre en compte dans un jury PE
|
||||||
@ -158,78 +257,151 @@ class RCSsJuryPE:
|
|||||||
self.annee_diplome = annee_diplome
|
self.annee_diplome = annee_diplome
|
||||||
"""Année de diplômation"""
|
"""Année de diplômation"""
|
||||||
|
|
||||||
self.rcss: dict[tuple:RCS] = {}
|
self.rcss: dict[tuple(int, str) : RCF] = {}
|
||||||
"""Ensemble des RCS recensés : {(nom_RCS, fid_terminal): RCS}"""
|
"""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,
|
"""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] = {}
|
||||||
"""Créé tous les RCS, au regard du cursus des étudiants
|
"""Ensemble des RCF recensés : {(nom_RCS, fid_terminal): RCF}"""
|
||||||
analysés et des SxTag calculés.
|
|
||||||
|
|
||||||
Les mémorise dans les données de chaque étudiant.
|
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 + les mémorise dans les données de l'étudiant
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
etudiants: Les étudiants à prendre en compte dans le Jury PE
|
etudiants: Les étudiants à prendre en compte dans le Jury PE
|
||||||
pe_sxtag: Les Sx taggués
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
for etudid in etudiants.trajectoires:
|
for etudid in etudiants.trajectoires:
|
||||||
if etudid not in self.suivi:
|
self.rcss_suivis[etudid] = {
|
||||||
self.suivi[etudid] = {
|
|
||||||
aggregat: None
|
aggregat: None
|
||||||
for aggregat in pe_comp.TOUS_LES_SEMESTRES
|
for aggregat in pe_comp.TOUS_LES_SEMESTRES
|
||||||
+ TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
|
+ 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"]
|
||||||
|
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
|
||||||
|
|
||||||
|
for etudid in etudiants.trajectoires:
|
||||||
# Le formsemestre terminal (dernier en date) associé au
|
# Le formsemestre terminal (dernier en date) associé au
|
||||||
# semestre marquant la fin de l'aggrégat
|
# semestre marquant la fin de l'aggrégat
|
||||||
# (par ex: son dernier S3 en date)
|
# (par ex: son dernier S3 en date)
|
||||||
semestres = etudiants.trajectoires[etudid][nom_semestre_terminal]
|
trajectoire = etudiants.trajectoires[etudid][nom_semestre_terminal]
|
||||||
if semestres:
|
if trajectoire:
|
||||||
formsemestre_final = get_dernier_semestre_en_date(semestres)
|
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)
|
rcs_id = (nom_rcs, formsemestre_final.formsemestre_id)
|
||||||
if rcs_id not in self.rcss:
|
if rcs_id not in self.rcss:
|
||||||
rcs = RCS(nom_rcs, formsemestre_final)
|
self.rcss[rcs_id] = RCF(nom_rcs, formsemestre_final)
|
||||||
self.rcss[rcs_id] = rcs
|
|
||||||
else:
|
|
||||||
rcs = self.rcss[rcs_id]
|
rcs = self.rcss[rcs_id]
|
||||||
|
|
||||||
# La liste des semestres de l'étudiant à prendre en compte
|
# La liste des semestres de l'étudiant à prendre en compte
|
||||||
# pour cette trajectoire
|
# pour cette trajectoire
|
||||||
semestres_a_aggreger = get_trajectoire_etudiant(
|
semestres_a_aggreger = get_rcs_etudiant(
|
||||||
etudiants.trajectoires[etudid], formsemestre_final, nom_rcs
|
etudiants.trajectoires[etudid], formsemestre_final, nom_rcs
|
||||||
)
|
)
|
||||||
|
|
||||||
# Extrait les sxtags correspondants aux semestres à aggréger
|
# Ajout des semestres au RCS
|
||||||
# (par ex. des 2 semestres S1(18)+S1(26) récupère le sxtag S1(26)
|
rcs.add_semestres_a_aggreger(semestres_a_aggreger)
|
||||||
sxtags_a_aggreger = {}
|
|
||||||
|
# Mémorise le RCS suivi par l'étudiant
|
||||||
|
self.rcss_suivis[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)
|
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 à la trajectoire
|
# Récupére les RCFs de type Sx traduisant sa trajectoire
|
||||||
rcs.add_sxtags_a_aggreger(sxtags_a_aggreger)
|
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]
|
||||||
|
|
||||||
# Mémoire la trajectoire suivie par l'étudiant
|
# Ajout du RCRCF
|
||||||
self.suivi[etudid][nom_rcs] = rcs
|
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
|
semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str
|
||||||
) -> dict[int, FormSemestre]:
|
) -> dict[int, FormSemestre]:
|
||||||
"""Ensemble des semestres parcourus (trajectoire)
|
"""Ensemble des semestres parcourus (trajectoire)
|
||||||
@ -273,9 +445,3 @@ def get_trajectoire_etudiant(
|
|||||||
):
|
):
|
||||||
semestres_aggreges[fid] = semestre
|
semestres_aggreges[fid] = semestre
|
||||||
return semestres_aggreges
|
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
|
from app.pe.pe_ressemtag import ResSemBUTTag
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
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_tabletags import TableTag
|
||||||
from app.pe.pe_moytag import MoyennesTag
|
from app.pe.pe_moytag import MoyennesTag
|
||||||
|
|
||||||
|
|
||||||
class RCSTag(TableTag):
|
class RCSTag(TableTag):
|
||||||
|
def __init__(self, rcrcf: RCS, sxstags: dict[(str, int): SxTag]):
|
||||||
def __init__(
|
"""Calcule les moyennes par tag (orientées compétences)
|
||||||
self, rcs: RCS, semestres_taggues: dict[int, ResSemBUTTag]
|
d'un regroupement de SxTag
|
||||||
):
|
(RCRCF), pour extraire les classements par tag pour un
|
||||||
"""Calcule les moyennes par tag d'une combinaison de semestres
|
|
||||||
(RCS), pour extraire les classements par tag pour un
|
|
||||||
groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous
|
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:
|
Args:
|
||||||
rcs: Un RCS (identifié par un nom et l'id de son semestre terminal)
|
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)
|
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)"""
|
"""Identifiant du RCS taggué (identique au RCS sur lequel il s'appuie)"""
|
||||||
|
|
||||||
self.rcs: RCS = rcs
|
self.rcrcf: RCRCF = rcrcf
|
||||||
"""RCS associé au RCS taggué"""
|
"""RCRCF associé au RCS taggué"""
|
||||||
|
|
||||||
self.nom = self.get_repr()
|
self.nom = self.get_repr()
|
||||||
"""Représentation textuelle du RCS taggué"""
|
"""Représentation textuelle du RCS taggué"""
|
||||||
|
|
||||||
self.formsemestre_terminal = rcs.formsemestre_final
|
self.formsemestre_terminal = rcrcf.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)
|
||||||
|
|
||||||
self.semestres_aggreges = rcs.semestres_aggreges
|
self.rcfs_aggreges = rcrcf.rcfs_aggreges
|
||||||
"""Les semestres aggrégés"""
|
"""Les RCFs aggrégés"""
|
||||||
|
|
||||||
self.res_sems_tags = {}
|
self.sxstags = {}
|
||||||
"""Les semestres tags associés aux semestres aggrégés"""
|
"""Les SxTag associés aux RCF aggrégés"""
|
||||||
try:
|
try:
|
||||||
for frmsem_id in self.semestres_aggreges:
|
for rcf_id in self.rcfs_aggreges:
|
||||||
self.res_sems_tags[frmsem_id] = semestres_taggues[frmsem_id]
|
self.sxstags[rcf_id] = sxstags[rcf_id]
|
||||||
except:
|
except:
|
||||||
raise ValueError("Semestres taggués manquants")
|
raise ValueError("Semestres SxTag manquants")
|
||||||
|
|
||||||
# Les étudiants (etuds, états civils & etudis)
|
# Les étudiants (etuds, états civils & etudis)
|
||||||
|
self.etuds = nt.etuds
|
||||||
self.add_etuds(nt.etuds)
|
self.add_etuds(nt.etuds)
|
||||||
|
|
||||||
# Les compétences (extraites des ues de tous les semestres)
|
# Les compétences (extraites de tous les Sxtags)
|
||||||
self.ues = self.comp_ues(tag="but")
|
self.competences_sorted = self.do_complist()
|
||||||
|
"""Compétences extraites de tous les SxTag aggrégés"""
|
||||||
|
|
||||||
# Les tags
|
# Les tags
|
||||||
self.tags_sorted = self.do_taglist()
|
self.tags_sorted = self.do_taglist()
|
||||||
"""Tags extraits de tous les semestres"""
|
"""Tags extraits de tous les SxTag aggrégés"""
|
||||||
|
|
||||||
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"""
|
|
||||||
|
|
||||||
|
# Les moyennes
|
||||||
self.moyennes_tags: dict[str, MoyennesTag] = {}
|
self.moyennes_tags: dict[str, MoyennesTag] = {}
|
||||||
"""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]
|
# Cube de note
|
||||||
self.moyennes_tags[tag] = MoyennesTag(tag, moy_gen_tag)
|
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):
|
def __eq__(self, other):
|
||||||
"""Egalité de 2 RCS taggués sur la base de leur identifiant"""
|
"""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:
|
def get_repr(self, verbose=False) -> str:
|
||||||
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
||||||
est basée)"""
|
est basée)"""
|
||||||
return self.rcs.get_repr(verbose=verbose)
|
return self.rcrcf.get_repr(verbose=verbose)
|
||||||
|
|
||||||
def compute_notes_cube(self):
|
def compute_notes_comps_cube(self, tag):
|
||||||
"""Construit le cube de notes (etudid x tags x semestre_aggregé)
|
"""Pour un tag donné, construit :
|
||||||
nécessaire au calcul des moyennes de l'aggrégat
|
* 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_tags = len(self.tags_sorted)
|
||||||
# nb_etudiants = len(self.etuds)
|
# nb_etudiants = len(self.etuds)
|
||||||
@ -131,55 +139,90 @@ class RCSTag(TableTag):
|
|||||||
|
|
||||||
# Index du cube (etudids -> dim 0, tags -> dim 1)
|
# Index du cube (etudids -> dim 0, tags -> dim 1)
|
||||||
etudids = [etud.etudid for etud in self.etuds]
|
etudids = [etud.etudid for etud in self.etuds]
|
||||||
tags = self.tags_sorted
|
competences_sorted = self.competences_sorted
|
||||||
semestres_id = list(self.res_sems_tags.keys())
|
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
|
# 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
|
moys_tag = sxtag.moyennes_tags[tag]
|
||||||
notes = self.res_sems_tags[frmsem_id].notes
|
|
||||||
|
|
||||||
# Les étudiants & les tags commun au dataframe final et aux notes du semestre)
|
# Charge les notes et les coeffs du semestre tag
|
||||||
etudids_communs = df.index.intersection(notes.index)
|
notes = moys_tag.matrice_notes.copy() # avec une copie
|
||||||
tags_communs = df.columns.intersection(notes.columns)
|
coeffs = moys_tag.matrice_coeffs.copy() # les coeffs
|
||||||
|
|
||||||
# Injecte les notes par tag
|
# Traduction des UE en compétences
|
||||||
df.loc[etudids_communs, tags_communs] = notes.loc[
|
association_ues_comp = moys_tag.competences
|
||||||
etudids_communs, tags_communs
|
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
|
# Supprime tout ce qui n'est pas numérique
|
||||||
for col in df.columns:
|
for col in notes_df.columns:
|
||||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
notes_df[col] = pd.to_numeric(notes_df[col], errors="coerce")
|
||||||
|
|
||||||
# Stocke le df
|
# Stocke les dfs
|
||||||
dfs[frmsem_id] = df
|
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"""
|
"""Réunit les notes sous forme d'un cube etudids x competences x semestres"""
|
||||||
semestres_x_etudids_x_tags = [dfs[fid].values for fid in dfs]
|
sxtag_x_etudids_x_comps = [notes_dfs[fid].values for fid in notes_dfs]
|
||||||
etudids_x_tags_x_semestres = np.stack(semestres_x_etudids_x_tags, axis=-1)
|
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):
|
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:
|
Returns:
|
||||||
Une liste de tags triés par ordre alphabétique
|
Une liste de tags triés par ordre alphabétique
|
||||||
"""
|
"""
|
||||||
tags = []
|
tags = []
|
||||||
for frmsem_id in self.res_sems_tags:
|
for frmsem_id in self.sxstags:
|
||||||
tags.extend(self.res_sems_tags[frmsem_id].tags_sorted)
|
tags.extend(self.sxstags[frmsem_id].tags_sorted)
|
||||||
pe_affichage.pe_print(f"* Tags : {', '.join(tags)}")
|
pe_affichage.pe_print(f"* Tags : {', '.join(tags)}")
|
||||||
return sorted(set(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
|
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
|
*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:
|
Args:
|
||||||
set_cube: notes moyennes aux modules ndarray
|
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)
|
etudids: liste des étudiants (dim. 0 du cube)
|
||||||
|
competences: list
|
||||||
tags: liste des tags (dim. 1 du cube)
|
tags: liste des tags (dim. 1 du cube)
|
||||||
Returns:
|
Returns:
|
||||||
Un DataFrame avec pour columns les moyennes par tags,
|
Un DataFrame avec pour columns les moyennes par tags,
|
||||||
et pour rows les etudid
|
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_etuds == len(etudids)
|
||||||
assert nb_tags == len(tags)
|
assert nb_comps == len(competences)
|
||||||
|
|
||||||
# Quelles entrées du cube contiennent des notes ?
|
# Quelles entrées du cube contiennent des notes ?
|
||||||
mask = ~np.isnan(set_cube)
|
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)
|
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
|
# Les moyennes par tag
|
||||||
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
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)
|
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_df = pd.DataFrame(
|
||||||
etud_moy_tag,
|
etud_moy_tag,
|
||||||
index=etudids, # les etudids
|
index=etudids, # les etudids
|
||||||
columns=tags, # les tags
|
columns=competences, # les competences
|
||||||
)
|
)
|
||||||
|
|
||||||
etud_moy_tag_df.fillna(np.nan)
|
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]
|
ues_hors_sport = [ue for ue in self.ues if ue.type != UE_SPORT]
|
||||||
self.ues_inscr_parcours_df = self.load_ues_inscr_parcours()
|
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:
|
# Les tags personnalisés et auto:
|
||||||
tags_dict = self._get_tags_dict()
|
tags_dict = self._get_tags_dict()
|
||||||
self._check_tags(tags_dict)
|
self._check_tags(tags_dict)
|
||||||
# self.tags = [tag for cat in dict_tags for tag in dict_tags[cat]]
|
# 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
|
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
|
||||||
self.moyennes_tags = {}
|
self.moyennes_tags = {}
|
||||||
|
|
||||||
@ -99,13 +113,10 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
moy_ues_tag = self.compute_moy_ues_tag(infos_tag)
|
moy_ues_tag = self.compute_moy_ues_tag(infos_tag)
|
||||||
# moy_gen_tag = self.compute_moy_gen_tag(moy_ues_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(
|
self.moyennes_tags[tag] = MoyennesTag(
|
||||||
tag,
|
tag,
|
||||||
ues_dict,
|
|
||||||
moy_ues_tag,
|
moy_ues_tag,
|
||||||
self.ues_inscr_parcours_df
|
matrice_coeffs
|
||||||
# moy_gen_tag
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ajoute les d'UE moyennes générales de BUT pour le semestre considéré
|
# 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},
|
{ue.id: self.etud_moy_ue[ue.id] for ue in ues_hors_sport},
|
||||||
index=self.etudids,
|
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(
|
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()
|
self.tags_sorted = self.get_all_tags()
|
||||||
@ -183,6 +190,13 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
self.dispense_ues,
|
self.dispense_ues,
|
||||||
block=self.formsemestre.block_moyennes,
|
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
|
return moyennes_ues_tag
|
||||||
|
|
||||||
def _get_tags_dict(self):
|
def _get_tags_dict(self):
|
||||||
@ -299,27 +313,3 @@ def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
|
|||||||
|
|
||||||
return synthese_tags
|
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):
|
class SxTag(TableTag):
|
||||||
def __init__(self, sxtag_id: (int, int), ressembuttags: dict[int, ResSemBUTTag]):
|
def __init__(self, sxtag_id: (int, int), ressembuttags: dict[int, ResSemBUTTag]):
|
||||||
"""Calcule les moyennes/classements par tag d'un semestre de type 'Sx'
|
"""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
|
* pour les étudiants non redoublants, ce sont les moyennes/classements
|
||||||
du semestre suivi
|
du semestre suivi
|
||||||
@ -91,6 +91,7 @@ class SxTag(TableTag):
|
|||||||
self.etuds = ressembuttags[self.fid_final].etuds
|
self.etuds = ressembuttags[self.fid_final].etuds
|
||||||
self.add_etuds(self.etuds)
|
self.add_etuds(self.etuds)
|
||||||
|
|
||||||
|
# Affichage
|
||||||
pe_affichage.pe_print(f"--> {self.get_repr()}")
|
pe_affichage.pe_print(f"--> {self.get_repr()}")
|
||||||
|
|
||||||
# Les tags
|
# Les tags
|
||||||
@ -99,38 +100,53 @@ class SxTag(TableTag):
|
|||||||
pe_affichage.pe_print(f"* Tags : {', '.join(self.tags_sorted)}")
|
pe_affichage.pe_print(f"* Tags : {', '.join(self.tags_sorted)}")
|
||||||
|
|
||||||
# Les UE
|
# 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
|
# 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
|
# Les inscriptions des étudiants aux UEs
|
||||||
# => ne conserve que les UEs du semestre final (pour les redoublants)
|
# => 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"
|
"but"
|
||||||
].ues_inscr_parcours_df
|
].matrice_coeffs
|
||||||
|
self.ues_inscr_parcours = ~np.isnan(self.matrice_coeffs.to_numpy())
|
||||||
|
|
||||||
|
|
||||||
# Les moyennes par tag
|
# Les moyennes par tag
|
||||||
self.moyennes_tags: dict[str, pd.DataFrame] = {}
|
self.moyennes_tags: dict[str, pd.DataFrame] = {}
|
||||||
"""Les notes aux UEs dans différents tags"""
|
"""Les notes aux UEs dans différents tags"""
|
||||||
|
|
||||||
# Masque des inscriptions
|
# Masque des inscriptions
|
||||||
inscr_mask = self.ues_inscr_parcours_df.to_numpy()
|
inscr_mask = self.ues_inscr_parcours
|
||||||
for tag in self.tags_sorted:
|
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)
|
notes_cube = self.compute_notes_ues_cube(tag, self.acronymes_ues_sorted)
|
||||||
|
|
||||||
# Calcule des moyennes sous forme d'un dataframe"""
|
# Calcule des moyennes sous forme d'un dataframe"""
|
||||||
moys_ues = compute_notes_ues(
|
matrice_moys_ues = compute_notes_ues(
|
||||||
notes_cube,
|
notes_cube,
|
||||||
self.etudids,
|
self.etudids,
|
||||||
self.acronymes_ues_sorted,
|
self.acronymes_ues_sorted,
|
||||||
inscr_mask,
|
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
|
# Les moyennes
|
||||||
self.moyennes_tags[tag] = MoyennesTag(
|
self.moyennes_tags[tag] = MoyennesTag(tag,
|
||||||
tag, self.ues, moys_ues, self.ues_inscr_parcours_df
|
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):
|
def __eq__(self, other):
|
||||||
"""Egalité de 2 SxTag sur la base de leur identifiant"""
|
"""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
|
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
||||||
est basée)"""
|
est basée)"""
|
||||||
affichage = [str(fid) for fid in self.ressembuttags]
|
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):
|
def compute_notes_ues_cube(self, tag, acronymes_ues_sorted):
|
||||||
"""Construit le cube de notes des UEs (etudid x accronyme_ue x semestre_aggregé)
|
"""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
|
# Charge les notes du semestre tag
|
||||||
sem_tag = self.ressembuttags[frmsem_id]
|
sem_tag = self.ressembuttags[frmsem_id]
|
||||||
moys_tag = sem_tag.moyennes_tags[tag]
|
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(
|
acronymes_ues_sem = list(
|
||||||
notes.columns
|
notes.columns
|
||||||
) # les acronymes des UEs du semestre tag
|
) # les acronymes des UEs du semestre tag
|
||||||
@ -183,7 +199,7 @@ class SxTag(TableTag):
|
|||||||
# Stocke le df
|
# Stocke le df
|
||||||
dfs[frmsem_id] = 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]
|
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)
|
etudids_x_ues_x_semestres = np.stack(semestres_x_etudids_x_ues, axis=-1)
|
||||||
return etudids_x_ues_x_semestres
|
return etudids_x_ues_x_semestres
|
||||||
|
@ -94,13 +94,13 @@ class TableTag(object):
|
|||||||
tags_tries = self.get_all_tags()
|
tags_tries = self.get_all_tags()
|
||||||
for tag in tags_tries:
|
for tag in tags_tries:
|
||||||
moy_tag = self.moyennes_tags[tag]
|
moy_tag = self.moyennes_tags[tag]
|
||||||
for acronyme in moy_tag.moys_ues:
|
for acronyme in moy_tag.moyennes:
|
||||||
moy = moy_tag.moys_ues[acronyme] # une moyenne
|
moy = moy_tag.moyennes[acronyme] # une moyenne
|
||||||
df = df.join(moy.synthese["notes"].rename(f"Moy {tag}-{acronyme}"))
|
df = df.join(moy.synthese["notes"].rename(f"Moy {tag}-{acronyme}"))
|
||||||
df = df.join(
|
df = df.join(
|
||||||
moy.synthese["classements"].rename(f"Class {tag}-{acronyme}")
|
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["notes"].rename(f"Moy {tag} (gen)"))
|
||||||
df = df.join(moy_gen.synthese["classements"].rename(f"Class {tag} (gen)"))
|
df = df.join(moy_gen.synthese["classements"].rename(f"Class {tag} (gen)"))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user