ScoDoc-PE/app/pe/pe_rcs.py

282 lines
10 KiB
Python
Raw Normal View History

2024-02-08 22:09:11 +01:00
##############################################################################
# Module "Avis de poursuite d'étude"
# conçu et développé par Cléo Baras (IUT de Grenoble)
##############################################################################
"""
Created on 01-2024
@author: barasc
"""
import app.pe.pe_comp as pe_comp
from app.models import FormSemestre
2024-02-17 02:35:43 +01:00
from app.pe import pe_sxtag
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
TYPES_RCS = {
"S1": {
"aggregat": ["S1"],
"descr": "Semestre 1 (S1)",
},
"S2": {
"aggregat": ["S2"],
"descr": "Semestre 2 (S2)",
},
"1A": {
"aggregat": ["S1", "S2"],
"descr": "BUT1 (S1+S2)",
},
"S3": {
"aggregat": ["S3"],
"descr": "Semestre 3 (S3)",
},
"S4": {
"aggregat": ["S4"],
"descr": "Semestre 4 (S4)",
},
"2A": {
"aggregat": ["S3", "S4"],
"descr": "BUT2 (S3+S4)",
},
"3S": {
"aggregat": ["S1", "S2", "S3"],
"descr": "Moyenne du semestre 1 au semestre 3 (S1+S2+S3)",
},
"4S": {
"aggregat": ["S1", "S2", "S3", "S4"],
"descr": "Moyenne du semestre 1 au semestre 4 (S1+S2+S3+S4)",
},
"S5": {
"aggregat": ["S5"],
"descr": "Semestre 5 (S5)",
},
"S6": {
"aggregat": ["S6"],
"descr": "Semestre 6 (S6)",
},
"3A": {
"aggregat": ["S5", "S6"],
"descr": "3ème année (S5+S6)",
},
"5S": {
"aggregat": ["S1", "S2", "S3", "S4", "S5"],
"descr": "Moyenne du semestre 1 au semestre 5 (S1+S2+S3+S4+S5)",
},
"6S": {
"aggregat": ["S1", "S2", "S3", "S4", "S5", "S6"],
"descr": "Moyenne globale (S1+S2+S3+S4+S5+S6)",
},
}
2024-02-08 22:09:11 +01:00
"""Dictionnaire détaillant les différents regroupements cohérents
de semestres (RCS), en leur attribuant un nom et en détaillant
le nom des semestres qu'ils regroupent et l'affichage qui en sera fait
dans les tableurs de synthèse.
"""
2024-02-08 22:09:11 +01:00
TOUS_LES_RCS_AVEC_PLUSIEURS_SEM = [cle for cle in TYPES_RCS if not cle.startswith("S")]
TOUS_LES_RCS = list(TYPES_RCS.keys())
2024-02-08 22:09:11 +01:00
TOUS_LES_SEMESTRES = [cle for cle in TYPES_RCS if cle.startswith("S")]
class RCS:
2024-02-17 02:35:43 +01:00
"""Modélise un ensemble de semestres finals d'étudiants
2024-02-08 22:09:11 +01:00
associé à un type de regroupement cohérent de semestres
donné (par ex: 'S2', '3S', '2A').
2024-02-17 02:35:43 +01:00
Si le RCS est un semestre de type Si, stocke le
2024-02-08 22:09:11 +01:00
formsemestres de numéro i qu'ont suivi l'étudiant pour atteindre le Si
(en général 1 si personnes n'a redoublé, mais 2 s'il y a des redoublants)
2024-02-08 22:09:11 +01:00
Pour le RCS de type iS ou iA (par ex, 3A=S1+S2+S3), elle identifie
les semestres que les étudiants ont suivis pour les amener jusqu'au semestre
terminal de la trajectoire (par ex: ici un S3).
2024-02-08 22:09:11 +01:00
Ces semestres peuvent être :
2024-02-08 22:09:11 +01:00
* des S1+S2+S1+S2+S3 si redoublement de la 1ère année
* des S1+S2+(année de césure)+S3 si césure, ...
2024-02-08 22:09:11 +01:00
Args:
nom_rcs: Un nom du RCS (par ex: '5S')
semestre_final: Le semestre final du RCS
"""
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
2024-02-17 02:35:43 +01:00
self.nom: str = nom_rcs
"""Nom du RCS"""
2024-02-17 02:35:43 +01:00
self.formsemestre_final: FormSemestre = semestre_final
"""FormSemestre terminal du RCS"""
2024-02-17 02:35:43 +01:00
self.rcs_id: (str, int) = (nom_rcs, semestre_final.formsemestre_id)
"""Identifiant du RCS sous forme (nom_rcs, id du semestre_terminal)"""
2024-02-17 02:35:43 +01:00
# self.semestres_aggreges: dict[int:FormSemestre] = {}
# """Semestres regroupés dans le RCS"""
self.sxtags_aggreges: dict[(str, int): pe_sxtag.SxTag] = {}
"""Les SxTag aggrégés"""
def get_formsemestre_id_final(self):
"""Renvoie l'identifiant du formsemestre final du RCS"""
return self.formsemestre_final.formsemestre_id
2024-02-17 02:35:43 +01:00
def add_sxtags_a_aggreger(self, sxtags: dict[(str,int): pe_sxtag.SxTag]):
"""Ajout des SxTag aux semestres à regrouper
Args:
2024-02-17 02:35:43 +01:00
sxtags: Dictionnaire ``{(str,fid): SxTag}`` à ajouter
"""
2024-02-17 02:35:43 +01:00
self.sxtags_aggreges = self.sxtags_aggreges | sxtags
def get_repr(self, verbose=True) -> str:
"""Représentation textuelle d'un RCS
2024-02-17 02:35:43 +01:00
basé sur ses sxtags aggrégés"""
noms = []
2024-02-17 02:35:43 +01:00
for sxtag_id, sxtag in self.sxtags_aggreges.items():
noms.append(f"S{sxtag.semestre_id}")
noms = sorted(noms)
2024-02-08 22:09:11 +01:00
title = f"""{self.nom} ({
self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"""
if verbose and noms:
2024-02-08 22:09:11 +01:00
title += " - " + "+".join(noms)
return title
class RCSsJuryPE:
2024-02-17 02:35:43 +01:00
"""Classe centralisant tous les regroupements cohérents de
2024-02-08 22:09:11 +01:00
semestres (RCS) des étudiants à prendre en compte dans un jury PE
2024-02-08 22:09:11 +01:00
Args:
annee_diplome: L'année de diplomation
"""
2024-02-08 22:09:11 +01:00
def __init__(self, annee_diplome: int):
self.annee_diplome = annee_diplome
"""Année de diplômation"""
self.rcss: dict[tuple:RCS] = {}
"""Ensemble des RCS recensés : {(nom_RCS, fid_terminal): RCS}"""
self.suivi: dict[int:str] = {}
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
son RCS : {etudid: {nom_RCS: RCS}}"""
2024-02-17 02:35:43 +01:00
def cree_rcss(self, etudiants: EtudiantsJuryPE, sxtags: dict[(str, int), pe_sxtag.SxTag]):
"""Créé tous les RCS, au regard du cursus des étudiants
2024-02-17 02:35:43 +01:00
analysés et des SxTag calculés.
Les mémorise dans les données de chaque étudiant.
Args:
etudiants: Les étudiants à prendre en compte dans le Jury PE
2024-02-17 02:35:43 +01:00
pe_sxtag: Les Sx taggués
"""
for nom_rcs in pe_comp.TOUS_LES_SEMESTRES + TOUS_LES_RCS_AVEC_PLUSIEURS_SEM:
2024-02-08 22:09:11 +01:00
# 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)
2024-02-17 02:35:43 +01:00
noms_semestre_de_aggregat = TYPES_RCS[nom_rcs]["aggregat"] # ["S1", "S2", "S3"]
nom_semestre_terminal = noms_semestre_de_aggregat[-1] # "S3"
2024-02-17 02:35:43 +01:00
for etudid in etudiants.trajectoires:
if etudid not in self.suivi:
self.suivi[etudid] = {
aggregat: None
for aggregat in pe_comp.TOUS_LES_SEMESTRES
2024-02-08 22:09:11 +01:00
+ TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
}
2024-02-08 22:09:11 +01:00
# Le formsemestre terminal (dernier en date) associé au
# semestre marquant la fin de l'aggrégat
# (par ex: son dernier S3 en date)
2024-02-17 02:35:43 +01:00
semestres = etudiants.trajectoires[etudid][nom_semestre_terminal]
if semestres:
formsemestre_final = get_dernier_semestre_en_date(semestres)
2024-02-08 22:09:11 +01:00
# Ajout ou récupération de la trajectoire
2024-02-17 02:35:43 +01:00
rcs_id = (nom_rcs, formsemestre_final.formsemestre_id)
if rcs_id not in self.rcss:
rcs = RCS(nom_rcs, formsemestre_final)
self.rcss[rcs_id] = rcs
else:
2024-02-17 02:35:43 +01:00
rcs = self.rcss[rcs_id]
2024-02-08 22:09:11 +01:00
# La liste des semestres de l'étudiant à prendre en compte
# pour cette trajectoire
2024-02-17 02:35:43 +01:00
semestres_a_aggreger = get_trajectoire_etudiant(
etudiants.trajectoires[etudid], formsemestre_final, nom_rcs
)
2024-02-17 02:35:43 +01:00
# Extrait les sxtags correspondants aux semestres à aggréger
# (par ex. des 2 semestres S1(18)+S1(26) récupère le sxtag S1(26)
sxtags_a_aggreger = {}
semestres_tries = pe_comp.tri_semestres_par_rang(semestres_a_aggreger)
for rang in semestres_tries:
sems = semestres_tries[rang] # les 1 ou 2 semestres de même rang suivi
sxtag_id = pe_sxtag.get_sxtag_from_semestres(sems, sxtags)
if not sxtag_id:
raise ValueError(f"Il manque un sxtag pour {sems}")
sxtags_a_aggreger[sxtag_id] = sxtags[sxtag_id]
2024-02-08 22:09:11 +01:00
# Ajout des semestres à la trajectoire
2024-02-17 02:35:43 +01:00
rcs.add_sxtags_a_aggreger(sxtags_a_aggreger)
2024-02-08 22:09:11 +01:00
# Mémoire la trajectoire suivie par l'étudiant
2024-02-17 02:35:43 +01:00
self.suivi[etudid][nom_rcs] = rcs
2024-02-17 02:35:43 +01:00
def get_trajectoire_etudiant(
2024-02-08 22:09:11 +01:00
semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str
) -> dict[int, FormSemestre]:
2024-02-17 02:35:43 +01:00
"""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`.
2024-02-17 02:35:43 +01:00
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:
2024-02-17 02:35:43 +01:00
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 = {}
2024-02-08 22:09:11 +01:00
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 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
2024-02-08 22:09:11 +01:00
def get_descr_rcs(nom_rcs: str) -> str:
"""Renvoie la description pour les tableurs de synthèse
Excel d'un nom de RCS"""
return TYPES_RCS[nom_rcs]["descr"]