1
0
forked from ScoDoc/ScoDoc

Ajout de l'option "Générer les moyennes par RCUEs (compétences)"

This commit is contained in:
Cléo Baras 2024-02-27 18:00:06 +01:00
parent c2a66b607f
commit b9b9a172c7
5 changed files with 115 additions and 77 deletions

View File

@ -45,10 +45,15 @@ class ParametrageClasseurPE(FlaskForm):
default=True,
render_kw={"checked": ""},
)
moyennes_ues_rcues = BooleanField("Générer les moyennes par RCUEs (compétences)")
moyennes_ues_rcues = BooleanField(
"Générer les moyennes par RCUEs (compétences)",
default=True,
render_kw={"checked": ""},
)
min_max_moy = BooleanField("Afficher les colonnes min/max/moy")
synthese_individuelle_etud = BooleanField(
"Générer la feuille synthèse avec un onglet par étudiant"
"Générer les synthèses HTML étudiant par étudiant"
)
submit = SubmitField("Générer les classeurs poursuites d'études")

View File

@ -207,3 +207,28 @@ def repr_comp_et_ues(acronymes_ues_to_competences):
liste += ["📍" + acro]
aff_comp += [f" 💡{comp} (⇔ {', '.join(liste)})"]
return "\n".join(aff_comp)
def aff_rcsemxs_suivis_par_etudiants(etudiants):
"""Affiche les RCSemX (regroupement de SemX)
amenant un étudiant du S1 à un Sx"""
etudiants_ids = etudiants.etudiants_ids
jeunes = list(enumerate(etudiants_ids))
for no_etud, etudid in jeunes:
etat = "" if etudid in etudiants.abandons_ids else ""
pe_print(f"-> {etat} {etudiants.identites[etudid].nomprenom} :")
for nom_rcs, rcs in etudiants.rcsemXs[etudid].items():
if rcs:
pe_print(f" > RCSemX ⏯️{nom_rcs}: {rcs.get_repr()}")
vides = []
for nom_rcs in pe_rcs.TOUS_LES_RCS:
les_rcssemX_suivis = []
for no_etud, etudid in jeunes:
if etudiants.rcsemXs[etudid][nom_rcs]:
les_rcssemX_suivis.append(etudiants.rcsemXs[etudid][nom_rcs])
if not les_rcssemX_suivis:
vides += [nom_rcs]
vides = sorted(list(set(vides)))
pe_print(f"⚠️ RCSemX vides : {', '.join(vides)}")

View File

@ -69,6 +69,9 @@ class EtudiantsJuryPE:
self.semXs: dict[int:dict] = {}
"""Les semXs (RCS de type Sx) suivis par chaque étudiant"""
self.rcsemXs: dict[int:dict] = {}
"""Les RC de SemXs (RCS de type Sx, xA, xS) suivis par chaque étudiant"""
self.etudiants_diplomes = {}
"""Les identités des étudiants à considérer au jury (ceux qui seront effectivement
diplômés)"""
@ -274,6 +277,7 @@ class EtudiantsJuryPE:
# Initialise ses trajectoires/SemX/RCSemX
self.trajectoires[etudid] = {aggregat: None for aggregat in pe_rcs.TOUS_LES_RCS}
self.semXs[etudid] = {aggregat: None for aggregat in pe_rcs.TOUS_LES_SEMESTRES}
self.rcsemXs[etudid] = {aggregat: None for aggregat in pe_rcs.TOUS_LES_RCS}
def structure_cursus_etudiant(self, etudid: int):
"""Structure les informations sur les semestres suivis par un

View File

@ -102,9 +102,8 @@ class JuryPE(object):
"Nom du zip où ranger les fichiers générés"
# Les options
self.options = options
"""Options de configuration"""
"""Options de configuration (cf. pe_sem_recap)"""
pe_affichage.pe_print(
f"Données de poursuite d'étude générées le {time.strftime('%d/%m/%Y à %H:%M')}\n",
@ -350,8 +349,9 @@ class JuryPE(object):
pe_affichage.pe_print(
"""******************************************************************************"""
)
self.rcss_jury.cree_rcsemxs(self.etudiants)
self.rcss_jury._aff_rcsemxs_suivis(self.etudiants)
self.rcss_jury.cree_rcsemxs(options=self.options)
if "moyennes_ues_rcues" in self.options and self.options["moyennes_ues_rcues"]:
pe_affichage.aff_rcsemxs_suivis_par_etudiants(self.etudiants)
def _gen_xls_rcstags(self, zipfile: ZipFile):
"""Génère les RCS taggués traduisant les moyennes (orientées compétences)
@ -384,6 +384,20 @@ class JuryPE(object):
)
pe_affichage.pe_print("1) Calcul des moyennes des RCSTag", info=True)
if not self.rcss_jury.rcsemxs:
if (
"moyennes_ues_rcues" in self.options
and not self.options["moyennes_ues_rcues"]
):
pe_affichage.pe_print(" -> Pas de RCSemX à calculer (cf. options)")
else:
pe_affichage.pe_print(
" -> Pas de RCSemX à calculer (alors qu'aucune option ne les limite) => problème"
)
self.rcsstags = {}
return
# Calcul des RCSTags sur la base des RCSemX
self.rcsstags = {}
for rcs_id, rcsemx in self.rcss_jury.rcsemxs.items():
self.rcsstags[rcs_id] = pe_rcstag.RCSemXTag(
@ -431,20 +445,26 @@ class JuryPE(object):
"""******************************************************************"""
)
pe_affichage.pe_print(
"""*** Génère les interclassements sur chaque type de RCS/agrgégat"""
"""*** Génère les interclassements sur chaque type de RCS/agrégat"""
)
pe_affichage.pe_print(
"""******************************************************************"""
)
self.interclasstags = {
pe_moytag.CODE_MOY_UE: {},
pe_moytag.CODE_MOY_COMPETENCES: {},
}
if (
"moyennes_ues_rcues" not in self.options
or self.options["moyennes_ues_rcues"]
):
self.interclasstags = {
pe_moytag.CODE_MOY_UE: {},
pe_moytag.CODE_MOY_COMPETENCES: {},
}
else:
self.interclasstags = {pe_moytag.CODE_MOY_UE: {}}
etudiants_diplomes = self.etudiants.etudiants_diplomes
# Les interclassements par UE
# Les interclassements par UE (toujours présents par défaut)
for Sx in pe_rcs.TOUS_LES_SEMESTRES:
interclass = pe_interclasstag.InterClassTag(
Sx,
@ -457,16 +477,22 @@ class JuryPE(object):
self.interclasstags[pe_moytag.CODE_MOY_UE][Sx] = interclass
# Les interclassements par compétences
for nom_rcs in pe_rcs.TOUS_LES_RCS:
interclass = pe_interclasstag.InterClassTag(
nom_rcs,
pe_moytag.CODE_MOY_COMPETENCES,
etudiants_diplomes,
self.rcss_jury.rcsemxs,
self.rcsstags,
self.rcss_jury.rcsemxs_suivis,
)
self.interclasstags[pe_moytag.CODE_MOY_COMPETENCES][nom_rcs] = interclass
if (
"moyennes_ues_rcues" not in self.options
or self.options["moyennes_ues_rcues"]
):
for nom_rcs in pe_rcs.TOUS_LES_RCS:
interclass = pe_interclasstag.InterClassTag(
nom_rcs,
pe_moytag.CODE_MOY_COMPETENCES,
etudiants_diplomes,
self.rcss_jury.rcsemxs,
self.rcsstags,
self.rcss_jury.rcsemxs_suivis,
)
self.interclasstags[pe_moytag.CODE_MOY_COMPETENCES][
nom_rcs
] = interclass
# Intègre le bilan des aggrégats (interclassé par promo) au zip final
output = io.BytesIO()
@ -474,10 +500,9 @@ class JuryPE(object):
output, engine="openpyxl"
) as writer:
onglets = []
for type_interclass in [
pe_moytag.CODE_MOY_UE,
pe_moytag.CODE_MOY_COMPETENCES,
]:
for (
type_interclass
) in self.interclasstags: # Pour les types d'interclassements prévus
interclasstag = self.interclasstags[type_interclass]
for nom_rcs, interclass in interclasstag.items():
if interclass.is_significatif():
@ -522,7 +547,7 @@ class JuryPE(object):
tags = self._do_tags_list(self.interclasstags)
for tag in tags:
for type_moy in [pe_moytag.CODE_MOY_UE, pe_moytag.CODE_MOY_COMPETENCES]:
for type_moy in self.interclasstags:
self.synthese[(tag, type_moy)] = self.df_tag_type(tag, type_moy)
# Export des données => mode 1 seule feuille -> supprimé
@ -555,7 +580,7 @@ class JuryPE(object):
if onglets:
self.add_file_to_zip(
zipfile, f"synthese_jury_{self.diplome}_par_tag.xlsx", output.read()
zipfile, f"synthese_jury_{self.diplome}.xlsx", output.read()
)
def _gen_html_synthese_par_etudiant(self, zipfile: ZipFile):
@ -565,12 +590,18 @@ class JuryPE(object):
pe_affichage.pe_print("*** Synthèse finale étudiant par étudiant", info=True)
pe_affichage.pe_print("**************************************************")
etudids = list(self.diplomes_ids)
for etudid in etudids:
nom, prenom, html = self.synthetise_jury_etudiant(etudid)
self.add_file_to_zip(
zipfile, f"{nom}_{prenom}.html", html, path="etudiants"
)
if (
"moyennes_ues_rcues" not in self.options
or self.options["moyennes_ues_rcues"]
):
etudids = list(self.diplomes_ids)
for etudid in etudids:
nom, prenom, html = self.synthetise_jury_etudiant(etudid)
self.add_file_to_zip(
zipfile, f"{nom}_{prenom}.html", html, path="etudiants"
)
else:
pe_affichage.pe_print(" > Pas de synthèse étudiant/étudiant possible/prévu")
def _add_log_to_zip(self, zipfile):
"""Add a text file with the log messages"""

View File

@ -117,7 +117,7 @@ class RCSsJuryPE:
self.semXs_suivis[etudid][agregat] = semX
self.etudiants.semXs[etudid][agregat] = semX
def cree_rcsemxs(self, etudiants: pe_etudiant.EtudiantsJuryPE):
def cree_rcsemxs(self, options={"moyennes_ues_rcues": True}):
"""Créé tous les RCSemXs, au regard du cursus des étudiants
analysés (trajectoires traduisant son parcours dans les
différents semestres) + les mémorise dans les données de l'étudiant
@ -125,6 +125,11 @@ class RCSsJuryPE:
self.rcsemxs_suivis = {}
self.rcsemxs = {}
if "moyennes_ues_rcues" in options and options["moyennes_ues_rcues"] == False:
# Pas de RCSemX généré
pe_affichage.pe_print("⚠️ Pas de RCSemX générés")
return
# Pour tous les étudiants du jury
pas_de_semestres = []
for etudid in self.trajectoires_suivies:
@ -132,20 +137,11 @@ class RCSsJuryPE:
nom_rcs: None for nom_rcs in pe_rcs.TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
}
# Recopie des SemX & des suivis associés => est-ce utile ?
# for nom_rcs in pe_rcs.TOUS_LES_SEMESTRES:
# trajectoire = self.semXs_suivis[etudid][nom_rcs]
# if trajectoire:
# self.rcsemxs[trajectoire.rcs_id] = trajectoire
# self.rcsemxs_suivis[etudid][nom_rcs] = trajectoire
# Pour chaque aggréggat de type xA ou Sx
tous_les_agregats = pe_rcs.TOUS_LES_RCS
for nom_rcs in tous_les_agregats:
trajectoire = self.trajectoires_suivies[etudid][nom_rcs]
# Pour chaque aggréggat de type xA ou Sx ou xS
for agregat in pe_rcs.TOUS_LES_RCS:
trajectoire = self.trajectoires_suivies[etudid][agregat]
if not trajectoire:
self.rcsemxs_suivis[etudid][nom_rcs] = None
self.rcsemxs_suivis[etudid][agregat] = None
else:
# Identifiant de la trajectoire => donnera ceux du RCSemX
tid = trajectoire.rcs_id
@ -159,14 +155,14 @@ class RCSsJuryPE:
# Par ex: dans S1+S2+S1+S2+S3 => les 2 S1 devient le SemX('S1'), les 2 S2 le SemX('S2'), etc..
# Les Sx pris en compte dans l'aggrégat
noms_sems_aggregat = pe_rcs.TYPES_RCS[nom_rcs]["aggregat"]
noms_sems_aggregat = pe_rcs.TYPES_RCS[agregat]["aggregat"]
semxs_a_aggreger = {}
for Sx in noms_sems_aggregat:
semestres_etudiants = etudiants.cursus[etudid][Sx]
semestres_etudiants = self.etudiants.cursus[etudid][Sx]
if not semestres_etudiants:
pas_de_semestres += [
f"{Sx} pour {etudiants.identites[etudid].nomprenom}"
f"{Sx} pour {self.etudiants.identites[etudid].nomprenom}"
]
else:
semx_id = get_semx_from_semestres_aggreges(
@ -184,9 +180,10 @@ class RCSsJuryPE:
rcsemx.add_semXs(semxs_a_aggreger)
# Mémoire du RCSemX aux informations de suivi de l'étudiant
self.rcsemxs_suivis[etudid][nom_rcs] = rcsemx
self.rcsemxs_suivis[etudid][agregat] = rcsemx
self.etudiants.rcsemXs[etudid][agregat] = rcsemx
# Affichage des étudiants pour lesquels il manque un semestre
# Affichage des étudiants pour lesquels il manque un semestre
pas_de_semestres = sorted(set(pas_de_semestres))
if pas_de_semestres:
pe_affichage.pe_print("⚠️ Semestres manquants :")
@ -194,30 +191,6 @@ class RCSsJuryPE:
"\n".join([" " * 10 + psd for psd in pas_de_semestres])
)
def _aff_rcsemxs_suivis(self, etudiants):
"""Affiche les RCSemX suivis par les étudiants"""
# Affichage pour debug
jeunes = list(enumerate(self.rcsemxs_suivis.keys()))
for no_etud, etudid in jeunes:
etat = "" if etudid in etudiants.abandons_ids else ""
pe_affichage.pe_print(
f"-> {etat} {etudiants.identites[etudid].nomprenom} :"
)
for nom_rcs, rcs in self.rcsemxs_suivis[etudid].items():
if rcs:
pe_affichage.pe_print(f" > RCSemX ⏯️{nom_rcs}: {rcs.get_repr()}")
vides = []
for nom_rcs in pe_rcs.TOUS_LES_RCS:
les_rcssemX_suivis = []
for no_etud, etudid in jeunes:
if self.rcsemxs_suivis[etudid][nom_rcs]:
les_rcssemX_suivis.append(self.rcsemxs_suivis[etudid][nom_rcs])
if not les_rcssemX_suivis:
vides += [nom_rcs]
vides = sorted(list(set(vides)))
pe_affichage.pe_print(f"⚠️ RCSemX vides : {', '.join(vides)}")
def get_rcs_etudiant(
semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str