2024-02-21 20:02:38 +01:00
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# Gestion scolarite IUT
|
|
|
|
#
|
|
|
|
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
#
|
|
|
|
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
|
|
|
|
##############################################################################
|
|
|
|
# Module "Avis de poursuite d'étude"
|
|
|
|
# conçu et développé par Cléo Baras (IUT de Grenoble)
|
|
|
|
##############################################################################
|
|
|
|
|
|
|
|
"""
|
|
|
|
Created on Thu Sep 8 09:36:33 2016
|
|
|
|
|
|
|
|
@author: barasc
|
|
|
|
"""
|
|
|
|
|
|
|
|
import pandas as pd
|
|
|
|
import numpy as np
|
|
|
|
|
|
|
|
from app.models import Identite
|
2024-02-25 16:25:28 +01:00
|
|
|
from app.pe import pe_affichage
|
2024-02-21 20:02:38 +01:00
|
|
|
from app.pe.moys import pe_tabletags, pe_moy, pe_moytag, pe_sxtag
|
|
|
|
from app.pe.rcss import pe_rcs
|
|
|
|
import app.pe.pe_comp as pe_comp
|
2024-02-27 14:39:14 +01:00
|
|
|
from app.scodoc.sco_utils import ModuleType
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
class InterClassTag(pe_tabletags.TableTag):
|
|
|
|
"""
|
|
|
|
Interclasse l'ensemble des étudiants diplômés à une année
|
|
|
|
donnée (celle du jury), pour un RCS donné (par ex: 'S2', '3S'), qu'il soit
|
|
|
|
de type SemX ou RCSemX,
|
|
|
|
en reportant les moyennes obtenues sur à la version tagguée
|
|
|
|
du RCS (de type SxTag ou RCSTag).
|
|
|
|
Sont ensuite calculés les classements (uniquement)
|
|
|
|
sur les étudiants diplômes.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
nom_rcs: Le nom de l'aggrégat
|
|
|
|
type_interclassement: Le type d'interclassement (par UE ou par compétences)
|
|
|
|
etudiants_diplomes: L'identité des étudiants diplômés
|
|
|
|
rcss: Un dictionnaire {(nom_rcs, fid_final): RCS} donnant soit
|
|
|
|
les SemX soit les RCSemX recencés par le jury PE
|
|
|
|
rcstag: Un dictionnaire {(nom_rcs, fid_final): RCSTag} donnant
|
|
|
|
soit les SxTag (associés aux SemX)
|
|
|
|
soit les RCSTags (associés au RCSemX) calculés par le jury PE
|
|
|
|
suivis: Un dictionnaire associé à chaque étudiant son rcss
|
|
|
|
(de la forme ``{etudid: {nom_rcs: RCS_suivi}}``)
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
nom_rcs: str,
|
|
|
|
type_interclassement: str,
|
|
|
|
etudiants_diplomes: dict[int, Identite],
|
|
|
|
rcss: dict[(str, int) : pe_rcs.RCS],
|
|
|
|
rcstags: dict[(str, int) : pe_tabletags.TableTag],
|
|
|
|
suivis: dict[int:dict],
|
|
|
|
):
|
|
|
|
pe_tabletags.TableTag.__init__(self)
|
|
|
|
|
|
|
|
self.nom_rcs: str = nom_rcs
|
|
|
|
"""Le nom du RCS interclassé"""
|
|
|
|
|
|
|
|
# Le type d'interclassement
|
|
|
|
self.type = type_interclassement
|
|
|
|
|
2024-02-25 16:25:28 +01:00
|
|
|
pe_affichage.pe_print(
|
2024-02-26 12:03:19 +01:00
|
|
|
f"*** Interclassement par 🗂️{type_interclassement} pour le RCS ⏯️{nom_rcs}"
|
2024-02-25 16:25:28 +01:00
|
|
|
)
|
|
|
|
|
2024-02-21 20:02:38 +01:00
|
|
|
# Les informations sur les étudiants diplômés
|
|
|
|
self.etuds: list[Identite] = list(etudiants_diplomes.values())
|
|
|
|
"""Identités des étudiants diplômés"""
|
|
|
|
self.add_etuds(self.etuds)
|
|
|
|
|
|
|
|
self.diplomes_ids = set(etudiants_diplomes.keys())
|
|
|
|
"""Etudids des étudiants diplômés"""
|
|
|
|
|
|
|
|
# Les RCS de l'aggrégat (SemX ou RCSemX)
|
|
|
|
self.rcss: dict[(str, int), pe_rcs.RCS] = {}
|
|
|
|
"""Ensemble des SemX ou des RCSemX associés à l'aggrégat"""
|
|
|
|
for (nom, fid), rcs in rcss.items():
|
|
|
|
if nom == nom_rcs:
|
|
|
|
self.rcss[(nom, fid)] = rcss
|
|
|
|
|
|
|
|
# Les données tagguées
|
|
|
|
self.rcstags: dict[(str, int), pe_tabletags.TableTag] = {}
|
|
|
|
"""Ensemble des SxTag ou des RCSTags associés à l'aggrégat"""
|
|
|
|
for rcs_id in self.rcss:
|
|
|
|
self.rcstags[rcs_id] = rcstags[rcs_id]
|
|
|
|
|
|
|
|
# Les RCS (SemX ou RCSemX) suivis par les étudiants du jury,
|
|
|
|
# en ne gardant que ceux associés aux diplomés
|
|
|
|
self.suivis: dict[int, pe_rcs.RCS] = {}
|
|
|
|
"""Association entre chaque étudiant et le SxTag ou RCSTag à prendre
|
|
|
|
pour l'aggrégat"""
|
|
|
|
for etudid in self.diplomes_ids:
|
|
|
|
self.suivis[etudid] = suivis[etudid][nom_rcs]
|
|
|
|
|
|
|
|
# Les données sur les tags
|
|
|
|
self.tags_sorted = self._do_taglist()
|
|
|
|
"""Liste des tags (triés par ordre alphabétique)"""
|
2024-02-27 14:39:14 +01:00
|
|
|
aff = pe_affichage.repr_tags(self.tags_sorted)
|
2024-02-26 12:03:19 +01:00
|
|
|
pe_affichage.pe_print(f"--> Tags : {aff}")
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
# Les données sur les UEs (si SxTag) ou compétences (si RCSTag)
|
|
|
|
self.champs_sorted = self._do_ues_ou_competences_list()
|
2024-02-25 16:25:28 +01:00
|
|
|
"""Les champs (UEs ou compétences) de l'interclassement"""
|
2024-02-26 12:03:19 +01:00
|
|
|
if self.type == pe_moytag.CODE_MOY_UE:
|
|
|
|
pe_affichage.pe_print(
|
|
|
|
f"--> UEs : {pe_affichage.aff_UEs(self.champs_sorted)}"
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
pe_affichage.pe_print(
|
|
|
|
f"--> Compétences : {pe_affichage.aff_competences(self.champs_sorted)}"
|
|
|
|
)
|
2024-02-21 20:02:38 +01:00
|
|
|
|
2024-02-27 14:39:14 +01:00
|
|
|
# Etudids triés
|
|
|
|
self.etudids_sorted = sorted(list(self.diplomes_ids))
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
self.nom = self.get_repr()
|
|
|
|
"""Représentation textuelle de l'interclassement"""
|
|
|
|
|
|
|
|
# Synthétise les moyennes/classements par tag
|
|
|
|
self.moyennes_tags: dict[str, pe_moytag.MoyennesTag] = {}
|
|
|
|
for tag in self.tags_sorted:
|
2024-02-27 14:39:14 +01:00
|
|
|
# Les moyennes tous modules confondus
|
2024-02-27 16:18:08 +01:00
|
|
|
notes_gen = self.compute_notes_matrice(tag)
|
2024-02-27 14:39:14 +01:00
|
|
|
|
|
|
|
# Les coefficients de la moyenne générale
|
|
|
|
coeffs = self.compute_coeffs_matrice(tag)
|
|
|
|
aff = pe_affichage.repr_profil_coeffs(coeffs, with_index=True)
|
2024-02-26 12:03:19 +01:00
|
|
|
pe_affichage.pe_print(f"--> Moyenne 👜{tag} avec coeffs: {aff} ")
|
2024-02-25 16:25:28 +01:00
|
|
|
|
2024-02-21 20:02:38 +01:00
|
|
|
self.moyennes_tags[tag] = pe_moytag.MoyennesTag(
|
|
|
|
tag,
|
|
|
|
self.type,
|
2024-02-27 14:39:14 +01:00
|
|
|
notes_gen,
|
2024-02-21 20:02:38 +01:00
|
|
|
coeffs, # limite les moyennes aux étudiants de la promo
|
|
|
|
)
|
|
|
|
|
|
|
|
def get_repr(self) -> str:
|
|
|
|
"""Une représentation textuelle"""
|
|
|
|
return f"{self.nom_rcs} par {self.type}"
|
|
|
|
|
|
|
|
def _do_taglist(self):
|
|
|
|
"""Synthétise les tags à partir des TableTags (SXTag ou RCSTag)
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Une liste de tags triés par ordre alphabétique
|
|
|
|
"""
|
|
|
|
tags = []
|
|
|
|
for rcstag in self.rcstags.values():
|
|
|
|
tags.extend(rcstag.tags_sorted)
|
|
|
|
return sorted(set(tags))
|
|
|
|
|
2024-02-27 16:18:08 +01:00
|
|
|
def compute_notes_matrice(self, tag) -> pd.DataFrame:
|
2024-02-21 20:02:38 +01:00
|
|
|
"""Construit la matrice de notes (etudids x champs) en
|
|
|
|
reportant les moyennes obtenues par les étudiants
|
|
|
|
aux semestres de l'aggrégat pour le tag visé.
|
|
|
|
|
|
|
|
Les champs peuvent être des acronymes d'UEs ou des compétences.
|
|
|
|
|
|
|
|
Args:
|
2024-02-27 14:39:14 +01:00
|
|
|
tag: Le tag visé
|
2024-02-21 20:02:38 +01:00
|
|
|
Return:
|
|
|
|
Le dataFrame (etudids x champs)
|
|
|
|
reportant les moyennes des étudiants aux champs
|
|
|
|
"""
|
2024-02-27 14:39:14 +01:00
|
|
|
# etudids_sorted: Les etudids des étudiants (diplômés) triés
|
|
|
|
# champs_sorted: Les champs (UE ou compétences) à faire apparaitre dans la matrice
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
# Partant d'un dataframe vierge
|
2024-02-27 14:39:14 +01:00
|
|
|
df = pd.DataFrame(np.nan, index=self.etudids_sorted, columns=self.champs_sorted)
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
for rcstag in self.rcstags.values():
|
|
|
|
# Charge les moyennes au tag d'un RCStag
|
|
|
|
if tag in rcstag.moyennes_tags:
|
2024-02-27 14:39:14 +01:00
|
|
|
moytag = rcstag.moyennes_tags[tag]
|
2024-02-27 16:18:08 +01:00
|
|
|
|
|
|
|
notes = moytag.matrice_notes_gen # dataframe etudids x ues
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
# Etudiants/Champs communs entre le RCSTag et les données interclassées
|
|
|
|
(
|
|
|
|
etudids_communs,
|
|
|
|
champs_communs,
|
2024-02-27 14:39:14 +01:00
|
|
|
) = pe_comp.find_index_and_columns_communs(df, notes)
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
# Injecte les notes par tag
|
2024-02-27 14:39:14 +01:00
|
|
|
df.loc[etudids_communs, champs_communs] = notes.loc[
|
2024-02-21 20:02:38 +01:00
|
|
|
etudids_communs, champs_communs
|
|
|
|
]
|
|
|
|
|
|
|
|
return df
|
|
|
|
|
2024-02-27 14:39:14 +01:00
|
|
|
def compute_coeffs_matrice(self, tag) -> pd.DataFrame:
|
2024-02-21 20:02:38 +01:00
|
|
|
"""Idem que compute_notes_matrices mais pour les coeffs
|
|
|
|
|
|
|
|
Args:
|
2024-02-27 14:39:14 +01:00
|
|
|
tag: Le tag visé
|
2024-02-21 20:02:38 +01:00
|
|
|
Return:
|
|
|
|
Le dataFrame (etudids x champs)
|
|
|
|
reportant les moyennes des étudiants aux champs
|
|
|
|
"""
|
2024-02-27 14:39:14 +01:00
|
|
|
# etudids_sorted: Les etudids des étudiants (diplômés) triés
|
|
|
|
# champs_sorted: Les champs (UE ou compétences) à faire apparaitre dans la matrice
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
# Partant d'un dataframe vierge
|
2024-02-27 14:39:14 +01:00
|
|
|
df = pd.DataFrame(np.nan, index=self.etudids_sorted, columns=self.champs_sorted)
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
for rcstag in self.rcstags.values():
|
|
|
|
if tag in rcstag.moyennes_tags:
|
|
|
|
# Charge les coeffs au tag d'un RCStag
|
2024-02-26 12:03:19 +01:00
|
|
|
coeffs: pd.DataFrame = rcstag.moyennes_tags[tag].matrice_coeffs_moy_gen
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
# Etudiants/Champs communs entre le RCSTag et les données interclassées
|
|
|
|
(
|
|
|
|
etudids_communs,
|
|
|
|
champs_communs,
|
|
|
|
) = pe_comp.find_index_and_columns_communs(df, coeffs)
|
|
|
|
|
|
|
|
# Injecte les coeffs par tag
|
|
|
|
df.loc[etudids_communs, champs_communs] = coeffs.loc[
|
|
|
|
etudids_communs, champs_communs
|
|
|
|
]
|
|
|
|
|
|
|
|
return df
|
|
|
|
|
|
|
|
def _do_ues_ou_competences_list(self) -> list[str]:
|
|
|
|
"""Synthétise les champs (UEs ou compétences) sur lesquels
|
|
|
|
sont calculés les moyennes.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Un dictionnaire {'acronyme_ue' : 'compétences'}
|
|
|
|
"""
|
|
|
|
dict_champs = []
|
|
|
|
for rcstag in self.rcstags.values():
|
|
|
|
if isinstance(rcstag, pe_sxtag.SxTag):
|
|
|
|
champs = rcstag.acronymes_sorted
|
|
|
|
else: # pe_rcstag.RCSTag
|
|
|
|
champs = rcstag.competences_sorted
|
|
|
|
dict_champs.extend(champs)
|
|
|
|
return sorted(set(dict_champs))
|
|
|
|
|
|
|
|
def has_tags(self):
|
|
|
|
"""Indique si l'interclassement a des tags (cas d'un
|
|
|
|
interclassement sur un S5 qui n'a pas eu lieu)
|
|
|
|
"""
|
|
|
|
return len(self.tags_sorted) > 0
|
|
|
|
|
|
|
|
def _un_rcstag_significatif(self, rcsstags: dict[(str, int):pe_tabletags]):
|
|
|
|
"""Renvoie un rcstag significatif (ayant des tags et des notes aux tags)
|
|
|
|
parmi le dictionnaire de rcsstags"""
|
|
|
|
for rcstag_id, rcstag in rcsstags.items():
|
|
|
|
moystags: pe_moytag.MoyennesTag = rcstag.moyennes_tags
|
|
|
|
for tag, moystag in moystags.items():
|
|
|
|
tags_tries = moystag.get_all_significant_tags()
|
|
|
|
if tags_tries:
|
|
|
|
return moystag
|
|
|
|
return None
|
|
|
|
|
|
|
|
def compute_df_synthese_moyennes_tag(
|
2024-02-27 18:16:25 +01:00
|
|
|
self, tag, aggregat=None, type_colonnes=False, options={"min_max_moy": True}
|
2024-02-21 20:02:38 +01:00
|
|
|
) -> pd.DataFrame:
|
|
|
|
"""Construit le dataframe retraçant pour les données des moyennes
|
|
|
|
pour affichage dans la synthèse du jury PE. (cf. to_df())
|
|
|
|
|
|
|
|
Args:
|
|
|
|
etudids_sorted: Les etudids des étudiants (diplômés) triés
|
|
|
|
champs_sorted: Les champs (UE ou compétences) à faire apparaitre dans la matrice
|
|
|
|
Return:
|
|
|
|
Le dataFrame (etudids x champs)
|
|
|
|
reportant les moyennes des étudiants aux champs
|
|
|
|
"""
|
|
|
|
if aggregat:
|
|
|
|
assert (
|
|
|
|
aggregat == self.nom_rcs
|
|
|
|
), "L'interclassement ciblé ne correspond pas à l'aggrégat visé"
|
|
|
|
|
|
|
|
etudids_sorted = sorted(list(self.diplomes_ids))
|
|
|
|
|
2024-02-24 09:31:47 +01:00
|
|
|
if not self.rcstags:
|
2024-02-21 20:02:38 +01:00
|
|
|
return None
|
|
|
|
|
|
|
|
# Partant d'un dataframe vierge
|
2024-02-24 09:31:47 +01:00
|
|
|
initialisation = False
|
|
|
|
df = pd.DataFrame()
|
|
|
|
|
2024-02-27 14:58:15 +01:00
|
|
|
# Pour chaque rcs (suivi) associe la liste des etudids l'ayant suivi
|
|
|
|
asso_rcs_etudids = {}
|
2024-02-24 09:31:47 +01:00
|
|
|
for etudid in etudids_sorted:
|
2024-02-27 14:58:15 +01:00
|
|
|
rcs = self.suivis[etudid]
|
2024-02-24 09:31:47 +01:00
|
|
|
if rcs:
|
2024-02-27 14:58:15 +01:00
|
|
|
if rcs.rcs_id not in asso_rcs_etudids:
|
|
|
|
asso_rcs_etudids[rcs.rcs_id] = []
|
|
|
|
asso_rcs_etudids[rcs.rcs_id].append(etudid)
|
2024-02-24 09:31:47 +01:00
|
|
|
|
2024-02-27 14:58:15 +01:00
|
|
|
for rcs_id, etudids in asso_rcs_etudids.items():
|
|
|
|
# Charge ses moyennes au RCSTag suivi
|
|
|
|
rcstag = self.rcstags[rcs_id] # Le SxTag ou RCSTag
|
|
|
|
# Charge la moyenne
|
|
|
|
if tag in rcstag.moyennes_tags:
|
|
|
|
moytag: pd.DataFrame = rcstag.moyennes_tags[tag]
|
|
|
|
df_moytag = moytag.to_df(
|
2024-02-27 18:16:25 +01:00
|
|
|
aggregat=aggregat, cohorte="Groupe", options=options
|
2024-02-27 14:58:15 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
# Modif les colonnes au regard du 1er df_moytag significatif lu
|
|
|
|
if not initialisation:
|
|
|
|
df = pd.DataFrame(
|
|
|
|
np.nan, index=etudids_sorted, columns=df_moytag.columns
|
|
|
|
)
|
|
|
|
colonnes = list(df_moytag.columns)
|
|
|
|
for col in colonnes:
|
|
|
|
if col.endswith("rang"):
|
|
|
|
df[col] = df[col].astype(str)
|
|
|
|
initialisation = True
|
|
|
|
|
2024-02-27 16:18:08 +01:00
|
|
|
# Injecte les notes des étudiants
|
|
|
|
df.loc[etudids, :] = df_moytag.loc[etudids, :]
|
2024-02-21 20:02:38 +01:00
|
|
|
|
|
|
|
return df
|