forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -41,13 +41,13 @@ import datetime
|
|||||||
import re
|
import re
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
from flask import g
|
from flask import g
|
||||||
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
|
||||||
from app.models import FormSemestre
|
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 import sco_formsemestre
|
||||||
from app.scodoc.sco_logos import find_logo
|
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]}"""
|
dictionnaire {rang: [liste des semestres du dit rang]}"""
|
||||||
cosemestres_tries = {}
|
cosemestres_tries = {}
|
||||||
for sem in cosemestres.values():
|
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
|
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_tabletags import TableTag
|
||||||
from app.pe.pe_moytag import MoyennesTag
|
from app.pe.pe_moytag import MoyennesTag
|
||||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
import app.pe.pe_etudiant as pe_etudiant
|
||||||
from app.pe.pe_rcs import RCS, RCSsJuryPE
|
import app.pe.rcss.pe_rcs as pe_rcs
|
||||||
from app.pe.pe_rcstag import RCSTag
|
import app.pe.pe_rcss_jury as pe_rcss_jury
|
||||||
|
import app.pe.pe_rcstag as pe_rcstag
|
||||||
|
|
||||||
|
|
||||||
class RCSInterclasseTag(TableTag):
|
class RCSInterclasseTag(TableTag):
|
||||||
@ -58,9 +59,9 @@ class RCSInterclasseTag(TableTag):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
nom_rcs: str,
|
nom_rcs: str,
|
||||||
etudiants: EtudiantsJuryPE,
|
etudiants: pe_etudiant.EtudiantsJuryPE,
|
||||||
rcss_jury_pe: RCSsJuryPE,
|
rcss_jury_pe: pe_rcss_jury.RCSsJuryPE,
|
||||||
rcss_tags: dict[tuple, RCSTag],
|
rcss_tags: dict[tuple, pe_rcstag.RCSTag],
|
||||||
):
|
):
|
||||||
TableTag.__init__(self)
|
TableTag.__init__(self)
|
||||||
|
|
||||||
@ -80,21 +81,21 @@ class RCSInterclasseTag(TableTag):
|
|||||||
|
|
||||||
# Les trajectoires (et leur version tagguées), en ne gardant que
|
# Les trajectoires (et leur version tagguées), en ne gardant que
|
||||||
# celles associées à l'aggrégat
|
# 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"""
|
"""Ensemble des trajectoires associées à l'aggrégat"""
|
||||||
for trajectoire_id in rcss_jury_pe.rcss:
|
for trajectoire_id in rcss_jury_pe.rcss:
|
||||||
trajectoire = rcss_jury_pe.rcss[trajectoire_id]
|
trajectoire = rcss_jury_pe.rcss[trajectoire_id]
|
||||||
if trajectoire_id[0] == nom_rcs:
|
if trajectoire_id[0] == nom_rcs:
|
||||||
self.rcss[trajectoire_id] = trajectoire
|
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"""
|
"""Ensemble des trajectoires tagguées associées à l'aggrégat"""
|
||||||
for trajectoire_id in self.rcss:
|
for trajectoire_id in self.rcss:
|
||||||
self.trajectoires_taggues[trajectoire_id] = rcss_tags[trajectoire_id]
|
self.trajectoires_taggues[trajectoire_id] = rcss_tags[trajectoire_id]
|
||||||
|
|
||||||
# Les trajectoires suivies par les étudiants du jury, en ne gardant que
|
# Les trajectoires suivies par les étudiants du jury, en ne gardant que
|
||||||
# celles associées aux diplomés
|
# 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
|
"""Association entre chaque étudiant et la trajectoire tagguée à prendre en
|
||||||
compte pour l'aggrégat"""
|
compte pour l'aggrégat"""
|
||||||
for etudid in self.diplomes_ids:
|
for etudid in self.diplomes_ids:
|
||||||
|
@ -50,15 +50,16 @@ from zipfile import ZipFile
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
|
import app.pe.rcss.rcss_constantes as rcss_constants
|
||||||
from app.pe import pe_sxtag
|
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
|
||||||
import app.pe.pe_etudiant as pe_etudiant
|
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_rcstag import RCSTag
|
||||||
from app.pe.pe_ressemtag import ResSemBUTTag
|
from app.pe.pe_ressemtag import ResSemBUTTag
|
||||||
from app.pe.pe_interclasstag import RCSInterclasseTag
|
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):
|
class JuryPE(object):
|
||||||
@ -94,7 +95,7 @@ class JuryPE(object):
|
|||||||
self.etudiants.find_etudiants()
|
self.etudiants.find_etudiants()
|
||||||
self.diplomes_ids = self.etudiants.diplomes_ids
|
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"""
|
"""Les informations sur les regroupements de semestres"""
|
||||||
|
|
||||||
self.zipdata = io.BytesIO()
|
self.zipdata = io.BytesIO()
|
||||||
@ -102,16 +103,18 @@ class JuryPE(object):
|
|||||||
if not self.diplomes_ids:
|
if not self.diplomes_ids:
|
||||||
pe_affichage.pe_print("*** Aucun étudiant diplômé")
|
pe_affichage.pe_print("*** Aucun étudiant diplômé")
|
||||||
else:
|
else:
|
||||||
self._gen_xls_diplomes(zipfile)
|
try:
|
||||||
|
self._gen_xls_diplomes(zipfile)
|
||||||
self._gen_xls_ressembuttags(zipfile)
|
self._gen_xls_ressembuttags(zipfile)
|
||||||
self._gen_rcss()
|
self._gen_rcss()
|
||||||
self._gen_xls_sxtags(zipfile)
|
self._gen_xls_sxtags(zipfile)
|
||||||
self._gen_rcrcfs()
|
self._gen_rcrcfs()
|
||||||
self._gen_xls_rcrcss_tags(zipfile)
|
self._gen_xls_rcrcss_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)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
# et le log
|
# et le log
|
||||||
self._add_log_to_zip(zipfile)
|
self._add_log_to_zip(zipfile)
|
||||||
|
|
||||||
@ -150,7 +153,7 @@ class JuryPE(object):
|
|||||||
|
|
||||||
formsemestres = get_formsemestres_etudiants(self.etudiants)
|
formsemestres = get_formsemestres_etudiants(self.etudiants)
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
f" --> {len(formsemestres)} résultats de semestres à considérer"
|
f"--> {len(formsemestres)} résultats de semestres à considérer"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.ressembuttags = {}
|
self.ressembuttags = {}
|
||||||
@ -186,14 +189,15 @@ class JuryPE(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.rcss_jury.cree_rcss(self.etudiants)
|
self.rcss_jury.cree_rcss(self.etudiants)
|
||||||
self.rcss_jury.cree_rcfs(self.etudiants)
|
|
||||||
|
|
||||||
def _gen_xls_sxtags(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 regroupements de semestres de type Sx
|
# 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)
|
self.rcss_jury.cree_rcfs(self.etudiants)
|
||||||
|
|
||||||
# Génère les moyennes des RCS de type Sx
|
# 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():
|
for rcf_id, rcf in self.rcss_jury.rcfs.items():
|
||||||
# SxTag traduisant le RCF
|
# SxTag traduisant le RCF
|
||||||
sxtag_id = rcf_id
|
sxtag_id = rcf_id
|
||||||
# Les resultats des semestres taggués à prendre en compte dans le RCF
|
|
||||||
ressemstags = {
|
self.sxtags[sxtag_id] = pe_sxtag.SxTag(sxtag_id, rcf, self.ressembuttags)
|
||||||
fid: self.ressembuttags[fid] for fid in rcf.semestres_aggreges
|
|
||||||
}
|
|
||||||
self.sxtags[sxtag_id] = pe_sxtag.SxTag(sxtag_id, ressemstags)
|
|
||||||
|
|
||||||
# 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()
|
||||||
@ -232,10 +233,11 @@ class JuryPE(object):
|
|||||||
def _gen_rcrcfs(self):
|
def _gen_rcrcfs(self):
|
||||||
"""Génère les regroupements cohérents de RCFs qu'ont suivi chaque étudiant"""
|
"""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)
|
self.rcss_jury.cree_rcrcfs(self.etudiants)
|
||||||
|
|
||||||
|
|
||||||
def _gen_xls_rcrcss_tags(self, zipfile: ZipFile):
|
def _gen_xls_rcrcss_tags(self, zipfile: ZipFile):
|
||||||
"""Génère les RCS taggués traduisant les moyennes (orientées compétences)
|
"""Génère les RCS taggués traduisant les moyennes (orientées compétences)
|
||||||
de regroupements de semestre de type Sx, xA ou xS.
|
de regroupements de semestre de type Sx, xA ou xS.
|
||||||
@ -268,7 +270,7 @@ class JuryPE(object):
|
|||||||
output, engine="openpyxl"
|
output, engine="openpyxl"
|
||||||
) as writer:
|
) as writer:
|
||||||
for rcs_tag in self.rcss_tags.values():
|
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()
|
df = rcs_tag.df_moyennes_et_classements()
|
||||||
# écriture dans l'onglet
|
# écriture dans l'onglet
|
||||||
df.to_excel(writer, onglet, index=True, header=True)
|
df.to_excel(writer, onglet, index=True, header=True)
|
||||||
@ -276,7 +278,7 @@ class JuryPE(object):
|
|||||||
|
|
||||||
self.add_file_to_zip(
|
self.add_file_to_zip(
|
||||||
zipfile,
|
zipfile,
|
||||||
f"RCS_taggues_{self.diplome}.xlsx",
|
f"RCRCFs_{self.diplome}.xlsx",
|
||||||
output.read(),
|
output.read(),
|
||||||
path="details",
|
path="details",
|
||||||
)
|
)
|
||||||
@ -431,7 +433,7 @@ class JuryPE(object):
|
|||||||
|
|
||||||
# Ajout des aggrégats
|
# Ajout des aggrégats
|
||||||
for aggregat in pe_rcs.TOUS_LES_RCS:
|
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
|
# Les trajectoires (tagguées) suivies par les étudiants pour l'aggrégat et le tag
|
||||||
# considéré
|
# considéré
|
||||||
@ -632,7 +634,7 @@ def get_formsemestres_etudiants(etudiants: pe_etudiant.EtudiantsJuryPE) -> dict:
|
|||||||
|
|
||||||
def compute_interclassements(
|
def compute_interclassements(
|
||||||
etudiants: pe_etudiant.EtudiantsJuryPE,
|
etudiants: pe_etudiant.EtudiantsJuryPE,
|
||||||
trajectoires_jury_pe: pe_rcs.RCSsJuryPE,
|
trajectoires_jury_pe: pe_rcss_jury.RCSsJuryPE,
|
||||||
trajectoires_tagguees: dict[tuple, pe_rcs.RCS],
|
trajectoires_tagguees: dict[tuple, pe_rcs.RCS],
|
||||||
):
|
):
|
||||||
"""Interclasse les étudiants, (nom d') aggrégat par aggrégat,
|
"""Interclasse les étudiants, (nom d') aggrégat par aggrégat,
|
||||||
|
@ -181,7 +181,7 @@ class MoyennesTag:
|
|||||||
self.matrice_notes: pd.DataFrame = matrice_notes
|
self.matrice_notes: pd.DataFrame = matrice_notes
|
||||||
"""Les notes aux UEs ou aux compétences (DataFrame)"""
|
"""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
|
"""Les coeffs à appliquer pour le calcul des moyennes générales
|
||||||
(toutes UE ou compétences confondues). NaN si étudiant non inscrit"""
|
(toutes UE ou compétences confondues). NaN si étudiant non inscrit"""
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ class MoyennesTag:
|
|||||||
self.moyennes[col] = Moyenne(notes)
|
self.moyennes[col] = Moyenne(notes)
|
||||||
|
|
||||||
# Les moyennes générales
|
# 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
|
self.notes_gen = notes_gen
|
||||||
"""Les notes générales (moyenne toutes UEs confonudes)"""
|
"""Les notes générales (moyenne toutes UEs confonudes)"""
|
||||||
self.moyenne_gen = Moyenne(notes_gen)
|
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.pe_comp as pe_comp
|
||||||
|
import app.pe.rcss.rcss_constantes as rcss_constantes
|
||||||
from app.models import FormSemestre
|
from app.models import FormSemestre
|
||||||
from app.pe import pe_sxtag, pe_affichage
|
from app.pe import 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
|
|
||||||
|
|
||||||
|
|
||||||
class RCSsJuryPE:
|
class RCSsJuryPE:
|
||||||
@ -257,28 +20,28 @@ class RCSsJuryPE:
|
|||||||
self.annee_diplome = annee_diplome
|
self.annee_diplome = annee_diplome
|
||||||
"""Année de diplômation"""
|
"""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"""
|
"""Ensemble des RCS recensés"""
|
||||||
|
|
||||||
self.rcss_suivis: dict[int:dict] = {}
|
self.rcss_suivis: dict[int:dict] = {}
|
||||||
"""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}}"""
|
||||||
|
|
||||||
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}"""
|
"""Ensemble des RCF recensés : {(nom_RCS, fid_terminal): RCF}"""
|
||||||
|
|
||||||
self.rcfs_suivis: dict[int:dict] = {}
|
self.rcfs_suivis: dict[int:dict] = {}
|
||||||
"""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: RCF}}"""
|
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}"""
|
"""Ensemble des RCS recensés : {(nom_RCS, fid_terminal): RCRCF}"""
|
||||||
|
|
||||||
self.rcrcfs_suivis: dict[int:str] = {}
|
self.rcrcfs_suivis: dict[int:str] = {}
|
||||||
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
"""Dictionnaire associant, pour chaque étudiant et pour chaque type de RCS,
|
||||||
son RCRCF : {etudid: {nom_RCS: RCSx}}"""
|
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
|
"""Créé tous les RCS, au regard du cursus des étudiants
|
||||||
analysés + les mémorise dans les données de l'étudiant
|
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
|
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:
|
for etudid in etudiants.trajectoires:
|
||||||
self.rcss_suivis[etudid] = {
|
self.rcss_suivis[etudid] = {
|
||||||
aggregat: None
|
aggregat: None
|
||||||
for aggregat in pe_comp.TOUS_LES_SEMESTRES
|
for aggregat in tous_les_aggregats
|
||||||
+ TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
# 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 = rcss_constantes.TYPES_RCS[nom_rcs]["aggregat"]
|
||||||
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
|
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
|
||||||
|
|
||||||
for etudid in etudiants.trajectoires:
|
for etudid in etudiants.trajectoires:
|
||||||
@ -305,12 +68,12 @@ class RCSsJuryPE:
|
|||||||
# (par ex: son dernier S3 en date)
|
# (par ex: son dernier S3 en date)
|
||||||
trajectoire = etudiants.trajectoires[etudid][nom_semestre_terminal]
|
trajectoire = etudiants.trajectoires[etudid][nom_semestre_terminal]
|
||||||
if trajectoire:
|
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é
|
# Ajout ou récupération du RCS associé
|
||||||
rcs_id = (nom_rcs, formsemestre_final.formsemestre_id)
|
rcs_id = (nom_rcs, formsemestre_final.formsemestre_id)
|
||||||
if rcs_id not in self.rcss:
|
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]
|
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
|
||||||
@ -328,23 +91,22 @@ class RCSsJuryPE:
|
|||||||
# Affichage pour debug
|
# Affichage pour debug
|
||||||
jeunes = list(enumerate(self.rcss_suivis))
|
jeunes = list(enumerate(self.rcss_suivis))
|
||||||
for no_etud, etudid in jeunes[:20]:
|
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():
|
for nom_rcs, rcs in self.rcss_suivis[etudid].items():
|
||||||
if rcs:
|
if rcs:
|
||||||
pe_affichage.pe_print(f" > RCS {nom_rcs}: {rcs.get_repr()}")
|
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
|
"""Créé les RCFs en ne conservant dans les RCS que les regroupements
|
||||||
de type Sx"""
|
de type Sx"""
|
||||||
self.rcfs = {}
|
self.rcfs = {}
|
||||||
for rcs_id, rcs in self.rcss.items():
|
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
|
self.rcfs[rcs_id] = rcs
|
||||||
print(self.rcfs)
|
|
||||||
|
|
||||||
for etudid in self.rcss_suivis:
|
for etudid in self.rcss_suivis:
|
||||||
for nom_rcs, rcs in self.rcss_suivis[etudid].items():
|
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:
|
if etudid not in self.rcfs_suivis:
|
||||||
self.rcfs_suivis[etudid] = {}
|
self.rcfs_suivis[etudid] = {}
|
||||||
self.rcfs_suivis[etudid][nom_rcs] = rcs
|
self.rcfs_suivis[etudid][nom_rcs] = rcs
|
||||||
@ -355,11 +117,11 @@ class RCSsJuryPE:
|
|||||||
pe_affichage.pe_print(f"-> {etudiants.identites[etudid].nomprenom} :")
|
pe_affichage.pe_print(f"-> {etudiants.identites[etudid].nomprenom} :")
|
||||||
for nom_rcs, rcs in self.rcfs_suivis[etudid].items():
|
for nom_rcs, rcs in self.rcfs_suivis[etudid].items():
|
||||||
if rcs:
|
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:
|
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
|
"""Créé tous les RCRCF, au regard du cursus des étudiants
|
||||||
analysés (trajectoires traduisant son parcours dans les
|
analysés (trajectoires traduisant son parcours dans les
|
||||||
différents semestres) + les mémorise dans les données de l'étudiant
|
différents semestres) + les mémorise dans les données de l'étudiant
|
||||||
@ -390,7 +152,7 @@ class RCSsJuryPE:
|
|||||||
|
|
||||||
# Ajout du RCRCF
|
# Ajout du RCRCF
|
||||||
if rcf_id not in self.rcrcfs:
|
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]
|
rcrcf = self.rcrcfs[rcf_id]
|
||||||
|
|
||||||
# Ajout des RCFs au RCRCF
|
# Ajout des RCFs au RCRCF
|
||||||
@ -410,6 +172,8 @@ class RCSsJuryPE:
|
|||||||
else:
|
else:
|
||||||
pe_affichage.pe_print(f" > RCRCF {nom_rcs}: <vide> !!! ")
|
pe_affichage.pe_print(f" > RCRCF {nom_rcs}: <vide> !!! ")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_rcs_etudiant(
|
def get_rcs_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]:
|
||||||
@ -439,7 +203,7 @@ def get_rcs_etudiant(
|
|||||||
numero_semestres_possibles = [numero_semestre_terminal]
|
numero_semestres_possibles = [numero_semestre_terminal]
|
||||||
elif nom_rcs.endswith("A"): # les années
|
elif nom_rcs.endswith("A"): # les années
|
||||||
numero_semestres_possibles = [
|
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
|
assert numero_semestre_terminal in numero_semestres_possibles
|
||||||
else: # les xS = tous les semestres jusqu'à Sx (eg S1, S2, S3 pour un S3 terminal)
|
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
|
semestres_aggreges[fid] = semestre
|
||||||
return semestres_aggreges
|
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.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 ResSemBUTTag
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from app.pe.pe_rcs import RCS, RCRCF
|
import app.pe.rcss.pe_rcs as pe_rcs
|
||||||
from app.pe.pe_sxtag import SxTag
|
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_tabletags import TableTag
|
||||||
from app.pe.pe_moytag import MoyennesTag
|
from app.pe.pe_moytag import MoyennesTag
|
||||||
|
|
||||||
|
|
||||||
class RCSTag(TableTag):
|
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)
|
"""Calcule les moyennes par tag (orientées compétences)
|
||||||
d'un regroupement de SxTag
|
d'un regroupement de SxTag
|
||||||
(RCRCF), pour extraire les classements par tag pour un
|
(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
|
self.rcs_id: tuple(str, int) = rcrcf.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.rcrcf: RCRCF = rcrcf
|
self.rcrcf: pe_rcrcf.RCRCF = rcrcf
|
||||||
"""RCRCF associé au RCS taggué"""
|
"""RCRCF associé au RCS taggué"""
|
||||||
|
|
||||||
self.nom = self.get_repr()
|
self.nom = self.get_repr()
|
||||||
@ -74,6 +74,8 @@ class RCSTag(TableTag):
|
|||||||
self.formsemestre_terminal = rcrcf.formsemestre_final
|
self.formsemestre_terminal = rcrcf.formsemestre_final
|
||||||
"""Le formsemestre terminal"""
|
"""Le formsemestre terminal"""
|
||||||
|
|
||||||
|
pe_affichage.pe_print(f"-> {self.get_repr(verbose=True)}")
|
||||||
|
|
||||||
# Les résultats du formsemestre terminal
|
# Les résultats du formsemestre terminal
|
||||||
nt = load_formsemestre_results(self.formsemestre_terminal)
|
nt = load_formsemestre_results(self.formsemestre_terminal)
|
||||||
|
|
||||||
@ -95,10 +97,12 @@ class RCSTag(TableTag):
|
|||||||
# Les compétences (extraites de tous les Sxtags)
|
# Les compétences (extraites de tous les Sxtags)
|
||||||
self.competences_sorted = self.do_complist()
|
self.competences_sorted = self.do_complist()
|
||||||
"""Compétences extraites de tous les SxTag aggrégés"""
|
"""Compétences extraites de tous les SxTag aggrégés"""
|
||||||
|
pe_affichage.pe_print(f"* Compétences : {', '.join(self.competences_sorted)}")
|
||||||
|
|
||||||
# Les tags
|
# Les tags
|
||||||
self.tags_sorted = self.do_taglist()
|
self.tags_sorted = self.do_taglist()
|
||||||
"""Tags extraits de tous les SxTag aggrégés"""
|
"""Tags extraits de tous les SxTag aggrégés"""
|
||||||
|
pe_affichage.pe_print(f"* Tags : {', '.join(self.tags_sorted)}")
|
||||||
|
|
||||||
# Les moyennes
|
# Les moyennes
|
||||||
self.moyennes_tags: dict[str, MoyennesTag] = {}
|
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"""
|
"""Egalité de 2 RCS taggués sur la base de leur identifiant"""
|
||||||
return self.rcs_id == other.sxtag_id
|
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
|
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
||||||
est basée)"""
|
est basée)"""
|
||||||
return self.rcrcf.get_repr(verbose=verbose)
|
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):
|
def compute_notes_comps_cube(self, tag):
|
||||||
"""Pour un tag donné, construit :
|
"""Pour un tag donné, construit :
|
||||||
@ -154,10 +161,11 @@ class RCSTag(TableTag):
|
|||||||
|
|
||||||
# Charge les notes et les coeffs du semestre tag
|
# Charge les notes et les coeffs du semestre tag
|
||||||
notes = moys_tag.matrice_notes.copy() # avec une copie
|
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
|
# 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
|
ues_columns_df = notes.columns
|
||||||
comp_associes_aux_ues = [association_ues_comp[ue] for ue in ues_columns_df]
|
comp_associes_aux_ues = [association_ues_comp[ue] for ue in ues_columns_df]
|
||||||
notes.columns = comp_associes_aux_ues
|
notes.columns = comp_associes_aux_ues
|
||||||
@ -204,16 +212,20 @@ class RCSTag(TableTag):
|
|||||||
tags = []
|
tags = []
|
||||||
for frmsem_id in self.sxstags:
|
for frmsem_id in self.sxstags:
|
||||||
tags.extend(self.sxstags[frmsem_id].tags_sorted)
|
tags.extend(self.sxstags[frmsem_id].tags_sorted)
|
||||||
pe_affichage.pe_print(f"* Tags : {', '.join(tags)}")
|
|
||||||
return sorted(set(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):
|
def do_complist(self):
|
||||||
"""Synthétise les compétences à partir des Sxtags aggrégés"""
|
"""Synthétise les compétences à partir des Sxtags aggrégés"""
|
||||||
competences = []
|
dict_comptences = self.mapping_ue_competences()
|
||||||
for sxtag_id, sxtag in self.sxstags.items():
|
return sorted(set(dict_comptences.values()))
|
||||||
comp = list(sxtag.moyennes_tags["but"].matrice_notes.columns)
|
|
||||||
competences.extend(comp)
|
|
||||||
return sorted(set(competences))
|
|
||||||
|
|
||||||
|
|
||||||
def compute_notes_competences(
|
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
|
# 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)
|
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
|
# Les moyennes par tag
|
||||||
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
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
|
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 import codes_cursus as sco_codes
|
||||||
|
|
||||||
|
|
||||||
class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
||||||
@ -66,44 +66,51 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
ResultatsSemestreBUT.__init__(self, formsemestre)
|
ResultatsSemestreBUT.__init__(self, formsemestre)
|
||||||
pe_tabletags.TableTag.__init__(self)
|
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é
|
# Le nom du res_semestre taggué
|
||||||
self.nom = self.get_repr(verbose=True)
|
self.nom = self.get_repr(verbose=True)
|
||||||
|
|
||||||
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
|
|
||||||
# self.nt = load_formsemestre_results(self.formsemestre)
|
|
||||||
|
|
||||||
# Les étudiants (etuds, états civils & etudis) ajouté
|
# Les étudiants (etuds, états civils & etudis) ajouté
|
||||||
self.add_etuds(self.etuds)
|
self.add_etuds(self.etuds)
|
||||||
|
self.etudids_sorted = sorted(self.etudids)
|
||||||
|
|
||||||
# Les UEs (et les dispenses d'UE)
|
# 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()
|
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 = {}
|
self.competences = {}
|
||||||
|
"""L'association acronyme d'UEs -> compétence"""
|
||||||
for ue in self.ues:
|
for ue in self.ues:
|
||||||
if ue.type != UE_SPORT:
|
if ue.type == sco_codes.UE_STANDARD:
|
||||||
assert ue.niveau_competence, ScoValueError("Des UEs ne sont pas rattachées à des compétences")
|
assert ue.niveau_competence, ScoValueError(
|
||||||
|
"Des UEs ne sont pas rattachées à des compétences"
|
||||||
|
)
|
||||||
nom = ue.niveau_competence.competence.titre
|
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:
|
# Les tags personnalisés et auto:
|
||||||
tags_dict = self._get_tags_dict()
|
tags_dict = self._get_tags_dict()
|
||||||
self._check_tags(tags_dict)
|
self._check_tags(tags_dict)
|
||||||
# self.tags = [tag for cat in dict_tags for tag in dict_tags[cat]]
|
|
||||||
|
|
||||||
# Les coefficients
|
# Les coefficients pour le calcul de la moyenne générale
|
||||||
matrice_coeffs = self.ues_inscr_parcours_df * [
|
self.matrice_coeffs_moy_gen = self.ues_inscr_parcours_df * [
|
||||||
ue.ects
|
ue.ects for ue in ues_standards # if ue.type != UE_SPORT <= déjà supprimé
|
||||||
for ue in ues_hors_sport # 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
|
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
|
||||||
self.moyennes_tags = {}
|
self.moyennes_tags = {}
|
||||||
|
|
||||||
@ -111,46 +118,56 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
||||||
infos_tag = tags_dict["personnalises"][tag]
|
infos_tag = tags_dict["personnalises"][tag]
|
||||||
moy_ues_tag = self.compute_moy_ues_tag(infos_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(
|
self.moyennes_tags[tag] = MoyennesTag(
|
||||||
tag,
|
tag, moy_ues_tag, self.matrice_coeffs_moy_gen
|
||||||
moy_ues_tag,
|
|
||||||
matrice_coeffs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ajoute les d'UE moyennes générales de BUT pour le semestre considéré
|
# Ajoute les moyennes par UEs + la moyenne générale (but)
|
||||||
# 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)
|
|
||||||
df_ues = pd.DataFrame(
|
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,
|
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(
|
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()
|
self.tags_sorted = self.get_all_tags()
|
||||||
"""Tags (personnalisés+compétences) par ordre alphabétique"""
|
"""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):
|
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.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)
|
||||||
|
|
||||||
|
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:
|
def compute_moy_ues_tag(self, info_tag: dict[int, dict]) -> pd.DataFrame:
|
||||||
"""Calcule la moyenne par UE des étudiants pour un tag,
|
"""Calcule la moyenne par UE des étudiants pour un tag,
|
||||||
en ayant connaissance des informations sur le 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é
|
# Adaptation du mask de calcul des moyennes au tag visé
|
||||||
modimpls_mask = [
|
modimpls_mask = [
|
||||||
modimpl.module.ue.type != UE_SPORT
|
modimpl.module.ue.type == sco_codes.UE_STANDARD
|
||||||
for modimpl in self.formsemestre.modimpls_sorted
|
for modimpl in self.formsemestre.modimpls_sorted
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -216,9 +233,6 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
self.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(
|
|
||||||
f"* Tags personnalisés (extraits du programme de formation) : {', '.join(noms_tags_perso)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Les tags automatiques
|
# Les tags automatiques
|
||||||
# Déduit des compétences
|
# Déduit des compétences
|
||||||
@ -229,8 +243,11 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
dict_tags["auto"] = {"but": {}}
|
dict_tags["auto"] = {"but": {}}
|
||||||
|
|
||||||
noms_tags_auto = sorted(list(set(dict_tags["auto"].keys()))) # + noms_tags_comp
|
noms_tags_auto = sorted(list(set(dict_tags["auto"].keys()))) # + noms_tags_comp
|
||||||
|
|
||||||
|
# Affichage
|
||||||
pe_affichage.pe_print(
|
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
|
return dict_tags
|
||||||
|
|
||||||
@ -312,4 +329,3 @@ def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return synthese_tags
|
return synthese_tags
|
||||||
|
|
||||||
|
@ -36,19 +36,23 @@ Created on Fri Sep 9 09:15:05 2016
|
|||||||
@author: barasc
|
@author: barasc
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from app.comp.res_sem import load_formsemestre_results
|
from app.pe import pe_affichage, pe_comp
|
||||||
from app.models import UniteEns, FormSemestre
|
import app.pe.pe_ressemtag as pe_ressemtag
|
||||||
from app.pe import pe_affichage
|
|
||||||
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_tabletags import TableTag
|
from app.pe.pe_tabletags import TableTag
|
||||||
from app.pe.pe_moytag import MoyennesTag
|
from app.pe.pe_moytag import MoyennesTag
|
||||||
|
import app.pe.rcss.pe_rcf as pe_rcf
|
||||||
|
|
||||||
|
|
||||||
class SxTag(TableTag):
|
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'
|
"""Calcule les moyennes/classements par tag d'un semestre de type 'Sx'
|
||||||
(par ex. 'S1', 'S2', ...) avec une orientation par UE :
|
(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
|
assert sxtag_id and len(sxtag_id) == 2 and sxtag_id[1] in ressembuttags
|
||||||
|
|
||||||
self.sxtag_id: (int, int) = sxtag_id
|
self.sxtag_id: (str, int) = sxtag_id
|
||||||
"""Identifiant du SxTag de la forme (semestre_id, fid_semestre_final)"""
|
"""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 ResSemBUTTags à regrouper dans le SxTag"""
|
||||||
|
|
||||||
# Les données du semestre final
|
# Les données du semestre final
|
||||||
@ -87,9 +95,13 @@ class SxTag(TableTag):
|
|||||||
self.ressembuttag_final = ressembuttags[self.fid_final]
|
self.ressembuttag_final = ressembuttags[self.fid_final]
|
||||||
"""Le ResSemBUTTag final"""
|
"""Le ResSemBUTTag final"""
|
||||||
|
|
||||||
# Les étudiants (etuds, états civils & etudis)
|
|
||||||
self.etuds = ressembuttags[self.fid_final].etuds
|
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.add_etuds(self.etuds)
|
||||||
|
self.etudids_sorted = sorted(self.etudids)
|
||||||
|
"""Les etudids triés"""
|
||||||
|
|
||||||
# Affichage
|
# Affichage
|
||||||
pe_affichage.pe_print(f"--> {self.get_repr()}")
|
pe_affichage.pe_print(f"--> {self.get_repr()}")
|
||||||
@ -103,50 +115,87 @@ class SxTag(TableTag):
|
|||||||
moy_sem_final = self.ressembuttag_final.moyennes_tags["but"]
|
moy_sem_final = self.ressembuttag_final.moyennes_tags["but"]
|
||||||
self.ues = list(moy_sem_final.matrice_notes.columns)
|
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
|
# Les acronymes des UE
|
||||||
self.acronymes_ues_sorted = sorted(self.ues)
|
self.acronymes_ues_sorted = sorted(self.ues)
|
||||||
|
|
||||||
# Les inscriptions des étudiants aux UEs
|
# Les inscriptions des étudiants aux UEs
|
||||||
# => ne conserve que les UEs du semestre final (pour les redoublants)
|
# => ne conserve que les UEs du semestre final (pour les redoublants)
|
||||||
self.matrice_coeffs = self.ressembuttag_final.moyennes_tags[
|
self.ues_inscr_parcours_df = self.ressembuttag_final.ues_inscr_parcours_df
|
||||||
"but"
|
self.ues_inscr_parcours_df.sort_index()
|
||||||
].matrice_coeffs
|
|
||||||
self.ues_inscr_parcours = ~np.isnan(self.matrice_coeffs.to_numpy())
|
|
||||||
|
|
||||||
|
# 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
|
# 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 et des capitalisations
|
||||||
inscr_mask = self.ues_inscr_parcours
|
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:
|
for tag in self.tags_sorted:
|
||||||
# Cube de note etudids x UEs
|
# 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,
|
notes_cube,
|
||||||
self.etudids,
|
masque_cube,
|
||||||
|
self.etudids_sorted,
|
||||||
self.acronymes_ues_sorted,
|
self.acronymes_ues_sorted,
|
||||||
inscr_mask,
|
inscr_mask,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Les profils d'ects (pour debug)
|
# Les profils d'ects (pour debug)
|
||||||
profils_ects = []
|
profils_ects = []
|
||||||
for i in self.matrice_coeffs.index:
|
for i in self.matrice_coeffs_moy_gen.index:
|
||||||
val = tuple(self.matrice_coeffs.loc[i].fillna("x"))
|
val = tuple(self.matrice_coeffs_moy_gen.loc[i].fillna("x"))
|
||||||
if tuple(val) not in profils_ects:
|
if tuple(val) not in profils_ects:
|
||||||
profils_ects.append(tuple(val))
|
profils_ects.append(tuple(val))
|
||||||
|
|
||||||
# Les moyennes
|
# Les moyennes
|
||||||
self.moyennes_tags[tag] = MoyennesTag(tag,
|
self.moyennes_tags[tag] = MoyennesTag(
|
||||||
matrice_moys_ues,
|
tag, matrice_moys_ues, self.matrice_coeffs_moy_gen
|
||||||
self.matrice_coeffs)
|
)
|
||||||
|
|
||||||
pe_affichage.pe_print(f"> MoyTag pour {tag} avec")
|
pe_affichage.pe_print(
|
||||||
pe_affichage.pe_print(f" - ues={self.acronymes_ues_sorted}")
|
f"> MoyTag 🏷{tag} avec "
|
||||||
pe_affichage.pe_print(f" - ects={profils_ects}")
|
+ f"ues={self.acronymes_ues_sorted} "
|
||||||
|
+ f"ects={profils_ects}"
|
||||||
|
)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""Egalité de 2 SxTag sur la base de leur identifiant"""
|
"""Egalité de 2 SxTag sur la base de leur identifiant"""
|
||||||
@ -155,60 +204,123 @@ class SxTag(TableTag):
|
|||||||
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)"""
|
||||||
affichage = [str(fid) for fid in self.ressembuttags]
|
if verbose:
|
||||||
return f"{self.sxtag_id[0]}Tag ({'+'.join(affichage)})"
|
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):
|
|
||||||
"""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
|
|
||||||
"""
|
|
||||||
# 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())
|
|
||||||
|
|
||||||
dfs = {}
|
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.
|
||||||
|
(Renvoie également le dataframe associé pour debug).
|
||||||
|
|
||||||
for frmsem_id in semestres_id:
|
Args:
|
||||||
# Partant d'un dataframe vierge
|
etudids_sorted: La liste des etudids triés par ordre croissant (dim 0)
|
||||||
df = pd.DataFrame(np.nan, index=etudids, columns=acronymes_ues_sorted)
|
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_sorted = etudids_sorted
|
||||||
|
# acronymes_ues = sorted([ue.acronyme for ue in selMf.ues.values()])
|
||||||
|
semestres_id = list(ressembuttags.keys())
|
||||||
|
|
||||||
# Charge les notes du semestre tag
|
dfs = {}
|
||||||
sem_tag = self.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)
|
for frmsem_id in semestres_id:
|
||||||
ues_communes = list(set(acronymes_ues_sorted) & set(acronymes_ues_sem))
|
# Partant d'un dataframe vierge
|
||||||
|
df = pd.DataFrame(np.nan, index=etudids_sorted, columns=acronymes_ues_sorted)
|
||||||
|
|
||||||
# Etudiants communs
|
# Charge les notes du semestre tag
|
||||||
etudids_communs = df.index.intersection(notes.index)
|
sem_tag = ressembuttags[frmsem_id]
|
||||||
|
moys_tag = sem_tag.moyennes_tags[tag]
|
||||||
|
notes = moys_tag.matrice_notes # dataframe etudids x ues
|
||||||
|
|
||||||
# Recopie
|
# les étudiants et les acronymes communs
|
||||||
df.loc[etudids_communs, ues_communes] = notes.loc[
|
etudids_communs, acronymes_communs = pe_comp.find_index_and_columns_communs(
|
||||||
etudids_communs, ues_communes
|
df, notes
|
||||||
|
)
|
||||||
|
|
||||||
|
# Recopie
|
||||||
|
df.loc[etudids_communs, acronymes_communs] = notes.loc[
|
||||||
|
etudids_communs, acronymes_communs
|
||||||
|
]
|
||||||
|
|
||||||
|
# Supprime tout ce qui n'est pas numérique
|
||||||
|
for col in df.columns:
|
||||||
|
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||||
|
|
||||||
|
# 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_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
|
||||||
]
|
]
|
||||||
|
|
||||||
# Supprime tout ce qui n'est pas numérique
|
# Stocke le df
|
||||||
for col in df.columns:
|
dfs[frmsem_id] = df
|
||||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
|
||||||
|
|
||||||
# Stocke le df
|
"""Réunit les notes sous forme d'un cube etudids x ues x semestres"""
|
||||||
dfs[frmsem_id] = df
|
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)
|
||||||
"""Réunit les notes sous forme d'un cube etudids x ues x semestres"""
|
return dfs, 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
|
|
||||||
|
|
||||||
|
|
||||||
def compute_notes_ues(
|
def compute_notes_ues(
|
||||||
set_cube: np.array,
|
set_cube: np.array,
|
||||||
etudids: list,
|
masque_cube: np.array,
|
||||||
acronymes_ues: list,
|
etudids_sorted: list,
|
||||||
|
acronymes_ues_sorted: list,
|
||||||
inscr_mask: np.array,
|
inscr_mask: np.array,
|
||||||
):
|
):
|
||||||
"""Calcule la moyenne par UEs à un tag donné en prenant la note maximum (UE
|
"""Calcule la moyenne par UEs à un tag donné en prenant la note maximum (UE
|
||||||
@ -217,8 +329,10 @@ def compute_notes_ues(
|
|||||||
Args:
|
Args:
|
||||||
set_cube: notes moyennes aux modules ndarray
|
set_cube: notes moyennes aux modules ndarray
|
||||||
(semestre_ids x etudids x UEs), des floats avec des NaN
|
(semestre_ids x etudids x UEs), des floats avec des NaN
|
||||||
etudids: liste des étudiants (dim. 0 du cube)
|
masque_cube: masque indiquant si la note doit être prise en compte ndarray
|
||||||
acronymes_ues: liste des acronymes des ues (dim. 1 du cube)
|
(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
|
inscr_mask: masque etudids x UE traduisant les inscriptions des
|
||||||
étudiants aux UE (du semestre terminal)
|
étudiants aux UE (du semestre terminal)
|
||||||
Returns:
|
Returns:
|
||||||
@ -227,18 +341,21 @@ def compute_notes_ues(
|
|||||||
"""
|
"""
|
||||||
nb_etuds, nb_ues, nb_semestres = set_cube.shape
|
nb_etuds, nb_ues, nb_semestres = set_cube.shape
|
||||||
nb_etuds_mask, nb_ues_mask = inscr_mask.shape
|
nb_etuds_mask, nb_ues_mask = inscr_mask.shape
|
||||||
assert nb_etuds == len(etudids)
|
assert nb_etuds == len(etudids_sorted)
|
||||||
assert nb_ues == len(acronymes_ues)
|
assert nb_ues == len(acronymes_ues_sorted)
|
||||||
assert nb_etuds == nb_etuds_mask
|
assert nb_etuds == nb_etuds_mask
|
||||||
assert nb_ues == nb_ues_mask
|
assert nb_ues == nb_ues_mask
|
||||||
|
|
||||||
# Quelles entrées du cube contiennent des notes ?
|
# Entrées à garder dans le cube en fonction du masque d'inscription aux UEs du parcours
|
||||||
mask = ~np.isnan(set_cube)
|
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
|
# 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
|
# 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)
|
||||||
|
|
||||||
@ -252,8 +369,8 @@ def compute_notes_ues(
|
|||||||
# Le dataFrame
|
# Le dataFrame
|
||||||
etud_moy_tag_df = pd.DataFrame(
|
etud_moy_tag_df = pd.DataFrame(
|
||||||
etud_moy,
|
etud_moy,
|
||||||
index=etudids, # les etudids
|
index=etudids_sorted, # les etudids
|
||||||
columns=acronymes_ues, # les tags
|
columns=acronymes_ues_sorted, # les tags
|
||||||
)
|
)
|
||||||
|
|
||||||
etud_moy_tag_df.fillna(np.nan)
|
etud_moy_tag_df.fillna(np.nan)
|
||||||
|
@ -52,9 +52,7 @@ class TableTag(object):
|
|||||||
# 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.etudids: list[int] = {}
|
||||||
"""Les états civils"""
|
|
||||||
# self.etudids: list[int] = None
|
|
||||||
"""Les etudids"""
|
"""Les etudids"""
|
||||||
|
|
||||||
def add_etuds(self, etuds: list[Identite]):
|
def add_etuds(self, etuds: list[Identite]):
|
||||||
@ -64,8 +62,7 @@ class TableTag(object):
|
|||||||
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.etudids = list({etud.etudid for etud in etuds})
|
||||||
self.etudids = list(self.etats_civils.keys())
|
|
||||||
|
|
||||||
def get_all_tags(self):
|
def get_all_tags(self):
|
||||||
"""Liste des tags de la table, triée par ordre alphabétique,
|
"""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
|
Le dataframe des notes et des classements
|
||||||
"""
|
"""
|
||||||
|
|
||||||
etudiants = self.etats_civils
|
etudiants = {etud.etudid: [etud.nom, etud.prenom] for etud in self.etuds}
|
||||||
df = pd.DataFrame.from_dict(etudiants, orient="index", columns=["nom"])
|
df = pd.DataFrame.from_dict(etudiants, orient="index", columns=["nom", "prenom"])
|
||||||
|
|
||||||
tags_tries = self.get_all_tags()
|
tags_tries = self.get_all_tags()
|
||||||
for tag in tags_tries:
|
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["notes"].rename(f"Moy {tag} (gen)"))
|
||||||
df = df.join(moy_gen.synthese["classements"].rename(f"Class {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
|
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