forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -41,13 +41,13 @@ import datetime
|
||||
import re
|
||||
import unicodedata
|
||||
|
||||
|
||||
import pandas as pd
|
||||
from flask import g
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
||||
from app.models import FormSemestre
|
||||
from app.pe.pe_rcs import TYPES_RCS
|
||||
from app.pe.rcss.rcss_constantes import TYPES_RCS
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc.sco_logos import find_logo
|
||||
|
||||
@ -291,6 +291,29 @@ def tri_semestres_par_rang(cosemestres: dict[int, FormSemestre]):
|
||||
dictionnaire {rang: [liste des semestres du dit rang]}"""
|
||||
cosemestres_tries = {}
|
||||
for sem in cosemestres.values():
|
||||
cosemestres_tries[sem.semestre_id] = cosemestres_tries.get(sem.semestre_id, []) + [sem]
|
||||
cosemestres_tries[sem.semestre_id] = cosemestres_tries.get(
|
||||
sem.semestre_id, []
|
||||
) + [sem]
|
||||
return cosemestres_tries
|
||||
|
||||
|
||||
def find_index_and_columns_communs(
|
||||
df1: pd.DataFrame, df2: pd.DataFrame
|
||||
) -> (list, list):
|
||||
"""Partant de 2 DataFrames ``df1`` et ``df2``, renvoie les indices de lignes
|
||||
et de colonnes, communes aux 2 dataframes
|
||||
|
||||
Args:
|
||||
df1: Un dataFrame
|
||||
df2: Un dataFrame
|
||||
Returns:
|
||||
Le tuple formé par la liste des indices de lignes communs et la liste des indices
|
||||
de colonnes communes entre les 2 dataFrames
|
||||
"""
|
||||
indices1 = df1.index
|
||||
indices2 = df2.index
|
||||
indices_communs = list(df1.index.intersection(df2.index))
|
||||
colonnes1 = df1.columns
|
||||
colonnes2 = df2.columns
|
||||
colonnes_communes = list(set(colonnes1) & set(colonnes2))
|
||||
return indices_communs, colonnes_communes
|
||||
|
@ -38,9 +38,10 @@ import numpy as np
|
||||
|
||||
from app.pe.pe_tabletags import TableTag
|
||||
from app.pe.pe_moytag import MoyennesTag
|
||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
||||
from app.pe.pe_rcs import RCS, RCSsJuryPE
|
||||
from app.pe.pe_rcstag import RCSTag
|
||||
import app.pe.pe_etudiant as pe_etudiant
|
||||
import app.pe.rcss.pe_rcs as pe_rcs
|
||||
import app.pe.pe_rcss_jury as pe_rcss_jury
|
||||
import app.pe.pe_rcstag as pe_rcstag
|
||||
|
||||
|
||||
class RCSInterclasseTag(TableTag):
|
||||
@ -58,9 +59,9 @@ class RCSInterclasseTag(TableTag):
|
||||
def __init__(
|
||||
self,
|
||||
nom_rcs: str,
|
||||
etudiants: EtudiantsJuryPE,
|
||||
rcss_jury_pe: RCSsJuryPE,
|
||||
rcss_tags: dict[tuple, RCSTag],
|
||||
etudiants: pe_etudiant.EtudiantsJuryPE,
|
||||
rcss_jury_pe: pe_rcss_jury.RCSsJuryPE,
|
||||
rcss_tags: dict[tuple, pe_rcstag.RCSTag],
|
||||
):
|
||||
TableTag.__init__(self)
|
||||
|
||||
@ -80,21 +81,21 @@ class RCSInterclasseTag(TableTag):
|
||||
|
||||
# Les trajectoires (et leur version tagguées), en ne gardant que
|
||||
# celles associées à l'aggrégat
|
||||
self.rcss: dict[int, RCS] = {}
|
||||
self.rcss: dict[int, pe_rcs.RCS] = {}
|
||||
"""Ensemble des trajectoires associées à l'aggrégat"""
|
||||
for trajectoire_id in rcss_jury_pe.rcss:
|
||||
trajectoire = rcss_jury_pe.rcss[trajectoire_id]
|
||||
if trajectoire_id[0] == nom_rcs:
|
||||
self.rcss[trajectoire_id] = trajectoire
|
||||
|
||||
self.trajectoires_taggues: dict[int, RCS] = {}
|
||||
self.trajectoires_taggues: dict[int, pe_rcs.RCS] = {}
|
||||
"""Ensemble des trajectoires tagguées associées à l'aggrégat"""
|
||||
for trajectoire_id in self.rcss:
|
||||
self.trajectoires_taggues[trajectoire_id] = rcss_tags[trajectoire_id]
|
||||
|
||||
# Les trajectoires suivies par les étudiants du jury, en ne gardant que
|
||||
# celles associées aux diplomés
|
||||
self.suivi: dict[int, RCS] = {}
|
||||
self.suivi: dict[int, pe_rcs.RCS] = {}
|
||||
"""Association entre chaque étudiant et la trajectoire tagguée à prendre en
|
||||
compte pour l'aggrégat"""
|
||||
for etudid in self.diplomes_ids:
|
||||
|
@ -50,15 +50,16 @@ from zipfile import ZipFile
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import app.pe.rcss.rcss_constantes as rcss_constants
|
||||
from app.pe import pe_sxtag
|
||||
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_etudiant as pe_etudiant
|
||||
import app.pe.pe_rcs as pe_rcs
|
||||
import app.pe.rcss.pe_rcs as pe_rcs
|
||||
from app.pe.pe_rcstag import RCSTag
|
||||
from app.pe.pe_ressemtag import ResSemBUTTag
|
||||
from app.pe.pe_interclasstag import RCSInterclasseTag
|
||||
import app.pe.pe_comp as pe_comp
|
||||
import app.pe.pe_rcss_jury as pe_rcss_jury
|
||||
|
||||
|
||||
class JuryPE(object):
|
||||
@ -94,7 +95,7 @@ class JuryPE(object):
|
||||
self.etudiants.find_etudiants()
|
||||
self.diplomes_ids = self.etudiants.diplomes_ids
|
||||
|
||||
self.rcss_jury = pe_rcs.RCSsJuryPE(self.diplome)
|
||||
self.rcss_jury = pe_rcss_jury.RCSsJuryPE(self.diplome)
|
||||
"""Les informations sur les regroupements de semestres"""
|
||||
|
||||
self.zipdata = io.BytesIO()
|
||||
@ -102,8 +103,8 @@ class JuryPE(object):
|
||||
if not self.diplomes_ids:
|
||||
pe_affichage.pe_print("*** Aucun étudiant diplômé")
|
||||
else:
|
||||
try:
|
||||
self._gen_xls_diplomes(zipfile)
|
||||
|
||||
self._gen_xls_ressembuttags(zipfile)
|
||||
self._gen_rcss()
|
||||
self._gen_xls_sxtags(zipfile)
|
||||
@ -112,6 +113,8 @@ class JuryPE(object):
|
||||
# self._gen_xls_interclassements_rcss(zipfile)
|
||||
# self._gen_xls_synthese_jury_par_tag(zipfile)
|
||||
# self._gen_xls_synthese_par_etudiant(zipfile)
|
||||
except Exception as e:
|
||||
raise e
|
||||
# et le log
|
||||
self._add_log_to_zip(zipfile)
|
||||
|
||||
@ -150,7 +153,7 @@ class JuryPE(object):
|
||||
|
||||
formsemestres = get_formsemestres_etudiants(self.etudiants)
|
||||
pe_affichage.pe_print(
|
||||
f" --> {len(formsemestres)} résultats de semestres à considérer"
|
||||
f"--> {len(formsemestres)} résultats de semestres à considérer"
|
||||
)
|
||||
|
||||
self.ressembuttags = {}
|
||||
@ -186,14 +189,15 @@ class JuryPE(object):
|
||||
)
|
||||
|
||||
self.rcss_jury.cree_rcss(self.etudiants)
|
||||
self.rcss_jury.cree_rcfs(self.etudiants)
|
||||
|
||||
def _gen_xls_sxtags(self, zipfile: ZipFile):
|
||||
"""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).
|
||||
"""
|
||||
# Génère les regroupements de semestres de type Sx
|
||||
pe_affichage.pe_print("*** Génère les RCF (RCS de type Sx)***")
|
||||
pe_affichage.pe_print(
|
||||
"*** Génère les RCSValid (RCS de même Sx donnant lieu à validation du semestre)"
|
||||
)
|
||||
self.rcss_jury.cree_rcfs(self.etudiants)
|
||||
|
||||
# Génère les moyennes des RCS de type Sx
|
||||
@ -204,11 +208,8 @@ class JuryPE(object):
|
||||
for rcf_id, rcf in self.rcss_jury.rcfs.items():
|
||||
# SxTag traduisant le RCF
|
||||
sxtag_id = rcf_id
|
||||
# Les resultats des semestres taggués à prendre en compte dans le RCF
|
||||
ressemstags = {
|
||||
fid: self.ressembuttags[fid] for fid in rcf.semestres_aggreges
|
||||
}
|
||||
self.sxtags[sxtag_id] = pe_sxtag.SxTag(sxtag_id, ressemstags)
|
||||
|
||||
self.sxtags[sxtag_id] = pe_sxtag.SxTag(sxtag_id, rcf, self.ressembuttags)
|
||||
|
||||
# Intègre le bilan des semestres taggués au zip final
|
||||
output = io.BytesIO()
|
||||
@ -232,10 +233,11 @@ class JuryPE(object):
|
||||
def _gen_rcrcfs(self):
|
||||
"""Génère les regroupements cohérents de RCFs qu'ont suivi chaque étudiant"""
|
||||
|
||||
pe_affichage.pe_print("*** Génère les RCRCF (regroupements de RCF de type Sx) amenant du S1 à un semestre final***")
|
||||
pe_affichage.pe_print(
|
||||
"*** Génère les RCRCF (regroupements de RCF de type Sx) amenant du S1 à un semestre final***"
|
||||
)
|
||||
self.rcss_jury.cree_rcrcfs(self.etudiants)
|
||||
|
||||
|
||||
def _gen_xls_rcrcss_tags(self, zipfile: ZipFile):
|
||||
"""Génère les RCS taggués traduisant les moyennes (orientées compétences)
|
||||
de regroupements de semestre de type Sx, xA ou xS.
|
||||
@ -268,7 +270,7 @@ class JuryPE(object):
|
||||
output, engine="openpyxl"
|
||||
) as writer:
|
||||
for rcs_tag in self.rcss_tags.values():
|
||||
onglet = rcs_tag.get_repr(mode="short")
|
||||
onglet = rcs_tag.get_repr(verbose=False)
|
||||
df = rcs_tag.df_moyennes_et_classements()
|
||||
# écriture dans l'onglet
|
||||
df.to_excel(writer, onglet, index=True, header=True)
|
||||
@ -276,7 +278,7 @@ class JuryPE(object):
|
||||
|
||||
self.add_file_to_zip(
|
||||
zipfile,
|
||||
f"RCS_taggues_{self.diplome}.xlsx",
|
||||
f"RCRCFs_{self.diplome}.xlsx",
|
||||
output.read(),
|
||||
path="details",
|
||||
)
|
||||
@ -431,7 +433,7 @@ class JuryPE(object):
|
||||
|
||||
# Ajout des aggrégats
|
||||
for aggregat in pe_rcs.TOUS_LES_RCS:
|
||||
descr = pe_rcs.TYPES_RCS[aggregat]["descr"]
|
||||
descr = app.pe.rcss.constantes.TYPES_RCS[aggregat]["descr"]
|
||||
|
||||
# Les trajectoires (tagguées) suivies par les étudiants pour l'aggrégat et le tag
|
||||
# considéré
|
||||
@ -632,7 +634,7 @@ def get_formsemestres_etudiants(etudiants: pe_etudiant.EtudiantsJuryPE) -> dict:
|
||||
|
||||
def compute_interclassements(
|
||||
etudiants: pe_etudiant.EtudiantsJuryPE,
|
||||
trajectoires_jury_pe: pe_rcs.RCSsJuryPE,
|
||||
trajectoires_jury_pe: pe_rcss_jury.RCSsJuryPE,
|
||||
trajectoires_tagguees: dict[tuple, pe_rcs.RCS],
|
||||
):
|
||||
"""Interclasse les étudiants, (nom d') aggrégat par aggrégat,
|
||||
|
@ -181,7 +181,7 @@ class MoyennesTag:
|
||||
self.matrice_notes: pd.DataFrame = matrice_notes
|
||||
"""Les notes aux UEs ou aux compétences (DataFrame)"""
|
||||
|
||||
self.matrice_coeffs: pd.DataFrame = matrice_coeffs
|
||||
self.matrice_coeffs_moy_gen: pd.DataFrame = matrice_coeffs
|
||||
"""Les coeffs à appliquer pour le calcul des moyennes générales
|
||||
(toutes UE ou compétences confondues). NaN si étudiant non inscrit"""
|
||||
|
||||
@ -193,7 +193,7 @@ class MoyennesTag:
|
||||
self.moyennes[col] = Moyenne(notes)
|
||||
|
||||
# Les moyennes générales
|
||||
notes_gen = self.compute_moy_gen(self.matrice_notes, self.matrice_coeffs)
|
||||
notes_gen = self.compute_moy_gen(self.matrice_notes, self.matrice_coeffs_moy_gen)
|
||||
self.notes_gen = notes_gen
|
||||
"""Les notes générales (moyenne toutes UEs confonudes)"""
|
||||
self.moyenne_gen = Moyenne(notes_gen)
|
||||
|
@ -1,248 +1,11 @@
|
||||
##############################################################################
|
||||
# Module "Avis de poursuite d'étude"
|
||||
# conçu et développé par Cléo Baras (IUT de Grenoble)
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
Created on 01-2024
|
||||
|
||||
@author: barasc
|
||||
"""
|
||||
|
||||
import app.pe.rcss.pe_rcf as pe_rcf
|
||||
import app.pe.rcss.pe_rcrcf as pe_rcrcf
|
||||
import app.pe.pe_etudiant as pe_etudiant
|
||||
import app.pe.pe_comp as pe_comp
|
||||
|
||||
import app.pe.rcss.rcss_constantes as rcss_constantes
|
||||
from app.models import FormSemestre
|
||||
from app.pe import pe_sxtag, pe_affichage
|
||||
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
|
||||
|
||||
TYPES_RCS = {
|
||||
"S1": {
|
||||
"aggregat": ["S1"],
|
||||
"descr": "Semestre 1 (S1)",
|
||||
},
|
||||
"S2": {
|
||||
"aggregat": ["S2"],
|
||||
"descr": "Semestre 2 (S2)",
|
||||
},
|
||||
"1A": {
|
||||
"aggregat": ["S1", "S2"],
|
||||
"descr": "BUT1 (S1+S2)",
|
||||
},
|
||||
"S3": {
|
||||
"aggregat": ["S3"],
|
||||
"descr": "Semestre 3 (S3)",
|
||||
},
|
||||
"S4": {
|
||||
"aggregat": ["S4"],
|
||||
"descr": "Semestre 4 (S4)",
|
||||
},
|
||||
"2A": {
|
||||
"aggregat": ["S3", "S4"],
|
||||
"descr": "BUT2 (S3+S4)",
|
||||
},
|
||||
"3S": {
|
||||
"aggregat": ["S1", "S2", "S3"],
|
||||
"descr": "Moyenne du semestre 1 au semestre 3 (S1+S2+S3)",
|
||||
},
|
||||
"4S": {
|
||||
"aggregat": ["S1", "S2", "S3", "S4"],
|
||||
"descr": "Moyenne du semestre 1 au semestre 4 (S1+S2+S3+S4)",
|
||||
},
|
||||
"S5": {
|
||||
"aggregat": ["S5"],
|
||||
"descr": "Semestre 5 (S5)",
|
||||
},
|
||||
"S6": {
|
||||
"aggregat": ["S6"],
|
||||
"descr": "Semestre 6 (S6)",
|
||||
},
|
||||
"3A": {
|
||||
"aggregat": ["S5", "S6"],
|
||||
"descr": "3ème année (S5+S6)",
|
||||
},
|
||||
"5S": {
|
||||
"aggregat": ["S1", "S2", "S3", "S4", "S5"],
|
||||
"descr": "Moyenne du semestre 1 au semestre 5 (S1+S2+S3+S4+S5)",
|
||||
},
|
||||
"6S": {
|
||||
"aggregat": ["S1", "S2", "S3", "S4", "S5", "S6"],
|
||||
"descr": "Moyenne globale (S1+S2+S3+S4+S5+S6)",
|
||||
},
|
||||
}
|
||||
"""Dictionnaire détaillant les différents regroupements cohérents
|
||||
de semestres (RCS), en leur attribuant un nom et en détaillant
|
||||
le nom des semestres qu'ils regroupent et l'affichage qui en sera fait
|
||||
dans les tableurs de synthèse.
|
||||
"""
|
||||
|
||||
TOUS_LES_RCS_AVEC_PLUSIEURS_SEM = [cle for cle in TYPES_RCS if not cle.startswith("S")]
|
||||
TOUS_LES_RCS = list(TYPES_RCS.keys())
|
||||
TOUS_LES_SEMESTRES = [cle for cle in TYPES_RCS if cle.startswith("S")]
|
||||
|
||||
|
||||
def get_descr_rcs(nom_rcs: str) -> str:
|
||||
"""Renvoie la description pour les tableurs de synthèse
|
||||
Excel d'un nom de RCS"""
|
||||
return TYPES_RCS[nom_rcs]["descr"]
|
||||
|
||||
|
||||
class RCS:
|
||||
"""Modélise un regroupement cohérent de semestres (formsemestre ou de Sx)"""
|
||||
|
||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||
self.nom: str = nom_rcs
|
||||
"""Nom du RCS"""
|
||||
|
||||
self.formsemestre_final: FormSemestre = semestre_final
|
||||
"""FormSemestre terminal du RCS"""
|
||||
|
||||
self.rang_final = self.formsemestre_final.semestre_id
|
||||
"""Le rang du formsemestre final"""
|
||||
|
||||
self.rcs_id: (str, int) = (nom_rcs, semestre_final.formsemestre_id)
|
||||
"""Identifiant du RCS sous forme (nom_rcs, id du semestre_terminal)"""
|
||||
|
||||
def get_formsemestre_id_final(self) -> int:
|
||||
"""Renvoie l'identifiant du formsemestre final du RCS
|
||||
|
||||
Returns:
|
||||
L'id du formsemestre final (marquant la fin) du RCS
|
||||
"""
|
||||
return self.formsemestre_final.formsemestre_id
|
||||
|
||||
def __repr__(self):
|
||||
"""Représentation textuelle d'un RCS"""
|
||||
return f"{self.nom} ({self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"
|
||||
|
||||
def get_repr(self):
|
||||
return self.__repr__()
|
||||
|
||||
def __eq(self, other):
|
||||
"""Egalité de RCS"""
|
||||
return (
|
||||
self.nom == other.nom
|
||||
and self.formsemestre_final == other.formsemestre_final
|
||||
)
|
||||
|
||||
|
||||
class RCF(RCS):
|
||||
"""Modélise un ensemble de (form)semestres d'étudiants
|
||||
associé à un type de regroupement cohérent de semestres
|
||||
donné (par ex: 'S2', '3S', '2A').
|
||||
|
||||
Si le RCF est un semestre de type Si, stocke les
|
||||
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)
|
||||
|
||||
Pour le RCF de type iS ou iA (par ex, 3A=S1+S2+S3), identifie
|
||||
les semestres que les étudiants ont suivis pour les amener jusqu'au semestre
|
||||
terminal du RCS (par ex: ici un S3).
|
||||
|
||||
Ces semestres peuvent être :
|
||||
|
||||
* des S1+S2+S1+S2+S3 si redoublement de la 1ère année
|
||||
* des S1+S2+(année de césure)+S3 si césure, ...
|
||||
|
||||
Args:
|
||||
nom_rcs: Un nom du RCS (par ex: '5S')
|
||||
semestre_final: Le formsemestre final du RCS
|
||||
"""
|
||||
|
||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||
RCS.__init__(self, nom_rcs, semestre_final)
|
||||
|
||||
self.semestres_aggreges: dict[int:FormSemestre] = {}
|
||||
"""Formsemestres regroupés dans le RCS"""
|
||||
|
||||
def add_semestres_a_aggreger(self, semestres: dict[int:FormSemestre]):
|
||||
"""Ajout de semestres aux semestres à regrouper
|
||||
|
||||
Args:
|
||||
semestres: Dictionnaire ``{fid: Formsemestre)``
|
||||
"""
|
||||
self.semestres_aggreges = self.semestres_aggreges | semestres
|
||||
|
||||
def get_repr(self, verbose=True) -> str:
|
||||
"""Représentation textuelle d'un RCS
|
||||
basé sur ses semestres aggrégés"""
|
||||
|
||||
noms = []
|
||||
for fid in self.semestres_aggreges:
|
||||
semestre = self.semestres_aggreges[fid]
|
||||
noms.append(f"S{semestre.semestre_id}({fid})")
|
||||
noms = sorted(noms)
|
||||
title = f"""{str(self)}"""
|
||||
if verbose and noms:
|
||||
title += " - " + "+".join(noms)
|
||||
return title
|
||||
|
||||
|
||||
|
||||
def get_rcf_from_semestres_aggreges(
|
||||
rcfs: dict[(str, int):RCF], semestres_a_aggreges: list[FormSemestre]
|
||||
) -> (str, int):
|
||||
"""Partant d'un dictionnaire de RCFs (de la forme
|
||||
``{ (nom_rcs, fid): RCF }, et connaissant une liste
|
||||
de (form)semestres à aggréger, renvoie l'identifiant
|
||||
(nom_rcs, fid) du RCFs qui lui correspond (c'est à dire celui dont
|
||||
les semestres_aggregés par le RCF sont les même que les
|
||||
semestres_a_aggreger.
|
||||
|
||||
Returns:
|
||||
rcf_id: L'identifiant du RCF trouvé
|
||||
"""
|
||||
fids_semestres_a_aggreger = set(
|
||||
[frms.formsemestre_id for frms in semestres_a_aggreges]
|
||||
)
|
||||
for rcf_id, rcf in rcfs.items():
|
||||
fids_rcf = set(rcf.semestres_aggreges)
|
||||
if fids_rcf == fids_semestres_a_aggreger:
|
||||
return rcf_id
|
||||
return None
|
||||
|
||||
|
||||
class RCRCF:
|
||||
"""Modélise les RCF d'étudiants suivis par un étudiant dans
|
||||
le cadre d'un RCS donné (par ex: 3S=S1+S2+S3).
|
||||
|
||||
Pour rappel : un RCF (par ex. S1) combine les semestres 1 qu'a suivi
|
||||
l'étudiant pour valider son S1 (1 si étudiant standard, 2 si redoublant).
|
||||
|
||||
Le RCRCF 3S est donc le regroupement du RCF S1 + RCF S2 + RCF S3.
|
||||
|
||||
Il est identifié par le formsemestre de S3 marquant la fin du regroupement.
|
||||
|
||||
|
||||
Args:
|
||||
nom_rcs: Un nom du RCS (par ex: '5S')
|
||||
semestre_final: Le semestre final du RCS
|
||||
"""
|
||||
|
||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||
RCS.__init__(self, nom_rcs, semestre_final)
|
||||
|
||||
self.rcfs_aggreges: dict[(str, int) : pe_sxtag.SxTag] = {}
|
||||
"""Les RCFs à aggréger"""
|
||||
|
||||
def add_rcfs_a_aggreger(self, rcfs: dict[(str, int):RCF]):
|
||||
"""Ajout des RCFs aux RCFS à regrouper
|
||||
|
||||
Args:
|
||||
rcfs: Dictionnaire ``{(str,fid): RCF}`` à ajouter
|
||||
"""
|
||||
self.rcfs_aggreges = self.rcfs_aggreges | rcfs
|
||||
|
||||
def get_repr(self, verbose=True) -> str:
|
||||
"""Représentation textuelle d'un RCSF
|
||||
basé sur ses RCF aggrégés"""
|
||||
|
||||
noms = []
|
||||
for rcf_id, rcf in self.rcfs_aggreges.items():
|
||||
noms.append(rcf.get_repr())
|
||||
title = f"""{str(self)}"""
|
||||
if verbose and noms:
|
||||
title += " : " + "+".join(noms)
|
||||
return title
|
||||
from app.pe import pe_affichage
|
||||
|
||||
|
||||
class RCSsJuryPE:
|
||||
@ -257,28 +20,28 @@ class RCSsJuryPE:
|
||||
self.annee_diplome = annee_diplome
|
||||
"""Année de diplômation"""
|
||||
|
||||
self.rcss: dict[tuple(int, str) : RCF] = {}
|
||||
self.rcss: dict[tuple(int, str): pe_rcf.RCF] = {}
|
||||
"""Ensemble des RCS recensés"""
|
||||
|
||||
self.rcss_suivis: dict[int:dict] = {}
|
||||
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
||||
son RCS : {etudid: {nom_RCS: RCS}}"""
|
||||
|
||||
self.rcfs: dict[tuple(int, str) : RCF] = {}
|
||||
self.rcfs: dict[tuple(int, str) : pe_rcf.RCF] = {}
|
||||
"""Ensemble des RCF recensés : {(nom_RCS, fid_terminal): RCF}"""
|
||||
|
||||
self.rcfs_suivis: dict[int:dict] = {}
|
||||
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
||||
son RCS : {etudid: {nom_RCS: RCF}}"""
|
||||
|
||||
self.rcrcfs: dict[tuple(int, str) : RCRCF] = {}
|
||||
self.rcrcfs: dict[tuple(int, str) : pe_rcrcf.RCRCF] = {}
|
||||
"""Ensemble des RCS recensés : {(nom_RCS, fid_terminal): RCRCF}"""
|
||||
|
||||
self.rcrcfs_suivis: dict[int:str] = {}
|
||||
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
||||
son RCRCF : {etudid: {nom_RCS: RCSx}}"""
|
||||
|
||||
def cree_rcss(self, etudiants: EtudiantsJuryPE):
|
||||
def cree_rcss(self, etudiants: pe_etudiant.EtudiantsJuryPE):
|
||||
"""Créé tous les RCS, au regard du cursus des étudiants
|
||||
analysés + les mémorise dans les données de l'étudiant
|
||||
|
||||
@ -286,17 +49,17 @@ class RCSsJuryPE:
|
||||
etudiants: Les étudiants à prendre en compte dans le Jury PE
|
||||
"""
|
||||
|
||||
tous_les_aggregats = rcss_constantes.TOUS_LES_SEMESTRES + rcss_constantes.TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
|
||||
for etudid in etudiants.trajectoires:
|
||||
self.rcss_suivis[etudid] = {
|
||||
aggregat: None
|
||||
for aggregat in pe_comp.TOUS_LES_SEMESTRES
|
||||
+ TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
|
||||
for aggregat in tous_les_aggregats
|
||||
}
|
||||
|
||||
for nom_rcs in pe_comp.TOUS_LES_SEMESTRES + TOUS_LES_RCS_AVEC_PLUSIEURS_SEM:
|
||||
for nom_rcs in tous_les_aggregats:
|
||||
# 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)
|
||||
noms_semestre_de_aggregat = TYPES_RCS[nom_rcs]["aggregat"]
|
||||
noms_semestre_de_aggregat = rcss_constantes.TYPES_RCS[nom_rcs]["aggregat"]
|
||||
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
|
||||
|
||||
for etudid in etudiants.trajectoires:
|
||||
@ -305,12 +68,12 @@ class RCSsJuryPE:
|
||||
# (par ex: son dernier S3 en date)
|
||||
trajectoire = etudiants.trajectoires[etudid][nom_semestre_terminal]
|
||||
if trajectoire:
|
||||
formsemestre_final = get_dernier_semestre_en_date(trajectoire)
|
||||
formsemestre_final = pe_etudiant.get_dernier_semestre_en_date(trajectoire)
|
||||
|
||||
# Ajout ou récupération du RCS associé
|
||||
rcs_id = (nom_rcs, formsemestre_final.formsemestre_id)
|
||||
if rcs_id not in self.rcss:
|
||||
self.rcss[rcs_id] = RCF(nom_rcs, formsemestre_final)
|
||||
self.rcss[rcs_id] = pe_rcf.RCF(nom_rcs, formsemestre_final)
|
||||
rcs = self.rcss[rcs_id]
|
||||
|
||||
# La liste des semestres de l'étudiant à prendre en compte
|
||||
@ -328,23 +91,22 @@ class RCSsJuryPE:
|
||||
# Affichage pour debug
|
||||
jeunes = list(enumerate(self.rcss_suivis))
|
||||
for no_etud, etudid in jeunes[:20]:
|
||||
pe_affichage.pe_print(f"-> {etudiants.identites[etudid].nomprenom} :")
|
||||
pe_affichage.pe_print(f"--> {etudiants.identites[etudid].nomprenom} (#{etudid}) :")
|
||||
for nom_rcs, rcs in self.rcss_suivis[etudid].items():
|
||||
if rcs:
|
||||
pe_affichage.pe_print(f" > RCS {nom_rcs}: {rcs.get_repr()}")
|
||||
|
||||
def cree_rcfs(self, etudiants: EtudiantsJuryPE):
|
||||
def cree_rcfs(self, etudiants: pe_etudiant.EtudiantsJuryPE):
|
||||
"""Créé les RCFs en ne conservant dans les RCS que les regroupements
|
||||
de type Sx"""
|
||||
self.rcfs = {}
|
||||
for rcs_id, rcs in self.rcss.items():
|
||||
if rcs and rcs.nom in pe_comp.TOUS_LES_SEMESTRES:
|
||||
if rcs and rcs.nom in rcss_constantes.TOUS_LES_SEMESTRES:
|
||||
self.rcfs[rcs_id] = rcs
|
||||
print(self.rcfs)
|
||||
|
||||
for etudid in self.rcss_suivis:
|
||||
for nom_rcs, rcs in self.rcss_suivis[etudid].items():
|
||||
if rcs and nom_rcs in pe_comp.TOUS_LES_SEMESTRES:
|
||||
if rcs and nom_rcs in rcss_constantes.TOUS_LES_SEMESTRES:
|
||||
if etudid not in self.rcfs_suivis:
|
||||
self.rcfs_suivis[etudid] = {}
|
||||
self.rcfs_suivis[etudid][nom_rcs] = rcs
|
||||
@ -355,11 +117,11 @@ class RCSsJuryPE:
|
||||
pe_affichage.pe_print(f"-> {etudiants.identites[etudid].nomprenom} :")
|
||||
for nom_rcs, rcs in self.rcfs_suivis[etudid].items():
|
||||
if rcs:
|
||||
pe_affichage.pe_print(f" > RCF {nom_rcs}: {rcs.get_repr()}")
|
||||
pe_affichage.pe_print(f" > RCSValid {nom_rcs}: {rcs.get_repr()}")
|
||||
else:
|
||||
pe_affichage.pe_print(f" > RCF {nom_rcs}: <vide> !!! ")
|
||||
pe_affichage.pe_print(f" > RCSValid {nom_rcs}: <vide>")
|
||||
|
||||
def cree_rcrcfs(self, etudiants: EtudiantsJuryPE):
|
||||
def cree_rcrcfs(self, etudiants: pe_etudiant.EtudiantsJuryPE):
|
||||
"""Créé tous les RCRCF, au regard du cursus des étudiants
|
||||
analysés (trajectoires traduisant son parcours dans les
|
||||
différents semestres) + les mémorise dans les données de l'étudiant
|
||||
@ -390,7 +152,7 @@ class RCSsJuryPE:
|
||||
|
||||
# Ajout du RCRCF
|
||||
if rcf_id not in self.rcrcfs:
|
||||
self.rcrcfs[rcf_id] = RCRCF(rcf_id, rcf.formsemestre_final)
|
||||
self.rcrcfs[rcf_id] = pe_rcrcf.RCRCF(rcf_id, rcf.formsemestre_final)
|
||||
rcrcf = self.rcrcfs[rcf_id]
|
||||
|
||||
# Ajout des RCFs au RCRCF
|
||||
@ -410,6 +172,8 @@ class RCSsJuryPE:
|
||||
else:
|
||||
pe_affichage.pe_print(f" > RCRCF {nom_rcs}: <vide> !!! ")
|
||||
|
||||
|
||||
|
||||
def get_rcs_etudiant(
|
||||
semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str
|
||||
) -> dict[int, FormSemestre]:
|
||||
@ -439,7 +203,7 @@ def get_rcs_etudiant(
|
||||
numero_semestres_possibles = [numero_semestre_terminal]
|
||||
elif nom_rcs.endswith("A"): # les années
|
||||
numero_semestres_possibles = [
|
||||
int(sem[-1]) for sem in TYPES_RCS[nom_rcs]["aggregat"]
|
||||
int(sem[-1]) for sem in rcss_constantes.TYPES_RCS[nom_rcs]["aggregat"]
|
||||
]
|
||||
assert numero_semestre_terminal in numero_semestres_possibles
|
||||
else: # les xS = tous les semestres jusqu'à Sx (eg S1, S2, S3 pour un S3 terminal)
|
||||
@ -454,3 +218,27 @@ def get_rcs_etudiant(
|
||||
):
|
||||
semestres_aggreges[fid] = semestre
|
||||
return semestres_aggreges
|
||||
|
||||
|
||||
def get_rcf_from_semestres_aggreges(
|
||||
rcfs: dict[(str, int):pe_rcf.RCF], semestres_a_aggreges: list[FormSemestre]
|
||||
) -> (str, int):
|
||||
"""Partant d'un dictionnaire de RCFs (de la forme
|
||||
``{ (nom_rcs, fid): RCF }, et connaissant une liste
|
||||
de (form)semestres à aggréger, renvoie l'identifiant
|
||||
(nom_rcs, fid) du RCFs qui lui correspond (c'est à dire celui dont
|
||||
les semestres_aggregés par le RCF sont les même que les
|
||||
semestres_a_aggreger.
|
||||
|
||||
Returns:
|
||||
rcf_id: L'identifiant du RCF trouvé
|
||||
"""
|
||||
fids_semestres_a_aggreger = set(
|
||||
[frms.formsemestre_id for frms in semestres_a_aggreges]
|
||||
)
|
||||
for rcf_id, rcf in rcfs.items():
|
||||
fids_rcf = set(rcf.semestres_aggreges)
|
||||
if fids_rcf == fids_semestres_a_aggreger:
|
||||
return rcf_id
|
||||
return None
|
||||
|
@ -38,18 +38,18 @@ Created on Fri Sep 9 09:15:05 2016
|
||||
|
||||
from app.comp.res_sem import load_formsemestre_results
|
||||
from app.pe import pe_affichage
|
||||
from app.pe.pe_ressemtag import ResSemBUTTag
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from app.pe.pe_rcs import RCS, RCRCF
|
||||
from app.pe.pe_sxtag import SxTag
|
||||
import app.pe.rcss.pe_rcs as pe_rcs
|
||||
import app.pe.rcss.pe_rcrcf as pe_rcrcf
|
||||
import app.pe.pe_sxtag as pe_sxtag
|
||||
|
||||
from app.pe.pe_tabletags import TableTag
|
||||
from app.pe.pe_moytag import MoyennesTag
|
||||
|
||||
|
||||
class RCSTag(TableTag):
|
||||
def __init__(self, rcrcf: RCS, sxstags: dict[(str, int): SxTag]):
|
||||
def __init__(self, rcrcf: pe_rcs.RCS, sxstags: dict[(str, int): pe_sxtag.SxTag]):
|
||||
"""Calcule les moyennes par tag (orientées compétences)
|
||||
d'un regroupement de SxTag
|
||||
(RCRCF), pour extraire les classements par tag pour un
|
||||
@ -65,7 +65,7 @@ class RCSTag(TableTag):
|
||||
self.rcs_id: tuple(str, int) = rcrcf.rcs_id
|
||||
"""Identifiant du RCS taggué (identique au RCS sur lequel il s'appuie)"""
|
||||
|
||||
self.rcrcf: RCRCF = rcrcf
|
||||
self.rcrcf: pe_rcrcf.RCRCF = rcrcf
|
||||
"""RCRCF associé au RCS taggué"""
|
||||
|
||||
self.nom = self.get_repr()
|
||||
@ -74,6 +74,8 @@ class RCSTag(TableTag):
|
||||
self.formsemestre_terminal = rcrcf.formsemestre_final
|
||||
"""Le formsemestre terminal"""
|
||||
|
||||
pe_affichage.pe_print(f"-> {self.get_repr(verbose=True)}")
|
||||
|
||||
# Les résultats du formsemestre terminal
|
||||
nt = load_formsemestre_results(self.formsemestre_terminal)
|
||||
|
||||
@ -95,10 +97,12 @@ class RCSTag(TableTag):
|
||||
# Les compétences (extraites de tous les Sxtags)
|
||||
self.competences_sorted = self.do_complist()
|
||||
"""Compétences extraites de tous les SxTag aggrégés"""
|
||||
pe_affichage.pe_print(f"* Compétences : {', '.join(self.competences_sorted)}")
|
||||
|
||||
# Les tags
|
||||
self.tags_sorted = self.do_taglist()
|
||||
"""Tags extraits de tous les SxTag aggrégés"""
|
||||
pe_affichage.pe_print(f"* Tags : {', '.join(self.tags_sorted)}")
|
||||
|
||||
# Les moyennes
|
||||
self.moyennes_tags: dict[str, MoyennesTag] = {}
|
||||
@ -121,10 +125,13 @@ class RCSTag(TableTag):
|
||||
"""Egalité de 2 RCS taggués sur la base de leur identifiant"""
|
||||
return self.rcs_id == other.sxtag_id
|
||||
|
||||
def get_repr(self, verbose=False) -> str:
|
||||
def get_repr(self, verbose=True) -> str:
|
||||
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
||||
est basée)"""
|
||||
if verbose:
|
||||
return self.rcrcf.get_repr(verbose=verbose)
|
||||
else:
|
||||
return f"{self.__class__.__name__} ({self.rcs_id})"
|
||||
|
||||
def compute_notes_comps_cube(self, tag):
|
||||
"""Pour un tag donné, construit :
|
||||
@ -154,10 +161,11 @@ class RCSTag(TableTag):
|
||||
|
||||
# Charge les notes et les coeffs du semestre tag
|
||||
notes = moys_tag.matrice_notes.copy() # avec une copie
|
||||
coeffs = moys_tag.matrice_coeffs.copy() # les coeffs
|
||||
coeffs = moys_tag.matrice_coeffs_moy_gen.copy() # les coeffs
|
||||
|
||||
# Traduction des UE en compétences
|
||||
association_ues_comp = moys_tag.competences
|
||||
association_ues_comp = self.mapping_ue_competences()
|
||||
print(association_ues_comp)
|
||||
ues_columns_df = notes.columns
|
||||
comp_associes_aux_ues = [association_ues_comp[ue] for ue in ues_columns_df]
|
||||
notes.columns = comp_associes_aux_ues
|
||||
@ -204,16 +212,20 @@ class RCSTag(TableTag):
|
||||
tags = []
|
||||
for frmsem_id in self.sxstags:
|
||||
tags.extend(self.sxstags[frmsem_id].tags_sorted)
|
||||
pe_affichage.pe_print(f"* Tags : {', '.join(tags)}")
|
||||
return sorted(set(tags))
|
||||
|
||||
def mapping_ue_competences(self):
|
||||
"""Dictionnaire {ue: competences} extrait des SxTags"""
|
||||
dict_competences = {}
|
||||
for sxtag_id, sxtag in self.sxstags.items():
|
||||
comp = sxtag.competences
|
||||
dict_competences |= comp
|
||||
return dict_competences
|
||||
|
||||
def do_complist(self):
|
||||
"""Synthétise les compétences à partir des Sxtags aggrégés"""
|
||||
competences = []
|
||||
for sxtag_id, sxtag in self.sxstags.items():
|
||||
comp = list(sxtag.moyennes_tags["but"].matrice_notes.columns)
|
||||
competences.extend(comp)
|
||||
return sorted(set(competences))
|
||||
dict_comptences = self.mapping_ue_competences()
|
||||
return sorted(set(dict_comptences.values()))
|
||||
|
||||
|
||||
def compute_notes_competences(
|
||||
@ -248,7 +260,7 @@ def compute_notes_competences(
|
||||
|
||||
# Enlève les NaN du cube de notes pour les entrées manquantes
|
||||
set_cube_no_nan = np.nan_to_num(set_cube, nan=0.0)
|
||||
coeffs_cube_no_nan = no.nan_to_num(coeff_cube, nan=0.0)
|
||||
coeffs_cube_no_nan = np.nan_to_num(coeff_cube, nan=0.0)
|
||||
|
||||
# Les moyennes par tag
|
||||
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
||||
|
@ -48,7 +48,7 @@ import app.pe.pe_etudiant as pe_etudiant
|
||||
import app.pe.pe_tabletags as pe_tabletags
|
||||
from app.pe.pe_moytag import MoyennesTag
|
||||
from app.scodoc import sco_tag_module
|
||||
from app.scodoc.codes_cursus import UE_SPORT
|
||||
from app.scodoc import codes_cursus as sco_codes
|
||||
|
||||
|
||||
class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||
@ -66,44 +66,51 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||
ResultatsSemestreBUT.__init__(self, formsemestre)
|
||||
pe_tabletags.TableTag.__init__(self)
|
||||
|
||||
# Le semestre
|
||||
# self.formsemestre_id = self.formsemestre.formsemestre_id
|
||||
# self.formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
|
||||
# Le nom du res_semestre taggué
|
||||
self.nom = self.get_repr(verbose=True)
|
||||
|
||||
pe_affichage.pe_print(f"--> Résultats de semestre taggués {self.nom}")
|
||||
|
||||
# Les résultats du semestre
|
||||
# self.nt = load_formsemestre_results(self.formsemestre)
|
||||
|
||||
# Les étudiants (etuds, états civils & etudis) ajouté
|
||||
self.add_etuds(self.etuds)
|
||||
self.etudids_sorted = sorted(self.etudids)
|
||||
|
||||
# Les UEs (et les dispenses d'UE)
|
||||
ues_hors_sport = [ue for ue in self.ues if ue.type != UE_SPORT]
|
||||
# self.ues
|
||||
ues_standards = [ue for ue in self.ues if ue.type == sco_codes.UE_STANDARD]
|
||||
|
||||
# Les UEs en fonction des parcours
|
||||
self.ues_inscr_parcours_df = self.load_ues_inscr_parcours()
|
||||
|
||||
# Les compétences associées aux UEs
|
||||
# Les compétences associées aux UEs (définies par les acronymes)
|
||||
self.competences = {}
|
||||
"""L'association acronyme d'UEs -> compétence"""
|
||||
for ue in self.ues:
|
||||
if ue.type != UE_SPORT:
|
||||
assert ue.niveau_competence, ScoValueError("Des UEs ne sont pas rattachées à des compétences")
|
||||
if ue.type == sco_codes.UE_STANDARD:
|
||||
assert ue.niveau_competence, ScoValueError(
|
||||
"Des UEs ne sont pas rattachées à des compétences"
|
||||
)
|
||||
nom = ue.niveau_competence.competence.titre
|
||||
self.competences[ue.ue_id] = nom
|
||||
self.competences[ue.acronyme] = nom
|
||||
|
||||
# Les acronymes des UEs
|
||||
self.ues_to_acronymes = {ue.id: ue.acronyme for ue in ues_standards}
|
||||
self.acronymes_sorted = sorted(self.ues_to_acronymes.values())
|
||||
"""Les acronymes de UE triés par ordre alphabétique"""
|
||||
|
||||
# Les tags personnalisés et auto:
|
||||
tags_dict = self._get_tags_dict()
|
||||
self._check_tags(tags_dict)
|
||||
# self.tags = [tag for cat in dict_tags for tag in dict_tags[cat]]
|
||||
|
||||
# Les coefficients
|
||||
matrice_coeffs = self.ues_inscr_parcours_df * [
|
||||
ue.ects
|
||||
for ue in ues_hors_sport # if ue.type != UE_SPORT <= déjà supprimé
|
||||
# Les coefficients pour le calcul de la moyenne générale
|
||||
self.matrice_coeffs_moy_gen = self.ues_inscr_parcours_df * [
|
||||
ue.ects for ue in ues_standards # if ue.type != UE_SPORT <= déjà supprimé
|
||||
]
|
||||
|
||||
# Les capitalisations (mask etuids x acronyme_ue valant True si capitalisée, False sinon)
|
||||
self.capitalisations = self._get_capitalisations(ues_standards)
|
||||
|
||||
|
||||
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
|
||||
self.moyennes_tags = {}
|
||||
|
||||
@ -111,46 +118,56 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
||||
infos_tag = tags_dict["personnalises"][tag]
|
||||
moy_ues_tag = self.compute_moy_ues_tag(infos_tag)
|
||||
# moy_gen_tag = self.compute_moy_gen_tag(moy_ues_tag)
|
||||
|
||||
self.moyennes_tags[tag] = MoyennesTag(
|
||||
tag,
|
||||
moy_ues_tag,
|
||||
matrice_coeffs
|
||||
tag, moy_ues_tag, self.matrice_coeffs_moy_gen
|
||||
)
|
||||
|
||||
# Ajoute les d'UE moyennes générales de BUT pour le semestre considéré
|
||||
# moy_gen_but = self.nt.etud_moy_gen
|
||||
# self.moyennes_tags["but"] = MoyenneTag("but", [], None, moy_gen_but, )
|
||||
|
||||
# Ajoute les moyennes par UEs (et donc par compétence) + la moyenne générale (but)
|
||||
# Ajoute les moyennes par UEs + la moyenne générale (but)
|
||||
df_ues = pd.DataFrame(
|
||||
{ue.id: self.etud_moy_ue[ue.id] for ue in ues_hors_sport},
|
||||
{ue.id: self.etud_moy_ue[ue.id] for ue in ues_standards},
|
||||
index=self.etudids,
|
||||
)
|
||||
# Transforme les UEs en acronyme
|
||||
colonnes = df_ues.columns
|
||||
acronymes = [self.ues_to_acronymes[col] for col in colonnes]
|
||||
df_ues.columns = acronymes
|
||||
|
||||
self.moyennes_tags["but"] = MoyennesTag(
|
||||
"but", df_ues, matrice_coeffs # , moy_gen_but
|
||||
"but", df_ues, self.matrice_coeffs_moy_gen # , moy_gen_but
|
||||
)
|
||||
|
||||
self.tags_sorted = self.get_all_tags()
|
||||
"""Tags (personnalisés+compétences) par ordre alphabétique"""
|
||||
|
||||
# Synthétise l'ensemble des moyennes dans un dataframe
|
||||
|
||||
# self.notes = self.df_notes()
|
||||
# """Dataframe synthétique des notes par tag"""
|
||||
|
||||
# pe_affichage.pe_print(
|
||||
# f" => Traitement des tags {', '.join(self.tags_sorted)}"
|
||||
# )
|
||||
|
||||
def get_repr(self, verbose=False):
|
||||
"""Nom affiché pour le semestre taggué"""
|
||||
if verbose:
|
||||
return f"{self.formsemestre} ({self.formsemestre.formsemestre_id})"
|
||||
return f"{self.formsemestre} (#{self.formsemestre.formsemestre_id})"
|
||||
else:
|
||||
return pe_etudiant.nom_semestre_etape(self.formsemestre, avec_fid=True)
|
||||
|
||||
def _get_capitalisations(self, ues_hors_sport) -> pd.DataFrame:
|
||||
"""Renvoie un dataFrame résumant les UEs capitalisables par les
|
||||
étudiants, d'après les décisions de jury
|
||||
|
||||
Args:
|
||||
ues_hors_sport: Liste des UEs autres que le sport
|
||||
"""
|
||||
capitalisations = pd.DataFrame(False, index=self.etudids_sorted, columns=self.acronymes_sorted)
|
||||
self.get_formsemestre_validations() # charge les validations
|
||||
res_jury = self.validations
|
||||
if res_jury:
|
||||
for etud in self.etuds:
|
||||
etudid = etud.etudid
|
||||
decisions = res_jury.decisions_jury_ues.get(etudid, {})
|
||||
for ue in ues_hors_sport:
|
||||
if ue.id in decisions and decisions[ue.id]["code"] == sco_codes.ADM:
|
||||
capitalisations.loc[etudid, ue.acronyme] = True
|
||||
# pe_affichage.pe_print(
|
||||
# f" ⚠ Capitalisation de {ue.acronyme} pour {etud.etat_civil}"
|
||||
# )
|
||||
return capitalisations
|
||||
|
||||
def compute_moy_ues_tag(self, info_tag: dict[int, dict]) -> pd.DataFrame:
|
||||
"""Calcule la moyenne par UE des étudiants pour un tag,
|
||||
en ayant connaissance des informations sur le tag.
|
||||
@ -164,7 +181,7 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||
|
||||
# Adaptation du mask de calcul des moyennes au tag visé
|
||||
modimpls_mask = [
|
||||
modimpl.module.ue.type != UE_SPORT
|
||||
modimpl.module.ue.type == sco_codes.UE_STANDARD
|
||||
for modimpl in self.formsemestre.modimpls_sorted
|
||||
]
|
||||
|
||||
@ -216,9 +233,6 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||
self.formsemestre
|
||||
)
|
||||
noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys())))
|
||||
pe_affichage.pe_print(
|
||||
f"* Tags personnalisés (extraits du programme de formation) : {', '.join(noms_tags_perso)}"
|
||||
)
|
||||
|
||||
# Les tags automatiques
|
||||
# Déduit des compétences
|
||||
@ -229,8 +243,11 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||
dict_tags["auto"] = {"but": {}}
|
||||
|
||||
noms_tags_auto = sorted(list(set(dict_tags["auto"].keys()))) # + noms_tags_comp
|
||||
|
||||
# Affichage
|
||||
pe_affichage.pe_print(
|
||||
f"* Tags automatiquement ajoutés : {', '.join(noms_tags_auto)}"
|
||||
f"* Tags du programme de formation : {', '.join(noms_tags_perso)} "
|
||||
+ f"Tags automatiques : {', '.join(noms_tags_auto)}"
|
||||
)
|
||||
return dict_tags
|
||||
|
||||
@ -312,4 +329,3 @@ def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
|
||||
}
|
||||
|
||||
return synthese_tags
|
||||
|
||||
|
@ -36,19 +36,23 @@ Created on Fri Sep 9 09:15:05 2016
|
||||
@author: barasc
|
||||
"""
|
||||
|
||||
from app.comp.res_sem import load_formsemestre_results
|
||||
from app.models import UniteEns, FormSemestre
|
||||
from app.pe import pe_affichage
|
||||
from app.pe.pe_ressemtag import ResSemBUTTag
|
||||
from app.pe import pe_affichage, pe_comp
|
||||
import app.pe.pe_ressemtag as pe_ressemtag
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
from app.pe.pe_tabletags import TableTag
|
||||
from app.pe.pe_moytag import MoyennesTag
|
||||
import app.pe.rcss.pe_rcf as pe_rcf
|
||||
|
||||
|
||||
class SxTag(TableTag):
|
||||
def __init__(self, sxtag_id: (int, int), ressembuttags: dict[int, ResSemBUTTag]):
|
||||
def __init__(
|
||||
self,
|
||||
sxtag_id: (str, int),
|
||||
rcf: pe_rcf.RCF,
|
||||
ressembuttags: dict[int, pe_ressemtag.ResSemBUTTag],
|
||||
):
|
||||
"""Calcule les moyennes/classements par tag d'un semestre de type 'Sx'
|
||||
(par ex. 'S1', 'S2', ...) avec une orientation par UE :
|
||||
|
||||
@ -76,10 +80,14 @@ class SxTag(TableTag):
|
||||
|
||||
assert sxtag_id and len(sxtag_id) == 2 and sxtag_id[1] in ressembuttags
|
||||
|
||||
self.sxtag_id: (int, int) = sxtag_id
|
||||
"""Identifiant du SxTag de la forme (semestre_id, fid_semestre_final)"""
|
||||
self.sxtag_id: (str, int) = sxtag_id
|
||||
"""Identifiant du SxTag de la forme (nom_Sx, fid_semestre_final)"""
|
||||
|
||||
self.ressembuttags = ressembuttags
|
||||
self.rcf = rcf
|
||||
"""Le RCF sur lequel il s'appuie"""
|
||||
|
||||
# Les resultats des semestres taggués à prendre en compte dans le RCF
|
||||
self.ressembuttags = {fid: ressembuttags[fid] for fid in rcf.semestres_aggreges}
|
||||
"""Les ResSemBUTTags à regrouper dans le SxTag"""
|
||||
|
||||
# Les données du semestre final
|
||||
@ -87,9 +95,13 @@ class SxTag(TableTag):
|
||||
self.ressembuttag_final = ressembuttags[self.fid_final]
|
||||
"""Le ResSemBUTTag final"""
|
||||
|
||||
# Les étudiants (etuds, états civils & etudis)
|
||||
self.etuds = ressembuttags[self.fid_final].etuds
|
||||
"""Les étudiants du ReSemBUTTag final"""
|
||||
|
||||
# Ajout les etudids et les états civils
|
||||
self.add_etuds(self.etuds)
|
||||
self.etudids_sorted = sorted(self.etudids)
|
||||
"""Les etudids triés"""
|
||||
|
||||
# Affichage
|
||||
pe_affichage.pe_print(f"--> {self.get_repr()}")
|
||||
@ -103,50 +115,87 @@ class SxTag(TableTag):
|
||||
moy_sem_final = self.ressembuttag_final.moyennes_tags["but"]
|
||||
self.ues = list(moy_sem_final.matrice_notes.columns)
|
||||
|
||||
# L'association UE-compétences extraites du dernier semestre
|
||||
self.competences = self.ressembuttag_final.competences
|
||||
|
||||
# Les acronymes des UE
|
||||
self.acronymes_ues_sorted = sorted(self.ues)
|
||||
|
||||
# Les inscriptions des étudiants aux UEs
|
||||
# => ne conserve que les UEs du semestre final (pour les redoublants)
|
||||
self.matrice_coeffs = self.ressembuttag_final.moyennes_tags[
|
||||
"but"
|
||||
].matrice_coeffs
|
||||
self.ues_inscr_parcours = ~np.isnan(self.matrice_coeffs.to_numpy())
|
||||
self.ues_inscr_parcours_df = self.ressembuttag_final.ues_inscr_parcours_df
|
||||
self.ues_inscr_parcours_df.sort_index()
|
||||
|
||||
# Les coeffs pour la moyenne générale
|
||||
self.matrice_coeffs_moy_gen = self.ressembuttag_final.moyennes_tags[
|
||||
"but"
|
||||
].matrice_coeffs_moy_gen
|
||||
self.matrice_coeffs_moy_gen.sort_index() # Trie les coeff par etudids
|
||||
|
||||
# Les moyennes par tag
|
||||
self.moyennes_tags: dict[str, pd.DataFrame] = {}
|
||||
"""Les notes aux UEs dans différents tags"""
|
||||
|
||||
# Masque des inscriptions
|
||||
inscr_mask = self.ues_inscr_parcours
|
||||
# Masque des inscriptions et des capitalisations
|
||||
self.masque_df, masque_cube = compute_masques_ues_cube(
|
||||
self.etudids_sorted,
|
||||
self.acronymes_ues_sorted,
|
||||
self.ressembuttags,
|
||||
self.fid_final,
|
||||
)
|
||||
# Affichage pour debug
|
||||
for etud in self.etuds:
|
||||
cap = []
|
||||
for frmsem_id in self.ressembuttags:
|
||||
if frmsem_id != self.fid_final:
|
||||
for accr in self.acronymes_ues_sorted:
|
||||
if self.masque_df[frmsem_id].loc[etud.etudid, accr] > 0.0:
|
||||
cap += [accr]
|
||||
if cap:
|
||||
pe_affichage.pe_print(
|
||||
f" ⚠ Capitalisation de {etud.etat_civil} : {', '.join(cap)}"
|
||||
)
|
||||
|
||||
for tag in self.tags_sorted:
|
||||
# Cube de note etudids x UEs
|
||||
notes_cube = self.compute_notes_ues_cube(tag, self.acronymes_ues_sorted)
|
||||
notes_df, notes_cube = compute_notes_ues_cube(
|
||||
tag, self.etudids_sorted, self.acronymes_ues_sorted, self.ressembuttags
|
||||
)
|
||||
|
||||
# Calcule des moyennes sous forme d'un dataframe"""
|
||||
matrice_moys_ues = compute_notes_ues(
|
||||
|
||||
|
||||
|
||||
|
||||
# self.ues_inscr_parcours = ~np.isnan(self.matrice_coeffs.to_numpy())
|
||||
# inscr_mask = self.ues_inscr_parcours
|
||||
|
||||
# Calcule des moyennes sous forme d'un dataframe
|
||||
inscr_mask = ~np.isnan(self.ues_inscr_parcours_df.to_numpy())
|
||||
matrice_moys_ues: pd.DataFrame = compute_notes_ues(
|
||||
notes_cube,
|
||||
self.etudids,
|
||||
masque_cube,
|
||||
self.etudids_sorted,
|
||||
self.acronymes_ues_sorted,
|
||||
inscr_mask,
|
||||
)
|
||||
|
||||
# Les profils d'ects (pour debug)
|
||||
profils_ects = []
|
||||
for i in self.matrice_coeffs.index:
|
||||
val = tuple(self.matrice_coeffs.loc[i].fillna("x"))
|
||||
for i in self.matrice_coeffs_moy_gen.index:
|
||||
val = tuple(self.matrice_coeffs_moy_gen.loc[i].fillna("x"))
|
||||
if tuple(val) not in profils_ects:
|
||||
profils_ects.append(tuple(val))
|
||||
|
||||
# Les moyennes
|
||||
self.moyennes_tags[tag] = MoyennesTag(tag,
|
||||
matrice_moys_ues,
|
||||
self.matrice_coeffs)
|
||||
self.moyennes_tags[tag] = MoyennesTag(
|
||||
tag, matrice_moys_ues, self.matrice_coeffs_moy_gen
|
||||
)
|
||||
|
||||
pe_affichage.pe_print(f"> MoyTag pour {tag} avec")
|
||||
pe_affichage.pe_print(f" - ues={self.acronymes_ues_sorted}")
|
||||
pe_affichage.pe_print(f" - ects={profils_ects}")
|
||||
pe_affichage.pe_print(
|
||||
f"> MoyTag 🏷{tag} avec "
|
||||
+ f"ues={self.acronymes_ues_sorted} "
|
||||
+ f"ects={profils_ects}"
|
||||
)
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Egalité de 2 SxTag sur la base de leur identifiant"""
|
||||
@ -155,41 +204,49 @@ class SxTag(TableTag):
|
||||
def get_repr(self, verbose=False) -> str:
|
||||
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
||||
est basée)"""
|
||||
affichage = [str(fid) for fid in self.ressembuttags]
|
||||
return f"{self.sxtag_id[0]}Tag ({'+'.join(affichage)})"
|
||||
if verbose:
|
||||
return f"{self.sxtag_id[0]}Tag basé sur {self.rcf.get_repr()}"
|
||||
else:
|
||||
# affichage = [str(fid) for fid in self.ressembuttags]
|
||||
return f"{self.sxtag_id[0]}Tag (#{self.fid_final})"
|
||||
|
||||
def compute_notes_ues_cube(self, tag, acronymes_ues_sorted):
|
||||
|
||||
def compute_notes_ues_cube(
|
||||
tag, etudids_sorted, acronymes_ues_sorted, ressembuttags
|
||||
) -> (pd.DataFrame, np.array):
|
||||
"""Construit le cube de notes des UEs (etudid x accronyme_ue x semestre_aggregé)
|
||||
nécessaire au calcul des moyennes du tag pour le RCS Sx
|
||||
nécessaire au calcul des moyennes du tag pour le RCS Sx.
|
||||
(Renvoie également le dataframe associé pour debug).
|
||||
|
||||
Args:
|
||||
etudids_sorted: La liste des etudids triés par ordre croissant (dim 0)
|
||||
acronymes_ues_sorted: La liste des acronymes de UEs triés par acronyme croissant (dim 1)
|
||||
ressembuttags: Le dictionnaire des résultats de semestres BUT (tous tags confondus)
|
||||
"""
|
||||
# Index du cube (etudids -> dim 0, ues -> dim 1, semestres -> dim2)
|
||||
etudids = [etud.etudid for etud in self.etuds]
|
||||
# acronymes_ues = sorted([ue.acronyme for ue in self.ues.values()])
|
||||
semestres_id = list(self.ressembuttags.keys())
|
||||
# etudids_sorted = etudids_sorted
|
||||
# acronymes_ues = sorted([ue.acronyme for ue in selMf.ues.values()])
|
||||
semestres_id = list(ressembuttags.keys())
|
||||
|
||||
dfs = {}
|
||||
|
||||
for frmsem_id in semestres_id:
|
||||
# Partant d'un dataframe vierge
|
||||
df = pd.DataFrame(np.nan, index=etudids, columns=acronymes_ues_sorted)
|
||||
df = pd.DataFrame(np.nan, index=etudids_sorted, columns=acronymes_ues_sorted)
|
||||
|
||||
# Charge les notes du semestre tag
|
||||
sem_tag = self.ressembuttags[frmsem_id]
|
||||
sem_tag = ressembuttags[frmsem_id]
|
||||
moys_tag = sem_tag.moyennes_tags[tag]
|
||||
notes = moys_tag.matrice_notes # dataframe etudids x ues
|
||||
acronymes_ues_sem = list(
|
||||
notes.columns
|
||||
) # les acronymes des UEs du semestre tag
|
||||
|
||||
# UEs communes à celles du SemTag (celles du dernier semestre du RCS)
|
||||
ues_communes = list(set(acronymes_ues_sorted) & set(acronymes_ues_sem))
|
||||
|
||||
# Etudiants communs
|
||||
etudids_communs = df.index.intersection(notes.index)
|
||||
# les étudiants et les acronymes communs
|
||||
etudids_communs, acronymes_communs = pe_comp.find_index_and_columns_communs(
|
||||
df, notes
|
||||
)
|
||||
|
||||
# Recopie
|
||||
df.loc[etudids_communs, ues_communes] = notes.loc[
|
||||
etudids_communs, ues_communes
|
||||
df.loc[etudids_communs, acronymes_communs] = notes.loc[
|
||||
etudids_communs, acronymes_communs
|
||||
]
|
||||
|
||||
# Supprime tout ce qui n'est pas numérique
|
||||
@ -202,13 +259,68 @@ class SxTag(TableTag):
|
||||
"""Réunit les notes sous forme d'un cube etudids x ues x semestres"""
|
||||
semestres_x_etudids_x_ues = [dfs[fid].values for fid in dfs]
|
||||
etudids_x_ues_x_semestres = np.stack(semestres_x_etudids_x_ues, axis=-1)
|
||||
return etudids_x_ues_x_semestres
|
||||
return dfs, etudids_x_ues_x_semestres
|
||||
|
||||
|
||||
def compute_masques_ues_cube(
|
||||
etudids_sorted: list[int],
|
||||
acronymes_ues_sorted: list[str],
|
||||
ressembuttags: dict[int, pe_ressemtag.ResSemBUTTag],
|
||||
formsemestre_id_final: int,
|
||||
) -> (pd.DataFrame, np.array):
|
||||
"""Construit le cube traduisant le masque des UEs à prendre en compte dans le calcul
|
||||
des moyennes, en utilisant le df capitalisations de chaque ResSemBUTTag
|
||||
|
||||
Ce masque contient : 1 si la note doit être prise en compte ; 0 sinon
|
||||
|
||||
Args:
|
||||
etudids_sorted: La liste des etudids triés par ordre croissant (dim 0)
|
||||
acronymes_ues_sorted: La liste des acronymes de UEs triés par acronyme croissant (dim 1)
|
||||
# ues_inscr_parcours_df: Le dataFrame des inscriptions au UE en fonction du parcours
|
||||
ressembuttags: Le dictionnaire des résultats de semestres BUT (tous tags confondus)
|
||||
formsemestre_id_final: L'identifiant du formsemestre_id_final (dont il faut forcément prendre en compte les coeffs)
|
||||
"""
|
||||
# Index du cube (etudids -> dim 0, ues -> dim 1, semestres -> dim2)
|
||||
# etudids_sorted = etudids_sorted
|
||||
# acronymes_ues = sorted([ue.acronyme for ue in selMf.ues.values()])
|
||||
semestres_id = list(ressembuttags.keys())
|
||||
|
||||
dfs = {}
|
||||
|
||||
for frmsem_id in semestres_id:
|
||||
# Partant d'un dataframe contenant des 1.0
|
||||
if frmsem_id == formsemestre_id_final:
|
||||
df = pd.DataFrame(1.0, index=etudids_sorted, columns=acronymes_ues_sorted)
|
||||
else: # semestres redoublés
|
||||
df = pd.DataFrame(0.0, index=etudids_sorted, columns=acronymes_ues_sorted)
|
||||
|
||||
# Traitement des capitalisations
|
||||
capitalisations = ressembuttags[frmsem_id].capitalisations
|
||||
capitalisations = capitalisations.replace(True, 1.0).replace(False, 0.0)
|
||||
|
||||
# Met à 0 les coeffs des UEs non capitalisées : 1.0*False => 0.0
|
||||
etudids_communs, acronymes_communs = pe_comp.find_index_and_columns_communs(
|
||||
df, capitalisations
|
||||
)
|
||||
|
||||
df.loc[etudids_communs, acronymes_communs] = capitalisations.loc[
|
||||
etudids_communs, acronymes_communs
|
||||
]
|
||||
|
||||
# Stocke le df
|
||||
dfs[frmsem_id] = df
|
||||
|
||||
"""Réunit les notes sous forme d'un cube etudids x ues x semestres"""
|
||||
semestres_x_etudids_x_ues = [dfs[fid].values for fid in dfs]
|
||||
etudids_x_ues_x_semestres = np.stack(semestres_x_etudids_x_ues, axis=-1)
|
||||
return dfs, etudids_x_ues_x_semestres
|
||||
|
||||
|
||||
def compute_notes_ues(
|
||||
set_cube: np.array,
|
||||
etudids: list,
|
||||
acronymes_ues: list,
|
||||
masque_cube: np.array,
|
||||
etudids_sorted: list,
|
||||
acronymes_ues_sorted: list,
|
||||
inscr_mask: np.array,
|
||||
):
|
||||
"""Calcule la moyenne par UEs à un tag donné en prenant la note maximum (UE
|
||||
@ -217,8 +329,10 @@ def compute_notes_ues(
|
||||
Args:
|
||||
set_cube: notes moyennes aux modules ndarray
|
||||
(semestre_ids x etudids x UEs), des floats avec des NaN
|
||||
etudids: liste des étudiants (dim. 0 du cube)
|
||||
acronymes_ues: liste des acronymes des ues (dim. 1 du cube)
|
||||
masque_cube: masque indiquant si la note doit être prise en compte ndarray
|
||||
(semestre_ids x etudids x UEs), des 1.0 ou des 0.0
|
||||
etudids_sorted: liste des étudiants (dim. 0 du cube) trié par etudid
|
||||
acronymes_ues_sorted: liste des acronymes des ues (dim. 1 du cube) trié par acronyme
|
||||
inscr_mask: masque etudids x UE traduisant les inscriptions des
|
||||
étudiants aux UE (du semestre terminal)
|
||||
Returns:
|
||||
@ -227,18 +341,21 @@ def compute_notes_ues(
|
||||
"""
|
||||
nb_etuds, nb_ues, nb_semestres = set_cube.shape
|
||||
nb_etuds_mask, nb_ues_mask = inscr_mask.shape
|
||||
assert nb_etuds == len(etudids)
|
||||
assert nb_ues == len(acronymes_ues)
|
||||
assert nb_etuds == len(etudids_sorted)
|
||||
assert nb_ues == len(acronymes_ues_sorted)
|
||||
assert nb_etuds == nb_etuds_mask
|
||||
assert nb_ues == nb_ues_mask
|
||||
|
||||
# Quelles entrées du cube contiennent des notes ?
|
||||
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 masque d'inscription aux UEs du parcours
|
||||
inscr_mask_3D = np.stack([inscr_mask] * nb_semestres, axis=-1)
|
||||
set_cube = set_cube * inscr_mask_3D
|
||||
|
||||
# Entrées à garder en fonction des UEs capitalisées ou non
|
||||
set_cube = set_cube * masque_cube
|
||||
|
||||
# Quelles entrées du cube contiennent des notes ?
|
||||
mask = ~np.isnan(set_cube)
|
||||
|
||||
# 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)
|
||||
|
||||
@ -252,8 +369,8 @@ def compute_notes_ues(
|
||||
# Le dataFrame
|
||||
etud_moy_tag_df = pd.DataFrame(
|
||||
etud_moy,
|
||||
index=etudids, # les etudids
|
||||
columns=acronymes_ues, # les tags
|
||||
index=etudids_sorted, # les etudids
|
||||
columns=acronymes_ues_sorted, # les tags
|
||||
)
|
||||
|
||||
etud_moy_tag_df.fillna(np.nan)
|
||||
|
@ -52,9 +52,7 @@ class TableTag(object):
|
||||
# Les étudiants
|
||||
# self.etuds: list[Identite] = None # A venir
|
||||
"""Les étudiants"""
|
||||
# self.etats_civils: dict[int, Identite] = None
|
||||
"""Les états civils"""
|
||||
# self.etudids: list[int] = None
|
||||
# self.etudids: list[int] = {}
|
||||
"""Les etudids"""
|
||||
|
||||
def add_etuds(self, etuds: list[Identite]):
|
||||
@ -64,8 +62,7 @@ class TableTag(object):
|
||||
etuds: la liste des identités de l'étudiant
|
||||
"""
|
||||
# self.etuds = etuds
|
||||
self.etats_civils = {etud.etudid: etud.etat_civil for etud in self.etuds}
|
||||
self.etudids = list(self.etats_civils.keys())
|
||||
self.etudids = list({etud.etudid for etud in etuds})
|
||||
|
||||
def get_all_tags(self):
|
||||
"""Liste des tags de la table, triée par ordre alphabétique,
|
||||
@ -88,8 +85,8 @@ class TableTag(object):
|
||||
Le dataframe des notes et des classements
|
||||
"""
|
||||
|
||||
etudiants = self.etats_civils
|
||||
df = pd.DataFrame.from_dict(etudiants, orient="index", columns=["nom"])
|
||||
etudiants = {etud.etudid: [etud.nom, etud.prenom] for etud in self.etuds}
|
||||
df = pd.DataFrame.from_dict(etudiants, orient="index", columns=["nom", "prenom"])
|
||||
|
||||
tags_tries = self.get_all_tags()
|
||||
for tag in tags_tries:
|
||||
@ -104,7 +101,7 @@ class TableTag(object):
|
||||
df = df.join(moy_gen.synthese["notes"].rename(f"Moy {tag} (gen)"))
|
||||
df = df.join(moy_gen.synthese["classements"].rename(f"Class {tag} (gen)"))
|
||||
|
||||
df.sort_values(by=['nom'])
|
||||
df.sort_values(by=["nom", "prenom"])
|
||||
|
||||
return df
|
||||
|
||||
|
0
app/pe/rcss/__init__.py
Normal file
0
app/pe/rcss/__init__.py
Normal file
57
app/pe/rcss/pe_rcf.py
Normal file
57
app/pe/rcss/pe_rcf.py
Normal file
@ -0,0 +1,57 @@
|
||||
|
||||
from app.models import FormSemestre
|
||||
import app.pe.rcss.pe_rcs as pe_rcs
|
||||
|
||||
|
||||
class RCF(pe_rcs.RCS):
|
||||
"""Modélise un ensemble de (form)semestres d'étudiants
|
||||
associé à un type de regroupement cohérent de semestres
|
||||
donné (par ex: 'S2', '3S', '2A').
|
||||
|
||||
Si le RCF est un semestre de type Si, stocke les
|
||||
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)
|
||||
|
||||
Pour le RCF de type iS ou iA (par ex, 3A=S1+S2+S3), identifie
|
||||
les semestres que les étudiants ont suivis pour les amener jusqu'au semestre
|
||||
terminal du RCS (par ex: ici un S3).
|
||||
|
||||
Ces semestres peuvent être :
|
||||
|
||||
* des S1+S2+S1+S2+S3 si redoublement de la 1ère année
|
||||
* des S1+S2+(année de césure)+S3 si césure, ...
|
||||
|
||||
Args:
|
||||
nom_rcs: Un nom du RCS (par ex: '5S')
|
||||
semestre_final: Le formsemestre final du RCS
|
||||
"""
|
||||
|
||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||
pe_rcs.RCS.__init__(self, nom_rcs, semestre_final)
|
||||
|
||||
self.semestres_aggreges: dict[int:FormSemestre] = {}
|
||||
"""Formsemestres regroupés dans le RCS"""
|
||||
|
||||
def add_semestres_a_aggreger(self, semestres: dict[int:FormSemestre]):
|
||||
"""Ajout de semestres aux semestres à regrouper
|
||||
|
||||
Args:
|
||||
semestres: Dictionnaire ``{fid: Formsemestre)``
|
||||
"""
|
||||
self.semestres_aggreges = self.semestres_aggreges | semestres
|
||||
|
||||
def get_repr(self, verbose=True) -> str:
|
||||
"""Représentation textuelle d'un RCS
|
||||
basé sur ses semestres aggrégés"""
|
||||
title = f"""{self.__class__.__name__} {pe_rcs.RCS.__str__(self)}"""
|
||||
if verbose:
|
||||
noms = []
|
||||
for fid in self.semestres_aggreges:
|
||||
semestre = self.semestres_aggreges[fid]
|
||||
noms.append(f"S{semestre.semestre_id}(#{fid})")
|
||||
noms = sorted(noms)
|
||||
if noms:
|
||||
title += " <" + "+".join(noms) + ">"
|
||||
else:
|
||||
title += " <vide>"
|
||||
return title
|
69
app/pe/rcss/pe_rcrcf.py
Normal file
69
app/pe/rcss/pe_rcrcf.py
Normal file
@ -0,0 +1,69 @@
|
||||
##############################################################################
|
||||
# Module "Avis de poursuite d'étude"
|
||||
# conçu et développé par Cléo Baras (IUT de Grenoble)
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
Created on 01-2024
|
||||
|
||||
@author: barasc
|
||||
"""
|
||||
|
||||
import app.pe.pe_comp as pe_comp
|
||||
import app.pe.rcss.pe_rcf
|
||||
import app.pe.rcss.rcss_constantes
|
||||
|
||||
from app.models import FormSemestre
|
||||
from app.pe import pe_sxtag, pe_affichage
|
||||
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
|
||||
import app.pe.rcss.pe_rcs as pe_rcs
|
||||
import app.pe.rcss.pe_rcf as pe_rcf
|
||||
|
||||
|
||||
|
||||
class RCRCF:
|
||||
"""Modélise les RCF d'étudiants suivis par un étudiant dans
|
||||
le cadre d'un RCS donné (par ex: 3S=S1+S2+S3).
|
||||
|
||||
Pour rappel : un RCF (par ex. S1) combine les semestres 1 qu'a suivi
|
||||
l'étudiant pour valider son S1 (1 si étudiant standard, 2 si redoublant).
|
||||
|
||||
Le RCRCF 3S est donc le regroupement du RCF S1 + RCF S2 + RCF S3.
|
||||
|
||||
Il est identifié par le formsemestre de S3 marquant la fin du regroupement.
|
||||
|
||||
|
||||
Args:
|
||||
nom_rcs: Un nom du RCS (par ex: '5S')
|
||||
semestre_final: Le semestre final du RCS
|
||||
"""
|
||||
|
||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||
pe_rcs.RCS.__init__(self, nom_rcs, semestre_final)
|
||||
|
||||
self.rcfs_aggreges: dict[(str, int) : pe_sxtag.SxTag] = {}
|
||||
"""Les RCFs à aggréger"""
|
||||
|
||||
def add_rcfs_a_aggreger(self, rcfs: dict[(str, int): app.pe.rcss.pe_rcf.RCF]):
|
||||
"""Ajout des RCFs aux RCFS à regrouper
|
||||
|
||||
Args:
|
||||
rcfs: Dictionnaire ``{(str,fid): RCF}`` à ajouter
|
||||
"""
|
||||
self.rcfs_aggreges = self.rcfs_aggreges | rcfs
|
||||
|
||||
def get_repr(self, verbose=True) -> str:
|
||||
"""Représentation textuelle d'un RCSF
|
||||
basé sur ses RCF aggrégés"""
|
||||
title = f"""{self.__class__.__name__}{pe_rcs.RCS.__str__(self)}"""
|
||||
if verbose:
|
||||
noms = []
|
||||
for rcf_id, rcf in self.rcfs_aggreges.items():
|
||||
noms.append(rcf.get_repr(verbose=False))
|
||||
if noms:
|
||||
title += " <<" + "+".join(noms) + ">>"
|
||||
else:
|
||||
title += " <<vide>>"
|
||||
return title
|
||||
|
||||
|
62
app/pe/rcss/pe_rcs.py
Normal file
62
app/pe/rcss/pe_rcs.py
Normal file
@ -0,0 +1,62 @@
|
||||
##############################################################################
|
||||
# Module "Avis de poursuite d'étude"
|
||||
# conçu et développé par Cléo Baras (IUT de Grenoble)
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
Created on 01-2024
|
||||
|
||||
@author: barasc
|
||||
"""
|
||||
|
||||
from app.models import FormSemestre
|
||||
import app.pe.rcss.rcss_constantes as rcss_constantes
|
||||
|
||||
|
||||
|
||||
def get_descr_rcs(nom_rcs: str) -> str:
|
||||
"""Renvoie la description pour les tableurs de synthèse
|
||||
Excel d'un nom de RCS"""
|
||||
return rcss_constantes.TYPES_RCS[nom_rcs]["descr"]
|
||||
|
||||
|
||||
class RCS:
|
||||
"""Modélise un regroupement cohérent de semestres (formsemestre ou de Sx)"""
|
||||
|
||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||
self.nom: str = nom_rcs
|
||||
"""Nom du RCS"""
|
||||
|
||||
self.formsemestre_final: FormSemestre = semestre_final
|
||||
"""FormSemestre terminal du RCS"""
|
||||
|
||||
self.rang_final = self.formsemestre_final.semestre_id
|
||||
"""Le rang du formsemestre final"""
|
||||
|
||||
self.rcs_id: (str, int) = (nom_rcs, semestre_final.formsemestre_id)
|
||||
"""Identifiant du RCS sous forme (nom_rcs, id du semestre_terminal)"""
|
||||
|
||||
def get_formsemestre_id_final(self) -> int:
|
||||
"""Renvoie l'identifiant du formsemestre final du RCS
|
||||
|
||||
Returns:
|
||||
L'id du formsemestre final (marquant la fin) du RCS
|
||||
"""
|
||||
return self.formsemestre_final.formsemestre_id
|
||||
|
||||
def __str__(self):
|
||||
"""Représentation textuelle d'un RCS"""
|
||||
return f"{self.nom}[#{self.formsemestre_final.formsemestre_id}✟{self.formsemestre_final.date_fin.year}]"
|
||||
|
||||
def get_repr(self, verbose=True):
|
||||
"""Représentation textuelle d'un RCS"""
|
||||
return self.__str__()
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Egalité de RCS"""
|
||||
return (
|
||||
self.nom == other.nom
|
||||
and self.formsemestre_final == other.formsemestre_final
|
||||
)
|
||||
|
||||
|
64
app/pe/rcss/rcss_constantes.py
Normal file
64
app/pe/rcss/rcss_constantes.py
Normal file
@ -0,0 +1,64 @@
|
||||
|
||||
TYPES_RCS = {
|
||||
"S1": {
|
||||
"aggregat": ["S1"],
|
||||
"descr": "Semestre 1 (S1)",
|
||||
},
|
||||
"S2": {
|
||||
"aggregat": ["S2"],
|
||||
"descr": "Semestre 2 (S2)",
|
||||
},
|
||||
"1A": {
|
||||
"aggregat": ["S1", "S2"],
|
||||
"descr": "BUT1 (S1+S2)",
|
||||
},
|
||||
"S3": {
|
||||
"aggregat": ["S3"],
|
||||
"descr": "Semestre 3 (S3)",
|
||||
},
|
||||
"S4": {
|
||||
"aggregat": ["S4"],
|
||||
"descr": "Semestre 4 (S4)",
|
||||
},
|
||||
"2A": {
|
||||
"aggregat": ["S3", "S4"],
|
||||
"descr": "BUT2 (S3+S4)",
|
||||
},
|
||||
"3S": {
|
||||
"aggregat": ["S1", "S2", "S3"],
|
||||
"descr": "Moyenne du semestre 1 au semestre 3 (S1+S2+S3)",
|
||||
},
|
||||
"4S": {
|
||||
"aggregat": ["S1", "S2", "S3", "S4"],
|
||||
"descr": "Moyenne du semestre 1 au semestre 4 (S1+S2+S3+S4)",
|
||||
},
|
||||
"S5": {
|
||||
"aggregat": ["S5"],
|
||||
"descr": "Semestre 5 (S5)",
|
||||
},
|
||||
"S6": {
|
||||
"aggregat": ["S6"],
|
||||
"descr": "Semestre 6 (S6)",
|
||||
},
|
||||
"3A": {
|
||||
"aggregat": ["S5", "S6"],
|
||||
"descr": "3ème année (S5+S6)",
|
||||
},
|
||||
"5S": {
|
||||
"aggregat": ["S1", "S2", "S3", "S4", "S5"],
|
||||
"descr": "Moyenne du semestre 1 au semestre 5 (S1+S2+S3+S4+S5)",
|
||||
},
|
||||
"6S": {
|
||||
"aggregat": ["S1", "S2", "S3", "S4", "S5", "S6"],
|
||||
"descr": "Moyenne globale (S1+S2+S3+S4+S5+S6)",
|
||||
},
|
||||
}
|
||||
"""Dictionnaire détaillant les différents regroupements cohérents
|
||||
de semestres (RCS), en leur attribuant un nom et en détaillant
|
||||
le nom des semestres qu'ils regroupent et l'affichage qui en sera fait
|
||||
dans les tableurs de synthèse.
|
||||
"""
|
||||
|
||||
TOUS_LES_RCS_AVEC_PLUSIEURS_SEM = [cle for cle in TYPES_RCS if not cle.startswith("S")]
|
||||
TOUS_LES_RCS = list(TYPES_RCS.keys())
|
||||
TOUS_LES_SEMESTRES = [cle for cle in TYPES_RCS if cle.startswith("S")]
|
Loading…
Reference in New Issue
Block a user