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: class EtudiantsJuryPE:
"""Classe centralisant la gestion des étudiants à prendre en compte dans un jury de PE""" """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" "Les identités des étudiants traités pour le jury"
self.identites = {} # ex. ETUDINFO_DICT self.identites = {} # ex. ETUDINFO_DICT
@ -70,21 +76,20 @@ class EtudiantsJuryPE:
"Les formsemestres dont il faut calculer les moyennes par tag" "Les formsemestres dont il faut calculer les moyennes par tag"
self.formsemestres_jury_ids = {} 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 """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``
dans la formation ``formation_id``. dans la formation ``formation_id``.
Les données obtenues sont stockées dans les attributs de EtudiantsJuryPE. 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é) formation_id: L'identifiant de la formation (inutilisé)
*Remarque* : ex: JuryPE.get_etudiants_in_jury() *Remarque* : ex: JuryPE.get_etudiants_in_jury()
""" """
"Les cosemestres donnant lieu à même année de diplome" "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 self.cosemestres = cosemestres
pe_tools.pe_print( pe_tools.pe_print(
"1) Recherche des coSemestres -> %d trouvés" % len(cosemestres) "1) Recherche des coSemestres -> %d trouvés" % len(cosemestres)
@ -119,7 +124,7 @@ class EtudiantsJuryPE:
pe_tools.pe_print() pe_tools.pe_print()
"""Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris""" """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""" """Les étudiants dont il faut calculer les moyennes"""
self.etudiants_ids = {etudid for etudid in self.identites} self.etudiants_ids = {etudid for etudid in self.identites}
@ -129,11 +134,13 @@ class EtudiantsJuryPE:
# Synthèse # Synthèse
pe_tools.pe_print( 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) 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" => {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( pe_tools.pe_print(
f" => quelques étudiants futurs diplômés : " f" => quelques étudiants futurs diplômés : "
+ ", ".join([str(etudid) for etudid in list(self.diplomes_ids)[:10]]) + ", ".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 : " f" => semestres dont il faut calculer les moyennes : "
+ ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)]) + ", ".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)}` """Identités des étudiants (sous forme d'un dictionnaire `{etudid: Identite(etudid)}`
qui vont être à traiter au jury PE pour 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é. 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: Returns:
Un dictionnaire `{etudid: Identite(etudid)}` Un dictionnaire `{etudid: Identite(etudid)}`
@ -157,7 +164,7 @@ class EtudiantsJuryPE:
etudids = [ etudids = [
etudid etudid
for etudid in self.cursus 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 and self.cursus[etudid]["abandon"] == False
] ]
etudiants = {etudid: self.identites[etudid] for etudid in etudids} 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 ?""" """ Est-il réorienté / démissionnaire ou a-t-il arrêté volontairement sa formation ?"""
self.cursus[etudid]["abandon"] = arret_de_formation(identite, cosemestres) 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 """Structure les informations sur les semestres suivis par un
étudiant, pour identifier les semestres qui seront pris en compte lors de ses calculs étudiant, pour identifier les semestres qui seront pris en compte lors de ses calculs
de moyennes PE. 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 : 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+S1+S2+S3 si redoublement de la 1ère année
** S1+S2+(année de césure)+S3 si césure, ... ** 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"] 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] = {} self.aggregats[etudid] = {}
"""Tri des semestres par numéro de semestre""" """Tri des semestres par numéro de semestre"""
for nom_sem in pe_tools.TOUS_LES_SEMESTRES: for nom_sem in pe_tools.TOUS_LES_SEMESTRES:
i = int(nom_sem[1]) # le n° du semestre i = int(nom_sem[1]) # le n° du semestre
semestres_i = { semestres_i = {
fid: semestres_etudiant[fid] fid: semestres_significatifs[fid]
for fid in semestres_etudiant for fid in semestres_significatifs
if semestres_etudiant[fid].semestre_id == i if semestres_significatifs[fid].semestre_id == i
} # les semestres de n°i de l'étudiant } # les semestres de n°i de l'étudiant
self.aggregats[etudid][nom_sem] = semestres_i dernier_semestre_i = get_dernier_semestre(semestres_i)
self.cursus[etudid][nom_sem] = 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""" """Tri des semestres par aggrégat et par semestre terminal"""
for aggregat in pe_tools.TOUS_LES_AGGREGATS: 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 de n° inférieur (pax ex: des S1, S2, S3 pour un S3 terminal) et qui lui sont antérieurs"""
semestres_aggreges = {} semestres_aggreges = {}
for fid in self.cursus[etudid]["formsemestres"]: for fid in semestres_significatifs:
semestre = self.cursus[etudid]["formsemestres"][fid] semestre = semestres_significatifs[fid]
if ( if (
semestre.semestre_id <= numero_semestre_terminal semestre.semestre_id <= numero_semestre_terminal
and semestre.date_fin <= formsemestre_terminal.date_fin and semestre.date_fin <= formsemestre_terminal.date_fin
@ -273,8 +319,9 @@ class EtudiantsJuryPE:
assert dernier_semestre_aggregat == dernier_formsemestre_terminal assert dernier_semestre_aggregat == dernier_formsemestre_terminal
def get_formsemestres_terminaux_aggregat(self, aggregat: str): 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' """Pour un aggrégat donné, ensemble des formsemestres terminaux possibles pour l'aggrégat
incluant S1+S2+S3, a pour semestre terminal S3). Ces formsemestres traduisent : (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 * 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. 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)} Un dictionnaire {fid: FormSemestre(fid)}
""" """
formsemestres_terminaux = {} formsemestres_terminaux = {}
for etudid in self.cursus: for etudid in self.aggregats:
"""Les formsemestre_id des semestres terminaux""" if self.aggregats[etudid][aggregat]:
fids = self.cursus[etudid][aggregat].keys() print(self.aggregats[etudid][aggregat])
"""Pour chaque identifiant de semestre terminal, récupère le formsemestre associé""" """Le formsemestre_id du semestre terminal de l'étudiant (s'il existe)"""
for fid in fids: fid = list(self.aggregats[etudid][aggregat].keys())[0]
if fid not in formsemestres_terminaux: """Le formsemestre associé (en le prenant dans l'aggrégat)"""
formsemestres_terminaux[fid] = self.cursus[etudid][aggregat][fid][ formsemestres_terminaux[fid] = self.aggregats[etudid][aggregat][fid][
fid fid
] ]
return formsemestres_terminaux return formsemestres_terminaux
@ -373,7 +420,9 @@ class EtudiantsJuryPE:
nom_sem = semestres_recherches nom_sem = semestres_recherches
semestres = {} semestres = {}
for etudid in self.etudiants_ids: 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 return semestres
else: else:
raise ValueError( raise ValueError(

View File

@ -138,8 +138,8 @@ class JuryPE(object):
pe_tools.pe_print( pe_tools.pe_print(
f"*** Recherche et chargement des étudiants diplômés en {self.diplome} pour la formation {self.formation_id}" 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 = EtudiantsJuryPE(self.diplome) # Les infos sur les étudiants
self.etudiants.find_etudiants(self.diplome, self.formation_id) self.etudiants.find_etudiants(self.formation_id)
"""Génère les semestres taggués (avec le calcul des moyennes) pour le jury PE""" """Génère les semestres taggués (avec le calcul des moyennes) pour le jury PE"""
self.semestres_taggues = compute_semestres_tag(self.etudiants) self.semestres_taggues = compute_semestres_tag(self.etudiants)
@ -689,10 +689,8 @@ def compute_semestres_tag(etudiants: EtudiantsJuryPE):
return semestres_tags return semestres_tags
def compute_aggregats_tag( def compute_aggregats_tag(etudiants: EtudiantsJuryPE, semestres_tag: dict[SemestreTag]):
self, etudiants: EtudiantsJuryPE, semestres_tag: dict[SemestreTag] """Créé les combinaisons de semestres (aggrégats), en calculant les moyennes et les
):
"""Créé les combinaisons de semestres (aggrégat), en calculant les moyennes et les
classements par tag pour chacune. Chaque combinaison (aggrégat) est identifiée classements par tag pour chacune. Chaque combinaison (aggrégat) est identifiée
par un formsemestre terminal. 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 * 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. é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: Args:
etudiants: Les données des étudiants etudiants: Les données des étudiants
semestres_tag: Les semestres tag (pour lesquels des moyennes par tag ont été calculés) 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 = {} 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] = {} sets_tags[aggregat] = {}
"""Semestres aggrégés""" """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"] noms_semestres_aggreges = pe_tools.PARCOURS[aggregat]["aggregat"]
nom_semestre_terminal = noms_semestres_aggreges[-1] nom_semestre_terminal = noms_semestres_aggreges[-1]
@ -730,9 +736,10 @@ def compute_aggregats_tag(
for frmsem_id in formsemestres_terminal: for frmsem_id in formsemestres_terminal:
formsemestre_terminal = formsemestres_terminal[frmsem_id] formsemestre_terminal = formsemestres_terminal[frmsem_id]
"""Nom du set_tag""" """Nom du set_tag"""
nom = "Aggrégat S%d %d %d-%d" % ( nom = "Aggrégat %s %d %s %d-%d" % (
formsemestre_terminal.semestre_id, aggregat,
frmsem_id, frmsem_id,
"+".join(noms_semestres_aggreges),
formsemestre_terminal.date_debut.year, formsemestre_terminal.date_debut.year,
formsemestre_terminal.date_fin.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_AGGREGATS = [cle for cle in PARCOURS.keys() if not cle.startswith("S")]
TOUS_LES_PARCOURS = list(PARCOURS.keys()) 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): def calcul_age(born):
"""Calcule l'age à partir de la date de naissance sous forme d'une chaine de caractère 'jj/mm/aaaa'. """Calcule l'age à partir de la date de naissance sous forme d'une chaine de caractère 'jj/mm/aaaa'.