diff --git a/app/pe/pe_etudiant.py b/app/pe/pe_etudiant.py index 45c5bc23..cda39fa1 100644 --- a/app/pe/pe_etudiant.py +++ b/app/pe/pe_etudiant.py @@ -55,15 +55,17 @@ class EtudiantsJuryPE: def __init__(self): """ """ - "Les identités des étudiants du jury" + "Les identités des étudiants traités pour le jury" self.identites = {} # ex. ETUDINFO_DICT - "Les cursus (semestres suivis, abandons, ...)" + "Les cursus (semestres suivis, abandons, ...) des étudiants" self.cursus = {} - "Les etudids des étudiants à considérer au jury" + + "Les etudids des étudiants à considérer au jury (ceux qui seront effectivement diplômés)" self.etudiants_jury_ids = {} - "Les etudids des étudiants dont il faut calculer les moyennes/classements" + "Les etudids des étudiants dont il faut calculer les moyennes/classements (même si d'éventuels abandons)" self.etudiants_ids = {} - "Les formsemestres dont il faut calculer les moyennes" + + "Les formsemestres dont il faut calculer les moyennes par tag" self.formsemestres_jury_ids = {} def find_etudiants(self, annee_diplome: int, formation_id: int): @@ -75,38 +77,46 @@ class EtudiantsJuryPE: Args: annee_diplome: L'année de diplomation - formation_id: L'identifiant de la formation + formation_id: L'identifiant de la formation (inutilisé) *Remarque* : ex: JuryPE.get_etudiants_in_jury() """ "Les cosemestres donnant lieu à même année de diplome" - cosemestres = pe_tools.get_cosemestres_diplomants( - annee_diplome, None # formation_id, - ) + cosemestres = pe_tools.get_cosemestres_diplomants(annee_diplome, None) pe_tools.pe_print( "1) Recherche des coSemestres -> %d trouvés" % len(cosemestres) ) """Les étudiants inscrits dans les co-semestres (ceux du jury mais aussi d'autres ayant été réorientés ou ayant abandonnés)""" pe_tools.pe_print("2) Liste des étudiants dans les différents co-semestres") - self.etudiants_ids = get_etudiants_dans_semestres( - cosemestres - ) # étudiants faisant partie de tous les cosemestres - pe_tools.pe_print(" => %d étudiants trouvés" % len(self.etudiants_ids)) + self.etudiants_ids = get_etudiants_dans_semestres(cosemestres) + pe_tools.pe_print( + " => %d étudiants trouvés dans les cosemestres" % len(self.etudiants_ids) + ) - # L'analyse des parcours étudiants pour déterminer leur année effective de diplome avec prise en compte des redoublements, des abandons, .... + """Analyse des parcours étudiants pour déterminer leur année effective de diplome + avec prise en compte des redoublements, des abandons, ....""" pe_tools.pe_print("3) Analyse des parcours individuels des étudiants") no_etud = 0 for no_etud, etudid in enumerate(self.etudiants_ids): - self.add_etudid(etudid, cosemestres) + """L'identité de l'étudiant""" + identite = Identite.get_etud(etudid) + self.identites[etudid] = identite + + """L'analyse de son cursus""" + self.analyse_etat_etudiant(etudid, cosemestres) + + """L'analyse de son parcours pour atteindre chaque semestre de la formation""" + self.analyse_parcours_etudiant_dans_semestres(etudid) + if (no_etud + 1) % 10 == 0: pe_tools.pe_print((no_etud + 1), " ", end="") no_etud += 1 pe_tools.pe_print() """Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris""" - self.etudiants_jury_ids = self.get_etudids(annee_diplome) + self.etudiants_jury_ids = self.get_etudiants(annee_diplome) """Les étudiants dont il faut calculer les moyennes""" self.etudiants_ids = {etudid for etudid in self.cursus} @@ -129,89 +139,48 @@ class EtudiantsJuryPE: + ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)]) ) - def get_etudids(self, annee_diplome: int = None, ordre="aucun") -> list: - """Liste des etudid des étudiants qui vont être à traiter au jury PE pour - l'année de diplômation donnée et n'ayant ni été réorienté, ni abandonné. - - Si l'année de diplômation n'est pas précisée (None), inclus les étudiants réorientés - ou ayant abandonné. - - Si l'``ordre`` est précisé, trie la liste par ordre alphabétique de etat_civil - - Args: - annee_diplome: Année de diplomation visée pour le jury - ordre: Un ordre de tri - - Returns: - Une liste contenant des ``etudids`` - - Note: ex JuryPE.get_etudids_du_jury() - """ - if annee_diplome: - etudids = [ - etudid - for (etudid, donnees) in self.cursus.items() - if donnees["diplome"] == annee_diplome and not donnees["abandon"] - ] - else: - etudids = [ - etudid - for (etudid, donnees) in self.cursus.items() - ] - if ordre == "alphabetique": # Tri alphabétique - etudidsAvecNom = [ - (etudid, etud["etat_civil"]) - for (etudid, etud) in self.cursus.items() - if etudid in etudids - ] - etudidsAvecNomTrie = sorted(etudidsAvecNom, key=lambda col: col[1]) - etudids = [etud[0] for etud in etudidsAvecNomTrie] - return etudids - - def get_etudiants(self, annee_diplome: int = None) -> dict[Identite]: + def get_etudiants(self, annee_diplome: int) -> dict[Identite]: """Identités des étudiants (sous forme d'un dictionnaire `{etudid: Identite(etudid)}` qui vont être à traiter au jury PE pour l'année de diplômation donnée et n'ayant ni été réorienté, ni abandonné. - Si l'année de diplômation n'est pas précisée (None), inclus les étudiants réorientés - ou ayant abandonné. - Args: annee_diplome: Année de diplomation visée pour le jury Returns: Un dictionnaire `{etudid: Identite(etudid)}` """ - etudids = self.get_etudids(annee_diplome=annee_diplome) - etudiants = {etudid: self.identites[etudids] for etudid in etudids} + etudids = [ + etudid + for etudid in self.cursus + if self.cursus[etudid]["diplome"] == annee_diplome and self.cursus[etudid]["abandon"] + ] + etudiants = {etudid: self.identites[etudid] for etudid in etudids} return etudiants - def add_etudid(self, etudid: int, cosemestres): - """Ajoute un étudiant à ceux qui devront être traités pendant le jury pouvant être : - * des étudiants sur lesquels le jury va statuer (année de diplômation du jury considéré) - * des étudiants qui ne seront pas considérés dans le jury mais ont participé dans leur scolarité - à un (ou plusieurs) semestres communs aux étudiants du jury (et impacteront les classements) + def analyse_etat_etudiant( + self, etudid: int, cosemestres: dict[int, FormSemestre] + ): + """Analyse le cursus d'un étudiant pouvant être : - L'ajout consiste : + * l'un de ceux sur lesquels le jury va statuer (année de diplômation du jury considéré) + * un étudiant qui ne sera pas considéré dans le jury mais qui a participé dans sa scolarité + à un (ou plusieurs) semestres communs aux étudiants du jury (et impactera les classements) - * à insérer une entrée pour l'étudiant en mémorisant son identité, + L'analyse consiste : + + * à insérer une entrée dans ``self.cursus`` pour mémoriser son identité, avec son nom, prénom, etc... * à analyser son parcours, pour déterminer s'il n'a (ou non) abandonné l'IUT en cours de route (cf. clé abandon) - * à chercher ses semestres valides (formsemestre_id) et ses années valides (formannee_id), - c'est-à-dire ceux pour lesquels il faudra prendre en compte ses notes dans les calculs de - moyenne (type 1A=S1+S2/2) Args: etudid: L'etudid d'un étudiant, à ajouter à ceux traiter par le jury - cosemestres: Dictionnaire {fid: Formsemestre(fid)} donnant accès aux cosemestres de même année de diplomation - Note: ex JuryPE.add_etudid_to_jury() + cosemestres: Dictionnaire {fid: Formsemestre(fid)} donnant accès aux cosemestres + de même année de diplomation """ - - """L'identité de l'étudiant""" identite = Identite.get_etud(etudid) - self.identites[etudid] = identite """Le cursus global de l'étudiant (restreint aux semestres APC)""" semestres_etudiant = { @@ -225,38 +194,121 @@ class EtudiantsJuryPE: "etat_civil": identite.etat_civil, # Ajout à la table jury "diplome": annee_diplome(identite), # Le date prévisionnelle de son diplôme "formsemestres": semestres_etudiant, # les semestres de l'étudiant + "semestres": {}, + "aggregats": {}, } """ Est-il réorienté / démissionnaire ou a-t-il arrêté volontairement sa formation ?""" self.cursus[etudid]["abandon"] = arret_de_formation(identite, cosemestres) - """Tri des semestres par n° de semestre""" + + def analyse_parcours_etudiant_dans_semestres(self, etudid): + """Structure les informations sur les semestres suivis par un + étudiant, pour identifier les semestres qui seront pris en compte lors de ses calculs + de moyennes PE. + + La structure s'appuie sur les numéros de semestre: pour chaque Si, stocke : + * le (ou les) formsemestres de numéro i qu'a suivi un étudiant (2 si redoublant) + * le dernier semestre de numéro i qu'il a suivi (1 ou 0 si pas encore suivi) + + Elle s'appuie également sur les aggrégats: pour chaque aggrégat (par ex, 3A=S1+S2+S3), + identifie les semestres que l'étudiant a suivi pour l'amener jusqu'au semestre terminal + de l'aggrégat. Ce parcours peut être : + * S1+S2+S1+S2+S3 si redoublement de la 1ère année + * S1+S2+(année de césure)+S3 si césure, ... + """ + semestres_etudiant = self.cursus[etudid]["formsemestres"] + for nom_sem in pe_tools.TOUS_LES_SEMESTRES: - i = int(nom_sem[1]) + 1 # le n° du semestre + i = int(nom_sem[1]) # le n° du semestre semestres_i = { fid: semestres_etudiant[fid] for fid in semestres_etudiant if semestres_etudiant[fid].semestre_id == i } # les semestres de n°i de l'étudiant - dernier_semestre_i = get_dernier_semestre(semestres_i) - self.cursus[etudid][nom_sem] = dernier_semestre_i + self.cursus[etudid]["aggregats"][nom_sem] = semestres_i + self.cursus[etudid]["semestres"][nom_sem] = get_dernier_semestre(semestres_i) - """Tri des semestres par aggrégat""" - for parcours in pe_tools.TOUS_LES_AGGREGATS: - """L'aggrégat considéré""" - noms_semestre_de_aggregat = pe_tools.PARCOURS[parcours]["aggregat"] - self.cursus[etudid][parcours] = {} - for nom_sem in noms_semestre_de_aggregat: - self.cursus[etudid][parcours] = ( - self.cursus[etudid][parcours] | self.cursus[etudid][nom_sem] - ) + """Tri des semestres par aggrégat et par semestre terminal""" + for aggregat in pe_tools.TOUS_LES_AGGREGATS: + self.cursus[etudid][aggregat] = {} + """L'aggrégat considéré (par ex: 3S), son nom de son semestre terminal (par ex: S3) et son numéro (par ex: 3)""" + noms_semestre_de_aggregat = pe_tools.PARCOURS[aggregat]["aggregat"] + nom_semestre_terminal = noms_semestre_de_aggregat[-1] + numero_semestre_terminal = int(nom_semestre_terminal[-1]) - if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2: - pe_tools.pe_print( - parcours + "=" + str(self.cursus[etudid][parcours]), - end="", - ) + """Les semestres terminaux de l'aggrégat""" + # formsemestres_terminal = self.cursus[etudid]["aggregats"][nom_semestre_terminal] + # dernier_formsemestre_terminal = get_dernier_semestre(formsemestres_terminal) # le dernier en date + dernier_formsemestre_terminal = self.cursus[etudid]["semestres"][nom_semestre_terminal] + + # for formsem_id_term in formsemestres_terminal: + if dernier_formsemestre_terminal: # ne considérant que le dernier + formsem_id_term = list(dernier_formsemestre_terminal.keys())[0] + + formsemestre_terminal = self.cursus[etudid]["formsemestres"][formsem_id_term] + + """Semestres de n° inférieur (pax ex: des S1, S2, S3 pour un S3 terminal) et qui lui sont antérieurs""" + semestres_aggreges = {} + for fid in self.cursus[etudid]["formsemestres"]: + semestre = self.cursus[etudid]["formsemestres"][fid] + if ( + semestre.semestre_id <= numero_semestre_terminal + and semestre.date_fin <= formsemestre_terminal.date_fin + ): + semestres_aggreges[fid] = semestre + + self.cursus[etudid][aggregat][formsem_id_term] = semestres_aggreges + + def get_formsemestres_terminaux_aggregat(self, aggregat: str): + """Pour un aggrégat donné, ensemble des formsemestres terminaux possibles pour l'aggrégat (pour l'aggrégat '3S' + incluant S1+S2+S3, a pour semestre terminal S3). Ces formsemestres traduisent : + + * les différents parcours des étudiants liés par exemple au choix de modalité (par ex: S1 FI + S2 FI + S3 FI + ou S1 FI + S2 FI + S3 UFA), en renvoyant les formsemestre_id du S3 FI et du S3 UFA. + * les éventuelles situations de redoublement (par ex pour 1 étudiant ayant redoublé sa 2ème année : + S1 + S2 + S3 (1ère session) et S1 + S2 + S3 + S4 + S3 (2ème session), en renvoyant les formsemestre_id du + S3 (1ère session) et du S3 (2ème session) + + Args: + aggregat: L'aggrégat + + Returns: + Un dictionnaire {fid: FormSemestre(fid)} + """ + formsemestres_terminaux = {} + for etudid in self.cursus: + """Les formsemestre_id des semestres terminaux""" + fids = self.cursus[etudid][aggregat].keys() + """Pour chaque identifiant de semestre terminal, récupère le formsemestre associé""" + for fid in fids: + if fid not in formsemestres_terminaux: + formsemestres_terminaux[fid] = self.cursus[etudid][aggregat][fid][ + fid + ] + return formsemestres_terminaux + + def get_semestres_a_aggreger(self, aggregat: str, formsemestre_id_terminal: int): + """Pour un aggrégat donné associé à un formsemestre terminal cible, renvoie l'ensemble des semestres à + prendre en compte dans l'aggrégat sous la forme d'un dictionnaire {fid: FormSemestre(fid)}. + + Fusionne les cursus individuels des étudiants, dont le cursus correspond à l'aggrégat visé. + + Args: + aggregat: Un aggrégat (par ex. 1A, 2A, 3S, 6S) + formsemestre_id_terminal: L'identifiant du formsemestre terminal de l'aggrégat, devant correspondre au + dernier semestre de l'aggrégat + """ + noms_semestres_aggreges = pe_tools.PARCOURS[aggregat]["aggregat"] + + formsemestres = {} + for etudid in self.cursus: + cursus_etudiant = self.cursus[etudid][aggregat] + if formsemestre_id_terminal in cursus_etudiant: + formsemestres_etudiant = cursus_etudiant[formsemestre_id_terminal] + formsemestres = formsemestres | formsemestres_etudiant + return formsemestres def get_formsemestres_jury(self, semestres_recherches=None): """Ayant connaissance des étudiants dont il faut calculer les moyennes pour @@ -310,7 +362,7 @@ class EtudiantsJuryPE: nom_sem = semestres_recherches semestres = {} for etudid in self.etudiants_ids: - semestres = semestres | self.cursus[etudid][nom_sem] + semestres = semestres | self.cursus[etudid]["aggregats"][nom_sem] return semestres else: raise ValueError( @@ -368,25 +420,6 @@ def annee_diplome(identite: Identite) -> int: return None -def semestres_etudiant(etudid: int, semestre_id=None): - """La liste des semestres BUT d'un étudiant - pour un semestre_id (parmi 1, 2, 3, 4, 5, 6) donné - en fonction de ses infos d'etud (cf. sco_etud.get_etud_info(etudid=etudid, filled=True)[0]), - les semestres étant triés par ordre décroissant. - Si semestre_id == None renvoie tous les semestres - - NOTE:: ex:: JuryPE.get_semestresBUT_d_un_etudiant() - TODO:: A revoir""" - etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] - nbre_semestres = int(pe_tools.AGGREGAT_DIPLOMANT[0]) # 6 - if semestre_id == None: - sesSems = [ - sem for sem in etud["sems"] if 1 <= sem["semestre_id"] <= nbre_semestres - ] - else: - sesSems = [sem for sem in etud["sems"] if sem["semestre_id"] == semestre_id] - return sesSems - def arret_de_formation(identite: Identite, cosemestres: list[FormSemestre]) -> bool: """Détermine si un étudiant a arrêté sa formation. Il peut s'agir : @@ -447,9 +480,10 @@ def arret_de_formation(identite: Identite, cosemestres: list[FormSemestre]) -> b return False -def get_dernier_semestre(semestres: dict[FormSemestre]): +def get_dernier_semestre(semestres: dict[int, FormSemestre]): """Renvoie le dernier semestre en date d'un dictionnaire - de semestres de la forme {fid: FormSemestre(fid) + de semestres de la forme {fid: FormSemestre(fid)}. + La date prise en compte est celle marquant la **fin** des semestres. Args: semestres: Un dictionnaire de semestres diff --git a/app/pe/pe_jurype.py b/app/pe/pe_jurype.py index dad54978..70fa3297 100644 --- a/app/pe/pe_jurype.py +++ b/app/pe/pe_jurype.py @@ -48,11 +48,13 @@ import os from zipfile import ZipFile import app.pe.pe_etudiant +import app.pe.pe_settag_interclasse from app.comp import res_sem from app.comp.res_compat import NotesTableCompat from app.comp.res_sem import load_formsemestre_results from app.models import Formation, FormSemestre from app.models.etudiants import Identite +from app.pe.pe_semestretag import SemestreTag from app.scodoc.gen_tables import GenTable, SeqGenTable import app.scodoc.sco_utils as scu @@ -117,13 +119,6 @@ class JuryPE(object): meme_programme: si True, impose un même programme pour tous les étudiants participant au jury, si False, permet des programmes differents """ - - self.semTagDict = ( - {} - ) # Les semestres taggués à la base des calculs de moyenne par tag - self.setTagDict = ( - {} - ) # dictionnaire récapitulant les semTag impliqués dans le jury de la forme { 'formsemestre_id' : object Semestre_tag self.promoTagDict = {} "L'année du diplome" @@ -137,18 +132,45 @@ class JuryPE(object): self.zipdata = io.BytesIO() self.zipfile = ZipFile(self.zipdata, "w") - "Les informations sur les étudiants édités par le jury PE" - self.etudiants = EtudiantsJuryPE() # Les infos sur les étudiants self.syntheseJury = {} # Le jury de synthèse """Chargement des étudiants à prendre en compte dans le jury""" pe_tools.pe_print( f"*** Recherche et chargement des étudiants diplômés en {self.diplome} pour la formation {self.formation_id}" ) + self.etudiants = EtudiantsJuryPE() # Les infos sur les étudiants self.etudiants.find_etudiants(self.diplome, self.formation_id) - """Calcul des moyennes pour le jury PE""" - self.exe_calculs_juryPE() + """Génère les semestres taggués (avec le calcul des moyennes) pour le jury PE""" + self.semestres_taggues = compute_semestres_tag(self.etudiants) + + if pe_tools.PE_DEBUG: + """Intègre le bilan des semestres taggués au zip final""" + for fid in self.semestres_taggues: + formsemestretag = self.semestres_taggues[fid] + filename = formsemestretag.nom.replace(" ", "_") + ".csv" + pe_tools.pe_print(f" - Export csv de {filename} ") + self.add_file_to_zip( + filename, formsemestretag.str_tagtable(), path="details_semestres" + ) + + """Génère les aggrégats de semestre (par ex: 1A, 3S, 5S) avec calcul + des moyennes pour le jury""" + self.aggregats_taggues = compute_aggregats_tag(self.etudiants, self.semestres_taggues) + + if pe_tools.PE_DEBUG: + """Intègre le bilan des aggrégats de semestres au zip final""" + for aggregat in self.aggregats_taggues: + for fid in self.aggregats_taggues[aggregat]: + set_tag = self.aggregats_taggues[aggregat][fid] + filename = set_tag.nom.replace(" ", "_") + ".csv" + pe_tools.pe_print(f" - Export csv de {filename} ") + self.add_file_to_zip( + filename, set_tag.str_tagtable(), path="details_semestres" + ) + + """Génère les interclassements par (nom d') aggrégat""" + """Synthèse des éléments du jury PE""" if False: @@ -164,21 +186,35 @@ class JuryPE(object): filename = self.nom_export_zip + "_jurySyntheseDict" + scu.XLSX_SUFFIX self.xlsV2 = self.table_syntheseJury(mode="multiplesheet") if self.xlsV2: - self.add_file_to_zip(filename, self.xlsV2.excel()) + pe_tools.add_file_to_zip( + self.nom_export_zip, filename, self.xlsV2.excel() + ) # Pour debug # self.syntheseJury = pe_tools.JURY_SYNTHESE_POUR_DEBUG #Un dictionnaire fictif pour debug - # ------------------------------------------------------------------------------------------------------------------ - def add_file_to_zip(self, filename, data, path=""): + # Les interclassements + # -------------------- + if pe_tools.PE_DEBUG: + pe_tools.pe_print( + "*** Création des interclassements au sein de la promo sur différentes combinaisons de semestres" + ) + if False: + self.get_promotags_in_jury() + + def add_file_to_zip(self, filename: str, data, path=""): """Add a file to our zip All files under NOM_EXPORT_ZIP/ path may specify a subdirectory + + Args: + filename: Le nom du fichier à intégrer au zip + data: Les données du fichier + path: Un dossier dans l'arborescence du zip """ path_in_zip = os.path.join(self.nom_export_zip, path, filename) self.zipfile.writestr(path_in_zip, data) - # ------------------------------------------------------------------------------------------------------------------ def get_zipped_data(self): """returns file-like data with a zip of all generated (CSV) files. Reset file cursor at the beginning ! @@ -189,191 +225,26 @@ class JuryPE(object): self.zipdata.seek(0) return self.zipdata - # **************************************************************************************************************** # - # Lancement des différentes actions permettant le calcul du jury PE - # **************************************************************************************************************** # - def exe_calculs_juryPE(self): - """Centralise les élements de calcul des moyennes de poursuites - d'études - """ - - """Création des semestres taggués, de type 'S1', 'S2', ...""" - pe_tools.pe_print("*** Création des semestres taggués") - - formsemestres = self.etudiants.get_formsemestres_jury( - semestres_recherches=pe_tools.TOUS_LES_SEMESTRES - ) - for frmsem_id, formsemestre in formsemestres.items(): - """Choix d'un nom pour le semestretag""" - nom = "S%d %d %d-%d" % ( - formsemestre.semestre_id, - formsemestre.formsemestre_id, - formsemestre.date_debut.year, - formsemestre.date_fin.year, - ) - - pe_tools.pe_print( - f" --> Semestre taggué {nom} sur la base de {formsemestre}" - ) - - self.add_semestretag_in_jury(nom, frmsem_id) - - # Les moyennes sur toute la scolarité - # ----------------------------------- - if pe_tools.PE_DEBUG: - pe_tools.pe_print( - "*** Création des moyennes sur différentes combinaisons de semestres et différents groupes d'étudiant" - ) - if False: - self.get_settags_in_jury() - if pe_tools.PE_DEBUG: - for settagdict in self.setTagDict.values(): # Export - for settag in settagdict.values(): - filename = self.nom_export_zip + semtag.nom + ".csv" - self.add_file_to_zip( - filename, semtag.str_tagtable(), path="details_semestres" - ) - # self.export_juryPEDict() - - # Les interclassements - # -------------------- - if pe_tools.PE_DEBUG: - pe_tools.pe_print( - "*** Création des interclassements au sein de la promo sur différentes combinaisons de semestres" - ) - if False: - self.get_promotags_in_jury() - # **************************************************************************************************************** # # Traitements des semestres impliqués dans le jury # **************************************************************************************************************** # - # ------------------------------------------------------------------------------------------------------------------ - def add_semestretag_in_jury(self, nom: str, formsemestre_id: int): - """Ajoute (après création si nécessaire) un semtag dans `self.semTag` et - charge également les données des étudiants (découverts avec ce semestre). - - Args: - nom: Le nom à donner au SemestrreTag - formsemestre_id: L'identifiant d'un FormSemestre - """ - if formsemestre_id in self.semTagDict: - return - - """Créé le SemestreTag et exécute les calculs de moyennes""" - formsemestretag = pe_semestretag.SemestreTag(nom, formsemestre_id) - - self.semTagDict[formsemestre_id] = formsemestretag - - if pe_tools.PE_DEBUG: - filename = nom.replace(" ", "_") + ".csv" - pe_tools.pe_print(f" - Export csv de {filename} ") - self.zipfile.writestr(filename, formsemestretag.str_tagtable()) - - # **************************************************************************************************************** # - # Traitements des parcours impliquées dans le jury - # **************************************************************************************************************** # - - # # ---------------------------------------------------------------------------------------------------------------- - # def get_antags_in_jury(self, avec_affichage_debug=True ): - # """Construit les settag associés aux années 1A et 2A du jury""" - # lesAnnees = {'1A' : ['S1', 'S2'], '2A' : ['S3', 'S4'] } - # for nom_annee in lesAnnees: - # lesAidDesAnnees = self.get_anneeids_du_jury(annee= nom_annee) # les annee_ids des étudiants du jury - # for aid in lesAidDesAnnees: - # fidSemTagFinal = JuryPE.convert_aid_en_fid( aid ) - # lesEtudisDelAnnee = self.semTagDict[ fidSemTagFinal ].get_etudids() # les etudiants sont ceux inscrits dans le semestre final de l'année - # parcoursDesEtudiants = { etudid : self.PARCOURSINFO_DICT[etudid] for etudid in lesEtudisDelAnnee } # les parcours des etudid aka quels semestres sont à prendre en compte - # - # lesFidsDesEtudiants = self.get_formsemestreids_du_jury(lesEtudisDelAnnee, nom_annee) # les formsemestres_id à prendre en compte pour les moyennes - # # Manque-t-il des semtag associés ; si oui, les créé - # pe_tools.pe_print(aid, lesFidsDesEtudiants) - # for fid in lesFidsDesEtudiants: - # self.add_semtags_in_jury(fid, avec_affichage_debug=avec_affichage_debug) - # lesSemTagDesEtudiants = { fid: self.semTagDict[fid] for fid in lesFidsDesEtudiants } - # - # # Tous les semtag nécessaires pour ses étudiants avec ajout éventuel s'ils n'ont pas été chargés - # pe_tools.pe_print(" -> Création de l'année tagguée " + str( aid )) - # #settag_id, short_name, listeEtudId, groupe, listeSemAAggreger, ParcoursEtudDict, SemTagDict, with_comp_moy=True) - # self.anTagDict[ aid ] = pe_settag.SetTag( aid, "Annee " + self.semTagDict[fidSemTagFinal].short_name, \ - # lesEtudisDelAnnee, 'groupe', lesAnnees[ nom_annee ], parcoursDesEtudiants, lesSemTagDesEtudiants ) - # self.anTagDict[ aid ].comp_data_settag() # calcul les moyennes - # **************************************************************************************************************** # # Traitements des moyennes sur différentes combinaisons de parcours 1A, 2A, 3S et 4S, # impliquées dans le jury # **************************************************************************************************************** # - def get_settags_in_jury(self): - """Calcule les moyennes sur la totalité du parcours (S1 jusqu'à S3 ou S4) - en classant les étudiants au sein du semestre final du parcours (même S3, même S4, ...) - """ - - # Par groupe : - # combinaisons = { 'S1' : ['S1'], 'S2' : ['S2'], 'S3' : ['S3'], 'S4' : ['S4'], \ - # '1A' : ['S1', 'S2'], '2A' : ['S3', 'S4'], - # '3S' : ['S1', 'S2', 'S3'], '4S' : ['S1', 'S2', 'S3', 'S4'] } - - # ---> sur 2 parcours DUT (cas S3 fini, cas S4 fini) - - for i, nom in enumerate(pe_tools.TOUS_LES_AGGREGATS): - parcours = pe_tools.PARCOURS[nom][ - "aggregat" - ] # La liste des noms de semestres (S1, S2, ...) impliqués dans l'aggrégat - - # Recherche des parcours possibles par le biais de leur Fid final - fids_finaux = self.get_formsemestreids_du_jury( - self.etudiants.get_etudids(self.diplome), nom - ) # les formsemestre_ids validant finaux des étudiants du jury - - if len(fids_finaux) > 0: # S'il existe des parcours validant - pe_tools.pe_print("%d) Fusion %s avec" % (i + 1, nom)) - - if nom not in self.setTagDict: - self.setTagDict[nom] = {} - - for fid in fids_finaux: - pe_tools.pe_print(" - semestre final %s" % (fid)) - settag = pe_settag.SetTag( - nom, parcours=parcours - ) # Le set tag fusionnant les données - etudiants = self.semTagDict[ - fid - ].get_etudids() # Les étudiants du sem final - - # ajoute les étudiants au semestre - settag.set_Etudiants( - etudiants, - self.etudiants.cursus, - self.etudiants.identites, - nom_sem_final=self.semTagDict[fid].nom, - ) - - # manque-t-il des semestres ? Si oui, les ajoute au jurype puis au settag - for ffid in settag.get_Fids_in_settag(): - if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 1: - pe_tools.pe_print( - " -> ajout du semestre tagué %s" % (ffid) - ) - self.add_semestretag_in_jury(ffid) - settag.set_SemTagDict( - self.semTagDict - ) # ajoute les semestres au settag - - settag.comp_data_settag() # Calcul les moyennes, les rangs, .. - - self.setTagDict[nom][fid] = settag # Mémorise le résultat - - else: - pe_tools.pe_print("%d) Pas de fusion %s possible" % (i + 1, nom)) - def get_promotags_in_jury(self): - """Calcule les aggrégats en interclassant les étudiants du jury (les moyennes ont déjà été calculées en amont)""" + """Interclasse les étudiants, (nom d') aggrégat par aggrégat, + pour fournir un classement sur la promo. + """ lesEtudids = self.etudiants.get_etudids(self.diplome) for i, nom in enumerate(pe_tools.PARCOURS.keys()): - settag = pe_settag.SetTagInterClasse(nom, diplome=self.diplome) + settag = app.pe.pe_settag_interclasse.SetTagInterClasse( + nom, diplome=self.diplome + ) nbreEtudInscrits = settag.set_Etudiants( lesEtudids, self.etudiants.cursus, self.etudiants.identites ) @@ -383,9 +254,9 @@ class JuryPE(object): "%d) %s avec interclassement sur la promo" % (i + 1, nom) ) if nom in pe_tools.TOUS_LES_SEMESTRES: - settag.set_SetTagDict(self.semTagDict) + settag.set_SetTagDict(self.semestres_taggues) else: # cas des aggrégats - settag.set_SetTagDict(self.setTagDict[nom]) + settag.set_SetTagDict(self.aggregats_taggues[nom]) settag.comp_data_settag() self.promoTagDict[nom] = settag else: @@ -435,9 +306,11 @@ class JuryPE(object): self.etudiants.cursus[etudid][nom] != None ): # Un parcours valide existe if nom in pe_tools.TOUS_LES_SEMESTRES: - tagtable = self.semTagDict[self.etudiants.cursus[etudid][nom]] + tagtable = self.semestres_taggues[ + self.etudiants.cursus[etudid][nom] + ] else: - tagtable = self.setTagDict[nom][ + tagtable = self.aggregats_taggues[nom][ self.etudiants.cursus[etudid][nom] ] for tag in tagtable.get_all_tags(): @@ -467,7 +340,7 @@ class JuryPE(object): def get_parcoursIUT(self, etudid): """Renvoie une liste d'infos sur les semestres du parcours d'un étudiant""" # etudinfo = self.ETUDINFO_DICT[etudid] - sems = pe_etudiants.semestres_etudiant(etudid) + sems = self.etudiants.semestres_etudiant(etudid) infos = [] for sem in sems: @@ -505,8 +378,8 @@ class JuryPE(object): # les semestres et les aggrégats for nom_sem in pe_tools.TOUS_LES_PARCOURS: table = ( - self.semTagDict[donnees[nom_sem]].nom - if donnees[nom_sem] in self.semTagDict + self.semestres_taggues[donnees[nom_sem]].nom + if donnees[nom_sem] in self.semestres_taggues else "manquant" ) descr += [ @@ -746,7 +619,7 @@ class JuryPE(object): semtagid = self.etudiants.cursus[etudid][ nom_sem ] # le formsemestre_id du semestre taggué de l'étudiant - semtag = self.semTagDict[semtagid] + semtag = self.semestres_taggues[semtagid] chaine += "Semestre " + nom_sem + str(semtagid) + "\n" # le détail du calcul tag par tag # chaine += "Détail du calcul du tag\n" @@ -772,3 +645,112 @@ class JuryPE(object): if annees_debut: return str(min(annees_debut)) return "" + + +def compute_semestres_tag(etudiants: EtudiantsJuryPE): + """Créé les semestres taggués, de type 'S1', 'S2', ..., pour un groupe d'étudiants donnés. + Chaque semestre taggué est rattaché à l'un des FormSemestre faisant partie du cursus scolaire + des étudiants (cf. attribut etudiants.cursus). + En crééant le semestre taggué, sont calculées les moyennes/classements par tag associé. + . + + Args: + etudiants: Un groupe d'étudiants participant au jury + + Returns: + Un dictionnaire {fid: SemestreTag(fid)} + """ + + """Création des semestres taggués, de type 'S1', 'S2', ...""" + pe_tools.pe_print("*** Création des semestres taggués") + + formsemestres = etudiants.get_formsemestres_jury( + semestres_recherches=pe_tools.TOUS_LES_SEMESTRES + ) + + semestres_tags = {} + for frmsem_id, formsemestre in formsemestres.items(): + """Choix d'un nom pour le semestretag""" + nom = "S%d %d %d-%d" % ( + formsemestre.semestre_id, + frmsem_id, + formsemestre.date_debut.year, + formsemestre.date_fin.year, + ) + + pe_tools.pe_print(f" --> Semestre taggué {nom} sur la base de {formsemestre}") + + """Créé le semestre_tag et exécute les calculs de moyennes""" + formsemestretag = pe_semestretag.SemestreTag(nom, frmsem_id) + + """Stocke le semestre taggué""" + semestres_tags[frmsem_id] = formsemestretag + + return semestres_tags + + +def compute_aggregats_tag( + self, etudiants: EtudiantsJuryPE, semestres_tag: dict[SemestreTag] +): + """Créé les combinaisons de semestres (aggrégat), en calculant les moyennes et les + classements par tag pour chacune. Chaque combinaison (aggrégat) est identifiée + par un formsemestre terminal. + + Par exemple : + + * combinaisons '3S' : S1+S2+S3 en prenant en compte tous les S3 qu'ont fréquentés les + étudiants du jury PE. Ces S3 marquent les formsemestre terminal de chaque combinaison. + + Args: + etudiants: Les données des étudiants + semestres_tag: Les semestres tag (pour lesquels des moyennes par tag ont été calculés) + + Return: + Un dictionnaire de la forme {nom_aggregat: {fid_terminal: SetTag(fid_terminal)} } + """ + + pe_tools.pe_print(" *** Création des aggrégats ") + + sets_tags = {} + + for aggregat in pe_tools.TOUS_LES_AGGREGATS: + sets_tags[aggregat] = {} + + """Semestres aggrégés""" + noms_semestres_aggreges = pe_tools.PARCOURS[aggregat]["aggregat"] + nom_semestre_terminal = noms_semestres_aggreges[-1] + + pe_tools.pe_print(f"* {aggregat}: " + "+".join(noms_semestres_aggreges)) + + """Les formsemestres terminaux des aggrégats""" + formsemestres_terminal = etudiants.get_formsemestres_terminaux_aggregat( + aggregat + ) + + for frmsem_id in formsemestres_terminal: + formsemestre_terminal = formsemestres_terminal[frmsem_id] + """Nom du set_tag""" + nom = "Aggrégat S%d %d %d-%d" % ( + formsemestre_terminal.semestre_id, + frmsem_id, + formsemestre_terminal.date_debut.year, + formsemestre_terminal.date_fin.year, + ) + + """Semestres à aggreger dans l'aggrégat ayant amené des étudiants jusqu'au formsemestre_terminal""" + semestres_aggreges = etudiants.get_semestres_a_aggreger(aggregat, frmsem_id) + + pe_tools.pe_print(" --> Fusion de :") + for fid in semestres_aggreges: + pe_tools.pe_print(str(semestres_aggreges[fid])) + + """Création du settag associé""" + settag = pe_settag.SetTag( + nom, formsemestre_terminal, semestres_aggreges, semestres_tag, etudiants + ) + + settag.compute_notes_cube() # Calcul les moyennes, les rangs, .. + + sets_tags[aggregat][fid] = settag # Mémorise le résultat + + return sets_tags diff --git a/app/pe/pe_settag.py b/app/pe/pe_settag.py index 61f4fe90..2474d0b9 100644 --- a/app/pe/pe_settag.py +++ b/app/pe/pe_settag.py @@ -39,7 +39,6 @@ from app.comp import moy_sem from app.comp.res_sem import load_formsemestre_results from app.models import FormSemestre from app.pe.pe_semestretag import SemestreTag -from app.pe.pe_tools import pe_print, PE_DEBUG from app.pe import pe_tagtable import pandas as pd import numpy as np @@ -169,128 +168,6 @@ class SetTag(pe_tagtable.TableTag): return sorted(set(tags)) -class SetTagInterClasse(pe_tagtable.TableTag): - """Récupère les moyennes de SetTag aggrégeant un même parcours (par ex un ['S1', 'S2'] n'ayant pas fini au même S2 - pour fournir un interclassement sur un groupe d'étudiant => seul compte alors la promo - nom_combinaison = 'S1' ou '1A' - """ - - # ------------------------------------------------------------------------------------------------------------------- - def __init__(self, nom_combinaison, diplome): - pe_tagtable.TableTag.__init__(self, nom=f"{nom_combinaison}_{diplome or ''}") - self.combinaison = nom_combinaison - self.parcoursDict = {} - - # ------------------------------------------------------------------------------------------- - def set_Etudiants(self, etudiants, juryPEDict, etudInfoDict, nom_sem_final=None): - """Détermine la liste des étudiants à prendre en compte, en partant de - la liste fournie en paramètre et en vérifiant que l'étudiant dispose bien d'un parcours valide pour la combinaison demandée. - Renvoie le nombre d'étudiants effectivement inscrits.""" - if nom_sem_final: - self.nom += "_" + nom_sem_final - for etudid in etudiants: - if juryPEDict[etudid][self.combinaison] != None: - self.inscrlist.append(etudInfoDict[etudid]) - self.identdict[etudid] = etudInfoDict[etudid] - self.parcoursDict[etudid] = juryPEDict[etudid] - return len(self.inscrlist) - - # ------------------------------------------------------------------------------------------- - def get_Fids_in_settag(self): - """Renvoie la liste des semestres (les formsemestre_id finissant la combinaison par ex. '3S' dont les fid des S3) à prendre en compte - pour les moyennes, en considérant tous les étudiants inscrits""" - return list( - {self.parcoursDict[etudid][self.combinaison] for etudid in self.identdict} - ) - - # --------------------------------------------------------------------------------------------- - def set_SetTagDict(self, SetTagDict): - """Mémorise les settag nécessaires au jury.""" - self.SetTagDict = { - fid: SetTagDict[fid] for fid in self.get_Fids_in_settag() if fid != None - } - if PE_DEBUG >= 1: - pe_print(" => %d semestres utilisés" % len(self.SetTagDict)) - - # ------------------------------------------------------------------------------------------------------------------- - def comp_data_settag(self): - """Calcule tous les données numériques relatives au settag""" - # Attributs relatifs aux tag pour les modules pris en compte - self.taglist = self.do_taglist() - - # if PE_DEBUG >= 1: pe_print(" => Tags = " + ", ".join( self.taglist )) - - # Calcul des moyennes de chaque étudiant par tag - reussiteAjoutTag = {"OK": [], "KO": []} - for tag in self.taglist: - moyennes = self.get_MoyennesSetTag(tag, force=False) - res = self.add_moyennesTag(tag, moyennes) # pas de notes => pas de moyenne - reussiteAjoutTag["OK" if res else "KO"].append(tag) - if len(reussiteAjoutTag["OK"]) > 0 and PE_DEBUG: - pe_print( - " => Interclassement de %d tags : " % (len(reussiteAjoutTag["OK"])) - + ", ".join(reussiteAjoutTag["OK"]) - ) - if len(reussiteAjoutTag["KO"]) > 0 and PE_DEBUG: - pe_print( - " => %d tags manquants : " % (len(reussiteAjoutTag["KO"])) - + ", ".join(reussiteAjoutTag["KO"]) - ) - - # ------------------------------------------------------------------------------------------------------------------- - def get_etudids(self): - return list(self.identdict.keys()) - - # ------------------------------------------------------------------------------------------------------------------- - def do_taglist(self): - """Parcourt les tags des semestres taggués et les synthétise sous la forme - d'une liste en supprimant les doublons - """ - ensemble = [] - for settag in self.SetTagDict.values(): - ensemble.extend(settag.get_all_tags()) - return sorted(list(set(ensemble))) - - # ------------------------------------------------------------------------------------------------------------------- - def get_NotesEtCoeffsSetTagEtudiant(self, tag, etudid): - """Récupère tous les notes et les coeffs d'un étudiant relatives à un tag dans ses semestres valides et les renvoie dans un tuple (notes, coeffs) - avec notes et coeffs deux listes""" - leSetTagDeLetudiant = self.parcoursDict[etudid][self.combinaison] - - note = self.SetTagDict[leSetTagDeLetudiant].get_moy_from_resultats(tag, etudid) - coeff = self.SetTagDict[leSetTagDeLetudiant].get_coeff_from_resultats( - tag, etudid - ) - return (note, coeff) - - # ------------------------------------------------------------------------------------------------------------------- - def get_MoyennesSetTag(self, tag, force=False): - """Renvoie les "moyennes" des étudiants à un tag donné, en prenant en compte tous les settag de l'aggrégat, - et leur coeff Par moyenne, s'entend une note moyenne, la somme des coefficients de pondération - appliqué dans cette moyenne. - - Force ou non le calcul de la moyenne lorsque des notes sont manquantes. - - Renvoie les informations sous la forme d'une liste [etudid: (moy, somme_coeff_normalisée, rang), ...} - """ - # if tag not in self.get_all_tags() : return None - - # Calcule les moyennes - lesMoyennes = [] - for ( - etudid - ) in ( - self.get_etudids() - ): # Pour tous les étudiants non défaillants du semestre inscrits dans des modules relatifs au tag - (moyenne, somme_coeffs) = self.get_NotesEtCoeffsSetTagEtudiant( - tag, etudid - ) # lecture des notes associées au tag - lesMoyennes += [ - (moyenne, somme_coeffs, etudid) - ] # Un tuple (pour classement résumant les données) - return lesMoyennes - - def compute_tag_moy(set_cube: np.array, etudids: list, tags: list): """Calcul de la moyenne par tag sur plusieurs semestres. La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles diff --git a/app/pe/pe_settag_interclasse.py b/app/pe/pe_settag_interclasse.py new file mode 100644 index 00000000..c3e6ad0f --- /dev/null +++ b/app/pe/pe_settag_interclasse.py @@ -0,0 +1,125 @@ +from app.pe import pe_tagtable +from app.pe.pe_tools import PE_DEBUG, pe_print + + +class SetTagInterClasse(pe_tagtable.TableTag): + """Récupère les moyennes de SetTag aggrégeant un même parcours (par ex un ['S1', 'S2'] + n'ayant pas fini au même S2 + pour fournir un interclassement sur un groupe d'étudiant => seul compte alors la promo + nom_combinaison = 'S1' ou '1A' + """ + + # ------------------------------------------------------------------------------------------------------------------- + def __init__(self, nom_combinaison, diplome): + pe_tagtable.TableTag.__init__(self, nom=f"{nom_combinaison}_{diplome or ''}") + self.combinaison = nom_combinaison + self.parcoursDict = {} + + # ------------------------------------------------------------------------------------------- + def set_Etudiants(self, etudiants, juryPEDict, etudInfoDict, nom_sem_final=None): + """Détermine la liste des étudiants à prendre en compte, en partant de + la liste fournie en paramètre et en vérifiant que l'étudiant dispose bien d'un parcours valide pour la combinaison demandée. + Renvoie le nombre d'étudiants effectivement inscrits.""" + if nom_sem_final: + self.nom += "_" + nom_sem_final + for etudid in etudiants: + if juryPEDict[etudid][self.combinaison] != None: + self.inscrlist.append(etudInfoDict[etudid]) + self.identdict[etudid] = etudInfoDict[etudid] + self.parcoursDict[etudid] = juryPEDict[etudid] + return len(self.inscrlist) + + # ------------------------------------------------------------------------------------------- + def get_Fids_in_settag(self): + """Renvoie la liste des semestres (les formsemestre_id finissant la combinaison par ex. '3S' dont les fid des S3) à prendre en compte + pour les moyennes, en considérant tous les étudiants inscrits""" + return list( + {self.parcoursDict[etudid][self.combinaison] for etudid in self.identdict} + ) + + # --------------------------------------------------------------------------------------------- + def set_SetTagDict(self, SetTagDict): + """Mémorise les settag nécessaires au jury.""" + self.SetTagDict = { + fid: SetTagDict[fid] for fid in self.get_Fids_in_settag() if fid != None + } + if PE_DEBUG >= 1: + pe_print(" => %d semestres utilisés" % len(self.SetTagDict)) + + # ------------------------------------------------------------------------------------------------------------------- + def comp_data_settag(self): + """Calcule tous les données numériques relatives au settag""" + # Attributs relatifs aux tag pour les modules pris en compte + self.taglist = self.do_taglist() + + # if PE_DEBUG >= 1: pe_print(" => Tags = " + ", ".join( self.taglist )) + + # Calcul des moyennes de chaque étudiant par tag + reussiteAjoutTag = {"OK": [], "KO": []} + for tag in self.taglist: + moyennes = self.get_MoyennesSetTag(tag, force=False) + res = self.add_moyennesTag(tag, moyennes) # pas de notes => pas de moyenne + reussiteAjoutTag["OK" if res else "KO"].append(tag) + if len(reussiteAjoutTag["OK"]) > 0 and PE_DEBUG: + pe_print( + " => Interclassement de %d tags : " % (len(reussiteAjoutTag["OK"])) + + ", ".join(reussiteAjoutTag["OK"]) + ) + if len(reussiteAjoutTag["KO"]) > 0 and PE_DEBUG: + pe_print( + " => %d tags manquants : " % (len(reussiteAjoutTag["KO"])) + + ", ".join(reussiteAjoutTag["KO"]) + ) + + # ------------------------------------------------------------------------------------------------------------------- + def get_etudids(self): + return list(self.identdict.keys()) + + # ------------------------------------------------------------------------------------------------------------------- + def do_taglist(self): + """Parcourt les tags des semestres taggués et les synthétise sous la forme + d'une liste en supprimant les doublons + """ + ensemble = [] + for settag in self.SetTagDict.values(): + ensemble.extend(settag.get_all_tags()) + return sorted(list(set(ensemble))) + + # ------------------------------------------------------------------------------------------------------------------- + def get_NotesEtCoeffsSetTagEtudiant(self, tag, etudid): + """Récupère tous les notes et les coeffs d'un étudiant relatives à un tag dans ses semestres valides et les renvoie dans un tuple (notes, coeffs) + avec notes et coeffs deux listes""" + leSetTagDeLetudiant = self.parcoursDict[etudid][self.combinaison] + + note = self.SetTagDict[leSetTagDeLetudiant].get_moy_from_resultats(tag, etudid) + coeff = self.SetTagDict[leSetTagDeLetudiant].get_coeff_from_resultats( + tag, etudid + ) + return (note, coeff) + + # ------------------------------------------------------------------------------------------------------------------- + def get_MoyennesSetTag(self, tag, force=False): + """Renvoie les "moyennes" des étudiants à un tag donné, en prenant en compte tous les settag de l'aggrégat, + et leur coeff Par moyenne, s'entend une note moyenne, la somme des coefficients de pondération + appliqué dans cette moyenne. + + Force ou non le calcul de la moyenne lorsque des notes sont manquantes. + + Renvoie les informations sous la forme d'une liste [etudid: (moy, somme_coeff_normalisée, rang), ...} + """ + # if tag not in self.get_all_tags() : return None + + # Calcule les moyennes + lesMoyennes = [] + for ( + etudid + ) in ( + self.get_etudids() + ): # Pour tous les étudiants non défaillants du semestre inscrits dans des modules relatifs au tag + (moyenne, somme_coeffs) = self.get_NotesEtCoeffsSetTagEtudiant( + tag, etudid + ) # lecture des notes associées au tag + lesMoyennes += [ + (moyenne, somme_coeffs, etudid) + ] # Un tuple (pour classement résumant les données) + return lesMoyennes diff --git a/app/pe/pe_tagtable.py b/app/pe/pe_tagtable.py index ad69a090..5c995095 100644 --- a/app/pe/pe_tagtable.py +++ b/app/pe/pe_tagtable.py @@ -83,8 +83,11 @@ class TableTag(object): # ----------------------------------------------------------------------------------------------------------- def get_all_tags(self): - """Renvoie la liste des tags du semestre triée par ordre alphabétique""" - # return self.taglist + """Liste des tags de la table, triée par ordre alphabétique + + Returns: + Liste de tags triés par ordre alphabétique + """ return sorted(self.moyennes_tags.keys()) @@ -270,6 +273,20 @@ class TableTag(object): str_moytag = classmethod(str_moytag) # ----------------------------------------------------------------------- + def df_tagtable(self): + """Renvoie un dataframe (etudid x tag) listant toutes les moyennes par tags + + Returns: + Un dataframe etudids x tag (avec tag par ordre alphabétique) + """ + tags = self.get_all_tags() + if tags: + dict_series = {tag: self.moyennes_tags[tag]["notes"] for tag in tags} + df = pd.DataFrame(dict_series) + return df + else: + return None + def str_tagtable(self): """Renvoie une chaine de caractère listant toutes les moyennes, les rangs des étudiants pour tous les tags.""" diff --git a/app/pe/pe_tools.py b/app/pe/pe_tools.py index a2264fc0..b34ec3be 100644 --- a/app/pe/pe_tools.py +++ b/app/pe/pe_tools.py @@ -164,6 +164,10 @@ TOUS_LES_AGGREGATS = [cle for cle in PARCOURS.keys() if not cle.startswith("S")] TOUS_LES_PARCOURS = list(PARCOURS.keys()) + + + + # ---------------------------------------------------------------------------------------- def print_semestres_description(sems, avec_affichage_debug=False): """Dediee a l'affichage d'un semestre pour debug du module"""