From e3cde87a0f469a33a350251d4a0933dbe93eafcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9o=20BARAS=20=28IUT1=20Grenoble=29?= Date: Tue, 23 Jan 2024 18:44:44 +0100 Subject: [PATCH] =?UTF-8?q?Adaptation=20diverses=20pour=20la=20gestion=20d?= =?UTF-8?q?es=20aggr=C3=A9gats=20(dont=20les=20redoublements=20de=20semest?= =?UTF-8?q?re)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/pe/pe_etudiant.py | 115 ++++++++++++++++++++++++++++++------------ app/pe/pe_jurype.py | 29 +++++++---- app/pe/pe_tools.py | 31 ------------ 3 files changed, 100 insertions(+), 75 deletions(-) diff --git a/app/pe/pe_etudiant.py b/app/pe/pe_etudiant.py index 81c3e7ef6..880de9573 100644 --- a/app/pe/pe_etudiant.py +++ b/app/pe/pe_etudiant.py @@ -52,8 +52,14 @@ import datetime class EtudiantsJuryPE: """Classe centralisant la gestion des étudiants à prendre en compte dans un jury de PE""" - def __init__(self): - """ """ + def __init__(self, annee_diplome: int): + """ + + Args: + annee_diplome: L'année de diplomation + """ + + self.annee_diplome = annee_diplome "Les identités des étudiants traités pour le jury" self.identites = {} # ex. ETUDINFO_DICT @@ -70,21 +76,20 @@ class EtudiantsJuryPE: "Les formsemestres dont il faut calculer les moyennes par tag" self.formsemestres_jury_ids = {} - def find_etudiants(self, annee_diplome: int, formation_id: int): + def find_etudiants(self, formation_id: int): """Liste des étudiants à prendre en compte dans le jury PE, en les recherchant de manière automatique par rapport à leur année de diplomation ``annee_diplome`` dans la formation ``formation_id``. Les données obtenues sont stockées dans les attributs de EtudiantsJuryPE. - Args: - annee_diplome: L'année de diplomation + 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) + cosemestres = pe_tools.get_cosemestres_diplomants(self.annee_diplome, None) self.cosemestres = cosemestres pe_tools.pe_print( "1) Recherche des coSemestres -> %d trouvés" % len(cosemestres) @@ -119,7 +124,7 @@ class EtudiantsJuryPE: pe_tools.pe_print() """Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris""" - self.diplomes_ids = self.get_etudiants(annee_diplome) + self.diplomes_ids = self.get_etudiants_diplomes() """Les étudiants dont il faut calculer les moyennes""" self.etudiants_ids = {etudid for etudid in self.identites} @@ -129,11 +134,13 @@ class EtudiantsJuryPE: # Synthèse pe_tools.pe_print( - f" => {len(self.diplomes_ids)} étudiants à diplômer en {annee_diplome}" + f" => {len(self.diplomes_ids)} étudiants à diplômer en {self.annee_diplome}" ) nbre_abandons = len(self.etudiants_ids) - len(self.diplomes_ids) pe_tools.pe_print(f" => {nbre_abandons} étudiants éliminer pour abandon") - pe_tools.pe_print(f" => {len(self.formsemestres_jury_ids)} semestres dont il faut calculer la moyenne") + pe_tools.pe_print( + f" => {len(self.formsemestres_jury_ids)} semestres dont il faut calculer la moyenne" + ) pe_tools.pe_print( f" => quelques étudiants futurs diplômés : " + ", ".join([str(etudid) for etudid in list(self.diplomes_ids)[:10]]) @@ -142,14 +149,14 @@ class EtudiantsJuryPE: f" => semestres dont il faut calculer les moyennes : " + ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)]) ) + # Les abandons : + # sorted([etudiants.cursus[etudid]['nom'] for etudid in etudiants.cursus if etudid not in etudiants.diplomes_ids]) - def get_etudiants(self, annee_diplome: int) -> dict[Identite]: + def get_etudiants_diplomes(self) -> dict[int, 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é. - Args: - annee_diplome: Année de diplomation visée pour le jury Returns: Un dictionnaire `{etudid: Identite(etudid)}` @@ -157,7 +164,7 @@ class EtudiantsJuryPE: etudids = [ etudid for etudid in self.cursus - if self.cursus[etudid]["diplome"] == annee_diplome + if self.cursus[etudid]["diplome"] == self.annee_diplome and self.cursus[etudid]["abandon"] == False ] etudiants = {etudid: self.identites[etudid] for etudid in etudids} @@ -203,7 +210,7 @@ class EtudiantsJuryPE: """ Est-il réorienté / démissionnaire ou a-t-il arrêté volontairement sa formation ?""" self.cursus[etudid]["abandon"] = arret_de_formation(identite, cosemestres) - def analyse_parcours_etudiant_dans_semestres(self, etudid): + def analyse_parcours_etudiant_dans_semestres(self, etudid: int): """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. @@ -220,21 +227,60 @@ class EtudiantsJuryPE: 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, ... + + Par ex: M. N..z (redoublant en 2ème année) au moment de son 2ème S3 : + {'1A': {26: {18: , + 26: }}, + '2A': {79: {18: , + 26: , + 56: , + 79: }}, + '3A': {}, + '3S': {112: {18: , + 26: , + 56: , + 112: }}, + '4S': {79: {18: , + 26: , + 56: , + 79: }}, + '5S': {}, + '6S': {}, + 'S1': {18: {18: }}, + 'S2': {26: {26: }}, + 'S3': {112: {56: , + 112: }}, + 'S4': {79: {79: }}, + 'S5': {}, + 'S6': {} + } """ semestres_etudiant = self.cursus[etudid]["formsemestres"] + """Ne conserve que les semestres qui l'auraient amené à être diplomé l'année visée""" + semestres_significatifs = {} + for fid in semestres_etudiant: + semestre = semestres_etudiant[fid] + if pe_tools.get_annee_diplome_semestre(semestre) <= self.annee_diplome: + semestres_significatifs[fid] = semestre + self.aggregats[etudid] = {} """Tri des semestres par numéro de semestre""" for nom_sem in pe_tools.TOUS_LES_SEMESTRES: 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 + fid: semestres_significatifs[fid] + for fid in semestres_significatifs + if semestres_significatifs[fid].semestre_id == i } # les semestres de n°i de l'étudiant - self.aggregats[etudid][nom_sem] = semestres_i - self.cursus[etudid][nom_sem] = get_dernier_semestre(semestres_i) + dernier_semestre_i = get_dernier_semestre(semestres_i) + self.cursus[etudid][nom_sem] = dernier_semestre_i + self.aggregats[etudid][nom_sem] = {} + if dernier_semestre_i: + fid_dernier_semestre_i = list(dernier_semestre_i.keys())[0] + self.aggregats[etudid][nom_sem][fid_dernier_semestre_i] = semestres_i + """Tri des semestres par aggrégat et par semestre terminal""" for aggregat in pe_tools.TOUS_LES_AGGREGATS: @@ -258,8 +304,8 @@ class EtudiantsJuryPE: """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] + for fid in semestres_significatifs: + semestre = semestres_significatifs[fid] if ( semestre.semestre_id <= numero_semestre_terminal and semestre.date_fin <= formsemestre_terminal.date_fin @@ -273,8 +319,9 @@ class EtudiantsJuryPE: assert dernier_semestre_aggregat == dernier_formsemestre_terminal 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 : + """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. @@ -289,15 +336,15 @@ class EtudiantsJuryPE: 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 - ] + for etudid in self.aggregats: + if self.aggregats[etudid][aggregat]: + print(self.aggregats[etudid][aggregat]) + """Le formsemestre_id du semestre terminal de l'étudiant (s'il existe)""" + fid = list(self.aggregats[etudid][aggregat].keys())[0] + """Le formsemestre associé (en le prenant dans l'aggrégat)""" + formsemestres_terminaux[fid] = self.aggregats[etudid][aggregat][fid][ + fid + ] return formsemestres_terminaux def get_semestres_a_aggreger(self, aggregat: str, formsemestre_id_terminal: int): @@ -373,7 +420,9 @@ class EtudiantsJuryPE: nom_sem = semestres_recherches semestres = {} for etudid in self.etudiants_ids: - semestres = semestres | self.aggregats[etudid][nom_sem] + for sem_terminal in self.aggregats[etudid]: + for sem in self.aggregats[etudid][sem_terminal]: + semestres = semestres | self.aggregats[etudid][sem_terminal][sem] return semestres else: raise ValueError( diff --git a/app/pe/pe_jurype.py b/app/pe/pe_jurype.py index 70fa32979..627dc68a8 100644 --- a/app/pe/pe_jurype.py +++ b/app/pe/pe_jurype.py @@ -138,8 +138,8 @@ class JuryPE(object): 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) + 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) @@ -689,10 +689,8 @@ def compute_semestres_tag(etudiants: EtudiantsJuryPE): 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 +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. @@ -701,6 +699,11 @@ def compute_aggregats_tag( * 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) @@ -713,12 +716,15 @@ def compute_aggregats_tag( sets_tags = {} - for aggregat in pe_tools.TOUS_LES_AGGREGATS: + for aggregat in pe_tools.TOUS_LES_SEMESTRES + 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] + 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)) @@ -730,9 +736,10 @@ def compute_aggregats_tag( 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, + 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, ) diff --git a/app/pe/pe_tools.py b/app/pe/pe_tools.py index dba1c00f7..c5ada4214 100644 --- a/app/pe/pe_tools.py +++ b/app/pe/pe_tools.py @@ -170,37 +170,6 @@ TOUS_LES_SEMESTRES = PARCOURS[AGGREGAT_DIPLOMANT]["aggregat"] 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""" - - def chaine_semestre(sem): - desc = ( - "S" - + str(sem["semestre_id"]) - + " " - + sem["modalite"] - + " " - + sem["anneescolaire"] - ) - desc += " (" + sem["annee_debut"] + "/" + sem["annee_fin"] + ") " - desc += str(sem["formation_id"]) + " / " + str(sem["formsemestre_id"]) - desc += " - " + sem["titre_num"] - return desc - - if avec_affichage_debug == True: - if isinstance(sems, list): - for sem in sems: - pe_print(chaine_semestre(sem)) - else: - pe_print(chaine_semestre(sems)) - - # ---------------------------------------------------------------------------------------- def calcul_age(born): """Calcule l'age à partir de la date de naissance sous forme d'une chaine de caractère 'jj/mm/aaaa'.