1
0
forked from ScoDoc/ScoDoc

Améliorations diverses

This commit is contained in:
Cléo Baras 2024-02-17 02:35:43 +01:00
parent b8cb592ac9
commit 828c619c74
7 changed files with 259 additions and 307 deletions

View File

@ -5,7 +5,7 @@
# #
# Gestion scolarite IUT # Gestion scolarite IUT
# #
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. # Copyright (c) 1999 - 2024 Emmanuel Viennet. c All rights reserved.
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -59,7 +59,7 @@ class EtudiantsJuryPE:
self.identites: dict[int, Identite] = {} # ex. ETUDINFO_DICT self.identites: dict[int, Identite] = {} # ex. ETUDINFO_DICT
"Les identités des étudiants traités pour le jury" "Les identités des étudiants traités pour le jury"
self.cursus: dict[int, dict] = {} self.trajectoires: dict[int, dict] = {}
"Les cursus (semestres suivis, abandons) des étudiants" "Les cursus (semestres suivis, abandons) des étudiants"
self.trajectoires = {} self.trajectoires = {}
@ -164,7 +164,7 @@ class EtudiantsJuryPE:
""" """
etudids = [ etudids = [
etudid etudid
for etudid, cursus_etud in self.cursus.items() for etudid, cursus_etud in self.trajectoires.items()
if cursus_etud["diplome"] == self.annee_diplome if cursus_etud["diplome"] == self.annee_diplome
and cursus_etud["abandon"] is False and cursus_etud["abandon"] is False
] ]
@ -181,7 +181,7 @@ class EtudiantsJuryPE:
""" """
etudids = [ etudids = [
etudid etudid
for etudid, cursus_etud in self.cursus.items() for etudid, cursus_etud in self.trajectoires.items()
if cursus_etud["diplome"] != self.annee_diplome if cursus_etud["diplome"] != self.annee_diplome
or cursus_etud["abandon"] is True or cursus_etud["abandon"] is True
] ]
@ -225,7 +225,7 @@ class EtudiantsJuryPE:
if formsemestre.formation.is_apc() if formsemestre.formation.is_apc()
} }
self.cursus[etudid] = { self.trajectoires[etudid] = {
"etudid": etudid, # les infos sur l'étudiant "etudid": etudid, # les infos sur l'étudiant
"etat_civil": identite.etat_civil, # Ajout à la table jury "etat_civil": identite.etat_civil, # Ajout à la table jury
"nom": identite.nom, "nom": identite.nom,
@ -241,32 +241,36 @@ class EtudiantsJuryPE:
} }
# Si l'étudiant est succeptible d'être diplomé # Si l'étudiant est succeptible d'être diplomé
if self.cursus[etudid]["diplome"] == self.annee_diplome: if self.trajectoires[etudid]["diplome"] == self.annee_diplome:
# Est-il démissionnaire : charge son dernier semestre pour connaitre son état ? # Est-il démissionnaire : charge son dernier semestre pour connaitre son état ?
dernier_semes_etudiant = formsemestres[0] dernier_semes_etudiant = formsemestres[0]
res = load_formsemestre_results(dernier_semes_etudiant) res = load_formsemestre_results(dernier_semes_etudiant)
etud_etat = res.get_etud_etat(etudid) etud_etat = res.get_etud_etat(etudid)
if etud_etat == scu.DEMISSION: if etud_etat == scu.DEMISSION:
self.cursus[etudid]["abandon"] = True self.trajectoires[etudid]["abandon"] = True
else: else:
# Est-il réorienté ou a-t-il arrêté (volontairement) sa formation ? # Est-il réorienté ou a-t-il arrêté (volontairement) sa formation ?
self.cursus[etudid]["abandon"] = arret_de_formation( self.trajectoires[etudid]["abandon"] = arret_de_formation(
identite, cosemestres identite, cosemestres
) )
def get_semestres_significatifs(self, etudid: int): def get_semestres_significatifs(self, etudid: int):
"""Ensemble des semestres d'un étudiant, qui l'auraient amené à être diplomé """Ensemble des semestres d'un étudiant, qui :
l'année visée (supprime les semestres qui conduisent à une diplomation
postérieure à celle du jury visé) * l'amènent à être diplômé à l'année visée
* l'auraient amené à être diplômé à l'année visée s'il n'avait pas redoublé et sera donc
diplômé plus tard
Supprime les semestres qui conduisent à une diplomation postérieure à celle du jury visé.
Args: Args:
etudid: L'identifiant d'un étudiant etudid: L'identifiant d'un étudiant
Returns: Returns:
Un dictionnaire ``{fid: FormSemestre(fid)`` dans lequel les semestres Un dictionnaire ``{fid: FormSemestre(fid)}`` dans lequel les semestres
amènent à une diplomation avant l'annee de diplomation du jury amènent à une diplômation antérieur à celle de la diplômation visée par le jury jury
""" """
semestres_etudiant = self.cursus[etudid]["formsemestres"] semestres_etudiant = self.trajectoires[etudid]["formsemestres"]
semestres_significatifs = {} semestres_significatifs = {}
for fid in semestres_etudiant: for fid in semestres_etudiant:
semestre = semestres_etudiant[fid] semestre = semestres_etudiant[fid]
@ -281,7 +285,7 @@ class EtudiantsJuryPE:
Cette structuration s'appuie sur les numéros de semestre: pour chaque Si, stocke : Cette structuration s'appuie sur les numéros de semestre: pour chaque Si, stocke :
le dernier semestre (en date) de numéro i qu'il a suivi (1 ou 0 si pas encore suivi). le dernier semestre (en date) de numéro i qu'il a suivi (1 ou 0 si pas encore suivi).
Ce semestre influera les interclassement par semestre dans la promo. Ce semestre influera les interclassements par semestre dans la promo.
""" """
semestres_significatifs = self.get_semestres_significatifs(etudid) semestres_significatifs = self.get_semestres_significatifs(etudid)
@ -293,14 +297,14 @@ class EtudiantsJuryPE:
for fid, sem_sig in semestres_significatifs.items() for fid, sem_sig in semestres_significatifs.items()
if sem_sig.semestre_id == i if sem_sig.semestre_id == i
} }
self.cursus[etudid][f"S{i}"] = semestres_i self.trajectoires[etudid][f"S{i}"] = semestres_i
def get_formsemestres_terminaux_aggregat( def get_formsemestres_finals_des_rcs(
self, aggregat: str self, nom_rcs: str
) -> dict[int, FormSemestre]: ) -> dict[int, FormSemestre]:
"""Pour un aggrégat donné, ensemble des formsemestres terminaux possibles pour l'aggrégat """Pour un nom de RCS donné, ensemble des formsemestres finals possibles
(pour l'aggrégat '3S' incluant S1+S2+S3, a pour semestre terminal S3). pour les RCS. Par ex. un RCS '3S' incluant S1+S2+S3 a pour semestre final un S3.
Ces formsemestres traduisent : Les formsemestres finals obtenus traduisent :
* les différents parcours des étudiants liés par exemple au choix de modalité * 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 (par ex: S1 FI + S2 FI + S3 FI ou S1 FI + S2 FI + S3 UFA), en renvoyant les
@ -311,14 +315,14 @@ class EtudiantsJuryPE:
renvoyant les formsemestre_id du S3 (1ère session) et du S3 (2ème session) renvoyant les formsemestre_id du S3 (1ère session) et du S3 (2ème session)
Args: Args:
aggregat: L'aggrégat nom_rcs: Le nom du RCS (parmi Sx, xA, xS)
Returns: Returns:
Un dictionnaire ``{fid: FormSemestre(fid)}`` Un dictionnaire ``{fid: FormSemestre(fid)}``
""" """
formsemestres_terminaux = {} formsemestres_terminaux = {}
for trajectoire_aggr in self.trajectoires.values(): for trajectoire_aggr in self.trajectoires.values():
trajectoire = trajectoire_aggr[aggregat] trajectoire = trajectoire_aggr[nom_rcs]
if trajectoire: if trajectoire:
# Le semestre terminal de l'étudiant de l'aggrégat # Le semestre terminal de l'étudiant de l'aggrégat
fid = trajectoire.formsemestre_final.formsemestre_id fid = trajectoire.formsemestre_final.formsemestre_id
@ -334,7 +338,7 @@ class EtudiantsJuryPE:
""" """
nbres_semestres = [] nbres_semestres = []
for etudid in etudids: for etudid in etudids:
nbres_semestres.append(self.cursus[etudid]["nb_semestres"]) nbres_semestres.append(self.trajectoires[etudid]["nb_semestres"])
if not nbres_semestres: if not nbres_semestres:
return 0 return 0
return max(nbres_semestres) return max(nbres_semestres)
@ -355,7 +359,7 @@ class EtudiantsJuryPE:
for etudid in etudids: for etudid in etudids:
etudiant = self.identites[etudid] etudiant = self.identites[etudid]
cursus = self.cursus[etudid] cursus = self.trajectoires[etudid]
formsemestres = cursus["formsemestres"] formsemestres = cursus["formsemestres"]
if cursus["diplome"]: if cursus["diplome"]:
@ -442,7 +446,7 @@ def get_annee_diplome(etud: Identite) -> int | None:
def get_semestres_apc(identite: Identite) -> list: def get_semestres_apc(identite: Identite) -> list:
"""Liste des semestres d'un étudiant qui corresponde à une formation APC. """Liste des semestres d'un étudiant qui correspondent à une formation APC.
Args: Args:
identite: L'identité d'un étudiant identite: L'identité d'un étudiant
@ -675,3 +679,19 @@ def nom_semestre_etape(semestre: FormSemestre, avec_fid=False) -> str:
description.append(f"({semestre.formsemestre_id})") description.append(f"({semestre.formsemestre_id})")
return " ".join(description) return " ".join(description)
def convert_trajectoire_to_sxtag_id(trajectoire: dict[int:FormSemestre]) -> (int, int):
"""Partant d'une trajectoire (dictionnaire de la forme {fid: FormSemestre}),
renvoie l'identifiant (rang_sem, fid du semestre_terminal) associé"""
if not trajectoire:
return None
rangs = [formsemestre.semestre_id for formsemestre in trajectoire.values()]
assert rangs == min(rangs), "Les trajectoires doivent être de même rang"
rang = min(rangs)
fid_terminal = list(trajectoire.values())[0].formsemestre_id
for fid, formsemestre in trajectoire.items():
if trajectoire[fid_terminal].date_fin <= formsemestre.date_fin:
fid_terminal = fid
return (rang, fid_terminal)

View File

@ -50,13 +50,13 @@ from zipfile import ZipFile
import numpy as np import numpy as np
import pandas as pd import pandas as pd
from app.pe import pe_semtag from app.pe import pe_sxtag
from app.pe.pe_affichage import NOM_STAT_PROMO, SANS_NOTE, NOM_STAT_GROUPE from app.pe.pe_affichage import NOM_STAT_PROMO, SANS_NOTE, NOM_STAT_GROUPE
import app.pe.pe_affichage as pe_affichage import app.pe.pe_affichage as pe_affichage
from app.pe.pe_etudiant import * # TODO A éviter -> pe_etudiant. from app.pe.pe_etudiant import * # TODO A éviter -> pe_etudiant.
import app.pe.pe_rcs as pe_rcs import app.pe.pe_rcs as pe_rcs
from app.pe.pe_rcstag import RCSTag from app.pe.pe_rcstag import RCSTag
from app.pe.pe_ressemtag import ResSemTag from app.pe.pe_ressemtag import ResSemBUTTag
from app.pe.pe_interclasstag import RCSInterclasseTag from app.pe.pe_interclasstag import RCSInterclasseTag
@ -97,10 +97,10 @@ class JuryPE(object):
pe_affichage.pe_print("*** Aucun étudiant diplômé") pe_affichage.pe_print("*** Aucun étudiant diplômé")
else: else:
self._gen_xls_diplomes(zipfile) self._gen_xls_diplomes(zipfile)
self._gen_rcss()
self._gen_xls_resultats_semestres_taggues(zipfile) self._gen_xls_ressembuttags(zipfile)
self._gen_xls_semestres_taggues(zipfile) self._gen_xls_sxtags(zipfile)
# self._gen_xls_rcss_tags(zipfile) self._gen_xls_rcss_tags(zipfile)
# self._gen_xls_interclassements_rcss(zipfile) # self._gen_xls_interclassements_rcss(zipfile)
# self._gen_xls_synthese_jury_par_tag(zipfile) # self._gen_xls_synthese_jury_par_tag(zipfile)
# self._gen_xls_synthese_par_etudiant(zipfile) # self._gen_xls_synthese_par_etudiant(zipfile)
@ -134,17 +134,26 @@ class JuryPE(object):
path="details", path="details",
) )
def _gen_xls_resultats_semestres_taggues(self, zipfile: ZipFile): def _gen_xls_ressembuttags(self, zipfile: ZipFile):
"""Génère les semestres taggués (avec le calcul des moyennes) pour le jury PE""" """Calcule les moyennes par tag des résultats des Semestres BUT"""
pe_affichage.pe_print("*** Génère les résultats des semestres taggués") pe_affichage.pe_print("*** Génère les ResSemBUTTag (résultats des semestres BUT taggués)")
self.res_sems_tags = compute_resultats_semestres_tag(self.etudiants)
formsemestres = get_formsemestres_etudiants(self.etudiants)
pe_affichage.pe_print(f" --> {len(formsemestres)} résultats de semestres à considérer")
ressembuttags = {}
for frmsem_id, formsemestre in formsemestres.items():
# Crée le semestre_tag et exécute les calculs de moyennes
ressembuttags[frmsem_id] = ResSemBUTTag(formsemestre)
return ressembuttags
# Intègre le bilan des semestres taggués au zip final # Intègre le bilan des semestres taggués au zip final
output = io.BytesIO() output = io.BytesIO()
with pd.ExcelWriter( # pylint: disable=abstract-class-instantiated with pd.ExcelWriter( # pylint: disable=abstract-class-instantiated
output, engine="openpyxl" output, engine="openpyxl"
) as writer: ) as writer:
for res_sem_tag in self.res_sems_tags.values(): for res_sem_tag in self.ressembuttags.values():
onglet = res_sem_tag.get_repr(verbose=False) onglet = res_sem_tag.get_repr(verbose=False)
df = res_sem_tag.df_moyennes_et_classements() df = res_sem_tag.df_moyennes_et_classements()
# écriture dans l'onglet # écriture dans l'onglet
@ -153,14 +162,14 @@ class JuryPE(object):
self.add_file_to_zip( self.add_file_to_zip(
zipfile, zipfile,
f"resultats_semestres_taggues_{self.diplome}.xlsx", f"ResSemBUTTags_{self.diplome}.xlsx",
output.read(), output.read(),
path="details", path="details",
) )
def _gen_rcss(self): def _gen_rcss(self):
"""Génère les RCS (attribut `rcss_jury`), combinaisons de semestres suivis par les étudiants au sens """Génère les RCS (attribut `rcss_jury`), combinaisons de semestres suivis par les étudiants au sens
d'un nom de RCS (par ex: '3S'). d'un nom de RCS (par ex: 'S2' ou '3S').
""" """
pe_affichage.pe_print( pe_affichage.pe_print(
"*** Génère les RCS (différentes combinaisons de semestres) des étudiants" "*** Génère les RCS (différentes combinaisons de semestres) des étudiants"
@ -168,24 +177,35 @@ class JuryPE(object):
self.rcss_jury = pe_rcs.RCSsJuryPE(self.diplome) self.rcss_jury = pe_rcs.RCSsJuryPE(self.diplome)
self.rcss_jury.cree_rcss(self.etudiants) self.rcss_jury.cree_rcss(self.etudiants)
def _gen_xls_semestres_taggues(self, zipfile: ZipFile): def _gen_xls_sxtags(self, zipfile: ZipFile):
"""Génère les semestres taggués en s'appuyant sur les RCS de type Sx (pour """Génère les semestres taggués en s'appuyant sur les RCS de type Sx (pour
identifier les redoublements impactant les semestres taggués). identifier les redoublements impactant les semestres taggués).
""" """
# Génère les moyennes des RCS de type Sx # Génère les moyennes des RCS de type Sx
pe_affichage.pe_print("*** Calcule les moyennes de semestres = RCS de type Sx") pe_affichage.pe_print("*** Calcule les moyennes des SxTag")
self.sems_tags = {} # Les regroupements de Sx
for rcs_id, rcs in self.rcss_jury.rcss.items(): self.regroupements = {}
if rcs.nom.startswith("S"): for rang in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT+1):
self.sems_tags[rcs_id] = pe_semtag.SemTag(rcs, self.res_sems_tags) self.regroupements[rang] = {}
for etudid in self.etudiants.etudiants_ids:
trajectoire = self.etudiants.cursus[etudid][f"S{rang}"]
self.regroupements[rang] |= trajectoire
# Les SxTag
self.sxtags = {}
for rang in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT+1):
trajectoire = self.regroupements[rang]
sxtag_id = pe_sxtag.get_sxtag_from_semestres(trajectoire)
ressemstags = {fid: self.ressembuttags[fid] for fid in trajectoire}
self.sxtags[sxtag_id] = pe_sxtag.SxTag(sxtag_id, ressemtags)
# Intègre le bilan des semestres taggués au zip final # Intègre le bilan des semestres taggués au zip final
output = io.BytesIO() output = io.BytesIO()
with pd.ExcelWriter( # pylint: disable=abstract-class-instantiated with pd.ExcelWriter( # pylint: disable=abstract-class-instantiated
output, engine="openpyxl" output, engine="openpyxl"
) as writer: ) as writer:
for sem_tag in self.sems_tags.values(): for sem_tag in self.sxtags.values():
onglet = sem_tag.get_repr(verbose=False) onglet = sem_tag.get_repr(verbose=False)
df = sem_tag.df_moyennes_et_classements() df = sem_tag.df_moyennes_et_classements()
# écriture dans l'onglet # écriture dans l'onglet
@ -200,11 +220,11 @@ class JuryPE(object):
) )
def _gen_xls_rcss_tags(self, zipfile: ZipFile): def _gen_xls_rcss_tags(self, zipfile: ZipFile):
"""Génère les RCS tagguées des RCS, en calculant les moyennes et les classements par tag """Génère les RCS taggués (autres que ceux de type Sx), etc...
pour chacune. en calculant les moyennes et les classements par tag pour chaque RCS.
Stocke le résultat dans self.rccs_tag, un dictionnaire de Stocke le résultat dans self.rccs_tag, un dictionnaire de
la forme ``{nom_aggregat: {fid_terminal: SetTag(fid_terminal)} }`` la forme ``{nom_aggregat: {fid_terminal: RCSTag(fid_terminal)} }``
Pour rappel : Chaque RCS est identifié par un nom d'aggrégat et par un formsemestre terminal. Pour rappel : Chaque RCS est identifié par un nom d'aggrégat et par un formsemestre terminal.
@ -213,19 +233,13 @@ class JuryPE(object):
* combinaisons '3S' : S1+S2+S3 en prenant en compte tous les S3 qu'ont fréquenté les * combinaisons '3S' : S1+S2+S3 en prenant en compte tous les S3 qu'ont fréquenté les
étudiants du jury PE. Ces S3 marquent les formsemestre terminal de chaque combinaison. étudiants du jury PE. Ces S3 marquent les formsemestre terminal de chaque combinaison.
* combinaisons 'S2' : 1 seul S2 pour des étudiants n'ayant pas redoublé, 2 pour des redoublants (dont les
notes seront moyennées sur leur 2 semestres S2). Ces combinaisons ont pour formsemestre le dernier S2 en
date (le S2 redoublé par les redoublants est forcément antérieur)
Args: Args:
etudiants: Les données des étudiants etudiants: Les données des étudiants
semestres_tag: Les semestres tag (pour lesquels des moyennes par tag ont été calculés) semestres_tag: Les semestres tag (pour lesquels des moyennes par tag ont été calculés)
""" """
# Génère les moyennes des RCS de type Sx # Génère les moyennes des RCS de type Sx
pe_affichage.pe_print("*** Calcule les moyennes des RCS de type Sx") pe_affichage.pe_print("*** Calcule les moyennes des RCS")
self.rcss_tags = {} self.rcss_tags = {}
for rcs_id, rcs in self.rcss_jury.rcss.items(): for rcs_id, rcs in self.rcss_jury.rcss.items():
@ -409,7 +423,7 @@ class JuryPE(object):
for etudid in etudids: for etudid in etudids:
trajectoire = self.rcss_jury.suivi[etudid][aggregat] trajectoire = self.rcss_jury.suivi[etudid][aggregat]
if trajectoire: if trajectoire:
tid = trajectoire.rcs_id tid = trajectoire.sxtag_id
trajectoire_tagguee = self.rcss_tags[tid] trajectoire_tagguee = self.rcss_tags[tid]
if ( if (
tag in trajectoire_tagguee.moyennes_tags tag in trajectoire_tagguee.moyennes_tags
@ -555,7 +569,7 @@ class JuryPE(object):
# La trajectoire de l'étudiant sur l'aggrégat # La trajectoire de l'étudiant sur l'aggrégat
trajectoire = self.rcss_jury.suivi[etudid][aggregat] trajectoire = self.rcss_jury.suivi[etudid][aggregat]
if trajectoire: if trajectoire:
trajectoire_tagguee = self.rcss_tags[trajectoire.rcs_id] trajectoire_tagguee = self.rcss_tags[trajectoire.sxtag_id]
if tag in trajectoire_tagguee.moyennes_tags: if tag in trajectoire_tagguee.moyennes_tags:
# L'interclassement # L'interclassement
interclass = self.interclassements_taggues[aggregat] interclass = self.interclassements_taggues[aggregat]
@ -581,9 +595,8 @@ class JuryPE(object):
def get_formsemestres_etudiants(etudiants: EtudiantsJuryPE) -> dict: def get_formsemestres_etudiants(etudiants: EtudiantsJuryPE) -> dict:
"""Ayant connaissance des étudiants dont il faut calculer les moyennes pour """Ayant connaissance des étudiants dont il faut calculer les moyennes pour
le jury PE (attribut `self.etudiant_ids) et de leur cursus (semestres le jury PE (attribut `self.etudiant_ids) et de leurs trajectoires (semestres
parcourus), parcourus), renvoie un dictionnaire ``{fid: FormSemestre(fid)}``
renvoie un dictionnaire ``{fid: FormSemestre(fid)}``
contenant l'ensemble des formsemestres de leurs cursus, dont il faudra calculer contenant l'ensemble des formsemestres de leurs cursus, dont il faudra calculer
la moyenne. la moyenne.
@ -592,44 +605,17 @@ def get_formsemestres_etudiants(etudiants: EtudiantsJuryPE) -> dict:
Returns: Returns:
Un dictionnaire de la forme `{fid: FormSemestre(fid)}` Un dictionnaire de la forme `{fid: FormSemestre(fid)}`
""" """
semestres = {} semestres = {}
for etudid in etudiants.etudiants_ids: for etudid in etudiants.etudiants_ids:
for cle in etudiants.cursus[etudid]: for cle in etudiants.trajectoires[etudid]:
if cle.startswith("S"): if cle.startswith("S"):
semestres = semestres | etudiants.cursus[etudid][cle] semestres = semestres | etudiants.trajectoires[etudid][cle]
return semestres return semestres
def compute_resultats_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
"""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_affichage.pe_print("*** Création des semestres taggués")
formsemestres = get_formsemestres_etudiants(etudiants)
semestres_tags = {}
for frmsem_id, formsemestre in formsemestres.items():
# Crée le semestre_tag et exécute les calculs de moyennes
formsemestretag = ResSemTag(frmsem_id)
# Stocke le semestre taggué
semestres_tags[frmsem_id] = formsemestretag
return semestres_tags
def compute_interclassements( def compute_interclassements(

View File

@ -12,9 +12,9 @@ Created on 01-2024
import app.pe.pe_comp as pe_comp import app.pe.pe_comp as pe_comp
from app.models import FormSemestre from app.models import FormSemestre
from app.pe import pe_sxtag
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
TYPES_RCS = { TYPES_RCS = {
"S1": { "S1": {
"aggregat": ["S1"], "aggregat": ["S1"],
@ -81,11 +81,11 @@ TOUS_LES_SEMESTRES = [cle for cle in TYPES_RCS if cle.startswith("S")]
class RCS: class RCS:
"""Modélise un ensemble de semestres d'étudiants """Modélise un ensemble de semestres finals d'étudiants
associé à un type de regroupement cohérent de semestres associé à un type de regroupement cohérent de semestres
donné (par ex: 'S2', '3S', '2A'). donné (par ex: 'S2', '3S', '2A').
Si le RCS est un semestre de type Si, stocke le (ou les) Si le RCS est un semestre de type Si, stocke le
formsemestres de numéro i qu'ont suivi l'étudiant pour atteindre le Si formsemestres de numéro i qu'ont suivi l'étudiant pour atteindre le Si
(en général 1 si personnes n'a redoublé, mais 2 s'il y a des redoublants) (en général 1 si personnes n'a redoublé, mais 2 s'il y a des redoublants)
@ -104,38 +104,40 @@ class RCS:
""" """
def __init__(self, nom_rcs: str, semestre_final: FormSemestre): def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
self.nom = nom_rcs self.nom: str = nom_rcs
"""Nom du RCS""" """Nom du RCS"""
self.formsemestre_final = semestre_final self.formsemestre_final: FormSemestre = semestre_final
"""FormSemestre terminal du RCS""" """FormSemestre terminal du RCS"""
self.rcs_id = (nom_rcs, semestre_final.formsemestre_id) self.rcs_id: (str, int) = (nom_rcs, semestre_final.formsemestre_id)
"""Identifiant du RCS sous forme (nom_rcs, id du semestre_terminal)""" """Identifiant du RCS sous forme (nom_rcs, id du semestre_terminal)"""
self.semestres_aggreges = {} # self.semestres_aggreges: dict[int:FormSemestre] = {}
"""Semestres regroupés dans le RCS""" # """Semestres regroupés dans le RCS"""
self.sxtags_aggreges: dict[(str, int): pe_sxtag.SxTag] = {}
"""Les SxTag aggrégés"""
def get_formsemestre_id_final(self): def get_formsemestre_id_final(self):
"""Renvoie l'identifiant du formsemestre final du RCS""" """Renvoie l'identifiant du formsemestre final du RCS"""
return self.formsemestre_final.formsemestre_id return self.formsemestre_final.formsemestre_id
def add_semestres_a_aggreger(self, semestres: dict[int:FormSemestre]): def add_sxtags_a_aggreger(self, sxtags: dict[(str,int): pe_sxtag.SxTag]):
"""Ajout de semestres aux semestres à regrouper """Ajout des SxTag aux semestres à regrouper
Args: Args:
semestres: Dictionnaire ``{fid: FormSemestre(fid)}`` à ajouter sxtags: Dictionnaire ``{(str,fid): SxTag}`` à ajouter
""" """
self.semestres_aggreges = self.semestres_aggreges | semestres self.sxtags_aggreges = self.sxtags_aggreges | sxtags
def get_repr(self, verbose=True) -> str: def get_repr(self, verbose=True) -> str:
"""Représentation textuelle d'un RCS """Représentation textuelle d'un RCS
basé sur ses semestres aggrégés""" basé sur ses sxtags aggrégés"""
noms = [] noms = []
for fid in self.semestres_aggreges: for sxtag_id, sxtag in self.sxtags_aggreges.items():
semestre = self.semestres_aggreges[fid] noms.append(f"S{sxtag.semestre_id}")
noms.append(f"S{semestre.semestre_id}({fid})")
noms = sorted(noms) noms = sorted(noms)
title = f"""{self.nom} ({ title = f"""{self.nom} ({
self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}""" self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"""
@ -145,7 +147,7 @@ class RCS:
class RCSsJuryPE: class RCSsJuryPE:
"""Classe centralisant toutes les regroupements cohérents de """Classe centralisant tous les regroupements cohérents de
semestres (RCS) des étudiants à prendre en compte dans un jury PE semestres (RCS) des étudiants à prendre en compte dans un jury PE
Args: Args:
@ -163,21 +165,24 @@ class RCSsJuryPE:
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS, """Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
son RCS : {etudid: {nom_RCS: RCS}}""" son RCS : {etudid: {nom_RCS: RCS}}"""
def cree_rcss(self, etudiants: EtudiantsJuryPE): def cree_rcss(self, etudiants: EtudiantsJuryPE, sxtags: dict[(str, int), pe_sxtag.SxTag]):
"""Créé tous les RCS, au regard du cursus des étudiants """Créé tous les RCS, au regard du cursus des étudiants
analysés + les mémorise dans les données de l'étudiant analysés et des SxTag calculés.
Les mémorise dans les données de chaque étudiant.
Args: Args:
etudiants: Les étudiants à prendre en compte dans le Jury PE etudiants: Les étudiants à prendre en compte dans le Jury PE
pe_sxtag: Les Sx taggués
""" """
for nom_rcs in pe_comp.TOUS_LES_SEMESTRES + TOUS_LES_RCS_AVEC_PLUSIEURS_SEM: for nom_rcs in pe_comp.TOUS_LES_SEMESTRES + TOUS_LES_RCS_AVEC_PLUSIEURS_SEM:
# L'aggrégat considéré (par ex: 3S=S1+S2+S3), son nom de son semestre # L'aggrégat considéré (par ex: 3S=S1+S2+S3), son nom de son semestre
# terminal (par ex: S3) et son numéro (par ex: 3) # terminal (par ex: S3) et son numéro (par ex: 3)
noms_semestre_de_aggregat = TYPES_RCS[nom_rcs]["aggregat"] noms_semestre_de_aggregat = TYPES_RCS[nom_rcs]["aggregat"] # ["S1", "S2", "S3"]
nom_semestre_terminal = noms_semestre_de_aggregat[-1] nom_semestre_terminal = noms_semestre_de_aggregat[-1] # "S3"
for etudid in etudiants.cursus: for etudid in etudiants.trajectoires:
if etudid not in self.suivi: if etudid not in self.suivi:
self.suivi[etudid] = { self.suivi[etudid] = {
aggregat: None aggregat: None
@ -188,54 +193,57 @@ class RCSsJuryPE:
# Le formsemestre terminal (dernier en date) associé au # Le formsemestre terminal (dernier en date) associé au
# semestre marquant la fin de l'aggrégat # semestre marquant la fin de l'aggrégat
# (par ex: son dernier S3 en date) # (par ex: son dernier S3 en date)
semestres = etudiants.cursus[etudid][nom_semestre_terminal] semestres = etudiants.trajectoires[etudid][nom_semestre_terminal]
if semestres: if semestres:
formsemestre_final = get_dernier_semestre_en_date(semestres) formsemestre_final = get_dernier_semestre_en_date(semestres)
# Ajout ou récupération de la trajectoire # Ajout ou récupération de la trajectoire
trajectoire_id = (nom_rcs, formsemestre_final.formsemestre_id) rcs_id = (nom_rcs, formsemestre_final.formsemestre_id)
if trajectoire_id not in self.rcss: if rcs_id not in self.rcss:
trajectoire = RCS(nom_rcs, formsemestre_final) rcs = RCS(nom_rcs, formsemestre_final)
self.rcss[trajectoire_id] = trajectoire self.rcss[rcs_id] = rcs
else: else:
trajectoire = self.rcss[trajectoire_id] rcs = self.rcss[rcs_id]
# La liste des semestres de l'étudiant à prendre en compte # La liste des semestres de l'étudiant à prendre en compte
# pour cette trajectoire # pour cette trajectoire
semestres_a_aggreger = get_rcs_etudiant( semestres_a_aggreger = get_trajectoire_etudiant(
etudiants.cursus[etudid], formsemestre_final, nom_rcs etudiants.trajectoires[etudid], formsemestre_final, nom_rcs
) )
# Extrait les sxtags correspondants aux semestres à aggréger
# (par ex. des 2 semestres S1(18)+S1(26) récupère le sxtag S1(26)
sxtags_a_aggreger = {}
semestres_tries = pe_comp.tri_semestres_par_rang(semestres_a_aggreger)
for rang in semestres_tries:
sems = semestres_tries[rang] # les 1 ou 2 semestres de même rang suivi
sxtag_id = pe_sxtag.get_sxtag_from_semestres(sems, sxtags)
if not sxtag_id:
raise ValueError(f"Il manque un sxtag pour {sems}")
sxtags_a_aggreger[sxtag_id] = sxtags[sxtag_id]
# Ajout des semestres à la trajectoire # Ajout des semestres à la trajectoire
trajectoire.add_semestres_a_aggreger(semestres_a_aggreger) rcs.add_sxtags_a_aggreger(sxtags_a_aggreger)
# Mémoire la trajectoire suivie par l'étudiant # Mémoire la trajectoire suivie par l'étudiant
self.suivi[etudid][nom_rcs] = trajectoire self.suivi[etudid][nom_rcs] = rcs
def get_rcs_etudiant( def get_trajectoire_etudiant(
semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str
) -> dict[int, FormSemestre]: ) -> dict[int, FormSemestre]:
"""Ensemble des semestres parcourus par un étudiant, connaissant """Ensemble des semestres parcourus (trajectoire)
les semestres de son cursus, par un étudiant dans le cadre
dans le cadre du RCS visé et ayant pour semestre terminal `formsemestre_final`. d'un RCS de type Sx, iA ou iS et ayant pour semestre terminal `formsemestre_final`.
Si le RCS est de type "Si", limite les semestres à ceux de numéro i. Par ex: pour un RCS "3S", dont le formsemestre_terminal est un S3, regroupe
Par ex: si formsemestre_terminal est un S3 et nom_agrregat "S3", ne prend en compte que les le ou les S1 qu'il a suivi (1 ou 2 si redoublement) + le ou les S2 + le ou les S3.
semestres 3.
Si le RCS est de type "iA" ou "iS" (incluant plusieurs numéros de semestres), prend en
compte les dit numéros de semestres.
Par ex: si formsemestre_terminal est un S3, ensemble des S1,
S2, S3 suivi pour l'amener au S3 (il peut y avoir plusieurs S1,
ou S2, ou S3 s'il a redoublé).
Les semestres parcourus sont antérieurs (en terme de date de fin) Les semestres parcourus sont antérieurs (en terme de date de fin)
au formsemestre_terminal. au formsemestre_terminal.
Args: Args:
cursus: Dictionnaire {fid: FormSemestre(fid)} donnant l'ensemble des semestres cursus: Dictionnaire {fid: Formsemestre} donnant l'ensemble des semestres
dans lesquels l'étudiant a été inscrit dans lesquels l'étudiant a été inscrit
formsemestre_final: le semestre final visé formsemestre_final: le semestre final visé
nom_rcs: Nom du RCS visé nom_rcs: Nom du RCS visé

View File

@ -38,7 +38,7 @@ Created on Fri Sep 9 09:15:05 2016
from app.comp.res_sem import load_formsemestre_results from app.comp.res_sem import load_formsemestre_results
from app.pe import pe_affichage from app.pe import pe_affichage
from app.pe.pe_ressemtag import ResSemTag from app.pe.pe_ressemtag import ResSemBUTTag
import pandas as pd import pandas as pd
import numpy as np import numpy as np
from app.pe.pe_rcs import RCS from app.pe.pe_rcs import RCS
@ -48,26 +48,25 @@ from app.pe.pe_moytag import MoyennesTag
class RCSTag(TableTag): class RCSTag(TableTag):
def __init__( def __init__(
self, rcs: RCS, semestres_taggues: dict[int, ResSemTag] self, rcs: RCS, semestres_taggues: dict[int, ResSemBUTTag]
): ):
"""Calcule les moyennes par tag d'une combinaison de semestres """Calcule les moyennes par tag d'une combinaison de semestres
(RCS), pour extraire les classements par tag pour un (RCS), pour extraire les classements par tag pour un
groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous
participé au semestre terminal. participé au semestre terminal.
Args: Args:
rcs: Un RCS (identifié par un nom et l'id de son semestre terminal) rcs: Un RCS (identifié par un nom et l'id de son semestre terminal)
semestres_taggues: Les données sur les semestres taggués semestres_taggues: Les données sur les semestres taggués
""" """
TableTag.__init__(self) TableTag.__init__(self)
self.rcs_id: tuple(str, int) = rcs.rcs_id
self.rcs_id = rcs.rcs_id
"""Identifiant du RCS taggué (identique au RCS sur lequel il s'appuie)""" """Identifiant du RCS taggué (identique au RCS sur lequel il s'appuie)"""
self.rcs = rcs self.rcs: RCS = rcs
"""RCS associé au RCS taggué""" """RCS associé au RCS taggué"""
self.nom = self.get_repr() self.nom = self.get_repr()
@ -82,20 +81,21 @@ class RCSTag(TableTag):
self.semestres_aggreges = rcs.semestres_aggreges self.semestres_aggreges = rcs.semestres_aggreges
"""Les semestres aggrégés""" """Les semestres aggrégés"""
self.semestres_tags_aggreges = {} self.res_sems_tags = {}
"""Les semestres tags associés aux semestres aggrégés""" """Les semestres tags associés aux semestres aggrégés"""
for frmsem_id in self.semestres_aggreges:
try: try:
self.semestres_tags_aggreges[frmsem_id] = semestres_taggues[frmsem_id] for frmsem_id in self.semestres_aggreges:
self.res_sems_tags[frmsem_id] = semestres_taggues[frmsem_id]
except: except:
raise ValueError("Semestres taggués manquants") raise ValueError("Semestres taggués manquants")
"""Les étudiants (état civil + cursus connu)""" # Les étudiants (etuds, états civils & etudis)
self.etuds = nt.etuds self.add_etuds(nt.etuds)
# assert self.etuds == trajectoire.suivi # manque-t-il des étudiants ? # Les compétences (extraites des ues de tous les semestres)
self.etats_civils = {etud.etudid: etud.nomprenom for etud in self.etuds} self.ues = self.comp_ues(tag="but")
# Les tags
self.tags_sorted = self.do_taglist() self.tags_sorted = self.do_taglist()
"""Tags extraits de tous les semestres""" """Tags extraits de tous les semestres"""
@ -114,7 +114,7 @@ class RCSTag(TableTag):
def __eq__(self, other): def __eq__(self, other):
"""Egalité de 2 RCS taggués sur la base de leur identifiant""" """Egalité de 2 RCS taggués sur la base de leur identifiant"""
return self.rcs_id == other.rcs_id return self.rcs_id == other.sxtag_id
def get_repr(self, verbose=False) -> str: def get_repr(self, verbose=False) -> str:
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle """Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
@ -132,7 +132,7 @@ class RCSTag(TableTag):
# Index du cube (etudids -> dim 0, tags -> dim 1) # Index du cube (etudids -> dim 0, tags -> dim 1)
etudids = [etud.etudid for etud in self.etuds] etudids = [etud.etudid for etud in self.etuds]
tags = self.tags_sorted tags = self.tags_sorted
semestres_id = list(self.semestres_tags_aggreges.keys()) semestres_id = list(self.res_sems_tags.keys())
dfs = {} dfs = {}
@ -141,7 +141,7 @@ class RCSTag(TableTag):
df = pd.DataFrame(np.nan, index=etudids, columns=tags) df = pd.DataFrame(np.nan, index=etudids, columns=tags)
# Charge les notes du semestre tag # Charge les notes du semestre tag
notes = self.semestres_tags_aggreges[frmsem_id].notes notes = self.res_sems_tags[frmsem_id].notes
# Les étudiants & les tags commun au dataframe final et aux notes du semestre) # Les étudiants & les tags commun au dataframe final et aux notes du semestre)
etudids_communs = df.index.intersection(notes.index) etudids_communs = df.index.intersection(notes.index)
@ -172,8 +172,8 @@ class RCSTag(TableTag):
Une liste de tags triés par ordre alphabétique Une liste de tags triés par ordre alphabétique
""" """
tags = [] tags = []
for frmsem_id in self.semestres_tags_aggreges: for frmsem_id in self.res_sems_tags:
tags.extend(self.semestres_tags_aggreges[frmsem_id].tags_sorted) tags.extend(self.res_sems_tags[frmsem_id].tags_sorted)
pe_affichage.pe_print(f"* Tags : {', '.join(tags)}") pe_affichage.pe_print(f"* Tags : {', '.join(tags)}")
return sorted(set(tags)) return sorted(set(tags))

View File

@ -37,37 +37,38 @@ Created on Fri Sep 9 09:15:05 2016
""" """
import pandas as pd import pandas as pd
import app.pe.pe_etudiant
from app import db, ScoValueError from app import db, ScoValueError
from app import comp from app import comp
from app.comp.res_but import ResultatsSemestreBUT
from app.comp.res_sem import load_formsemestre_results from app.comp.res_sem import load_formsemestre_results
from app.models import FormSemestre from app.models import FormSemestre
from app.models.moduleimpls import ModuleImpl from app.models.moduleimpls import ModuleImpl
import app.pe.pe_affichage as pe_affichage import app.pe.pe_affichage as pe_affichage
import app.pe.pe_etudiant as pe_etudiant import app.pe.pe_etudiant as pe_etudiant
from app.pe.pe_tabletags import TableTag import app.pe.pe_tabletags as pe_tabletags
from app.pe.pe_moytag import MoyennesTag from app.pe.pe_moytag import MoyennesTag
from app.scodoc import sco_tag_module from app.scodoc import sco_tag_module
from app.scodoc.codes_cursus import UE_SPORT from app.scodoc.codes_cursus import UE_SPORT
class ResSemTag(TableTag): class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
""" """
Un ResSemTag représente les résultats des étudiants à un semestre, en donnant Un ResSemBUTTag représente les résultats des étudiants à un semestre, en donnant
accès aux moyennes par tag. accès aux moyennes par tag.
Il s'appuie principalement sur FormSemestre et sur ResultatsSemestreBUT. Il s'appuie principalement sur FormSemestre et sur ResultatsSemestreBUT.
""" """
def __init__(self, formsemestre_id: int): def __init__(self, formsemestre: FormSemestre):
""" """
Args: Args:
formsemestre_id: Identifiant du ``FormSemestre`` sur lequel il se base formsemestre: le ``FormSemestre`` sur lequel il se base
""" """
TableTag.__init__(self) ResultatsSemestreBUT.__init__(self, formsemestre)
pe_tabletags.TableTag.__init__(self)
# Le semestre # Le semestre
self.formsemestre_id = formsemestre_id # self.formsemestre_id = self.formsemestre.formsemestre_id
self.formsemestre = FormSemestre.get_formsemestre(formsemestre_id) # self.formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
# Le nom du res_semestre taggué # Le nom du res_semestre taggué
self.nom = self.get_repr(verbose=True) self.nom = self.get_repr(verbose=True)
@ -75,25 +76,14 @@ class ResSemTag(TableTag):
pe_affichage.pe_print(f"--> Résultats de semestre taggués {self.nom}") pe_affichage.pe_print(f"--> Résultats de semestre taggués {self.nom}")
# Les résultats du semestre # Les résultats du semestre
self.nt = load_formsemestre_results(self.formsemestre) # self.nt = load_formsemestre_results(self.formsemestre)
# Les étudiants (etuds, états civils & etudis) # Les étudiants (etuds, états civils & etudis) ajouté
self.add_etuds(self.nt.etuds) self.add_etuds(self.etuds)
# Les notes, les modules implémentés triés, les étudiants, les coeffs,
# récupérés notamment de py:mod:`res_but`
self.sem_cube = self.nt.sem_cube
self.modimpls_sorted = self.nt.formsemestre.modimpls_sorted
self.modimpl_coefs_df = self.nt.modimpl_coefs_df
# Les inscriptions aux modules
self.modimpl_inscr_df = self.nt.modimpl_inscr_df
# Les UEs (et les dispenses d'UE) # Les UEs (et les dispenses d'UE)
self.ues = self.nt.ues
ues_hors_sport = [ue for ue in self.ues if ue.type != UE_SPORT] ues_hors_sport = [ue for ue in self.ues if ue.type != UE_SPORT]
self.ues_inscr_parcours_df = self.nt.load_ues_inscr_parcours() self.ues_inscr_parcours_df = self.load_ues_inscr_parcours()
self.dispense_ues = self.nt.dispense_ues
# Les tags personnalisés et auto: # Les tags personnalisés et auto:
tags_dict = self._get_tags_dict() tags_dict = self._get_tags_dict()
@ -124,7 +114,7 @@ class ResSemTag(TableTag):
# Ajoute les moyennes par UEs (et donc par compétence) + la moyenne générale (but) # Ajoute les moyennes par UEs (et donc par compétence) + la moyenne générale (but)
df_ues = pd.DataFrame( df_ues = pd.DataFrame(
{ue.id: self.nt.etud_moy_ue[ue.id] for ue in ues_hors_sport}, {ue.id: self.etud_moy_ue[ue.id] for ue in ues_hors_sport},
index=self.etudids, index=self.etudids,
) )
# moy_ues = self.nt.etud_moy_ue[ue_id] # moy_ues = self.nt.etud_moy_ue[ue_id]
@ -150,7 +140,7 @@ class ResSemTag(TableTag):
def get_repr(self, verbose=False): def get_repr(self, verbose=False):
"""Nom affiché pour le semestre taggué""" """Nom affiché pour le semestre taggué"""
if verbose: if verbose:
return f"{self.formsemestre} ({self.formsemestre_id})" return f"{self.formsemestre} ({self.formsemestre.formsemestre_id})"
else: else:
return pe_etudiant.nom_semestre_etape(self.formsemestre, avec_fid=True) return pe_etudiant.nom_semestre_etape(self.formsemestre, avec_fid=True)
@ -209,7 +199,7 @@ class ResSemTag(TableTag):
dict_tags = {"personnalises": dict(), "auto": dict()} dict_tags = {"personnalises": dict(), "auto": dict()}
# Les tags perso # Les tags perso
dict_tags["personnalises"] = get_synthese_tags_personnalises_semestre( dict_tags["personnalises"] = get_synthese_tags_personnalises_semestre(
self.nt.formsemestre self.formsemestre
) )
noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys()))) noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys())))
pe_affichage.pe_print( pe_affichage.pe_print(
@ -256,26 +246,6 @@ class ResSemTag(TableTag):
raise ScoValueError(message) raise ScoValueError(message)
def get_moduleimpl(modimpl_id) -> dict:
"""Renvoie l'objet modimpl dont l'id est modimpl_id"""
modimpl = db.session.get(ModuleImpl, modimpl_id)
if modimpl:
return modimpl
return None
def get_moy_ue_from_nt(nt, etudid, modimpl_id) -> float:
"""Renvoie la moyenne de l'UE d'un etudid dans laquelle se trouve
le module de modimpl_id
"""
# ré-écrit
modimpl = get_moduleimpl(modimpl_id) # le module
ue_status = nt.get_etud_ue_status(etudid, modimpl.module.ue.id)
if ue_status is None:
return None
return ue_status["moy"]
def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre): def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
"""Etant données les implémentations des modules du semestre (modimpls), """Etant données les implémentations des modules du semestre (modimpls),
synthétise les tags renseignés dans le programme pédagogique & synthétise les tags renseignés dans le programme pédagogique &

View File

@ -37,87 +37,79 @@ Created on Fri Sep 9 09:15:05 2016
""" """
from app.comp.res_sem import load_formsemestre_results from app.comp.res_sem import load_formsemestre_results
from app.models import UniteEns from app.models import UniteEns, FormSemestre
from app.pe import pe_affichage from app.pe import pe_affichage
from app.pe.pe_ressemtag import ResSemTag from app.pe.pe_ressemtag import ResSemBUTTag
import pandas as pd import pandas as pd
import numpy as np import numpy as np
from app.pe.pe_rcs import RCS
from app.pe.pe_tabletags import TableTag from app.pe.pe_tabletags import TableTag
from app.pe.pe_moytag import MoyennesTag from app.pe.pe_moytag import MoyennesTag
class SemTag(TableTag): class SxTag(TableTag):
def __init__(self, rcs: RCS, res_sems_tags: dict[int, ResSemTag]): def __init__(self, sxtag_id: (int, int), ressembuttags: dict[int, ResSemBUTTag]):
"""Calcule les moyennes/classements par tag à un RCS d'un seul semestre """Calcule les moyennes/classements par tag d'un semestre de type 'Sx'
(ici semestre) de type 'Sx' (par ex. 'S1', 'S2', ...) : (par ex. 'S1', 'S2', ...) :
* pour les étudiants non redoublants, ce sont les moyennes/classements * pour les étudiants non redoublants, ce sont les moyennes/classements
du semestre suivi du semestre suivi
* pour les étudiants redoublants, c'est une fusion des moyennes/classements * pour les étudiants redoublants, c'est une fusion des moyennes/classements
suivis les différents 'Sx' (donné par dans le rcs) dans les (2) 'Sx' qu'il a suivi
Les **tags considérés** sont uniquement ceux du dernier semestre du RCS Un SxTag peut donc regrouper plusieurs semestres.
Un SxTag est identifié par un tuple (x, fid) x est le numéro (semestre_id)
du semestre et fid le formsemestre_id du semestre final (le plus récent) du
regrouprement.
Les **tags**, les **UE** et les inscriptions aux UEs (pour les etudiants)
considérés sont uniquement ceux du semestre final.
Args: Args:
rcs: Un RCS (identifié par un nom et l'id de son semestre terminal) sxtag_id: L'identifiant de SxTag
res_sems_tags: Les données sur les résultats des semestres taggués ressembuttags: Un dictionnaire de la forme `{fid: ResSemBUTTag(fid)}` donnant
les semestres à regrouper et les résultats/moyennes par tag des
semestres
""" """
TableTag.__init__(self) TableTag.__init__(self)
self.rcs_id = rcs.rcs_id assert sxtag_id and len(sxtag_id) == 2 and sxtag_id in ressembuttags
"""Identifiant du RCS taggué (identique au RCS sur lequel il s'appuie)"""
self.rcs = rcs self.sxtag_id: (int, int) = sxtag_id
"""RCS associé au RCS taggué""" """Identifiant du SxTag de la forme (semestre_id, fid_semestre_final)"""
assert self.rcs.nom.startswith( self.ressembuttags = ressembuttags
"S" """Les ResSemBUTTags à regrouper dans le SxTag"""
), "Un SemTag ne peut être utilisé que pour un RCS de la forme Sx"
self.nom = self.get_repr()
"""Représentation textuelle du RCS taggué"""
# Les données du formsemestre_terminal # Les données du semestre final
self.formsemestre_terminal = rcs.formsemestre_final self.fid_final = sxtag_id[1]
"""Le formsemestre terminal""" self.ressembuttag_final = ressembuttags[self.fid_final]
"""Le ResSemBUTTag final"""
# Les résultats du formsemestre terminal
nt = load_formsemestre_results(self.formsemestre_terminal)
self.semestres_aggreges = rcs.semestres_aggreges
"""Les semestres aggrégés"""
self.res_sems_tags = {}
"""Les résultats des semestres taggués (limités aux semestres aggrégés)"""
try:
for frmsem_id in self.semestres_aggreges:
self.res_sems_tags[frmsem_id] = res_sems_tags[frmsem_id]
except:
raise ValueError("Résultats des semestres taggués manquants")
# Les étudiants (etuds, états civils & etudis) # Les étudiants (etuds, états civils & etudis)
self.add_etuds(nt.etuds) self.etuds = ressembuttags[self.fid_final].etuds
self.add_etuds(self.etuds)
# Les tags # Les tags
self.tags_sorted = self.comp_tags_list() self.tags_sorted = self.ressembuttag_final.tags_sorted
"""Tags (extraits uniquement du semestre terminal de l'aggrégat)""" """Tags (extraits uniquement du semestre final)"""
pe_affichage.pe_print(f"* Tags : {', '.join(self.tags_sorted)}")
# Les UEs # Les UE
self.ues = self.comp_ues(tag="but") self.ues = self.ressembuttag_final.moyennes_tags["but"].ues
# Les acronymes des UE
self.acronymes_ues_sorted = sorted([ue.acronyme for ue in self.ues.values()]) self.acronymes_ues_sorted = sorted([ue.acronyme for ue in self.ues.values()])
"""UEs extraites du semestre terminal de l'aggrégat (avec
check de concordance sur les UE des semestres_aggrégés)"""
# Les inscriptions aux UEs # Les inscriptions des étudiants aux UEs
self.ues_inscr_parcours_df = self.comp_ues_inscr_parcours(tag="but") # => ne conserve que les UEs du semestre final (pour les redoublants)
"""Les inscriptions aux UEs (extraites uniquement du semestre terminal)""" self.ues_inscr_parcours_df = self.ressembuttag_final.moyennes_tags["but"].ues_inscr_parcours_df
self.moyennes_tags: dict[str, MoyennesTag] = {}
"""Moyennes/classements par tag (qu'ils soient personnalisés ou automatiques)"""
# Les moyennes par tag
self.moyennes_tags: dict[str, pd.DataFrame] = {} self.moyennes_tags: dict[str, pd.DataFrame] = {}
"""Les notes aux UEs dans différents tags""" """Les notes aux UEs dans différents tags"""
# Masque des inscriptions # Masque des inscriptions
inscr_mask = self.ues_inscr_parcours_df.to_numpy() inscr_mask = self.ues_inscr_parcours_df.to_numpy()
for tag in self.tags_sorted: for tag in self.tags_sorted:
@ -132,19 +124,19 @@ class SemTag(TableTag):
inscr_mask, inscr_mask,
) )
# Les moyennes # Les moyennes
self.moyennes_tags[tag] = MoyennesTag(tag, self.moyennes_tags[tag] = MoyennesTag(
self.ues, tag, self.ues, moys_ues, self.ues_inscr_parcours_df
moys_ues, )
self.ues_inscr_parcours_df)
def __eq__(self, other): def __eq__(self, other):
"""Egalité de 2 RCS taggués sur la base de leur identifiant""" """Egalité de 2 SxTag sur la base de leur identifiant"""
return self.rcs_id == other.rcs_id return self.sxtag_id == other.sxtag_id
def get_repr(self, verbose=False) -> str: def get_repr(self, verbose=False) -> str:
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle """Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
est basée)""" est basée)"""
return self.rcs.get_repr(verbose=verbose) affichage = str(fid) for fid in self.res
return f"S{sxtag_id[0]}Tag ({'+'.join()})"
def compute_notes_ues_cube(self, tag, acronymes_ues_sorted): def compute_notes_ues_cube(self, tag, acronymes_ues_sorted):
"""Construit le cube de notes des UEs (etudid x accronyme_ue x semestre_aggregé) """Construit le cube de notes des UEs (etudid x accronyme_ue x semestre_aggregé)
@ -153,7 +145,7 @@ class SemTag(TableTag):
# Index du cube (etudids -> dim 0, ues -> dim 1, semestres -> dim2) # Index du cube (etudids -> dim 0, ues -> dim 1, semestres -> dim2)
etudids = [etud.etudid for etud in self.etuds] etudids = [etud.etudid for etud in self.etuds]
# acronymes_ues = sorted([ue.acronyme for ue in self.ues.values()]) # acronymes_ues = sorted([ue.acronyme for ue in self.ues.values()])
semestres_id = list(self.res_sems_tags.keys()) semestres_id = list(self.ressembuttags.keys())
dfs = {} dfs = {}
@ -162,7 +154,7 @@ class SemTag(TableTag):
df = pd.DataFrame(np.nan, index=etudids, columns=acronymes_ues_sorted) df = pd.DataFrame(np.nan, index=etudids, columns=acronymes_ues_sorted)
# Charge les notes du semestre tag # Charge les notes du semestre tag
sem_tag = self.res_sems_tags[frmsem_id] sem_tag = self.ressembuttags[frmsem_id]
moys_tag = sem_tag.moyennes_tags[tag] moys_tag = sem_tag.moyennes_tags[tag]
notes = moys_tag.notes_ues # dataframe etudids x ues notes = moys_tag.notes_ues # dataframe etudids x ues
acronymes_ues_sem = list( acronymes_ues_sem = list(
@ -192,43 +184,6 @@ class SemTag(TableTag):
etudids_x_ues_x_semestres = np.stack(semestres_x_etudids_x_ues, axis=-1) etudids_x_ues_x_semestres = np.stack(semestres_x_etudids_x_ues, axis=-1)
return etudids_x_ues_x_semestres return etudids_x_ues_x_semestres
def comp_tags_list(self) -> list[str]:
"""Récupère les tag du semestre taggué associé au semestre final du RCS
Returns:
Une liste de tags triés par ordre alphabétique
"""
tags = []
dernier_frmid = self.formsemestre_terminal.formsemestre_id
dernier_semestre_tag = self.res_sems_tags[dernier_frmid]
tags = dernier_semestre_tag.tags_sorted
pe_affichage.pe_print(f"* Tags : {', '.join(tags)}")
return tags
def comp_ues(self, tag="but") -> dict[int, UniteEns]:
"""Récupère les UEs à aggréger, en s'appuyant sur la moyenne générale
(tag but) du semestre final du RCS
Returns:
Un dictionnaire donnant les UEs
"""
dernier_frmid = self.formsemestre_terminal.formsemestre_id
dernier_semestre_tag = self.res_sems_tags[dernier_frmid]
moy_tag = dernier_semestre_tag.moyennes_tags[tag]
return moy_tag.ues # les UEs
def comp_ues_inscr_parcours(self, tag="but") -> pd.DataFrame:
"""Récupère les informations d'inscription des étudiants aux UEs : ne
conserve que les UEs du semestre terminal (pour les redoublants)
Returns:
Un dataFrame etudids x UE indiquant si un étudiant est inscrit à une UE
"""
dernier_frmid = self.formsemestre_terminal.formsemestre_id
dernier_semestre_tag = self.res_sems_tags[dernier_frmid]
moy_tag = dernier_semestre_tag.moyennes_tags[tag]
return moy_tag.ues_inscr_parcours_df
def compute_notes_ues( def compute_notes_ues(
set_cube: np.array, set_cube: np.array,
@ -261,8 +216,8 @@ def compute_notes_ues(
mask = ~np.isnan(set_cube) mask = ~np.isnan(set_cube)
# Entrées à garder dans le cube en fonction du mask d'inscription # Entrées à garder dans le cube en fonction du mask d'inscription
inscr_mask_3D = np.stack([inscr_mask]*nb_semestres, axis=-1) inscr_mask_3D = np.stack([inscr_mask] * nb_semestres, axis=-1)
set_cube = set_cube*inscr_mask_3D set_cube = set_cube * inscr_mask_3D
# Enlève les NaN du cube pour les entrées manquantes : NaN -> -1.0 # Enlève les NaN du cube pour les entrées manquantes : NaN -> -1.0
set_cube_no_nan = np.nan_to_num(set_cube, nan=-1.0) set_cube_no_nan = np.nan_to_num(set_cube, nan=-1.0)
@ -284,3 +239,16 @@ def compute_notes_ues(
etud_moy_tag_df.fillna(np.nan) etud_moy_tag_df.fillna(np.nan)
return etud_moy_tag_df return etud_moy_tag_df
def get_sxtag_from_semestres(
formsemestres: dict[int:FormSemestre], sxtags: dict[(str, int):SxTag]
) -> (str, int):
"""Partant d'un dictionnaire de SxTags, renvoie l'identifiant (str, fid) du
sxtag correspondant aux semestres de formsemestres.
(Utilisé pour transformer une trajectoire d'étudiants (par ex. S1+S2+S1+S2+S3)
en une suite de sxtags (S1+S2+S3)
"""
for sxtag_id, sxtag in sxtags:
if set(formsemestres.keys()) == set(sxtag.semestres_aggreges.keys()):
return sxtag_id

View File

@ -50,11 +50,11 @@ class TableTag(object):
SemestreTag, TrajectoireTag, AggregatInterclassTag SemestreTag, TrajectoireTag, AggregatInterclassTag
""" """
# Les étudiants # Les étudiants
self.etuds: list[Identite] = None # A venir # self.etuds: list[Identite] = None # A venir
"""Les étudiants""" """Les étudiants"""
self.etats_civils: dict[int, Identite] = None # self.etats_civils: dict[int, Identite] = None
"""Les états civils""" """Les états civils"""
self.etudids: list[int] = None # self.etudids: list[int] = None
"""Les etudids""" """Les etudids"""
def add_etuds(self, etuds: list[Identite]): def add_etuds(self, etuds: list[Identite]):
@ -63,7 +63,7 @@ class TableTag(object):
Args: Args:
etuds: la liste des identités de l'étudiant etuds: la liste des identités de l'étudiant
""" """
self.etuds = etuds # self.etuds = etuds
self.etats_civils = {etud.etudid: etud.etat_civil for etud in self.etuds} self.etats_civils = {etud.etudid: etud.etat_civil for etud in self.etuds}
self.etudids = list(self.etats_civils.keys()) self.etudids = list(self.etats_civils.keys())