import numpy as np import pandas as pd from app import comp from app.comp.moy_sem import comp_ranks_series from app.pe.moys import pe_moy from app.scodoc.sco_utils import ModuleType CODE_MOY_UE = "UEs" CODE_MOY_COMPETENCES = "Compétences" CHAMP_GENERAL = "Général" # Nom du champ de la moyenne générale class MoyennesTag: def __init__( self, tag: str, type_moyenne: str, matrice_notes: pd.DataFrame, # etudids x UEs|comp matrice_coeffs: pd.DataFrame, # etudids x UEs|comp ): """Classe centralisant la synthèse des moyennes/classements d'une série d'étudiants à un tag donné, en différenciant les notes obtenues aux UE et au général (toutes UEs confondues) Args: tag: Un tag matrice_notes: Les moyennes (etudid x acronymes_ues|compétences) aux différentes UEs ou compétences matrice_coeffs: Les coeffs (etudid x acronymes_ues|compétences) aux différentes UEs ou compétences """ self.tag = tag """Le tag associé aux moyennes""" self.type = type_moyenne """Le type de moyennes (par UEs ou par compétences)""" # Les moyennes par UE/compétences (ressources/SAEs confondues) self.matrice_notes: pd.DataFrame = matrice_notes """Les notes par UEs ou Compétences (DataFrame etudids x UEs|comp)""" self.matrice_coeffs: pd.DataFrame = matrice_coeffs """Les coeffs à appliquer pour le calcul des moyennes générales (toutes UE ou compétences confondues). NaN si étudiant non inscrit""" self.champs: list[str] = list(self.matrice_notes.columns) """Les champs (acronymes d'UE ou compétences) renseignés dans les moyennes""" assert len(self.champs) == len( set(self.champs) ), "Des champs de moyennes en doublons" self.etudids: list[int] = list(self.matrice_notes.index) """Les étudids renseignés dans les moyennes""" self.moyennes_dict: dict[str, pe_moy.Moyenne] = {} """Dictionnaire associant à chaque UE|Compétence ses données moyenne/class/stat""" for col in self.champs: # if ue.type != UE_SPORT: # Les moyennes tous modules confondus notes = matrice_notes[col] self.moyennes_dict[col] = pe_moy.Moyenne(notes) # Les moyennes générales (toutes UEs confondues) self.notes_gen = pd.Series(np.nan, index=self.matrice_notes.index) """Notes de la moyenne générale (toutes UEs|Comp confondues)""" if self.has_notes(): self.notes_gen = self.compute_moy_gen( self.matrice_notes, self.matrice_coeffs ) self.moyenne_gen = pe_moy.Moyenne(self.notes_gen) """Dataframe retraçant les moyennes/classements/statistiques général (toutes UESs confondues et modules confondus)""" def has_notes(self): """Détermine si les moyennes (aux UEs ou aux compétences) ont des notes Returns: True si la moytag a des notes, False sinon """ for col, moy in self.moyennes_dict.items(): if not moy.has_notes(): return False return True # notes = self.matrice_notes # 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 compute_moy_gen(self, moys: pd.DataFrame, coeffs: pd.DataFrame) -> pd.Series: """Calcule la moyenne générale (toutes UE/compétences confondus), en pondérant les notes obtenues aux UEs|Compétences par les coeff (ici les crédits ECTS). Args: moys: Les moyennes (etudids x acronymes_ues/compétences) coeff: Les coeff (etudids x acronymes_ues/compétences) """ # Calcule la moyenne générale dans le semestre (pondérée par le ECTS) try: moy_gen_tag = comp.moy_sem.compute_sem_moys_apc_using_ects( moys, coeffs.fillna(0.0), # formation_id=self.formsemestre.formation_id, skip_empty_ues=True, ) return moy_gen_tag except TypeError as e: raise TypeError( "Pb dans le calcul de la moyenne toutes UEs/compétences confondues" ) def to_df( self, aggregat=None, cohorte=None, options={"min_max_moy": True} ) -> pd.DataFrame: """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: aggregat: Le nom de l'aggrégat (éventuellement `None` si non connu) cohorte: La cohorte Groupe ou Promo (éventuellement `None` si non connue) """ if "min_max_moy" not in options or options["min_max_moy"]: with_min_max_moy = True else: with_min_max_moy = False # Les étudiants triés par etudid etudids_sorted = sorted(self.etudids) # Le dataFrame à générer df = pd.DataFrame(index=etudids_sorted) # Ajout des notes pour tous les champs champs = list(self.champs) for champ in champs: moy: pe_moy.Moyenne = self.moyennes_dict[champ] df_champ = moy.to_df(with_min_max_moy=with_min_max_moy) # le dataframe # Renomme les colonnes cols = [ get_colonne_df(aggregat, self.tag, champ, cohorte, critere) for critere in pe_moy.Moyenne.get_colonnes_synthese( with_min_max_moy=with_min_max_moy ) ] df_champ.columns = cols df = df.join(df_champ) # Ajoute la moy générale df_moy_gen = self.moyenne_gen.to_df(with_min_max_moy=with_min_max_moy) cols = [ get_colonne_df(aggregat, self.tag, CHAMP_GENERAL, cohorte, critere) for critere in pe_moy.Moyenne.get_colonnes_synthese( with_min_max_moy=with_min_max_moy ) ] df_moy_gen.columns = cols df = df.join(df_moy_gen) return df def get_colonne_df(aggregat, tag, champ, cohorte, critere): """Renvoie le tuple (aggregat, tag, champ, 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") 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 = [] if aggregat != None: liste_champs += [aggregat] liste_champs += [tag, champ] if cohorte != None: liste_champs += [cohorte] liste_champs += [critere] return "|".join(liste_champs)