Améliore l'analyse des abandons de formation (sans prise en compte du formsemestre_base)

This commit is contained in:
Cléo Baras 2024-02-14 15:19:21 +01:00
parent e78a2d3ffe
commit 02a73de04d
4 changed files with 38 additions and 33 deletions

View File

@ -88,7 +88,7 @@ class EtudiantsJuryPE:
self.abandons_ids = {} self.abandons_ids = {}
"""Les etudids des étudiants redoublants/réorientés""" """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 """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``. 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) self.identites[etudid] = Identite.get_etud(etudid)
# Analyse son cursus # 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 # Analyse son parcours pour atteindre chaque semestre de la formation
self.structure_cursus_etudiant(etudid) self.structure_cursus_etudiant(etudid)
@ -142,7 +142,7 @@ class EtudiantsJuryPE:
assert nbre_abandons == len(self.abandons_ids) assert nbre_abandons == len(self.abandons_ids)
pe_affichage.pe_print( 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( # pe_affichage.pe_print(
# " => quelques étudiants futurs diplômés : " # " => quelques étudiants futurs diplômés : "
@ -191,8 +191,7 @@ class EtudiantsJuryPE:
def analyse_etat_etudiant( def analyse_etat_etudiant(
self, self,
etudid: int, etudid: int,
cosemestres: dict[int, FormSemestre], cosemestres: dict[int, FormSemestre]
formsemestre_base: FormSemestre,
): ):
"""Analyse le cursus d'un étudiant pouvant être : """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é, * à insérer une entrée dans ``self.cursus`` pour mémoriser son identité,
avec son nom, prénom, etc... avec son nom, prénom, etc...
* à analyser son parcours, pour déterminer s'il a démissionné, redoublé (autre année de diplôme) * à 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 ou a abandonné l'IUT en cours de route (cf. clé abandon). Un étudiant est considéré
inscrit à aucun cosemestres de rang supérieur ou égal (et donc de dates) en abandon si connaissant son dernier semestre (par ex. un S3) il n'est pas systématiquement
à celui ayant servi à lancer le jury (`formsemestre_base`) inscrit à l'un des S4, S5 ou S6 existants dans les cosemestres.
Args: Args:
etudid: L'etudid d'un étudiant, à ajouter à ceux traiter par le jury etudid: L'etudid d'un étudiant, à ajouter à ceux traiter par le jury
@ -251,7 +251,7 @@ class EtudiantsJuryPE:
else: else:
# Est-il réorienté ou a-t-il arrêté (volontairement) sa formation ? # Est-il réorienté ou a-t-il arrêté (volontairement) sa formation ?
self.cursus[etudid]["abandon"] = arret_de_formation( self.cursus[etudid]["abandon"] = arret_de_formation(
identite, cosemestres, formsemestre_base identite, cosemestres
) )
def get_semestres_significatifs(self, etudid: int): def get_semestres_significatifs(self, etudid: int):
@ -458,9 +458,7 @@ def get_semestres_apc(identite: Identite) -> list:
return semestres_apc return semestres_apc
def arret_de_formation( def arret_de_formation(etud: Identite, cosemestres: dict[int, FormSemestre]) -> bool:
etud: Identite, cosemestres: dict[int, FormSemestre], formsemestre_base: FormSemestre
) -> bool:
"""Détermine si un étudiant a arrêté sa formation (volontairement ou non). Il peut s'agir : """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 * 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 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) 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 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 de rang/semestre_id supérieur (et donc de dates) au dernier semestre dans lequel il a été inscrit.
le jury PE.
Par ex: au moment du jury PE en fin de S5 (pas de S6 renseigné dans Scodoc), 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 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 ? 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 pour le cas des étudiants à l'étranger
TODO:: A reprendre si BUT avec semestres décalés
""" """
# Les semestres APC de l'étudiant # Les semestres APC de l'étudiant
semestres = get_semestres_apc(etud) semestres = get_semestres_apc(etud)
@ -509,13 +505,17 @@ def arret_de_formation(
if not semestres_apc: if not semestres_apc:
return True 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, # 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]}`` # sous la forme ``{semestre_id: [liste des comestres associé à ce semestre_id]}``
cosemestres_tries_par_rang = pe_comp.tri_semestres_par_rang(cosemestres) cosemestres_tries_par_rang = pe_comp.tri_semestres_par_rang(cosemestres)
cosemestres_superieurs = {} cosemestres_superieurs = {}
for rang in cosemestres_tries_par_rang: 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] cosemestres_superieurs[rang] = cosemestres_tries_par_rang[rang]
# Si pas d'autres cosemestres postérieurs # Si pas d'autres cosemestres postérieurs
@ -531,18 +531,24 @@ def arret_de_formation(
etat_inscriptions[rang] = True etat_inscriptions[rang] = True
# Vérifie qu'il n'y a pas de "trous" dans les rangs des cosemestres # Vérifie qu'il n'y a pas de "trous" dans les rangs des cosemestres
rangs = etat_inscriptions.keys() rangs = sorted(etat_inscriptions.keys())
if list(rangs) != list(range(min(rangs), max(rangs) + 1)): if list(rangs) != list(range(min(rangs), max(rangs) + 1)):
difference = set(range(min(rangs), max(rangs) + 1)) - set(rangs) difference = set(range(min(rangs), max(rangs) + 1)) - set(rangs)
affichage = ",".join([f"S{val}" for val in difference]) 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-il inscrit à tous les semestres de rang supérieur ? Si non, est démissionnaire
est_demissionnaire = sum(etat_inscriptions.values()) != len(rangs) est_demissionnaire = sum(etat_inscriptions.values()) != len(rangs)
if est_demissionnaire: 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]) 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 return est_demissionnaire

View File

@ -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) 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() pe_affichage.pe_start_log()
self.diplome = diplome self.diplome = diplome
"L'année du 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}" self.nom_export_zip = f"Jury_PE_{self.diplome}"
"Nom du zip où ranger les fichiers générés" "Nom du zip où ranger les fichiers générés"
@ -90,7 +87,7 @@ class JuryPE(object):
self.diplome}""" self.diplome}"""
) )
self.etudiants = EtudiantsJuryPE(self.diplome) # Les infos sur les étudiants 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.diplomes_ids = self.etudiants.diplomes_ids
self.zipdata = io.BytesIO() self.zipdata = io.BytesIO()

View File

@ -72,18 +72,20 @@ def pe_view_sem_recap(formsemestre_id: int):
# Cosemestres diplomants # Cosemestres diplomants
cosemestres = pe_comp.get_cosemestres_diplomants(annee_diplome) 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": if request.method == "GET":
return render_template( return render_template(
"pe/pe_view_sem_recap.j2", "pe/pe_view_sem_recap.j2",
annee_diplome=annee_diplome, annee_diplome=annee_diplome,
formsemestre=formsemestre, formsemestre=formsemestre,
sco=ScoData(formsemestre=formsemestre), sco=ScoData(formsemestre=formsemestre),
cosemestres=cosemestres, cosemestres=affichage_cosemestres_tries,
rangs_tries=sorted(affichage_cosemestres_tries.keys())
) )
# request.method == "POST" # request.method == "POST"
jury = pe_jury.JuryPE(annee_diplome, formsemestre) jury = pe_jury.JuryPE(annee_diplome)
if not jury.diplomes_ids: if not jury.diplomes_ids:
flash("aucun étudiant à considérer !") flash("aucun étudiant à considérer !")
return redirect( return redirect(

View File

@ -43,12 +43,12 @@
<h3>Avis de poursuites d'études de la promo {{ annee_diplome }}</h3> <h3>Avis de poursuites d'études de la promo {{ annee_diplome }}</h3>
<div class="help"> <div class="help">
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 :
<ul> <ul>
{% for fid in cosemestres %} {% for rang in rangs_tries %}
<li> <li>
{{ cosemestres[fid].titre_annee() }} <strong>Semestre {{rang}}</strong> : {{ cosemestres[rang] }}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>