Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
7 changed files with 337 additions and 371 deletions
Showing only changes of commit 9c4e2627ba - Show all commits

View File

@ -47,6 +47,7 @@ from flask import g
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.models import FormSemestre from app.models import FormSemestre
from app.pe.pe_rcs import TYPES_RCS
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc.sco_logos import find_logo 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 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 NBRE_SEMESTRES_DIPLOMANT = 6
AGGREGAT_DIPLOMANT = ( AGGREGAT_DIPLOMANT = (
"6S" # aggrégat correspondant à la totalité des notes pour le diplôme "6S" # aggrégat correspondant à la totalité des notes pour le diplôme
) )
TOUS_LES_SEMESTRES = PARCOURS[AGGREGAT_DIPLOMANT]["aggregat"] TOUS_LES_SEMESTRES = TYPES_RCS[AGGREGAT_DIPLOMANT]["aggregat"]
TOUS_LES_AGGREGATS = [cle for cle in PARCOURS.keys() if not cle.startswith("S")]
TOUS_LES_PARCOURS = list(PARCOURS.keys())
# ---------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------

View File

@ -37,6 +37,7 @@ Created on 17/01/2024
""" """
import pandas as pd import pandas as pd
import app.pe.pe_rcs
from app.models import FormSemestre, Identite, Formation from app.models import FormSemestre, Identite, Formation
from app.pe import pe_comp, pe_affichage from app.pe import pe_comp, pe_affichage
from app.scodoc import codes_cursus 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 # Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris
self.etudiants_diplomes = self.get_etudiants_diplomes() self.etudiants_diplomes = self.get_etudiants_diplomes()
"""Les identités des étudiants diplômés"""
self.diplomes_ids = set(self.etudiants_diplomes.keys()) self.diplomes_ids = set(self.etudiants_diplomes.keys())
"""Les identifiants des étudiants diplômés"""
self.etudiants_ids = set(self.identites.keys()) self.etudiants_ids = set(self.identites.keys())
"""Les identifiants des étudiants (diplômés, redoublants ou ayant abandonnés) à traiter"""
self.formsemestres_jury_ids = self.get_formsemestres()
"""Les formsemestres (des étudiants) dont il faut calculer les moyennes"""
# Les abandons (pour debug) # Les abandons (pour debug)
self.abandons = self.get_etudiants_redoublants_ou_reorientes() 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) self.abandons_ids = set(self.abandons)
"""Les identifiants des étudiants ayant redoublés ou ayant abandonnés"""
# Synthèse # Synthèse
pe_affichage.pe_print( pe_affichage.pe_print(
@ -142,9 +147,6 @@ class EtudiantsJuryPE:
pe_affichage.pe_print( pe_affichage.pe_print(
f" => {nbre_abandons} étudiants non considérés (redoublement, réorientation, abandon" 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( # pe_affichage.pe_print(
# " => quelques étudiants futurs diplômés : " # " => quelques étudiants futurs diplômés : "
# + ", ".join([str(etudid) for etudid in list(self.etudiants_diplomes)[:10]]) # + ", ".join([str(etudid) for etudid in list(self.etudiants_diplomes)[:10]])
@ -270,62 +272,16 @@ class EtudiantsJuryPE:
semestres_significatifs = self.get_semestres_significatifs(etudid) semestres_significatifs = self.get_semestres_significatifs(etudid)
# Tri des semestres par numéro de semestre # Tri des semestres par numéro de semestre
for nom_sem in pe_comp.TOUS_LES_SEMESTRES: for i in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT+1):
i = int(nom_sem[1]) # le n° du semestre
# les semestres de n°i de l'étudiant: # les semestres de n°i de l'étudiant:
semestres_i = { semestres_i = {
fid: sem_sig fid: sem_sig
for fid, sem_sig in semestres_significatifs.items() for fid, sem_sig in semestres_significatifs.items()
if sem_sig.semestre_id == i 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): def get_formsemestres_terminaux_aggregat(self, aggregat: str):
"""Pour un aggrégat donné, ensemble des formsemestres terminaux possibles pour l'aggrégat """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 formsemestres_terminaux[fid] = trajectoire.formsemestre_final
return formsemestres_terminaux 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: def nbre_etapes_max_diplomes(self, etudids: list[int]) -> int:
"""Partant d'un ensemble d'étudiants, """Partant d'un ensemble d'étudiants,

View File

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

View File

@ -48,17 +48,14 @@ from zipfile import ZipFile
import numpy as np 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_affichage import NOM_STAT_PROMO, SANS_NOTE, NOM_STAT_GROUPE
from app.pe.pe_tabletags import TableTag from app.pe.pe_etudiant import *
from app.scodoc.gen_tables import SeqGenTable from app.pe.pe_rcs import *
from app.pe.pe_etudiant import EtudiantsJuryPE
from app.pe.pe_trajectoire import TrajectoiresJuryPE, Trajectoire
import app.pe.pe_comp as pe_comp import app.pe.pe_comp as pe_comp
from app.pe.pe_semtag import SemestreTag from app.pe.pe_semtag import SemestreTag
from app.pe.pe_interclasstag import AggregatInterclasseTag 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 app.pe.pe_affichage as pe_affichage
import pandas as pd import pandas as pd
@ -160,8 +157,8 @@ class JuryPE(object):
pe_affichage.pe_print( pe_affichage.pe_print(
"*** Génère les trajectoires (différentes combinaisons de semestres) des étudiants" "*** Génère les trajectoires (différentes combinaisons de semestres) des étudiants"
) )
self.trajectoires = TrajectoiresJuryPE(self.diplome) self.trajectoires = RCSsJuryPE(self.diplome)
self.trajectoires.cree_trajectoires(self.etudiants) self.trajectoires.cree_rcss(self.etudiants)
# Génère les moyennes par tags des trajectoires # Génère les moyennes par tags des trajectoires
pe_affichage.pe_print( pe_affichage.pe_print(
@ -334,10 +331,8 @@ class JuryPE(object):
df_synthese = pd.DataFrame.from_dict(donnees_etudiants, orient="index") df_synthese = pd.DataFrame.from_dict(donnees_etudiants, orient="index")
# Ajout des aggrégats # Ajout des aggrégats
aggregats = pe_comp.TOUS_LES_PARCOURS for aggregat in TOUS_LES_RCS:
descr = TYPES_RCS[aggregat]["descr"]
for aggregat in aggregats:
descr = pe_comp.PARCOURS[aggregat]["descr"]
# Les trajectoires (tagguées) suivies par les étudiants pour l'aggrégat et le tag # Les trajectoires (tagguées) suivies par les étudiants pour l'aggrégat et le tag
# considéré # considéré
@ -345,7 +340,7 @@ class JuryPE(object):
for etudid in etudids: for etudid in etudids:
trajectoire = self.trajectoires.suivi[etudid][aggregat] trajectoire = self.trajectoires.suivi[etudid][aggregat]
if trajectoire: if trajectoire:
tid = trajectoire.trajectoire_id tid = trajectoire.rcs_id
trajectoire_tagguee = self.trajectoires_tagguees[tid] trajectoire_tagguee = self.trajectoires_tagguees[tid]
if ( if (
tag in trajectoire_tagguee.moyennes_tags 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 """Créé un DataFrame pour un étudiant donné par son etudid, retraçant
toutes ses moyennes aux différents tag et aggrégats""" toutes ses moyennes aux différents tag et aggrégats"""
tags = self.do_tags_list(self.interclassements_taggues) tags = self.do_tags_list(self.interclassements_taggues)
aggregats = pe_comp.TOUS_LES_PARCOURS
donnees = {} donnees = {}
@ -484,7 +478,7 @@ class JuryPE(object):
# Une ligne pour le tag # Une ligne pour le tag
donnees[tag] = {("", "", "tag"): tag} donnees[tag] = {("", "", "tag"): tag}
for aggregat in aggregats: for aggregat in TOUS_LES_RCS:
# Le dictionnaire par défaut des moyennes # Le dictionnaire par défaut des moyennes
donnees[tag] |= get_defaut_dict_synthese_aggregat( donnees[tag] |= get_defaut_dict_synthese_aggregat(
aggregat, self.diplome aggregat, self.diplome
@ -494,7 +488,7 @@ class JuryPE(object):
trajectoire = self.trajectoires.suivi[etudid][aggregat] trajectoire = self.trajectoires.suivi[etudid][aggregat]
if trajectoire: if trajectoire:
trajectoire_tagguee = self.trajectoires_tagguees[ trajectoire_tagguee = self.trajectoires_tagguees[
trajectoire.trajectoire_id trajectoire.rcs_id
] ]
if tag in trajectoire_tagguee.moyennes_tags: if tag in trajectoire_tagguee.moyennes_tags:
# L'interclassement # L'interclassement
@ -518,6 +512,27 @@ class JuryPE(object):
df.sort_values(by=[("", "", "tag")], inplace=True) df.sort_values(by=[("", "", "tag")], inplace=True)
return df 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: def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
"""Créé les semestres taggués, de type 'S1', 'S2', ..., pour un groupe d'étudiants donnés. """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', ...""" """Création des semestres taggués, de type 'S1', 'S2', ..."""
pe_affichage.pe_print("*** Création des semestres taggués") pe_affichage.pe_print("*** Création des semestres taggués")
formsemestres = etudiants.get_formsemestres( formsemestres = get_formsemestres_etudiants(etudiants)
semestres_recherches=pe_comp.TOUS_LES_SEMESTRES
)
semestres_tags = {} semestres_tags = {}
for frmsem_id, formsemestre in formsemestres.items(): for frmsem_id, formsemestre in formsemestres.items():
@ -554,7 +567,7 @@ def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
def compute_trajectoires_tag( def compute_trajectoires_tag(
trajectoires: TrajectoiresJuryPE, trajectoires: RCSsJuryPE,
etudiants: EtudiantsJuryPE, etudiants: EtudiantsJuryPE,
semestres_taggues: dict[int, SemestreTag], semestres_taggues: dict[int, SemestreTag],
): ):
@ -583,11 +596,11 @@ def compute_trajectoires_tag(
""" """
trajectoires_tagguees = {} trajectoires_tagguees = {}
for trajectoire_id, trajectoire in trajectoires.trajectoires.items(): for trajectoire_id, trajectoire in trajectoires.rcss.items():
nom = trajectoire.get_repr() nom = trajectoire.get_repr()
pe_affichage.pe_print(f" --> Aggrégat {nom}") pe_affichage.pe_print(f" --> Aggrégat {nom}")
# Trajectoire_tagguee associée # Trajectoire_tagguee associée
trajectoire_tagguee = TrajectoireTag(trajectoire, semestres_taggues) trajectoire_tagguee = RCSTag(trajectoire, semestres_taggues)
# Mémorise le résultat # Mémorise le résultat
trajectoires_tagguees[trajectoire_id] = trajectoire_tagguee trajectoires_tagguees[trajectoire_id] = trajectoire_tagguee
@ -596,15 +609,15 @@ def compute_trajectoires_tag(
def compute_interclassements( def compute_interclassements(
etudiants: EtudiantsJuryPE, etudiants: EtudiantsJuryPE,
trajectoires_jury_pe: TrajectoiresJuryPE, trajectoires_jury_pe: RCSsJuryPE,
trajectoires_tagguees: dict[tuple, Trajectoire], trajectoires_tagguees: dict[tuple, RCS],
): ):
"""Interclasse les étudiants, (nom d') aggrégat par aggrégat, """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 pour fournir un classement sur la promo. Le classement est établi au regard du nombre
d'étudiants ayant participé au même aggrégat. d'étudiants ayant participé au même aggrégat.
""" """
aggregats_interclasses_taggues = {} 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}") pe_affichage.pe_print(f" --> Interclassement {nom_aggregat}")
interclass = AggregatInterclasseTag( interclass = AggregatInterclasseTag(
nom_aggregat, etudiants, trajectoires_jury_pe, trajectoires_tagguees nom_aggregat, etudiants, trajectoires_jury_pe, trajectoires_tagguees
@ -613,11 +626,16 @@ def compute_interclassements(
return aggregats_interclasses_taggues 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 """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 # 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}" nom_stat_promo = f"{NOM_STAT_PROMO} {diplome}"
donnees = { donnees = {
@ -650,7 +668,7 @@ def get_defaut_dict_synthese_aggregat(aggregat: str, diplome: int) -> dict:
def get_dict_synthese_aggregat( def get_dict_synthese_aggregat(
aggregat: str, aggregat: str,
trajectoire_tagguee: TrajectoireTag, trajectoire_tagguee: RCSTag,
interclassement_taggue: AggregatInterclasseTag, interclassement_taggue: AggregatInterclasseTag,
etudid: int, etudid: int,
tag: str, tag: str,
@ -661,7 +679,7 @@ def get_dict_synthese_aggregat(
à l'aggrégat donné et pour un tag donné""" à l'aggrégat donné et pour un tag donné"""
donnees = {} donnees = {}
# L'affichage de l'aggrégat dans le tableur excel # 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) # La note de l'étudiant (chargement à venir)
note = np.nan 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 @author: barasc
""" """
from app.comp import moy_sem
from app.comp.res_sem import load_formsemestre_results from app.comp.res_sem import load_formsemestre_results
from app.pe.pe_semtag import SemestreTag from app.pe.pe_semtag import SemestreTag
import pandas as pd import pandas as pd
import numpy as np 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 from app.pe.pe_tabletags import TableTag, MoyenneTag
class TrajectoireTag(TableTag): class RCSTag(TableTag):
def __init__( 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 """Calcule les moyennes par tag d'une combinaison de semestres
(trajectoires), identifiée par un nom d'aggrégat (par ex: '3S') et (RCS), pour extraire les classements par tag pour un
par un semestre terminal, pour extraire les classements par tag pour un
groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous
participé au semestre terminal. participé au semestre terminal.
Par ex: fusion d'un parcours ['S1', 'S2', 'S3'] donnant un nom_combinaison = '3S'
Args: 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 semestres_taggues: Les données sur les semestres taggués
""" """
TableTag.__init__(self) TableTag.__init__(self)
self.trajectoire_id = trajectoire.trajectoire_id self.rcs_id = rcs.rcs_id
"""Identifiant de la trajectoire tagguée""" """Identifiant du RCS taggué (identique au RCS sur lequel il s'appuie)"""
self.trajectoire = trajectoire self.rcs = rcs
"""Trajectoire associée à la trajectoire tagguée""" """RCS associé au RCS taggué"""
self.nom = self.get_repr() 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""" """Le formsemestre terminal"""
# Les résultats du formsemestre terminal # Les résultats du formsemestre terminal
nt = load_formsemestre_results(self.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""" """Les semestres aggrégés"""
self.semestres_tags_aggreges = {} self.semestres_tags_aggreges = {}
@ -114,13 +111,13 @@ class TrajectoireTag(TableTag):
self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_tag) self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_tag)
def __eq__(self, other): def __eq__(self, other):
"""Egalité de 2 trajectoires tagguées sur la base de leur identifiant""" """Egalité de 2 RCS taggués sur la base de leur identifiant"""
return self.trajectoire_id == other.trajectoire_id return self.rcs_id == other.rcs_id
def get_repr(self, verbose=False) -> str: def get_repr(self, verbose=False) -> str:
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle """Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
est basée)""" est basée)"""
return self.trajectoire.get_repr(verbose=verbose) return self.rcs.get_repr(verbose=verbose)
def compute_notes_cube(self): def compute_notes_cube(self):
"""Construit le cube de notes (etudid x tags x semestre_aggregé) """Construit le cube de notes (etudid x tags x semestre_aggregé)