Amélioration nommencalature moyenne (état intermédiaire)

This commit is contained in:
Cléo Baras 2024-03-13 19:39:05 +01:00
parent 1712dcf8f7
commit dd98e19624
11 changed files with 286 additions and 144 deletions

View File

@ -152,11 +152,14 @@ class InterClassTag(pe_tabletags.TableTag):
aff = pe_affichage.repr_profil_coeffs(coeffs, with_index=True) aff = pe_affichage.repr_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} ")
infos = {"aggregat": self.nom_rcs, "cohorte": pe_moytag.CHAMP_PROMO}
self.moyennes_tags[tag] = pe_moytag.MoyennesTag( self.moyennes_tags[tag] = pe_moytag.MoyennesTag(
tag, tag,
self.type, self.type,
notes_gen, notes_gen,
coeffs, # limite les moyennes aux étudiants de la promo coeffs, # limite les moyennes aux étudiants de la promo
infos,
) )
def get_repr(self) -> str: def get_repr(self) -> str:
@ -321,9 +324,7 @@ class InterClassTag(pe_tabletags.TableTag):
# Charge la moyenne # Charge la moyenne
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(options=options)
aggregat=aggregat, cohorte="Groupe", options=options
)
# Modif les colonnes au regard du 1er df_moytag significatif lu # Modif les colonnes au regard du 1er df_moytag significatif lu
if not initialisation: if not initialisation:

View File

@ -20,21 +20,30 @@ class Moyenne:
@classmethod @classmethod
def get_colonnes_synthese(cls, with_min_max_moy): def get_colonnes_synthese(cls, with_min_max_moy):
"""Renvoie le nom des colonnes à prendre en compte pour la génération
d'un dataFrame résumant les données d'un objet pe_moy.Moyenne"""
if with_min_max_moy: if with_min_max_moy:
return ["note", "rang", "min", "max", "moy"] return ["note", "rang", "min", "max", "moy"]
else: else:
return ["note", "rang"] return ["note", "rang"]
def __init__(self, notes: pd.Series): def __init__(self, notes: pd.Series, infos: dict[str]):
"""Classe centralisant la synthèse des moyennes/classements d'une série """Classe centralisant la synthèse des moyennes/class/stat d'une série
de notes : de notes pour un groupe d'étudiants (déduits des notes).
* des "notes" : la Serie pandas des notes (float), Sont génerés des Séries/DataFrame donnant :
* des "classements" : la Serie pandas des classements (float),
* des "min" : la note minimum, * les "notes" : notes (float),
* des "max" : la note maximum, * des "classements" : classements (float),
* des "moy" : la moyenne, * des "min" : la note minimum sur tout le groupe d'étudiants,
* des "nb_inscrits" : le nombre d'étudiants ayant une note, * des "max" : la note maximum sur tout le groupe d'étudiants,
* des "moy" : la moyenne des notes sur tout le groupe d'étudiants,
* des "nb_inscrits" : le nombre d'étudiants ayant une note (non NaN)
Args:
notes: Une (pandas.)Série de notes
infos: Un dictionnaire donnant les informations sur la moyenne (aggrégat,
tag, intitule, cohorte, groupe)
""" """
self.notes = notes self.notes = notes
"""Les notes""" """Les notes"""
@ -44,9 +53,28 @@ class Moyenne:
"""Les id des étudiants dont la note est non nan/renseignée""" """Les id des étudiants dont la note est non nan/renseignée"""
self.df: pd.DataFrame = self.comp_moy_et_stat(self.notes) self.df: pd.DataFrame = self.comp_moy_et_stat(self.notes)
"""Le dataframe retraçant les moyennes/classements/statistiques""" """Le dataframe retraçant les moyennes/classements/statistiques"""
self.infos = {
"aggregat": infos["aggregat"],
"tag": infos["tag"],
"intitule": infos["intitule"],
"cohorte": infos["cohorte"],
}
"""Dictionnaire donnant des informations sur la note (aggrégat, cohorte, tag, type_de_moyenne)"""
# self.synthese = self.to_dict() # self.synthese = self.to_dict()
# """La synthèse (dictionnaire) des notes/classements/statistiques""" # """La synthèse (dictionnaire) des notes/classements/statistiques"""
def __repr__(self):
"""Représentation textuelle d'un objet Moyenne
sur la base de ses `infos`.
"""
repr = get_repr(
self.infos["aggregat"],
self.infos["tag"],
self.infos["intitule"],
self.infos["cohorte"],
)
return f"Moyenne {repr}"
def comp_moy_et_stat(self, notes: pd.Series) -> dict: def comp_moy_et_stat(self, notes: pd.Series) -> dict:
"""Calcule et structure les données nécessaires au PE pour une série """Calcule et structure les données nécessaires au PE pour une série
de notes (pouvant être une moyenne d'un tag à une UE ou une moyenne générale de notes (pouvant être une moyenne d'un tag à une UE ou une moyenne générale
@ -103,14 +131,29 @@ class Moyenne:
return df return df
def to_df(self, with_min_max_moy=None): def to_df(self, with_min_max_moy=None):
"""Renvoie le df de synthèse, en limitant les colonnes à celles attendues """Renvoie le df de synthèse (tel qu'attendu dans les exports Excel),
(dépendantes de l'option `with_min_max_moy`) en limitant les colonnes à celles attendues (dépendantes de l'option
``with_min_max_moy``)
""" """
colonnes_synthese = Moyenne.get_colonnes_synthese( colonnes_synthese = Moyenne.get_colonnes_synthese(
with_min_max_moy=with_min_max_moy with_min_max_moy=with_min_max_moy
) )
# Copie le df modélisant les données
df = self.df[colonnes_synthese].copy() df = self.df[colonnes_synthese].copy()
df["rang"] = df["rang"].replace("nan", "") df["rang"] = df["rang"].replace("nan", "")
# Remplace les noms de colonnes par leur intitulé dans le tableur excel
cols = []
for critere in colonnes_synthese:
nom_col = get_colonne_df(
self.infos["aggregat"],
self.infos["tag"],
self.infos["intitule"], # UEs ou compétences
self.infos["cohorte"],
critere,
)
cols += [nom_col]
df.columns = cols
return df return df
def to_json(self) -> dict: def to_json(self) -> dict:
@ -122,3 +165,39 @@ class Moyenne:
def has_notes(self) -> bool: def has_notes(self) -> bool:
"""Indique si la moyenne est significative (c'est-à-dire à des notes) et/ou des inscrits""" """Indique si la moyenne est significative (c'est-à-dire à des notes) et/ou des inscrits"""
return len(self.inscrits_ids) > 0 return len(self.inscrits_ids) > 0
def get_repr(aggregat, tag, intitule, cohorte):
"""Renvoie une représentation textuelle "aggregat|tag|intitule|cohorte"
pour représenter une moyenne
"""
liste_champs = []
if aggregat != None:
liste_champs += [aggregat]
liste_champs += [tag, intitule]
if cohorte != None:
liste_champs += [cohorte]
return "|".join(liste_champs)
def get_colonne_df(aggregat, tag, intitule, cohorte, critere):
"""Renvoie la chaine de caractère "aggregat|tag|intitule|cohorte|critere"
utilisé pour désigner les colonnes du df.
Args:
aggregat: Un nom d'aggrégat (généralement "S1" ou "3S")
pouvant être optionnel (si `None`)
tag: Un nom de tags (par ex. "maths")
intitule: Un nom d'UE ou de compétences ou "Général"
cohorte: Une cohorte pour les interclassements (généralement
Groupe ou Promo
pouvant être optionnel (si `None`)
critere: Un critère correspondant à l'une des colonnes
d'une pe_moy.Moyenne
Returns:
Une chaine de caractères indiquant les champs séparés par
un ``"|"``, généralement de la forme
"S1|maths|UE|Groupe|note"
"""
liste_champs = [get_repr(aggregat, tag, intitule, cohorte), critere]
return "|".join(liste_champs)

View File

@ -4,11 +4,14 @@ 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 from app.pe.moys.pe_moy import get_colonne_df
import re
CODE_MOY_UE = "UEs" CODE_MOY_UE = "UEs"
CODE_MOY_COMPETENCES = "Compétences" CODE_MOY_COMPETENCES = "Compétences"
CHAMP_GENERAL = "Général" # Nom du champ de la moyenne générale CHAMP_GENERAL = "Général" # Nom du champ de la moyenne générale
CHAMP_GROUPE = "groupe"
CHAMP_PROMO = "promo"
class MoyennesTag: class MoyennesTag:
@ -18,18 +21,22 @@ class MoyennesTag:
type_moyenne: str, type_moyenne: str,
matrice_notes: pd.DataFrame, # etudids x UEs|comp matrice_notes: pd.DataFrame, # etudids x UEs|comp
matrice_coeffs: pd.DataFrame, # etudids x UEs|comp matrice_coeffs: pd.DataFrame, # etudids x UEs|comp
infos: dict,
): ):
"""Classe centralisant la synthèse des moyennes/classements d'une série """Classe centralisant un ensemble de moyennes/class/stat,
d'étudiants à un tag donné, en différenciant les notes obtenu par un groupe d'étudiants, à un tag donné,
obtenues aux UE et au général (toutes UEs confondues) en stockant les moyennes aux UEs|Compétences
et la moyenne générale (toutes UEs confondues).
Args: Args:
tag: Un tag tag: Un tag
matrice_notes: Les moyennes (etudid x acronymes_ues|compétences) matrice_notes: Les moyennes (etudid x acronymes_ues|compétences)
aux différentes UEs ou compétences aux différentes UEs ou compétences
matrice_coeffs: Les coeffs (etudid x acronymes_ues|compétences) matrice_coeffs: Les coeffs (etudid x acronymes_ues|compétences)
aux différentes UEs ou compétences aux différentes UEs ou compétences
infos: Informations (aggrégat, cohorte ayant servi à calculer les moyennes)
""" """
self.tag = tag self.tag = tag
"""Le tag associé aux moyennes""" """Le tag associé aux moyennes"""
@ -37,6 +44,13 @@ 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)"""
self.infos = {
"aggregat": infos["aggregat"],
"tag": tag,
"cohorte": infos["cohorte"],
}
"""Info sur les éléments (aggrégat, cohorte) ayant servi à calculer les moyennes"""
# Les moyennes par UE/compétences (ressources/SAEs confondues) # Les moyennes par UE/compétences (ressources/SAEs confondues)
self.matrice_notes: pd.DataFrame = matrice_notes self.matrice_notes: pd.DataFrame = matrice_notes
"""Les notes par UEs ou Compétences (DataFrame etudids x UEs|comp)""" """Les notes par UEs ou Compétences (DataFrame etudids x UEs|comp)"""
@ -45,10 +59,10 @@ class MoyennesTag:
"""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.champs: list[str] = list(self.matrice_notes.columns) self.intitules: list[str] = list(self.matrice_notes.columns)
"""Les champs (acronymes d'UE ou compétences) renseignés dans les moyennes""" """Les intitules (acronymes d'UE ou compétences) renseignés dans les moyennes"""
assert len(self.champs) == len( assert len(self.intitules) == len(
set(self.champs) set(self.intitules)
), "Des champs de moyennes en doublons" ), "Des champs de moyennes en doublons"
self.etudids: list[int] = list(self.matrice_notes.index) self.etudids: list[int] = list(self.matrice_notes.index)
@ -56,10 +70,11 @@ class MoyennesTag:
self.moyennes_dict: dict[str, pe_moy.Moyenne] = {} self.moyennes_dict: dict[str, pe_moy.Moyenne] = {}
"""Dictionnaire associant à chaque UE|Compétence ses données moyenne/class/stat""" """Dictionnaire associant à chaque UE|Compétence ses données moyenne/class/stat"""
for col in self.champs: # if ue.type != UE_SPORT: for col in self.intitules: # if ue.type != UE_SPORT:
# Les moyennes tous modules confondus # Les moyennes tous modules confondus
notes = matrice_notes[col] notes = matrice_notes[col]
self.moyennes_dict[col] = pe_moy.Moyenne(notes) infos = self.infos | {"intitule": col}
self.moyennes_dict[col] = pe_moy.Moyenne(notes, infos)
# Les moyennes générales (toutes UEs confondues) # Les moyennes générales (toutes UEs confondues)
self.notes_gen = pd.Series(np.nan, index=self.matrice_notes.index) self.notes_gen = pd.Series(np.nan, index=self.matrice_notes.index)
@ -68,7 +83,8 @@ class MoyennesTag:
self.notes_gen = self.compute_moy_gen( self.notes_gen = self.compute_moy_gen(
self.matrice_notes, self.matrice_coeffs self.matrice_notes, self.matrice_coeffs
) )
self.moyenne_gen = pe_moy.Moyenne(self.notes_gen) infos = self.infos | {"intitule": CHAMP_GENERAL}
self.moyenne_gen = pe_moy.Moyenne(self.notes_gen, infos)
"""Dataframe retraçant les moyennes/classements/statistiques général (toutes UESs confondues et modules confondus)""" """Dataframe retraçant les moyennes/classements/statistiques général (toutes UESs confondues et modules confondus)"""
def has_notes(self): def has_notes(self):
@ -114,16 +130,13 @@ class MoyennesTag:
"Pb dans le calcul de la moyenne toutes UEs/compétences confondues" "Pb dans le calcul de la moyenne toutes UEs/compétences confondues"
) )
def to_df( def to_df(self, options={"min_max_moy": True}) -> pd.DataFrame:
self, aggregat=None, cohorte=None, options={"min_max_moy": True}
) -> pd.DataFrame:
"""Renvoie le df synthétisant l'ensemble des données connues. """Renvoie le df synthétisant l'ensemble des données connues.
Adapte les intitulés des colonnes aux données fournies
(nom d'aggrégat, type de cohorte).
Args: Adapte :
aggregat: Le nom de l'aggrégat (éventuellement `None` si non connu) * les noms des colonnes aux données fournies dans l'attribut
cohorte: La cohorte Groupe ou Promo (éventuellement `None` si non connue) ``infos`` (nom d'aggrégat, type de cohorte).
* à l'option ``min_max_moy`` (limitant les colonnes)
""" """
if "min_max_moy" not in options or options["min_max_moy"]: if "min_max_moy" not in options or options["min_max_moy"]:
with_min_max_moy = True with_min_max_moy = True
@ -137,60 +150,61 @@ class MoyennesTag:
df = pd.DataFrame(index=etudids_sorted) df = pd.DataFrame(index=etudids_sorted)
# Ajout des notes pour tous les champs # Ajout des notes pour tous les champs
champs = list(self.champs) champs = list(self.intitules)
for champ in champs: for champ in champs:
moy: pe_moy.Moyenne = self.moyennes_dict[champ] moy: pe_moy.Moyenne = self.moyennes_dict[champ]
df_champ = moy.to_df(with_min_max_moy=with_min_max_moy) # le dataframe df_champ = moy.to_df(
# Renomme les colonnes with_min_max_moy=with_min_max_moy
) # le dataframe (les colonnes ayant été renommées)
cols = [ colonnes_renommees = ajout_numero_a_colonnes(
get_colonne_df(aggregat, self.tag, champ, cohorte, critere) list(df.columns), list(df_champ.columns)
for critere in pe_moy.Moyenne.get_colonnes_synthese( )
with_min_max_moy=with_min_max_moy if colonnes_renommees:
) df_champ.columns = colonnes_renommees
]
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 = self.moyenne_gen.to_df(with_min_max_moy=with_min_max_moy) df_moy_gen = self.moyenne_gen.to_df(with_min_max_moy=with_min_max_moy)
cols = [ colonnes_renommees = ajout_numero_a_colonnes(
get_colonne_df(aggregat, self.tag, CHAMP_GENERAL, cohorte, critere) list(df.columns), list(df_moy_gen.columns)
for critere in pe_moy.Moyenne.get_colonnes_synthese( )
with_min_max_moy=with_min_max_moy if colonnes_renommees:
) df_moy_gen.columns = colonnes_renommees
]
df_moy_gen.columns = cols
df = df.join(df_moy_gen) df = df.join(df_moy_gen)
return df return df
def get_colonne_df(aggregat, tag, champ, cohorte, critere): def ajout_numero_a_colonnes(colonnes, colonnes_a_ajouter):
"""Renvoie le tuple (aggregat, tag, champ, cohorte, critere) """Partant d'une liste de noms de colonnes, vérifie si les noms des colonnes_a_ajouter
utilisé pour désigner les colonnes du df. n'entre pas en conflit (aka ne sont pas déjà présent dans colonnes).
Si nom, renvoie `None`.
Si oui, propose une liste de noms de colonnes_a_ajouter dans laquelle chaque nom
est suivi d'un `"(X)"` (où X est un numéro choisi au regard des noms de colonnes).
Les noms des colonnes sont de la forme "S1|maths|UE|Groupe|note (1)"
Args: Devrait être supprimé à terme, car les noms des colonnes sont théoriquement prévus pour être
aggregat: Un nom d'aggrégat (généralement "S1" ou "3S") unique/sans doublons.
pouvant être optionnel (si `None`)
tag: Un nom de tags (par ex. "maths")
champ: Un nom d'UE ou de compétences
cohorte: Une cohorte pour les interclassements (généralement
Groupe ou Promo
pouvant être optionnel (si `None`)
critere: Un critère correspondant à l'une des colonnes
d'une pe_moy.Moyenne
Returns:
Une chaine de caractères indiquant les champs séparés par
un ``"|"``, généralement de la forme
"S1|maths|UE|Groupe|note"
""" """
liste_champs = [] assert len(colonnes) == len(set(colonnes)), "Il y a déjà des doublons dans colonnes"
if aggregat != None: colonnes_sans_numero = [col.split(" (")[0] for col in colonnes]
liste_champs += [aggregat]
liste_champs += [tag, champ] conflits = set(colonnes_sans_numero).intersection(colonnes_a_ajouter)
if cohorte != None: if not conflits:
liste_champs += [cohorte] # Pas de conflit
liste_champs += [critere] return None
return "|".join(liste_champs)
pattern = r"\((\d*)\)"
p = re.compile(pattern)
numeros = []
for col in colonnes:
numeros.extend(p.findall(col))
if numeros:
numeros = [int(num) for num in numeros]
num_max = max(numeros)
else:
num_max = 0
ajouts = [f"{col} ({num_max+1})" for col in colonnes_a_ajouter]
return ajouts

View File

@ -101,7 +101,7 @@ class RCSemXTag(pe_tabletags.TableTag):
self.sxtags_connus = sxstags # Tous les sxstags connus self.sxtags_connus = sxstags # Tous les sxstags connus
# Les étudiants (etuds, états civils & etudis) # Les étudiants (etuds, états civils & etudis)
sems_dans_aggregat = rcsemx.aggregat sems_dans_aggregat = rcsemx.noms_semestres_aggreges
sxtag_final = self.sxstags_aggreges[(sems_dans_aggregat[-1], self.rcs_id[1])] sxtag_final = self.sxstags_aggreges[(sems_dans_aggregat[-1], self.rcs_id[1])]
self.etuds = sxtag_final.etuds self.etuds = sxtag_final.etuds
"""Les étudiants (extraits du semestre final)""" """Les étudiants (extraits du semestre final)"""
@ -162,11 +162,13 @@ class RCSemXTag(pe_tabletags.TableTag):
pe_affichage.pe_print(f" > Moyenne calculée avec pour coeffs : {aff}") pe_affichage.pe_print(f" > Moyenne calculée avec pour coeffs : {aff}")
# Mémorise les moyennes et les coeff associés # Mémorise les moyennes et les coeff associés
infos = {"aggregat": self.rcs_id[0], "cohorte": pe_moytag.CHAMP_GROUPE}
self.moyennes_tags[tag] = pe_moytag.MoyennesTag( self.moyennes_tags[tag] = pe_moytag.MoyennesTag(
tag, tag,
pe_moytag.CODE_MOY_COMPETENCES, pe_moytag.CODE_MOY_COMPETENCES,
moys_competences, moys_competences,
matrice_coeffs_moy_gen, matrice_coeffs_moy_gen,
infos,
) )
def __eq__(self, other): def __eq__(self, other):

View File

@ -179,20 +179,20 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
self.ues_inscr_parcours_df, info_tag=info_tag, pole=None self.ues_inscr_parcours_df, info_tag=info_tag, pole=None
) )
# Mémorise les moyennes # Mémorise les moyennes
infos = {"aggregat": None, "cohorte": pe_moytag.CHAMP_GROUPE}
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,
moy_ues_tag, moy_ues_tag,
self.matrice_coeffs_moy_gen, self.matrice_coeffs_moy_gen,
infos,
) )
# 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(self.acro_ues_inscr_parcours) moy_gen = self.compute_moy_gen(self.acro_ues_inscr_parcours)
infos = {"aggregat": None, "cohorte": pe_moytag.CHAMP_GROUPE}
self.moyennes_tags["but"] = pe_moytag.MoyennesTag( self.moyennes_tags["but"] = pe_moytag.MoyennesTag(
"but", "but", pe_moytag.CODE_MOY_UE, moy_gen, self.matrice_coeffs_moy_gen, infos
pe_moytag.CODE_MOY_UE,
moy_gen,
self.matrice_coeffs_moy_gen,
) )
# Ajoute la moyenne générale par ressources # Ajoute la moyenne générale par ressources
@ -200,11 +200,13 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
moy_res_gen = self.compute_moy_ues_tag( moy_res_gen = self.compute_moy_ues_tag(
self.ues_inscr_parcours_df, info_tag=None, pole=ModuleType.RESSOURCE self.ues_inscr_parcours_df, info_tag=None, pole=ModuleType.RESSOURCE
) )
infos = {"aggregat": None, "cohorte": pe_moytag.CHAMP_GROUPE}
self.moyennes_tags["ressources"] = pe_moytag.MoyennesTag( self.moyennes_tags["ressources"] = pe_moytag.MoyennesTag(
"ressources", "ressources",
pe_moytag.CODE_MOY_UE, pe_moytag.CODE_MOY_UE,
moy_res_gen, moy_res_gen,
self.matrice_coeffs_moy_gen, self.matrice_coeffs_moy_gen,
infos,
) )
# Ajoute la moyenne générale par saes # Ajoute la moyenne générale par saes
@ -212,11 +214,13 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
moy_saes_gen = self.compute_moy_ues_tag( moy_saes_gen = self.compute_moy_ues_tag(
self.ues_inscr_parcours_df, info_tag=None, pole=ModuleType.SAE self.ues_inscr_parcours_df, info_tag=None, pole=ModuleType.SAE
) )
infos = {"aggregat": None, "cohorte": pe_moytag.CHAMP_GROUPE}
self.moyennes_tags["saes"] = pe_moytag.MoyennesTag( self.moyennes_tags["saes"] = pe_moytag.MoyennesTag(
"saes", "saes",
pe_moytag.CODE_MOY_UE, pe_moytag.CODE_MOY_UE,
moy_saes_gen, moy_saes_gen,
self.matrice_coeffs_moy_gen, self.matrice_coeffs_moy_gen,
infos,
) )
# Tous les tags # Tous les tags

View File

@ -211,11 +211,13 @@ class SxTag(pe_tabletags.TableTag):
) )
# Mémorise les infos pour la moyenne au tag # Mémorise les infos pour la moyenne au tag
infos = {"aggregat": self.sxtag_id[0], "cohorte": pe_moytag.CHAMP_GROUPE}
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,
self.matrice_coeffs_moy_gen, self.matrice_coeffs_moy_gen,
infos,
) )
# Affichage de debug # Affichage de debug

View File

@ -90,7 +90,7 @@ class TableTag(object):
options={"min_max_moy": True}, options={"min_max_moy": True},
) -> 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/min/max/moy
des étudiants aux différents tags. des étudiants aux différents tags.
tags_cibles limitent le dataframe aux tags indiqués tags_cibles limitent le dataframe aux tags indiqués
@ -122,9 +122,7 @@ class TableTag(object):
# 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( moy_tag_df = self.moyennes_tags[tag].to_df(options=options)
aggregat=aggregat, cohorte=cohorte, options=options
)
df = df.join(moy_tag_df) df = df.join(moy_tag_df)
# Tri par nom, prénom # Tri par nom, prénom

View File

@ -11,7 +11,7 @@ from app import log
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
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

View File

@ -51,6 +51,8 @@ import numpy as np
import pandas as pd import pandas as pd
import jinja2 import jinja2
import app.pe.moys.pe_moy
import app.pe.moys.pe_moytag as pe_moytag
from app.pe.rcss import pe_rcs from app.pe.rcss import pe_rcs
from app.pe.moys import pe_sxtag from app.pe.moys import pe_sxtag
@ -730,20 +732,32 @@ class JuryPE(object):
) )
if not df_groupe.empty: if not df_groupe.empty:
aff_aggregat += [aggregat + " (Groupe)"] aff_aggregat += [aggregat + " (Groupe)"]
colonnes_renommees = pe_moytag.ajout_numero_a_colonnes(
list(df.columns), list(df_groupe.columns)
)
if colonnes_renommees:
df_groupe.columns = colonnes_renommees
df = df.join(df_groupe) df = df.join(df_groupe)
# Le dataframe du classement sur la promo # Le dataframe du classement sur la promo
df_promo = interclass.to_df( df_promo = interclass.to_df(
administratif=False, administratif=False,
aggregat=aggregat, aggregat=aggregat,
tags_cibles=[tag], tags_cibles=[tag],
cohorte="Promo", cohorte="Promo",
options=self.options, options=self.options,
) )
if not df_promo.empty: if not df_promo.empty:
aff_aggregat += [aggregat + " (Promo)"] aff_aggregat += [aggregat + " (Promo)"]
df = df.join(df_promo) colonnes_renommees = pe_moytag.ajout_numero_a_colonnes(
list(df.columns), list(df_groupe.columns)
)
if colonnes_renommees:
df_promo.columns = colonnes_renommees
df = df.join(df_promo)
if aff_aggregat: if aff_aggregat:
pe_affichage.pe_print( pe_affichage.pe_print(
@ -806,7 +820,7 @@ class JuryPE(object):
"rang_groupe": "", "rang_groupe": "",
"rang_promo": "", "rang_promo": "",
} }
colonne = pe_moytag.get_colonne_df( colonne = app.pe.moys.pe_moy.get_colonne_df(
aggregat, tag, comp, "Groupe", "note" aggregat, tag, comp, "Groupe", "note"
) )
if colonne in df.columns: if colonne in df.columns:
@ -816,14 +830,14 @@ class JuryPE(object):
est_significatif = True est_significatif = True
# else: # else:
# print(f"{colonne} manquante") # print(f"{colonne} manquante")
colonne = pe_moytag.get_colonne_df( colonne = app.pe.moys.pe_moy.get_colonne_df(
aggregat, tag, comp, "Groupe", "rang" aggregat, 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":
moy[comp]["rang_groupe"] = valeur moy[comp]["rang_groupe"] = valeur
colonne = pe_moytag.get_colonne_df( colonne = app.pe.moys.pe_moy.get_colonne_df(
aggregat, tag, comp, "Promo", "rang" aggregat, tag, comp, "Promo", "rang"
) )
if colonne in df.columns: if colonne in df.columns:

View File

@ -92,8 +92,8 @@ class RCS:
"""Nom du RCS""" """Nom du RCS"""
assert self.nom in TOUS_LES_RCS, "Le nom d'un RCS doit être un aggrégat" assert self.nom in TOUS_LES_RCS, "Le nom d'un RCS doit être un aggrégat"
self.aggregat: list[str] = TYPES_RCS[nom]["aggregat"] self.noms_semestres_aggreges: list[str] = TYPES_RCS[nom]["aggregat"]
"""Aggrégat (liste des nom des semestres aggrégés)""" """Noms des semestres aggrégés)"""
self.formsemestre_final: FormSemestre = semestre_final self.formsemestre_final: FormSemestre = semestre_final
"""(Form)Semestre final du RCS""" """(Form)Semestre final du RCS"""

View File

@ -4,6 +4,7 @@ Test calcul moyennes pour les poursuites d'études
import numpy as np import numpy as np
import pytest import pytest
import app.pe.moys.pe_moy
from tests.unit import setup from tests.unit import setup
from app import db from app import db
@ -24,10 +25,17 @@ def egalite_df(df1, df2):
class Test_pe_moy: class Test_pe_moy:
infos = {
"aggregat": "S1",
"tag": "maths",
"intitule": "UE1.1",
"cohorte": "groupe",
}
def test_init(self): def test_init(self):
"""Test de pe_moy.Moyenne.__init__""" """Test de pe_moy.Moyenne.__init__"""
notes = pd.Series({1: 10.0, 2: 14.0, 3: np.nan, 4: 0.0}) notes = pd.Series({1: 10.0, 2: 14.0, 3: np.nan, 4: 0.0})
moy = pe_moy.Moyenne(notes) moy = pe_moy.Moyenne(notes, Test_pe_moy.infos)
assert moy.etudids == [1, 2, 3, 4], "Etudids incorrect" assert moy.etudids == [1, 2, 3, 4], "Etudids incorrect"
assert moy.inscrits_ids == [1, 2, 4], "Inscriptions incorrectes" assert moy.inscrits_ids == [1, 2, 4], "Inscriptions incorrectes"
# Les notes # Les notes
@ -67,7 +75,7 @@ class Test_pe_moy:
def test_init_ex_aequo(self): def test_init_ex_aequo(self):
"""Test de pe_moy.Moyenne.__init__ pour des ex-aequo""" """Test de pe_moy.Moyenne.__init__ pour des ex-aequo"""
notes = pd.Series({1: 10.0, 2: 14.0, 3: 10.0, 4: 0.0}) notes = pd.Series({1: 10.0, 2: 14.0, 3: 10.0, 4: 0.0})
moy = pe_moy.Moyenne(notes) moy = pe_moy.Moyenne(notes, Test_pe_moy.infos)
# Les rangs # Les rangs
rang = pd.Series(["2 ex/4", "1/4", "2 ex/4", "3/4"], index=[1, 2, 3, 4]) rang = pd.Series(["2 ex/4", "1/4", "2 ex/4", "3/4"], index=[1, 2, 3, 4])
assert moy.df["rang"].isnull().sum() == 0, "Des Nan dans les rangs interdits" assert moy.df["rang"].isnull().sum() == 0, "Des Nan dans les rangs interdits"
@ -84,16 +92,11 @@ class Test_pe_moy:
], ],
) )
def test_has_notes(self, notes, resultat): def test_has_notes(self, notes, resultat):
moy = pe_moy.Moyenne(notes) moy = pe_moy.Moyenne(notes, Test_pe_moy.infos)
assert ( assert (
moy.has_notes() == resultat moy.has_notes() == resultat
), "Le test sur la présence de notes est incorrect" ), "Le test sur la présence de notes est incorrect"
# ******************************
# app.pe.moys.pe_moytag
# ******************************
class Test_pe_moytag:
@pytest.mark.parametrize( @pytest.mark.parametrize(
"aggregat, tag, champ, cohorte, critere, attendu", "aggregat, tag, champ, cohorte, critere, attendu",
[ [
@ -121,9 +124,36 @@ class Test_pe_moytag:
], ],
) )
def test_colonnes_df(self, aggregat, tag, champ, cohorte, critere, attendu): def test_colonnes_df(self, aggregat, tag, champ, cohorte, critere, attendu):
descr = pe_moytag.get_colonne_df(aggregat, tag, champ, cohorte, critere) descr = app.pe.moys.pe_moy.get_colonne_df(
aggregat, tag, champ, cohorte, critere
)
assert descr == attendu, "Nom de colonne incorrect" assert descr == attendu, "Nom de colonne incorrect"
def to_df(self):
notes = pd.Series({1: 10.0, 2: 14.0, 3: np.nan, 4: 0.0})
infos = {
"aggregat": "S1",
"tag": "maths",
"intitule": "Général",
"cohorte": "promo",
}
cols = [
f"S1|maths|Général|promo|{critere}"
for critere in ["note", "rang", "min", "max", "moy"]
]
moy = pe_moy.Moyenne(notes, infos)
df = moy.to_df(with_min_max_moy=True)
assert (
list(df.columns) == cols
), "Colonnes du df de synthèse pour Excel pas correctes"
# ******************************
# app.pe.moys.pe_moytag
# ******************************
class Test_pe_moytag:
infos = {"aggregat": "S1", "cohorte": "promo"}
def test_moyennes_tag__init__(self): def test_moyennes_tag__init__(self):
matrice_notes = pd.DataFrame.from_dict( matrice_notes = pd.DataFrame.from_dict(
{ {
@ -149,7 +179,9 @@ class Test_pe_moytag:
orient="index", orient="index",
columns=["UE1.1", "UE1.2", "UE1.3"], columns=["UE1.1", "UE1.2", "UE1.3"],
) )
moy_tag = pe_moytag.MoyennesTag("maths", None, matrice_notes, matrice_coeffs) moy_tag = pe_moytag.MoyennesTag(
"maths", None, matrice_notes, matrice_coeffs, Test_pe_moytag.infos
)
attendu = pd.Series( attendu = pd.Series(
[ [
(12 * 1 + 14 * 2 + 15 * 3) / (1 + 2 + 3), (12 * 1 + 14 * 2 + 15 * 3) / (1 + 2 + 3),
@ -163,35 +195,9 @@ class Test_pe_moytag:
) )
assert egalite_df(moy_tag.notes_gen, attendu), "La moyenne n'est pas correcte" assert egalite_df(moy_tag.notes_gen, attendu), "La moyenne n'est pas correcte"
def test_to_df(self): def test_to_df_sans_renommage_colonne(self):
matrice_notes = pd.DataFrame.from_dict(
{
1: [12.0, 14.0, 15.0],
2: [8.0, np.nan, 12.0],
3: [0.0, 11.0, 13.0],
4: [np.nan, np.nan, np.nan],
5: [np.nan, np.nan, np.nan],
6: [0.0, 0.0, 0.0],
},
orient="index",
columns=["UE1.1", "UE1.2", "UE1.3"],
)
matrice_coeffs = pd.DataFrame.from_dict(
{
1: [1, 2, 3],
2: [2, 10, 6],
3: [1, 2, np.nan],
4: [5, 4, 3],
5: [np.nan, np.nan, np.nan],
6: [1, 1, 1],
},
orient="index",
columns=["UE1.1", "UE1.2", "UE1.3"],
)
moy_tag = pe_moytag.MoyennesTag("maths", None, matrice_notes, matrice_coeffs)
def test_to_df(self):
"""Test le dataframe de synthèse""" """Test le dataframe de synthèse"""
infos = {"aggregat": "S1", "cohorte": "promo"}
matrice_notes = pd.DataFrame.from_dict( matrice_notes = pd.DataFrame.from_dict(
{ {
2: [13.0, 13.0, 13], 2: [13.0, 13.0, 13],
@ -208,14 +214,16 @@ class Test_pe_moytag:
orient="index", orient="index",
columns=["UE1.1", "UE1.2", "UE1.3"], columns=["UE1.1", "UE1.2", "UE1.3"],
) )
moy_tag = pe_moytag.MoyennesTag("maths", None, matrice_notes, matrice_coeffs) moy_tag = pe_moytag.MoyennesTag(
"maths", None, matrice_notes, matrice_coeffs, infos
)
synthese = moy_tag.to_df( synthese = moy_tag.to_df(
aggregat="S1", cohorte="groupe", options={"min_max_moy": True} aggregat="S1", cohorte="promo", options={"min_max_moy": True}
) )
colonnes_attendues = [] colonnes_attendues = []
for ue in ["UE1.1", "UE1.2", "UE1.3", "Général"]: for ue in ["UE1.1", "UE1.2", "UE1.3", "Général"]:
for champ in ["note", "rang", "min", "max", "moy"]: for champ in ["note", "rang", "min", "max", "moy"]:
colonnes_attendues += [f"S1|maths|{ue}|groupe|{champ}"] colonnes_attendues += [f"S1|maths|{ue}|promo|{champ}"]
assert ( assert (
list(synthese.columns) == colonnes_attendues list(synthese.columns) == colonnes_attendues
), "Les colonnes de synthèse ne sont pas correctes" ), "Les colonnes de synthèse ne sont pas correctes"
@ -224,6 +232,26 @@ class Test_pe_moytag:
2, 2,
], "Les lignes ne sont pas triées par id d'étudiants" ], "Les lignes ne sont pas triées par id d'étudiants"
@pytest.mark.parametrize(
"colonnes, ajouts, resultat",
[
pytest.param(["toto", "titi"], ["tutu"], None, id="pas de modif"),
pytest.param(["toto", "titi"], ["titi"], ["titi (1)"], id="ajout de (1)"),
pytest.param(
["toto", "toto (1)"], ["toto"], ["toto (2)"], id="ajout de (2)"
),
pytest.param(
["toto (1)", "titi (3)"],
["toto", "titi"],
["toto (4)", "titi (4)"],
id="ajout multiple",
),
],
)
def test_renomme_colonnes(self, colonnes, ajouts, resultat):
res = pe_moytag.ajout_numero_a_colonnes(colonnes, ajouts)
assert res == resultat, "L'ajout d'un numéro au colonne est incorrect"
# ****************************** # ******************************
# app.pe.moys.pe_rcstag # app.pe.moys.pe_rcstag