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
5 changed files with 60 additions and 68 deletions
Showing only changes of commit d991eb007c - Show all commits

View File

@ -170,6 +170,7 @@ TOUS_LES_SEMESTRES = PARCOURS[AGGREGAT_DIPLOMANT]["aggregat"]
TOUS_LES_AGGREGATS = [cle for cle in PARCOURS.keys() if not cle.startswith("S")] TOUS_LES_AGGREGATS = [cle for cle in PARCOURS.keys() if not cle.startswith("S")]
TOUS_LES_PARCOURS = list(PARCOURS.keys()) TOUS_LES_PARCOURS = list(PARCOURS.keys())
# ---------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------
def calcul_age(born: datetime.date) -> int: def calcul_age(born: datetime.date) -> int:
"""Calcule l'age connaissant la date de naissance ``born``. (L'age est calculé """Calcule l'age connaissant la date de naissance ``born``. (L'age est calculé
@ -185,11 +186,7 @@ def calcul_age(born: datetime.date) -> int:
return None return None
today = datetime.date.today() today = datetime.date.today()
return ( return today.year - born.year - ((today.month, today.day) < (born.month, born.day))
today.year
- born.year
- ((today.month, today.day) < (born.month, born.day))
)
def remove_accents(input_unicode_str): def remove_accents(input_unicode_str):
@ -286,7 +283,9 @@ def add_pe_stuff_to_zip(zipfile, ziproot):
# ---------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------
def get_annee_diplome_semestre(sem_base, nbre_sem_formation=6) -> int: def get_annee_diplome_semestre(
sem_base: FormSemestre | dict, nbre_sem_formation: int = 6
) -> int:
"""Pour un semestre ``sem_base`` donné (supposé être un semestre d'une formation BUT à 6 semestres) """Pour un semestre ``sem_base`` donné (supposé être un semestre d'une formation BUT à 6 semestres)
et connaissant le numéro du semestre, ses dates de début et de fin du semestre, prédit l'année à laquelle et connaissant le numéro du semestre, ses dates de début et de fin du semestre, prédit l'année à laquelle
sera remis le diplôme BUT des étudiants qui y sont scolarisés sera remis le diplôme BUT des étudiants qui y sont scolarisés
@ -339,7 +338,9 @@ def get_annee_diplome_semestre(sem_base, nbre_sem_formation=6) -> int:
return annee_fin + nbreAnRestant + increment return annee_fin + nbreAnRestant + increment
def get_cosemestres_diplomants(annee_diplome: int, formation_id: int) -> list: def get_cosemestres_diplomants(
annee_diplome: int, formation_id: int
) -> dict[int, FormSemestre]:
"""Ensemble des cosemestres donnant lieu à diplomation à l'``annee_diplome`` """Ensemble des cosemestres donnant lieu à diplomation à l'``annee_diplome``
et s'intégrant à la formation donnée par son ``formation_id``. et s'intégrant à la formation donnée par son ``formation_id``.
@ -381,4 +382,3 @@ def get_cosemestres_diplomants(annee_diplome: int, formation_id: int) -> list:
cosemestres[fid] = cosem cosemestres[fid] = cosem
return cosemestres return cosemestres

View File

@ -66,11 +66,15 @@ class EtudiantsJuryPE:
"Les etudids des étudiants dont il faut calculer les moyennes/classements (même si d'éventuels abandons)" "Les etudids des étudiants dont il faut calculer les moyennes/classements (même si d'éventuels abandons)"
self.etudiants_ids = {} self.etudiants_ids = {}
"""Les étudiants inscrits dans les co-semestres (ceux du jury mais aussi d'autres ayant été réorientés ou ayant abandonnés)"""
self.cosemestres: dict[int, FormSemestre] = None
"Les cosemestres donnant lieu à même année de diplome"
def find_etudiants(self, formation_id: int): def find_etudiants(self, formation_id: int):
"""Liste des étudiants à prendre en compte dans le jury PE, en les recherchant """Liste des étudiants à prendre en compte dans le jury PE, en les recherchant
de manière automatique par rapport à leur année de diplomation ``annee_diplome`` de manière automatique par rapport à leur année de diplomation ``annee_diplome``
dans la formation ``formation_id``. dans la formation ``formation_id``. XXX TODO voir si on garde formation_id qui n'est pas utilisé ici
Les données obtenues sont stockées dans les attributs de EtudiantsJuryPE. Les données obtenues sont stockées dans les attributs de EtudiantsJuryPE.
@ -79,14 +83,11 @@ class EtudiantsJuryPE:
*Remarque* : ex: JuryPE.get_etudiants_in_jury() *Remarque* : ex: JuryPE.get_etudiants_in_jury()
""" """
"Les cosemestres donnant lieu à même année de diplome"
cosemestres = pe_comp.get_cosemestres_diplomants(self.annee_diplome, None) cosemestres = pe_comp.get_cosemestres_diplomants(self.annee_diplome, None)
self.cosemestres = cosemestres self.cosemestres = cosemestres
pe_comp.pe_print(
"1) Recherche des coSemestres -> %d trouvés" % len(cosemestres)
)
"""Les étudiants inscrits dans les co-semestres (ceux du jury mais aussi d'autres ayant été réorientés ou ayant abandonnés)""" pe_comp.pe_print(f"1) Recherche des coSemestres -> {len(cosemestres)} trouvés")
pe_comp.pe_print("2) Liste des étudiants dans les différents co-semestres") pe_comp.pe_print("2) Liste des étudiants dans les différents co-semestres")
self.etudiants_ids = get_etudiants_dans_semestres(cosemestres) self.etudiants_ids = get_etudiants_dans_semestres(cosemestres)
pe_comp.pe_print( pe_comp.pe_print(
@ -142,9 +143,13 @@ class EtudiantsJuryPE:
+ ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)]) + ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)])
) )
# Les abandons : # Les abandons :
self.abandons = sorted([self.cursus[etudid]['nom'] self.abandons = sorted(
for etudid in self.cursus if etudid not in self.diplomes_ids]) [
self.cursus[etudid]["nom"]
for etudid in self.cursus
if etudid not in self.diplomes_ids
]
)
def get_etudiants_diplomes(self) -> dict[int, Identite]: def get_etudiants_diplomes(self) -> dict[int, Identite]:
"""Identités des étudiants (sous forme d'un dictionnaire `{etudid: Identite(etudid)}` """Identités des étudiants (sous forme d'un dictionnaire `{etudid: Identite(etudid)}`
@ -201,7 +206,9 @@ class EtudiantsJuryPE:
"entree": formsemestres[-1].date_debut.year, # La date d'entrée à l'IUT "entree": formsemestres[-1].date_debut.year, # La date d'entrée à l'IUT
"diplome": annee_diplome(identite), # Le date prévisionnelle de son diplôme "diplome": annee_diplome(identite), # Le date prévisionnelle de son diplôme
"formsemestres": semestres_etudiant, # les semestres de l'étudiant "formsemestres": semestres_etudiant, # les semestres de l'étudiant
"nb_semestres": len(semestres_etudiant), # le nombre de semestres de l'étudiant "nb_semestres": len(
semestres_etudiant
), # le nombre de semestres de l'étudiant
"abandon": False, # va être traité en dessous "abandon": False, # va être traité en dessous
} }
@ -250,7 +257,6 @@ class EtudiantsJuryPE:
} # les semestres de n°i de l'étudiant } # les semestres de n°i de l'étudiant
self.cursus[etudid][nom_sem] = semestres_i self.cursus[etudid][nom_sem] = semestres_i
def get_trajectoire(self, etudid: int, formsemestre_final: FormSemestre): def get_trajectoire(self, etudid: int, formsemestre_final: FormSemestre):
"""Ensemble des semestres parcourus par """Ensemble des semestres parcourus par
un étudiant pour l'amener à un semestre terminal. un étudiant pour l'amener à un semestre terminal.
@ -417,10 +423,7 @@ def annee_diplome(identite: Identite) -> int:
formsemestres = identite.get_formsemestres() formsemestres = identite.get_formsemestres()
if formsemestres: if formsemestres:
return max( return max(
[ [pe_comp.get_annee_diplome_semestre(sem_base) for sem_base in formsemestres]
pe_comp.get_annee_diplome_semestre(sem_base)
for sem_base in formsemestres
]
) )
else: else:
return None return None
@ -502,8 +505,6 @@ def arret_de_formation(identite: Identite, cosemestres: list[FormSemestre]) -> b
return False return False
def get_dernier_semestre_en_date(semestres: dict[int, FormSemestre]) -> FormSemestre: def get_dernier_semestre_en_date(semestres: dict[int, FormSemestre]) -> FormSemestre:
"""Renvoie le dernier semestre en **date de fin** d'un dictionnaire """Renvoie le dernier semestre en **date de fin** d'un dictionnaire
de semestres (potentiellement non trié) de la forme ``{fid: FormSemestre(fid)}``. de semestres (potentiellement non trié) de la forme ``{fid: FormSemestre(fid)}``.
@ -523,5 +524,3 @@ def get_dernier_semestre_en_date(semestres: dict[int, FormSemestre]) -> FormSeme
return dernier_semestre return dernier_semestre
else: else:
return None return None

View File

@ -1,4 +1,3 @@
from app.pe.pe_tabletags import TableTag from app.pe.pe_tabletags import TableTag
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_trajectoire import Trajectoire, TrajectoiresJuryPE
@ -27,16 +26,14 @@ class AggregatInterclasseTag(TableTag):
trajectoires_jury_pe: TrajectoiresJuryPE, trajectoires_jury_pe: TrajectoiresJuryPE,
trajectoires_taggues: dict[tuple, TrajectoireTag], trajectoires_taggues: dict[tuple, TrajectoireTag],
): ):
"""""" # Table nommée au nom de l'aggrégat (par ex: 3S)
"""Table nommée au nom de l'aggrégat (par ex: 3S"""
TableTag.__init__(self, nom_aggregat) TableTag.__init__(self, nom_aggregat)
"""Les étudiants diplômés et leurs trajectoires (cf. trajectoires.suivis)""" """Les étudiants diplômés et leurs trajectoires (cf. trajectoires.suivis)""" # TODO
self.diplomes_ids = etudiants.etudiants_diplomes self.diplomes_ids = etudiants.etudiants_diplomes
self.etudiants_diplomes = {etudid for etudid in self.diplomes_ids} self.etudiants_diplomes = {etudid for etudid in self.diplomes_ids}
"""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, Trajectoire] = {}
for trajectoire_id in trajectoires_jury_pe.trajectoires: for trajectoire_id in trajectoires_jury_pe.trajectoires:
trajectoire = trajectoires_jury_pe.trajectoires[trajectoire_id] trajectoire = trajectoires_jury_pe.trajectoires[trajectoire_id]
@ -49,8 +46,8 @@ class AggregatInterclasseTag(TableTag):
trajectoire_id trajectoire_id
] ]
"""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, Trajectoire] = {}
for etudid in self.diplomes_ids: for etudid in self.diplomes_ids:
self.suivi[etudid] = trajectoires_jury_pe.suivi[etudid][nom_aggregat] self.suivi[etudid] = trajectoires_jury_pe.suivi[etudid][nom_aggregat]
@ -58,10 +55,10 @@ class AggregatInterclasseTag(TableTag):
"""Les tags""" """Les tags"""
self.tags_sorted = self.do_taglist() self.tags_sorted = self.do_taglist()
"""Construit la matrice de notes""" # Construit la matrice de notes
self.notes = self.compute_notes_matrice() self.notes = self.compute_notes_matrice()
"""Synthétise les moyennes/classements par tag""" # Synthétise les moyennes/classements par tag
self.moyennes_tags = {} self.moyennes_tags = {}
for tag in self.tags_sorted: for tag in self.tags_sorted:
moy_gen_tag = self.notes[tag] moy_gen_tag = self.notes[tag]
@ -79,7 +76,6 @@ class AggregatInterclasseTag(TableTag):
"""Une représentation textuelle""" """Une représentation textuelle"""
return f"Aggrégat {self.nom}" return f"Aggrégat {self.nom}"
def do_taglist(self): def do_taglist(self):
"""Synthétise les tags à partir des trajectoires_tagguées """Synthétise les tags à partir des trajectoires_tagguées
@ -87,39 +83,35 @@ class AggregatInterclasseTag(TableTag):
Une liste de tags triés par ordre alphabétique Une liste de tags triés par ordre alphabétique
""" """
tags = [] tags = []
for trajectoire_id in self.trajectoires_taggues: for trajectoire in self.trajectoires_taggues.values():
trajectoire = self.trajectoires_taggues[trajectoire_id]
tags.extend(trajectoire.tags_sorted) tags.extend(trajectoire.tags_sorted)
return sorted(set(tags)) return sorted(set(tags))
def compute_notes_matrice(self): def compute_notes_matrice(self):
"""Construit la matrice de notes (etudid x tags) """Construit la matrice de notes (etudid x tags)
retraçant les moyennes obtenues par les étudiants dans les semestres associés à retraçant les moyennes obtenues par les étudiants dans les semestres associés à
l'aggrégat (une trajectoire ayant pour numéro de semestre final, celui de l'aggrégat). l'aggrégat (une trajectoire ayant pour numéro de semestre final, celui de l'aggrégat).
""" """
nb_tags = len(self.tags_sorted) # nb_tags = len(self.tags_sorted) unused ?
nb_etudiants = len(self.diplomes_ids) # nb_etudiants = len(self.diplomes_ids)
"""Index de la matrice (etudids -> dim 0, tags -> dim 1)""" # Index de la matrice (etudids -> dim 0, tags -> dim 1)
etudids = list(self.diplomes_ids) etudids = list(self.diplomes_ids)
tags = self.tags_sorted tags = self.tags_sorted
"""Partant d'un dataframe vierge""" # Partant d'un dataframe vierge
df = pd.DataFrame(np.nan, index=etudids, columns=tags) df = pd.DataFrame(np.nan, index=etudids, columns=tags)
for trajectoire_id in self.trajectoires_taggues: for notes in self.trajectoires_taggues.values():
"""Charge les moyennes par tag de la trajectoire tagguée""" # Charge les moyennes par tag de la trajectoire tagguée
notes = self.trajectoires_taggues[trajectoire_id].notes
"""Etudiants/Tags communs entre la trajectoire_tagguée et les données interclassées""" # Etudiants/Tags communs entre la trajectoire_tagguée et les données interclassées
etudids_communs = df.index.intersection(notes.index) etudids_communs = df.index.intersection(notes.index)
tags_communs = df.columns.intersection(notes.columns) tags_communs = df.columns.intersection(notes.columns)
"""Injecte les notes par tag""" # Injecte les notes par tag
df.loc[etudids_communs, tags_communs] = notes.loc[ df.loc[etudids_communs, tags_communs] = notes.loc[
etudids_communs, tags_communs etudids_communs, tags_communs
] ]
return df return df

View File

@ -107,13 +107,14 @@ class JuryPE(object):
self.formation_id = formation_id self.formation_id = formation_id
"Un zip où ranger les fichiers générés" "Un zip où ranger les fichiers générés"
self.nom_export_zip = "Jury_PE_%s" % self.diplome self.nom_export_zip = f"Jury_PE_{self.diplome}"
self.zipdata = io.BytesIO() self.zipdata = io.BytesIO()
self.zipfile = ZipFile(self.zipdata, "w") self.zipfile = ZipFile(self.zipdata, "w")
"""Chargement des étudiants à prendre en compte dans le jury""" """Chargement des étudiants à prendre en compte dans le jury"""
pe_comp.pe_print( pe_comp.pe_print(
f"*** Recherche et chargement des étudiants diplômés en {self.diplome} pour la formation {self.formation_id}" f"""*** Recherche et chargement des étudiants diplômés en {
self.diplome} pour la formation {self.formation_id}"""
) )
self.etudiants = EtudiantsJuryPE(self.diplome) # Les infos sur les étudiants self.etudiants = EtudiantsJuryPE(self.diplome) # Les infos sur les étudiants
self.etudiants.find_etudiants(self.formation_id) self.etudiants.find_etudiants(self.formation_id)
@ -197,7 +198,7 @@ class JuryPE(object):
open(filename, "rb").read(), open(filename, "rb").read(),
) )
"""Fin !!!! Tada :)""" # Fin !!!! Tada :)
def add_file_to_zip(self, filename: str, data, path=""): def add_file_to_zip(self, filename: str, data, path=""):
"""Add a file to our zip """Add a file to our zip
@ -256,7 +257,7 @@ class JuryPE(object):
etudids = list(self.diplomes_ids) etudids = list(self.diplomes_ids)
"""Récupération des données des étudiants""" # Récupération des données des étudiants
administratif = {} administratif = {}
nbre_semestres_max = self.etudiants.nbre_etapes_max_diplomes() nbre_semestres_max = self.etudiants.nbre_etapes_max_diplomes()
@ -279,10 +280,10 @@ class JuryPE(object):
etapes = pe_affichage.etapes_du_cursus(formsemestres, nbre_semestres_max) etapes = pe_affichage.etapes_du_cursus(formsemestres, nbre_semestres_max)
administratif[etudid] |= etapes administratif[etudid] |= etapes
"""Construction du dataframe""" # Construction du dataframe
df = pd.DataFrame.from_dict(administratif, orient="index") df = pd.DataFrame.from_dict(administratif, orient="index")
"""Tri par nom/prénom""" # Tri par nom/prénom
df.sort_values(by=["Nom", "Prenom"], inplace=True) df.sort_values(by=["Nom", "Prenom"], inplace=True)
return df return df
@ -348,10 +349,10 @@ class JuryPE(object):
} }
# Fin de l'aggrégat # Fin de l'aggrégat
"""Construction du dataFrame""" # Construction du dataFrame
df = pd.DataFrame.from_dict(donnees, orient="index") df = pd.DataFrame.from_dict(donnees, orient="index")
"""Tri par nom/prénom""" # Tri par nom/prénom
df.sort_values(by=["Nom", "Prenom"], inplace=True) df.sort_values(by=["Nom", "Prenom"], inplace=True)
return df return df
@ -400,10 +401,10 @@ def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
pe_comp.pe_print(f" --> Semestre taggué {nom} sur la base de {formsemestre}") pe_comp.pe_print(f" --> Semestre taggué {nom} sur la base de {formsemestre}")
"""Créé le semestre_tag et exécute les calculs de moyennes""" # Crée le semestre_tag et exécute les calculs de moyennes
formsemestretag = SemestreTag(nom, frmsem_id) formsemestretag = SemestreTag(nom, frmsem_id)
"""Stocke le semestre taggué""" # Stocke le semestre taggué
semestres_tags[frmsem_id] = formsemestretag semestres_tags[frmsem_id] = formsemestretag
return semestres_tags return semestres_tags