Amélioration nomenclature : trajectoire => RCS + ménage et refonte codes (état intermédiaire n°1)

This commit is contained in:
Cléo Baras 2024-02-05 12:58:09 +01:00
parent 5828d4aaaf
commit 9c4e2627ba
7 changed files with 337 additions and 371 deletions

View File

@ -47,6 +47,7 @@ from flask import g
import app.scodoc.sco_utils as scu
from app.models import FormSemestre
from app.pe.pe_rcs import TYPES_RCS
from app.scodoc import sco_formsemestre
from app.scodoc.sco_logos import find_logo
@ -72,67 +73,11 @@ Descriptif d'un parcours classique BUT
TODO:: A améliorer si BUT en moins de 6 semestres
"""
PARCOURS = {
"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 BUT (S1+S2+S3+S4+S5+S6)",
},
}
NBRE_SEMESTRES_DIPLOMANT = 6
AGGREGAT_DIPLOMANT = (
"6S" # aggrégat correspondant à la totalité des notes pour le diplôme
)
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())
TOUS_LES_SEMESTRES = TYPES_RCS[AGGREGAT_DIPLOMANT]["aggregat"]
# ----------------------------------------------------------------------------------------

View File

@ -37,6 +37,7 @@ Created on 17/01/2024
"""
import pandas as pd
import app.pe.pe_rcs
from app.models import FormSemestre, Identite, Formation
from app.pe import pe_comp, pe_affichage
from app.scodoc import codes_cursus
@ -121,16 +122,20 @@ class EtudiantsJuryPE:
# Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris
self.etudiants_diplomes = self.get_etudiants_diplomes()
"""Les identités des étudiants diplômés"""
self.diplomes_ids = set(self.etudiants_diplomes.keys())
"""Les identifiants des étudiants diplômés"""
self.etudiants_ids = set(self.identites.keys())
self.formsemestres_jury_ids = self.get_formsemestres()
"""Les formsemestres (des étudiants) dont il faut calculer les moyennes"""
"""Les identifiants des étudiants (diplômés, redoublants ou ayant abandonnés) à traiter"""
# Les abandons (pour debug)
self.abandons = self.get_etudiants_redoublants_ou_reorientes()
"""Les identités des étudiants ayant redoublés ou ayant abandonnés"""
self.abandons_ids = set(self.abandons)
"""Les identifiants des étudiants ayant redoublés ou ayant abandonnés"""
# Synthèse
pe_affichage.pe_print(
@ -142,9 +147,6 @@ class EtudiantsJuryPE:
pe_affichage.pe_print(
f" => {nbre_abandons} étudiants non considérés (redoublement, réorientation, abandon"
)
pe_affichage.pe_print(
f" => {len(self.formsemestres_jury_ids)} semestres dont il faut calculer la moyenne"
)
# pe_affichage.pe_print(
# " => quelques étudiants futurs diplômés : "
# + ", ".join([str(etudid) for etudid in list(self.etudiants_diplomes)[:10]])
@ -270,62 +272,16 @@ class EtudiantsJuryPE:
semestres_significatifs = self.get_semestres_significatifs(etudid)
# Tri des semestres par numéro de semestre
for nom_sem in pe_comp.TOUS_LES_SEMESTRES:
i = int(nom_sem[1]) # le n° du semestre
for i in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT+1):
# les semestres de n°i de l'étudiant:
semestres_i = {
fid: sem_sig
for fid, sem_sig in semestres_significatifs.items()
if sem_sig.semestre_id == i
}
self.cursus[etudid][nom_sem] = semestres_i
self.cursus[etudid][f"S{i}"] = semestres_i
def get_trajectoire(
self, etudid: int, formsemestre_final: FormSemestre, nom_aggregat: str
):
"""Ensemble des semestres parcourus par
un étudiant pour l'amener à un semestre terminal.
Si nom_aggregat est de type "Si", limite les semestres à ceux de numéro i.
Par ex: si formsemestre_terminal est un S3 et nom_agrregat "S3", ne prend en compte que les
semestres 3.
Si nom_aggregat est de type "iA" ou "iS" (incluant plusieurs numéros de semestres), prend en
compte les dit numéros de semestres.
Par ex: si formsemestre_terminal est un S3, ensemble des S1,
S2, S3 suivi pour l'amener au S3 (il peut y avoir plusieurs S1,
ou S2, ou S3 s'il a redoublé).
Les semestres parcourus sont antérieurs (en terme de date de fin)
au formsemestre_terminal.
Args:
etudid: L'identifiant de l'étudiant
formsemestre_final: le semestre final visé
"""
numero_semestre_terminal = formsemestre_final.semestre_id
semestres_significatifs = self.get_semestres_significatifs(etudid)
if nom_aggregat.startswith("S"): # les semestres
numero_semestres_possibles = [numero_semestre_terminal]
elif nom_aggregat.endswith("A"): # les années
numero_semestres_possibles = [
int(sem[-1]) for sem in pe_comp.PARCOURS[nom_aggregat]["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_formsemestres_terminaux_aggregat(self, aggregat: str):
"""Pour un aggrégat donné, ensemble des formsemestres terminaux possibles pour l'aggrégat
@ -353,64 +309,6 @@ class EtudiantsJuryPE:
formsemestres_terminaux[fid] = trajectoire.formsemestre_final
return formsemestres_terminaux
def get_formsemestres(self, semestres_recherches=None):
"""Ayant connaissance des étudiants dont il faut calculer les moyennes pour
le jury PE (attribut `self.etudiant_ids) et de leur cursus (semestres
parcourus),
renvoie un dictionnaire ``{fid: FormSemestre(fid)}``
contenant l'ensemble des formsemestres de leurs cursus, dont il faudra calculer
la moyenne.
Les formsemestres sont limités à ceux indiqués dans ``semestres_recherches``.
Args:
semestres_recherches: Une liste ou une chaine de caractères parmi :
* None : pour obtenir tous les formsemestres du jury
* 'Si' : pour obtenir les semestres de n° i (par ex. 'S1')
* 'iA' : pour obtenir les semestres de l'année i (par ex. '1A' donne ['S1, 'S2'])
* '3S', '4S' : pour obtenir les combinaisons de semestres définies par les aggrégats
Returns:
Un dictionnaire de la forme `{fid: FormSemestre(fid)}`
Remarque:
Une liste de la forme `[ 'Si', 'iA' , ... ]` (combinant les formats précédents) est possible.
"""
if semestres_recherches is None:
# Appel récursif pour obtenir tous les semestres (validants)
semestres = self.get_formsemestres(pe_comp.AGGREGAT_DIPLOMANT)
return semestres
if isinstance(semestres_recherches, list):
# Appel récursif sur tous les éléments de la liste
semestres = {}
for elmt in semestres_recherches:
semestres_elmt = self.get_formsemestres(elmt)
semestres = semestres | semestres_elmt
return semestres
if (
isinstance(semestres_recherches, str)
and semestres_recherches in pe_comp.TOUS_LES_AGGREGATS
):
# Cas d'un aggrégat avec appel récursif sur toutes les entrées de l'aggrégat
semestres = self.get_formsemestres(
pe_comp.PARCOURS[semestres_recherches]["aggregat"]
)
return semestres
if (
isinstance(semestres_recherches, str)
and semestres_recherches in pe_comp.TOUS_LES_SEMESTRES
):
# semestres_recherches est un nom de semestre de type S1,
# pour une recherche parmi les étudiants à prendre en compte
# dans le jury (diplômé et redoublants non diplômé)
nom_sem = semestres_recherches
semestres = {}
for etudid in self.etudiants_ids:
if self.cursus[etudid][nom_sem]:
semestres = semestres | self.cursus[etudid][nom_sem]
return semestres
raise ValueError("Probleme de paramètres d'appel dans get_formsemestreids")
def nbre_etapes_max_diplomes(self, etudids: list[int]) -> int:
"""Partant d'un ensemble d'étudiants,

View File

@ -1,8 +1,8 @@
from app.comp import moy_sem
from app.pe.pe_tabletags import TableTag, MoyenneTag
from app.pe.pe_etudiant import EtudiantsJuryPE
from app.pe.pe_trajectoire import Trajectoire, TrajectoiresJuryPE
from app.pe.pe_trajectoiretag import TrajectoireTag
from app.pe.pe_rcs import RCS, RCSsJuryPE
from app.pe.pe_trajectoiretag import RCSTag
import pandas as pd
@ -15,8 +15,8 @@ class AggregatInterclasseTag(TableTag):
self,
nom_aggregat: str,
etudiants: EtudiantsJuryPE,
trajectoires_jury_pe: TrajectoiresJuryPE,
trajectoires_taggues: dict[tuple, TrajectoireTag],
trajectoires_jury_pe: RCSsJuryPE,
trajectoires_taggues: dict[tuple, RCSTag],
):
"""
Interclasse l'ensemble des étudiants diplômés à une année
@ -44,14 +44,14 @@ class AggregatInterclasseTag(TableTag):
}
# Les trajectoires (et leur version tagguées), en ne gardant que celles associées à l'aggrégat
self.trajectoires: dict[int, Trajectoire] = {}
self.trajectoires: dict[int, RCS] = {}
"""Ensemble des trajectoires associées à l'aggrégat"""
for trajectoire_id in trajectoires_jury_pe.trajectoires:
trajectoire = trajectoires_jury_pe.trajectoires[trajectoire_id]
for trajectoire_id in trajectoires_jury_pe.rcss:
trajectoire = trajectoires_jury_pe.rcss[trajectoire_id]
if trajectoire_id[0] == nom_aggregat:
self.trajectoires[trajectoire_id] = trajectoire
self.trajectoires_taggues: dict[int, Trajectoire] = {}
self.trajectoires_taggues: dict[int, RCS] = {}
"""Ensemble des trajectoires tagguées associées à l'aggrégat"""
for trajectoire_id in self.trajectoires:
self.trajectoires_taggues[trajectoire_id] = trajectoires_taggues[
@ -60,7 +60,7 @@ class AggregatInterclasseTag(TableTag):
# Les trajectoires suivies par les étudiants du jury, en ne gardant que
# celles associées aux diplomés
self.suivi: dict[int, Trajectoire] = {}
self.suivi: dict[int, RCS] = {}
"""Association entre chaque étudiant et la trajectoire tagguée à prendre en
compte pour l'aggrégat"""
for etudid in self.diplomes_ids:

View File

@ -48,17 +48,14 @@ from zipfile import ZipFile
import numpy as np
from app.pe import pe_comp
from app.pe.pe_affichage import NOM_STAT_PROMO, SANS_NOTE, NOM_STAT_GROUPE
from app.pe.pe_tabletags import TableTag
from app.scodoc.gen_tables import SeqGenTable
from app.pe.pe_etudiant import EtudiantsJuryPE
from app.pe.pe_trajectoire import TrajectoiresJuryPE, Trajectoire
from app.pe.pe_etudiant import *
from app.pe.pe_rcs import *
import app.pe.pe_comp as pe_comp
from app.pe.pe_semtag import SemestreTag
from app.pe.pe_interclasstag import AggregatInterclasseTag
from app.pe.pe_trajectoiretag import TrajectoireTag
from app.pe.pe_trajectoiretag import RCSTag
import app.pe.pe_affichage as pe_affichage
import pandas as pd
@ -160,8 +157,8 @@ class JuryPE(object):
pe_affichage.pe_print(
"*** Génère les trajectoires (différentes combinaisons de semestres) des étudiants"
)
self.trajectoires = TrajectoiresJuryPE(self.diplome)
self.trajectoires.cree_trajectoires(self.etudiants)
self.trajectoires = RCSsJuryPE(self.diplome)
self.trajectoires.cree_rcss(self.etudiants)
# Génère les moyennes par tags des trajectoires
pe_affichage.pe_print(
@ -334,10 +331,8 @@ class JuryPE(object):
df_synthese = pd.DataFrame.from_dict(donnees_etudiants, orient="index")
# Ajout des aggrégats
aggregats = pe_comp.TOUS_LES_PARCOURS
for aggregat in aggregats:
descr = pe_comp.PARCOURS[aggregat]["descr"]
for aggregat in TOUS_LES_RCS:
descr = TYPES_RCS[aggregat]["descr"]
# Les trajectoires (tagguées) suivies par les étudiants pour l'aggrégat et le tag
# considéré
@ -345,7 +340,7 @@ class JuryPE(object):
for etudid in etudids:
trajectoire = self.trajectoires.suivi[etudid][aggregat]
if trajectoire:
tid = trajectoire.trajectoire_id
tid = trajectoire.rcs_id
trajectoire_tagguee = self.trajectoires_tagguees[tid]
if (
tag in trajectoire_tagguee.moyennes_tags
@ -476,7 +471,6 @@ class JuryPE(object):
"""Créé un DataFrame pour un étudiant donné par son etudid, retraçant
toutes ses moyennes aux différents tag et aggrégats"""
tags = self.do_tags_list(self.interclassements_taggues)
aggregats = pe_comp.TOUS_LES_PARCOURS
donnees = {}
@ -484,7 +478,7 @@ class JuryPE(object):
# Une ligne pour le tag
donnees[tag] = {("", "", "tag"): tag}
for aggregat in aggregats:
for aggregat in TOUS_LES_RCS:
# Le dictionnaire par défaut des moyennes
donnees[tag] |= get_defaut_dict_synthese_aggregat(
aggregat, self.diplome
@ -494,7 +488,7 @@ class JuryPE(object):
trajectoire = self.trajectoires.suivi[etudid][aggregat]
if trajectoire:
trajectoire_tagguee = self.trajectoires_tagguees[
trajectoire.trajectoire_id
trajectoire.rcs_id
]
if tag in trajectoire_tagguee.moyennes_tags:
# L'interclassement
@ -518,6 +512,27 @@ class JuryPE(object):
df.sort_values(by=[("", "", "tag")], inplace=True)
return df
def get_formsemestres_etudiants(etudiants: EtudiantsJuryPE) -> dict:
"""Ayant connaissance des étudiants dont il faut calculer les moyennes pour
le jury PE (attribut `self.etudiant_ids) et de leur cursus (semestres
parcourus),
renvoie un dictionnaire ``{fid: FormSemestre(fid)}``
contenant l'ensemble des formsemestres de leurs cursus, dont il faudra calculer
la moyenne.
Args:
etudiants: Les étudiants du jury PE
Returns:
Un dictionnaire de la forme `{fid: FormSemestre(fid)}`
"""
semestres = {}
for etudid in etudiants.etudiants_ids:
for cle in etudiants.cursus[etudid]:
if cle.startswith("S"):
semestres = semestres | etudiants.cursus[etudid][cle]
return semestres
def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
"""Créé les semestres taggués, de type 'S1', 'S2', ..., pour un groupe d'étudiants donnés.
@ -536,9 +551,7 @@ def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
"""Création des semestres taggués, de type 'S1', 'S2', ..."""
pe_affichage.pe_print("*** Création des semestres taggués")
formsemestres = etudiants.get_formsemestres(
semestres_recherches=pe_comp.TOUS_LES_SEMESTRES
)
formsemestres = get_formsemestres_etudiants(etudiants)
semestres_tags = {}
for frmsem_id, formsemestre in formsemestres.items():
@ -554,7 +567,7 @@ def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
def compute_trajectoires_tag(
trajectoires: TrajectoiresJuryPE,
trajectoires: RCSsJuryPE,
etudiants: EtudiantsJuryPE,
semestres_taggues: dict[int, SemestreTag],
):
@ -583,11 +596,11 @@ def compute_trajectoires_tag(
"""
trajectoires_tagguees = {}
for trajectoire_id, trajectoire in trajectoires.trajectoires.items():
for trajectoire_id, trajectoire in trajectoires.rcss.items():
nom = trajectoire.get_repr()
pe_affichage.pe_print(f" --> Aggrégat {nom}")
# Trajectoire_tagguee associée
trajectoire_tagguee = TrajectoireTag(trajectoire, semestres_taggues)
trajectoire_tagguee = RCSTag(trajectoire, semestres_taggues)
# Mémorise le résultat
trajectoires_tagguees[trajectoire_id] = trajectoire_tagguee
@ -596,15 +609,15 @@ def compute_trajectoires_tag(
def compute_interclassements(
etudiants: EtudiantsJuryPE,
trajectoires_jury_pe: TrajectoiresJuryPE,
trajectoires_tagguees: dict[tuple, Trajectoire],
trajectoires_jury_pe: RCSsJuryPE,
trajectoires_tagguees: dict[tuple, RCS],
):
"""Interclasse les étudiants, (nom d') aggrégat par aggrégat,
pour fournir un classement sur la promo. Le classement est établi au regard du nombre
d'étudiants ayant participé au même aggrégat.
"""
aggregats_interclasses_taggues = {}
for nom_aggregat in pe_comp.TOUS_LES_SEMESTRES + pe_comp.TOUS_LES_AGGREGATS:
for nom_aggregat in TOUS_LES_RCS:
pe_affichage.pe_print(f" --> Interclassement {nom_aggregat}")
interclass = AggregatInterclasseTag(
nom_aggregat, etudiants, trajectoires_jury_pe, trajectoires_tagguees
@ -613,11 +626,16 @@ def compute_interclassements(
return aggregats_interclasses_taggues
def get_defaut_dict_synthese_aggregat(aggregat: str, diplome: int) -> dict:
def get_defaut_dict_synthese_aggregat(nom_rcs: str, diplome: int) -> dict:
"""Renvoie le dictionnaire de synthèse (à intégrer dans
un tableur excel) pour décrire les résultats d'un aggrégat"""
un tableur excel) pour décrire les résultats d'un aggrégat
Args:
nom_rcs : Le nom du RCS visé
diplôme : l'année du diplôme
"""
# L'affichage de l'aggrégat dans le tableur excel
descr = pe_comp.PARCOURS[aggregat]["descr"]
descr = get_descr_rcs(nom_rcs)
nom_stat_promo = f"{NOM_STAT_PROMO} {diplome}"
donnees = {
@ -650,7 +668,7 @@ def get_defaut_dict_synthese_aggregat(aggregat: str, diplome: int) -> dict:
def get_dict_synthese_aggregat(
aggregat: str,
trajectoire_tagguee: TrajectoireTag,
trajectoire_tagguee: RCSTag,
interclassement_taggue: AggregatInterclasseTag,
etudid: int,
tag: str,
@ -661,7 +679,7 @@ def get_dict_synthese_aggregat(
à l'aggrégat donné et pour un tag donné"""
donnees = {}
# L'affichage de l'aggrégat dans le tableur excel
descr = pe_comp.PARCOURS[aggregat]["descr"]
descr = get_descr_rcs(aggregat)
# La note de l'étudiant (chargement à venir)
note = np.nan

253
app/pe/pe_rcs.py Normal file
View File

@ -0,0 +1,253 @@
import app.pe.pe_comp as pe_comp
import app.pe.pe_affichage as pe_affichage
from app.models import FormSemestre
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)",
},
}
"""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 regroupement et l'affichage qui en sera fait
dans les tableurs de synthèse"""
TOUS_LES_RCS_AVEC_PLUSIEURS_SEM = [cle for cle in TYPES_RCS.keys() if not cle.startswith("S")]
TOUS_LES_RCS = list(TYPES_RCS.keys())
TOUS_LES_SEMESTRES = [cle for cle in TYPES_RCS.keys() if cle.startswith("S")]
class RCS:
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
"""Modélise un ensemble de semestres d'étudiants
associé à un type de regroupement cohérent de semestres
donné (par ex: 'S2', '3S', '2A').
Si le RCS est un semestre de type Si, stocke le (ou les)
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)
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).
Ces semestres peuvent être :
* 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, ...
Args:
nom_rcs: Un nom du RCS (par ex: '5S')
semestre_final: Le semestre final du RCS
"""
self.nom = nom_rcs
"""Nom du RCS"""
self.formsemestre_final = semestre_final
"""FormSemestre terminal du RCS"""
self.rcs_id = (nom_rcs, semestre_final.formsemestre_id)
"""Identifiant du RCS sous forme (nom_rcs, id du semestre_terminal)"""
self.semestres_aggreges = {}
"""Semestres regroupés dans le RCS"""
def add_semestres_a_aggreger(self, semestres: dict[int:FormSemestre]):
"""Ajout de semestres aux semestres à regrouper
Args:
semestres: Dictionnaire ``{fid: FormSemestre(fid)}`` à ajouter
"""
self.semestres_aggreges = self.semestres_aggreges | semestres
def get_repr(self, verbose=True) -> str:
"""Représentation textuelle d'un RCS
basé sur ses semestres aggrégés"""
noms = []
for fid in self.semestres_aggreges:
semestre = self.semestres_aggreges[fid]
noms.append(f"S{semestre.semestre_id}({fid})")
noms = sorted(noms)
repr = f"{self.nom} ({self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"
if verbose and noms:
repr += " - " + "+".join(noms)
return repr
class RCSsJuryPE:
def __init__(self, annee_diplome: int):
"""Classe centralisant toutes les regroupements cohérents de
semestres (RCS) des étudiants à prendre en compte dans un jury PE
Args:
annee_diplome: L'année de diplomation
"""
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}}"""
def cree_rcss(self, etudiants: EtudiantsJuryPE):
"""Créé tous les RCS, 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
"""
for nom_rcs in pe_comp.TOUS_LES_SEMESTRES + TOUS_LES_RCS_AVEC_PLUSIEURS_SEM:
"""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_semestre_de_aggregat = TYPES_RCS[nom_rcs]["aggregat"]
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
for etudid in etudiants.cursus:
if etudid not in self.suivi:
self.suivi[etudid] = {
aggregat: None
for aggregat in pe_comp.TOUS_LES_SEMESTRES
+ TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
}
"""Le formsemestre terminal (dernier en date) associé au
semestre marquant la fin de l'aggrégat
(par ex: son dernier S3 en date)"""
semestres = etudiants.cursus[etudid][nom_semestre_terminal]
if semestres:
formsemestre_final = get_dernier_semestre_en_date(semestres)
"""Ajout ou récupération de la trajectoire"""
trajectoire_id = (nom_rcs, formsemestre_final.formsemestre_id)
if trajectoire_id not in self.rcss:
trajectoire = RCS(nom_rcs, formsemestre_final)
self.rcss[trajectoire_id] = trajectoire
else:
trajectoire = self.rcss[trajectoire_id]
"""La liste des semestres de l'étudiant à prendre en compte
pour cette trajectoire"""
semestres_a_aggreger = get_rcs_etudiant(
etudiants.cursus[etudid], formsemestre_final, nom_rcs
)
"""Ajout des semestres à la trajectoire"""
trajectoire.add_semestres_a_aggreger(semestres_a_aggreger)
"""Mémoire la trajectoire suivie par l'étudiant"""
self.suivi[etudid][nom_rcs] = trajectoire
def get_rcs_etudiant(semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str
):
"""Ensemble des semestres parcourus par un étudiant, connaissant
les semestres de son cursus,
dans le cadre du RCS visé et ayant pour semestre terminal `formsemestre_final`.
Si le RCS est de type "Si", limite les semestres à ceux de numéro i.
Par ex: si formsemestre_terminal est un S3 et nom_agrregat "S3", ne prend en compte que les
semestres 3.
Si le RCS est de type "iA" ou "iS" (incluant plusieurs numéros de semestres), prend en
compte les dit numéros de semestres.
Par ex: si formsemestre_terminal est un S3, ensemble des S1,
S2, S3 suivi pour l'amener au S3 (il peut y avoir plusieurs S1,
ou S2, ou S3 s'il a redoublé).
Les semestres parcourus sont antérieurs (en terme de date de fin)
au formsemestre_terminal.
Args:
cursus: Dictionnaire {fid: FormSemestre(fid)} 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 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_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"]

View File

@ -1,145 +0,0 @@
import app.pe.pe_comp as pe_comp
import app.pe.pe_affichage as pe_affichage
from app.models import FormSemestre
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
class Trajectoire:
def __init__(self, nom_aggregat: str, semestre_final: FormSemestre):
"""Modélise un ensemble de formsemestres d'étudiants
amenant à un semestre terminal, au sens d'un aggrégat (par ex: 'S2', '3S', '2A').
Si l'aggrégat est un semestre de type Si, elle stocke le (ou les)
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)
Pour des aggrégats 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).
Ces semestres peuvent être :
* 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, ...
Args:
nom_aggregat: Un nom d'aggrégat (par ex: '5S')
semestre_final: Le semestre final de l'aggrégat
"""
self.nom = nom_aggregat
"""Nom de l'aggrégat"""
self.formsemestre_final = semestre_final
"""FormSemestre terminal de la trajectoire"""
self.trajectoire_id = (nom_aggregat, semestre_final.formsemestre_id)
"""Identifiant de la trajectoire"""
self.semestres_aggreges = {}
"""Semestres aggrégés"""
def add_semestres_a_aggreger(self, semestres: dict[int:FormSemestre]):
"""Ajout de semestres aux semestres à aggréger
Args:
semestres: Dictionnaire ``{fid: FormSemestre(fid)}`` à ajouter
"""
self.semestres_aggreges = self.semestres_aggreges | semestres
def get_repr(self, verbose=True) -> str:
"""Représentation textuelle d'une trajectoire
basée sur ses semestres aggrégés"""
noms = []
for fid in self.semestres_aggreges:
semestre = self.semestres_aggreges[fid]
noms.append(f"S{semestre.semestre_id}({fid})")
noms = sorted(noms)
repr = f"{self.nom} ({self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"
if verbose and noms:
repr += " - " + "+".join(noms)
return repr
class TrajectoiresJuryPE:
def __init__(self, annee_diplome: int):
"""Classe centralisant toutes les trajectoires des étudiants à prendre
en compte dans un jury PE
Args:
annee_diplome: L'année de diplomation
"""
self.annee_diplome = annee_diplome
"""Toutes les trajectoires possibles"""
self.trajectoires: dict[tuple:Trajectoire] = {}
"""Ensemble des trajectoires recensées : {(aggregat, fid_terminal): Trajectoire}"""
self.suivi: dict[int:str] = {}
"""Dictionnaire associant, pour chaque étudiant et pour chaque aggrégat,
sa trajectoire : {etudid: {nom_aggregat: Trajectoire}}"""
def cree_trajectoires(self, etudiants: EtudiantsJuryPE):
"""Créé toutes les trajectoires, au regard du cursus des étudiants
analysés + les mémorise dans les données de l'étudiant
"""
for nom_aggregat in pe_comp.TOUS_LES_SEMESTRES + pe_comp.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_semestre_de_aggregat = pe_comp.PARCOURS[nom_aggregat]["aggregat"]
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
for etudid in etudiants.cursus:
if etudid not in self.suivi:
self.suivi[etudid] = {
aggregat: None
for aggregat in pe_comp.TOUS_LES_SEMESTRES
+ pe_comp.TOUS_LES_AGGREGATS
}
"""Le formsemestre terminal (dernier en date) associé au
semestre marquant la fin de l'aggrégat
(par ex: son dernier S3 en date)"""
semestres = etudiants.cursus[etudid][nom_semestre_terminal]
if semestres:
formsemestre_final = get_dernier_semestre_en_date(semestres)
"""Ajout ou récupération de la trajectoire"""
trajectoire_id = (nom_aggregat, formsemestre_final.formsemestre_id)
if trajectoire_id not in self.trajectoires:
trajectoire = Trajectoire(nom_aggregat, formsemestre_final)
self.trajectoires[trajectoire_id] = trajectoire
else:
trajectoire = self.trajectoires[trajectoire_id]
"""La liste des semestres de l'étudiant à prendre en compte
pour cette trajectoire"""
semestres_a_aggreger = etudiants.get_trajectoire(
etudid, formsemestre_final, nom_aggregat
)
"""Ajout des semestres à la trajectoire"""
trajectoire.add_semestres_a_aggreger(semestres_a_aggreger)
"""Mémoire la trajectoire suivie par l'étudiant"""
self.suivi[etudid][nom_aggregat] = trajectoire
def get_trajectoires_etudid(trajectoires, etudid):
"""Fonction pour débuggage: renvoie la liste des trajectoires_id des
trajectoires suivies par un étudiant
"""
if etudid not in trajectoires.suivi:
pe_affichage.pe_print(f"{etudid} fait-il bien partie du jury ?")
liste = []
for aggregat in pe_comp.TOUS_LES_PARCOURS:
trajet = trajectoires.suivi[etudid][aggregat]
if trajet:
liste.append(trajet.trajectoire_id)
return liste

View File

@ -36,51 +36,48 @@ Created on Fri Sep 9 09:15:05 2016
@author: barasc
"""
from app.comp import moy_sem
from app.comp.res_sem import load_formsemestre_results
from app.pe.pe_semtag import SemestreTag
import pandas as pd
import numpy as np
from app.pe.pe_trajectoire import Trajectoire
from app.pe.pe_rcs import RCS
from app.pe.pe_tabletags import TableTag, MoyenneTag
class TrajectoireTag(TableTag):
class RCSTag(TableTag):
def __init__(
self, trajectoire: Trajectoire, semestres_taggues: dict[int, SemestreTag]
self, rcs: RCS, semestres_taggues: dict[int, SemestreTag]
):
"""Calcule les moyennes par tag d'une combinaison de semestres
(trajectoires), identifiée par un nom d'aggrégat (par ex: '3S') et
par un semestre terminal, pour extraire les classements par tag pour un
(RCS), pour extraire les classements par tag pour un
groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous
participé au semestre terminal.
Par ex: fusion d'un parcours ['S1', 'S2', 'S3'] donnant un nom_combinaison = '3S'
Args:
trajectoire: Une trajectoire (aggrégat+semestre terminal)
rcs: Un RCS (identifié par un nom et l'id de son semestre terminal)
semestres_taggues: Les données sur les semestres taggués
"""
TableTag.__init__(self)
self.trajectoire_id = trajectoire.trajectoire_id
"""Identifiant de la trajectoire tagguée"""
self.rcs_id = rcs.rcs_id
"""Identifiant du RCS taggué (identique au RCS sur lequel il s'appuie)"""
self.trajectoire = trajectoire
"""Trajectoire associée à la trajectoire tagguée"""
self.rcs = rcs
"""RCS associé au RCS taggué"""
self.nom = self.get_repr()
"""Représentation textuelle de la trajectoire tagguée"""
"""Représentation textuelle du RCS taggué"""
self.formsemestre_terminal = trajectoire.formsemestre_final
self.formsemestre_terminal = rcs.formsemestre_final
"""Le formsemestre terminal"""
# Les résultats du formsemestre terminal
nt = load_formsemestre_results(self.formsemestre_terminal)
self.semestres_aggreges = trajectoire.semestres_aggreges
self.semestres_aggreges = rcs.semestres_aggreges
"""Les semestres aggrégés"""
self.semestres_tags_aggreges = {}
@ -114,13 +111,13 @@ class TrajectoireTag(TableTag):
self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_tag)
def __eq__(self, other):
"""Egalité de 2 trajectoires tagguées sur la base de leur identifiant"""
return self.trajectoire_id == other.trajectoire_id
"""Egalité de 2 RCS taggués sur la base de leur identifiant"""
return self.rcs_id == other.rcs_id
def get_repr(self, verbose=False) -> str:
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
est basée)"""
return self.trajectoire.get_repr(verbose=verbose)
return self.rcs.get_repr(verbose=verbose)
def compute_notes_cube(self):
"""Construit le cube de notes (etudid x tags x semestre_aggregé)