Compare commits

...

4 Commits

7 changed files with 616 additions and 568 deletions

View File

@ -55,15 +55,17 @@ class EtudiantsJuryPE:
def __init__(self):
""" """
"Les identités des étudiants du jury"
"Les identités des étudiants traités pour le jury"
self.identites = {} # ex. ETUDINFO_DICT
"Les cursus (semestres suivis, abandons, ...)"
"Les cursus (semestres suivis, abandons, ...) des étudiants"
self.cursus = {}
"Les etudids des étudiants à considérer au jury"
"Les etudids des étudiants à considérer au jury (ceux qui seront effectivement diplômés)"
self.etudiants_jury_ids = {}
"Les etudids des étudiants dont il faut calculer les moyennes/classements"
"Les etudids des étudiants dont il faut calculer les moyennes/classements (même si d'éventuels abandons)"
self.etudiants_ids = {}
"Les formsemestres dont il faut calculer les moyennes"
"Les formsemestres dont il faut calculer les moyennes par tag"
self.formsemestres_jury_ids = {}
def find_etudiants(self, annee_diplome: int, formation_id: int):
@ -75,38 +77,46 @@ class EtudiantsJuryPE:
Args:
annee_diplome: L'année de diplomation
formation_id: L'identifiant de la formation
formation_id: L'identifiant de la formation (inutilisé)
*Remarque* : ex: JuryPE.get_etudiants_in_jury()
"""
"Les cosemestres donnant lieu à même année de diplome"
cosemestres = pe_tools.get_cosemestres_diplomants(
annee_diplome, None # formation_id,
)
cosemestres = pe_tools.get_cosemestres_diplomants(annee_diplome, None)
pe_tools.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_tools.pe_print("2) Liste des étudiants dans les différents co-semestres")
self.etudiants_ids = get_etudiants_dans_semestres(
cosemestres
) # étudiants faisant partie de tous les cosemestres
pe_tools.pe_print(" => %d étudiants trouvés" % len(self.etudiants_ids))
self.etudiants_ids = get_etudiants_dans_semestres(cosemestres)
pe_tools.pe_print(
" => %d étudiants trouvés dans les cosemestres" % len(self.etudiants_ids)
)
# L'analyse des parcours étudiants pour déterminer leur année effective de diplome avec prise en compte des redoublements, des abandons, ....
"""Analyse des parcours étudiants pour déterminer leur année effective de diplome
avec prise en compte des redoublements, des abandons, ...."""
pe_tools.pe_print("3) Analyse des parcours individuels des étudiants")
no_etud = 0
for no_etud, etudid in enumerate(self.etudiants_ids):
self.add_etudid(etudid, cosemestres)
"""L'identité de l'étudiant"""
identite = Identite.get_etud(etudid)
self.identites[etudid] = identite
"""L'analyse de son cursus"""
self.analyse_etat_etudiant(etudid, cosemestres)
"""L'analyse de son parcours pour atteindre chaque semestre de la formation"""
self.analyse_parcours_etudiant_dans_semestres(etudid)
if (no_etud + 1) % 10 == 0:
pe_tools.pe_print((no_etud + 1), " ", end="")
no_etud += 1
pe_tools.pe_print()
"""Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris"""
self.etudiants_jury_ids = self.get_etudids(annee_diplome)
self.etudiants_jury_ids = self.get_etudiants(annee_diplome)
"""Les étudiants dont il faut calculer les moyennes"""
self.etudiants_ids = {etudid for etudid in self.cursus}
@ -129,89 +139,48 @@ class EtudiantsJuryPE:
+ ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)])
)
def get_etudids(self, annee_diplome: int = None, ordre="aucun") -> list:
"""Liste des etudid des étudiants qui vont être à traiter au jury PE pour
l'année de diplômation donnée et n'ayant ni été réorienté, ni abandonné.
Si l'année de diplômation n'est pas précisée (None), inclus les étudiants réorientés
ou ayant abandonné.
Si l'``ordre`` est précisé, trie la liste par ordre alphabétique de etat_civil
Args:
annee_diplome: Année de diplomation visée pour le jury
ordre: Un ordre de tri
Returns:
Une liste contenant des ``etudids``
Note: ex JuryPE.get_etudids_du_jury()
"""
if annee_diplome:
etudids = [
etudid
for (etudid, donnees) in self.cursus.items()
if donnees["diplome"] == annee_diplome and not donnees["abandon"]
]
else:
etudids = [
etudid
for (etudid, donnees) in self.cursus.items()
]
if ordre == "alphabetique": # Tri alphabétique
etudidsAvecNom = [
(etudid, etud["etat_civil"])
for (etudid, etud) in self.cursus.items()
if etudid in etudids
]
etudidsAvecNomTrie = sorted(etudidsAvecNom, key=lambda col: col[1])
etudids = [etud[0] for etud in etudidsAvecNomTrie]
return etudids
def get_etudiants(self, annee_diplome: int = None) -> dict[Identite]:
def get_etudiants(self, annee_diplome: int) -> dict[Identite]:
"""Identités des étudiants (sous forme d'un dictionnaire `{etudid: Identite(etudid)}`
qui vont être à traiter au jury PE pour
l'année de diplômation donnée et n'ayant ni été réorienté, ni abandonné.
Si l'année de diplômation n'est pas précisée (None), inclus les étudiants réorientés
ou ayant abandonné.
Args:
annee_diplome: Année de diplomation visée pour le jury
Returns:
Un dictionnaire `{etudid: Identite(etudid)}`
"""
etudids = self.get_etudids(annee_diplome=annee_diplome)
etudiants = {etudid: self.identites[etudids] for etudid in etudids}
etudids = [
etudid
for etudid in self.cursus
if self.cursus[etudid]["diplome"] == annee_diplome and self.cursus[etudid]["abandon"]
]
etudiants = {etudid: self.identites[etudid] for etudid in etudids}
return etudiants
def add_etudid(self, etudid: int, cosemestres):
"""Ajoute un étudiant à ceux qui devront être traités pendant le jury pouvant être :
* des étudiants sur lesquels le jury va statuer (année de diplômation du jury considéré)
* des étudiants qui ne seront pas considérés dans le jury mais ont participé dans leur scolarité
à un (ou plusieurs) semestres communs aux étudiants du jury (et impacteront les classements)
def analyse_etat_etudiant(
self, etudid: int, cosemestres: dict[int, FormSemestre]
):
"""Analyse le cursus d'un étudiant pouvant être :
L'ajout consiste :
* l'un de ceux sur lesquels le jury va statuer (année de diplômation du jury considéré)
* un étudiant qui ne sera pas considéré dans le jury mais qui a participé dans sa scolarité
à un (ou plusieurs) semestres communs aux étudiants du jury (et impactera les classements)
* à insérer une entrée pour l'étudiant en mémorisant son identité,
L'analyse consiste :
* à insérer une entrée dans ``self.cursus`` pour mémoriser son identité,
avec son nom, prénom, etc...
* à analyser son parcours, pour déterminer s'il n'a (ou non) abandonné l'IUT en cours de
route (cf. clé abandon)
* à chercher ses semestres valides (formsemestre_id) et ses années valides (formannee_id),
c'est-à-dire ceux pour lesquels il faudra prendre en compte ses notes dans les calculs de
moyenne (type 1A=S1+S2/2)
Args:
etudid: L'etudid d'un étudiant, à ajouter à ceux traiter par le jury
cosemestres: Dictionnaire {fid: Formsemestre(fid)} donnant accès aux cosemestres de même année de diplomation
Note: ex JuryPE.add_etudid_to_jury()
cosemestres: Dictionnaire {fid: Formsemestre(fid)} donnant accès aux cosemestres
de même année de diplomation
"""
"""L'identité de l'étudiant"""
identite = Identite.get_etud(etudid)
self.identites[etudid] = identite
"""Le cursus global de l'étudiant (restreint aux semestres APC)"""
semestres_etudiant = {
@ -225,38 +194,121 @@ class EtudiantsJuryPE:
"etat_civil": identite.etat_civil, # Ajout à la table jury
"diplome": annee_diplome(identite), # Le date prévisionnelle de son diplôme
"formsemestres": semestres_etudiant, # les semestres de l'étudiant
"semestres": {},
"aggregats": {},
}
""" Est-il réorienté / démissionnaire ou a-t-il arrêté volontairement sa formation ?"""
self.cursus[etudid]["abandon"] = arret_de_formation(identite, cosemestres)
"""Tri des semestres par n° de semestre"""
def analyse_parcours_etudiant_dans_semestres(self, etudid):
"""Structure les informations sur les semestres suivis par un
étudiant, pour identifier les semestres qui seront pris en compte lors de ses calculs
de moyennes PE.
La structure s'appuie sur les numéros de semestre: pour chaque Si, stocke :
* le (ou les) formsemestres de numéro i qu'a suivi un étudiant (2 si redoublant)
* le dernier semestre de numéro i qu'il a suivi (1 ou 0 si pas encore suivi)
Elle s'appuie également sur les aggrégats: pour chaque aggrégat (par ex, 3A=S1+S2+S3),
identifie les semestres que l'étudiant a suivi pour l'amener jusqu'au semestre terminal
de l'aggrégat. Ce parcours peut être :
* S1+S2+S1+S2+S3 si redoublement de la 1ère année
* S1+S2+(année de césure)+S3 si césure, ...
"""
semestres_etudiant = self.cursus[etudid]["formsemestres"]
for nom_sem in pe_tools.TOUS_LES_SEMESTRES:
i = int(nom_sem[1]) + 1 # le n° du semestre
i = int(nom_sem[1]) # le n° du semestre
semestres_i = {
fid: semestres_etudiant[fid]
for fid in semestres_etudiant
if semestres_etudiant[fid].semestre_id == i
} # les semestres de n°i de l'étudiant
dernier_semestre_i = get_dernier_semestre(semestres_i)
self.cursus[etudid][nom_sem] = dernier_semestre_i
self.cursus[etudid]["aggregats"][nom_sem] = semestres_i
self.cursus[etudid]["semestres"][nom_sem] = get_dernier_semestre(semestres_i)
"""Tri des semestres par aggrégat"""
for parcours in pe_tools.TOUS_LES_AGGREGATS:
"""L'aggrégat considéré"""
noms_semestre_de_aggregat = pe_tools.PARCOURS[parcours]["aggregat"]
self.cursus[etudid][parcours] = {}
for nom_sem in noms_semestre_de_aggregat:
self.cursus[etudid][parcours] = (
self.cursus[etudid][parcours] | self.cursus[etudid][nom_sem]
)
"""Tri des semestres par aggrégat et par semestre terminal"""
for aggregat in pe_tools.TOUS_LES_AGGREGATS:
self.cursus[etudid][aggregat] = {}
"""L'aggrégat considéré (par ex: 3S), son nom de son semestre terminal (par ex: S3) et son numéro (par ex: 3)"""
noms_semestre_de_aggregat = pe_tools.PARCOURS[aggregat]["aggregat"]
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
numero_semestre_terminal = int(nom_semestre_terminal[-1])
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
pe_tools.pe_print(
parcours + "=" + str(self.cursus[etudid][parcours]),
end="",
)
"""Les semestres terminaux de l'aggrégat"""
# formsemestres_terminal = self.cursus[etudid]["aggregats"][nom_semestre_terminal]
# dernier_formsemestre_terminal = get_dernier_semestre(formsemestres_terminal) # le dernier en date
dernier_formsemestre_terminal = self.cursus[etudid]["semestres"][nom_semestre_terminal]
# for formsem_id_term in formsemestres_terminal:
if dernier_formsemestre_terminal: # ne considérant que le dernier
formsem_id_term = list(dernier_formsemestre_terminal.keys())[0]
formsemestre_terminal = self.cursus[etudid]["formsemestres"][formsem_id_term]
"""Semestres de n° inférieur (pax ex: des S1, S2, S3 pour un S3 terminal) et qui lui sont antérieurs"""
semestres_aggreges = {}
for fid in self.cursus[etudid]["formsemestres"]:
semestre = self.cursus[etudid]["formsemestres"][fid]
if (
semestre.semestre_id <= numero_semestre_terminal
and semestre.date_fin <= formsemestre_terminal.date_fin
):
semestres_aggreges[fid] = semestre
self.cursus[etudid][aggregat][formsem_id_term] = semestres_aggreges
def get_formsemestres_terminaux_aggregat(self, aggregat: str):
"""Pour un aggrégat donné, ensemble des formsemestres terminaux possibles pour l'aggrégat (pour l'aggrégat '3S'
incluant S1+S2+S3, a pour semestre terminal S3). Ces formsemestres traduisent :
* les différents parcours des étudiants liés par exemple au choix de modalité (par ex: S1 FI + S2 FI + S3 FI
ou S1 FI + S2 FI + S3 UFA), en renvoyant les formsemestre_id du S3 FI et du S3 UFA.
* les éventuelles situations de redoublement (par ex pour 1 étudiant ayant redoublé sa 2ème année :
S1 + S2 + S3 (1ère session) et S1 + S2 + S3 + S4 + S3 (2ème session), en renvoyant les formsemestre_id du
S3 (1ère session) et du S3 (2ème session)
Args:
aggregat: L'aggrégat
Returns:
Un dictionnaire {fid: FormSemestre(fid)}
"""
formsemestres_terminaux = {}
for etudid in self.cursus:
"""Les formsemestre_id des semestres terminaux"""
fids = self.cursus[etudid][aggregat].keys()
"""Pour chaque identifiant de semestre terminal, récupère le formsemestre associé"""
for fid in fids:
if fid not in formsemestres_terminaux:
formsemestres_terminaux[fid] = self.cursus[etudid][aggregat][fid][
fid
]
return formsemestres_terminaux
def get_semestres_a_aggreger(self, aggregat: str, formsemestre_id_terminal: int):
"""Pour un aggrégat donné associé à un formsemestre terminal cible, renvoie l'ensemble des semestres à
prendre en compte dans l'aggrégat sous la forme d'un dictionnaire {fid: FormSemestre(fid)}.
Fusionne les cursus individuels des étudiants, dont le cursus correspond à l'aggrégat visé.
Args:
aggregat: Un aggrégat (par ex. 1A, 2A, 3S, 6S)
formsemestre_id_terminal: L'identifiant du formsemestre terminal de l'aggrégat, devant correspondre au
dernier semestre de l'aggrégat
"""
noms_semestres_aggreges = pe_tools.PARCOURS[aggregat]["aggregat"]
formsemestres = {}
for etudid in self.cursus:
cursus_etudiant = self.cursus[etudid][aggregat]
if formsemestre_id_terminal in cursus_etudiant:
formsemestres_etudiant = cursus_etudiant[formsemestre_id_terminal]
formsemestres = formsemestres | formsemestres_etudiant
return formsemestres
def get_formsemestres_jury(self, semestres_recherches=None):
"""Ayant connaissance des étudiants dont il faut calculer les moyennes pour
@ -310,7 +362,7 @@ class EtudiantsJuryPE:
nom_sem = semestres_recherches
semestres = {}
for etudid in self.etudiants_ids:
semestres = semestres | self.cursus[etudid][nom_sem]
semestres = semestres | self.cursus[etudid]["aggregats"][nom_sem]
return semestres
else:
raise ValueError(
@ -368,25 +420,6 @@ def annee_diplome(identite: Identite) -> int:
return None
def semestres_etudiant(etudid: int, semestre_id=None):
"""La liste des semestres BUT d'un étudiant
pour un semestre_id (parmi 1, 2, 3, 4, 5, 6) donné
en fonction de ses infos d'etud (cf. sco_etud.get_etud_info(etudid=etudid, filled=True)[0]),
les semestres étant triés par ordre décroissant.
Si semestre_id == None renvoie tous les semestres
NOTE:: ex:: JuryPE.get_semestresBUT_d_un_etudiant()
TODO:: A revoir"""
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
nbre_semestres = int(pe_tools.AGGREGAT_DIPLOMANT[0]) # 6
if semestre_id == None:
sesSems = [
sem for sem in etud["sems"] if 1 <= sem["semestre_id"] <= nbre_semestres
]
else:
sesSems = [sem for sem in etud["sems"] if sem["semestre_id"] == semestre_id]
return sesSems
def arret_de_formation(identite: Identite, cosemestres: list[FormSemestre]) -> bool:
"""Détermine si un étudiant a arrêté sa formation. Il peut s'agir :
@ -447,9 +480,10 @@ def arret_de_formation(identite: Identite, cosemestres: list[FormSemestre]) -> b
return False
def get_dernier_semestre(semestres: dict[FormSemestre]):
def get_dernier_semestre(semestres: dict[int, FormSemestre]):
"""Renvoie le dernier semestre en date d'un dictionnaire
de semestres de la forme {fid: FormSemestre(fid)
de semestres de la forme {fid: FormSemestre(fid)}.
La date prise en compte est celle marquant la **fin** des semestres.
Args:
semestres: Un dictionnaire de semestres

View File

@ -48,11 +48,13 @@ import os
from zipfile import ZipFile
import app.pe.pe_etudiant
import app.pe.pe_settag_interclasse
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.comp.res_sem import load_formsemestre_results
from app.models import Formation, FormSemestre
from app.models.etudiants import Identite
from app.pe.pe_semestretag import SemestreTag
from app.scodoc.gen_tables import GenTable, SeqGenTable
import app.scodoc.sco_utils as scu
@ -117,13 +119,6 @@ class JuryPE(object):
meme_programme: si True, impose un même programme pour tous les étudiants participant au jury,
si False, permet des programmes differents
"""
self.semTagDict = (
{}
) # Les semestres taggués à la base des calculs de moyenne par tag
self.setTagDict = (
{}
) # dictionnaire récapitulant les semTag impliqués dans le jury de la forme { 'formsemestre_id' : object Semestre_tag
self.promoTagDict = {}
"L'année du diplome"
@ -137,18 +132,45 @@ class JuryPE(object):
self.zipdata = io.BytesIO()
self.zipfile = ZipFile(self.zipdata, "w")
"Les informations sur les étudiants édités par le jury PE"
self.etudiants = EtudiantsJuryPE() # Les infos sur les étudiants
self.syntheseJury = {} # Le jury de synthèse
"""Chargement des étudiants à prendre en compte dans le jury"""
pe_tools.pe_print(
f"*** Recherche et chargement des étudiants diplômés en {self.diplome} pour la formation {self.formation_id}"
)
self.etudiants = EtudiantsJuryPE() # Les infos sur les étudiants
self.etudiants.find_etudiants(self.diplome, self.formation_id)
"""Calcul des moyennes pour le jury PE"""
self.exe_calculs_juryPE()
"""Génère les semestres taggués (avec le calcul des moyennes) pour le jury PE"""
self.semestres_taggues = compute_semestres_tag(self.etudiants)
if pe_tools.PE_DEBUG:
"""Intègre le bilan des semestres taggués au zip final"""
for fid in self.semestres_taggues:
formsemestretag = self.semestres_taggues[fid]
filename = formsemestretag.nom.replace(" ", "_") + ".csv"
pe_tools.pe_print(f" - Export csv de {filename} ")
self.add_file_to_zip(
filename, formsemestretag.str_tagtable(), path="details_semestres"
)
"""Génère les aggrégats de semestre (par ex: 1A, 3S, 5S) avec calcul
des moyennes pour le jury"""
self.aggregats_taggues = compute_aggregats_tag(self.etudiants, self.semestres_taggues)
if pe_tools.PE_DEBUG:
"""Intègre le bilan des aggrégats de semestres au zip final"""
for aggregat in self.aggregats_taggues:
for fid in self.aggregats_taggues[aggregat]:
set_tag = self.aggregats_taggues[aggregat][fid]
filename = set_tag.nom.replace(" ", "_") + ".csv"
pe_tools.pe_print(f" - Export csv de {filename} ")
self.add_file_to_zip(
filename, set_tag.str_tagtable(), path="details_semestres"
)
"""Génère les interclassements par (nom d') aggrégat"""
"""Synthèse des éléments du jury PE"""
if False:
@ -164,21 +186,35 @@ class JuryPE(object):
filename = self.nom_export_zip + "_jurySyntheseDict" + scu.XLSX_SUFFIX
self.xlsV2 = self.table_syntheseJury(mode="multiplesheet")
if self.xlsV2:
self.add_file_to_zip(filename, self.xlsV2.excel())
pe_tools.add_file_to_zip(
self.nom_export_zip, filename, self.xlsV2.excel()
)
# Pour debug
# self.syntheseJury = pe_tools.JURY_SYNTHESE_POUR_DEBUG #Un dictionnaire fictif pour debug
# ------------------------------------------------------------------------------------------------------------------
def add_file_to_zip(self, filename, data, path=""):
# Les interclassements
# --------------------
if pe_tools.PE_DEBUG:
pe_tools.pe_print(
"*** Création des interclassements au sein de la promo sur différentes combinaisons de semestres"
)
if False:
self.get_promotags_in_jury()
def add_file_to_zip(self, filename: str, data, path=""):
"""Add a file to our zip
All files under NOM_EXPORT_ZIP/
path may specify a subdirectory
Args:
filename: Le nom du fichier à intégrer au zip
data: Les données du fichier
path: Un dossier dans l'arborescence du zip
"""
path_in_zip = os.path.join(self.nom_export_zip, path, filename)
self.zipfile.writestr(path_in_zip, data)
# ------------------------------------------------------------------------------------------------------------------
def get_zipped_data(self):
"""returns file-like data with a zip of all generated (CSV) files.
Reset file cursor at the beginning !
@ -189,191 +225,26 @@ class JuryPE(object):
self.zipdata.seek(0)
return self.zipdata
# **************************************************************************************************************** #
# Lancement des différentes actions permettant le calcul du jury PE
# **************************************************************************************************************** #
def exe_calculs_juryPE(self):
"""Centralise les élements de calcul des moyennes de poursuites
d'études
"""
"""Création des semestres taggués, de type 'S1', 'S2', ..."""
pe_tools.pe_print("*** Création des semestres taggués")
formsemestres = self.etudiants.get_formsemestres_jury(
semestres_recherches=pe_tools.TOUS_LES_SEMESTRES
)
for frmsem_id, formsemestre in formsemestres.items():
"""Choix d'un nom pour le semestretag"""
nom = "S%d %d %d-%d" % (
formsemestre.semestre_id,
formsemestre.formsemestre_id,
formsemestre.date_debut.year,
formsemestre.date_fin.year,
)
pe_tools.pe_print(
f" --> Semestre taggué {nom} sur la base de {formsemestre}"
)
self.add_semestretag_in_jury(nom, frmsem_id)
# Les moyennes sur toute la scolarité
# -----------------------------------
if pe_tools.PE_DEBUG:
pe_tools.pe_print(
"*** Création des moyennes sur différentes combinaisons de semestres et différents groupes d'étudiant"
)
if False:
self.get_settags_in_jury()
if pe_tools.PE_DEBUG:
for settagdict in self.setTagDict.values(): # Export
for settag in settagdict.values():
filename = self.nom_export_zip + semtag.nom + ".csv"
self.add_file_to_zip(
filename, semtag.str_tagtable(), path="details_semestres"
)
# self.export_juryPEDict()
# Les interclassements
# --------------------
if pe_tools.PE_DEBUG:
pe_tools.pe_print(
"*** Création des interclassements au sein de la promo sur différentes combinaisons de semestres"
)
if False:
self.get_promotags_in_jury()
# **************************************************************************************************************** #
# Traitements des semestres impliqués dans le jury
# **************************************************************************************************************** #
# ------------------------------------------------------------------------------------------------------------------
def add_semestretag_in_jury(self, nom: str, formsemestre_id: int):
"""Ajoute (après création si nécessaire) un semtag dans `self.semTag` et
charge également les données des étudiants (découverts avec ce semestre).
Args:
nom: Le nom à donner au SemestrreTag
formsemestre_id: L'identifiant d'un FormSemestre
"""
if formsemestre_id in self.semTagDict:
return
"""Créé le SemestreTag et exécute les calculs de moyennes"""
formsemestretag = pe_semestretag.SemestreTag(nom, formsemestre_id)
self.semTagDict[formsemestre_id] = formsemestretag
if pe_tools.PE_DEBUG:
filename = nom.replace(" ", "_") + ".csv"
pe_tools.pe_print(f" - Export csv de {filename} ")
self.zipfile.writestr(filename, formsemestretag.str_tagtable())
# **************************************************************************************************************** #
# Traitements des parcours impliquées dans le jury
# **************************************************************************************************************** #
# # ----------------------------------------------------------------------------------------------------------------
# def get_antags_in_jury(self, avec_affichage_debug=True ):
# """Construit les settag associés aux années 1A et 2A du jury"""
# lesAnnees = {'1A' : ['S1', 'S2'], '2A' : ['S3', 'S4'] }
# for nom_annee in lesAnnees:
# lesAidDesAnnees = self.get_anneeids_du_jury(annee= nom_annee) # les annee_ids des étudiants du jury
# for aid in lesAidDesAnnees:
# fidSemTagFinal = JuryPE.convert_aid_en_fid( aid )
# lesEtudisDelAnnee = self.semTagDict[ fidSemTagFinal ].get_etudids() # les etudiants sont ceux inscrits dans le semestre final de l'année
# parcoursDesEtudiants = { etudid : self.PARCOURSINFO_DICT[etudid] for etudid in lesEtudisDelAnnee } # les parcours des etudid aka quels semestres sont à prendre en compte
#
# lesFidsDesEtudiants = self.get_formsemestreids_du_jury(lesEtudisDelAnnee, nom_annee) # les formsemestres_id à prendre en compte pour les moyennes
# # Manque-t-il des semtag associés ; si oui, les créé
# pe_tools.pe_print(aid, lesFidsDesEtudiants)
# for fid in lesFidsDesEtudiants:
# self.add_semtags_in_jury(fid, avec_affichage_debug=avec_affichage_debug)
# lesSemTagDesEtudiants = { fid: self.semTagDict[fid] for fid in lesFidsDesEtudiants }
#
# # Tous les semtag nécessaires pour ses étudiants avec ajout éventuel s'ils n'ont pas été chargés
# pe_tools.pe_print(" -> Création de l'année tagguée " + str( aid ))
# #settag_id, short_name, listeEtudId, groupe, listeSemAAggreger, ParcoursEtudDict, SemTagDict, with_comp_moy=True)
# self.anTagDict[ aid ] = pe_settag.SetTag( aid, "Annee " + self.semTagDict[fidSemTagFinal].short_name, \
# lesEtudisDelAnnee, 'groupe', lesAnnees[ nom_annee ], parcoursDesEtudiants, lesSemTagDesEtudiants )
# self.anTagDict[ aid ].comp_data_settag() # calcul les moyennes
# **************************************************************************************************************** #
# Traitements des moyennes sur différentes combinaisons de parcours 1A, 2A, 3S et 4S,
# impliquées dans le jury
# **************************************************************************************************************** #
def get_settags_in_jury(self):
"""Calcule les moyennes sur la totalité du parcours (S1 jusqu'à S3 ou S4)
en classant les étudiants au sein du semestre final du parcours (même S3, même S4, ...)
"""
# Par groupe :
# combinaisons = { 'S1' : ['S1'], 'S2' : ['S2'], 'S3' : ['S3'], 'S4' : ['S4'], \
# '1A' : ['S1', 'S2'], '2A' : ['S3', 'S4'],
# '3S' : ['S1', 'S2', 'S3'], '4S' : ['S1', 'S2', 'S3', 'S4'] }
# ---> sur 2 parcours DUT (cas S3 fini, cas S4 fini)
for i, nom in enumerate(pe_tools.TOUS_LES_AGGREGATS):
parcours = pe_tools.PARCOURS[nom][
"aggregat"
] # La liste des noms de semestres (S1, S2, ...) impliqués dans l'aggrégat
# Recherche des parcours possibles par le biais de leur Fid final
fids_finaux = self.get_formsemestreids_du_jury(
self.etudiants.get_etudids(self.diplome), nom
) # les formsemestre_ids validant finaux des étudiants du jury
if len(fids_finaux) > 0: # S'il existe des parcours validant
pe_tools.pe_print("%d) Fusion %s avec" % (i + 1, nom))
if nom not in self.setTagDict:
self.setTagDict[nom] = {}
for fid in fids_finaux:
pe_tools.pe_print(" - semestre final %s" % (fid))
settag = pe_settag.SetTag(
nom, parcours=parcours
) # Le set tag fusionnant les données
etudiants = self.semTagDict[
fid
].get_etudids() # Les étudiants du sem final
# ajoute les étudiants au semestre
settag.set_Etudiants(
etudiants,
self.etudiants.cursus,
self.etudiants.identites,
nom_sem_final=self.semTagDict[fid].nom,
)
# manque-t-il des semestres ? Si oui, les ajoute au jurype puis au settag
for ffid in settag.get_Fids_in_settag():
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 1:
pe_tools.pe_print(
" -> ajout du semestre tagué %s" % (ffid)
)
self.add_semestretag_in_jury(ffid)
settag.set_SemTagDict(
self.semTagDict
) # ajoute les semestres au settag
settag.comp_data_settag() # Calcul les moyennes, les rangs, ..
self.setTagDict[nom][fid] = settag # Mémorise le résultat
else:
pe_tools.pe_print("%d) Pas de fusion %s possible" % (i + 1, nom))
def get_promotags_in_jury(self):
"""Calcule les aggrégats en interclassant les étudiants du jury (les moyennes ont déjà été calculées en amont)"""
"""Interclasse les étudiants, (nom d') aggrégat par aggrégat,
pour fournir un classement sur la promo.
"""
lesEtudids = self.etudiants.get_etudids(self.diplome)
for i, nom in enumerate(pe_tools.PARCOURS.keys()):
settag = pe_settag.SetTagInterClasse(nom, diplome=self.diplome)
settag = app.pe.pe_settag_interclasse.SetTagInterClasse(
nom, diplome=self.diplome
)
nbreEtudInscrits = settag.set_Etudiants(
lesEtudids, self.etudiants.cursus, self.etudiants.identites
)
@ -383,9 +254,9 @@ class JuryPE(object):
"%d) %s avec interclassement sur la promo" % (i + 1, nom)
)
if nom in pe_tools.TOUS_LES_SEMESTRES:
settag.set_SetTagDict(self.semTagDict)
settag.set_SetTagDict(self.semestres_taggues)
else: # cas des aggrégats
settag.set_SetTagDict(self.setTagDict[nom])
settag.set_SetTagDict(self.aggregats_taggues[nom])
settag.comp_data_settag()
self.promoTagDict[nom] = settag
else:
@ -435,9 +306,11 @@ class JuryPE(object):
self.etudiants.cursus[etudid][nom] != None
): # Un parcours valide existe
if nom in pe_tools.TOUS_LES_SEMESTRES:
tagtable = self.semTagDict[self.etudiants.cursus[etudid][nom]]
tagtable = self.semestres_taggues[
self.etudiants.cursus[etudid][nom]
]
else:
tagtable = self.setTagDict[nom][
tagtable = self.aggregats_taggues[nom][
self.etudiants.cursus[etudid][nom]
]
for tag in tagtable.get_all_tags():
@ -467,7 +340,7 @@ class JuryPE(object):
def get_parcoursIUT(self, etudid):
"""Renvoie une liste d'infos sur les semestres du parcours d'un étudiant"""
# etudinfo = self.ETUDINFO_DICT[etudid]
sems = pe_etudiants.semestres_etudiant(etudid)
sems = self.etudiants.semestres_etudiant(etudid)
infos = []
for sem in sems:
@ -505,8 +378,8 @@ class JuryPE(object):
# les semestres et les aggrégats
for nom_sem in pe_tools.TOUS_LES_PARCOURS:
table = (
self.semTagDict[donnees[nom_sem]].nom
if donnees[nom_sem] in self.semTagDict
self.semestres_taggues[donnees[nom_sem]].nom
if donnees[nom_sem] in self.semestres_taggues
else "manquant"
)
descr += [
@ -746,7 +619,7 @@ class JuryPE(object):
semtagid = self.etudiants.cursus[etudid][
nom_sem
] # le formsemestre_id du semestre taggué de l'étudiant
semtag = self.semTagDict[semtagid]
semtag = self.semestres_taggues[semtagid]
chaine += "Semestre " + nom_sem + str(semtagid) + "\n"
# le détail du calcul tag par tag
# chaine += "Détail du calcul du tag\n"
@ -772,3 +645,112 @@ class JuryPE(object):
if annees_debut:
return str(min(annees_debut))
return ""
def compute_semestres_tag(etudiants: EtudiantsJuryPE):
"""Créé les semestres taggués, de type 'S1', 'S2', ..., pour un groupe d'étudiants donnés.
Chaque semestre taggué est rattaché à l'un des FormSemestre faisant partie du cursus scolaire
des étudiants (cf. attribut etudiants.cursus).
En crééant le semestre taggué, sont calculées les moyennes/classements par tag associé.
.
Args:
etudiants: Un groupe d'étudiants participant au jury
Returns:
Un dictionnaire {fid: SemestreTag(fid)}
"""
"""Création des semestres taggués, de type 'S1', 'S2', ..."""
pe_tools.pe_print("*** Création des semestres taggués")
formsemestres = etudiants.get_formsemestres_jury(
semestres_recherches=pe_tools.TOUS_LES_SEMESTRES
)
semestres_tags = {}
for frmsem_id, formsemestre in formsemestres.items():
"""Choix d'un nom pour le semestretag"""
nom = "S%d %d %d-%d" % (
formsemestre.semestre_id,
frmsem_id,
formsemestre.date_debut.year,
formsemestre.date_fin.year,
)
pe_tools.pe_print(f" --> Semestre taggué {nom} sur la base de {formsemestre}")
"""Créé le semestre_tag et exécute les calculs de moyennes"""
formsemestretag = pe_semestretag.SemestreTag(nom, frmsem_id)
"""Stocke le semestre taggué"""
semestres_tags[frmsem_id] = formsemestretag
return semestres_tags
def compute_aggregats_tag(
self, etudiants: EtudiantsJuryPE, semestres_tag: dict[SemestreTag]
):
"""Créé les combinaisons de semestres (aggrégat), en calculant les moyennes et les
classements par tag pour chacune. Chaque combinaison (aggrégat) est identifiée
par un formsemestre terminal.
Par exemple :
* combinaisons '3S' : S1+S2+S3 en prenant en compte tous les S3 qu'ont fréquentés les
étudiants du jury PE. Ces S3 marquent les formsemestre terminal de chaque combinaison.
Args:
etudiants: Les données des étudiants
semestres_tag: Les semestres tag (pour lesquels des moyennes par tag ont été calculés)
Return:
Un dictionnaire de la forme {nom_aggregat: {fid_terminal: SetTag(fid_terminal)} }
"""
pe_tools.pe_print(" *** Création des aggrégats ")
sets_tags = {}
for aggregat in pe_tools.TOUS_LES_AGGREGATS:
sets_tags[aggregat] = {}
"""Semestres aggrégés"""
noms_semestres_aggreges = pe_tools.PARCOURS[aggregat]["aggregat"]
nom_semestre_terminal = noms_semestres_aggreges[-1]
pe_tools.pe_print(f"* {aggregat}: " + "+".join(noms_semestres_aggreges))
"""Les formsemestres terminaux des aggrégats"""
formsemestres_terminal = etudiants.get_formsemestres_terminaux_aggregat(
aggregat
)
for frmsem_id in formsemestres_terminal:
formsemestre_terminal = formsemestres_terminal[frmsem_id]
"""Nom du set_tag"""
nom = "Aggrégat S%d %d %d-%d" % (
formsemestre_terminal.semestre_id,
frmsem_id,
formsemestre_terminal.date_debut.year,
formsemestre_terminal.date_fin.year,
)
"""Semestres à aggreger dans l'aggrégat ayant amené des étudiants jusqu'au formsemestre_terminal"""
semestres_aggreges = etudiants.get_semestres_a_aggreger(aggregat, frmsem_id)
pe_tools.pe_print(" --> Fusion de :")
for fid in semestres_aggreges:
pe_tools.pe_print(str(semestres_aggreges[fid]))
"""Création du settag associé"""
settag = pe_settag.SetTag(
nom, formsemestre_terminal, semestres_aggreges, semestres_tag, etudiants
)
settag.compute_notes_cube() # Calcul les moyennes, les rangs, ..
sets_tags[aggregat][fid] = settag # Mémorise le résultat
return sets_tags

View File

@ -95,10 +95,8 @@ class SemestreTag(pe_tagtable.TableTag):
self.ues_inscr_parcours_df = self.nt.load_ues_inscr_parcours()
self.dispense_ues = self.nt.dispense_ues
"""Les tags"""
"""Les tags (en supprimant les tags réservés)"""
self.tags = get_synthese_tags_semestre(self.nt.formsemestre)
"""Supprime les tags réservés"""
for tag in pe_tagtable.TAGS_RESERVES:
if tag in self.tags:
del self.tags[tag]
@ -132,6 +130,10 @@ class SemestreTag(pe_tagtable.TableTag):
"nb_inscrits": len(moy_gen_but),
}
"""Synthétise l'ensemble des moyennes dans un dataframe"""
self.tags_sorted = sorted(self.moyennes_tags) # les tags par ordre alphabétique
self.notes = self.df_tagtable() # Le dataframe synthétique des notes (=moyennes par tag)
# -----------------------------------------------------------------------------
def get_etudids(self):
"""Renvoie la liste des etud_id des étudiants inscrits au semestre"""

View File

@ -35,9 +35,16 @@ Created on Fri Sep 9 09:15:05 2016
@author: barasc
"""
from app.pe.pe_tools import pe_print, PE_DEBUG
from app.comp import moy_sem
from app.comp.res_sem import load_formsemestre_results
from app.models import FormSemestre
from app.pe.pe_semestretag import SemestreTag
from app.pe import pe_tagtable
import pandas as pd
import numpy as np
from app.pe.pe_etudiant import EtudiantsJuryPE
class SetTag(pe_tagtable.TableTag):
@ -50,275 +57,152 @@ class SetTag(pe_tagtable.TableTag):
un etudiant non inscrit dans un S1 mais dans un S2 et un S3 n'est pas pris en compte).
"""
# -------------------------------------------------------------------------------------------------------------------
def __init__(self, nom_combinaison, parcours):
pe_tagtable.TableTag.__init__(self, nom=nom_combinaison)
self.combinaison = nom_combinaison
self.parcours = parcours # Le groupe de semestres/parcours à aggréger
# -------------------------------------------------------------------------------------------
def set_Etudiants(
self, etudiants: list[dict], juryPEDict, etudInfoDict, nom_sem_final=None
def __init__(
self,
nom,
formsemestre_terminal: FormSemestre,
semestres_aggreges: dict[int, FormSemestre],
semestres_taggues: dict[int, SemestreTag],
donnees_etudiants: EtudiantsJuryPE,
):
"""Détermine la liste des étudiants à prendre en compte, en partant de
la liste en paramètre et en vérifiant qu'ils ont tous un parcours valide."""
if nom_sem_final:
self.nom += "_" + nom_sem_final
for etudid in etudiants:
parcours_incomplet = (
sum([juryPEDict[etudid][nom_sem] is None for nom_sem in self.parcours])
> 0
) # manque-t-il des formsemestre_id validant aka l'étudiant n'a pas été inscrit dans tous les semestres de l'aggrégat
if not parcours_incomplet:
self.inscrlist.append(etudInfoDict[etudid])
self.identdict[etudid] = etudInfoDict[etudid]
pe_tagtable.TableTag.__init__(self, nom)
delta = len(etudiants) - len(self.inscrlist)
if delta > 0:
pe_print(self.nom + " -> " + str(delta) + " étudiants supprimés")
"""Le formsemestre terminal et les semestres aggrégés"""
self.formsemestre_terminal = formsemestre_terminal
nt = load_formsemestre_results(formsemestre_terminal)
self.semestres_aggreges = semestres_aggreges
# Le sous-ensemble des parcours
self.parcoursDict = {etudid: juryPEDict[etudid] for etudid in self.identdict}
# -------------------------------------------------------------------------------------------
def get_Fids_in_settag(self):
"""Renvoie la liste des semestres (leur formsemestre_id) à prendre en compte
pour le calcul des moyennes, en considérant tous les étudiants inscrits et
tous les semestres de leur parcours"""
return list(
{
self.parcoursDict[etudid][nom_sem]
for etudid in self.identdict
for nom_sem in self.parcours
"""Les semestres tags associés aux semestres aggrégés"""
try:
self.semestres_tags_aggreges = {
frmsem_id: semestres_taggues[frmsem_id]
for frmsem_id in semestres_taggues
}
)
except:
raise ValueError("Semestres taggués manquants")
# ---------------------------------------------------------------------------------------------
def set_SemTagDict(self, SemTagDict):
"""Mémorise les semtag nécessaires au jury."""
self.SemTagDict = {fid: SemTagDict[fid] for fid in self.get_Fids_in_settag()}
if PE_DEBUG >= 1:
pe_print(" => %d semestres fusionnés" % len(self.SemTagDict))
# -------------------------------------------------------------------------------------------------------------------
def comp_data_settag(self):
"""Calcule tous les données numériques relatives au settag"""
# Attributs relatifs aux tag pour les modules pris en compte
self.taglist = self.do_taglist() # la liste des tags
self.do_tagdict() # le dico descriptif des tags
# if PE_DEBUG >= 1: pe_print(" => Tags = " + ", ".join( self.taglist ))
# Calcul des moyennes de chaque étudiant par tag
reussiteAjoutTag = {"OK": [], "KO": []}
for tag in self.taglist:
moyennes = self.comp_MoyennesSetTag(tag, force=False)
res = self.add_moyennesTag(tag, moyennes) # pas de notes => pas de moyenne
reussiteAjoutTag["OK" if res else "KO"].append(tag)
if len(reussiteAjoutTag["OK"]) > 0 and PE_DEBUG:
pe_print(
" => Fusion de %d tags : " % (len(reussiteAjoutTag["OK"]))
+ ", ".join(reussiteAjoutTag["OK"])
)
if len(reussiteAjoutTag["KO"]) > 0 and PE_DEBUG:
pe_print(
" => %d tags manquants : " % (len(reussiteAjoutTag["KO"]))
+ ", ".join(reussiteAjoutTag["KO"])
)
# -------------------------------------------------------------------------------------------------------------------
def get_etudids(self):
return list(self.identdict.keys())
# -------------------------------------------------------------------------------------------------------------------
def do_taglist(self):
"""Parcourt les tags des semestres taggués et les synthétise sous la forme
d'une liste en supprimant les doublons
"""
ensemble = []
for semtag in self.SemTagDict.values():
ensemble.extend(semtag.get_all_tags())
return sorted(list(set(ensemble)))
# -------------------------------------------------------------------------------------------------------------------
def do_tagdict(self):
"""Synthétise la liste des modules pris en compte dans le calcul d'un tag (pour analyse des résultats)"""
self.tagdict = {}
for semtag in self.SemTagDict.values():
for tag in semtag.get_all_tags():
if tag != "but":
if tag not in self.tagdict:
self.tagdict[tag] = {}
for mod in semtag.tagdict[tag]:
self.tagdict[tag][mod] = semtag.tagdict[tag][mod]
# -------------------------------------------------------------------------------------------------------------------
def get_NotesEtCoeffsSetTagEtudiant(self, tag, etudid):
"""Récupère tous les notes et les coeffs d'un étudiant relatives à un tag dans ses semestres valides et les renvoie dans un tuple (notes, coeffs)
avec notes et coeffs deux listes"""
lesSemsDeLEtudiant = [
self.parcoursDict[etudid][nom_sem] for nom_sem in self.parcours
] # peuvent être None
notes = [
self.SemTagDict[fid].get_moy_from_resultats(tag, etudid)
for fid in lesSemsDeLEtudiant
if tag in self.SemTagDict[fid].taglist
] # eventuellement None
coeffs = [
self.SemTagDict[fid].get_coeff_from_resultats(tag, etudid)
for fid in lesSemsDeLEtudiant
if tag in self.SemTagDict[fid].taglist
]
return (notes, coeffs)
# -------------------------------------------------------------------------------------------------------------------
def comp_MoyennesSetTag(self, tag, force=False):
"""Calcule et renvoie les "moyennes" des étudiants à un tag donné, en prenant en compte tous les semestres taggués
de l'aggrégat, et leur coeff Par moyenne, s'entend une note moyenne, la somme des coefficients de pondération
appliqué dans cette moyenne.
Force ou non le calcul de la moyenne lorsque des notes sont manquantes.
Renvoie les informations sous la forme d'une liste [etudid: (moy, somme_coeff_normalisée, rang), ...}
"""
# if tag not in self.get_all_tags() : return None
# Calcule les moyennes
lesMoyennes = []
for (
etudid
) in (
self.get_etudids()
): # Pour tous les étudiants non défaillants du semestre inscrits dans des modules relatifs au tag
(notes, coeffs_norm) = self.get_NotesEtCoeffsSetTagEtudiant(
tag, etudid
) # lecture des notes associées au tag
(moyenne, somme_coeffs) = pe_tagtable.moyenne_ponderee_terme_a_terme(
notes, coeffs_norm, force=force
)
lesMoyennes += [
(moyenne, somme_coeffs, etudid)
] # Un tuple (pour classement résumant les données)
return lesMoyennes
class SetTagInterClasse(pe_tagtable.TableTag):
"""Récupère les moyennes de SetTag aggrégeant un même parcours (par ex un ['S1', 'S2'] n'ayant pas fini au même S2
pour fournir un interclassement sur un groupe d'étudiant => seul compte alors la promo
nom_combinaison = 'S1' ou '1A'
"""
# -------------------------------------------------------------------------------------------------------------------
def __init__(self, nom_combinaison, diplome):
pe_tagtable.TableTag.__init__(self, nom=f"{nom_combinaison}_{diplome or ''}")
self.combinaison = nom_combinaison
self.parcoursDict = {}
# -------------------------------------------------------------------------------------------
def set_Etudiants(self, etudiants, juryPEDict, etudInfoDict, nom_sem_final=None):
"""Détermine la liste des étudiants à prendre en compte, en partant de
la liste fournie en paramètre et en vérifiant que l'étudiant dispose bien d'un parcours valide pour la combinaison demandée.
Renvoie le nombre d'étudiants effectivement inscrits."""
if nom_sem_final:
self.nom += "_" + nom_sem_final
for etudid in etudiants:
if juryPEDict[etudid][self.combinaison] != None:
self.inscrlist.append(etudInfoDict[etudid])
self.identdict[etudid] = etudInfoDict[etudid]
self.parcoursDict[etudid] = juryPEDict[etudid]
return len(self.inscrlist)
# -------------------------------------------------------------------------------------------
def get_Fids_in_settag(self):
"""Renvoie la liste des semestres (les formsemestre_id finissant la combinaison par ex. '3S' dont les fid des S3) à prendre en compte
pour les moyennes, en considérant tous les étudiants inscrits"""
return list(
{self.parcoursDict[etudid][self.combinaison] for etudid in self.identdict}
)
# ---------------------------------------------------------------------------------------------
def set_SetTagDict(self, SetTagDict):
"""Mémorise les settag nécessaires au jury."""
self.SetTagDict = {
fid: SetTagDict[fid] for fid in self.get_Fids_in_settag() if fid != None
"""Les étudiants (état civil + cursus connu)"""
self.etuds = nt.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
}
if PE_DEBUG >= 1:
pe_print(" => %d semestres utilisés" % len(self.SetTagDict))
# -------------------------------------------------------------------------------------------------------------------
def comp_data_settag(self):
"""Calcule tous les données numériques relatives au settag"""
# Attributs relatifs aux tag pour les modules pris en compte
self.taglist = self.do_taglist()
"""Les tags extraits de tous les semestres"""
self.tags_sorted = self.do_taglist()
# if PE_DEBUG >= 1: pe_print(" => Tags = " + ", ".join( self.taglist ))
"""Construit le cube de notes"""
self.notes_cube = self.compute_notes_cube()
# Calcul des moyennes de chaque étudiant par tag
reussiteAjoutTag = {"OK": [], "KO": []}
for tag in self.taglist:
moyennes = self.get_MoyennesSetTag(tag, force=False)
res = self.add_moyennesTag(tag, moyennes) # pas de notes => pas de moyenne
reussiteAjoutTag["OK" if res else "KO"].append(tag)
if len(reussiteAjoutTag["OK"]) > 0 and PE_DEBUG:
pe_print(
" => Interclassement de %d tags : " % (len(reussiteAjoutTag["OK"]))
+ ", ".join(reussiteAjoutTag["OK"])
)
if len(reussiteAjoutTag["KO"]) > 0 and PE_DEBUG:
pe_print(
" => %d tags manquants : " % (len(reussiteAjoutTag["KO"]))
+ ", ".join(reussiteAjoutTag["KO"])
)
"""Calcul les moyennes par tag sous forme d'un dataframe"""
etudids = self.get_etudids()
self.notes = compute_tag_moy(self.notes_cube, etudids, self.tags_sorted)
"""Synthétise les moyennes/classements par tag"""
self.moyennes_tags = {}
for tag in self.tags_sorted:
moy_gen_tag = self.notes[tag]
class_gen_tag = moy_sem.comp_ranks_series(moy_gen_tag)[1] # en int
self.moyennes_tags[tag] = {
"notes": moy_gen_tag,
"classements": class_gen_tag,
"min": moy_gen_tag.min(),
"max": moy_gen_tag.max(),
"moy": moy_gen_tag.mean(),
"nb_inscrits": len(moy_gen_tag),
}
def compute_notes_cube(self):
"""Construit le cube de notes (etudid x tags x semestre_aggregé)
nécessaire au calcul des moyennes de l'aggrégat
"""
nb_tags = len(self.tags_sorted)
nb_etudiants = len(self.etuds)
nb_semestres = len(self.semestres_tags_aggreges)
"""Index du cube (etudids -> dim 0, tags -> dim 1)"""
etudids = [etud.etudid for etud in self.etuds]
tags = self.tags_sorted
semestres_id = list(self.semestres_tags_aggreges.keys())
dfs = {}
for frmsem_id in semestres_id:
"""Partant d'un dataframe vierge"""
df = pd.DataFrame(np.nan, index=etudids, columns=tags)
"""Charge les notes du semestre tag"""
notes = self.semestres_tags_aggreges[frmsem_id].notes
"""Les étudiants & les tags commun au dataframe final et aux notes du semestre)"""
etudids_communs = df.index.intersection(notes.index)
tags_communs = df.columns.intersection(notes.columns)
"""Injecte les notes par tag"""
df.loc[etudids_communs, tags_communs] = notes.loc[
etudids_communs, tags_communs
]
"""Stocke le df"""
dfs[frmsem_id] = df
"""Réunit les notes sous forme d'un cube etdids x tags x semestres"""
semestres_x_etudids_x_tags = [dfs[fid].values for fid in dfs]
etudids_x_tags_x_semestres = np.stack(semestres_x_etudids_x_tags, axis=-1)
return etudids_x_tags_x_semestres
# -------------------------------------------------------------------------------------------------------------------
def get_etudids(self):
return list(self.identdict.keys())
return list(self.etudiants.keys())
# -------------------------------------------------------------------------------------------------------------------
def do_taglist(self):
"""Parcourt les tags des semestres taggués et les synthétise sous la forme
d'une liste en supprimant les doublons
"""Synthétise les tags à partir des semestres (taggués) aggrégés
Returns:
Une liste de tags triés par ordre alphabétique
"""
ensemble = []
for settag in self.SetTagDict.values():
ensemble.extend(settag.get_all_tags())
return sorted(list(set(ensemble)))
tags = []
for frmsem_id in self.semestres_tags_aggreges:
tags.extend(self.semestres_tags_aggreges[frmsem_id].tags_sorted)
return sorted(set(tags))
# -------------------------------------------------------------------------------------------------------------------
def get_NotesEtCoeffsSetTagEtudiant(self, tag, etudid):
"""Récupère tous les notes et les coeffs d'un étudiant relatives à un tag dans ses semestres valides et les renvoie dans un tuple (notes, coeffs)
avec notes et coeffs deux listes"""
leSetTagDeLetudiant = self.parcoursDict[etudid][self.combinaison]
note = self.SetTagDict[leSetTagDeLetudiant].get_moy_from_resultats(tag, etudid)
coeff = self.SetTagDict[leSetTagDeLetudiant].get_coeff_from_resultats(
tag, etudid
)
return (note, coeff)
def compute_tag_moy(set_cube: np.array, etudids: list, tags: list):
"""Calcul de la moyenne par tag sur plusieurs semestres.
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles
# -------------------------------------------------------------------------------------------------------------------
def get_MoyennesSetTag(self, tag, force=False):
"""Renvoie les "moyennes" des étudiants à un tag donné, en prenant en compte tous les settag de l'aggrégat,
et leur coeff Par moyenne, s'entend une note moyenne, la somme des coefficients de pondération
appliqué dans cette moyenne.
*Remarque* : Adaptation de moy_ue.compute_ue_moys_apc au cas des moyennes de tag
par aggrégat de plusieurs semestres.
Force ou non le calcul de la moyenne lorsque des notes sont manquantes.
Args:
set_cube: notes moyennes aux modules ndarray
(etuds x modimpls x UEs), des floats avec des NaN
etudids: liste des étudiants (dim. 0 du cube)
tags: liste des tags (dim. 1 du cube)
Returns:
Un DataFrame avec pour columns les moyennes par tags,
et pour rows les etudid
"""
nb_etuds, nb_tags, nb_semestres = set_cube.shape
assert nb_etuds == len(etudids)
assert nb_tags == len(tags)
Renvoie les informations sous la forme d'une liste [etudid: (moy, somme_coeff_normalisée, rang), ...}
"""
# if tag not in self.get_all_tags() : return None
# Quelles entrées du cube contiennent des notes ?
mask = ~np.isnan(set_cube)
# Calcule les moyennes
lesMoyennes = []
for (
etudid
) in (
self.get_etudids()
): # Pour tous les étudiants non défaillants du semestre inscrits dans des modules relatifs au tag
(moyenne, somme_coeffs) = self.get_NotesEtCoeffsSetTagEtudiant(
tag, etudid
) # lecture des notes associées au tag
lesMoyennes += [
(moyenne, somme_coeffs, etudid)
] # Un tuple (pour classement résumant les données)
return lesMoyennes
# Enlève les NaN du cube pour les entrées manquantes
set_cube_no_nan = np.nan_to_num(set_cube, nan=0.0)
# Les moyennes par tag
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
etud_moy_tag = np.sum(set_cube_no_nan, axis=2) / np.sum(mask, axis=2)
# Le dataFrame
etud_moy_tag_df = pd.DataFrame(
etud_moy_tag,
index=etudids, # les etudids
columns=tags, # les tags
)
return etud_moy_tag_df

View File

@ -0,0 +1,125 @@
from app.pe import pe_tagtable
from app.pe.pe_tools import PE_DEBUG, pe_print
class SetTagInterClasse(pe_tagtable.TableTag):
"""Récupère les moyennes de SetTag aggrégeant un même parcours (par ex un ['S1', 'S2']
n'ayant pas fini au même S2
pour fournir un interclassement sur un groupe d'étudiant => seul compte alors la promo
nom_combinaison = 'S1' ou '1A'
"""
# -------------------------------------------------------------------------------------------------------------------
def __init__(self, nom_combinaison, diplome):
pe_tagtable.TableTag.__init__(self, nom=f"{nom_combinaison}_{diplome or ''}")
self.combinaison = nom_combinaison
self.parcoursDict = {}
# -------------------------------------------------------------------------------------------
def set_Etudiants(self, etudiants, juryPEDict, etudInfoDict, nom_sem_final=None):
"""Détermine la liste des étudiants à prendre en compte, en partant de
la liste fournie en paramètre et en vérifiant que l'étudiant dispose bien d'un parcours valide pour la combinaison demandée.
Renvoie le nombre d'étudiants effectivement inscrits."""
if nom_sem_final:
self.nom += "_" + nom_sem_final
for etudid in etudiants:
if juryPEDict[etudid][self.combinaison] != None:
self.inscrlist.append(etudInfoDict[etudid])
self.identdict[etudid] = etudInfoDict[etudid]
self.parcoursDict[etudid] = juryPEDict[etudid]
return len(self.inscrlist)
# -------------------------------------------------------------------------------------------
def get_Fids_in_settag(self):
"""Renvoie la liste des semestres (les formsemestre_id finissant la combinaison par ex. '3S' dont les fid des S3) à prendre en compte
pour les moyennes, en considérant tous les étudiants inscrits"""
return list(
{self.parcoursDict[etudid][self.combinaison] for etudid in self.identdict}
)
# ---------------------------------------------------------------------------------------------
def set_SetTagDict(self, SetTagDict):
"""Mémorise les settag nécessaires au jury."""
self.SetTagDict = {
fid: SetTagDict[fid] for fid in self.get_Fids_in_settag() if fid != None
}
if PE_DEBUG >= 1:
pe_print(" => %d semestres utilisés" % len(self.SetTagDict))
# -------------------------------------------------------------------------------------------------------------------
def comp_data_settag(self):
"""Calcule tous les données numériques relatives au settag"""
# Attributs relatifs aux tag pour les modules pris en compte
self.taglist = self.do_taglist()
# if PE_DEBUG >= 1: pe_print(" => Tags = " + ", ".join( self.taglist ))
# Calcul des moyennes de chaque étudiant par tag
reussiteAjoutTag = {"OK": [], "KO": []}
for tag in self.taglist:
moyennes = self.get_MoyennesSetTag(tag, force=False)
res = self.add_moyennesTag(tag, moyennes) # pas de notes => pas de moyenne
reussiteAjoutTag["OK" if res else "KO"].append(tag)
if len(reussiteAjoutTag["OK"]) > 0 and PE_DEBUG:
pe_print(
" => Interclassement de %d tags : " % (len(reussiteAjoutTag["OK"]))
+ ", ".join(reussiteAjoutTag["OK"])
)
if len(reussiteAjoutTag["KO"]) > 0 and PE_DEBUG:
pe_print(
" => %d tags manquants : " % (len(reussiteAjoutTag["KO"]))
+ ", ".join(reussiteAjoutTag["KO"])
)
# -------------------------------------------------------------------------------------------------------------------
def get_etudids(self):
return list(self.identdict.keys())
# -------------------------------------------------------------------------------------------------------------------
def do_taglist(self):
"""Parcourt les tags des semestres taggués et les synthétise sous la forme
d'une liste en supprimant les doublons
"""
ensemble = []
for settag in self.SetTagDict.values():
ensemble.extend(settag.get_all_tags())
return sorted(list(set(ensemble)))
# -------------------------------------------------------------------------------------------------------------------
def get_NotesEtCoeffsSetTagEtudiant(self, tag, etudid):
"""Récupère tous les notes et les coeffs d'un étudiant relatives à un tag dans ses semestres valides et les renvoie dans un tuple (notes, coeffs)
avec notes et coeffs deux listes"""
leSetTagDeLetudiant = self.parcoursDict[etudid][self.combinaison]
note = self.SetTagDict[leSetTagDeLetudiant].get_moy_from_resultats(tag, etudid)
coeff = self.SetTagDict[leSetTagDeLetudiant].get_coeff_from_resultats(
tag, etudid
)
return (note, coeff)
# -------------------------------------------------------------------------------------------------------------------
def get_MoyennesSetTag(self, tag, force=False):
"""Renvoie les "moyennes" des étudiants à un tag donné, en prenant en compte tous les settag de l'aggrégat,
et leur coeff Par moyenne, s'entend une note moyenne, la somme des coefficients de pondération
appliqué dans cette moyenne.
Force ou non le calcul de la moyenne lorsque des notes sont manquantes.
Renvoie les informations sous la forme d'une liste [etudid: (moy, somme_coeff_normalisée, rang), ...}
"""
# if tag not in self.get_all_tags() : return None
# Calcule les moyennes
lesMoyennes = []
for (
etudid
) in (
self.get_etudids()
): # Pour tous les étudiants non défaillants du semestre inscrits dans des modules relatifs au tag
(moyenne, somme_coeffs) = self.get_NotesEtCoeffsSetTagEtudiant(
tag, etudid
) # lecture des notes associées au tag
lesMoyennes += [
(moyenne, somme_coeffs, etudid)
] # Un tuple (pour classement résumant les données)
return lesMoyennes

View File

@ -83,8 +83,11 @@ class TableTag(object):
# -----------------------------------------------------------------------------------------------------------
def get_all_tags(self):
"""Renvoie la liste des tags du semestre triée par ordre alphabétique"""
# return self.taglist
"""Liste des tags de la table, triée par ordre alphabétique
Returns:
Liste de tags triés par ordre alphabétique
"""
return sorted(self.moyennes_tags.keys())
@ -270,6 +273,20 @@ class TableTag(object):
str_moytag = classmethod(str_moytag)
# -----------------------------------------------------------------------
def df_tagtable(self):
"""Renvoie un dataframe (etudid x tag) listant toutes les moyennes par tags
Returns:
Un dataframe etudids x tag (avec tag par ordre alphabétique)
"""
tags = self.get_all_tags()
if tags:
dict_series = {tag: self.moyennes_tags[tag]["notes"] for tag in tags}
df = pd.DataFrame(dict_series)
return df
else:
return None
def str_tagtable(self):
"""Renvoie une chaine de caractère listant toutes les moyennes,
les rangs des étudiants pour tous les tags."""

View File

@ -164,6 +164,10 @@ TOUS_LES_AGGREGATS = [cle for cle in PARCOURS.keys() if not cle.startswith("S")]
TOUS_LES_PARCOURS = list(PARCOURS.keys())
# ----------------------------------------------------------------------------------------
def print_semestres_description(sems, avec_affichage_debug=False):
"""Dediee a l'affichage d'un semestre pour debug du module"""