From 02a73de04db58b17c1106540292eeec0b0ab5a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9o=20BARAS=20=28IUT1=20Grenoble=29?= Date: Wed, 14 Feb 2024 15:19:21 +0100 Subject: [PATCH] =?UTF-8?q?Am=C3=A9liore=20l'analyse=20des=20abandons=20de?= =?UTF-8?q?=20formation=20(sans=20prise=20en=20compte=20du=20formsemestre?= =?UTF-8?q?=5Fbase)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/pe/pe_etudiant.py | 50 +++++++++++++++------------ app/pe/pe_jury.py | 7 ++-- app/pe/pe_view.py | 8 +++-- app/templates/pe/pe_view_sem_recap.j2 | 6 ++-- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/app/pe/pe_etudiant.py b/app/pe/pe_etudiant.py index 8b941d7f..7448bf04 100644 --- a/app/pe/pe_etudiant.py +++ b/app/pe/pe_etudiant.py @@ -88,7 +88,7 @@ class EtudiantsJuryPE: self.abandons_ids = {} """Les etudids des étudiants redoublants/réorientés""" - def find_etudiants(self, formsemestre_base: FormSemestre): + def find_etudiants(self): """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``. @@ -117,7 +117,7 @@ class EtudiantsJuryPE: self.identites[etudid] = Identite.get_etud(etudid) # Analyse son cursus - self.analyse_etat_etudiant(etudid, cosemestres, formsemestre_base) + self.analyse_etat_etudiant(etudid, cosemestres) # Analyse son parcours pour atteindre chaque semestre de la formation self.structure_cursus_etudiant(etudid) @@ -142,7 +142,7 @@ class EtudiantsJuryPE: assert nbre_abandons == len(self.abandons_ids) pe_affichage.pe_print( - f" => {nbre_abandons} étudiants non considéré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 : " @@ -191,8 +191,7 @@ class EtudiantsJuryPE: def analyse_etat_etudiant( self, etudid: int, - cosemestres: dict[int, FormSemestre], - formsemestre_base: FormSemestre, + cosemestres: dict[int, FormSemestre] ): """Analyse le cursus d'un étudiant pouvant être : @@ -205,9 +204,10 @@ class EtudiantsJuryPE: * à 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 a démissionné, redoublé (autre année de diplôme) - ou a abandonné l'IUT en cours de route (cf. clé abandon). Un étudiant est considéré en abandon s'il n'est - inscrit à aucun cosemestres de rang supérieur ou égal (et donc de dates) - à celui ayant servi à lancer le jury (`formsemestre_base`) + ou a abandonné l'IUT en cours de route (cf. clé abandon). Un étudiant est considéré + en abandon si connaissant son dernier semestre (par ex. un S3) il n'est pas systématiquement + inscrit à l'un des S4, S5 ou S6 existants dans les cosemestres. + Args: etudid: L'etudid d'un étudiant, à ajouter à ceux traiter par le jury @@ -251,7 +251,7 @@ class EtudiantsJuryPE: else: # Est-il réorienté ou a-t-il arrêté (volontairement) sa formation ? self.cursus[etudid]["abandon"] = arret_de_formation( - identite, cosemestres, formsemestre_base + identite, cosemestres ) def get_semestres_significatifs(self, etudid: int): @@ -458,9 +458,7 @@ def get_semestres_apc(identite: Identite) -> list: return semestres_apc -def arret_de_formation( - etud: Identite, cosemestres: dict[int, FormSemestre], formsemestre_base: FormSemestre -) -> bool: +def arret_de_formation(etud: Identite, cosemestres: dict[int, FormSemestre]) -> bool: """Détermine si un étudiant a arrêté sa formation (volontairement ou non). Il peut s'agir : * d'une réorientation à l'initiative du jury de semestre ou d'une démission @@ -473,8 +471,7 @@ def arret_de_formation( Dans les cas, on considérera que l'étudiant a arrêté sa formation s'il n'est pas dans l'un des "derniers" cosemestres (semestres conduisant à la même année de diplômation) connu dans Scodoc. Par "derniers" cosemestres, est fait le choix d'analyser tous les cosemestres - de rang/semestre_id supérieur ou égal (et donc de dates) à celui du ``formsemestre_base`` ayant servi à lancer - le jury PE. + de rang/semestre_id supérieur (et donc de dates) au dernier semestre dans lequel il a été inscrit. Par ex: au moment du jury PE en fin de S5 (pas de S6 renseigné dans Scodoc), l'étudiant doit appartenir à une instance des S5 qui conduisent à la diplomation dans @@ -501,7 +498,6 @@ def arret_de_formation( Est-il réorienté, démissionnaire ou a-t-il arrêté de son propre chef sa formation ? TODO:: A reprendre pour le cas des étudiants à l'étranger - TODO:: A reprendre si BUT avec semestres décalés """ # Les semestres APC de l'étudiant semestres = get_semestres_apc(etud) @@ -509,13 +505,17 @@ def arret_de_formation( if not semestres_apc: return True + # Le dernier semestre de l'étudiant + dernier_formsemestre = semestres[0] + rang_dernier_semestre = dernier_formsemestre.semestre_id + # Les cosemestres de rang supérieur ou égal à celui de formsemestre, triés par rang, # sous la forme ``{semestre_id: [liste des comestres associé à ce semestre_id]}`` cosemestres_tries_par_rang = pe_comp.tri_semestres_par_rang(cosemestres) cosemestres_superieurs = {} for rang in cosemestres_tries_par_rang: - if rang >= formsemestre_base.semestre_id: + if rang > rang_dernier_semestre: cosemestres_superieurs[rang] = cosemestres_tries_par_rang[rang] # Si pas d'autres cosemestres postérieurs @@ -531,18 +531,24 @@ def arret_de_formation( etat_inscriptions[rang] = True # Vérifie qu'il n'y a pas de "trous" dans les rangs des cosemestres - rangs = etat_inscriptions.keys() - if list(rangs) != list(range(min(rangs), max(rangs)+1)): - difference = set(range(min(rangs), max(rangs)+1)) - set(rangs) + rangs = sorted(etat_inscriptions.keys()) + if list(rangs) != list(range(min(rangs), max(rangs) + 1)): + difference = set(range(min(rangs), max(rangs) + 1)) - set(rangs) affichage = ",".join([f"S{val}" for val in difference]) - raise ScoValueError(f"Il manque le(s) semestre(s) {affichage} au cursus de vos étudiants.") + raise ScoValueError( + f"Il manque le(s) semestre(s) {affichage} au cursus de {etud.etat_civil} ({etud.etudid})." + ) # Est-il inscrit à tous les semestres de rang supérieur ? Si non, est démissionnaire est_demissionnaire = sum(etat_inscriptions.values()) != len(rangs) if est_demissionnaire: - non_inscrit_a = [rang for rang in etat_inscriptions if not etat_inscriptions[rang]] + non_inscrit_a = [ + rang for rang in etat_inscriptions if not etat_inscriptions[rang] + ] affichage = ",".join([f"S{val}" for val in non_inscrit_a]) - pe_affichage.pe_print(f"{etud.etat_civil} ({etud.etudid} considéré en abandon car non inscrit dans un (ou des) semestre(s) {affichage} amenant à diplômation") + pe_affichage.pe_print( + f"{etud.etat_civil} ({etud.etudid}) considéré en abandon car non inscrit dans un (ou des) semestre(s) {affichage} amenant à diplômation" + ) return est_demissionnaire diff --git a/app/pe/pe_jury.py b/app/pe/pe_jury.py index b83f40f2..d486692f 100644 --- a/app/pe/pe_jury.py +++ b/app/pe/pe_jury.py @@ -70,14 +70,11 @@ class JuryPE(object): diplome : l'année d'obtention du diplome BUT et du jury de PE (généralement février XXXX) """ - def __init__(self, diplome: int, formsemestre_base: FormSemestre): + def __init__(self, diplome: int): pe_affichage.pe_start_log() self.diplome = diplome "L'année du diplome" - self.formsemestre_base = formsemestre_base - "Le formsemestre ayant servi à lancer le jury PE (souvent un S3 ou un S5)" - self.nom_export_zip = f"Jury_PE_{self.diplome}" "Nom du zip où ranger les fichiers générés" @@ -90,7 +87,7 @@ class JuryPE(object): self.diplome}""" ) self.etudiants = EtudiantsJuryPE(self.diplome) # Les infos sur les étudiants - self.etudiants.find_etudiants(formsemestre_base) + self.etudiants.find_etudiants() self.diplomes_ids = self.etudiants.diplomes_ids self.zipdata = io.BytesIO() diff --git a/app/pe/pe_view.py b/app/pe/pe_view.py index 0c2b733d..28a8c8db 100644 --- a/app/pe/pe_view.py +++ b/app/pe/pe_view.py @@ -72,18 +72,20 @@ def pe_view_sem_recap(formsemestre_id: int): # Cosemestres diplomants cosemestres = pe_comp.get_cosemestres_diplomants(annee_diplome) - + cosemestres_tries = pe_comp.tri_semestres_par_rang(cosemestres) + affichage_cosemestres_tries = {rang: ", ".join([sem.titre_annee() for sem in cosemestres_tries[rang]]) for rang in cosemestres_tries} if request.method == "GET": return render_template( "pe/pe_view_sem_recap.j2", annee_diplome=annee_diplome, formsemestre=formsemestre, sco=ScoData(formsemestre=formsemestre), - cosemestres=cosemestres, + cosemestres=affichage_cosemestres_tries, + rangs_tries=sorted(affichage_cosemestres_tries.keys()) ) # request.method == "POST" - jury = pe_jury.JuryPE(annee_diplome, formsemestre) + jury = pe_jury.JuryPE(annee_diplome) if not jury.diplomes_ids: flash("aucun étudiant à considérer !") return redirect( diff --git a/app/templates/pe/pe_view_sem_recap.j2 b/app/templates/pe/pe_view_sem_recap.j2 index 756b7f87..250dc078 100644 --- a/app/templates/pe/pe_view_sem_recap.j2 +++ b/app/templates/pe/pe_view_sem_recap.j2 @@ -43,12 +43,12 @@

Avis de poursuites d'études de la promo {{ annee_diplome }}

- Seront (a minima) pris en compte les étudiants ayant été inscrits aux semestres suivants : + Seront pris en compte les étudiants ayant été inscrits à l'un des semestres suivants :