317 lines
14 KiB
Python
317 lines
14 KiB
Python
import app.pe.pe_comp
|
|
from app.pe.rcss import pe_rcs, pe_trajectoires, pe_rcsemx
|
|
import app.pe.pe_etudiant as pe_etudiant
|
|
import app.pe.pe_comp as pe_comp
|
|
from app.models import FormSemestre
|
|
from app.pe import pe_affichage
|
|
|
|
|
|
class RCSsJuryPE:
|
|
"""Classe centralisant tous les regroupements cohérents de
|
|
semestres (RCS) des étudiants à prendre en compte dans un jury PE
|
|
|
|
Args:
|
|
annee_diplome: L'année de diplomation
|
|
"""
|
|
|
|
def __init__(self, annee_diplome: int, etudiants: pe_etudiant.EtudiantsJuryPE):
|
|
self.annee_diplome = annee_diplome
|
|
"""Année de diplômation"""
|
|
|
|
self.etudiants = etudiants
|
|
"""Les étudiants recensés"""
|
|
|
|
self.trajectoires: dict[tuple(int, str) : pe_trajectoires.Trajectoire] = {}
|
|
"""Ensemble des trajectoires recensées (regroupement de (form)semestres BUT)"""
|
|
|
|
self.trajectoires_suivies: dict[int:dict] = {}
|
|
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
|
sa Trajectoire : {etudid: {nom_RCS: Trajectoire}}"""
|
|
|
|
self.semXs: dict[tuple(int, str) : pe_trajectoires.SemX] = {}
|
|
"""Ensemble des SemX recensés (regroupement de (form)semestre BUT de rang x) :
|
|
{(nom_RCS, fid_terminal): SemX}"""
|
|
|
|
self.semXs_suivis: dict[int:dict] = {}
|
|
"""Dictionnaire associant, pour chaque étudiant et pour chaque RCS de type Sx,
|
|
son SemX : {etudid: {nom_RCS_de_type_Sx: SemX}}"""
|
|
|
|
self.rcsemxs: dict[tuple(int, str) : pe_rcsemx.RCSemX] = {}
|
|
"""Ensemble des RCSemX (regroupement de SemX donnant les résultats aux sems de rang x)
|
|
recensés : {(nom_RCS, fid_terminal): RCSemX}"""
|
|
|
|
self.rcsemxs_suivis: dict[int:str] = {}
|
|
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
|
son RCSemX : {etudid: {nom_RCS: RCSemX}}"""
|
|
|
|
def cree_trajectoires(self):
|
|
"""Créé toutes les trajectoires, au regard du cursus des étudiants
|
|
analysés + les mémorise dans les données de l'étudiant
|
|
|
|
Args:
|
|
etudiants: Les étudiants à prendre en compte dans le Jury PE
|
|
"""
|
|
|
|
tous_les_aggregats = pe_rcs.TOUS_LES_RCS
|
|
|
|
for etudid in self.etudiants.cursus:
|
|
self.trajectoires_suivies[etudid] = self.etudiants.trajectoires[etudid]
|
|
|
|
for nom_rcs in tous_les_aggregats:
|
|
# L'aggrégat considéré (par ex: 3S=S1+S2+S3), son nom de son semestre
|
|
# terminal (par ex: S3) et son numéro (par ex: 3)
|
|
noms_semestres = pe_rcs.TYPES_RCS[nom_rcs]["aggregat"]
|
|
nom_semestre_final = noms_semestres[-1]
|
|
|
|
for etudid in self.etudiants.cursus:
|
|
# Le (ou les) semestre(s) marquant la fin du cursus de l'étudiant
|
|
sems_final = self.etudiants.cursus[etudid][nom_semestre_final]
|
|
if sems_final:
|
|
# Le formsemestre final (dernier en date) de l'étudiant,
|
|
# marquant la fin de son aggrégat (par ex: son dernier S3 en date)
|
|
formsemestre_final = app.pe.pe_comp.get_dernier_semestre_en_date(
|
|
sems_final
|
|
)
|
|
|
|
# Ajout (si nécessaire) et récupération du RCS associé
|
|
rcs_id = (nom_rcs, formsemestre_final.formsemestre_id)
|
|
if rcs_id not in self.trajectoires:
|
|
self.trajectoires[rcs_id] = pe_trajectoires.Trajectoire(
|
|
nom_rcs, formsemestre_final
|
|
)
|
|
rcs = self.trajectoires[rcs_id]
|
|
|
|
# La liste des semestres de l'étudiant à prendre en compte
|
|
# pour cette trajectoire
|
|
semestres_a_aggreger = get_rcs_etudiant(
|
|
self.etudiants.cursus[etudid], formsemestre_final, nom_rcs
|
|
)
|
|
|
|
# Ajout des semestres au RCS
|
|
rcs.add_semestres(semestres_a_aggreger)
|
|
|
|
# Mémorise le RCS suivi par l'étudiant
|
|
self.trajectoires_suivies[etudid][nom_rcs] = rcs
|
|
self.etudiants.trajectoires[etudid][nom_rcs] = rcs
|
|
|
|
def cree_semxs(self):
|
|
"""Créé les SemXs (trajectoires/combinaisons de semestre de même rang x),
|
|
en ne conservant dans les trajectoires que les regroupements
|
|
de type Sx"""
|
|
self.semXs = {}
|
|
for rcs_id, trajectoire in self.trajectoires.items():
|
|
if trajectoire.nom in pe_rcs.TOUS_LES_SEMESTRES:
|
|
self.semXs[rcs_id] = pe_trajectoires.SemX(trajectoire)
|
|
|
|
# L'association (pour chaque étudiant entre chaque Sx et le SemX associé)
|
|
self.semXs_suivis = {}
|
|
for etudid in self.etudiants.trajectoires:
|
|
self.semXs_suivis[etudid] = {
|
|
agregat: None for agregat in pe_rcs.TOUS_LES_SEMESTRES
|
|
}
|
|
for agregat in pe_rcs.TOUS_LES_SEMESTRES:
|
|
trajectoire = self.etudiants.trajectoires[etudid][agregat]
|
|
if trajectoire:
|
|
rcs_id = trajectoire.rcs_id
|
|
semX = self.semXs[rcs_id]
|
|
self.semXs_suivis[etudid][agregat] = semX
|
|
self.etudiants.semXs[etudid][agregat] = semX
|
|
|
|
def cree_rcsemxs(self, etudiants: pe_etudiant.EtudiantsJuryPE):
|
|
"""Créé tous les RCSemXs, au regard du cursus des étudiants
|
|
analysés (trajectoires traduisant son parcours dans les
|
|
différents semestres) + les mémorise dans les données de l'étudiant
|
|
"""
|
|
self.rcsemxs_suivis = {}
|
|
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
|
|
}
|
|
|
|
# Recopie des SemX & des suivis associés => est-ce utile ?
|
|
# for nom_rcs in pe_rcs.TOUS_LES_SEMESTRES:
|
|
# trajectoire = self.semXs_suivis[etudid][nom_rcs]
|
|
# if trajectoire:
|
|
# self.rcsemxs[trajectoire.rcs_id] = trajectoire
|
|
# self.rcsemxs_suivis[etudid][nom_rcs] = trajectoire
|
|
|
|
# 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:
|
|
self.rcsemxs_suivis[etudid][nom_rcs] = None
|
|
else:
|
|
# Identifiant de la trajectoire => donnera ceux du RCSemX
|
|
tid = trajectoire.rcs_id
|
|
# Ajout du RCSemX
|
|
if tid not in self.rcsemxs:
|
|
self.rcsemxs[tid] = pe_rcsemx.RCSemX(
|
|
trajectoire.nom, trajectoire.formsemestre_final
|
|
)
|
|
|
|
# Récupére les SemX (RC de type Sx) associés aux semestres de son cursus
|
|
# Par ex: dans S1+S2+S1+S2+S3 => les 2 S1 devient le SemX('S1'), les 2 S2 le SemX('S2'), etc..
|
|
|
|
# Les Sx pris en compte dans l'aggrégat
|
|
noms_sems_aggregat = pe_rcs.TYPES_RCS[nom_rcs]["aggregat"]
|
|
|
|
semxs_a_aggreger = {}
|
|
for Sx in noms_sems_aggregat:
|
|
semestres_etudiants = etudiants.cursus[etudid][Sx]
|
|
if not semestres_etudiants:
|
|
pas_de_semestres += [
|
|
f"{Sx} pour {etudiants.identites[etudid].nomprenom}"
|
|
]
|
|
else:
|
|
semx_id = get_semx_from_semestres_aggreges(
|
|
self.semXs, semestres_etudiants
|
|
)
|
|
if not semx_id:
|
|
raise (
|
|
"Il manque un SemX pour créer les RCSemX dans cree_rcsemxs"
|
|
)
|
|
# Les SemX à ajouter au RCSemX
|
|
semxs_a_aggreger[semx_id] = self.semXs[semx_id]
|
|
|
|
# Ajout des SemX à ceux à aggréger dans le RCSemX
|
|
rcsemx = self.rcsemxs[tid]
|
|
rcsemx.add_semXs(semxs_a_aggreger)
|
|
|
|
# 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()))
|
|
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.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(
|
|
semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str
|
|
) -> dict[int, FormSemestre]:
|
|
"""Ensemble des semestres parcourus (trajectoire)
|
|
par un étudiant dans le cadre
|
|
d'un RCS de type Sx, iA ou iS et ayant pour semestre terminal `formsemestre_final`.
|
|
|
|
Par ex: pour un RCS "3S", dont le formsemestre_terminal est un S3, regroupe
|
|
le ou les S1 qu'il a suivi (1 ou 2 si redoublement) + le ou les S2 + le ou les S3.
|
|
|
|
Les semestres parcourus sont antérieurs (en terme de date de fin)
|
|
au formsemestre_terminal.
|
|
|
|
Args:
|
|
cursus: Dictionnaire {fid: Formsemestre} donnant l'ensemble des semestres
|
|
dans lesquels l'étudiant a été inscrit
|
|
formsemestre_final: le semestre final visé
|
|
nom_rcs: Nom du RCS visé
|
|
"""
|
|
numero_semestre_terminal = formsemestre_final.semestre_id
|
|
# semestres_significatifs = self.get_semestres_significatifs(etudid)
|
|
semestres_significatifs = {}
|
|
for i in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT + 1):
|
|
semestres_significatifs = semestres_significatifs | semestres[f"S{i}"]
|
|
|
|
if nom_rcs.startswith("S"): # les semestres
|
|
numero_semestres_possibles = [numero_semestre_terminal]
|
|
elif nom_rcs.endswith("A"): # les années
|
|
numero_semestres_possibles = [
|
|
int(sem[-1]) for sem in pe_rcs.TYPES_RCS[nom_rcs]["aggregat"]
|
|
]
|
|
assert numero_semestre_terminal in numero_semestres_possibles
|
|
else: # les xS = tous les semestres jusqu'à Sx (eg S1, S2, S3 pour un S3 terminal)
|
|
numero_semestres_possibles = list(range(1, numero_semestre_terminal + 1))
|
|
|
|
semestres_aggreges = {}
|
|
for fid, semestre in semestres_significatifs.items():
|
|
# Semestres parmi ceux de n° possibles & qui lui sont antérieurs
|
|
if (
|
|
semestre.semestre_id in numero_semestres_possibles
|
|
and semestre.date_fin <= formsemestre_final.date_fin
|
|
):
|
|
semestres_aggreges[fid] = semestre
|
|
return semestres_aggreges
|
|
|
|
|
|
def get_semx_from_semestres_aggreges(
|
|
semXs: dict[(str, int) : pe_trajectoires.SemX],
|
|
semestres_a_aggreger: dict[(str, int):FormSemestre],
|
|
) -> (str, int):
|
|
"""Partant d'un dictionnaire de SemX (de la forme
|
|
``{ (nom_rcs, fid): SemX }, et connaissant une liste
|
|
de (form)semestres suivis, renvoie l'identifiant
|
|
(nom_rcs, fid) du SemX qui lui correspond.
|
|
|
|
Le SemX qui correspond est tel que :
|
|
|
|
* le semestre final du SemX correspond au dernier semestre en date des
|
|
semestres_a_aggreger
|
|
* le rang du SemX est le même que celui des semestres_aggreges
|
|
* les semestres_a_aggreger (plus large, car contenant plusieurs
|
|
parcours), matchent avec les semestres aggrégés
|
|
par le SemX
|
|
|
|
|
|
Returns:
|
|
rcf_id: L'identifiant du RCF trouvé
|
|
"""
|
|
assert semestres_a_aggreger, "Pas de semestres à aggréger"
|
|
rangs_a_aggreger = [sem.semestre_id for fid, sem in semestres_a_aggreger.items()]
|
|
assert (
|
|
len(set(rangs_a_aggreger)) == 1
|
|
), "Tous les sem à aggréger doivent être de même rang"
|
|
|
|
# Le dernier semestre des semestres à regrouper
|
|
dernier_sem_a_aggreger = pe_comp.get_dernier_semestre_en_date(semestres_a_aggreger)
|
|
|
|
semxs_ids = [] # Au cas où il y ait plusieurs solutions
|
|
for semx_id, semx in semXs.items():
|
|
# Même semestre final ?
|
|
if semx.get_formsemestre_id_final() == dernier_sem_a_aggreger.formsemestre_id:
|
|
# Les fids
|
|
fids_a_aggreger = set(semestres_a_aggreger.keys())
|
|
# Ceux du semx
|
|
fids_semx = set(semx.semestres_aggreges.keys())
|
|
if fids_a_aggreger.issubset(
|
|
fids_semx
|
|
): # tous les semestres du semx correspond à des sems de la trajectoire
|
|
semxs_ids += [semx_id]
|
|
if len(semxs_ids) == 0:
|
|
return None # rien trouvé
|
|
elif len(semxs_ids) == 1:
|
|
return semxs_ids[0]
|
|
else:
|
|
raise "Plusieurs solutions :)"
|