Moyenne avec UEs multiples pour une même comp ; Amélioration calcul coeffs + moyennes notamment pour démissionnaires

This commit is contained in:
Cléo Baras 2024-03-06 13:53:47 +01:00
parent 35a038fd3a
commit 99bb0f471b
7 changed files with 239 additions and 228 deletions

View File

@ -119,7 +119,7 @@ class RCSemXTag(pe_tabletags.TableTag):
)
"""Compétences (triées par nom, extraites des SxTag aggrégés)"""
aff = pe_affichage.repr_comp_et_ues(self.acronymes_ues_to_competences)
pe_affichage.pe_print(f"--> Compétences : {', '.join(self.competences_sorted)}")
pe_affichage.pe_print(f"--> Compétences : {aff}")
# Les tags
self.tags_sorted = self._do_taglist()
@ -134,28 +134,24 @@ class RCSemXTag(pe_tabletags.TableTag):
for tag in self.tags_sorted:
pe_affichage.pe_print(f"--> Moyennes du tag 👜{tag}")
# Traitement des inscriptions aux semX(tags)
# ******************************************
# Cube d'inscription (etudids_sorted x compétences_sorted x sxstags)
# indiquant quel sxtag est valide pour chaque étudiant
inscr_df, inscr_cube = self.compute_inscriptions_comps_cube(tag)
# Cubes d'inscription (etudids_sorted x compétences_sorted x sxstags),
# de notes et de coeffs pour la moyenne générale
# en "aggrégant" les données des sxstags, compétence par compétence
(
inscr_df,
inscr_cube,
notes_df,
notes_cube,
coeffs_df,
coeffs_cube,
) = self.compute_cubes(tag)
# Traitement des notes
# ********************
# Cube de notes (etudids_sorted x compétences_sorted x sxstags)
notes_df, notes_cube = self.compute_notes_comps_cube(tag)
# Calcule les moyennes sous forme d'un dataframe en les "aggrégant"
# compétence par compétence
moys_competences = self.compute_notes_competences(notes_cube, inscr_cube)
# Traitement des coeffs pour la moyenne générale
# ***********************************************
# Df des coeffs sur tous les SxTags aggrégés
coeffs_df, coeffs_cube = self.compute_coeffs_comps_cube(tag)
# Synthèse des coefficients à prendre en compte pour la moyenne générale
matrice_coeffs_moy_gen = self.compute_coeffs_competences(
coeffs_cube, inscr_cube, notes_cube
# Calcule les moyennes, et synthétise les coeffs
(
moys_competences,
matrice_coeffs_moy_gen,
) = self.compute_notes_et_coeffs_competences(
notes_cube, coeffs_cube, inscr_cube
)
# Affichage des coeffs
@ -186,10 +182,17 @@ class RCSemXTag(pe_tabletags.TableTag):
else:
return f"{self.__class__.__name__} {self.rcs_id}"
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
def compute_cubes(self, tag):
"""Pour un tag donné, construit les cubes de :
* d'inscriptions aux compétences (etudid x competences x SxTag)
* de notes (etudid x competences x SxTag)
* de coeffs (etudid x competences x SxTag)
nécessaire au calcul des moyennes, en :
* transformant les données des UEs en données de compétences (changement de noms)
* fusionnant les données d'un même semestre, lorsque plusieurs UEs traitent d'une même compétence (cas des RCSx = Sx)
* aggrégeant les données de compétences sur plusieurs semestres (cas des RCSx = xA ou xS)
Args:
tag: Le tag visé
@ -197,144 +200,75 @@ class RCSemXTag(pe_tabletags.TableTag):
# etudids_sorted: list[int],
# competences_sorted: list[str],
# sxstags: dict[(str, int) : pe_sxtag.SxTag],
inscriptions_dfs = {}
notes_dfs = {}
coeffs_dfs = {}
for sxtag_id, sxtag in self.sxstags_aggreges.items():
# Partant d'un dataframe vierge
# Partant de dataframes vierges
inscription_df = pd.DataFrame(
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
)
notes_df = pd.DataFrame(
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
)
# Charge les notes du semestre tag (copie car changement de nom de colonnes à venir)
coeffs_df = pd.DataFrame(
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
)
# Charge les données du semestre tag (copie car changement de nom de colonnes à venir)
if tag in sxtag.moyennes_tags: # si le tag est présent dans le semestre
moys_tag = sxtag.moyennes_tags[tag]
notes = moys_tag.matrice_notes_gen.copy() # dataframe etudids x ues
# Les inscr, les notes, les coeffs
acro_ues_inscr_parcours = sxtag.acro_ues_inscr_parcours
notes = moys_tag.matrice_notes_gen
coeffs = moys_tag.matrice_coeffs_moy_gen # les coeffs
# Traduction des acronymes d'UE en compétences
# comp_to_ues = pe_comp.asso_comp_to_accronymes(self.acronymes_ues_to_competences)
acronymes_ues_columns = notes.columns
acronymes_to_comps = [
self.acronymes_ues_to_competences[acro]
for acro in acronymes_ues_columns
]
notes.columns = acronymes_to_comps
for acronyme in acronymes_ues_columns:
# La compétence visée
competence = self.acronymes_ues_to_competences[acronyme] # La comp
# Les étudiants et les compétences communes
(
etudids_communs,
comp_communes,
) = pe_comp.find_index_and_columns_communs(notes_df, notes)
# Les étud inscrits à la comp reportés dans l'inscription au RCSemX
comp_inscr = acro_ues_inscr_parcours[
acro_ues_inscr_parcours.notnull()
].index
etudids_communs = list(
inscription_df.index.intersection(comp_inscr)
)
inscription_df.loc[
etudids_communs, competence
] = acro_ues_inscr_parcours.loc[etudids_communs, acronyme]
# Recopie des notes et des coeffs
notes_df.loc[etudids_communs, comp_communes] = notes.loc[
etudids_communs, comp_communes
]
# Les étud ayant une note à l'acronyme de la comp (donc à la comp)
etuds_avec_notes = notes[notes[acronyme].notnull()].index
etudids_communs = list(
notes_df.index.intersection(etuds_avec_notes)
)
notes_df.loc[etudids_communs, competence] = notes.loc[
etudids_communs, acronyme
]
# Les coeffs
etuds_avec_coeffs = coeffs[coeffs[acronyme].notnull()].index
etudids_communs = list(
coeffs_df.index.intersection(etuds_avec_coeffs)
)
coeffs_df.loc[etudids_communs, competence] = coeffs.loc[
etudids_communs, acronyme
]
# Supprime tout ce qui n'est pas numérique
# for col in notes_df.columns:
# notes_df[col] = pd.to_numeric(notes_df[col], errors="coerce")
# Stocke les dfs
notes_dfs[sxtag_id] = notes_df
"""Réunit les notes sous forme d'un cube etudids x competences x semestres"""
sxtag_x_etudids_x_comps = [
notes_dfs[sxtag_id] for sxtag_id in self.sxstags_aggreges
]
notes_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
return notes_dfs, notes_etudids_x_comps_x_sxtag
def compute_coeffs_comps_cube(self, tag):
"""Pour un tag donné, construit
le cube de coeffs (etudid x competences x SxTag) (traduisant les inscriptions
des étudiants aux UEs en fonction de leur parcours)
qui s'applique aux différents SxTag
en remplaçant les données d'UE (obtenus du SxTag) par les compétences
Args:
tag: Le tag visé
"""
# etudids_sorted: list[int],
# competences_sorted: list[str],
# sxstags: dict[(str, int) : pe_sxtag.SxTag],
coeffs_dfs = {}
for sxtag_id, sxtag in self.sxstags_aggreges.items():
# Partant d'un dataframe vierge
coeffs_df = pd.DataFrame(
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
)
if tag in sxtag.moyennes_tags:
moys_tag = sxtag.moyennes_tags[tag]
# Charge les notes et les coeffs du semestre tag
coeffs = moys_tag.matrice_coeffs_moy_gen.copy() # les coeffs
# Traduction des acronymes d'UE en compétences
acronymes_ues_columns = coeffs.columns
acronymes_to_comps = [
self.acronymes_ues_to_competences[acro]
for acro in acronymes_ues_columns
]
coeffs.columns = acronymes_to_comps
# Les étudiants et les compétences communes
etudids_communs, comp_communes = pe_comp.find_index_and_columns_communs(
coeffs_df, coeffs
)
# Recopie des notes et des coeffs
coeffs_df.loc[etudids_communs, comp_communes] = coeffs.loc[
etudids_communs, comp_communes
]
# Stocke les dfs
coeffs_dfs[sxtag_id] = coeffs_df
"""Réunit les coeffs sous forme d'un cube etudids x competences x semestres"""
sxtag_x_etudids_x_comps = [
coeffs_dfs[sxtag_id] for sxtag_id in self.sxstags_aggreges
]
coeffs_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
return coeffs_dfs, coeffs_etudids_x_comps_x_sxtag
def compute_inscriptions_comps_cube(
self,
tag,
):
"""Pour un tag donné, construit
le cube etudid x competences x SxTag traduisant quels sxtags est à prendre
en compte pour chaque étudiant.
Contient des 0 et des 1 pour indiquer la prise en compte.
Args:
tag: Le tag visé
"""
# etudids_sorted: list[int],
# competences_sorted: list[str],
# sxstags: dict[(str, int) : pe_sxtag.SxTag],
# Initialisation
inscriptions_dfs = {}
for sxtag_id, sxtag in self.sxstags_aggreges.items():
# Partant d'un dataframe vierge
inscription_df = pd.DataFrame(
0, index=self.etudids_sorted, columns=self.competences_sorted
)
# Les étudiants dont les résultats au sxtag ont été calculés
etudids_sxtag = sxtag.etudids_sorted
# Les étudiants communs
etudids_communs = sorted(set(self.etudids_sorted) & set(etudids_sxtag))
# Acte l'inscription
inscription_df.loc[etudids_communs, :] = 1
# Stocke les dfs
inscriptions_dfs[sxtag_id] = inscription_df
notes_dfs[sxtag_id] = notes_df
coeffs_dfs[sxtag_id] = coeffs_df
"""Réunit les inscriptions sous forme d'un cube etudids x competences x semestres"""
sxtag_x_etudids_x_comps = [
@ -344,7 +278,26 @@ class RCSemXTag(pe_tabletags.TableTag):
sxtag_x_etudids_x_comps, axis=-1
)
return inscriptions_dfs, inscriptions_etudids_x_comps_x_sxtag
"""Réunit les notes sous forme d'un cube etudids x competences x semestres"""
sxtag_x_etudids_x_comps = [
notes_dfs[sxtag_id] for sxtag_id in self.sxstags_aggreges
]
notes_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
"""Réunit les coeffs sous forme d'un cube etudids x competences x semestres"""
sxtag_x_etudids_x_comps = [
coeffs_dfs[sxtag_id] for sxtag_id in self.sxstags_aggreges
]
coeffs_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
return (
inscriptions_dfs,
inscriptions_etudids_x_comps_x_sxtag,
notes_dfs,
notes_etudids_x_comps_x_sxtag,
coeffs_dfs,
coeffs_etudids_x_comps_x_sxtag,
)
def _do_taglist(self) -> list[str]:
"""Synthétise les tags à partir des Sxtags aggrégés.
@ -370,7 +323,9 @@ class RCSemXTag(pe_tabletags.TableTag):
dict_competences |= sxtag.acronymes_ues_to_competences
return dict_competences
def compute_notes_competences(self, set_cube: np.array, inscriptions: np.array):
def compute_notes_et_coeffs_competences(
self, notes_cube: np.array, coeffs_cube: np.array, inscr_mask: np.array
):
"""Calcule la moyenne par compétences (à un tag donné) sur plusieurs semestres (partant du set_cube).
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles
@ -379,9 +334,11 @@ class RCSemXTag(pe_tabletags.TableTag):
par aggrégat de plusieurs semestres.
Args:
set_cube: notes moyennes aux compétences ndarray
notes_cube: notes moyennes aux compétences ndarray
(etuds x UEs|compétences x sxtags), des floats avec des NaN
inscriptions: inscrptions aux compétences ndarray
coeffs_cube: coeffs appliqués aux compétences
(etuds x UEs|compétences x sxtags), des floats avec des NaN
inscr_mask: inscrptions aux compétences ndarray
(etuds x UEs|compétences x sxtags), des 0 et des 1
Returns:
Un DataFrame avec pour columns les moyennes par tags,
@ -389,78 +346,45 @@ class RCSemXTag(pe_tabletags.TableTag):
"""
# etudids_sorted: liste des étudiants (dim. 0 du cube)
# competences_sorted: list (dim. 1 du cube)
nb_etuds, nb_comps, nb_semestres = set_cube.shape
nb_etuds, nb_comps, nb_semestres = notes_cube.shape
# assert nb_etuds == len(etudids_sorted)
# assert nb_comps == len(competences_sorted)
# Applique le masque d'inscriptions
set_cube_significatif = set_cube * inscriptions
# Applique le masque d'inscriptions aux notes et aux coeffs
notes_significatives = notes_cube * inscr_mask
coeffs_significatifs = coeffs_cube * inscr_mask
# Quelles entrées du cube contiennent des notes ?
mask = ~np.isnan(set_cube_significatif)
# Enlève les NaN du cube de notes pour les entrées manquantes
set_cube_no_nan = np.nan_to_num(set_cube_significatif, nan=0.0)
# Enlève les NaN des cubes pour les entrées manquantes
notes_no_nan = np.nan_to_num(notes_significatives, nan=0.0)
coeffs_no_nan = np.nan_to_num(coeffs_significatifs, 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)
mask = ~np.isnan(
notes_significatives
) # Quelles entrées contiennent des notes ?
etud_moy_tag = np.sum(notes_no_nan, axis=2) / np.sum(mask, axis=2)
coeffs_pris_en_compte = coeffs_no_nan * mask
coeff_tag = np.sum(coeffs_pris_en_compte, axis=2)
inscr_prise_en_compte = inscr_mask * mask
inscr_prise_en_compte = np.nan_to_num(inscr_prise_en_compte, nan=-1.0)
inscr_tag = np.max(inscr_prise_en_compte, axis=2)
inscr_tag[inscr_tag < 0] = np.NaN # fix les max non calculés (-1) -> Na?
# Le dataFrame des notes moyennes
etud_moy_tag = etud_moy_tag * inscr_tag
etud_moy_tag_df = pd.DataFrame(
etud_moy_tag,
index=self.etudids_sorted, # les etudids
columns=self.competences_sorted, # les competences
)
etud_moy_tag_df.fillna(np.nan)
return etud_moy_tag_df
def compute_coeffs_competences(
self,
coeff_cube: np.array,
inscriptions: np.array,
set_cube: np.array,
):
"""Calcule les coeffs à utiliser pour la moyenne générale (toutes compétences
confondues), en fonction des inscriptions.
Args:
coeffs_cube: coeffs impliqués dans la moyenne générale (semestres par semestres)
inscriptions: inscriptions aux UES|Compétences ndarray
(etuds x UEs|compétences x sxtags), des 0 ou des 1
set_cube: les notes
Returns:
Un DataFrame de coefficients (etudids_sorted x compétences_sorted)
"""
# etudids_sorted: liste des étudiants (dim. 0 du cube)
# competences_sorted: list (dim. 1 du cube)
nb_etuds, nb_comps, nb_semestres = inscriptions.shape
# assert nb_etuds == len(etudids_sorted)
# assert nb_comps == len(competences_sorted)
# Applique le masque des inscriptions aux coeffs et aux notes
coeffs_significatifs = coeff_cube * inscriptions
# Enlève les NaN du cube de notes pour les entrées manquantes
coeffs_cube_no_nan = np.nan_to_num(coeffs_significatifs, nan=0.0)
# Quelles entrées du cube contiennent des notes ?
mask = ~np.isnan(set_cube)
# Retire les coefficients associés à des données sans notes
coeffs_cube_no_nan = coeffs_cube_no_nan * mask
# Somme les coefficients (correspondant à des notes)
coeff_tag = np.sum(coeffs_cube_no_nan, axis=2)
# Le dataFrame des coeffs
coeff_tag = coeff_tag * inscr_tag # Réapplique le masque des inscriptions
coeffs_df = pd.DataFrame(
coeff_tag, index=self.etudids_sorted, columns=self.competences_sorted
)
# Remet à Nan les coeffs à 0
coeffs_df = coeffs_df.fillna(np.nan)
return coeffs_df
return etud_moy_tag_df, coeffs_df

View File

@ -98,8 +98,10 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
self.parcours += [None]
# Les UEs en fonction des parcours
self.ues_inscr_parcours_df = self.load_ues_inscr_parcours()
"""Inscription des étudiants aux UEs des parcours"""
self.ues_inscr_parcours_df = (
self.load_ues_inscr_parcours()
) # peut contenir du sport
"""Inscription des étudiants aux UEs des parcours (etudids x ue_ids)"""
# Les acronymes des UEs
self.ues_to_acronymes = {ue.id: ue.acronyme for ue in self.ues_standards}
@ -144,6 +146,12 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
f"--> Moyenne générale calculée avec pour coeffs d'UEs : {profils_aff}"
)
# Les inscriptions aux acronymes d'ues
self.acro_ues_inscr_parcours = self._get_acro_ues_inscr_parcours(
self.ues_inscr_parcours_df, self.ues_standards
)
"""DataFrame indiquant à quelles UEs (données par leurs acronymes) sont inscrits les étudiants)"""
# Les capitalisations (mask etuids x acronyme_ue valant True si capitalisée, False sinon)
self.capitalisations = self._get_capitalisations(self.ues_standards)
"""DataFrame indiquant les UEs capitalisables d'un étudiant (etudids x )"""
@ -167,7 +175,7 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
)
# Ajoute les moyennes par UEs + la moyenne générale (but)
moy_gen = self.compute_moy_gen()
moy_gen = self.compute_moy_gen(self.acro_ues_inscr_parcours)
self.moyennes_tags["but"] = pe_moytag.MoyennesTag(
"but",
pe_moytag.CODE_MOY_UE,
@ -240,6 +248,31 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
matrice_coeffs_moy_gen = matrice_coeffs_moy_gen.sort_index(axis=1)
return matrice_coeffs_moy_gen
def _get_acro_ues_inscr_parcours(
self, ues_inscr_parcours_df: pd.DataFrame, ues_standards: list[UniteEns]
) -> pd.DataFrame:
"""Renvoie un dataFrame donnant les inscriptions (Nan ou 1) des
étudiants aux UEs définies par leur acronyme, en fonction de leur parcours
(cf. ues_inscr_parcours_df) et en limitant les données aux UEs standards (hors sport=
Args:
ues_inscr_parcours_df: Les inscriptions des étudiants aux UEs
ues_standards: Les UEs standards à prendre en compte
Returns:
Un dataFrame etudids x acronymes_UEs avec les coeffs des UEs
"""
matrice_inscription = ues_inscr_parcours_df * [
1 for ue in ues_standards # if ue.type != UE_SPORT <= déjà supprimé
]
matrice_inscription.columns = [
self.ues_to_acronymes[ue.id] for ue in ues_standards
]
# Tri par etudids (dim 0) et par acronymes (dim 1)
matrice_inscription = matrice_inscription.sort_index()
matrice_inscription = matrice_inscription.sort_index(axis=1)
return matrice_inscription
def _get_capitalisations(self, ues_standards) -> pd.DataFrame:
"""Renvoie un dataFrame résumant les UEs capitalisables par les
étudiants, d'après les décisions de jury (sous réserve qu'elles existent).
@ -342,6 +375,9 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
colonnes = [ue.id for ue in self.ues_standards]
moyennes_ues_tag = moyennes_ues_tag[colonnes]
# Met à zéro les moyennes non calculées/calculables
moyennes_ues_tag.fillna(0.0, inplace=True)
# Applique le masque d'inscription aux UE pour ne conserver que les UE dans lequel l'étudiant est inscrit
moyennes_ues_tag = moyennes_ues_tag[colonnes] * ues_inscr_parcours_df[colonnes]
@ -355,7 +391,7 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
return moyennes_ues_tag
def compute_moy_gen(self):
def compute_moy_gen(self, acro_ues_inscr_parcours):
"""Récupère les moyennes des UEs pour le calcul de la moyenne générale,
en associant à chaque UE.id son acronyme (toutes UEs confondues)
"""
@ -368,6 +404,12 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
acronymes = [self.ues_to_acronymes[col] for col in colonnes]
df_ues.columns = acronymes
# Met à zéro les moyennes non calculées/calculables
df_ues.fillna(0.0, inplace=True)
# Réapplique le mask d'inscription
df_ues = df_ues * acro_ues_inscr_parcours
# Tri par ordre aphabétique de colonnes
df_ues.sort_index(axis=1)

View File

@ -141,6 +141,10 @@ class SxTag(pe_tabletags.TableTag):
aff = pe_affichage.repr_asso_ue_comp(self.acronymes_ues_to_competences)
pe_affichage.pe_print(f"--> UEs/Compétences : {aff}")
# Les inscriptions des étudiants aux UEs (donnée par leur acronyme)
# par report de celle du ressemfinal
self.acro_ues_inscr_parcours = self.ressembuttag_final.acro_ues_inscr_parcours
# Les coeffs pour la moyenne générale (traduisant également l'inscription
# des étudiants aux UEs) (etudids_sorted x acronymes_ues_sorted)
self.matrice_coeffs_moy_gen = self.ressembuttag_final.matrice_coeffs_moy_gen
@ -178,7 +182,7 @@ class SxTag(pe_tabletags.TableTag):
pe_affichage.pe_print(f" > MoyTag 👜{tag}")
# Masque des inscriptions aux UEs (extraits de la matrice de coefficients)
inscr_mask: np.array = ~np.isnan(self.matrice_coeffs_moy_gen.to_numpy())
# inscr_mask: np.array = ~np.isnan(self.matrice_coeffs_moy_gen.to_numpy())
# Moyennes (tous modules confondus)
if not self.has_notes_tag(tag):
@ -194,6 +198,7 @@ class SxTag(pe_tabletags.TableTag):
notes_df_gen, notes_cube_gen = self.compute_notes_ues_cube(tag)
# DataFrame des moyennes (tous modules confondus)
inscr_mask = self.acro_ues_inscr_parcours.to_numpy()
matrice_moys_ues = self.compute_notes_ues(
notes_cube_gen, masque_cube, inscr_mask
)
@ -289,7 +294,7 @@ class SxTag(pe_tabletags.TableTag):
def compute_notes_ues(
self,
set_cube: np.array,
masque_cube: np.array,
cap_mask_3D: np.array,
inscr_mask: np.array,
) -> pd.DataFrame:
"""Calcule la moyenne par UEs à un tag donné en prenant la note maximum (UE
@ -298,7 +303,8 @@ class SxTag(pe_tabletags.TableTag):
Args:
set_cube: notes moyennes aux modules ndarray
(semestre_ids x etudids x UEs), des floats avec des NaN
masque_cube: masque indiquant si la note doit être prise en compte ndarray
cap_mask_3D
: masque indiquant si la note doit être prise en compte ndarray
(semestre_ids x etudids x UEs), des 1.0 ou des 0.0
inscr_mask: masque etudids x UE traduisant les inscriptions des
étudiants aux UE (du semestre terminal)
@ -320,10 +326,7 @@ class SxTag(pe_tabletags.TableTag):
set_cube = set_cube * inscr_mask_3D
# Entrées à garder en fonction des UEs capitalisées ou non
set_cube = set_cube * masque_cube
# Quelles entrées du cube contiennent des notes ?
mask = ~np.isnan(set_cube)
set_cube = set_cube * cap_mask_3D
# Enlève les NaN du cube pour les entrées manquantes : NaN -> -1.0
set_cube_no_nan = np.nan_to_num(set_cube, nan=-1.0)
@ -332,9 +335,12 @@ class SxTag(pe_tabletags.TableTag):
# TODO: Pour l'instant un max sans prise en compte des UE capitalisées
etud_moy = np.max(set_cube_no_nan, axis=2)
# Fix les max non calculé -1 -> NaN
# Fix les max non calculés (-1) -> NaN
etud_moy[etud_moy < 0] = np.NaN
# Réapplique le masque d'inscription (dans le doute)
etud_moy = etud_moy * inscr_mask
# Le dataFrame
etud_moy_tag_df = pd.DataFrame(
etud_moy,

View File

@ -9,6 +9,7 @@
from flask import g
from app import log
from app.pe.rcss import pe_rcs
import app.pe.pe_comp as pe_comp
PE_DEBUG = False
@ -135,23 +136,45 @@ def aff_tags_par_categories(dict_tags):
aff_tags_auto = ", ".join([f"👜{nom}" for nom in noms_tags_auto])
return f"Tags automatiques {aff_tags_auto} (aucun tag personnalisé)"
# Affichage
def repr_jeune(etudid, etudiants):
"""Renvoie la représentation d'un étudiant"""
etat = "" if etudid in etudiants.abandons_ids else ""
jeune = f"{etat} {etudiants.identites[etudid].nomprenom} (#{etudid})"
return jeune
def aff_trajectoires_suivies_par_etudiants(etudiants):
"""Affiche les trajectoires (regroupement de (form)semestres)
amenant un étudiant du S1 à un semestre final"""
amenant un étudiant du S1 à un semestre final,
en regroupant les étudiants par profil de trajectoires"""
# Affichage pour debug
etudiants_ids = etudiants.etudiants_ids
jeunes = list(enumerate(etudiants_ids))
for no_etud, etudid in jeunes:
etat = "" if etudid in etudiants.abandons_ids else ""
pe_print(f"--> {etat} {etudiants.identites[etudid].nomprenom} (#{etudid}) :")
profils_traj = {}
for no_etud, etudid in jeunes:
jeune = repr_jeune(etudid, etudiants)
# La trajectoire du jeune
trajectoires = etudiants.trajectoires[etudid]
profil_traj = []
for nom_rcs, rcs in trajectoires.items():
if rcs:
pe_print(f" > RCS ⏯️{nom_rcs}: {rcs.get_repr()}")
profil_traj += [f" > RCS ⏯️{nom_rcs}: {rcs.get_repr()}"]
aff_profil_traj = "\n".join(profil_traj)
if aff_profil_traj not in profils_traj:
profils_traj[aff_profil_traj] = []
profils_traj[aff_profil_traj] += [jeune]
# Affichage final
for profil, jeunes in profils_traj.items():
pe_print(f"--> Trajectoire suivie par : ")
pe_print("\n".join([" " + jeune for jeune in jeunes]))
pe_print(profil)
def aff_semXs_suivis_par_etudiants(etudiants):
@ -198,13 +221,11 @@ def aff_capitalisations(etuds, ressembuttags, fid_final, acronymes_sorted, masqu
def repr_comp_et_ues(acronymes_ues_to_competences):
"""Affichage pour debug"""
asso_comp_to_ues = pe_comp.asso_comp_to_accronymes(acronymes_ues_to_competences)
aff_comp = []
competences_sorted = sorted(acronymes_ues_to_competences.keys())
competences_sorted = sorted(asso_comp_to_ues.keys())
for comp in competences_sorted:
liste = []
for acro in acronymes_ues_to_competences:
if acronymes_ues_to_competences[acro] == comp:
liste += ["📍" + acro]
liste = ["📍" + accro for accro in asso_comp_to_ues[comp]]
aff_comp += [f" 💡{comp} (⇔ {', '.join(liste)})"]
return "\n".join(aff_comp)

View File

@ -337,3 +337,21 @@ def get_dernier_semestre_en_date(semestres: dict[int, FormSemestre]) -> FormSeme
dernier_semestre = semestres[fid]
return dernier_semestre
return None
def asso_comp_to_accronymes(accro_ues_to_competences):
"""Partant d'un dictionnaire ``{nom_ue: compétence}`` associant des
accronymes d'UEs à des compétences, renvoie l'association d'une compétence
à ou aux UEs qui l'adresse : ``{competence: [liste_nom_ue]}``
Args:
accro_ues_to_competences: Dictionnaire ``{nom_ue: compétence}``
Return:
Le dictionnaire ``{competence: [liste_nom_ue]}``
"""
asso = {}
for accro, comp in accro_ues_to_competences.items():
if comp not in asso:
asso[comp] = []
asso[comp].append(accro)
return asso

View File

@ -705,7 +705,7 @@ class JuryPE(object):
tag, aggregat=aggregat, type_colonnes=False, options=self.options
)
if not df_groupe.empty:
aff_aggregat += [aggregat]
aff_aggregat += [aggregat + " (Groupe)"]
df = df.join(df_groupe)
# Le dataframe du classement sur la promo
@ -718,7 +718,7 @@ class JuryPE(object):
)
if not df_promo.empty:
aff_aggregat += [aggregat]
aff_aggregat += [aggregat + " (Promo)"]
df = df.join(df_promo)
if aff_aggregat:

View File

@ -15,7 +15,7 @@ from app.pe.rcss import pe_rcs, pe_trajectoires
class RCSemX(pe_rcs.RCS):
"""Modélise un regroupement cohérent de SemX (en même regroupant
"""Modélise un regroupement cohérent de SemX (en regroupant
des semestres Sx combinés pour former les résultats des étudiants
au semestre de rang x) dans le but de synthétiser les résultats
du S1 jusqu'au semestre final ciblé par le RCSemX (dépendant de l'aggrégat