Adaptation diverses pour la gestion des aggrégats (dont les redoublements de semestre)

This commit is contained in:
Cléo Baras 2024-01-23 18:44:44 +01:00
parent 8b3efe9dad
commit e3cde87a0f
3 changed files with 100 additions and 75 deletions

View File

@ -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: <FormSemestre 18 BUT Réseaux et Télécommunications semestre 1 FI 2021-2022>,
26: <FormSemestre 26 BUT Réseaux et Télécommunications semestre 2 FI 2022>}},
'2A': {79: {18: <FormSemestre 18 BUT Réseaux et Télécommunications semestre 1 FI 2021-2022>,
26: <FormSemestre 26 BUT Réseaux et Télécommunications semestre 2 FI 2022>,
56: <FormSemestre 56 BUT Réseaux et Télécommunications semestre 3 FI 2022-2023>,
79: <FormSemestre 79 BUT Réseaux et Télécommunications semestre 4 FI 2023>}},
'3A': {},
'3S': {112: {18: <FormSemestre 18 BUT Réseaux et Télécommunications semestre 1 FI 2021-2022>,
26: <FormSemestre 26 BUT Réseaux et Télécommunications semestre 2 FI 2022>,
56: <FormSemestre 56 BUT Réseaux et Télécommunications semestre 3 FI 2022-2023>,
112: <FormSemestre 112 BUT Réseaux et Télécommunications 2023 semestre 3 FI 2023-2024>}},
'4S': {79: {18: <FormSemestre 18 BUT Réseaux et Télécommunications semestre 1 FI 2021-2022>,
26: <FormSemestre 26 BUT Réseaux et Télécommunications semestre 2 FI 2022>,
56: <FormSemestre 56 BUT Réseaux et Télécommunications semestre 3 FI 2022-2023>,
79: <FormSemestre 79 BUT Réseaux et Télécommunications semestre 4 FI 2023>}},
'5S': {},
'6S': {},
'S1': {18: {18: <FormSemestre 18 BUT Réseaux et Télécommunications semestre 1 FI 2021-2022>}},
'S2': {26: {26: <FormSemestre 26 BUT Réseaux et Télécommunications semestre 2 FI 2022>}},
'S3': {112: {56: <FormSemestre 56 BUT Réseaux et Télécommunications semestre 3 FI 2022-2023>,
112: <FormSemestre 112 BUT Réseaux et Télécommunications 2023 semestre 3 FI 2023-2024>}},
'S4': {79: {79: <FormSemestre 79 BUT Réseaux et Télécommunications semestre 4 FI 2023>}},
'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,13 +336,13 @@ 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][
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
@ -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(

View File

@ -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,10 +716,13 @@ 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"""
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]
@ -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,
)

View File

@ -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'.