forked from ScoDoc/ScoDoc
Ajoute les moyennes de ressources/saes
This commit is contained in:
parent
a50bbe9223
commit
5ea79c03a3
@ -41,6 +41,7 @@ from app.pe import pe_affichage
|
|||||||
from app.pe.moys import pe_tabletags, pe_moy, pe_moytag, pe_sxtag
|
from app.pe.moys import pe_tabletags, pe_moy, pe_moytag, pe_sxtag
|
||||||
from app.pe.rcss import pe_rcs
|
from app.pe.rcss import pe_rcs
|
||||||
import app.pe.pe_comp as pe_comp
|
import app.pe.pe_comp as pe_comp
|
||||||
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
|
|
||||||
class InterClassTag(pe_tabletags.TableTag):
|
class InterClassTag(pe_tabletags.TableTag):
|
||||||
@ -119,7 +120,7 @@ class InterClassTag(pe_tabletags.TableTag):
|
|||||||
# Les données sur les tags
|
# Les données sur les tags
|
||||||
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)"""
|
||||||
aff = pe_affichage.aff_tag(self.tags_sorted)
|
aff = pe_affichage.repr_tags(self.tags_sorted)
|
||||||
pe_affichage.pe_print(f"--> Tags : {aff}")
|
pe_affichage.pe_print(f"--> Tags : {aff}")
|
||||||
|
|
||||||
# Les données sur les UEs (si SxTag) ou compétences (si RCSTag)
|
# Les données sur les UEs (si SxTag) ou compétences (si RCSTag)
|
||||||
@ -134,8 +135,8 @@ class InterClassTag(pe_tabletags.TableTag):
|
|||||||
f"--> Compétences : {pe_affichage.aff_competences(self.champs_sorted)}"
|
f"--> Compétences : {pe_affichage.aff_competences(self.champs_sorted)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Construit la matrice de notes
|
# Etudids triés
|
||||||
etudids_sorted = sorted(list(self.diplomes_ids))
|
self.etudids_sorted = sorted(list(self.diplomes_ids))
|
||||||
|
|
||||||
self.nom = self.get_repr()
|
self.nom = self.get_repr()
|
||||||
"""Représentation textuelle de l'interclassement"""
|
"""Représentation textuelle de l'interclassement"""
|
||||||
@ -143,19 +144,24 @@ class InterClassTag(pe_tabletags.TableTag):
|
|||||||
# Synthétise les moyennes/classements par tag
|
# Synthétise les moyennes/classements par tag
|
||||||
self.moyennes_tags: dict[str, pe_moytag.MoyennesTag] = {}
|
self.moyennes_tags: dict[str, pe_moytag.MoyennesTag] = {}
|
||||||
for tag in self.tags_sorted:
|
for tag in self.tags_sorted:
|
||||||
notes = self.compute_notes_matrice(tag, etudids_sorted, self.champs_sorted)
|
# Les moyennes tous modules confondus
|
||||||
|
notes_gen = self.compute_notes_matrice(tag, pole=None)
|
||||||
|
# Les ressources
|
||||||
|
notes_res = self.compute_notes_matrice(tag, pole=ModuleType.RESSOURCE)
|
||||||
|
# Les SAEs
|
||||||
|
notes_saes = self.compute_notes_matrice(tag, pole=ModuleType.SAE)
|
||||||
|
|
||||||
coeffs = self.compute_coeffs_matrice(
|
# Les coefficients de la moyenne générale
|
||||||
tag, etudids_sorted, self.champs_sorted
|
coeffs = self.compute_coeffs_matrice(tag)
|
||||||
)
|
aff = pe_affichage.repr_profil_coeffs(coeffs, with_index=True)
|
||||||
|
|
||||||
aff = pe_affichage.aff_profil_coeffs(coeffs, with_index=True)
|
|
||||||
pe_affichage.pe_print(f"--> Moyenne 👜{tag} avec coeffs: {aff} ")
|
pe_affichage.pe_print(f"--> Moyenne 👜{tag} avec coeffs: {aff} ")
|
||||||
|
|
||||||
self.moyennes_tags[tag] = pe_moytag.MoyennesTag(
|
self.moyennes_tags[tag] = pe_moytag.MoyennesTag(
|
||||||
tag,
|
tag,
|
||||||
self.type,
|
self.type,
|
||||||
notes,
|
notes_gen,
|
||||||
|
notes_res,
|
||||||
|
notes_saes,
|
||||||
coeffs, # limite les moyennes aux étudiants de la promo
|
coeffs, # limite les moyennes aux étudiants de la promo
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -163,29 +169,6 @@ class InterClassTag(pe_tabletags.TableTag):
|
|||||||
"""Une représentation textuelle"""
|
"""Une représentation textuelle"""
|
||||||
return f"{self.nom_rcs} par {self.type}"
|
return f"{self.nom_rcs} par {self.type}"
|
||||||
|
|
||||||
def __aff_profil_coeffs(self, matrice_coeffs_moy_gen):
|
|
||||||
"""Extrait de la matrice des coeffs, les différents types d'inscription
|
|
||||||
et de coefficients (appelés profil) des étudiants et les affiche
|
|
||||||
(pour debug)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Les profils des coeffs d'UE (pour debug)
|
|
||||||
profils = []
|
|
||||||
for i in matrice_coeffs_moy_gen.index:
|
|
||||||
val = matrice_coeffs_moy_gen.loc[i].fillna("-")
|
|
||||||
val = " | ".join([str(v) for v in val])
|
|
||||||
if val not in profils:
|
|
||||||
profils += [val]
|
|
||||||
|
|
||||||
# L'affichage
|
|
||||||
if len(profils) > 1:
|
|
||||||
profils_aff = "\n" + "\n".join([" " * 10 + prof for prof in profils])
|
|
||||||
else:
|
|
||||||
profils_aff = "\n".join(profils)
|
|
||||||
pe_affichage.pe_print(
|
|
||||||
f" > Moyenne calculée avec pour coeffs (de compétences) : {profils_aff}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _do_taglist(self):
|
def _do_taglist(self):
|
||||||
"""Synthétise les tags à partir des TableTags (SXTag ou RCSTag)
|
"""Synthétise les tags à partir des TableTags (SXTag ou RCSTag)
|
||||||
|
|
||||||
@ -197,9 +180,7 @@ class InterClassTag(pe_tabletags.TableTag):
|
|||||||
tags.extend(rcstag.tags_sorted)
|
tags.extend(rcstag.tags_sorted)
|
||||||
return sorted(set(tags))
|
return sorted(set(tags))
|
||||||
|
|
||||||
def compute_notes_matrice(
|
def compute_notes_matrice(self, tag, pole=None) -> pd.DataFrame:
|
||||||
self, tag, etudids_sorted: list[int], champs_sorted: list[str]
|
|
||||||
) -> pd.DataFrame:
|
|
||||||
"""Construit la matrice de notes (etudids x champs) en
|
"""Construit la matrice de notes (etudids x champs) en
|
||||||
reportant les moyennes obtenues par les étudiants
|
reportant les moyennes obtenues par les étudiants
|
||||||
aux semestres de l'aggrégat pour le tag visé.
|
aux semestres de l'aggrégat pour le tag visé.
|
||||||
@ -207,49 +188,56 @@ class InterClassTag(pe_tabletags.TableTag):
|
|||||||
Les champs peuvent être des acronymes d'UEs ou des compétences.
|
Les champs peuvent être des acronymes d'UEs ou des compétences.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
etudids_sorted: Les etudids des étudiants (diplômés) triés
|
tag: Le tag visé
|
||||||
champs_sorted: Les champs (UE ou compétences) à faire apparaitre dans la matrice
|
|
||||||
Return:
|
Return:
|
||||||
Le dataFrame (etudids x champs)
|
Le dataFrame (etudids x champs)
|
||||||
reportant les moyennes des étudiants aux champs
|
reportant les moyennes des étudiants aux champs
|
||||||
"""
|
"""
|
||||||
|
# etudids_sorted: Les etudids des étudiants (diplômés) triés
|
||||||
|
# champs_sorted: Les champs (UE ou compétences) à faire apparaitre dans la matrice
|
||||||
|
|
||||||
# Partant d'un dataframe vierge
|
# Partant d'un dataframe vierge
|
||||||
df = pd.DataFrame(np.nan, index=etudids_sorted, columns=champs_sorted)
|
df = pd.DataFrame(np.nan, index=self.etudids_sorted, columns=self.champs_sorted)
|
||||||
|
|
||||||
for rcstag in self.rcstags.values():
|
for rcstag in self.rcstags.values():
|
||||||
# Charge les moyennes au tag d'un RCStag
|
# Charge les moyennes au tag d'un RCStag
|
||||||
if tag in rcstag.moyennes_tags:
|
if tag in rcstag.moyennes_tags:
|
||||||
moytag: pd.DataFrame = rcstag.moyennes_tags[tag].matrice_notes
|
moytag = rcstag.moyennes_tags[tag]
|
||||||
|
notes: pd.DataFrame = None
|
||||||
|
if pole == ModuleType.RESSOURCE:
|
||||||
|
notes = moytag.matrice_notes_res
|
||||||
|
elif pole == ModuleType.SAE:
|
||||||
|
notes = moytag.matrice_notes_saes
|
||||||
|
else:
|
||||||
|
notes = moytag.matrice_notes_gen # dataframe etudids x ues
|
||||||
|
|
||||||
# Etudiants/Champs communs entre le RCSTag et les données interclassées
|
# Etudiants/Champs communs entre le RCSTag et les données interclassées
|
||||||
(
|
(
|
||||||
etudids_communs,
|
etudids_communs,
|
||||||
champs_communs,
|
champs_communs,
|
||||||
) = pe_comp.find_index_and_columns_communs(df, moytag)
|
) = pe_comp.find_index_and_columns_communs(df, notes)
|
||||||
|
|
||||||
# Injecte les notes par tag
|
# Injecte les notes par tag
|
||||||
df.loc[etudids_communs, champs_communs] = moytag.loc[
|
df.loc[etudids_communs, champs_communs] = notes.loc[
|
||||||
etudids_communs, champs_communs
|
etudids_communs, champs_communs
|
||||||
]
|
]
|
||||||
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
def compute_coeffs_matrice(
|
def compute_coeffs_matrice(self, tag) -> pd.DataFrame:
|
||||||
self, tag, etudids_sorted: list[int], champs_sorted: list[str]
|
|
||||||
) -> pd.DataFrame:
|
|
||||||
"""Idem que compute_notes_matrices mais pour les coeffs
|
"""Idem que compute_notes_matrices mais pour les coeffs
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
etudids_sorted: Les etudids des étudiants (diplômés) triés
|
tag: Le tag visé
|
||||||
champs_sorted: Les champs (UE ou compétences) à faire apparaitre dans la matrice
|
|
||||||
Return:
|
Return:
|
||||||
Le dataFrame (etudids x champs)
|
Le dataFrame (etudids x champs)
|
||||||
reportant les moyennes des étudiants aux champs
|
reportant les moyennes des étudiants aux champs
|
||||||
"""
|
"""
|
||||||
|
# etudids_sorted: Les etudids des étudiants (diplômés) triés
|
||||||
|
# champs_sorted: Les champs (UE ou compétences) à faire apparaitre dans la matrice
|
||||||
|
|
||||||
# Partant d'un dataframe vierge
|
# Partant d'un dataframe vierge
|
||||||
df = pd.DataFrame(np.nan, index=etudids_sorted, columns=champs_sorted)
|
df = pd.DataFrame(np.nan, index=self.etudids_sorted, columns=self.champs_sorted)
|
||||||
|
|
||||||
for rcstag in self.rcstags.values():
|
for rcstag in self.rcstags.values():
|
||||||
if tag in rcstag.moyennes_tags:
|
if tag in rcstag.moyennes_tags:
|
||||||
@ -303,7 +291,7 @@ class InterClassTag(pe_tabletags.TableTag):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def compute_df_synthese_moyennes_tag(
|
def compute_df_synthese_moyennes_tag(
|
||||||
self, tag, aggregat=None, type_colonnes=False
|
self, tag, pole, aggregat=None, type_colonnes=False
|
||||||
) -> pd.DataFrame:
|
) -> pd.DataFrame:
|
||||||
"""Construit le dataframe retraçant pour les données des moyennes
|
"""Construit le dataframe retraçant pour les données des moyennes
|
||||||
pour affichage dans la synthèse du jury PE. (cf. to_df())
|
pour affichage dans la synthèse du jury PE. (cf. to_df())
|
||||||
@ -338,6 +326,7 @@ class InterClassTag(pe_tabletags.TableTag):
|
|||||||
if tag in rcstag.moyennes_tags:
|
if tag in rcstag.moyennes_tags:
|
||||||
moytag: pd.DataFrame = rcstag.moyennes_tags[tag]
|
moytag: pd.DataFrame = rcstag.moyennes_tags[tag]
|
||||||
df_moytag = moytag.to_df(
|
df_moytag = moytag.to_df(
|
||||||
|
pole,
|
||||||
aggregat=aggregat,
|
aggregat=aggregat,
|
||||||
cohorte="Groupe",
|
cohorte="Groupe",
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,7 @@ import pandas as pd
|
|||||||
from app import comp
|
from app import comp
|
||||||
from app.comp.moy_sem import comp_ranks_series
|
from app.comp.moy_sem import comp_ranks_series
|
||||||
from app.pe.moys import pe_moy
|
from app.pe.moys import pe_moy
|
||||||
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
CODE_MOY_UE = "UEs"
|
CODE_MOY_UE = "UEs"
|
||||||
CODE_MOY_COMPETENCES = "Compétences"
|
CODE_MOY_COMPETENCES = "Compétences"
|
||||||
@ -16,7 +16,9 @@ class MoyennesTag:
|
|||||||
self,
|
self,
|
||||||
tag: str,
|
tag: str,
|
||||||
type_moyenne: str,
|
type_moyenne: str,
|
||||||
matrice_notes: pd.DataFrame, # etudids x colonnes
|
matrice_notes_gen: pd.DataFrame, # etudids x colonnes
|
||||||
|
matrice_notes_res: pd.DataFrame,
|
||||||
|
matrice_notes_saes: pd.DataFrame,
|
||||||
matrice_coeffs: pd.DataFrame, # etudids x colonnes
|
matrice_coeffs: pd.DataFrame, # etudids x colonnes
|
||||||
):
|
):
|
||||||
"""Classe centralisant la synthèse des moyennes/classements d'une série
|
"""Classe centralisant la synthèse des moyennes/classements d'une série
|
||||||
@ -26,7 +28,11 @@ class MoyennesTag:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
tag: Un tag
|
tag: Un tag
|
||||||
matrice_notes: Les moyennes (etudid x acronymes_ues ou etudid x compétences) aux différentes UEs ou compétences
|
matrice_notes_gen: Les moyennes (etudid x acronymes_ues ou etudid x compétences)
|
||||||
|
aux différentes UEs ou compétences (indépendamment des ressources
|
||||||
|
ou SAEs)
|
||||||
|
matrice_notes_res: Les moyennes limitées aux ressources
|
||||||
|
matrice_notes_saes: Les moyennes limitées aux saes
|
||||||
matrice_coeffs: Les coeff à appliquer pour le calcul de la moyenne générale
|
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)
|
# notes_gen: Une série de notes (moyenne) sous forme d'un ``pd.Series`` (toutes UEs confondues)
|
||||||
"""
|
"""
|
||||||
@ -36,45 +42,94 @@ class MoyennesTag:
|
|||||||
self.type = type_moyenne
|
self.type = type_moyenne
|
||||||
"""Le type de moyennes (par UEs ou par compétences)"""
|
"""Le type de moyennes (par UEs ou par compétences)"""
|
||||||
|
|
||||||
# Les moyennes par UE
|
# Les moyennes par UE/compétences (ressources/SAEs confondues)
|
||||||
self.matrice_notes: pd.DataFrame = matrice_notes
|
self.matrice_notes_gen: pd.DataFrame = matrice_notes_gen
|
||||||
"""Les notes aux UEs ou aux compétences (DataFrame)"""
|
"""Les notes par UEs ou Compétences (DataFrame)"""
|
||||||
|
|
||||||
|
# Les moyennes par UE/compétences (limitées aux ressources)
|
||||||
|
self.matrice_notes_res: pd.DataFrame = matrice_notes_res
|
||||||
|
"""Les notes aux ressources par UEs ou Compétences"""
|
||||||
|
|
||||||
|
# Les moyennes par UE/compétences (limitées aux SAEs)
|
||||||
|
self.matrice_notes_saes: pd.DataFrame = matrice_notes_saes
|
||||||
|
"""Les notes aux SAEs par UEs ou Compétences"""
|
||||||
|
|
||||||
self.matrice_coeffs_moy_gen: pd.DataFrame = matrice_coeffs
|
self.matrice_coeffs_moy_gen: pd.DataFrame = matrice_coeffs
|
||||||
"""Les coeffs à appliquer pour le calcul des moyennes générales
|
"""Les coeffs à appliquer pour le calcul des moyennes générales
|
||||||
(toutes UE ou compétences confondues). NaN si étudiant non inscrit"""
|
(toutes UE ou compétences confondues). NaN si étudiant non inscrit"""
|
||||||
|
|
||||||
self.moyennes: dict[int, pd.DataFrame] = {}
|
self.moyennes_gen: dict[int, pd.DataFrame] = {}
|
||||||
"""Les dataframes retraçant les moyennes/classements/statistiques des étudiants aux UEs"""
|
"""Dataframes retraçant les moyennes/classements/statistiques des étudiants aux UEs"""
|
||||||
|
|
||||||
self.etudids = self.matrice_notes.index
|
self.moyennes_res: dict[int, pd.DataFrame] = {}
|
||||||
|
"""Dataframes retraçant les moyennes/classements/statistiques des étudiants, limitées aux ressources"""
|
||||||
|
|
||||||
|
self.moyennes_saes: dict[int, pd.DataFrame] = {}
|
||||||
|
"""Dataframes retraçant les moyennes/classements/statistiques des étudiants, limitées aux SAEs"""
|
||||||
|
|
||||||
|
self.etudids = self.matrice_notes_gen.index
|
||||||
"""Les étudids renseignés dans les moyennes"""
|
"""Les étudids renseignés dans les moyennes"""
|
||||||
|
|
||||||
self.champs = self.matrice_notes.columns
|
self.champs = self.matrice_notes_gen.columns
|
||||||
"""Les champs (acronymes d'UE ou compétences) renseignés dans les moyennes"""
|
"""Les champs (acronymes d'UE ou compétences) renseignés dans les moyennes"""
|
||||||
for col in self.champs: # if ue.type != UE_SPORT:
|
for col in self.champs: # if ue.type != UE_SPORT:
|
||||||
notes = matrice_notes[col]
|
# Les moyennes tous modules confondus
|
||||||
self.moyennes[col] = pe_moy.Moyenne(notes)
|
notes = matrice_notes_gen[col]
|
||||||
|
self.moyennes_gen[col] = pe_moy.Moyenne(notes)
|
||||||
|
# par ressources
|
||||||
|
notes = matrice_notes_res[col]
|
||||||
|
self.moyennes_res[col] = pe_moy.Moyenne(notes)
|
||||||
|
# par SAEs
|
||||||
|
notes = matrice_notes_saes[col]
|
||||||
|
self.moyennes_saes[col] = pe_moy.Moyenne(notes)
|
||||||
|
|
||||||
# Les moyennes générales
|
# Les moyennes générales (toutes UEs confondues)
|
||||||
notes_gen = pd.Series(np.nan, index=self.matrice_notes.index)
|
self.notes_gen = pd.Series(np.nan, index=self.matrice_notes_gen.index)
|
||||||
"""Les notes générales (moyenne toutes UEs confonudes)"""
|
if self.has_notes(pole=None):
|
||||||
if self.has_notes():
|
self.notes_gen = self.compute_moy_gen(
|
||||||
notes_gen = self.compute_moy_gen(
|
self.matrice_notes_gen, self.matrice_coeffs_moy_gen
|
||||||
self.matrice_notes, self.matrice_coeffs_moy_gen
|
|
||||||
)
|
)
|
||||||
self.notes_gen = notes_gen
|
self.moyenne_gen = pe_moy.Moyenne(self.notes_gen)
|
||||||
self.moyenne_gen = pe_moy.Moyenne(notes_gen)
|
"""Dataframe retraçant les moyennes/classements/statistiques général (toutes UESs confondues et modules confondus)"""
|
||||||
"""Le dataframe retraçant les moyennes/classements/statistiques général"""
|
|
||||||
|
|
||||||
def has_notes(self):
|
self.notes_res = pd.Series(np.nan, index=self.matrice_notes_res.index)
|
||||||
|
if self.has_notes(pole=ModuleType.RESSOURCE):
|
||||||
|
self.notes_res = self.compute_moy_gen(
|
||||||
|
self.matrice_notes_res, self.matrice_coeffs_moy_gen
|
||||||
|
)
|
||||||
|
self.moyenne_res = pe_moy.Moyenne(self.notes_res)
|
||||||
|
"""Dataframe retraçant les moyennes/classements/statistiques général (toutes UESs confondues et uniquement sur les ressources)"""
|
||||||
|
|
||||||
|
self.notes_saes = pd.Series(np.nan, index=self.matrice_notes_saes.index)
|
||||||
|
if self.has_notes(pole=ModuleType.SAE):
|
||||||
|
self.notes_saes = self.compute_moy_gen(
|
||||||
|
self.matrice_notes_saes, self.matrice_coeffs_moy_gen
|
||||||
|
)
|
||||||
|
self.moyenne_saes = pe_moy.Moyenne(self.notes_saes)
|
||||||
|
"""Dataframe retraçant les moyennes/classements/statistiques général (toutes UESs confondues et uniquement sur les SAEs)"""
|
||||||
|
|
||||||
|
def has_notes(self, pole):
|
||||||
"""Détermine si les moyennes (aux UEs ou aux compétences)
|
"""Détermine si les moyennes (aux UEs ou aux compétences)
|
||||||
ont des notes
|
ont des notes
|
||||||
|
|
||||||
|
`pole` détermine les modules pris en compte :
|
||||||
|
|
||||||
|
* si `pole` vaut `ModuleType.RESSOURCE`, seules les ressources sont prises
|
||||||
|
en compte (moyenne de ressources par UEs)
|
||||||
|
* si `pole` vaut `ModuleType.SAE`, seules les SAEs sont prises en compte
|
||||||
|
* si `pole` vaut `None` (ou toute autre valeur),
|
||||||
|
tous les modules sont pris en compte (moyenne d'UEs)
|
||||||
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True si la moytag a des notes, False sinon
|
True si la moytag a des notes, False sinon
|
||||||
"""
|
"""
|
||||||
notes = self.matrice_notes
|
if pole == ModuleType.RESSOURCE:
|
||||||
|
notes = self.matrice_notes_res
|
||||||
|
elif pole == ModuleType.SAE:
|
||||||
|
notes = self.matrice_notes_saes
|
||||||
|
else:
|
||||||
|
notes = self.matrice_notes_gen
|
||||||
nbre_nan = notes.isna().sum().sum()
|
nbre_nan = notes.isna().sum().sum()
|
||||||
nbre_notes_potentielles = len(notes.index) * len(notes.columns)
|
nbre_notes_potentielles = len(notes.index) * len(notes.columns)
|
||||||
if nbre_nan == nbre_notes_potentielles:
|
if nbre_nan == nbre_notes_potentielles:
|
||||||
@ -107,11 +162,14 @@ class MoyennesTag:
|
|||||||
|
|
||||||
return moy_gen_tag
|
return moy_gen_tag
|
||||||
|
|
||||||
def to_df(self, aggregat=None, cohorte=None) -> pd.DataFrame:
|
def to_df(self, pole, aggregat=None, cohorte=None) -> pd.DataFrame:
|
||||||
"""Renvoie le df synthétisant l'ensemble des données
|
"""Renvoie le df synthétisant l'ensemble des données
|
||||||
connues
|
connues
|
||||||
Adapte les intitulés des colonnes aux données fournies
|
Adapte les intitulés des colonnes aux données fournies
|
||||||
(nom d'aggrégat, type de cohorte).
|
(nom d'aggrégat, type de cohorte).
|
||||||
|
|
||||||
|
`pole` détermine les modules à prendre en compte dans la moyenne (None=tous,
|
||||||
|
RESSOURCES ou SAES)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
etudids_sorted = sorted(self.etudids)
|
etudids_sorted = sorted(self.etudids)
|
||||||
@ -121,19 +179,30 @@ class MoyennesTag:
|
|||||||
# Ajout des notes pour tous les champs
|
# Ajout des notes pour tous les champs
|
||||||
champs = list(self.champs)
|
champs = list(self.champs)
|
||||||
for champ in champs:
|
for champ in champs:
|
||||||
df_champ = self.moyennes[champ].get_df_synthese() # le dataframe
|
if pole == ModuleType.RESSOURCE:
|
||||||
|
df_champ = self.moyennes_res[champ].get_df_synthese()
|
||||||
|
elif pole == ModuleType.SAE:
|
||||||
|
df_champ = self.moyennes_saes[champ].get_df_synthese()
|
||||||
|
else:
|
||||||
|
df_champ = self.moyennes_gen[champ].get_df_synthese() # le dataframe
|
||||||
# Renomme les colonnes
|
# Renomme les colonnes
|
||||||
cols = [
|
cols = [
|
||||||
get_colonne_df(aggregat, self.tag, champ, cohorte, critere)
|
get_colonne_df(aggregat, pole, self.tag, champ, cohorte, critere)
|
||||||
for critere in pe_moy.Moyenne.COLONNES_SYNTHESE
|
for critere in pe_moy.Moyenne.COLONNES_SYNTHESE
|
||||||
]
|
]
|
||||||
df_champ.columns = cols
|
df_champ.columns = cols
|
||||||
df = df.join(df_champ)
|
df = df.join(df_champ)
|
||||||
|
|
||||||
# Ajoute la moy générale
|
# Ajoute la moy générale
|
||||||
|
df_moy_gen: pd.DataFrame = None
|
||||||
|
if pole == ModuleType.RESSOURCE:
|
||||||
|
df_moy_gen = self.moyenne_res.get_df_synthese()
|
||||||
|
elif pole == ModuleType.SAE:
|
||||||
|
df_moy_gen = self.moyenne_saes.get_df_synthese()
|
||||||
|
else:
|
||||||
df_moy_gen = self.moyenne_gen.get_df_synthese()
|
df_moy_gen = self.moyenne_gen.get_df_synthese()
|
||||||
cols = [
|
cols = [
|
||||||
get_colonne_df(aggregat, self.tag, CHAMP_GENERAL, cohorte, critere)
|
get_colonne_df(aggregat, pole, self.tag, CHAMP_GENERAL, cohorte, critere)
|
||||||
for critere in pe_moy.Moyenne.COLONNES_SYNTHESE
|
for critere in pe_moy.Moyenne.COLONNES_SYNTHESE
|
||||||
]
|
]
|
||||||
df_moy_gen.columns = cols
|
df_moy_gen.columns = cols
|
||||||
@ -142,12 +211,18 @@ class MoyennesTag:
|
|||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
def get_colonne_df(aggregat, tag, champ, cohorte, critere):
|
def get_colonne_df(aggregat, pole, tag, champ, cohorte, critere):
|
||||||
"""Renvoie le tuple (aggregat, tag, champ, cohorte, critere)
|
"""Renvoie le tuple (aggregat, tag, champ, cohorte, critere)
|
||||||
utilisé pour désigner les colonnes du df"""
|
utilisé pour désigner les colonnes du df"""
|
||||||
liste_champs = []
|
liste_champs = []
|
||||||
if aggregat != None:
|
if aggregat != None:
|
||||||
liste_champs += [aggregat]
|
liste_champs += [aggregat]
|
||||||
|
if pole == ModuleType.RESSOURCE:
|
||||||
|
liste_champs += ["ressources"]
|
||||||
|
elif pole == ModuleType.SAE:
|
||||||
|
liste_champs += ["saes"]
|
||||||
|
else:
|
||||||
|
liste_champs += ["global"]
|
||||||
liste_champs += [tag, champ]
|
liste_champs += [tag, champ]
|
||||||
if cohorte != None:
|
if cohorte != None:
|
||||||
liste_champs += [cohorte]
|
liste_champs += [cohorte]
|
||||||
|
@ -44,6 +44,7 @@ from app.pe.rcss import pe_rcs, pe_rcsemx
|
|||||||
import app.pe.moys.pe_sxtag as pe_sxtag
|
import app.pe.moys.pe_sxtag as pe_sxtag
|
||||||
import app.pe.pe_comp as pe_comp
|
import app.pe.pe_comp as pe_comp
|
||||||
from app.pe.moys import pe_tabletags, pe_moytag
|
from app.pe.moys import pe_tabletags, pe_moytag
|
||||||
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
|
|
||||||
class RCSemXTag(pe_tabletags.TableTag):
|
class RCSemXTag(pe_tabletags.TableTag):
|
||||||
@ -117,8 +118,8 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
set(self.acronymes_ues_to_competences.values())
|
set(self.acronymes_ues_to_competences.values())
|
||||||
)
|
)
|
||||||
"""Compétences (triées par nom, extraites des SxTag aggrégés)"""
|
"""Compétences (triées par nom, extraites des SxTag aggrégés)"""
|
||||||
self._aff_comp_et_ues_debug()
|
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 : {', '.join(self.competences_sorted)}")
|
||||||
|
|
||||||
# Les tags
|
# Les tags
|
||||||
self.tags_sorted = self._do_taglist()
|
self.tags_sorted = self._do_taglist()
|
||||||
@ -137,43 +138,45 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
# ******************************************
|
# ******************************************
|
||||||
# Cube d'inscription (etudids_sorted x compétences_sorted x sxstags)
|
# Cube d'inscription (etudids_sorted x compétences_sorted x sxstags)
|
||||||
# indiquant quel sxtag est valide pour chaque étudiant
|
# indiquant quel sxtag est valide pour chaque étudiant
|
||||||
inscriptions_df, inscriptions_cube = self.compute_inscriptions_comps_cube(
|
inscr_df, inscr_cube = self.compute_inscriptions_comps_cube(tag)
|
||||||
tag, self.etudids_sorted, self.competences_sorted, self.sxstags_aggreges
|
|
||||||
)
|
|
||||||
|
|
||||||
# Traitement des notes
|
# Traitement des notes
|
||||||
# ********************
|
# ********************
|
||||||
|
### Moyennes tous modules confondus
|
||||||
# Cube de notes (etudids_sorted x compétences_sorted x sxstags)
|
# Cube de notes (etudids_sorted x compétences_sorted x sxstags)
|
||||||
notes_df, notes_cube = self.compute_notes_comps_cube(
|
notes_df, notes_cube = self.compute_notes_comps_cube(tag, mode=None)
|
||||||
tag, self.etudids_sorted, self.competences_sorted, self.sxstags_aggreges
|
|
||||||
)
|
|
||||||
# Calcule les moyennes sous forme d'un dataframe en les "aggrégant"
|
# Calcule les moyennes sous forme d'un dataframe en les "aggrégant"
|
||||||
# compétence par compétence
|
# compétence par compétence
|
||||||
moys_competences = compute_notes_competences(
|
moys_competences = self.compute_notes_competences(notes_cube, inscr_cube)
|
||||||
notes_cube,
|
|
||||||
inscriptions_cube,
|
## Moyennes des ressources
|
||||||
self.etudids_sorted,
|
notes_df_res, notes_cube_res = self.compute_notes_comps_cube(
|
||||||
self.competences_sorted,
|
tag, mode=ModuleType.RESSOURCE
|
||||||
|
)
|
||||||
|
moys_competences_res = self.compute_notes_competences(
|
||||||
|
notes_cube_res, inscr_cube
|
||||||
|
)
|
||||||
|
|
||||||
|
## Moyennes des SAEs
|
||||||
|
notes_df_sae, notes_cube_sae = self.compute_notes_comps_cube(
|
||||||
|
tag, mode=ModuleType.SAE
|
||||||
|
)
|
||||||
|
moys_competences_saes = self.compute_notes_competences(
|
||||||
|
notes_cube_sae, inscr_cube
|
||||||
)
|
)
|
||||||
|
|
||||||
# Traitement des coeffs pour la moyenne générale
|
# Traitement des coeffs pour la moyenne générale
|
||||||
# ***********************************************
|
# ***********************************************
|
||||||
# Df des coeffs sur tous les SxTags aggrégés
|
# Df des coeffs sur tous les SxTags aggrégés
|
||||||
coeffs_df, coeffs_cube = self.compute_coeffs_comps_cube(
|
coeffs_df, coeffs_cube = self.compute_coeffs_comps_cube(tag)
|
||||||
tag,
|
|
||||||
self.etudids_sorted,
|
|
||||||
self.competences_sorted,
|
|
||||||
self.sxstags_aggreges,
|
|
||||||
)
|
|
||||||
# Synthèse des coefficients à prendre en compte pour la moyenne générale
|
# Synthèse des coefficients à prendre en compte pour la moyenne générale
|
||||||
matrice_coeffs_moy_gen = compute_coeffs_competences(
|
matrice_coeffs_moy_gen = self.compute_coeffs_competences(
|
||||||
coeffs_cube,
|
coeffs_cube, inscr_cube, notes_cube
|
||||||
inscriptions_cube,
|
|
||||||
notes_cube,
|
|
||||||
self.etudids_sorted,
|
|
||||||
self.competences_sorted,
|
|
||||||
)
|
)
|
||||||
aff = pe_affichage.aff_profil_coeffs(
|
|
||||||
|
# Affichage des coeffs
|
||||||
|
aff = pe_affichage.repr_profil_coeffs(
|
||||||
matrice_coeffs_moy_gen, with_index=True
|
matrice_coeffs_moy_gen, with_index=True
|
||||||
)
|
)
|
||||||
pe_affichage.pe_print(f" > Moyenne calculée avec pour coeffs : {aff}")
|
pe_affichage.pe_print(f" > Moyenne calculée avec pour coeffs : {aff}")
|
||||||
@ -183,6 +186,8 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
tag,
|
tag,
|
||||||
pe_moytag.CODE_MOY_COMPETENCES,
|
pe_moytag.CODE_MOY_COMPETENCES,
|
||||||
moys_competences,
|
moys_competences,
|
||||||
|
moys_competences_res,
|
||||||
|
moys_competences_saes,
|
||||||
matrice_coeffs_moy_gen,
|
matrice_coeffs_moy_gen,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -200,35 +205,42 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
else:
|
else:
|
||||||
return f"{self.__class__.__name__} {self.rcs_id}"
|
return f"{self.__class__.__name__} {self.rcs_id}"
|
||||||
|
|
||||||
def compute_notes_comps_cube(
|
def compute_notes_comps_cube(self, tag, mode=None):
|
||||||
self,
|
|
||||||
tag,
|
|
||||||
etudids_sorted: list[int],
|
|
||||||
competences_sorted: list[str],
|
|
||||||
sxstags: dict[(str, int) : pe_sxtag.SxTag],
|
|
||||||
):
|
|
||||||
"""Pour un tag donné, construit le cube de notes (etudid x competences x SxTag)
|
"""Pour un tag donné, construit le cube de notes (etudid x competences x SxTag)
|
||||||
nécessaire au calcul des moyennes,
|
nécessaire au calcul des moyennes,
|
||||||
en remplaçant les données d'UE (obtenus du SxTag) par les compétences
|
en remplaçant les données d'UE (obtenus du SxTag) par les compétences
|
||||||
|
|
||||||
|
`mode` détermine les modules pris en compte :
|
||||||
|
|
||||||
|
* si `mode` vaut `ModuleType.RESSOURCE`, seules les ressources sont prises
|
||||||
|
en compte (moyenne de ressources par UEs)
|
||||||
|
* si `mode` vaut `ModuleType.SAE`, seules les SAEs sont prises en compte
|
||||||
|
* si `mode` vaut `None` (ou toute autre valeur),
|
||||||
|
tous les modules sont pris en compte (moyenne d'UEs)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tag: Le tag visé
|
tag: Le tag visé
|
||||||
etudids_sorted: Les etudis triés (dim 0)
|
|
||||||
competences_sorted: Les compétences triées (dim 1)
|
|
||||||
sxstags: Les SxTag à réunir
|
|
||||||
"""
|
"""
|
||||||
|
# etudids_sorted: list[int],
|
||||||
|
# competences_sorted: list[str],
|
||||||
|
# sxstags: dict[(str, int) : pe_sxtag.SxTag],
|
||||||
notes_dfs = {}
|
notes_dfs = {}
|
||||||
|
|
||||||
for sxtag_id, sxtag in sxstags.items():
|
for sxtag_id, sxtag in self.sxstags_aggreges.items():
|
||||||
# Partant d'un dataframe vierge
|
# Partant d'un dataframe vierge
|
||||||
notes_df = pd.DataFrame(
|
notes_df = pd.DataFrame(
|
||||||
np.nan, index=etudids_sorted, columns=competences_sorted
|
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
|
||||||
)
|
)
|
||||||
# Charge les notes du semestre tag (copie car changement de nom de colonnes à venir)
|
# Charge les notes 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
|
if tag in sxtag.moyennes_tags: # si le tag est présent dans le semestre
|
||||||
moys_tag = sxtag.moyennes_tags[tag]
|
moys_tag = sxtag.moyennes_tags[tag]
|
||||||
|
|
||||||
notes = moys_tag.matrice_notes.copy() # avec une copie
|
if mode == ModuleType.RESSOURCE:
|
||||||
|
notes = moys_tag.matrice_notes_res.copy() # avec une copie
|
||||||
|
elif mode == ModuleType.SAE:
|
||||||
|
notes = moys_tag.matrice_notes_saes.copy()
|
||||||
|
else:
|
||||||
|
notes = moys_tag.matrice_notes_gen.copy() # dataframe etudids x ues
|
||||||
|
|
||||||
# Traduction des acronymes d'UE en compétences
|
# Traduction des acronymes d'UE en compétences
|
||||||
acronymes_ues_columns = notes.columns
|
acronymes_ues_columns = notes.columns
|
||||||
@ -257,18 +269,14 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
notes_dfs[sxtag_id] = notes_df
|
notes_dfs[sxtag_id] = notes_df
|
||||||
|
|
||||||
"""Réunit les notes sous forme d'un cube etudids x competences x semestres"""
|
"""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 sxstags]
|
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)
|
notes_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
|
||||||
|
|
||||||
return notes_dfs, notes_etudids_x_comps_x_sxtag
|
return notes_dfs, notes_etudids_x_comps_x_sxtag
|
||||||
|
|
||||||
def compute_coeffs_comps_cube(
|
def compute_coeffs_comps_cube(self, tag):
|
||||||
self,
|
|
||||||
tag,
|
|
||||||
etudids_sorted: list[int],
|
|
||||||
competences_sorted: list[str],
|
|
||||||
sxstags: dict[(str, int) : pe_sxtag.SxTag],
|
|
||||||
):
|
|
||||||
"""Pour un tag donné, construit
|
"""Pour un tag donné, construit
|
||||||
le cube de coeffs (etudid x competences x SxTag) (traduisant les inscriptions
|
le cube de coeffs (etudid x competences x SxTag) (traduisant les inscriptions
|
||||||
des étudiants aux UEs en fonction de leur parcours)
|
des étudiants aux UEs en fonction de leur parcours)
|
||||||
@ -277,16 +285,17 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
tag: Le tag visé
|
tag: Le tag visé
|
||||||
etudids_sorted: Les etudis triés
|
|
||||||
competences_sorted: Les compétences triées
|
|
||||||
sxstags: Les SxTag à réunir
|
|
||||||
"""
|
"""
|
||||||
|
# etudids_sorted: list[int],
|
||||||
|
# competences_sorted: list[str],
|
||||||
|
# sxstags: dict[(str, int) : pe_sxtag.SxTag],
|
||||||
|
|
||||||
coeffs_dfs = {}
|
coeffs_dfs = {}
|
||||||
|
|
||||||
for sxtag_id, sxtag in sxstags.items():
|
for sxtag_id, sxtag in self.sxstags_aggreges.items():
|
||||||
# Partant d'un dataframe vierge
|
# Partant d'un dataframe vierge
|
||||||
coeffs_df = pd.DataFrame(
|
coeffs_df = pd.DataFrame(
|
||||||
np.nan, index=etudids_sorted, columns=competences_sorted
|
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
|
||||||
)
|
)
|
||||||
if tag in sxtag.moyennes_tags:
|
if tag in sxtag.moyennes_tags:
|
||||||
moys_tag = sxtag.moyennes_tags[tag]
|
moys_tag = sxtag.moyennes_tags[tag]
|
||||||
@ -316,7 +325,9 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
coeffs_dfs[sxtag_id] = coeffs_df
|
coeffs_dfs[sxtag_id] = coeffs_df
|
||||||
|
|
||||||
"""Réunit les coeffs sous forme d'un cube etudids x competences x semestres"""
|
"""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 sxstags]
|
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)
|
coeffs_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
|
||||||
|
|
||||||
return coeffs_dfs, coeffs_etudids_x_comps_x_sxtag
|
return coeffs_dfs, coeffs_etudids_x_comps_x_sxtag
|
||||||
@ -324,9 +335,6 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
def compute_inscriptions_comps_cube(
|
def compute_inscriptions_comps_cube(
|
||||||
self,
|
self,
|
||||||
tag,
|
tag,
|
||||||
etudids_sorted: list[int],
|
|
||||||
competences_sorted: list[str],
|
|
||||||
sxstags: dict[(str, int) : pe_sxtag.SxTag],
|
|
||||||
):
|
):
|
||||||
"""Pour un tag donné, construit
|
"""Pour un tag donné, construit
|
||||||
le cube etudid x competences x SxTag traduisant quels sxtags est à prendre
|
le cube etudid x competences x SxTag traduisant quels sxtags est à prendre
|
||||||
@ -335,24 +343,24 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
tag: Le tag visé
|
tag: Le tag visé
|
||||||
etudids_sorted: Les etudis triés
|
|
||||||
competences_sorted: Les compétences triées
|
|
||||||
sxstags: Les SxTag à réunir
|
|
||||||
"""
|
"""
|
||||||
|
# etudids_sorted: list[int],
|
||||||
|
# competences_sorted: list[str],
|
||||||
|
# sxstags: dict[(str, int) : pe_sxtag.SxTag],
|
||||||
# Initialisation
|
# Initialisation
|
||||||
inscriptions_dfs = {}
|
inscriptions_dfs = {}
|
||||||
|
|
||||||
for sxtag_id, sxtag in sxstags.items():
|
for sxtag_id, sxtag in self.sxstags_aggreges.items():
|
||||||
# Partant d'un dataframe vierge
|
# Partant d'un dataframe vierge
|
||||||
inscription_df = pd.DataFrame(
|
inscription_df = pd.DataFrame(
|
||||||
0, index=etudids_sorted, columns=competences_sorted
|
0, index=self.etudids_sorted, columns=self.competences_sorted
|
||||||
)
|
)
|
||||||
|
|
||||||
# Les étudiants dont les résultats au sxtag ont été calculés
|
# Les étudiants dont les résultats au sxtag ont été calculés
|
||||||
etudids_sxtag = sxtag.etudids_sorted
|
etudids_sxtag = sxtag.etudids_sorted
|
||||||
|
|
||||||
# Les étudiants communs
|
# Les étudiants communs
|
||||||
etudids_communs = sorted(set(etudids_sorted) & set(etudids_sxtag))
|
etudids_communs = sorted(set(self.etudids_sorted) & set(etudids_sxtag))
|
||||||
|
|
||||||
# Acte l'inscription
|
# Acte l'inscription
|
||||||
inscription_df.loc[etudids_communs, :] = 1
|
inscription_df.loc[etudids_communs, :] = 1
|
||||||
@ -361,7 +369,9 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
inscriptions_dfs[sxtag_id] = inscription_df
|
inscriptions_dfs[sxtag_id] = inscription_df
|
||||||
|
|
||||||
"""Réunit les inscriptions sous forme d'un cube etudids x competences x semestres"""
|
"""Réunit les inscriptions sous forme d'un cube etudids x competences x semestres"""
|
||||||
sxtag_x_etudids_x_comps = [inscriptions_dfs[sxtag_id] for sxtag_id in sxstags]
|
sxtag_x_etudids_x_comps = [
|
||||||
|
inscriptions_dfs[sxtag_id] for sxtag_id in self.sxstags_aggreges
|
||||||
|
]
|
||||||
inscriptions_etudids_x_comps_x_sxtag = np.stack(
|
inscriptions_etudids_x_comps_x_sxtag = np.stack(
|
||||||
sxtag_x_etudids_x_comps, axis=-1
|
sxtag_x_etudids_x_comps, axis=-1
|
||||||
)
|
)
|
||||||
@ -392,26 +402,57 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
dict_competences |= sxtag.acronymes_ues_to_competences
|
dict_competences |= sxtag.acronymes_ues_to_competences
|
||||||
return dict_competences
|
return dict_competences
|
||||||
|
|
||||||
def _aff_comp_et_ues_debug(self):
|
def compute_notes_competences(self, set_cube: np.array, inscriptions: np.array):
|
||||||
"""Affichage pour debug"""
|
"""Calcule la moyenne par compétences (à un tag donné) sur plusieurs semestres (partant du set_cube).
|
||||||
aff_comp = []
|
|
||||||
|
|
||||||
for comp in self.competences_sorted:
|
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles
|
||||||
liste = []
|
|
||||||
for acro in self.acronymes_ues_to_competences:
|
|
||||||
if self.acronymes_ues_to_competences[acro] == comp:
|
|
||||||
liste += ["📍" + acro]
|
|
||||||
aff_comp += [f" 💡{comp} (⇔ {', '.join(liste)})"]
|
|
||||||
pe_affichage.pe_print(f"--> Compétences :")
|
|
||||||
pe_affichage.pe_print("\n".join(aff_comp))
|
|
||||||
|
|
||||||
|
*Remarque* : Adaptation de moy_ue.compute_ue_moys_apc au cas des moyennes de tag
|
||||||
|
par aggrégat de plusieurs semestres.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
set_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
|
||||||
|
(etuds x UEs|compétences x sxtags), des 0 et des 1
|
||||||
|
Returns:
|
||||||
|
Un DataFrame avec pour columns les moyennes par tags,
|
||||||
|
et pour rows les etudid
|
||||||
|
"""
|
||||||
|
# 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
|
||||||
|
# assert nb_etuds == len(etudids_sorted)
|
||||||
|
# assert nb_comps == len(competences_sorted)
|
||||||
|
|
||||||
|
# Applique le masque d'inscriptions
|
||||||
|
set_cube_significatif = set_cube * inscriptions
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# Le dataFrame des notes moyennes
|
||||||
|
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(
|
def compute_coeffs_competences(
|
||||||
|
self,
|
||||||
coeff_cube: np.array,
|
coeff_cube: np.array,
|
||||||
inscriptions: np.array,
|
inscriptions: np.array,
|
||||||
set_cube: np.array,
|
set_cube: np.array,
|
||||||
etudids_sorted: list,
|
|
||||||
competences_sorted: list,
|
|
||||||
):
|
):
|
||||||
"""Calcule les coeffs à utiliser pour la moyenne générale (toutes compétences
|
"""Calcule les coeffs à utiliser pour la moyenne générale (toutes compétences
|
||||||
confondues), en fonction des inscriptions.
|
confondues), en fonction des inscriptions.
|
||||||
@ -421,15 +462,16 @@ def compute_coeffs_competences(
|
|||||||
inscriptions: inscriptions aux UES|Compétences ndarray
|
inscriptions: inscriptions aux UES|Compétences ndarray
|
||||||
(etuds x UEs|compétences x sxtags), des 0 ou des 1
|
(etuds x UEs|compétences x sxtags), des 0 ou des 1
|
||||||
set_cube: les notes
|
set_cube: les notes
|
||||||
etudids_sorted: liste des étudiants (dim. 0 du cube)
|
|
||||||
competences_sorted: list (dim. 1 du cube)
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Un DataFrame de coefficients (etudids_sorted x compétences_sorted)
|
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
|
nb_etuds, nb_comps, nb_semestres = inscriptions.shape
|
||||||
assert nb_etuds == len(etudids_sorted)
|
# assert nb_etuds == len(etudids_sorted)
|
||||||
assert nb_comps == len(competences_sorted)
|
# assert nb_comps == len(competences_sorted)
|
||||||
|
|
||||||
# Applique le masque des inscriptions aux coeffs et aux notes
|
# Applique le masque des inscriptions aux coeffs et aux notes
|
||||||
coeffs_significatifs = coeff_cube * inscriptions
|
coeffs_significatifs = coeff_cube * inscriptions
|
||||||
@ -448,61 +490,9 @@ def compute_coeffs_competences(
|
|||||||
|
|
||||||
# Le dataFrame des coeffs
|
# Le dataFrame des coeffs
|
||||||
coeffs_df = pd.DataFrame(
|
coeffs_df = pd.DataFrame(
|
||||||
coeff_tag, index=etudids_sorted, columns=competences_sorted
|
coeff_tag, index=self.etudids_sorted, columns=self.competences_sorted
|
||||||
)
|
)
|
||||||
# Remet à Nan les coeffs à 0
|
# Remet à Nan les coeffs à 0
|
||||||
coeffs_df = coeffs_df.fillna(np.nan)
|
coeffs_df = coeffs_df.fillna(np.nan)
|
||||||
|
|
||||||
return coeffs_df
|
return coeffs_df
|
||||||
|
|
||||||
|
|
||||||
def compute_notes_competences(
|
|
||||||
set_cube: np.array,
|
|
||||||
inscriptions: np.array,
|
|
||||||
etudids_sorted: list,
|
|
||||||
competences_sorted: list,
|
|
||||||
):
|
|
||||||
"""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
|
|
||||||
|
|
||||||
*Remarque* : Adaptation de moy_ue.compute_ue_moys_apc au cas des moyennes de tag
|
|
||||||
par aggrégat de plusieurs semestres.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
set_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
|
|
||||||
(etuds x UEs|compétences x sxtags), des 0 et des 1
|
|
||||||
etudids_sorted: liste des étudiants (dim. 0 du cube)
|
|
||||||
competences_sorted: list (dim. 1 du cube)
|
|
||||||
Returns:
|
|
||||||
Un DataFrame avec pour columns les moyennes par tags,
|
|
||||||
et pour rows les etudid
|
|
||||||
"""
|
|
||||||
nb_etuds, nb_comps, nb_semestres = set_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
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Le dataFrame des notes moyennes
|
|
||||||
etud_moy_tag_df = pd.DataFrame(
|
|
||||||
etud_moy_tag,
|
|
||||||
index=etudids_sorted, # les etudids
|
|
||||||
columns=competences_sorted, # les competences
|
|
||||||
)
|
|
||||||
etud_moy_tag_df.fillna(np.nan)
|
|
||||||
|
|
||||||
return etud_moy_tag_df
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- mode: python -*-
|
# -*- pole: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -46,6 +46,7 @@ import app.pe.pe_etudiant as pe_etudiant
|
|||||||
from app.pe.moys import pe_tabletags, pe_moytag
|
from app.pe.moys import pe_tabletags, pe_moytag
|
||||||
from app.scodoc import sco_tag_module
|
from app.scodoc import sco_tag_module
|
||||||
from app.scodoc import codes_cursus as sco_codes
|
from app.scodoc import codes_cursus as sco_codes
|
||||||
|
from app.scodoc.sco_utils import *
|
||||||
|
|
||||||
|
|
||||||
class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||||
@ -79,9 +80,20 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
]
|
]
|
||||||
"""Liste des UEs standards du ResultatsSemestreBUT"""
|
"""Liste des UEs standards du ResultatsSemestreBUT"""
|
||||||
|
|
||||||
|
# Les parcours des étudiants à ce semestre
|
||||||
|
self.parcours = []
|
||||||
|
"""Parcours auxquels sont inscrits les étudiants"""
|
||||||
|
for etudid in self.etudids_sorted:
|
||||||
|
parcour = self.formsemestre.etuds_inscriptions[etudid].parcour
|
||||||
|
if parcour:
|
||||||
|
self.parcours += [parcour.libelle]
|
||||||
|
else:
|
||||||
|
self.parcours += [None]
|
||||||
|
|
||||||
# Les UEs en fonction des parcours
|
# Les UEs en fonction des parcours
|
||||||
self.ues_inscr_parcours_df = self.load_ues_inscr_parcours()
|
self.ues_inscr_parcours_df = self.load_ues_inscr_parcours()
|
||||||
"""Les inscriptions des étudiants aux UEs du parcours"""
|
"""Inscription des étudiants aux UEs des parcours"""
|
||||||
|
|
||||||
# Les acronymes des UEs
|
# Les acronymes des UEs
|
||||||
self.ues_to_acronymes = {ue.id: ue.acronyme for ue in self.ues_standards}
|
self.ues_to_acronymes = {ue.id: ue.acronyme for ue in self.ues_standards}
|
||||||
self.acronymes_sorted = sorted(self.ues_to_acronymes.values())
|
self.acronymes_sorted = sorted(self.ues_to_acronymes.values())
|
||||||
@ -89,7 +101,7 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
|
|
||||||
# Les compétences associées aux UEs (définies par les acronymes)
|
# Les compétences associées aux UEs (définies par les acronymes)
|
||||||
self.acronymes_ues_to_competences = {}
|
self.acronymes_ues_to_competences = {}
|
||||||
"""L'association acronyme d'UEs -> compétence"""
|
"""Association acronyme d'UEs -> compétence"""
|
||||||
for ue in self.ues_standards:
|
for ue in self.ues_standards:
|
||||||
assert ue.niveau_competence, ScoValueError(
|
assert ue.niveau_competence, ScoValueError(
|
||||||
"Des UEs ne sont pas rattachées à des compétences"
|
"Des UEs ne sont pas rattachées à des compétences"
|
||||||
@ -99,12 +111,15 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
self.competences_sorted = sorted(
|
self.competences_sorted = sorted(
|
||||||
list(set(self.acronymes_ues_to_competences.values()))
|
list(set(self.acronymes_ues_to_competences.values()))
|
||||||
)
|
)
|
||||||
"""Les compétences triées par nom"""
|
"""Compétences triées par nom"""
|
||||||
self._aff_ue_et_comp_debug()
|
aff = pe_affichage.repr_asso_ue_comp(self.acronymes_ues_to_competences)
|
||||||
|
pe_affichage.pe_print(f"--> UEs/Compétences : {aff}")
|
||||||
|
|
||||||
# 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._aff_tags_debug(tags_dict)
|
pe_affichage.pe_print(
|
||||||
|
f"""--> {pe_affichage.aff_tags_par_categories(tags_dict)}"""
|
||||||
|
)
|
||||||
self._check_tags(tags_dict)
|
self._check_tags(tags_dict)
|
||||||
|
|
||||||
# Les coefficients pour le calcul de la moyenne générale, donnés par
|
# Les coefficients pour le calcul de la moyenne générale, donnés par
|
||||||
@ -113,7 +128,10 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
self.ues_inscr_parcours_df, self.ues_standards
|
self.ues_inscr_parcours_df, self.ues_standards
|
||||||
)
|
)
|
||||||
"""DataFrame indiquant les coeffs des UEs par ordre alphabétique d'acronyme"""
|
"""DataFrame indiquant les coeffs des UEs par ordre alphabétique d'acronyme"""
|
||||||
self.__aff_profil_coeffs()
|
profils_aff = pe_affichage.repr_profil_coeffs(self.matrice_coeffs_moy_gen)
|
||||||
|
pe_affichage.pe_print(
|
||||||
|
f"--> Moyenne générale calculée avec pour coeffs d'UEs : {profils_aff}"
|
||||||
|
)
|
||||||
|
|
||||||
# Les capitalisations (mask etuids x acronyme_ue valant True si capitalisée, False sinon)
|
# Les capitalisations (mask etuids x acronyme_ue valant True si capitalisée, False sinon)
|
||||||
self.capitalisations = self._get_capitalisations(self.ues_standards)
|
self.capitalisations = self._get_capitalisations(self.ues_standards)
|
||||||
@ -121,24 +139,44 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
|
|
||||||
# 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 = {}
|
||||||
"""Les moyennes par tags (personnalisés ou 'but')"""
|
"""Moyennes par tags (personnalisés ou 'but')"""
|
||||||
for tag in tags_dict["personnalises"]:
|
for tag in tags_dict["personnalises"]:
|
||||||
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
||||||
infos_tag = tags_dict["personnalises"][tag]
|
info_tag = tags_dict["personnalises"][tag]
|
||||||
moy_ues_tag = self.compute_moy_ues_tag(infos_tag)
|
# Les moyennes générales par UEs
|
||||||
|
moy_ues_tag = self.compute_moy_ues_tag(info_tag=info_tag, pole=None)
|
||||||
|
# Les moyennes par ressources de chaque UE
|
||||||
|
moy_res_tag = self.compute_moy_ues_tag(
|
||||||
|
info_tag=info_tag, pole=ModuleType.RESSOURCE
|
||||||
|
)
|
||||||
|
# Les moyennes par SAEs de chaque UE
|
||||||
|
moy_saes_tag = self.compute_moy_ues_tag(
|
||||||
|
info_tag=info_tag, pole=ModuleType.SAE
|
||||||
|
)
|
||||||
self.moyennes_tags[tag] = pe_moytag.MoyennesTag(
|
self.moyennes_tags[tag] = pe_moytag.MoyennesTag(
|
||||||
tag, pe_moytag.CODE_MOY_UE, moy_ues_tag, self.matrice_coeffs_moy_gen
|
tag,
|
||||||
|
pe_moytag.CODE_MOY_UE,
|
||||||
|
moy_ues_tag,
|
||||||
|
moy_res_tag,
|
||||||
|
moy_saes_tag,
|
||||||
|
self.matrice_coeffs_moy_gen,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ajoute les moyennes par UEs + la moyenne générale (but)
|
# Ajoute les moyennes par UEs + la moyenne générale (but)
|
||||||
moy_gen = self.compute_moy_gen()
|
moy_gen = self.compute_moy_gen()
|
||||||
|
moy_res_gen = self.compute_moy_ues_tag(info_tag=None, pole=ModuleType.RESSOURCE)
|
||||||
|
moy_saes_gen = self.compute_moy_ues_tag(info_tag=None, pole=ModuleType.SAE)
|
||||||
|
|
||||||
self.moyennes_tags["but"] = pe_moytag.MoyennesTag(
|
self.moyennes_tags["but"] = pe_moytag.MoyennesTag(
|
||||||
"but",
|
"but",
|
||||||
pe_moytag.CODE_MOY_UE,
|
pe_moytag.CODE_MOY_UE,
|
||||||
moy_gen,
|
moy_gen,
|
||||||
self.matrice_coeffs_moy_gen, # , moy_gen_but
|
moy_res_gen,
|
||||||
|
moy_saes_gen,
|
||||||
|
self.matrice_coeffs_moy_gen,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Tous les tags
|
||||||
self.tags_sorted = self.get_all_significant_tags()
|
self.tags_sorted = self.get_all_significant_tags()
|
||||||
"""Tags (personnalisés+compétences) par ordre alphabétique"""
|
"""Tags (personnalisés+compétences) par ordre alphabétique"""
|
||||||
|
|
||||||
@ -206,30 +244,55 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
capitalisations = capitalisations.sort_index(axis=1)
|
capitalisations = capitalisations.sort_index(axis=1)
|
||||||
return capitalisations
|
return capitalisations
|
||||||
|
|
||||||
def compute_moy_ues_tag(self, info_tag: dict[int, dict]) -> pd.DataFrame:
|
def compute_moy_ues_tag(
|
||||||
"""Calcule la moyenne par UE des étudiants pour un tag,
|
self, info_tag: dict[int, dict] = None, pole=None
|
||||||
|
) -> pd.DataFrame:
|
||||||
|
"""Calcule la moyenne par UE des étudiants pour un tag donné,
|
||||||
en ayant connaissance des informations sur le tag.
|
en ayant connaissance des informations sur le tag.
|
||||||
|
|
||||||
|
info_tag détermine les modules pris en compte :
|
||||||
|
* si non `None`, seuls les modules rattachés au tag sont pris en compte
|
||||||
|
* si `None`, tous les modules (quelque soit leur rattachement au tag) sont pris
|
||||||
|
en compte (sert au calcul de la moyenne générale par ressource ou SAE)
|
||||||
|
|
||||||
|
`pole` détermine les modules pris en compte :
|
||||||
|
|
||||||
|
* si `pole` vaut `ModuleType.RESSOURCE`, seules les ressources sont prises
|
||||||
|
en compte (moyenne de ressources par UEs)
|
||||||
|
* si `pole` vaut `ModuleType.SAE`, seules les SAEs sont prises en compte
|
||||||
|
* si `pole` vaut `None` (ou toute autre valeur),
|
||||||
|
tous les modules sont pris en compte (moyenne d'UEs)
|
||||||
|
|
||||||
|
|
||||||
Les informations sur le tag sont un dictionnaire listant les modimpl_id rattachés au tag,
|
Les informations sur le tag sont un dictionnaire listant les modimpl_id rattachés au tag,
|
||||||
et pour chacun leur éventuel coefficient de **repondération**.
|
et pour chacun leur éventuel coefficient de **repondération**.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Le dataframe des moyennes du tag par UE
|
Le dataframe des moyennes du tag par UE
|
||||||
"""
|
"""
|
||||||
|
modimpls_sorted = self.formsemestre.modimpls_sorted
|
||||||
|
|
||||||
# Adaptation du mask de calcul des moyennes au tag visé
|
# Adaptation du mask de calcul des moyennes au tag visé
|
||||||
modimpls_mask = [
|
modimpls_mask = []
|
||||||
modimpl.module.ue.type == sco_codes.UE_STANDARD
|
for modimpl in modimpls_sorted:
|
||||||
for modimpl in self.formsemestre.modimpls_sorted
|
module = modimpl.module # Le module
|
||||||
]
|
mask = module.ue.type == sco_codes.UE_STANDARD # Est-ce une UE stantard ?
|
||||||
|
if pole == ModuleType.RESSOURCE:
|
||||||
|
mask &= module.module_type == ModuleType.RESSOURCE
|
||||||
|
elif pole == ModuleType.SAE:
|
||||||
|
mask &= module.module_type == ModuleType.SAE
|
||||||
|
modimpls_mask += [mask]
|
||||||
|
|
||||||
|
# Prise en compte du tag
|
||||||
|
if info_tag:
|
||||||
# Désactive tous les modules qui ne sont pas pris en compte pour ce tag
|
# Désactive tous les modules qui ne sont pas pris en compte pour ce tag
|
||||||
for i, modimpl in enumerate(self.formsemestre.modimpls_sorted):
|
for i, modimpl in enumerate(modimpls_sorted):
|
||||||
if modimpl.moduleimpl_id not in info_tag:
|
if modimpl.moduleimpl_id not in info_tag:
|
||||||
modimpls_mask[i] = False
|
modimpls_mask[i] = False
|
||||||
|
|
||||||
# Applique la pondération des coefficients
|
# Applique la pondération des coefficients
|
||||||
modimpl_coefs_ponderes_df = self.modimpl_coefs_df.copy()
|
modimpl_coefs_ponderes_df = self.modimpl_coefs_df.copy()
|
||||||
|
if info_tag:
|
||||||
for modimpl_id in info_tag:
|
for modimpl_id in info_tag:
|
||||||
ponderation = info_tag[modimpl_id]["ponderation"]
|
ponderation = info_tag[modimpl_id]["ponderation"]
|
||||||
modimpl_coefs_ponderes_df[modimpl_id] *= ponderation
|
modimpl_coefs_ponderes_df[modimpl_id] *= ponderation
|
||||||
@ -304,24 +367,6 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
dict_tags["auto"] = {"but": {}}
|
dict_tags["auto"] = {"but": {}}
|
||||||
return dict_tags
|
return dict_tags
|
||||||
|
|
||||||
def _aff_ue_et_comp_debug(self):
|
|
||||||
"""Affichage pour debug"""
|
|
||||||
aff_comp = []
|
|
||||||
for acro in self.acronymes_sorted:
|
|
||||||
aff_comp += [f"📍{acro} (∈ 💡{self.acronymes_ues_to_competences[acro]})"]
|
|
||||||
pe_affichage.pe_print(f"--> UEs/Compétences : {', '.join(aff_comp)}")
|
|
||||||
|
|
||||||
def _aff_tags_debug(self, dict_tags):
|
|
||||||
"""Affichage pour debug"""
|
|
||||||
noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys())))
|
|
||||||
noms_tags_auto = sorted(list(set(dict_tags["auto"].keys()))) # + noms_tags_comp
|
|
||||||
aff_tags_auto = ", ".join([f"👜{nom}" for nom in noms_tags_auto])
|
|
||||||
aff_tags_perso = ", ".join([f"👜{nom}" for nom in noms_tags_perso])
|
|
||||||
# Affichage
|
|
||||||
pe_affichage.pe_print(
|
|
||||||
f"""--> Tags du programme de formation : {aff_tags_perso} + Automatiques : {aff_tags_auto}"""
|
|
||||||
)
|
|
||||||
|
|
||||||
def _check_tags(self, dict_tags):
|
def _check_tags(self, dict_tags):
|
||||||
"""Vérifie l'unicité des tags"""
|
"""Vérifie l'unicité des tags"""
|
||||||
noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys())))
|
noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys())))
|
||||||
@ -347,29 +392,6 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
"""
|
"""
|
||||||
raise ScoValueError(message)
|
raise ScoValueError(message)
|
||||||
|
|
||||||
def __aff_profil_coeffs(self):
|
|
||||||
"""Extrait de la matrice des coeffs, les différents types d'inscription
|
|
||||||
et de coefficients (appelés profil) des étudiants et les affiche
|
|
||||||
(pour debug)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Les profils des coeffs d'UE (pour debug)
|
|
||||||
profils = []
|
|
||||||
for i in self.matrice_coeffs_moy_gen.index:
|
|
||||||
val = self.matrice_coeffs_moy_gen.loc[i].fillna("-")
|
|
||||||
val = " | ".join([str(v) for v in val])
|
|
||||||
if val not in profils:
|
|
||||||
profils += [val]
|
|
||||||
|
|
||||||
# L'affichage
|
|
||||||
if len(profils) > 1:
|
|
||||||
profils_aff = "\n" + "\n".join([" " * 10 + prof for prof in profils])
|
|
||||||
else:
|
|
||||||
profils_aff = "\n".join(profils)
|
|
||||||
pe_affichage.pe_print(
|
|
||||||
f"--> Moyenne générale calculée avec pour coeffs d'UEs : {profils_aff}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
|
def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
|
||||||
"""Etant données les implémentations des modules du semestre (modimpls),
|
"""Etant données les implémentations des modules du semestre (modimpls),
|
||||||
@ -414,12 +436,7 @@ def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
|
|||||||
# Ajout du module (modimpl) au tagname considéré
|
# Ajout du module (modimpl) au tagname considéré
|
||||||
synthese_tags[tagname][modimpl_id] = {
|
synthese_tags[tagname][modimpl_id] = {
|
||||||
"modimpl": modimpl, # les données sur le module
|
"modimpl": modimpl, # les données sur le module
|
||||||
# "coeff": modimpl.module.coefficient, # le coeff du module dans le semestre
|
|
||||||
"ponderation": ponderation, # la pondération demandée pour le tag sur le module
|
"ponderation": ponderation, # la pondération demandée pour le tag sur le module
|
||||||
# "module_code": modimpl.module.code, # le code qui doit se retrouver à l'identique dans des ue capitalisee
|
|
||||||
# "ue_id": modimpl.module.ue.id, # les données sur l'ue
|
|
||||||
# "ue_code": modimpl.module.ue.ue_code,
|
|
||||||
# "ue_acronyme": modimpl.module.ue.acronyme,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return synthese_tags
|
return synthese_tags
|
||||||
|
@ -43,6 +43,7 @@ import numpy as np
|
|||||||
|
|
||||||
from app.pe.moys import pe_moytag, pe_tabletags
|
from app.pe.moys import pe_moytag, pe_tabletags
|
||||||
import app.pe.rcss.pe_trajectoires as pe_trajectoires
|
import app.pe.rcss.pe_trajectoires as pe_trajectoires
|
||||||
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
|
|
||||||
class SxTag(pe_tabletags.TableTag):
|
class SxTag(pe_tabletags.TableTag):
|
||||||
@ -91,7 +92,8 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
and isinstance(self.sxtag_id[1], int)
|
and isinstance(self.sxtag_id[1], int)
|
||||||
), "Format de l'identifiant du SxTag non respecté"
|
), "Format de l'identifiant du SxTag non respecté"
|
||||||
|
|
||||||
self.nom_rcs = sxtag_id[0]
|
self.agregat = sxtag_id[0]
|
||||||
|
"""Nom de l'aggrégat du RCS"""
|
||||||
|
|
||||||
self.semx = semx
|
self.semx = semx
|
||||||
"""Le SemX sur lequel il s'appuie"""
|
"""Le SemX sur lequel il s'appuie"""
|
||||||
@ -121,7 +123,7 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
# Les tags
|
# Les tags
|
||||||
self.tags_sorted = self.ressembuttag_final.tags_sorted
|
self.tags_sorted = self.ressembuttag_final.tags_sorted
|
||||||
"""Tags (extraits du ReSemBUTTag final)"""
|
"""Tags (extraits du ReSemBUTTag final)"""
|
||||||
aff_tag = ["👜" + tag for tag in self.tags_sorted]
|
aff_tag = pe_affichage.repr_tags(self.tags_sorted)
|
||||||
pe_affichage.pe_print(f"--> Tags : {', '.join(aff_tag)}")
|
pe_affichage.pe_print(f"--> Tags : {', '.join(aff_tag)}")
|
||||||
|
|
||||||
# Les UE données par leur acronyme
|
# Les UE données par leur acronyme
|
||||||
@ -135,13 +137,18 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
"""L'association acronyme d'UEs -> compétence"""
|
"""L'association acronyme d'UEs -> compétence"""
|
||||||
self.competences_sorted = sorted(self.acronymes_ues_to_competences.values())
|
self.competences_sorted = sorted(self.acronymes_ues_to_competences.values())
|
||||||
"""Les compétences triées par nom"""
|
"""Les compétences triées par nom"""
|
||||||
self._aff_ue_et_comp_debug()
|
|
||||||
|
aff = pe_affichage.repr_asso_ue_comp(self.acronymes_ues_to_competences)
|
||||||
|
pe_affichage.pe_print(f"--> UEs/Compétences : {aff}")
|
||||||
|
|
||||||
# Les coeffs pour la moyenne générale (traduisant également l'inscription
|
# Les coeffs pour la moyenne générale (traduisant également l'inscription
|
||||||
# des étudiants aux UEs) (etudids_sorted x acronymes_ues_sorted)
|
# des étudiants aux UEs) (etudids_sorted x acronymes_ues_sorted)
|
||||||
self.matrice_coeffs_moy_gen = self.ressembuttag_final.matrice_coeffs_moy_gen
|
self.matrice_coeffs_moy_gen = self.ressembuttag_final.matrice_coeffs_moy_gen
|
||||||
"""La matrice des coeffs pour la moyenne générale"""
|
"""La matrice des coeffs pour la moyenne générale"""
|
||||||
self.__aff_profil_coeffs()
|
aff = pe_affichage.repr_profil_coeffs(self.matrice_coeffs_moy_gen)
|
||||||
|
pe_affichage.pe_print(
|
||||||
|
f"--> Moyenne générale calculée avec pour coeffs d'UEs : {aff}"
|
||||||
|
)
|
||||||
|
|
||||||
# Masque des inscriptions et des capitalisations
|
# Masque des inscriptions et des capitalisations
|
||||||
self.masque_df = None
|
self.masque_df = None
|
||||||
@ -152,83 +159,85 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
self.ressembuttags,
|
self.ressembuttags,
|
||||||
self.fid_final,
|
self.fid_final,
|
||||||
)
|
)
|
||||||
self._aff_capitalisations()
|
pe_affichage.aff_capitalisations(
|
||||||
|
self.etuds,
|
||||||
|
self.ressembuttags,
|
||||||
|
self.fid_final,
|
||||||
|
self.acronymes_sorted,
|
||||||
|
self.masque_df,
|
||||||
|
)
|
||||||
|
|
||||||
# Les moyennes par tag
|
# Les moyennes par tag
|
||||||
self.moyennes_tags: dict[str, pd.DataFrame] = {}
|
self.moyennes_tags: dict[str, pd.DataFrame] = {}
|
||||||
"""Moyennes aux UEs (identifiées par leur acronyme) des différents tags"""
|
"""Moyennes aux UEs (identifiées par leur acronyme) des différents tags"""
|
||||||
|
|
||||||
if self.tags_sorted:
|
if self.tags_sorted:
|
||||||
pe_affichage.pe_print("--> Calcul des moyennes par tags :")
|
pe_affichage.pe_print("--> Calcul des moyennes par tags :")
|
||||||
|
|
||||||
for tag in self.tags_sorted:
|
for tag in self.tags_sorted:
|
||||||
# Y-a-t-il des notes ?
|
pe_affichage.pe_print(f" > MoyTag 👜{tag}")
|
||||||
if not self.has_notes(tag):
|
|
||||||
pe_affichage.pe_print(f" > MoyTag 👜{tag} actuellement sans notes")
|
|
||||||
matrice_moys_ues = pd.DataFrame(
|
|
||||||
np.nan, index=self.etudids_sorted, columns=self.acronymes_sorted
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Cube de note etudids x UEs
|
|
||||||
notes_df, notes_cube = compute_notes_ues_cube(
|
|
||||||
tag,
|
|
||||||
self.etudids_sorted,
|
|
||||||
self.acronymes_sorted,
|
|
||||||
self.ressembuttags,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Masque des inscriptions aux UEs (extraits de la matrice de coefficients)
|
# 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())
|
||||||
|
|
||||||
# Matrice des moyennes
|
# Moyennes (tous modules confondus)
|
||||||
matrice_moys_ues: pd.DataFrame = compute_notes_ues(
|
if not self.has_notes_tag(tag):
|
||||||
notes_cube,
|
pe_affichage.pe_print(
|
||||||
masque_cube,
|
f" --> Semestre (final) actuellement sans notes"
|
||||||
self.etudids_sorted,
|
)
|
||||||
self.acronymes_sorted,
|
matrice_moys_ues = pd.DataFrame(
|
||||||
inscr_mask,
|
np.nan, index=self.etudids_sorted, columns=self.acronymes_sorted
|
||||||
|
)
|
||||||
|
matrice_moys_res = pd.DataFrame(
|
||||||
|
np.nan, index=self.etudids_sorted, columns=self.acronymes_sorted
|
||||||
|
)
|
||||||
|
matrice_moys_saes = pd.DataFrame(
|
||||||
|
np.nan, index=self.etudids_sorted, columns=self.acronymes_sorted
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Moyennes tous modules confondus
|
||||||
|
### Cube de note etudids x UEs tous modules confondus
|
||||||
|
notes_df_gen, notes_cube_gen = self.compute_notes_ues_cube(
|
||||||
|
tag, mode=None
|
||||||
|
)
|
||||||
|
# DataFrame des moyennes (tous modules confondus)
|
||||||
|
matrice_moys_ues = self.compute_notes_ues(
|
||||||
|
notes_cube_gen, masque_cube, inscr_mask
|
||||||
)
|
)
|
||||||
|
|
||||||
# Affichage de debug
|
### Moyennes par ressources
|
||||||
aff = pe_affichage.aff_profil_coeffs(
|
notes_df_res, notes_cube_res = self.compute_notes_ues_cube(
|
||||||
self.matrice_coeffs_moy_gen, with_index=True
|
tag, mode=ModuleType.RESSOURCE
|
||||||
|
)
|
||||||
|
matrice_moys_res = self.compute_notes_ues(
|
||||||
|
notes_cube_res, masque_cube, inscr_mask
|
||||||
|
)
|
||||||
|
|
||||||
|
### Moyennes par SAEs
|
||||||
|
notes_df_saes, notes_cube_saes = self.compute_notes_ues_cube(
|
||||||
|
tag, mode=ModuleType.SAE
|
||||||
|
)
|
||||||
|
matrice_moys_saes = self.compute_notes_ues(
|
||||||
|
notes_cube_saes, masque_cube, inscr_mask
|
||||||
)
|
)
|
||||||
pe_affichage.pe_print(f" > MoyTag 👜{tag} : {aff}")
|
|
||||||
|
|
||||||
# Mémorise les infos pour la moyennes au tag
|
# Mémorise les infos pour la moyennes au tag
|
||||||
self.moyennes_tags[tag] = pe_moytag.MoyennesTag(
|
self.moyennes_tags[tag] = pe_moytag.MoyennesTag(
|
||||||
tag,
|
tag,
|
||||||
pe_moytag.CODE_MOY_UE,
|
pe_moytag.CODE_MOY_UE,
|
||||||
matrice_moys_ues,
|
matrice_moys_ues,
|
||||||
|
matrice_moys_res,
|
||||||
|
matrice_moys_saes,
|
||||||
self.matrice_coeffs_moy_gen,
|
self.matrice_coeffs_moy_gen,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __aff_profil_coeff_ects(self, tag):
|
# Affichage de debug
|
||||||
"""Extrait de la matrice des coeffs, les différents types d'inscription
|
aff = pe_affichage.repr_profil_coeffs(
|
||||||
et de coefficients (appelés profil) des étudiants et les affiche
|
self.matrice_coeffs_moy_gen, with_index=True
|
||||||
(pour debug)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Les profils des coeffs d'UE (pour debug)
|
|
||||||
profils = []
|
|
||||||
for i in self.matrice_coeffs_moy_gen.index:
|
|
||||||
val = self.matrice_coeffs_moy_gen.loc[i].fillna("-")
|
|
||||||
val = " | ".join([str(v) for v in val])
|
|
||||||
if val not in profils:
|
|
||||||
profils += [val]
|
|
||||||
|
|
||||||
# L'affichage
|
|
||||||
if len(profils) > 1:
|
|
||||||
profils_aff = "\n" + "\n".join([" " * 10 + prof for prof in profils])
|
|
||||||
else:
|
|
||||||
profils_aff = "\n".join(profils)
|
|
||||||
|
|
||||||
# L'affichage
|
|
||||||
ues = ", ".join(self.acronymes_sorted)
|
|
||||||
pe_affichage.pe_print(
|
|
||||||
f" > MoyTag 👜{tag} pour UEs : {ues} avec pour coeffs : {profils_aff}"
|
|
||||||
)
|
)
|
||||||
|
pe_affichage.pe_print(f" > Moyenne générale calculée avec : {aff}")
|
||||||
|
|
||||||
def has_notes(self, tag):
|
def has_notes_tag(self, tag):
|
||||||
"""Détermine si le SxTag, pour un tag donné, est en cours d'évaluation.
|
"""Détermine si le SxTag, pour un tag donné, est en cours d'évaluation.
|
||||||
Si oui, n'a pas (encore) de notes dans le resformsemestre final.
|
Si oui, n'a pas (encore) de notes dans le resformsemestre final.
|
||||||
|
|
||||||
@ -239,13 +248,7 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
True si a des notes, False sinon
|
True si a des notes, False sinon
|
||||||
"""
|
"""
|
||||||
moy_tag_dernier_sem = self.ressembuttag_final.moyennes_tags[tag]
|
moy_tag_dernier_sem = self.ressembuttag_final.moyennes_tags[tag]
|
||||||
notes = moy_tag_dernier_sem.matrice_notes
|
return moy_tag_dernier_sem.has_notes(None)
|
||||||
nbre_nan = notes.isna().sum().sum()
|
|
||||||
nbre_notes_potentielles = len(notes.index) * len(notes.columns)
|
|
||||||
if nbre_nan == nbre_notes_potentielles:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
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"""
|
||||||
@ -258,82 +261,46 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
return f"SXTag basé sur {self.semx.get_repr()}"
|
return f"SXTag basé sur {self.semx.get_repr()}"
|
||||||
else:
|
else:
|
||||||
# affichage = [str(fid) for fid in self.ressembuttags]
|
# affichage = [str(fid) for fid in self.ressembuttags]
|
||||||
return f"SXTag {self.nom_rcs}#{self.fid_final}"
|
return f"SXTag {self.agregat}#{self.fid_final}"
|
||||||
|
|
||||||
def _aff_ue_et_comp_debug(self):
|
def compute_notes_ues_cube(self, tag, mode=None) -> (pd.DataFrame, np.array):
|
||||||
"""Affichage pour debug"""
|
|
||||||
aff_comp = []
|
|
||||||
for acro in self.acronymes_sorted:
|
|
||||||
aff_comp += [f"📍{acro} (∈ 💡{self.acronymes_ues_to_competences[acro]})"]
|
|
||||||
pe_affichage.pe_print(f"--> UEs/Compétences : {', '.join(aff_comp)}")
|
|
||||||
|
|
||||||
def _aff_capitalisations(self):
|
|
||||||
"""Affichage des capitalisations du sxtag pour debug"""
|
|
||||||
aff_cap = []
|
|
||||||
for etud in self.etuds:
|
|
||||||
cap = []
|
|
||||||
for frmsem_id in self.ressembuttags:
|
|
||||||
if frmsem_id != self.fid_final:
|
|
||||||
for accr in self.acronymes_sorted:
|
|
||||||
if self.masque_df[frmsem_id].loc[etud.etudid, accr] > 0.0:
|
|
||||||
cap += [accr]
|
|
||||||
if cap:
|
|
||||||
aff_cap += [f" > {etud.nomprenom} : {', '.join(cap)}"]
|
|
||||||
if aff_cap:
|
|
||||||
pe_affichage.pe_print(f"--> ⚠️ Capitalisations :")
|
|
||||||
pe_affichage.pe_print("\n".join(aff_cap))
|
|
||||||
|
|
||||||
def __aff_profil_coeffs(self):
|
|
||||||
"""Extrait de la matrice des coeffs, les différents types d'inscription
|
|
||||||
et de coefficients (appelés profil) des étudiants et les affiche
|
|
||||||
(pour debug)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Les profils des coeffs d'UE (pour debug)
|
|
||||||
profils = []
|
|
||||||
for i in self.matrice_coeffs_moy_gen.index:
|
|
||||||
val = self.matrice_coeffs_moy_gen.loc[i].fillna("-")
|
|
||||||
val = " | ".join([str(v) for v in val])
|
|
||||||
if val not in profils:
|
|
||||||
profils += [val]
|
|
||||||
|
|
||||||
# L'affichage
|
|
||||||
if len(profils) > 1:
|
|
||||||
profils_aff = "\n" + "\n".join([" " * 10 + prof for prof in profils])
|
|
||||||
else:
|
|
||||||
profils_aff = "\n".join(profils)
|
|
||||||
pe_affichage.pe_print(
|
|
||||||
f"--> Moyenne générale calculée avec pour coeffs d'UEs : {profils_aff}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def compute_notes_ues_cube(
|
|
||||||
tag, etudids_sorted, acronymes_sorted, ressembuttags
|
|
||||||
) -> (pd.DataFrame, np.array):
|
|
||||||
"""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é)
|
||||||
nécessaire au calcul des moyennes du tag pour le RCS Sx.
|
nécessaire au calcul des moyennes du tag pour le RCS Sx.
|
||||||
(Renvoie également le dataframe associé pour debug).
|
(Renvoie également le dataframe associé pour debug).
|
||||||
|
|
||||||
|
`mode` détermine les modules pris en compte :
|
||||||
|
|
||||||
|
* si `mode` vaut `ModuleType.RESSOURCE`, seules les ressources sont prises
|
||||||
|
en compte (moyenne de ressources par UEs)
|
||||||
|
* si `mode` vaut `ModuleType.SAE`, seules les SAEs sont prises en compte
|
||||||
|
* si `mode` vaut `None` (ou toute autre valeur),
|
||||||
|
tous les modules sont pris en compte (moyenne d'UEs)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
etudids_sorted: La liste des etudids triés par ordre croissant (dim 0)
|
tag: Le tag considéré (personalisé ou "but")
|
||||||
acronymes_sorted: La liste des acronymes de UEs triés par acronyme croissant (dim 1)
|
|
||||||
ressembuttags: Le dictionnaire des résultats de semestres BUT (tous tags confondus)
|
|
||||||
"""
|
"""
|
||||||
# Index du cube (etudids -> dim 0, ues -> dim 1, semestres -> dim2)
|
# Index du cube (etudids -> dim 0, ues -> dim 1, semestres -> dim2)
|
||||||
# etudids_sorted = etudids_sorted
|
# etudids_sorted = etudids_sorted
|
||||||
# acronymes_ues = sorted([ue.acronyme for ue in selMf.ues.values()])
|
# acronymes_ues = sorted([ue.acronyme for ue in selMf.ues.values()])
|
||||||
semestres_id = list(ressembuttags.keys())
|
semestres_id = list(self.ressembuttags.keys())
|
||||||
|
|
||||||
dfs = {}
|
dfs = {}
|
||||||
|
|
||||||
for frmsem_id in semestres_id:
|
for frmsem_id in semestres_id:
|
||||||
# Partant d'un dataframe vierge
|
# Partant d'un dataframe vierge
|
||||||
df = pd.DataFrame(np.nan, index=etudids_sorted, columns=acronymes_sorted)
|
df = pd.DataFrame(
|
||||||
|
np.nan, index=self.etudids_sorted, columns=self.acronymes_sorted
|
||||||
|
)
|
||||||
|
|
||||||
# Charge les notes du semestre tag
|
# Charge les notes du semestre tag
|
||||||
sem_tag = 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.matrice_notes # dataframe etudids x ues
|
if mode == ModuleType.RESSOURCE:
|
||||||
|
notes = moys_tag.matrice_notes_res
|
||||||
|
elif mode == ModuleType.SAE:
|
||||||
|
notes = moys_tag.matrice_notes_saes
|
||||||
|
else:
|
||||||
|
notes = moys_tag.matrice_notes_gen # dataframe etudids x ues
|
||||||
|
|
||||||
# les étudiants et les acronymes communs
|
# les étudiants et les acronymes communs
|
||||||
etudids_communs, acronymes_communs = pe_comp.find_index_and_columns_communs(
|
etudids_communs, acronymes_communs = pe_comp.find_index_and_columns_communs(
|
||||||
@ -357,6 +324,66 @@ def compute_notes_ues_cube(
|
|||||||
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 dfs, etudids_x_ues_x_semestres
|
return dfs, etudids_x_ues_x_semestres
|
||||||
|
|
||||||
|
def compute_notes_ues(
|
||||||
|
self,
|
||||||
|
set_cube: np.array,
|
||||||
|
masque_cube: np.array,
|
||||||
|
inscr_mask: np.array,
|
||||||
|
) -> pd.DataFrame:
|
||||||
|
"""Calcule la moyenne par UEs à un tag donné en prenant la note maximum (UE
|
||||||
|
par UE) obtenue par un étudiant à un semestre.
|
||||||
|
|
||||||
|
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
|
||||||
|
(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)
|
||||||
|
Returns:
|
||||||
|
Un DataFrame avec pour columns les moyennes par ues,
|
||||||
|
et pour rows les etudid
|
||||||
|
"""
|
||||||
|
# etudids_sorted: liste des étudiants (dim. 0 du cube) trié par etudid
|
||||||
|
# acronymes_sorted: liste des acronymes des ues (dim. 1 du cube) trié par acronyme
|
||||||
|
nb_etuds, nb_ues, nb_semestres = set_cube.shape
|
||||||
|
nb_etuds_mask, nb_ues_mask = inscr_mask.shape
|
||||||
|
# assert nb_etuds == len(self.etudids_sorted)
|
||||||
|
# assert nb_ues == len(self.acronymes_sorted)
|
||||||
|
# assert nb_etuds == nb_etuds_mask
|
||||||
|
# assert nb_ues == nb_ues_mask
|
||||||
|
|
||||||
|
# Entrées à garder dans le cube en fonction du masque d'inscription aux UEs du parcours
|
||||||
|
inscr_mask_3D = np.stack([inscr_mask] * nb_semestres, axis=-1)
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# Les moyennes par ues
|
||||||
|
# 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
|
||||||
|
etud_moy[etud_moy < 0] = np.NaN
|
||||||
|
|
||||||
|
# Le dataFrame
|
||||||
|
etud_moy_tag_df = pd.DataFrame(
|
||||||
|
etud_moy,
|
||||||
|
index=self.etudids_sorted, # les etudids
|
||||||
|
columns=self.acronymes_sorted, # les acronymes d'UEs
|
||||||
|
)
|
||||||
|
|
||||||
|
etud_moy_tag_df = etud_moy_tag_df.fillna(np.nan)
|
||||||
|
|
||||||
|
return etud_moy_tag_df
|
||||||
|
|
||||||
|
|
||||||
def compute_masques_capitalisation_cube(
|
def compute_masques_capitalisation_cube(
|
||||||
etudids_sorted: list[int],
|
etudids_sorted: list[int],
|
||||||
@ -415,65 +442,3 @@ def compute_masques_capitalisation_cube(
|
|||||||
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 dfs, etudids_x_ues_x_semestres
|
return dfs, etudids_x_ues_x_semestres
|
||||||
|
|
||||||
|
|
||||||
def compute_notes_ues(
|
|
||||||
set_cube: np.array,
|
|
||||||
masque_cube: np.array,
|
|
||||||
etudids_sorted: list,
|
|
||||||
acronymes_sorted: list,
|
|
||||||
inscr_mask: np.array,
|
|
||||||
):
|
|
||||||
"""Calcule la moyenne par UEs à un tag donné en prenant la note maximum (UE
|
|
||||||
par UE) obtenue par un étudiant à un semestre.
|
|
||||||
|
|
||||||
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
|
|
||||||
(semestre_ids x etudids x UEs), des 1.0 ou des 0.0
|
|
||||||
etudids_sorted: liste des étudiants (dim. 0 du cube) trié par etudid
|
|
||||||
acronymes_sorted: liste des acronymes des ues (dim. 1 du cube) trié par acronyme
|
|
||||||
inscr_mask: masque etudids x UE traduisant les inscriptions des
|
|
||||||
étudiants aux UE (du semestre terminal)
|
|
||||||
Returns:
|
|
||||||
Un DataFrame avec pour columns les moyennes par ues,
|
|
||||||
et pour rows les etudid
|
|
||||||
"""
|
|
||||||
nb_etuds, nb_ues, nb_semestres = set_cube.shape
|
|
||||||
nb_etuds_mask, nb_ues_mask = inscr_mask.shape
|
|
||||||
assert nb_etuds == len(etudids_sorted)
|
|
||||||
assert nb_ues == len(acronymes_sorted)
|
|
||||||
assert nb_etuds == nb_etuds_mask
|
|
||||||
assert nb_ues == nb_ues_mask
|
|
||||||
|
|
||||||
# Entrées à garder dans le cube en fonction du masque d'inscription aux UEs du parcours
|
|
||||||
inscr_mask_3D = np.stack([inscr_mask] * nb_semestres, axis=-1)
|
|
||||||
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)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Les moyennes par ues
|
|
||||||
# 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
|
|
||||||
etud_moy[etud_moy < 0] = np.NaN
|
|
||||||
|
|
||||||
# Le dataFrame
|
|
||||||
etud_moy_tag_df = pd.DataFrame(
|
|
||||||
etud_moy,
|
|
||||||
index=etudids_sorted, # les etudids
|
|
||||||
columns=acronymes_sorted, # les acronymes d'UEs
|
|
||||||
)
|
|
||||||
|
|
||||||
etud_moy_tag_df = etud_moy_tag_df.fillna(np.nan)
|
|
||||||
|
|
||||||
return etud_moy_tag_df
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- mode: python -*-
|
# -*- pole: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -79,12 +79,17 @@ class TableTag(object):
|
|||||||
tag: str = ""
|
tag: str = ""
|
||||||
moytag: pe_moytag.MoyennesTag = None
|
moytag: pe_moytag.MoyennesTag = None
|
||||||
for tag, moytag in self.moyennes_tags.items():
|
for tag, moytag in self.moyennes_tags.items():
|
||||||
if moytag.has_notes():
|
if moytag.has_notes(None):
|
||||||
tags.append(tag)
|
tags.append(tag)
|
||||||
return sorted(tags)
|
return sorted(tags)
|
||||||
|
|
||||||
def to_df(
|
def to_df(
|
||||||
self, administratif=True, aggregat=None, tags_cibles=None, cohorte=None
|
self,
|
||||||
|
pole,
|
||||||
|
administratif=True,
|
||||||
|
aggregat=None,
|
||||||
|
tags_cibles=None,
|
||||||
|
cohorte=None,
|
||||||
) -> pd.DataFrame:
|
) -> pd.DataFrame:
|
||||||
"""Renvoie un dataframe listant toutes les données
|
"""Renvoie un dataframe listant toutes les données
|
||||||
des moyennes/classements/nb_inscrits/min/max/moy
|
des moyennes/classements/nb_inscrits/min/max/moy
|
||||||
@ -98,6 +103,7 @@ class TableTag(object):
|
|||||||
aggregat: l'aggrégat représenté
|
aggregat: l'aggrégat représenté
|
||||||
tags_cibles: la liste des tags ciblés
|
tags_cibles: la liste des tags ciblés
|
||||||
cohorte: la cohorte représentée
|
cohorte: la cohorte représentée
|
||||||
|
pole: Les modules à prendre en compte dans la moyenne (None=tous, Ressources ou SAEs)
|
||||||
Returns:
|
Returns:
|
||||||
Le dataframe complet de synthèse
|
Le dataframe complet de synthèse
|
||||||
"""
|
"""
|
||||||
@ -114,20 +120,22 @@ class TableTag(object):
|
|||||||
|
|
||||||
# Les étudiants visés
|
# Les étudiants visés
|
||||||
if administratif:
|
if administratif:
|
||||||
df = df_administratif(self.etuds, aggregat, cohorte)
|
df = df_administratif(self.etuds, aggregat=aggregat, cohorte=cohorte)
|
||||||
else:
|
else:
|
||||||
df = pd.DataFrame(index=self.etudids)
|
df = pd.DataFrame(index=self.etudids)
|
||||||
|
|
||||||
# Ajout des données par tags
|
# Ajout des données par tags
|
||||||
for tag in tags_cibles:
|
for tag in tags_cibles:
|
||||||
if tag in self.moyennes_tags:
|
if tag in self.moyennes_tags:
|
||||||
moy_tag_df = self.moyennes_tags[tag].to_df(aggregat, cohorte)
|
moy_tag_df = self.moyennes_tags[tag].to_df(
|
||||||
|
pole, aggregat=aggregat, cohorte=cohorte
|
||||||
|
)
|
||||||
df = df.join(moy_tag_df)
|
df = df.join(moy_tag_df)
|
||||||
|
|
||||||
# Tri par nom, prénom
|
# Tri par nom, prénom
|
||||||
if administratif:
|
if administratif:
|
||||||
colonnes_tries = [
|
colonnes_tries = [
|
||||||
_get_champ_administratif(champ, aggregat, cohorte)
|
_get_champ_administratif(champ, aggregat=aggregat, cohorte=cohorte)
|
||||||
for champ in CHAMPS_ADMINISTRATIFS[1:]
|
for champ in CHAMPS_ADMINISTRATIFS[1:]
|
||||||
] # Nom + Prénom
|
] # Nom + Prénom
|
||||||
df = df.sort_values(by=colonnes_tries)
|
df = df.sort_values(by=colonnes_tries)
|
||||||
@ -155,6 +163,7 @@ def _get_champ_administratif(champ, aggregat=None, cohorte=None):
|
|||||||
liste = []
|
liste = []
|
||||||
if aggregat != None:
|
if aggregat != None:
|
||||||
liste += [aggregat]
|
liste += [aggregat]
|
||||||
|
liste += [""] # le pole (None, RESSOURCES, SAEs)
|
||||||
liste += ["Administratif", "Identité"]
|
liste += ["Administratif", "Identité"]
|
||||||
if cohorte != None:
|
if cohorte != None:
|
||||||
liste += [champ]
|
liste += [champ]
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
|
|
||||||
from flask import g
|
from flask import g
|
||||||
from app import log
|
from app import log
|
||||||
|
from app.pe.rcss import pe_rcs
|
||||||
|
|
||||||
PE_DEBUG = False
|
PE_DEBUG = True
|
||||||
|
|
||||||
|
|
||||||
# On stocke les logs PE dans g.scodoc_pe_log
|
# On stocke les logs PE dans g.scodoc_pe_log
|
||||||
@ -20,7 +21,7 @@ def pe_start_log() -> list[str]:
|
|||||||
return g.scodoc_pe_log
|
return g.scodoc_pe_log
|
||||||
|
|
||||||
|
|
||||||
def pe_print(*a):
|
def pe_print(*a, **cles):
|
||||||
"Log (or print in PE_DEBUG mode) and store in g"
|
"Log (or print in PE_DEBUG mode) and store in g"
|
||||||
if PE_DEBUG:
|
if PE_DEBUG:
|
||||||
msg = " ".join(a)
|
msg = " ".join(a)
|
||||||
@ -31,6 +32,7 @@ def pe_print(*a):
|
|||||||
lines = pe_start_log()
|
lines = pe_start_log()
|
||||||
msg = " ".join(a)
|
msg = " ".join(a)
|
||||||
lines.append(msg)
|
lines.append(msg)
|
||||||
|
if "info" in cles:
|
||||||
log(msg)
|
log(msg)
|
||||||
|
|
||||||
|
|
||||||
@ -43,7 +45,7 @@ def pe_get_log() -> str:
|
|||||||
SANS_NOTE = "-"
|
SANS_NOTE = "-"
|
||||||
|
|
||||||
|
|
||||||
def aff_profil_coeffs(matrice_coeffs_moy_gen, with_index=False):
|
def repr_profil_coeffs(matrice_coeffs_moy_gen, with_index=False):
|
||||||
"""Affiche les différents types de coefficients (appelés profil)
|
"""Affiche les différents types de coefficients (appelés profil)
|
||||||
d'une matrice_coeffs_moy_gen (pour debug)
|
d'une matrice_coeffs_moy_gen (pour debug)
|
||||||
"""
|
"""
|
||||||
@ -79,8 +81,20 @@ def aff_profil_coeffs(matrice_coeffs_moy_gen, with_index=False):
|
|||||||
return profils_aff
|
return profils_aff
|
||||||
|
|
||||||
|
|
||||||
|
def repr_asso_ue_comp(acronymes_ues_to_competences):
|
||||||
|
"""Représentation textuelle de l'association UE -> Compétences
|
||||||
|
fournies dans acronymes_ues_to_competences
|
||||||
|
"""
|
||||||
|
champs = acronymes_ues_to_competences.keys()
|
||||||
|
champs = sorted(champs)
|
||||||
|
aff_comp = []
|
||||||
|
for acro in champs:
|
||||||
|
aff_comp += [f"📍{acro} (∈ 💡{acronymes_ues_to_competences[acro]})"]
|
||||||
|
return ", ".join(aff_comp)
|
||||||
|
|
||||||
|
|
||||||
def aff_UEs(champs):
|
def aff_UEs(champs):
|
||||||
"""Affiche les UEs"""
|
"""Représentation textuelle des UEs fournies dans `champs`"""
|
||||||
champs_tries = sorted(champs)
|
champs_tries = sorted(champs)
|
||||||
aff_comp = []
|
aff_comp = []
|
||||||
|
|
||||||
@ -99,8 +113,92 @@ def aff_competences(champs):
|
|||||||
return ", ".join(aff_comp)
|
return ", ".join(aff_comp)
|
||||||
|
|
||||||
|
|
||||||
def aff_tag(tags):
|
def repr_tags(tags):
|
||||||
"""Affiche les tags"""
|
"""Affiche les tags"""
|
||||||
tags_tries = sorted(tags)
|
tags_tries = sorted(tags)
|
||||||
aff_tag = ["👜" + tag for tag in tags_tries]
|
aff_tag = ["👜" + tag for tag in tags_tries]
|
||||||
return ", ".join(aff_tag)
|
return ", ".join(aff_tag)
|
||||||
|
|
||||||
|
|
||||||
|
def aff_tags_par_categories(dict_tags):
|
||||||
|
"""Etant donné un dictionnaire de tags, triés
|
||||||
|
par catégorie (ici "personnalisés" ou "auto")
|
||||||
|
représentation textuelle des tags
|
||||||
|
"""
|
||||||
|
noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys())))
|
||||||
|
noms_tags_auto = sorted(list(set(dict_tags["auto"].keys()))) # + noms_tags_comp
|
||||||
|
aff_tags_auto = ", ".join([f"👜{nom}" for nom in noms_tags_auto])
|
||||||
|
aff_tags_perso = ", ".join([f"👜{nom}" for nom in noms_tags_perso])
|
||||||
|
# Affichage
|
||||||
|
return f"Tags du programme de formation : {aff_tags_perso} + Automatiques : {aff_tags_auto}"
|
||||||
|
|
||||||
|
|
||||||
|
def aff_trajectoires_suivies_par_etudiants(etudiants):
|
||||||
|
"""Affiche les trajectoires (regroupement de (form)semestres)
|
||||||
|
amenant un étudiant du S1 à un semestre final"""
|
||||||
|
# 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}) :")
|
||||||
|
trajectoires = etudiants.trajectoires[etudid]
|
||||||
|
for nom_rcs, rcs in trajectoires.items():
|
||||||
|
if rcs:
|
||||||
|
pe_print(f" > RCS ⏯️{nom_rcs}: {rcs.get_repr()}")
|
||||||
|
|
||||||
|
|
||||||
|
def aff_semXs_suivis_par_etudiants(etudiants):
|
||||||
|
"""Affiche les SemX (regroupement de semestres de type Sx)
|
||||||
|
amenant un étudiant à valider un Sx"""
|
||||||
|
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} :")
|
||||||
|
for nom_rcs, rcs in etudiants.semXs[etudid].items():
|
||||||
|
if rcs:
|
||||||
|
pe_print(f" > SemX ⏯️{nom_rcs}: {rcs.get_repr()}")
|
||||||
|
|
||||||
|
vides = []
|
||||||
|
for nom_rcs in pe_rcs.TOUS_LES_SEMESTRES:
|
||||||
|
les_semX_suivis = []
|
||||||
|
for no_etud, etudid in jeunes:
|
||||||
|
if etudiants.semXs[etudid][nom_rcs]:
|
||||||
|
les_semX_suivis.append(etudiants.semXs[etudid][nom_rcs])
|
||||||
|
if not les_semX_suivis:
|
||||||
|
vides += [nom_rcs]
|
||||||
|
vides = sorted(list(set(vides)))
|
||||||
|
pe_print(f"⚠️ SemX sans données : {', '.join(vides)}")
|
||||||
|
|
||||||
|
|
||||||
|
def aff_capitalisations(etuds, ressembuttags, fid_final, acronymes_sorted, masque_df):
|
||||||
|
"""Affichage des capitalisations du sxtag pour debug"""
|
||||||
|
aff_cap = []
|
||||||
|
for etud in etuds:
|
||||||
|
cap = []
|
||||||
|
for frmsem_id in ressembuttags:
|
||||||
|
if frmsem_id != fid_final:
|
||||||
|
for accr in acronymes_sorted:
|
||||||
|
if masque_df[frmsem_id].loc[etud.etudid, accr] > 0.0:
|
||||||
|
cap += [accr]
|
||||||
|
if cap:
|
||||||
|
aff_cap += [f" > {etud.nomprenom} : {', '.join(cap)}"]
|
||||||
|
if aff_cap:
|
||||||
|
pe_print(f"--> ⚠️ Capitalisations :")
|
||||||
|
pe_print("\n".join(aff_cap))
|
||||||
|
|
||||||
|
|
||||||
|
def repr_comp_et_ues(acronymes_ues_to_competences):
|
||||||
|
"""Affichage pour debug"""
|
||||||
|
aff_comp = []
|
||||||
|
competences_sorted = sorted(acronymes_ues_to_competences.keys())
|
||||||
|
for comp in competences_sorted:
|
||||||
|
liste = []
|
||||||
|
for acro in acronymes_ues_to_competences:
|
||||||
|
if acronymes_ues_to_competences[acro] == comp:
|
||||||
|
liste += ["📍" + acro]
|
||||||
|
aff_comp += [f" 💡{comp} (⇔ {', '.join(liste)})"]
|
||||||
|
return "\n".join(aff_comp)
|
||||||
|
@ -40,10 +40,10 @@ import pandas as pd
|
|||||||
from app import ScoValueError
|
from app import ScoValueError
|
||||||
from app.models import FormSemestre, Identite, Formation
|
from app.models import FormSemestre, Identite, Formation
|
||||||
from app.pe import pe_comp, pe_affichage
|
from app.pe import pe_comp, pe_affichage
|
||||||
|
from app.pe.rcss import pe_rcs
|
||||||
from app.scodoc import codes_cursus
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.comp.res_sem import load_formsemestre_results
|
from app.comp.res_sem import load_formsemestre_results
|
||||||
import warnings
|
|
||||||
|
|
||||||
|
|
||||||
class EtudiantsJuryPE:
|
class EtudiantsJuryPE:
|
||||||
@ -57,16 +57,17 @@ class EtudiantsJuryPE:
|
|||||||
self.annee_diplome = annee_diplome
|
self.annee_diplome = annee_diplome
|
||||||
"""L'année du diplôme"""
|
"""L'année du diplôme"""
|
||||||
|
|
||||||
self.identites: dict[int, Identite] = {} # ex. ETUDINFO_DICT
|
self.identites: dict[int:Identite] = {} # ex. ETUDINFO_DICT
|
||||||
"Les identités des étudiants traités pour le jury"
|
"""Les identités des étudiants traités pour le jury"""
|
||||||
|
|
||||||
self.cursus: dict[int, dict] = {}
|
self.cursus: dict[int:dict] = {}
|
||||||
"Les cursus (semestres suivis, abandons) des étudiants"
|
"""Les cursus (semestres suivis, abandons) des étudiants"""
|
||||||
|
|
||||||
self.cursus = {}
|
self.trajectoires: dict[int:dict] = {}
|
||||||
"""Les trajectoires/chemins de semestres suivis par les étudiants
|
"""Les trajectoires (regroupement cohérents de semestres) suivis par les étudiants"""
|
||||||
pour atteindre un aggrégat donné
|
|
||||||
(par ex: 3S=S1+S2+S3 à prendre en compte avec d'éventuels redoublements)"""
|
self.semXs: dict[int:dict] = {}
|
||||||
|
"""Les semXs (RCS de type Sx) suivis par chaque étudiant"""
|
||||||
|
|
||||||
self.etudiants_diplomes = {}
|
self.etudiants_diplomes = {}
|
||||||
"""Les identités des étudiants à considérer au jury (ceux qui seront effectivement
|
"""Les identités des étudiants à considérer au jury (ceux qui seront effectivement
|
||||||
@ -101,27 +102,26 @@ class EtudiantsJuryPE:
|
|||||||
self.cosemestres = cosemestres
|
self.cosemestres = cosemestres
|
||||||
|
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
f"1) Recherche des cosemestres -> {len(cosemestres)} trouvés"
|
f"1) Recherche des cosemestres -> {len(cosemestres)} trouvés", info=True
|
||||||
)
|
)
|
||||||
|
|
||||||
pe_affichage.pe_print("2) Liste des étudiants dans les différents cosemestres")
|
|
||||||
self.etudiants_ids = get_etudiants_dans_semestres(cosemestres)
|
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
f" => {len(self.etudiants_ids)} étudiants trouvés dans les cosemestres"
|
"2) Liste des étudiants dans les différents cosemestres", info=True
|
||||||
|
)
|
||||||
|
etudiants_ids = get_etudiants_dans_semestres(cosemestres)
|
||||||
|
pe_affichage.pe_print(
|
||||||
|
f" => {len(etudiants_ids)} étudiants trouvés dans les cosemestres",
|
||||||
|
info=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Analyse des parcours étudiants pour déterminer leur année effective de diplome
|
# Analyse des parcours étudiants pour déterminer leur année effective de diplome
|
||||||
# avec prise en compte des redoublements, des abandons, ....
|
# avec prise en compte des redoublements, des abandons, ....
|
||||||
pe_affichage.pe_print("3) Analyse des parcours individuels des étudiants")
|
pe_affichage.pe_print(
|
||||||
|
"3) Analyse des parcours individuels des étudiants", info=True
|
||||||
|
)
|
||||||
|
|
||||||
for etudid in self.etudiants_ids:
|
# Ajoute une liste d'étudiants
|
||||||
self.identites[etudid] = Identite.get_etud(etudid)
|
self.add_etudiants(etudiants_ids)
|
||||||
|
|
||||||
# Analyse son cursus
|
|
||||||
self.analyse_etat_etudiant(etudid, cosemestres)
|
|
||||||
|
|
||||||
# Analyse son parcours pour atteindre chaque semestre de la formation
|
|
||||||
self.structure_cursus_etudiant(etudid)
|
|
||||||
|
|
||||||
# Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris
|
# Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris
|
||||||
self.etudiants_diplomes = self.get_etudiants_diplomes()
|
self.etudiants_diplomes = self.get_etudiants_diplomes()
|
||||||
@ -136,9 +136,10 @@ class EtudiantsJuryPE:
|
|||||||
# Les identifiants des étudiants ayant redoublés ou ayant abandonnés
|
# Les identifiants des étudiants ayant redoublés ou ayant abandonnés
|
||||||
|
|
||||||
# Synthèse
|
# Synthèse
|
||||||
pe_affichage.pe_print(f"4) Bilan")
|
pe_affichage.pe_print(f"4) Bilan", info=True)
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
f"--> {len(self.etudiants_diplomes)} étudiants à diplômer en {self.annee_diplome}"
|
f"--> {len(self.etudiants_diplomes)} étudiants à diplômer en {self.annee_diplome}",
|
||||||
|
info=True,
|
||||||
)
|
)
|
||||||
nbre_abandons = len(self.etudiants_ids) - len(self.etudiants_diplomes)
|
nbre_abandons = len(self.etudiants_ids) - len(self.etudiants_diplomes)
|
||||||
assert nbre_abandons == len(self.abandons_ids)
|
assert nbre_abandons == len(self.abandons_ids)
|
||||||
@ -147,6 +148,24 @@ class EtudiantsJuryPE:
|
|||||||
f"--> {nbre_abandons} étudiants traités mais non diplômés (redoublement, réorientation, abandon)"
|
f"--> {nbre_abandons} étudiants traités mais non diplômés (redoublement, réorientation, abandon)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def add_etudiants(self, etudiants_ids):
|
||||||
|
"""Ajoute une liste d'étudiants aux données du jury"""
|
||||||
|
nbre_etudiants_ajoutes = 0
|
||||||
|
for etudid in etudiants_ids:
|
||||||
|
if etudid not in self.identites:
|
||||||
|
nbre_etudiants_ajoutes += 1
|
||||||
|
|
||||||
|
# L'identité de l'étudiant
|
||||||
|
self.identites[etudid] = Identite.get_etud(etudid)
|
||||||
|
|
||||||
|
# Analyse son cursus
|
||||||
|
self.analyse_etat_etudiant(etudid, self.cosemestres)
|
||||||
|
|
||||||
|
# Analyse son parcours pour atteindre chaque semestre de la formation
|
||||||
|
self.structure_cursus_etudiant(etudid)
|
||||||
|
self.etudiants_ids = set(self.identites.keys())
|
||||||
|
return nbre_etudiants_ajoutes
|
||||||
|
|
||||||
def get_etudiants_diplomes(self) -> dict[int, Identite]:
|
def get_etudiants_diplomes(self) -> dict[int, Identite]:
|
||||||
"""Identités des étudiants (sous forme d'un dictionnaire `{etudid: Identite(etudid)}`
|
"""Identités des étudiants (sous forme d'un dictionnaire `{etudid: Identite(etudid)}`
|
||||||
qui vont être à traiter au jury PE pour
|
qui vont être à traiter au jury PE pour
|
||||||
@ -215,11 +234,19 @@ class EtudiantsJuryPE:
|
|||||||
if formsemestre.formation.is_apc()
|
if formsemestre.formation.is_apc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Le parcours final
|
||||||
|
parcour = formsemestres[0].etuds_inscriptions[etudid].parcour
|
||||||
|
if parcour:
|
||||||
|
libelle = parcour.libelle
|
||||||
|
else:
|
||||||
|
libelle = None
|
||||||
|
|
||||||
self.cursus[etudid] = {
|
self.cursus[etudid] = {
|
||||||
"etudid": etudid, # les infos sur l'étudiant
|
"etudid": etudid, # les infos sur l'étudiant
|
||||||
"etat_civil": identite.etat_civil, # Ajout à la table jury
|
"etat_civil": identite.etat_civil, # Ajout à la table jury
|
||||||
"nom": identite.nom,
|
"nom": identite.nom,
|
||||||
"entree": formsemestres[-1].date_debut.year, # La date d'entrée à l'IUT
|
"entree": formsemestres[-1].date_debut.year, # La date d'entrée à l'IUT
|
||||||
|
"parcours": libelle, # Le parcours final
|
||||||
"diplome": get_annee_diplome(
|
"diplome": get_annee_diplome(
|
||||||
identite
|
identite
|
||||||
), # Le date prévisionnelle de son diplôme
|
), # Le date prévisionnelle de son diplôme
|
||||||
@ -234,7 +261,6 @@ class EtudiantsJuryPE:
|
|||||||
if self.cursus[etudid]["diplome"] == self.annee_diplome:
|
if self.cursus[etudid]["diplome"] == self.annee_diplome:
|
||||||
# Est-il démissionnaire : charge son dernier semestre pour connaitre son état ?
|
# Est-il démissionnaire : charge son dernier semestre pour connaitre son état ?
|
||||||
dernier_semes_etudiant = formsemestres[0]
|
dernier_semes_etudiant = formsemestres[0]
|
||||||
with warnings.catch_warnings():
|
|
||||||
res = load_formsemestre_results(dernier_semes_etudiant)
|
res = load_formsemestre_results(dernier_semes_etudiant)
|
||||||
etud_etat = res.get_etud_etat(etudid)
|
etud_etat = res.get_etud_etat(etudid)
|
||||||
if etud_etat == scu.DEMISSION:
|
if etud_etat == scu.DEMISSION:
|
||||||
@ -245,29 +271,9 @@ class EtudiantsJuryPE:
|
|||||||
identite, cosemestres
|
identite, cosemestres
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_semestres_significatifs(self, etudid: int):
|
# Initialise ses trajectoires/SemX/RCSemX
|
||||||
"""Ensemble des semestres d'un étudiant, qui :
|
self.trajectoires[etudid] = {aggregat: None for aggregat in pe_rcs.TOUS_LES_RCS}
|
||||||
|
self.semXs[etudid] = {aggregat: None for aggregat in pe_rcs.TOUS_LES_SEMESTRES}
|
||||||
* l'amènent à être diplômé à l'année visée
|
|
||||||
* l'auraient amené à être diplômé à l'année visée s'il n'avait pas redoublé et sera donc
|
|
||||||
diplômé plus tard
|
|
||||||
|
|
||||||
Supprime les semestres qui conduisent à une diplomation postérieure à celle du jury visé.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
etudid: L'identifiant d'un étudiant
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Un dictionnaire ``{fid: FormSemestre(fid)}`` dans lequel les semestres
|
|
||||||
amènent à une diplômation antérieur à celle de la diplômation visée par le jury jury
|
|
||||||
"""
|
|
||||||
semestres_etudiant = self.cursus[etudid]["formsemestres"]
|
|
||||||
semestres_significatifs = {}
|
|
||||||
for fid in semestres_etudiant:
|
|
||||||
semestre = semestres_etudiant[fid]
|
|
||||||
if pe_comp.get_annee_diplome_semestre(semestre) <= self.annee_diplome:
|
|
||||||
semestres_significatifs[fid] = semestre
|
|
||||||
return semestres_significatifs
|
|
||||||
|
|
||||||
def structure_cursus_etudiant(self, etudid: int):
|
def structure_cursus_etudiant(self, etudid: int):
|
||||||
"""Structure les informations sur les semestres suivis par un
|
"""Structure les informations sur les semestres suivis par un
|
||||||
@ -278,7 +284,9 @@ class EtudiantsJuryPE:
|
|||||||
le dernier semestre (en date) de numéro i qu'il a suivi (1 ou 0 si pas encore suivi).
|
le dernier semestre (en date) de numéro i qu'il a suivi (1 ou 0 si pas encore suivi).
|
||||||
Ce semestre influera les interclassements par semestre dans la promo.
|
Ce semestre influera les interclassements par semestre dans la promo.
|
||||||
"""
|
"""
|
||||||
semestres_significatifs = self.get_semestres_significatifs(etudid)
|
semestres_significatifs = get_semestres_significatifs(
|
||||||
|
self.cursus[etudid]["formsemestres"], self.annee_diplome
|
||||||
|
)
|
||||||
|
|
||||||
# Tri des semestres par numéro de semestre
|
# Tri des semestres par numéro de semestre
|
||||||
for i in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT + 1):
|
for i in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT + 1):
|
||||||
@ -381,13 +389,35 @@ class EtudiantsJuryPE:
|
|||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
def get_semestres_significatifs(formsemestres, annee_diplome):
|
||||||
|
"""Partant d'un ensemble de semestre, renvoie les semestres qui amèneraient les étudiants
|
||||||
|
à être diplômé à l'année visée, y compris s'ils n'avaient pas redoublé et seraient donc
|
||||||
|
diplômé plus tard.
|
||||||
|
|
||||||
|
De fait, supprime les semestres qui conduisent à une diplomation postérieure
|
||||||
|
à celle visée.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
formsemestres: une liste de formsemestres
|
||||||
|
annee_diplome: l'année du diplôme visée
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Un dictionnaire ``{fid: FormSemestre(fid)}`` dans lequel les semestres
|
||||||
|
amènent à une diplômation antérieur à celle de la diplômation visée par le jury
|
||||||
|
"""
|
||||||
|
# semestres_etudiant = self.cursus[etudid]["formsemestres"]
|
||||||
|
semestres_significatifs = {}
|
||||||
|
for fid in formsemestres:
|
||||||
|
semestre = formsemestres[fid]
|
||||||
|
if pe_comp.get_annee_diplome_semestre(semestre) <= annee_diplome:
|
||||||
|
semestres_significatifs[fid] = semestre
|
||||||
|
return semestres_significatifs
|
||||||
|
|
||||||
|
|
||||||
def get_etudiants_dans_semestres(semestres: dict[int, FormSemestre]) -> set:
|
def get_etudiants_dans_semestres(semestres: dict[int, FormSemestre]) -> set:
|
||||||
"""Ensemble d'identifiants des étudiants (identifiés via leur ``etudid``)
|
"""Ensemble d'identifiants des étudiants (identifiés via leur ``etudid``)
|
||||||
inscrits à l'un des semestres de la liste de ``semestres``.
|
inscrits à l'un des semestres de la liste de ``semestres``.
|
||||||
|
|
||||||
Remarque : Les ``cosemestres`` sont généralement obtenus avec
|
|
||||||
``sco_formsemestre.do_formsemestre_list()``
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
semestres: Un dictionnaire ``{fid: Formsemestre(fid)}`` donnant un
|
semestres: Un dictionnaire ``{fid: Formsemestre(fid)}`` donnant un
|
||||||
ensemble d'identifiant de semestres
|
ensemble d'identifiant de semestres
|
||||||
|
@ -65,6 +65,7 @@ from app.pe.moys import (
|
|||||||
pe_moytag,
|
pe_moytag,
|
||||||
)
|
)
|
||||||
import app.pe.pe_rcss_jury as pe_rcss_jury
|
import app.pe.pe_rcss_jury as pe_rcss_jury
|
||||||
|
from app.scodoc.sco_utils import *
|
||||||
|
|
||||||
|
|
||||||
class JuryPE(object):
|
class JuryPE(object):
|
||||||
@ -93,7 +94,7 @@ class JuryPE(object):
|
|||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
f"""***********************************************************\n"""
|
f"""***********************************************************\n"""
|
||||||
f"""*** Recherche des étudiants diplômés 🎓 en {self.diplome}\n"""
|
f"""*** Recherche des étudiants diplômés 🎓 en {self.diplome}\n"""
|
||||||
f"""***********************************************************\n"""
|
f"""***********************************************************"""
|
||||||
)
|
)
|
||||||
|
|
||||||
# Les infos sur les étudiants
|
# Les infos sur les étudiants
|
||||||
@ -102,7 +103,7 @@ 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_rcss_jury.RCSsJuryPE(self.diplome)
|
self.rcss_jury = pe_rcss_jury.RCSsJuryPE(self.diplome, self.etudiants)
|
||||||
"""Les informations sur les regroupements de semestres"""
|
"""Les informations sur les regroupements de semestres"""
|
||||||
|
|
||||||
self.zipdata = io.BytesIO()
|
self.zipdata = io.BytesIO()
|
||||||
@ -168,7 +169,16 @@ class JuryPE(object):
|
|||||||
self.ressembuttags = {}
|
self.ressembuttags = {}
|
||||||
for frmsem_id, formsemestre in formsemestres.items():
|
for frmsem_id, formsemestre in formsemestres.items():
|
||||||
# Crée le semestre_tag et exécute les calculs de moyennes
|
# Crée le semestre_tag et exécute les calculs de moyennes
|
||||||
self.ressembuttags[frmsem_id] = pe_ressemtag.ResSemBUTTag(formsemestre)
|
ressembuttag = pe_ressemtag.ResSemBUTTag(formsemestre)
|
||||||
|
self.ressembuttags[frmsem_id] = ressembuttag
|
||||||
|
# Ajoute les étudiants découverts dans les ressembuttags aux données des étudiants
|
||||||
|
# nbre_etudiants_ajoutes = self.etudiants.add_etudiants(
|
||||||
|
# ressembuttag.etudids_sorted
|
||||||
|
# )
|
||||||
|
# if nbre_etudiants_ajoutes:
|
||||||
|
# pe_affichage.pe_print(
|
||||||
|
# f"--> Ajout de {nbre_etudiants_ajoutes} étudiants aux données du jury"
|
||||||
|
# )
|
||||||
|
|
||||||
# Intègre le bilan des semestres taggués au zip final
|
# Intègre le bilan des semestres taggués au zip final
|
||||||
pe_affichage.pe_print(f"2) Bilan")
|
pe_affichage.pe_print(f"2) Bilan")
|
||||||
@ -178,10 +188,15 @@ class JuryPE(object):
|
|||||||
) as writer:
|
) as writer:
|
||||||
onglets = []
|
onglets = []
|
||||||
for res_sem_tag in self.ressembuttags.values():
|
for res_sem_tag in self.ressembuttags.values():
|
||||||
|
for pole in [None, ModuleType.RESSOURCE, ModuleType.SAE]:
|
||||||
onglet = res_sem_tag.get_repr(verbose=True)
|
onglet = res_sem_tag.get_repr(verbose=True)
|
||||||
onglet = onglet.replace("Semestre ", "S")
|
onglet = onglet.replace("Semestre ", "S")
|
||||||
|
if pole:
|
||||||
|
onglet += (
|
||||||
|
" (res.)" if pole == ModuleType.RESSOURCE else " (saes)"
|
||||||
|
)
|
||||||
onglets += ["📊" + onglet]
|
onglets += ["📊" + onglet]
|
||||||
df = res_sem_tag.to_df()
|
df = res_sem_tag.to_df(pole)
|
||||||
# Conversion colonnes en multiindex
|
# Conversion colonnes en multiindex
|
||||||
df = convert_colonnes_to_multiindex(df)
|
df = convert_colonnes_to_multiindex(df)
|
||||||
# écriture dans l'onglet
|
# écriture dans l'onglet
|
||||||
@ -207,8 +222,8 @@ class JuryPE(object):
|
|||||||
"***************************************************************************"
|
"***************************************************************************"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.rcss_jury.cree_trajectoires(self.etudiants)
|
self.rcss_jury.cree_trajectoires()
|
||||||
self.rcss_jury._aff_trajectoires(self.etudiants)
|
pe_affichage.aff_trajectoires_suivies_par_etudiants(self.etudiants)
|
||||||
|
|
||||||
def _gen_semXs(self):
|
def _gen_semXs(self):
|
||||||
"""Génère les SemXs (trajectoires/combinaisons de semestre de même rang x)
|
"""Génère les SemXs (trajectoires/combinaisons de semestre de même rang x)
|
||||||
@ -218,8 +233,8 @@ class JuryPE(object):
|
|||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
"*** Génère les SemXs (RCS de même Sx donnant lieu à validation du semestre)"
|
"*** Génère les SemXs (RCS de même Sx donnant lieu à validation du semestre)"
|
||||||
)
|
)
|
||||||
self.rcss_jury.cree_semxs(self.etudiants)
|
self.rcss_jury.cree_semxs()
|
||||||
self.rcss_jury._aff_semxs_suivis(self.etudiants)
|
pe_affichage.aff_semXs_suivis_par_etudiants(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 RCF de type Sx (pour
|
"""Génère les semestres taggués en s'appuyant sur les RCF de type Sx (pour
|
||||||
@ -248,12 +263,18 @@ class JuryPE(object):
|
|||||||
) as writer:
|
) as writer:
|
||||||
onglets = []
|
onglets = []
|
||||||
for sxtag in self.sxtags.values():
|
for sxtag in self.sxtags.values():
|
||||||
onglet = sxtag.get_repr(verbose=False)
|
|
||||||
if sxtag.is_significatif():
|
if sxtag.is_significatif():
|
||||||
df = sxtag.to_df()
|
for pole in [None, ModuleType.RESSOURCE, ModuleType.SAE]:
|
||||||
|
onglet = sxtag.get_repr(verbose=False)
|
||||||
|
if pole:
|
||||||
|
onglet += (
|
||||||
|
" (res.)" if pole == ModuleType.RESSOURCE else " (saes)"
|
||||||
|
)
|
||||||
|
onglets += ["📊" + onglet]
|
||||||
|
df = sxtag.to_df(pole)
|
||||||
# Conversion colonnes en multiindex
|
# Conversion colonnes en multiindex
|
||||||
df = convert_colonnes_to_multiindex(df)
|
df = convert_colonnes_to_multiindex(df)
|
||||||
onglets += ["📊" + onglet]
|
|
||||||
# é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)
|
||||||
pe_affichage.pe_print(f"--> Export excel de {', '.join(onglets)}")
|
pe_affichage.pe_print(f"--> Export excel de {', '.join(onglets)}")
|
||||||
@ -320,9 +341,16 @@ class JuryPE(object):
|
|||||||
) as writer:
|
) as writer:
|
||||||
onglets = []
|
onglets = []
|
||||||
for rcs_tag in self.rcsstags.values():
|
for rcs_tag in self.rcsstags.values():
|
||||||
onglet = rcs_tag.get_repr(verbose=False)
|
|
||||||
if rcs_tag.is_significatif():
|
if rcs_tag.is_significatif():
|
||||||
df = rcs_tag.to_df()
|
for pole in [None, ModuleType.RESSOURCE, ModuleType.SAE]:
|
||||||
|
onglet = rcs_tag.get_repr(verbose=False)
|
||||||
|
if pole:
|
||||||
|
onglet += (
|
||||||
|
" (res.)" if pole == ModuleType.RESSOURCE else " (saes)"
|
||||||
|
)
|
||||||
|
onglets += ["📊" + onglet]
|
||||||
|
|
||||||
|
df = rcs_tag.to_df(pole)
|
||||||
# Conversion colonnes en multiindex
|
# Conversion colonnes en multiindex
|
||||||
df = convert_colonnes_to_multiindex(df)
|
df = convert_colonnes_to_multiindex(df)
|
||||||
onglets += ["📊" + onglet]
|
onglets += ["📊" + onglet]
|
||||||
@ -393,9 +421,17 @@ class JuryPE(object):
|
|||||||
]:
|
]:
|
||||||
interclasstag = self.interclasstags[type_interclass]
|
interclasstag = self.interclasstags[type_interclass]
|
||||||
for nom_rcs, interclass in interclasstag.items():
|
for nom_rcs, interclass in interclasstag.items():
|
||||||
onglet = interclass.get_repr()
|
|
||||||
if interclass.is_significatif():
|
if interclass.is_significatif():
|
||||||
df = interclass.to_df(cohorte="Promo")
|
for pole in [None, ModuleType.RESSOURCE, ModuleType.SAE]:
|
||||||
|
onglet = interclass.get_repr()
|
||||||
|
if pole:
|
||||||
|
onglet += (
|
||||||
|
" (res.)"
|
||||||
|
if pole == ModuleType.RESSOURCE
|
||||||
|
else " (saes)"
|
||||||
|
)
|
||||||
|
onglets += ["📊" + onglet]
|
||||||
|
df = interclass.to_df(pole, cohorte="Promo")
|
||||||
# Conversion colonnes en multiindex
|
# Conversion colonnes en multiindex
|
||||||
df = convert_colonnes_to_multiindex(df)
|
df = convert_colonnes_to_multiindex(df)
|
||||||
onglets += [onglet]
|
onglets += [onglet]
|
||||||
@ -428,7 +464,10 @@ class JuryPE(object):
|
|||||||
tags = self._do_tags_list(self.interclasstags)
|
tags = self._do_tags_list(self.interclasstags)
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
for type_moy in [pe_moytag.CODE_MOY_UE, pe_moytag.CODE_MOY_COMPETENCES]:
|
for type_moy in [pe_moytag.CODE_MOY_UE, pe_moytag.CODE_MOY_COMPETENCES]:
|
||||||
self.synthese[(tag, type_moy)] = self.df_tag_type(tag, type_moy)
|
for pole in [None, ModuleType.RESSOURCE, ModuleType.SAE]:
|
||||||
|
self.synthese[(tag, type_moy, pole)] = self.df_tag_type(
|
||||||
|
tag, type_moy, pole
|
||||||
|
)
|
||||||
|
|
||||||
# Export des données => mode 1 seule feuille -> supprimé
|
# Export des données => mode 1 seule feuille -> supprimé
|
||||||
pe_affichage.pe_print("*** Export du jury de synthese par tags")
|
pe_affichage.pe_print("*** Export du jury de synthese par tags")
|
||||||
@ -443,10 +482,18 @@ class JuryPE(object):
|
|||||||
df_final = convert_colonnes_to_multiindex(df_final)
|
df_final = convert_colonnes_to_multiindex(df_final)
|
||||||
# Nom de l'onglet
|
# Nom de l'onglet
|
||||||
if isinstance(onglet, tuple):
|
if isinstance(onglet, tuple):
|
||||||
if onglet[1] == pe_moytag.CODE_MOY_COMPETENCES:
|
(repr, type_moy, pole) = onglet
|
||||||
nom_onglet = onglet[0][: 31 - 7] + " (Comp)"
|
nom_onglet = onglet[0][: 31 - 11]
|
||||||
|
if type_moy == pe_moytag.CODE_MOY_COMPETENCES:
|
||||||
|
nom_onglet = nom_onglet + "(Comp"
|
||||||
else:
|
else:
|
||||||
nom_onglet = onglet[0][: 31 - 5] + " (UE)"
|
nom_onglet = nom_onglet + "(UE"
|
||||||
|
if pole and pole == ModuleType.RESSOURCE:
|
||||||
|
nom_onglet = nom_onglet + ",res)"
|
||||||
|
elif pole and pole == ModuleType.SAE:
|
||||||
|
nom_onglet = nom_onglet + ",saes)"
|
||||||
|
else:
|
||||||
|
nom_onglet = nom_onglet + ")"
|
||||||
else:
|
else:
|
||||||
nom_onglet = onglet
|
nom_onglet = onglet
|
||||||
onglets += [nom_onglet]
|
onglets += [nom_onglet]
|
||||||
@ -515,7 +562,7 @@ class JuryPE(object):
|
|||||||
# Méthodes pour la synthèse du juryPE
|
# Méthodes pour la synthèse du juryPE
|
||||||
# *****************************************************************************************************************
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
def df_tag_type(self, tag, type_moy):
|
def df_tag_type(self, tag, type_moy, pole):
|
||||||
"""Génère le DataFrame synthétisant les moyennes/classements (groupe +
|
"""Génère le DataFrame synthétisant les moyennes/classements (groupe +
|
||||||
interclassement promo) pour tous les aggrégats prévus, en fonction
|
interclassement promo) pour tous les aggrégats prévus, en fonction
|
||||||
du type (UEs ou Compétences) de données souhaitées,
|
du type (UEs ou Compétences) de données souhaitées,
|
||||||
@ -551,7 +598,7 @@ class JuryPE(object):
|
|||||||
if interclass.is_significatif():
|
if interclass.is_significatif():
|
||||||
# Le dataframe du classement sur le groupe
|
# Le dataframe du classement sur le groupe
|
||||||
df_groupe = interclass.compute_df_synthese_moyennes_tag(
|
df_groupe = interclass.compute_df_synthese_moyennes_tag(
|
||||||
tag, aggregat=aggregat, type_colonnes=False
|
tag, pole, aggregat=aggregat, type_colonnes=False
|
||||||
)
|
)
|
||||||
if not df_groupe.empty:
|
if not df_groupe.empty:
|
||||||
aff_aggregat += [aggregat]
|
aff_aggregat += [aggregat]
|
||||||
@ -559,6 +606,7 @@ class JuryPE(object):
|
|||||||
|
|
||||||
# Le dataframe du classement sur la promo
|
# Le dataframe du classement sur la promo
|
||||||
df_promo = interclass.to_df(
|
df_promo = interclass.to_df(
|
||||||
|
pole,
|
||||||
administratif=False,
|
administratif=False,
|
||||||
aggregat=aggregat,
|
aggregat=aggregat,
|
||||||
tags_cibles=[tag],
|
tags_cibles=[tag],
|
||||||
@ -571,8 +619,14 @@ class JuryPE(object):
|
|||||||
|
|
||||||
if aff_aggregat:
|
if aff_aggregat:
|
||||||
aff_aggregat = sorted(set(aff_aggregat))
|
aff_aggregat = sorted(set(aff_aggregat))
|
||||||
|
if pole and pole == ModuleType.RESSOURCE:
|
||||||
|
aff_pole = "et par ressources"
|
||||||
|
elif pole and pole == ModuleType.SAE:
|
||||||
|
aff_pole = "et par saes"
|
||||||
|
else:
|
||||||
|
aff_pole = "tous modules confondus"
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
f" -> Synthèse de 👜{tag} par {type_moy} avec {', '.join(aff_aggregat)}"
|
f" -> Synthèse de 👜{tag} par {type_moy} {aff_pole} avec {', '.join(aff_aggregat)}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
pe_affichage.pe_print(f" -> Synthèse du tag {tag} par {type_moy} : <vide>")
|
pe_affichage.pe_print(f" -> Synthèse du tag {tag} par {type_moy} : <vide>")
|
||||||
@ -587,6 +641,8 @@ class JuryPE(object):
|
|||||||
Returns:
|
Returns:
|
||||||
Un tuple nom, prenom, html
|
Un tuple nom, prenom, html
|
||||||
"""
|
"""
|
||||||
|
pole = None
|
||||||
|
|
||||||
etudiant = self.etudiants.identites[etudid]
|
etudiant = self.etudiants.identites[etudid]
|
||||||
nom = etudiant.nom
|
nom = etudiant.nom
|
||||||
prenom = etudiant.prenom # initial du prénom
|
prenom = etudiant.prenom # initial du prénom
|
||||||
@ -608,57 +664,58 @@ class JuryPE(object):
|
|||||||
|
|
||||||
tags = self._do_tags_list(self.interclasstags)
|
tags = self._do_tags_list(self.interclasstags)
|
||||||
|
|
||||||
# Descripti
|
|
||||||
|
|
||||||
# Les données par UE
|
# Les données par UE
|
||||||
moyennes = {}
|
moyennes = {}
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
moyennes[tag] = {}
|
moyennes[tag] = {}
|
||||||
# Les données de synthèse
|
# Les données de synthèse
|
||||||
df = self.synthese[(tag, pe_moytag.CODE_MOY_COMPETENCES)]
|
df = self.synthese[(tag, pe_moytag.CODE_MOY_COMPETENCES, pole)]
|
||||||
for aggregat in pe_rcs.TOUS_LES_RCS:
|
for aggregat in pe_rcs.TOUS_LES_RCS:
|
||||||
moyennes[tag][aggregat] = {}
|
# moyennes[tag][aggregat] = {}
|
||||||
|
descr = pe_rcs.get_descr_rcs(aggregat)
|
||||||
|
|
||||||
|
moy = {}
|
||||||
|
est_significatif = False
|
||||||
for comp in competences + ["Général"]:
|
for comp in competences + ["Général"]:
|
||||||
moyennes[tag][aggregat][comp] = {
|
moy[comp] = {
|
||||||
"note": "",
|
"note": "",
|
||||||
"rang_groupe": "",
|
"rang_groupe": "",
|
||||||
"rang_promo": "",
|
"rang_promo": "",
|
||||||
}
|
}
|
||||||
colonne = pe_moytag.get_colonne_df(
|
colonne = pe_moytag.get_colonne_df(
|
||||||
aggregat, tag, comp, "Groupe", "note"
|
aggregat, pole, tag, comp, "Groupe", "note"
|
||||||
)
|
)
|
||||||
if colonne in df.columns:
|
if colonne in df.columns:
|
||||||
valeur = df.loc[etudid, colonne]
|
valeur = df.loc[etudid, colonne]
|
||||||
if not np.isnan(valeur):
|
if not np.isnan(valeur):
|
||||||
moyennes[tag][aggregat][comp]["note"] = round(valeur, 2)
|
moy[comp]["note"] = round(valeur, 2)
|
||||||
|
est_significatif = True
|
||||||
colonne = pe_moytag.get_colonne_df(
|
colonne = pe_moytag.get_colonne_df(
|
||||||
aggregat, tag, comp, "Groupe", "rang"
|
aggregat, pole, tag, comp, "Groupe", "rang"
|
||||||
)
|
)
|
||||||
if colonne in df.columns:
|
if colonne in df.columns:
|
||||||
valeur = df.loc[etudid, colonne]
|
valeur = df.loc[etudid, colonne]
|
||||||
if valeur and str(valeur) != "nan":
|
if valeur and str(valeur) != "nan":
|
||||||
moyennes[tag][aggregat][comp]["rang_groupe"] = valeur
|
moy[comp]["rang_groupe"] = valeur
|
||||||
colonne = pe_moytag.get_colonne_df(
|
colonne = pe_moytag.get_colonne_df(
|
||||||
aggregat, tag, comp, "Promo", "rang"
|
aggregat, pole, tag, comp, "Promo", "rang"
|
||||||
)
|
)
|
||||||
if colonne in df.columns:
|
if colonne in df.columns:
|
||||||
valeur = df.loc[etudid, colonne]
|
valeur = df.loc[etudid, colonne]
|
||||||
if valeur and str(valeur) != "nan":
|
if valeur and str(valeur) != "nan":
|
||||||
moyennes[tag][aggregat][comp]["rang_promo"] = valeur
|
moy[comp]["rang_promo"] = valeur
|
||||||
|
|
||||||
|
if est_significatif:
|
||||||
|
moyennes[tag][descr] = moy
|
||||||
|
|
||||||
html = template.render(
|
html = template.render(
|
||||||
nom=nom,
|
nom=nom,
|
||||||
prenom=prenom,
|
prenom=prenom,
|
||||||
|
pole="Moyennes calculées tous modules (ressources/SAEs) confondus",
|
||||||
colonnes_html=colonnes_html,
|
colonnes_html=colonnes_html,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
moyennes=moyennes,
|
moyennes=moyennes,
|
||||||
)
|
)
|
||||||
# for onglet, df_synthese in self.synthese.items():
|
|
||||||
# if isinstance(onglet, tuple): # Les onglets autres que "administratif"
|
|
||||||
# tag = onglet[0]
|
|
||||||
# type_moy = onglet[1]
|
|
||||||
|
|
||||||
# colonnes = list(df_synthese.columns)
|
|
||||||
|
|
||||||
return (nom, prenom, html)
|
return (nom, prenom, html)
|
||||||
|
|
||||||
|
@ -14,10 +14,13 @@ class RCSsJuryPE:
|
|||||||
annee_diplome: L'année de diplomation
|
annee_diplome: L'année de diplomation
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, annee_diplome: int):
|
def __init__(self, annee_diplome: int, etudiants: pe_etudiant.EtudiantsJuryPE):
|
||||||
self.annee_diplome = annee_diplome
|
self.annee_diplome = annee_diplome
|
||||||
"""Année de diplômation"""
|
"""Année de diplômation"""
|
||||||
|
|
||||||
|
self.etudiants = etudiants
|
||||||
|
"""Les étudiants recensés"""
|
||||||
|
|
||||||
self.trajectoires: dict[tuple(int, str) : pe_trajectoires.Trajectoire] = {}
|
self.trajectoires: dict[tuple(int, str) : pe_trajectoires.Trajectoire] = {}
|
||||||
"""Ensemble des trajectoires recensées (regroupement de (form)semestres BUT)"""
|
"""Ensemble des trajectoires recensées (regroupement de (form)semestres BUT)"""
|
||||||
|
|
||||||
@ -41,7 +44,7 @@ class RCSsJuryPE:
|
|||||||
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
||||||
son RCSemX : {etudid: {nom_RCS: RCSemX}}"""
|
son RCSemX : {etudid: {nom_RCS: RCSemX}}"""
|
||||||
|
|
||||||
def cree_trajectoires(self, etudiants: pe_etudiant.EtudiantsJuryPE):
|
def cree_trajectoires(self):
|
||||||
"""Créé toutes les trajectoires, au regard du cursus des étudiants
|
"""Créé toutes les trajectoires, au regard du cursus des étudiants
|
||||||
analysés + les mémorise dans les données de l'étudiant
|
analysés + les mémorise dans les données de l'étudiant
|
||||||
|
|
||||||
@ -49,31 +52,28 @@ class RCSsJuryPE:
|
|||||||
etudiants: Les étudiants à prendre en compte dans le Jury PE
|
etudiants: Les étudiants à prendre en compte dans le Jury PE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tous_les_aggregats = (
|
tous_les_aggregats = pe_rcs.TOUS_LES_RCS
|
||||||
pe_rcs.TOUS_LES_SEMESTRES + pe_rcs.TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
|
|
||||||
)
|
for etudid in self.etudiants.cursus:
|
||||||
for etudid in etudiants.cursus:
|
self.trajectoires_suivies[etudid] = self.etudiants.trajectoires[etudid]
|
||||||
self.trajectoires_suivies[etudid] = {
|
|
||||||
aggregat: None for aggregat in tous_les_aggregats
|
|
||||||
}
|
|
||||||
|
|
||||||
for nom_rcs in tous_les_aggregats:
|
for nom_rcs in tous_les_aggregats:
|
||||||
# L'aggrégat considéré (par ex: 3S=S1+S2+S3), son nom de son semestre
|
# 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)
|
# terminal (par ex: S3) et son numéro (par ex: 3)
|
||||||
noms_semestre_de_aggregat = pe_rcs.TYPES_RCS[nom_rcs]["aggregat"]
|
noms_semestres = pe_rcs.TYPES_RCS[nom_rcs]["aggregat"]
|
||||||
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
|
nom_semestre_final = noms_semestres[-1]
|
||||||
|
|
||||||
for etudid in etudiants.cursus:
|
for etudid in self.etudiants.cursus:
|
||||||
# Le formsemestre terminal (dernier en date) associé au
|
# Le (ou les) semestre(s) marquant la fin du cursus de l'étudiant
|
||||||
# semestre marquant la fin de l'aggrégat
|
sems_final = self.etudiants.cursus[etudid][nom_semestre_final]
|
||||||
# (par ex: son dernier S3 en date)
|
if sems_final:
|
||||||
trajectoire = etudiants.cursus[etudid][nom_semestre_terminal]
|
# Le formsemestre final (dernier en date) de l'étudiant,
|
||||||
if trajectoire:
|
# marquant la fin de son aggrégat (par ex: son dernier S3 en date)
|
||||||
formsemestre_final = app.pe.pe_comp.get_dernier_semestre_en_date(
|
formsemestre_final = app.pe.pe_comp.get_dernier_semestre_en_date(
|
||||||
trajectoire
|
sems_final
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ajout ou récupération du RCS associé
|
# Ajout (si nécessaire) et 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.trajectoires:
|
if rcs_id not in self.trajectoires:
|
||||||
self.trajectoires[rcs_id] = pe_trajectoires.Trajectoire(
|
self.trajectoires[rcs_id] = pe_trajectoires.Trajectoire(
|
||||||
@ -84,7 +84,7 @@ class RCSsJuryPE:
|
|||||||
# 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_rcs_etudiant(
|
semestres_a_aggreger = get_rcs_etudiant(
|
||||||
etudiants.cursus[etudid], formsemestre_final, nom_rcs
|
self.etudiants.cursus[etudid], formsemestre_final, nom_rcs
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ajout des semestres au RCS
|
# Ajout des semestres au RCS
|
||||||
@ -92,64 +92,30 @@ class RCSsJuryPE:
|
|||||||
|
|
||||||
# Mémorise le RCS suivi par l'étudiant
|
# Mémorise le RCS suivi par l'étudiant
|
||||||
self.trajectoires_suivies[etudid][nom_rcs] = rcs
|
self.trajectoires_suivies[etudid][nom_rcs] = rcs
|
||||||
|
self.etudiants.trajectoires[etudid][nom_rcs] = rcs
|
||||||
|
|
||||||
def _aff_trajectoires(self, etudiants: pe_etudiant.EtudiantsJuryPE):
|
def cree_semxs(self):
|
||||||
"""Affiche les chemins trouvés pour debug"""
|
"""Créé les SemXs (trajectoires/combinaisons de semestre de même rang x),
|
||||||
# Affichage pour debug
|
|
||||||
jeunes = list(enumerate(self.trajectoires_suivies))
|
|
||||||
for no_etud, etudid in jeunes:
|
|
||||||
etat = "⛔" if etudid in etudiants.abandons_ids else "✅"
|
|
||||||
|
|
||||||
pe_affichage.pe_print(
|
|
||||||
f"--> {etat} {etudiants.identites[etudid].nomprenom} (#{etudid}) :"
|
|
||||||
)
|
|
||||||
for nom_rcs, rcs in self.trajectoires_suivies[etudid].items():
|
|
||||||
if rcs:
|
|
||||||
pe_affichage.pe_print(f" > RCS ⏯️{nom_rcs}: {rcs.get_repr()}")
|
|
||||||
|
|
||||||
def cree_semxs(self, etudiants: pe_etudiant.EtudiantsJuryPE):
|
|
||||||
"""Créé les les SemXs (trajectoires/combinaisons de semestre de même rang x),
|
|
||||||
en ne conservant dans les trajectoires que les regroupements
|
en ne conservant dans les trajectoires que les regroupements
|
||||||
de type Sx"""
|
de type Sx"""
|
||||||
self.semXs = {}
|
self.semXs = {}
|
||||||
for rcs_id, trajectoire in self.trajectoires.items():
|
for rcs_id, trajectoire in self.trajectoires.items():
|
||||||
if trajectoire and trajectoire.nom in pe_rcs.TOUS_LES_SEMESTRES:
|
if trajectoire.nom in pe_rcs.TOUS_LES_SEMESTRES:
|
||||||
self.semXs[rcs_id] = pe_trajectoires.SemX(trajectoire)
|
self.semXs[rcs_id] = pe_trajectoires.SemX(trajectoire)
|
||||||
|
|
||||||
|
# L'association (pour chaque étudiant entre chaque Sx et le SemX associé)
|
||||||
self.semXs_suivis = {}
|
self.semXs_suivis = {}
|
||||||
for etudid in self.trajectoires_suivies:
|
for etudid in self.etudiants.trajectoires:
|
||||||
self.semXs_suivis[etudid] = {
|
self.semXs_suivis[etudid] = {
|
||||||
nom_rcs: None for nom_rcs in pe_rcs.TOUS_LES_SEMESTRES
|
agregat: None for agregat in pe_rcs.TOUS_LES_SEMESTRES
|
||||||
}
|
}
|
||||||
|
for agregat in pe_rcs.TOUS_LES_SEMESTRES:
|
||||||
for nom_rcs, trajectoire in self.trajectoires_suivies[etudid].items():
|
trajectoire = self.etudiants.trajectoires[etudid][agregat]
|
||||||
if trajectoire and nom_rcs in pe_rcs.TOUS_LES_SEMESTRES:
|
if trajectoire:
|
||||||
rcs_id = trajectoire.rcs_id
|
rcs_id = trajectoire.rcs_id
|
||||||
self.semXs_suivis[etudid][nom_rcs] = self.semXs[rcs_id]
|
semX = self.semXs[rcs_id]
|
||||||
|
self.semXs_suivis[etudid][agregat] = semX
|
||||||
def _aff_semxs_suivis(self, etudiants: pe_etudiant.EtudiantsJuryPE):
|
self.etudiants.semXs[etudid][agregat] = semX
|
||||||
"""Affichage des SemX pour debug"""
|
|
||||||
jeunes = list(enumerate(self.semXs_suivis))
|
|
||||||
|
|
||||||
for no_etud, etudid in jeunes:
|
|
||||||
etat = "⛔" if etudid in etudiants.abandons_ids else "✅"
|
|
||||||
pe_affichage.pe_print(
|
|
||||||
f"--> {etat} {etudiants.identites[etudid].nomprenom} :"
|
|
||||||
)
|
|
||||||
for nom_rcs, rcs in self.semXs_suivis[etudid].items():
|
|
||||||
if rcs:
|
|
||||||
pe_affichage.pe_print(f" > SemX ⏯️{nom_rcs}: {rcs.get_repr()}")
|
|
||||||
|
|
||||||
vides = []
|
|
||||||
for nom_rcs in pe_rcs.TOUS_LES_SEMESTRES:
|
|
||||||
les_semX_suivis = []
|
|
||||||
for no_etud, etudid in jeunes:
|
|
||||||
if self.semXs_suivis[etudid][nom_rcs]:
|
|
||||||
les_semX_suivis.append(self.semXs_suivis[etudid][nom_rcs])
|
|
||||||
if not les_semX_suivis:
|
|
||||||
vides += [nom_rcs]
|
|
||||||
vides = sorted(list(set(vides)))
|
|
||||||
pe_affichage.pe_print(f"⚠️ SemX sans données : {', '.join(vides)}")
|
|
||||||
|
|
||||||
def cree_rcsemxs(self, etudiants: pe_etudiant.EtudiantsJuryPE):
|
def cree_rcsemxs(self, etudiants: pe_etudiant.EtudiantsJuryPE):
|
||||||
"""Créé tous les RCSemXs, au regard du cursus des étudiants
|
"""Créé tous les RCSemXs, au regard du cursus des étudiants
|
||||||
|
@ -4,20 +4,37 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>PE de {{ nom }}</title>
|
<title>PE de {{ nom }}</title>
|
||||||
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"/>
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"/>
|
||||||
<style>
|
<style>
|
||||||
.flash { padding: 8px; color: white; border-radius: 15px; font-weight: bold; }
|
.w3-badge{
|
||||||
.success { background-color: limegreen; }
|
background-color:#000;
|
||||||
.error { background-color: lightcoral; }
|
color:#fff;
|
||||||
|
display:inline-block;
|
||||||
|
padding-left:8px;
|
||||||
|
padding-right:8px;
|
||||||
|
text-align:center;
|
||||||
|
border-radius:50%;
|
||||||
|
font-size: 70%;
|
||||||
|
}
|
||||||
|
.w3-red{background-color: darkred;}
|
||||||
|
.w3-blue{background-color: darkblue;}
|
||||||
|
ul.legend li {list-style: none; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<main class="container">
|
<main class="container">
|
||||||
|
|
||||||
<h1>Résultats PE de {{prenom}} {{nom}}</h1>
|
<h1>Résultats PE de {{prenom}} {{nom}}</h1>
|
||||||
|
|
||||||
|
<h2>Légende</h2>
|
||||||
|
<ul class="legend">
|
||||||
|
<li><span class="w3-badge w3-red">../..</span> Classement par groupe</li>
|
||||||
|
<li><span class="w3-badge w3-blue">../..</span> Classement par promo</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{ pole }}
|
||||||
|
|
||||||
{% for tag in tags %}
|
{% for tag in tags %}
|
||||||
|
|
||||||
<h2>Tag <code>👜 {{ tag }}</code></h2>
|
<h2>Tag <code>👜 {{ tag }}</code></h2>
|
||||||
|
Loading…
Reference in New Issue
Block a user