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
4 changed files with 53 additions and 52 deletions
Showing only changes of commit cd8d73b41f - Show all commits

View File

@ -142,7 +142,9 @@ 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 :
# sorted([etudiants.cursus[etudid]['nom'] for etudid in etudiants.cursus if etudid not in etudiants.diplomes_ids]) self.abandons = sorted([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)}`

View File

@ -71,17 +71,17 @@ import numpy as np
# ---------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------
class JuryPE(object): class JuryPE(object):
"""Classe mémorisant toutes les informations nécessaires pour établir un jury de PE. """Classe mémorisant toutes les informations nécessaires pour établir un jury de PE.
Modèle basé sur NotesTable. Modèle basé sur NotesTable.
Attributs : Attributs :
* diplome : l'année d'obtention du diplome BUT et du jury de PE (généralement février XXXX) * diplome : l'année d'obtention du diplome BUT et du jury de PE (généralement février XXXX)
* juryEtudDict : dictionnaire récapitulant les étudiants participant au jury PE (données administratives + * juryEtudDict : dictionnaire récapitulant les étudiants participant au jury PE (données administratives +
celles des semestres valides à prendre en compte permettant le calcul des moyennes ... celles des semestres valides à prendre en compte permettant le calcul des moyennes ...
``{'etudid : { 'nom', 'prenom', 'civilite', 'diplome', '', }}`` ``{'etudid : { 'nom', 'prenom', 'civilite', 'diplome', '', }}``
a
Rq: il contient à la fois les étudiants qui vont être diplomés à la date prévue Rq: il contient à la fois les étudiants qui vont être diplomés à la date prévue
et ceux qui sont éliminés (abandon, redoublement, ...) pour affichage alternatif et ceux qui sont éliminés (abandon, redoublement, ...) pour affichage alternatif
""" """
# Variables de classe décrivant les aggrégats, leur ordre d'apparition temporelle et # Variables de classe décrivant les aggrégats, leur ordre d'apparition temporelle et
@ -100,8 +100,6 @@ class JuryPE(object):
meme_programme: si True, impose un même programme pour tous les étudiants participant au jury, meme_programme: si True, impose un même programme pour tous les étudiants participant au jury,
si False, permet des programmes differents si False, permet des programmes differents
""" """
self.promoTagDict = {}
"L'année du diplome" "L'année du diplome"
self.diplome = diplome self.diplome = diplome
@ -113,8 +111,6 @@ class JuryPE(object):
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}"
@ -187,14 +183,19 @@ class JuryPE(object):
# Export des données => mode 1 seule feuille -> supprimé # Export des données => mode 1 seule feuille -> supprimé
pe_comp.pe_print("*** Export du jury de synthese") pe_comp.pe_print("*** Export du jury de synthese")
filename = "synthese_jury_" + str(self.diplome) + '.xls' filename = "synthese_jury_" + str(self.diplome) + ".xlsx"
with pd.ExcelWriter(filename, engine="openpyxl") as writer: with pd.ExcelWriter(filename, engine="openpyxl") as writer:
for onglet in self.synthese: for onglet in self.synthese:
df = self.synthese[onglet] df = self.synthese[onglet]
df.to_excel(writer, onglet, index=True, header=True) # écriture dans l'onglet df.to_excel(
writer, onglet, index=True, header=True
) # écriture dans l'onglet
# worksheet = writer.sheets[onglet] # l'on # worksheet = writer.sheets[onglet] # l'on
self.zipfile.write(filename) self.add_file_to_zip(
filename,
open(filename, "rb").read(),
)
"""Fin !!!! Tada :)""" """Fin !!!! Tada :)"""
@ -208,7 +209,7 @@ class JuryPE(object):
data: Les données du fichier data: Les données du fichier
path: Un dossier dans l'arborescence du zip path: Un dossier dans l'arborescence du zip
""" """
path_in_zip = os.path.join(self.nom_export_zip, path, filename) path_in_zip = os.path.join(path, filename) # self.nom_export_zip,
self.zipfile.writestr(path_in_zip, data) self.zipfile.writestr(path_in_zip, data)
def get_zipped_data(self): def get_zipped_data(self):
@ -231,7 +232,6 @@ class JuryPE(object):
tags = sorted(set(tags)) tags = sorted(set(tags))
return tags return tags
# **************************************************************************************************************** # # **************************************************************************************************************** #
# Méthodes pour la synthèse du juryPE # Méthodes pour la synthèse du juryPE
# ***************************************************************************************************************** # *****************************************************************************************************************
@ -251,7 +251,6 @@ class JuryPE(object):
synthese[tag] = self.df_tag(tag) synthese[tag] = self.df_tag(tag)
return synthese return synthese
def df_administratif(self): def df_administratif(self):
"""Synthétise toutes les données administratives des étudiants""" """Synthétise toutes les données administratives des étudiants"""
@ -270,10 +269,10 @@ class JuryPE(object):
"Nom": etudiant.nom, "Nom": etudiant.nom,
"Prenom": etudiant.prenom, "Prenom": etudiant.prenom,
"Civilite": etudiant.civilite_str, "Civilite": etudiant.civilite_str,
"Age": pe_comp.calcul_age(etudiant.date_naissance), "Age": pe_comp.calcul_age(etudiant.date_naissance),
"Date d'entree": cursus["entree"], "Date d'entree": cursus["entree"],
"Date de diplome": cursus["diplome"], "Date de diplome": cursus["diplome"],
"Nbre de semestres": len(formsemestres) "Nbre de semestres": len(formsemestres),
} }
# Ajout des noms de semestres parcourus # Ajout des noms de semestres parcourus
@ -281,9 +280,11 @@ class JuryPE(object):
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")
return df
"""Tri par nom/prénom"""
df.sort_values(by=["Nom", "Prenom"], inplace = True)
return df
def df_tag(self, tag): def df_tag(self, tag):
"""Génère le DataFrame synthétisant les moyennes/classements (groupe, """Génère le DataFrame synthétisant les moyennes/classements (groupe,
@ -299,7 +300,6 @@ class JuryPE(object):
etudids = list(self.diplomes_ids) etudids = list(self.diplomes_ids)
aggregats = pe_comp.TOUS_LES_PARCOURS aggregats = pe_comp.TOUS_LES_PARCOURS
donnees = {} donnees = {}
for etudid in etudids: for etudid in etudids:
@ -315,18 +315,21 @@ class JuryPE(object):
trajectoire = self.trajectoires.suivi[etudid][aggregat] trajectoire = self.trajectoires.suivi[etudid][aggregat]
"""Les moyennes par tag de cette trajectoire""" """Les moyennes par tag de cette trajectoire"""
if trajectoire: if trajectoire:
trajectoire_tagguee = self.trajectoires_tagguees[trajectoire.trajectoire_id] trajectoire_tagguee = self.trajectoires_tagguees[
trajectoire.trajectoire_id
]
bilan = trajectoire_tagguee.moyennes_tags[tag] bilan = trajectoire_tagguee.moyennes_tags[tag]
donnees[etudid] |= { donnees[etudid] |= {
f"{aggregat} notes ": f"{bilan['notes'].loc[etudid]:.1f}", f"{aggregat} notes ": f"{bilan['notes'].loc[etudid]:.1f}",
f"{aggregat} class. (groupe)": f"{bilan['classements'].loc[etudid]}/{bilan['nb_inscrits']}", f"{aggregat} class. (groupe)": f"{bilan['classements'].loc[etudid]}/{bilan['nb_inscrits']}",
f"{aggregat} min/moy/max (groupe)": f"{bilan['min']:.1f}/{bilan['moy']:.1f}/{bilan['max']:.1f}"} f"{aggregat} min/moy/max (groupe)": f"{bilan['min']:.1f}/{bilan['moy']:.1f}/{bilan['max']:.1f}",
}
else: else:
donnees[etudid] |= { donnees[etudid] |= {
f"{aggregat} notes ": "-", f"{aggregat} notes ": "-",
f"{aggregat} class. (groupe)": "-", f"{aggregat} class. (groupe)": "-",
f"{aggregat} min/moy/max (groupe)": "-" f"{aggregat} min/moy/max (groupe)": "-",
} }
"""L'interclassement""" """L'interclassement"""
@ -335,29 +338,29 @@ class JuryPE(object):
bilan = interclass.moyennes_tags[tag] bilan = interclass.moyennes_tags[tag]
donnees[etudid] |= { donnees[etudid] |= {
f"{aggregat} class. (promo)": f"{bilan['classements'].loc[etudid]}/{bilan['nb_inscrits']}", f"{aggregat} class. (promo)": f"{bilan['classements'].loc[etudid]}/{bilan['nb_inscrits']}",
f"{aggregat} min/moy/max (promo)": f"{bilan['min']:.1f}/{bilan['moy']:.1f}/{bilan['max']:.1f}" f"{aggregat} min/moy/max (promo)": f"{bilan['min']:.1f}/{bilan['moy']:.1f}/{bilan['max']:.1f}",
} }
else: else:
donnees[etudid] |= { donnees[etudid] |= {
f"{aggregat} class. (promo)": "-", f"{aggregat} class. (promo)": "-",
f"{aggregat} min/moy/max (promo)": "-" f"{aggregat} min/moy/max (promo)": "-",
} }
# Fin de l'aggrégat # Fin de l'aggrégat
"""Construction du dataFrame"""
df = pd.DataFrame.from_dict(donnees, orient="index")
df = pd.DataFrame.from_dict(donnees, orient='index') """Tri par nom/prénom"""
df.sort_values(by=["Nom", "Prenom"], inplace = True)
return df return df
def table_syntheseJury(self, mode="singlesheet"): # was str_syntheseJury def table_syntheseJury(self, mode="singlesheet"): # was str_syntheseJury
"""Table(s) du jury """Table(s) du jury
mode: singlesheet ou multiplesheet pour export excel mode: singlesheet ou multiplesheet pour export excel
""" """
sT = SeqGenTable() # le fichier excel à générer sT = SeqGenTable() # le fichier excel à générer
if mode == "singlesheet": if mode == "singlesheet":
return sT.get_genTable("singlesheet") return sT.get_genTable("singlesheet")
else: else:

View File

@ -93,9 +93,6 @@ class TrajectoireTag(TableTag):
self.etuds = nt.etuds self.etuds = nt.etuds
# assert self.etuds == trajectoire.suivi # manque-t-il des étudiants ? # assert self.etuds == trajectoire.suivi # manque-t-il des étudiants ?
self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds} self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds}
self.cursus = {
etudid: donnees_etudiants.cursus[etudid] for etudid in self.etudiants
}
"""Les tags extraits de tous les semestres""" """Les tags extraits de tous les semestres"""
self.tags_sorted = self.do_taglist() self.tags_sorted = self.do_taglist()
@ -104,7 +101,7 @@ class TrajectoireTag(TableTag):
self.notes_cube = self.compute_notes_cube() self.notes_cube = self.compute_notes_cube()
"""Calcul les moyennes par tag sous forme d'un dataframe""" """Calcul les moyennes par tag sous forme d'un dataframe"""
etudids = self.get_etudids() etudids = list(self.etudiants.keys())
self.notes = compute_tag_moy(self.notes_cube, etudids, self.tags_sorted) self.notes = compute_tag_moy(self.notes_cube, etudids, self.tags_sorted)
"""Synthétise les moyennes/classements par tag""" """Synthétise les moyennes/classements par tag"""
@ -166,8 +163,7 @@ class TrajectoireTag(TableTag):
return etudids_x_tags_x_semestres return etudids_x_tags_x_semestres
def get_etudids(self):
return list(self.etudiants.keys())
def do_taglist(self): def do_taglist(self):
"""Synthétise les tags à partir des semestres (taggués) aggrégés """Synthétise les tags à partir des semestres (taggués) aggrégés

View File

@ -45,8 +45,8 @@ from app.scodoc import sco_formsemestre
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.pe import pe_tools from app.pe import pe_comp
from app.pe import pe_jurype from app.pe import pe_jury
from app.pe import pe_avislatex from app.pe import pe_avislatex
@ -75,7 +75,7 @@ def _pe_view_sem_recap_form(formsemestre_id):
return "\n".join(H) + html_sco_header.sco_footer() return "\n".join(H) + html_sco_header.sco_footer()
# L'année du diplome # L'année du diplome
diplome = pe_tools.get_annee_diplome_semestre(sem_base) diplome = pe_comp.get_annee_diplome_semestre(sem_base)
H = [ H = [
html_sco_header.sco_header(page_title="Avis de poursuite d'études"), html_sco_header.sco_header(page_title="Avis de poursuite d'études"),
@ -136,12 +136,12 @@ def pe_view_sem_recap(
) )
# L'année du diplome # L'année du diplome
diplome = pe_tools.get_annee_diplome_semestre(sem_base) diplome = pe_comp.get_annee_diplome_semestre(sem_base)
jury = pe_jurype.JuryPE(diplome, sem_base.formation.formation_id) jury = pe_jury.JuryPE(diplome, sem_base.formation.formation_id)
# Ajout avis LaTeX au même zip: # Ajout avis LaTeX au même zip:
etudids = list(jury.syntheseJury.keys()) # etudids = list(jury.syntheseJury.keys())
# Récupération du template latex, du footer latex et du tag identifiant les annotations relatives aux PE # Récupération du template latex, du footer latex et du tag identifiant les annotations relatives aux PE
# (chaines unicodes, html non quoté) # (chaines unicodes, html non quoté)
@ -187,11 +187,11 @@ def pe_view_sem_recap(
) )
# Ajout des annotations PE dans un fichier excel # Ajout des annotations PE dans un fichier excel
sT = pe_avislatex.table_syntheseAnnotationPE(jury.syntheseJury, tag_annotation_pe) # sT = pe_avislatex.table_syntheseAnnotationPE(jury.syntheseJury, tag_annotation_pe)
if sT: # if sT:
jury.add_file_to_zip( # jury.add_file_to_zip(
jury.nom_export_zip + "_annotationsPE" + scu.XLSX_SUFFIX, sT.excel() # jury.nom_export_zip + "_annotationsPE" + scu.XLSX_SUFFIX, sT.excel()
) # )
if False: if False:
latex_pages = {} # Dictionnaire de la forme nom_fichier => contenu_latex latex_pages = {} # Dictionnaire de la forme nom_fichier => contenu_latex