Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
9 changed files with 501 additions and 315 deletions
Showing only changes of commit 68bd20f8de - Show all commits

View File

@ -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)

View File

@ -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)"""

View File

@ -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,7 +105,9 @@ class JuryPE(object):
self._gen_xls_diplomes(zipfile)
self._gen_xls_ressembuttags(zipfile)
self._gen_rcss()
self._gen_xls_sxtags(zipfile)
# self._gen_rcrcfs()
# self._gen_xls_rcss_tags(zipfile)
# self._gen_xls_interclassements_rcss(zipfile)
# self._gen_xls_synthese_jury_par_tag(zipfile)
@ -173,41 +178,36 @@ 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}
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
@ -215,9 +215,9 @@ class JuryPE(object):
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:

View File

@ -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

View File

@ -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:
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:
"""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}}"""
def cree_rcss(self, etudiants: EtudiantsJuryPE, sxtags: dict[(str, int), pe_sxtag.SxTag]):
"""Créé tous les RCS, au regard du cursus des étudiants
analysés et des SxTag calculés.
self.rcfs: dict[tuple(int, str) : RCF] = {}
"""Ensemble des RCF recensés : {(nom_RCS, fid_terminal): RCF}"""
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:
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:
if etudid not in self.suivi:
self.suivi[etudid] = {
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"]
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
for etudid in etudiants.trajectoires:
# 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:
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 = {}
# Ajout des semestres au RCS
rcs.add_semestres_a_aggreger(semestres_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)
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
rcs.add_sxtags_a_aggreger(sxtags_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]
# Mémoire la trajectoire suivie par l'étudiant
self.suivi[etudid][nom_rcs] = rcs
# 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"]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)"))