From 960f8a346209ccd3929c25203000c4b2adbcebc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9o=20BARAS=20=28IUT1=20Grenoble=29?= Date: Sun, 25 Feb 2024 12:45:58 +0100 Subject: [PATCH] =?UTF-8?q?Am=C3=A9liore=20les=20affichages=20de=20debug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/pe/moys/pe_rcstag.py | 53 +++++++++++++++++++++---- app/pe/moys/pe_ressemtag.py | 44 ++++++++++++++++++--- app/pe/moys/pe_sxtag.py | 70 +++++++++++++++++++++++++++------ app/pe/pe_etudiant.py | 61 ++++++----------------------- app/pe/pe_jury.py | 53 +++++++++++++++++-------- app/pe/pe_rcss_jury.py | 77 ++++++++++++++++++++++++++----------- 6 files changed, 245 insertions(+), 113 deletions(-) diff --git a/app/pe/moys/pe_rcstag.py b/app/pe/moys/pe_rcstag.py index 664ff9888..d08dc9b5d 100644 --- a/app/pe/moys/pe_rcstag.py +++ b/app/pe/moys/pe_rcstag.py @@ -78,7 +78,7 @@ class RCSTag(pe_tabletags.TableTag): """Le fid du semestre final""" # Affichage pour debug - pe_affichage.pe_print(f"-> {self.get_repr(verbose=True)}") + pe_affichage.pe_print(f"*** {self.get_repr(verbose=True)}") # Les données aggrégés (RCRCF + SxTags self.semXs_aggreges: dict[(str, int) : pe_rcsemx.RCSemX] = rcsemx.semXs_aggreges @@ -103,25 +103,26 @@ class RCSTag(pe_tabletags.TableTag): # Les compétences (extraites de tous les Sxtags) self.acronymes_ues_to_competences = self._do_acronymes_to_competences() """L'association acronyme d'UEs -> compétence (extraites des SxTag aggrégés)""" - pe_affichage.pe_print( - f"* Association UEs -> compétences : {self.acronymes_ues_to_competences}" - ) + self.competences_sorted = sorted( set(self.acronymes_ues_to_competences.values()) ) - """Compétences (triées par nom, extraites des SxTag aggrégés)""" - pe_affichage.pe_print(f"* Compétences : {', '.join(self.competences_sorted)}") + self._aff_comp_et_ues_debug() + # pe_affichage.pe_print(f"--> Compétences : {', '.join(self.competences_sorted)}") # Les tags self.tags_sorted = self._do_taglist() """Tags extraits de tous les SxTag aggrégés""" - pe_affichage.pe_print(f"* Tags : {', '.join(self.tags_sorted)}") + aff_tag = ["👜" + tag for tag in self.tags_sorted] + pe_affichage.pe_print(f"--> Tags : {', '.join(aff_tag)}") # Les moyennes self.moyennes_tags: dict[str, pe_moytag.MoyennesTag] = {} + """Synthétise les moyennes/classements par tag (qu'ils soient personnalisé ou de compétences)""" for tag in self.tags_sorted: + pe_affichage.pe_print(f"--> Moyennes du tag 👜{tag}") # Cube de notes (etudids_sorted x compétences_sorted x sxstags) notes_df, notes_cube = self.compute_notes_comps_cube( tag, self.etudids_sorted, self.competences_sorted, self.sxstags @@ -143,6 +144,8 @@ class RCSTag(pe_tabletags.TableTag): matrice_coeffs_moy_gen = compute_coeffs_competences( coeffs_cube, notes_cube, self.etudids_sorted, self.competences_sorted ) + self.__aff_profil_coeffs(matrice_coeffs_moy_gen) + # Mémorise les moyennes et les coeff associés self.moyennes_tags[tag] = pe_moytag.MoyennesTag( tag, @@ -308,6 +311,42 @@ class RCSTag(pe_tabletags.TableTag): dict_competences |= sxtag.acronymes_ues_to_competences return dict_competences + def _aff_comp_et_ues_debug(self): + """Affichage pour debug""" + aff_comp = [] + + for comp in self.competences_sorted: + liste = [] + for acro in self.acronymes_ues_to_competences: + if self.acronymes_ues_to_competences[acro] == comp: + liste += ["📍" + acro] + aff_comp += [f" 💡{comp} (⇔ {', '.join(liste)})"] + pe_affichage.pe_print(f"--> Compétences :") + pe_affichage.pe_print("\n".join(aff_comp)) + + def __aff_profil_coeffs(self, matrice_coeffs_moy_gen): + """Extrait de la matrice des coeffs, les différents types d'inscription + et de coefficients (appelés profil) des étudiants et les affiche + (pour debug) + """ + + # Les profils des coeffs d'UE (pour debug) + profils = [] + for i in matrice_coeffs_moy_gen.index: + val = matrice_coeffs_moy_gen.loc[i].fillna("-") + val = " | ".join([str(v) for v in val]) + if val not in profils: + profils += [val] + + # L'affichage + if len(profils) > 1: + profils_aff = "\n" + "\n".join([" " * 10 + prof for prof in profils]) + else: + profils_aff = "\n".join(profils) + pe_affichage.pe_print( + f" > Moyenne calculée avec pour coeffs (de compétences) : {profils_aff}" + ) + def compute_coeffs_competences( coeff_cube: np.array, diff --git a/app/pe/moys/pe_ressemtag.py b/app/pe/moys/pe_ressemtag.py index 0732cb8e3..39636f1f5 100644 --- a/app/pe/moys/pe_ressemtag.py +++ b/app/pe/moys/pe_ressemtag.py @@ -66,7 +66,7 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag): # Le nom du res_semestre taggué self.nom = self.get_repr(verbose=True) - pe_affichage.pe_print(f"--> ResultatsSemestreBUT taggués {self.nom}") + pe_affichage.pe_print(f"*** ResSemBUTTag du {self.nom}") # Les étudiants (etuds, états civils & etudis) ajouté self.add_etuds(self.etuds) @@ -100,9 +100,11 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag): list(set(self.acronymes_ues_to_competences.values())) ) """Les compétences triées par nom""" + self._aff_ue_et_comp_debug() # Les tags personnalisés et auto: tags_dict = self._get_tags_dict() + self._aff_tags_debug(tags_dict) self._check_tags(tags_dict) # Les coefficients pour le calcul de la moyenne générale, donnés par @@ -111,6 +113,7 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag): self.ues_inscr_parcours_df, self.ues_standards ) """DataFrame indiquant les coeffs des UEs par ordre alphabétique d'acronyme""" + self.__aff_profil_coeffs() # Les capitalisations (mask etuids x acronyme_ue valant True si capitalisée, False sinon) self.capitalisations = self._get_capitalisations(self.ues_standards) @@ -291,7 +294,6 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag): dict_tags["personnalises"] = get_synthese_tags_personnalises_semestre( self.formsemestre ) - noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys()))) # Les tags automatiques # Déduit des compétences @@ -300,16 +302,25 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag): # BUT dict_tags["auto"] = {"but": {}} + return dict_tags + def _aff_ue_et_comp_debug(self): + """Affichage pour debug""" + aff_comp = [] + for acro in self.acronymes_sorted: + aff_comp += [f"📍{acro} (∈ 💡{self.acronymes_ues_to_competences[acro]})"] + pe_affichage.pe_print(f"--> UEs/Compétences : {', '.join(aff_comp)}") + + def _aff_tags_debug(self, dict_tags): + """Affichage pour debug""" + noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys()))) noms_tags_auto = sorted(list(set(dict_tags["auto"].keys()))) # + noms_tags_comp aff_tags_auto = ", ".join([f"👜{nom}" for nom in noms_tags_auto]) aff_tags_perso = ", ".join([f"👜{nom}" for nom in noms_tags_perso]) # Affichage pe_affichage.pe_print( - f"* Tags du programme de formation : {aff_tags_perso} + " - + f"Tags automatiques : {aff_tags_auto}" + f"""--> Tags du programme de formation : {aff_tags_perso} + Automatiques : {aff_tags_auto}""" ) - return dict_tags def _check_tags(self, dict_tags): """Vérifie l'unicité des tags""" @@ -336,6 +347,29 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag): """ raise ScoValueError(message) + def __aff_profil_coeffs(self): + """Extrait de la matrice des coeffs, les différents types d'inscription + et de coefficients (appelés profil) des étudiants et les affiche + (pour debug) + """ + + # Les profils des coeffs d'UE (pour debug) + profils = [] + for i in self.matrice_coeffs_moy_gen.index: + val = self.matrice_coeffs_moy_gen.loc[i].fillna("-") + val = " | ".join([str(v) for v in val]) + if val not in profils: + profils += [val] + + # L'affichage + if len(profils) > 1: + profils_aff = "\n" + "\n".join([" " * 10 + prof for prof in profils]) + else: + profils_aff = "\n".join(profils) + pe_affichage.pe_print( + f"--> Moyenne générale calculée avec pour coeffs d'UEs : {profils_aff}" + ) + def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre): """Etant données les implémentations des modules du semestre (modimpls), diff --git a/app/pe/moys/pe_sxtag.py b/app/pe/moys/pe_sxtag.py index abdd0bde6..6f381feb1 100644 --- a/app/pe/moys/pe_sxtag.py +++ b/app/pe/moys/pe_sxtag.py @@ -116,12 +116,13 @@ class SxTag(pe_tabletags.TableTag): """Les etudids triés""" # Affichage - pe_affichage.pe_print(f"--> {self.get_repr(verbose=True)}") + pe_affichage.pe_print(f"*** {self.get_repr(verbose=True)}") # Les tags self.tags_sorted = self.ressembuttag_final.tags_sorted """Tags (extraits du ReSemBUTTag final)""" - pe_affichage.pe_print(f"* Tags : {', '.join(self.tags_sorted)}") + aff_tag = ["👜" + tag for tag in self.tags_sorted] + pe_affichage.pe_print(f"--> Tags : {', '.join(aff_tag)}") # Les UE données par leur acronyme self.acronymes_sorted = self.ressembuttag_final.acronymes_sorted @@ -134,10 +135,13 @@ class SxTag(pe_tabletags.TableTag): """L'association acronyme d'UEs -> compétence""" self.competences_sorted = sorted(self.acronymes_ues_to_competences.values()) """Les compétences triées par nom""" + self._aff_ue_et_comp_debug() # Les coeffs pour la moyenne générale (traduisant également l'inscription # des étudiants aux UEs) (etudids_sorted x acronymes_ues_sorted) self.matrice_coeffs_moy_gen = self.ressembuttag_final.matrice_coeffs_moy_gen + """La matrice des coeffs pour la moyenne générale""" + self.__aff_profil_coeffs() # Masque des inscriptions et des capitalisations self.masque_df = None @@ -153,10 +157,13 @@ class SxTag(pe_tabletags.TableTag): # Les moyennes par tag self.moyennes_tags: dict[str, pd.DataFrame] = {} """Moyennes aux UEs (identifiées par leur acronyme) des différents tags""" + if self.tags_sorted: + pe_affichage.pe_print("--> Calcul des moyennes par tags :") + for tag in self.tags_sorted: # Y-a-t-il des notes ? if not self.has_notes(tag): - pe_affichage.pe_print(f"> MoyTag 🏷{tag} actuellement sans ◯ notes") + pe_affichage.pe_print(f" > MoyTag 👜{tag} actuellement sans notes") matrice_moys_ues = pd.DataFrame( np.nan, index=self.etudids_sorted, columns=self.acronymes_sorted ) @@ -198,17 +205,24 @@ class SxTag(pe_tabletags.TableTag): (pour debug) """ - # Les profils d'ects (pour debug) - profils_ects = [] + # Les profils des coeffs d'UE (pour debug) + profils = [] for i in self.matrice_coeffs_moy_gen.index: - val = tuple(self.matrice_coeffs_moy_gen.loc[i].fillna("x")) - if tuple(val) not in profils_ects: - profils_ects.append(tuple(val)) + val = self.matrice_coeffs_moy_gen.loc[i].fillna("-") + val = " | ".join([str(v) for v in val]) + if val not in profils: + profils += [val] + + # L'affichage + if len(profils) > 1: + profils_aff = "\n" + "\n".join([" " * 10 + prof for prof in profils]) + else: + profils_aff = "\n".join(profils) # L'affichage ues = ", ".join(self.acronymes_sorted) pe_affichage.pe_print( - f"> MoyTag 🏷{tag} avec " + f"ues={ues} " + f"inscr/ects={profils_ects}" + f" > MoyTag 👜{tag} pour UES: {ues} avec pour coeffs : {profils_aff}" ) def has_notes(self, tag): @@ -243,8 +257,16 @@ class SxTag(pe_tabletags.TableTag): # affichage = [str(fid) for fid in self.ressembuttags] return f"SXTag {self.nom_rcs}#{self.fid_final}" + def _aff_ue_et_comp_debug(self): + """Affichage pour debug""" + aff_comp = [] + for acro in self.acronymes_sorted: + aff_comp += [f"📍{acro} (∈ 💡{self.acronymes_ues_to_competences[acro]})"] + pe_affichage.pe_print(f"--> UEs/Compétences : {', '.join(aff_comp)}") + def _aff_capitalisations(self): """Affichage des capitalisations du sxtag pour debug""" + aff_cap = [] for etud in self.etuds: cap = [] for frmsem_id in self.ressembuttags: @@ -253,9 +275,33 @@ class SxTag(pe_tabletags.TableTag): if self.masque_df[frmsem_id].loc[etud.etudid, accr] > 0.0: cap += [accr] if cap: - pe_affichage.pe_print( - f" ⚠ Capitalisation de {etud.etat_civil} : {', '.join(cap)}" - ) + aff_cap += [f" > {etud.nomprenom} : {', '.join(cap)}"] + if aff_cap: + pe_affichage.pe_print(f"--> ⚠️ Capitalisations :") + pe_affichage.pe_print("\n".join(aff_cap)) + + def __aff_profil_coeffs(self): + """Extrait de la matrice des coeffs, les différents types d'inscription + et de coefficients (appelés profil) des étudiants et les affiche + (pour debug) + """ + + # Les profils des coeffs d'UE (pour debug) + profils = [] + for i in self.matrice_coeffs_moy_gen.index: + val = self.matrice_coeffs_moy_gen.loc[i].fillna("-") + val = " | ".join([str(v) for v in val]) + if val not in profils: + profils += [val] + + # L'affichage + if len(profils) > 1: + profils_aff = "\n" + "\n".join([" " * 10 + prof for prof in profils]) + else: + profils_aff = "\n".join(profils) + pe_affichage.pe_print( + f"--> Moyenne générale calculée avec pour coeffs d'UEs : {profils_aff}" + ) def compute_notes_ues_cube( diff --git a/app/pe/pe_etudiant.py b/app/pe/pe_etudiant.py index 94e17e430..386807845 100644 --- a/app/pe/pe_etudiant.py +++ b/app/pe/pe_etudiant.py @@ -43,6 +43,7 @@ from app.pe import pe_comp, pe_affichage from app.scodoc import codes_cursus from app.scodoc import sco_utils as scu from app.comp.res_sem import load_formsemestre_results +import warnings class EtudiantsJuryPE: @@ -100,10 +101,10 @@ class EtudiantsJuryPE: self.cosemestres = cosemestres pe_affichage.pe_print( - f"1) Recherche des coSemestres -> {len(cosemestres)} trouvés" + f"1) Recherche des cosemestres -> {len(cosemestres)} trouvés" ) - pe_affichage.pe_print("2) Liste des étudiants dans les différents co-semestres") + pe_affichage.pe_print("2) Liste des étudiants dans les différents cosemestres") self.etudiants_ids = get_etudiants_dans_semestres(cosemestres) pe_affichage.pe_print( f" => {len(self.etudiants_ids)} étudiants trouvés dans les cosemestres" @@ -135,23 +136,16 @@ class EtudiantsJuryPE: # Les identifiants des étudiants ayant redoublés ou ayant abandonnés # Synthèse + pe_affichage.pe_print(f"4) Bilan") pe_affichage.pe_print( - f" => {len(self.etudiants_diplomes)} étudiants à diplômer en {self.annee_diplome}" + f"--> {len(self.etudiants_diplomes)} étudiants à diplômer en {self.annee_diplome}" ) nbre_abandons = len(self.etudiants_ids) - len(self.etudiants_diplomes) assert nbre_abandons == len(self.abandons_ids) pe_affichage.pe_print( - f" => {nbre_abandons} étudiants traités mais non diplômés (redoublement, réorientation, abandon)" + f"--> {nbre_abandons} étudiants traités mais non diplômés (redoublement, réorientation, abandon)" ) - # pe_affichage.pe_print( - # " => quelques étudiants futurs diplômés : " - # + ", ".join([str(etudid) for etudid in list(self.etudiants_diplomes)[:10]]) - # ) - # pe_affichage.pe_print( - # " => semestres dont il faut calculer les moyennes : " - # + ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)]) - # ) def get_etudiants_diplomes(self) -> dict[int, Identite]: """Identités des étudiants (sous forme d'un dictionnaire `{etudid: Identite(etudid)}` @@ -240,7 +234,8 @@ class EtudiantsJuryPE: if self.cursus[etudid]["diplome"] == self.annee_diplome: # Est-il démissionnaire : charge son dernier semestre pour connaitre son état ? dernier_semes_etudiant = formsemestres[0] - res = load_formsemestre_results(dernier_semes_etudiant) + with warnings.catch_warnings(): + res = load_formsemestre_results(dernier_semes_etudiant) etud_etat = res.get_etud_etat(etudid) if etud_etat == scu.DEMISSION: self.cursus[etudid]["abandon"] = True @@ -547,45 +542,11 @@ def arret_de_formation(etud: Identite, cosemestres: dict[int, FormSemestre]) -> pe_affichage.pe_print( f"--> ⛔ {etud.etat_civil} ({etud.etudid}), non inscrit dans {affichage} amenant à diplômation" ) + else: + pe_affichage.pe_print(f"--> ✅ {etud.etat_civil} ({etud.etudid})") return est_demissionnaire - # # Son dernier semestre APC en date - # dernier_formsemestre = get_dernier_semestre_en_date(semestres_apc) - # numero_dernier_formsemestre = dernier_formsemestre.semestre_id - # - # # Les numéro de semestres possible dans lesquels il pourrait s'incrire - # # semestre impair => passage de droit en semestre pair suivant (effet de l'annualisation) - # if numero_dernier_formsemestre % 2 == 1: - # numeros_possibles = list( - # range(numero_dernier_formsemestre + 1, pe_comp.NBRE_SEMESTRES_DIPLOMANT) - # ) - # # semestre pair => passage en année supérieure ou redoublement - # else: # - # numeros_possibles = list( - # range( - # max(numero_dernier_formsemestre - 1, 1), - # pe_comp.NBRE_SEMESTRES_DIPLOMANT, - # ) - # ) - # - # # Y-a-t-il des cosemestres dans lesquels il aurait pu s'incrire ? - # formsestres_superieurs_possibles = [] - # for fid, sem in cosemestres.items(): # Les semestres ayant des inscrits - # if ( - # fid != dernier_formsemestre.formsemestre_id - # and sem.semestre_id in numeros_possibles - # and sem.date_debut.year >= dernier_formsemestre.date_debut.year - # ): - # # date de debut des semestres possibles postérieur au dernier semestre de l'étudiant - # # et de niveau plus élevé que le dernier semestre valide de l'étudiant - # formsestres_superieurs_possibles.append(fid) - # - # if len(formsestres_superieurs_possibles) > 0: - # return True - # - # return False - def etapes_du_cursus( semestres: dict[int, FormSemestre], nbre_etapes_max: int @@ -650,6 +611,6 @@ def nom_semestre_etape(semestre: FormSemestre, avec_fid=False) -> str: f"{semestre.date_debut.year}-{semestre.date_fin.year}", ] if avec_fid: - description.append(f"({semestre.formsemestre_id})") + description.append(f"(#{semestre.formsemestre_id})") return " ".join(description) diff --git a/app/pe/pe_jury.py b/app/pe/pe_jury.py index 5155457a0..cae47a696 100644 --- a/app/pe/pe_jury.py +++ b/app/pe/pe_jury.py @@ -91,11 +91,14 @@ class JuryPE(object): ) # Chargement des étudiants à prendre en compte dans le jury pe_affichage.pe_print( - f"""*** Recherche des étudiants diplômés 🎓 en {self.diplome}""" + f"""***********************************************************\n""" + f"""*** Recherche des étudiants diplômés 🎓 en {self.diplome}\n""" + f"""***********************************************************\n""" ) - self.etudiants = pe_etudiant.EtudiantsJuryPE( - self.diplome - ) # Les infos sur les étudiants + + # Les infos sur les étudiants + self.etudiants = pe_etudiant.EtudiantsJuryPE(self.diplome) + """Les informations sur les étudiants du jury PE""" self.etudiants.find_etudiants() self.diplomes_ids = self.etudiants.diplomes_ids @@ -153,13 +156,14 @@ class JuryPE(object): def _gen_xls_ressembuttags(self, zipfile: ZipFile): """Calcule les moyennes par tag des résultats des Semestres BUT""" pe_affichage.pe_print( - "*** Génère les ResSemBUTTag (résultats des semestres BUT taggués)" + f"""*************************************************************************\n""" + f"""*** Génère les ResSemBUTTag (ResSemestreBUT taggués)\n""" + f"""*************************************************************************""" ) + # Tous les formsestres des étudiants formsemestres = get_formsemestres_etudiants(self.etudiants) - pe_affichage.pe_print( - f"--> {len(formsemestres)} résultats de semestres à considérer" - ) + pe_affichage.pe_print(f"1) Génère les {len(formsemestres)} ResSemBUTTag") self.ressembuttags = {} for frmsem_id, formsemestre in formsemestres.items(): @@ -167,6 +171,7 @@ class JuryPE(object): self.ressembuttags[frmsem_id] = pe_ressemtag.ResSemBUTTag(formsemestre) # Intègre le bilan des semestres taggués au zip final + pe_affichage.pe_print(f"2) Bilan") output = io.BytesIO() with pd.ExcelWriter( # pylint: disable=abstract-class-instantiated output, engine="openpyxl" @@ -174,13 +179,14 @@ class JuryPE(object): onglets = [] for res_sem_tag in self.ressembuttags.values(): onglet = res_sem_tag.get_repr(verbose=True) - onglets += [] + onglet = onglet.replace("Semestre ", "S") + onglets += ["📊" + onglet] df = res_sem_tag.to_df() # Conversion colonnes en multiindex df = convert_colonnes_to_multiindex(df) # écriture dans l'onglet df.to_excel(writer, onglet, index=True, header=True) - pe_affichage.pe_print(f"=> Export excel de {', '.join(onglets)}") + pe_affichage.pe_print(f"--> Export excel de {', '.join(onglets)}") output.seek(0) self.add_file_to_zip( @@ -196,10 +202,13 @@ class JuryPE(object): RCS (par ex: 'S2' ou '3S'). """ pe_affichage.pe_print( - "*** Génère les trajectoires (différentes combinaisons de semestres) des étudiants" + "***************************************************************************\n" + "*** Génère les trajectoires (≠tes combinaisons de semestres) des étudiants" + "***************************************************************************\n" ) self.rcss_jury.cree_trajectoires(self.etudiants) + self.rcss_jury._aff_trajectoires(self.etudiants) def _gen_semXs(self): """Génère les SemXs (trajectoires/combinaisons de semestre de même rang x) @@ -218,10 +227,13 @@ class JuryPE(object): """ # Génère les moyennes des RCS de type Sx pe_affichage.pe_print( - "*** Calcule les moyennes des SxTag (moyennes d'un SemX/RCS de type Sx)" + "***************************************************************************\n" + "*** Calcule les moyennes des SxTag (moyennes d'un RCS de type Sx)" + "***************************************************************************\n" ) # Les SxTag (moyenne de Sx par UE) + pe_affichage.pe_print("1) Calcul des moyennes") self.sxtags = {} for rcf_id, rcf in self.rcss_jury.semXs.items(): # SxTag traduisant le RCF @@ -229,6 +241,7 @@ class JuryPE(object): self.sxtags[sxtag_id] = pe_sxtag.SxTag(sxtag_id, rcf, self.ressembuttags) # Intègre le bilan des semestres taggués au zip final + pe_affichage.pe_print("2) Bilan") output = io.BytesIO() with pd.ExcelWriter( # pylint: disable=abstract-class-instantiated output, engine="openpyxl" @@ -240,10 +253,10 @@ class JuryPE(object): df = sxtag.to_df() # Conversion colonnes en multiindex df = convert_colonnes_to_multiindex(df) - onglets += [onglet] + onglets += ["📊" + onglet] # écriture dans l'onglet df.to_excel(writer, onglet, index=True, header=True) - pe_affichage.pe_print(f"=> Export excel de {', '.join(onglets)}") + pe_affichage.pe_print(f"--> Export excel de {', '.join(onglets)}") output.seek(0) if onglets: @@ -258,8 +271,10 @@ class JuryPE(object): """Génère les regroupements cohérents de RCFs qu'ont suivi chaque étudiant""" pe_affichage.pe_print( - "*** Génère les RCSemX (regroupements cohérents de données" - " extraites des SemX) amenant du S1 à un semestre final***" + """******************************************************************************\n""" + """*** Génère les RCSemX (regroupements cohérents de données extraites des SemX)\n""" + """*** amenant du S1 à un semestre final\n""" + """******************************************************************************""" ) self.rcss_jury.cree_rcsemxs(self.etudiants) self.rcss_jury._aff_rcsemxs_suivis(self.etudiants) @@ -284,7 +299,11 @@ class JuryPE(object): """ # Génère les moyennes des RCS de type Sx - pe_affichage.pe_print("*** Calcule les moyennes des RC de RCFS") + pe_affichage.pe_print( + """****************************************************\n""" + """*** Génère les moyennes associées aux RCSemX \n""" + """****************************************************""" + ) self.rcss_tags = {} for rcs_id, rcsemx in self.rcss_jury.rcsemxs.items(): diff --git a/app/pe/pe_rcss_jury.py b/app/pe/pe_rcss_jury.py index 6b0fe5f62..9cf8ab298 100644 --- a/app/pe/pe_rcss_jury.py +++ b/app/pe/pe_rcss_jury.py @@ -93,15 +93,19 @@ class RCSsJuryPE: # Mémorise le RCS suivi par l'étudiant self.trajectoires_suivies[etudid][nom_rcs] = rcs + def _aff_trajectoires(self, etudiants: pe_etudiant.EtudiantsJuryPE): + """Affiche les chemins trouvés pour debug""" # Affichage pour debug jeunes = list(enumerate(self.trajectoires_suivies)) - for no_etud, etudid in jeunes[:20]: + for no_etud, etudid in jeunes: + etat = "⛔" if etudid in etudiants.abandons_ids else "✅" + pe_affichage.pe_print( - f"--> {etudiants.identites[etudid].nomprenom} (#{etudid}) :" + f"--> {etat} {etudiants.identites[etudid].nomprenom} (#{etudid}) :" ) for nom_rcs, rcs in self.trajectoires_suivies[etudid].items(): if rcs: - pe_affichage.pe_print(f" > RCS {nom_rcs}: {rcs.get_repr()}") + pe_affichage.pe_print(f" > RCS ⏯️{nom_rcs}: {rcs.get_repr()}") def cree_semxs(self, etudiants: pe_etudiant.EtudiantsJuryPE): """Créé les les SemXs (trajectoires/combinaisons de semestre de même rang x), @@ -126,16 +130,26 @@ class RCSsJuryPE: def _aff_semxs_suivis(self, etudiants: pe_etudiant.EtudiantsJuryPE): """Affichage des SemX pour debug""" jeunes = list(enumerate(self.semXs_suivis)) - vides = [] - for no_etud, etudid in jeunes[:20]: - pe_affichage.pe_print(f"-> {etudiants.identites[etudid].nomprenom} :") + + for no_etud, etudid in jeunes: + etat = "⛔" if etudid in etudiants.abandons_ids else "✅" + pe_affichage.pe_print( + f"--> {etat} {etudiants.identites[etudid].nomprenom} :" + ) for nom_rcs, rcs in self.semXs_suivis[etudid].items(): if rcs: - pe_affichage.pe_print(f" > SemX {nom_rcs}: {rcs.get_repr()}") - else: - vides += [nom_rcs] + pe_affichage.pe_print(f" > SemX ⏯️{nom_rcs}: {rcs.get_repr()}") + + vides = [] + for nom_rcs in pe_rcs.TOUS_LES_SEMESTRES: + les_semX_suivis = [] + for no_etud, etudid in jeunes: + if self.semXs_suivis[etudid][nom_rcs]: + les_semX_suivis.append(self.semXs_suivis[etudid][nom_rcs]) + if not les_semX_suivis: + vides += [nom_rcs] vides = sorted(list(set(vides))) - pe_affichage.pe_print(f"-> ⚠ SemX vides : {', '.join(vides)}") + pe_affichage.pe_print(f"⚠️ SemX sans données : {', '.join(vides)}") def cree_rcsemxs(self, etudiants: pe_etudiant.EtudiantsJuryPE): """Créé tous les RCSemXs, au regard du cursus des étudiants @@ -146,6 +160,7 @@ class RCSsJuryPE: self.rcsemxs = {} # Pour tous les étudiants du jury + pas_de_semestres = [] for etudid in self.trajectoires_suivies: self.rcsemxs_suivis[etudid] = { nom_rcs: None for nom_rcs in pe_rcs.TOUS_LES_RCS_AVEC_PLUSIEURS_SEM @@ -160,6 +175,7 @@ class RCSsJuryPE: # Pour chaque aggréggat de type xA ou Sx tous_les_agregats = pe_rcs.TOUS_LES_RCS + for nom_rcs in tous_les_agregats: trajectoire = self.trajectoires_suivies[etudid][nom_rcs] if not trajectoire: @@ -183,9 +199,9 @@ class RCSsJuryPE: for Sx in noms_sems_aggregat: semestres_etudiants = etudiants.cursus[etudid][Sx] if not semestres_etudiants: - pe_affichage.pe_print( - f"-> ⚠ Pas de semestres {Sx} pour {etudiants.identites[etudid].etat_civil}" - ) + pas_de_semestres += [ + f"{Sx} pour {etudiants.identites[etudid].nomprenom}" + ] else: semx_id = get_semx_from_semestres_aggreges( self.semXs, semestres_etudiants @@ -204,20 +220,37 @@ class RCSsJuryPE: # Mémoire du RCSemX aux informations de suivi de l'étudiant self.rcsemxs_suivis[etudid][nom_rcs] = rcsemx + # Affichage des étudiants pour lesquels il manque un semestre + pas_de_semestres = sorted(set(pas_de_semestres)) + if pas_de_semestres: + pe_affichage.pe_print("⚠️ Semestres manquants :") + pe_affichage.pe_print( + "\n".join([" " * 10 + psd for psd in pas_de_semestres]) + ) + def _aff_rcsemxs_suivis(self, etudiants): """Affiche les RCSemX suivis par les étudiants""" # Affichage pour debug jeunes = list(enumerate(self.rcsemxs_suivis.keys())) - vides = [] for no_etud, etudid in jeunes: - if etudid not in etudiants.abandons_ids: - pe_affichage.pe_print(f"-> {etudiants.identites[etudid].nomprenom} :") - for nom_rcs, rcs in self.rcsemxs_suivis[etudid].items(): - if rcs: - pe_affichage.pe_print(f" > RCSemX {nom_rcs}: {rcs.get_repr()}") - else: - vides += [f"{nom_rcs}"] - pe_affichage.pe_print(f"-> ⚠ RCSemX vides : {', '.join(list(set(vides)))}") + etat = "⛔" if etudid in etudiants.abandons_ids else "✅" + pe_affichage.pe_print( + f"-> {etat} {etudiants.identites[etudid].nomprenom} :" + ) + for nom_rcs, rcs in self.rcsemxs_suivis[etudid].items(): + if rcs: + pe_affichage.pe_print(f" > RCSemX ⏯️{nom_rcs}: {rcs.get_repr()}") + + vides = [] + for nom_rcs in pe_rcs.TOUS_LES_RCS: + les_rcssemX_suivis = [] + for no_etud, etudid in jeunes: + if self.rcsemxs_suivis[etudid][nom_rcs]: + les_rcssemX_suivis.append(self.rcsemxs_suivis[etudid][nom_rcs]) + if not les_rcssemX_suivis: + vides += [nom_rcs] + vides = sorted(list(set(vides))) + pe_affichage.pe_print(f"⚠️ RCSemX vides : {', '.join(vides)}") def get_rcs_etudiant(