# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Emmanuel Viennet emmanuel.viennet@viennet.net # ############################################################################## ############################################################################## # Module "Avis de poursuite d'étude" # conçu et développé par Cléo Baras (IUT de Grenoble) ############################################################################## """ Created on Fri Sep 9 09:15:05 2016 @author: barasc """ import datetime # ---------------------------------------------------------- # Ensemble des fonctions et des classes # permettant les calculs preliminaires (hors affichage) # a l'edition d'un jury de poursuites d'etudes # ---------------------------------------------------------- import io 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 from app.scodoc import ( codes_cursus, sco_formsemestre_inscriptions, ) # codes_cursus.NEXT -> sem suivant from app.scodoc import sco_etud from app.scodoc import sco_report from app.scodoc import sco_formsemestre from app.pe import pe_tagtable from app.pe import pe_tools from app.pe import pe_semestretag from app.pe import pe_settag from app.pe.pe_etudiant import EtudiantsJuryPE # ---------------------------------------------------------------------------------------- def comp_nom_semestre_dans_parcours(sem): """Le nom a afficher pour titrer un semestre par exemple: "semestre 2 FI 2015" """ formation: Formation = Formation.query.get_or_404(sem["formation_id"]) parcours = codes_cursus.get_cursus_from_code(formation.type_parcours) return "%s %s %s %s" % ( parcours.SESSION_NAME, # eg "semestre" sem["semestre_id"], # eg 2 sem.get("modalite", ""), # eg FI ou FC sem["annee_debut"], # eg 2015 ) # ---------------------------------------------------------------------------------------- class JuryPE(object): """Classe mémorisant toutes les informations nécessaires pour établir un jury de PE. Modèle basé sur NotesTable. Attributs : * diplome : l'année d'obtention du diplome BUT et du jury de PE (généralement février XXXX) * juryEtudDict : dictionnaire récapitulant les étudiants participant au jury PE (données administratives + celles des semestres valides à prendre en compte permettant le calcul des moyennes ... ``{'etudid : { 'nom', 'prenom', 'civilite', 'diplome', '', }}`` Rq: il contient à la fois les étudiants qui vont être diplomés à la date prévue et ceux qui sont éliminés (abandon, redoublement, ...) pour affichage alternatif """ # Variables de classe décrivant les aggrégats, leur ordre d'apparition temporelle et # leur affichage dans les avis latex # ------------------------------------------------------------------------------------------------------------------ def __init__(self, diplome, formation_id): """ Création d'une table PE sur la base d'un semestre selectionné. De ce semestre est déduit : 1. l'année d'obtention du DUT, 2. tous les étudiants susceptibles à ce stade (au regard de leur parcours) d'être diplomés. Args: sem_base: le FormSemestre donnant le semestre à la base du jury PE semBase: le dictionnaire sem donnant la base du jury (CB: TODO: A supprimer à long term) meme_programme: si True, impose un même programme pour tous les étudiants participant au jury, si False, permet des programmes differents """ self.promoTagDict = {} "L'année du diplome" self.diplome = diplome "La formation associée au diplome" self.formation_id = formation_id "Un zip où ranger les fichiers générés" self.nom_export_zip = "Jury_PE_%s" % self.diplome self.zipdata = io.BytesIO() self.zipfile = ZipFile(self.zipdata, "w") 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(self.diplome) # Les infos sur les étudiants self.etudiants.find_etudiants(self.formation_id) """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: self.synthetise_juryPE() # Export des données => mode 1 seule feuille -> supprimé # filename = self.NOM_EXPORT_ZIP + "jurySyntheseDict_" + str(self.diplome) + '.xls' # self.xls = self.table_syntheseJury(mode="singlesheet") # self.add_file_to_zip(filename, self.xls.excel()) # Fabrique 1 fichier excel résultat avec 1 seule feuille => trop gros if False: filename = self.nom_export_zip + "_jurySyntheseDict" + scu.XLSX_SUFFIX self.xlsV2 = self.table_syntheseJury(mode="multiplesheet") if self.xlsV2: 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 # 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 ! """ if self.zipfile: self.zipfile.close() self.zipfile = None self.zipdata.seek(0) return self.zipdata # **************************************************************************************************************** # # Traitements des semestres impliqués dans le jury # **************************************************************************************************************** # # **************************************************************************************************************** # # Traitements des moyennes sur différentes combinaisons de parcours 1A, 2A, 3S et 4S, # impliquées dans le jury # **************************************************************************************************************** # def get_promotags_in_jury(self): """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 = app.pe.pe_settag_interclasse.SetTagInterClasse( nom, diplome=self.diplome ) nbreEtudInscrits = settag.set_Etudiants( lesEtudids, self.etudiants.cursus, self.etudiants.identites ) if nbreEtudInscrits > 0: if pe_tools.PE_DEBUG: pe_tools.pe_print( "%d) %s avec interclassement sur la promo" % (i + 1, nom) ) if nom in pe_tools.TOUS_LES_SEMESTRES: settag.set_SetTagDict(self.semestres_taggues) else: # cas des aggrégats settag.set_SetTagDict(self.aggregats_taggues[nom]) settag.comp_data_settag() self.promoTagDict[nom] = settag else: pe_tools.pe_print( "%d) Pas d'interclassement %s sur la promo faute de notes" % (i + 1, nom) ) # **************************************************************************************************************** # # Méthodes pour la synthèse du juryPE # ***************************************************************************************************************** def synthetise_juryPE(self): """Synthétise tous les résultats du jury PE dans un dictionnaire""" self.syntheseJury = {} for etudid in self.etudiants.get_etudids(self.diplome): etudinfo = self.ETUDINFO_DICT[etudid] self.syntheseJury[etudid] = { "nom": etudinfo["nom"], "prenom": etudinfo["prenom"], "civilite": etudinfo["civilite"], "civilite_str": etudinfo["civilite_str"], "age": str(pe_tools.calcul_age(etudinfo["date_naissance"])), "lycee": etudinfo["nomlycee"] + ( " (" + etudinfo["villelycee"] + ")" if etudinfo["villelycee"] != "" else "" ), "bac": etudinfo["bac"], "code_nip": etudinfo["code_nip"], # pour la photo "entree": self.get_dateEntree(etudid), "promo": self.diplome, } # Le parcours self.syntheseJury[etudid]["parcours"] = self.get_parcoursIUT( etudid ) # liste des semestres self.syntheseJury[etudid]["nbSemestres"] = len( self.syntheseJury[etudid]["parcours"] ) # nombre de semestres # Ses résultats for nom in pe_tools.PARCOURS: # S1, puis S2, puis 1A # dans le groupe : la table tagguée dans les semtag ou les settag si aggrégat self.syntheseJury[etudid][nom] = {"groupe": {}, "promo": {}} if ( self.etudiants.cursus[etudid][nom] != None ): # Un parcours valide existe if nom in pe_tools.TOUS_LES_SEMESTRES: tagtable = self.semestres_taggues[ self.etudiants.cursus[etudid][nom] ] else: tagtable = self.aggregats_taggues[nom][ self.etudiants.cursus[etudid][nom] ] for tag in tagtable.get_all_tags(): self.syntheseJury[etudid][nom]["groupe"][ tag ] = tagtable.get_resultatsEtud( tag, etudid ) # Le tuple des résultats # interclassé dans la promo tagtable = self.promoTagDict[nom] for tag in tagtable.get_all_tags(): self.syntheseJury[etudid][nom]["promo"][ tag ] = tagtable.get_resultatsEtud(tag, etudid) def get_dateEntree(self, etudid): """Renvoie l'année d'entrée de l'étudiant à l'IUT""" # etudinfo = self.ETUDINFO_DICT[etudid] semestres = self.get_semestresBUT_d_un_etudiant(etudid) if semestres: # le 1er sem à l'IUT return semestres[0]["annee_debut"] else: return "" def get_parcoursIUT(self, etudid): """Renvoie une liste d'infos sur les semestres du parcours d'un étudiant""" # etudinfo = self.ETUDINFO_DICT[etudid] sems = self.etudiants.semestres_etudiant(etudid) infos = [] for sem in sems: nomsem = comp_nom_semestre_dans_parcours(sem) infos.append( { "nom_semestre_dans_parcours": nomsem, "titreannee": sem["titreannee"], "formsemestre_id": sem["formsemestre_id"], # utile dans le futur ? } ) return infos # **************************************************************************************************************** # # Méthodes d'affichage pour debug # **************************************************************************************************************** # def str_etudiants_in_jury(self, delim=";"): # En tete: entete = ["Id", "Nom", "Abandon", "Diplome"] for nom_sem in pe_tools.TOUS_LES_PARCOURS: entete += [nom_sem, "descr"] chaine = delim.join(entete) + "\n" for etudid in self.etudiants.cursus: donnees = self.etudiants.cursus[etudid] # pe_tools.pe_print(etudid, donnees) # les infos générales descr = [ etudid, donnees["nom"], str(donnees["abandon"]), str(donnees["diplome"]), ] # les semestres et les aggrégats for nom_sem in pe_tools.TOUS_LES_PARCOURS: table = ( self.semestres_taggues[donnees[nom_sem]].nom if donnees[nom_sem] in self.semestres_taggues else "manquant" ) descr += [ donnees[nom_sem] if donnees[nom_sem] != None else "manquant", table, ] chaine += delim.join(descr) + "\n" return chaine # def export_juryPEDict(self): """Export csv de self.PARCOURSINFO_DICT""" fichier = "juryParcoursDict_" + str(self.diplome) pe_tools.pe_print(" -> Export de " + fichier) filename = self.nom_export_zip + fichier + ".csv" self.zipfile.writestr(filename, self.str_etudiants_in_jury()) def get_allTagForAggregat(self, nom_aggregat): """Extrait du dictionnaire syntheseJury la liste des tags d'un semestre ou d'un aggrégat donné par son nom (S1, S2, S3 ou S4, 1A, ...). Renvoie [] si aucun tag. """ taglist = set() for etudid in self.etudiants.get_etudids(): taglist = taglist.union( set(self.syntheseJury[etudid][nom_aggregat]["groupe"].keys()) ) taglist = taglist.union( set(self.syntheseJury[etudid][nom_aggregat]["promo"].keys()) ) return list(taglist) def get_allTagInSyntheseJury(self): """Extrait tous les tags du dictionnaire syntheseJury trié par ordre alphabétique. [] si aucun tag""" allTags = set() for nom in pe_tools.TOUS_LES_PARCOURS: allTags = allTags.union(set(self.get_allTagForAggregat(nom))) return sorted(list(allTags)) if len(allTags) > 0 else [] def table_syntheseJury(self, mode="singlesheet"): # was str_syntheseJury """Table(s) du jury mode: singlesheet ou multiplesheet pour export excel """ sT = SeqGenTable() # le fichier excel à générer # Les etudids des étudiants à afficher, triés par ordre alphabétiques de nom+prénom donnees_tries = sorted( [ ( etudid, self.syntheseJury[etudid]["nom"] + " " + self.syntheseJury[etudid]["prenom"], ) for etudid in self.syntheseJury.keys() ], key=lambda c: c[1], ) etudids = [e[0] for e in donnees_tries] if not etudids: # Si pas d'étudiants T = GenTable( columns_ids=["pas d'étudiants"], rows=[], titles={"pas d'étudiants": "pas d'étudiants"}, html_sortable=True, xls_sheet_name="but", ) sT.add_genTable("but", T) return sT # Si des étudiants maxParcours = max( [self.syntheseJury[etudid]["nbSemestres"] for etudid in etudids] ) infos = ["civilite", "nom", "prenom", "age", "nbSemestres"] entete = ["etudid"] entete.extend(infos) entete.extend(["P%d" % i for i in range(1, maxParcours + 1)]) champs = [ "note", "class groupe", "class promo", "min/moy/max groupe", "min/moy/max promo", ] # Les aggrégats à afficher par ordre tel que indiqué dans le dictionnaire parcours aggregats = list(pe_tools.PARCOURS.keys()) # ['S1', 'S2', ..., '1A', '4S'] # aggregats = sorted( # aggregats, key=lambda t: pe_tools.PARCOURS[t]["ordre"] # ) # Tri des aggrégats if mode == "multiplesheet": allSheets = ( self.get_allTagInSyntheseJury() ) # tous les tags de syntheseJuryDict allSheets = sorted(allSheets) # Tri des tags par ordre alphabétique for sem in pe_tools.TOUS_LES_PARCOURS: entete.extend(["%s %s" % (sem, champ) for champ in champs]) else: # "singlesheet" allSheets = ["singlesheet"] for ( sem ) in ( pe_tools.TOUS_LES_PARCOURS ): # pe_tools.PARCOURS.keys() -> ['S1', 'S2', ..., '1A', '4S'] tags = self.get_allTagForAggregat(sem) entete.extend( ["%s %s %s" % (sem, tag, champ) for tag in tags for champ in champs] ) columns_ids = entete # les id et les titres de colonnes sont ici identiques titles = {i: i for i in columns_ids} for ( sheet ) in ( allSheets ): # Pour tous les sheets à générer (1 si singlesheet, autant que de tags si multiplesheet) rows = [] for etudid in etudids: e = self.syntheseJury[etudid] # Les info générales: row = { "etudid": etudid, "civilite": e["civilite"], "nom": e["nom"], "prenom": e["prenom"], "age": e["age"], "nbSemestres": e["nbSemestres"], } # Les parcours: P1, P2, ... n = 1 for p in e["parcours"]: row["P%d" % n] = p["titreannee"] n += 1 # if self.syntheseJury[etudid]['nbSemestres'] < maxParcours: # descr += delim.join( ['']*( maxParcours -self.syntheseJury[etudid]['nbSemestres']) ) + delim for sem in aggregats: # pe_tools.PARCOURS.keys(): listeTags = ( self.get_allTagForAggregat(sem) if mode == "singlesheet" else [sheet] ) for tag in listeTags: if tag in self.syntheseJury[etudid][sem]["groupe"]: resgroupe = self.syntheseJury[etudid][sem]["groupe"][ tag ] # tuple else: resgroupe = (None, None, None, None, None, None, None) if tag in self.syntheseJury[etudid][sem]["promo"]: respromo = self.syntheseJury[etudid][sem]["promo"][tag] else: respromo = (None, None, None, None, None, None, None) # note = "%2.2f" % resgroupe[0] if isinstance(resgroupe[0], float) else str(resgroupe[0]) champ = ( "%s %s " % (sem, tag) if mode == "singlesheet" else "%s " % (sem) ) row[champ + "note"] = scu.fmt_note(resgroupe[0]) row[champ + "class groupe"] = "%s / %s" % ( resgroupe[2] if resgroupe[2] else "-", resgroupe[3] if resgroupe[3] else "-", ) row[champ + "class promo"] = "%s / %s" % ( respromo[2] if respromo[2] else "-", respromo[3] if respromo[3] else "-", ) row[champ + "min/moy/max groupe"] = "%s / %s / %s" % tuple( (scu.fmt_note(x) if x is not None else "-") for x in (resgroupe[6], resgroupe[4], resgroupe[5]) ) row[champ + "min/moy/max promo"] = "%s / %s / %s" % tuple( (scu.fmt_note(x) if x is not None else "-") for x in (respromo[6], respromo[4], respromo[5]) ) rows.append(row) T = GenTable( columns_ids=columns_ids, rows=rows, titles=titles, html_sortable=True, xls_sheet_name=sheet, ) sT.add_genTable(sheet, T) if mode == "singlesheet": return sT.get_genTable("singlesheet") else: return sT # **************************************************************************************************************** # # Méthodes de classe pour gestion d'un cache de données accélérant les calculs / intérêt à débattre # **************************************************************************************************************** # # ------------------------------------------------------------------------------------------------------------------ def get_cache_etudInfo_d_un_etudiant(self, etudid): """Renvoie les informations sur le parcours d'un étudiant soit en les relisant depuis ETUDINFO_DICT si mémorisée soit en les chargeant et en les mémorisant TODO:: A supprimer à long terme """ if etudid not in self.ETUDINFO_DICT: self.ETUDINFO_DICT[etudid] = Identite.get_etud(etudid=etudid) # sco_etud.get_etud_info( # etudid=etudid, filled=True # ))[0] return self.ETUDINFO_DICT[etudid] # ------------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------------ def get_cache_notes_d_un_semestre(self, formsemestre_id: int) -> NotesTableCompat: """Charge la table des notes d'un formsemestre""" formsemestre = FormSemestre.get_formsemestre(formsemestre_id) return res_sem.load_formsemestre_results(formsemestre) # ------------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------------ def get_semestresBUT_d_un_etudiant(self, identite: Identite, semestre_id=None): """cf. pe_etudiant.semestres_etudiant()""" return None # ********************************************* # Fonctions d'affichage pour debug def get_resultat_d_un_etudiant(self, etudid): chaine = "" for nom_sem in pe_tools.TOUS_LES_SEMESTRES: semtagid = self.etudiants.cursus[etudid][ nom_sem ] # le formsemestre_id du semestre taggué de l'étudiant 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" # chaine += "-----------------------\n" # for tag in semtag.taglist: # chaine += "Tag=" + tag + "\n" # chaine += semtag.str_detail_resultat_d_un_tag(tag, etudid=etudid) + "\n" # le bilan des tags chaine += "Bilan des tags\n" chaine += "--------------\n" for tag in semtag.taglist: chaine += ( tag + ";" + semtag.str_resTag_d_un_etudiant(tag, etudid) + "\n" ) chaine += "\n" return chaine def get_date_entree_etudiant(self, etudid) -> str: """Renvoie la date d'entree d'un étudiant: "1996" """ annees_debut = [ int(sem["annee_debut"]) for sem in self.ETUDINFO_DICT[etudid]["sems"] ] 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(etudiants: EtudiantsJuryPE, semestres_tag: dict[SemestreTag]): """Créé les combinaisons de semestres (aggrégats), 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. * combinaisons 'S2' : 1 seul S2 pour des étudiants n'ayant pas redoublé, 2 pour des redoublants (dont les notes seront moyennées sur leur 2 semestres S2). Ces combinaisons ont pour formsemestre le dernier S2 en date (le S2 redoublé par les redoublants est forcément antérieur) 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_SEMESTRES + pe_tools.TOUS_LES_AGGREGATS: sets_tags[aggregat] = {} """Semestres aggrégés""" if aggregat in pe_tools.TOUS_LES_SEMESTRES: # par ex. 'S2' noms_semestres_aggreges = [ aggregat ] else: # par ex. "5S" 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 %s %d-%d" % ( aggregat, frmsem_id, "+".join(noms_semestres_aggreges), 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