2020-09-26 16:19:37 +02:00
|
|
|
# -*- mode: python -*-
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# Gestion scolarite IUT
|
|
|
|
#
|
2023-12-31 23:04:06 +01:00
|
|
|
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
|
2020-09-26 16:19:37 +02:00
|
|
|
#
|
|
|
|
# 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 Fri Sep 9 09:15:05 2016
|
|
|
|
|
|
|
|
@author: barasc
|
|
|
|
"""
|
2024-01-24 15:37:50 +01:00
|
|
|
|
2024-01-21 13:14:04 +01:00
|
|
|
from app.comp.res_sem import load_formsemestre_results
|
2024-02-15 17:05:03 +01:00
|
|
|
from app.pe import pe_affichage
|
2024-02-17 02:35:43 +01:00
|
|
|
from app.pe.pe_ressemtag import ResSemBUTTag
|
2024-01-21 13:14:04 +01:00
|
|
|
import pandas as pd
|
|
|
|
import numpy as np
|
2024-02-18 19:24:03 +01:00
|
|
|
from app.pe.pe_rcs import RCS, RCRCF
|
|
|
|
from app.pe.pe_sxtag import SxTag
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-15 17:05:03 +01:00
|
|
|
from app.pe.pe_tabletags import TableTag
|
|
|
|
from app.pe.pe_moytag import MoyennesTag
|
2024-01-24 15:37:50 +01:00
|
|
|
|
|
|
|
|
2024-02-05 12:58:09 +01:00
|
|
|
class RCSTag(TableTag):
|
2024-02-18 19:24:03 +01:00
|
|
|
def __init__(self, rcrcf: RCS, sxstags: dict[(str, int): SxTag]):
|
|
|
|
"""Calcule les moyennes par tag (orientées compétences)
|
|
|
|
d'un regroupement de SxTag
|
|
|
|
(RCRCF), pour extraire les classements par tag pour un
|
2024-01-27 09:15:17 +01:00
|
|
|
groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous
|
2024-02-18 19:24:03 +01:00
|
|
|
participé au même semestre terminal.
|
2024-01-27 09:15:17 +01:00
|
|
|
|
2024-02-03 10:46:14 +01:00
|
|
|
Args:
|
2024-02-05 12:58:09 +01:00
|
|
|
rcs: Un RCS (identifié par un nom et l'id de son semestre terminal)
|
2024-02-18 19:24:03 +01:00
|
|
|
sxstags: Les données sur les RCF taggués
|
2024-01-27 09:15:17 +01:00
|
|
|
"""
|
|
|
|
TableTag.__init__(self)
|
2024-01-24 15:37:50 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
self.rcs_id: tuple(str, int) = rcrcf.rcs_id
|
2024-02-05 12:58:09 +01:00
|
|
|
"""Identifiant du RCS taggué (identique au RCS sur lequel il s'appuie)"""
|
2024-02-03 15:26:58 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
self.rcrcf: RCRCF = rcrcf
|
|
|
|
"""RCRCF associé au RCS taggué"""
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-01-27 09:15:17 +01:00
|
|
|
self.nom = self.get_repr()
|
2024-02-05 12:58:09 +01:00
|
|
|
"""Représentation textuelle du RCS taggué"""
|
2024-01-27 09:15:17 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
self.formsemestre_terminal = rcrcf.formsemestre_final
|
2024-02-02 17:16:07 +01:00
|
|
|
"""Le formsemestre terminal"""
|
2024-02-03 10:46:14 +01:00
|
|
|
|
2024-02-02 17:16:07 +01:00
|
|
|
# Les résultats du formsemestre terminal
|
2024-01-24 15:37:50 +01:00
|
|
|
nt = load_formsemestre_results(self.formsemestre_terminal)
|
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
self.rcfs_aggreges = rcrcf.rcfs_aggreges
|
|
|
|
"""Les RCFs aggrégés"""
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
self.sxstags = {}
|
|
|
|
"""Les SxTag associés aux RCF aggrégés"""
|
2024-02-17 02:35:43 +01:00
|
|
|
try:
|
2024-02-18 19:24:03 +01:00
|
|
|
for rcf_id in self.rcfs_aggreges:
|
|
|
|
self.sxstags[rcf_id] = sxstags[rcf_id]
|
2024-02-17 02:35:43 +01:00
|
|
|
except:
|
2024-02-18 19:24:03 +01:00
|
|
|
raise ValueError("Semestres SxTag manquants")
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-17 02:35:43 +01:00
|
|
|
# Les étudiants (etuds, états civils & etudis)
|
2024-02-18 19:24:03 +01:00
|
|
|
self.etuds = nt.etuds
|
2024-02-17 02:35:43 +01:00
|
|
|
self.add_etuds(nt.etuds)
|
2024-02-02 17:16:07 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
# Les compétences (extraites de tous les Sxtags)
|
|
|
|
self.competences_sorted = self.do_complist()
|
|
|
|
"""Compétences extraites de tous les SxTag aggrégés"""
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2024-02-17 02:35:43 +01:00
|
|
|
# Les tags
|
2024-01-21 13:14:04 +01:00
|
|
|
self.tags_sorted = self.do_taglist()
|
2024-02-18 19:24:03 +01:00
|
|
|
"""Tags extraits de tous les SxTag aggrégés"""
|
2024-01-21 18:05:00 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
# Les moyennes
|
2024-02-15 17:05:03 +01:00
|
|
|
self.moyennes_tags: dict[str, MoyennesTag] = {}
|
2024-02-02 17:16:07 +01:00
|
|
|
"""Synthétise les moyennes/classements par tag (qu'ils soient personnalisé ou de compétences)"""
|
2024-02-18 19:24:03 +01:00
|
|
|
|
2024-01-21 18:05:00 +01:00
|
|
|
for tag in self.tags_sorted:
|
2024-02-18 19:24:03 +01:00
|
|
|
# Cube de note
|
|
|
|
notes_cube, coeffs_cube = self.compute_notes_comps_cube(tag)
|
|
|
|
|
|
|
|
# Calcule des moyennes/coeffs sous forme d'un dataframe"""
|
|
|
|
moys_competences, coeffs_competences = compute_notes_competences(
|
|
|
|
notes_cube, coeffs_cube, self.etudids, self.competences_sorted
|
|
|
|
)
|
|
|
|
|
|
|
|
# Les moyennes
|
|
|
|
self.moyennes_tags[tag] = MoyennesTag(tag, moys_competences,
|
|
|
|
coeffs_competences)
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2024-02-03 15:26:58 +01:00
|
|
|
def __eq__(self, other):
|
2024-02-05 12:58:09 +01:00
|
|
|
"""Egalité de 2 RCS taggués sur la base de leur identifiant"""
|
2024-02-17 02:35:43 +01:00
|
|
|
return self.rcs_id == other.sxtag_id
|
2024-02-03 15:26:58 +01:00
|
|
|
|
2024-01-27 09:15:17 +01:00
|
|
|
def get_repr(self, verbose=False) -> str:
|
2024-01-24 19:37:45 +01:00
|
|
|
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
|
|
|
est basée)"""
|
2024-02-18 19:24:03 +01:00
|
|
|
return self.rcrcf.get_repr(verbose=verbose)
|
|
|
|
|
|
|
|
def compute_notes_comps_cube(self, tag):
|
|
|
|
"""Pour un tag donné, construit :
|
|
|
|
* le cube de notes (etudid x competences x SxTag) nécessaire au calcul des moyennes,
|
|
|
|
en remplaçant les données d'UE (obtenus du SxTag) par les compétences
|
|
|
|
* le cube de coeffs (etudid x competences x SxTag) (traduisant les inscriptions)
|
|
|
|
appliqué au calcul des différents SxTag
|
2024-01-21 13:14:04 +01:00
|
|
|
"""
|
2024-02-02 17:16:07 +01:00
|
|
|
# nb_tags = len(self.tags_sorted)
|
|
|
|
# nb_etudiants = len(self.etuds)
|
|
|
|
# nb_semestres = len(self.semestres_tags_aggreges)
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-02 17:16:07 +01:00
|
|
|
# Index du cube (etudids -> dim 0, tags -> dim 1)
|
2024-01-21 13:14:04 +01:00
|
|
|
etudids = [etud.etudid for etud in self.etuds]
|
2024-02-18 19:24:03 +01:00
|
|
|
competences_sorted = self.competences_sorted
|
|
|
|
sxstags_ids = list(self.sxstags.keys())
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
notes_dfs = {}
|
|
|
|
coeffs_dfs = {}
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-18 19:50:49 +01:00
|
|
|
for sxtag_id, sxtag in self.sxstags.items():
|
2024-02-02 17:16:07 +01:00
|
|
|
# Partant d'un dataframe vierge
|
2024-02-18 19:24:03 +01:00
|
|
|
notes_df = pd.DataFrame(np.nan, index=etudids, columns=competences_sorted)
|
|
|
|
coeffs_df = pd.DataFrame(np.nan, index=etudids, columns=competences_sorted)
|
|
|
|
|
|
|
|
moys_tag = sxtag.moyennes_tags[tag]
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
# Charge les notes et les coeffs du semestre tag
|
|
|
|
notes = moys_tag.matrice_notes.copy() # avec une copie
|
|
|
|
coeffs = moys_tag.matrice_coeffs.copy() # les coeffs
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
# Traduction des UE en compétences
|
|
|
|
association_ues_comp = moys_tag.competences
|
|
|
|
ues_columns_df = notes.columns
|
|
|
|
comp_associes_aux_ues = [association_ues_comp[ue] for ue in ues_columns_df]
|
|
|
|
notes.columns = comp_associes_aux_ues
|
|
|
|
coeffs.columns = comp_associes_aux_ues
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
# Compétences communes
|
|
|
|
comp_communes = list(set(competences_sorted) & set(comp_associes_aux_ues))
|
|
|
|
|
|
|
|
# Etudiants communs
|
|
|
|
etudids_communs = notes_df.index.intersection(notes.index)
|
|
|
|
|
|
|
|
# Recopie des notes et des coeffs
|
|
|
|
notes_df.loc[etudids_communs, comp_communes] = notes.loc[
|
|
|
|
etudids_communs, comp_communes
|
|
|
|
]
|
|
|
|
coeffs_df.loc[etudids_communs, comp_communes] = coeffs.loc[
|
|
|
|
etudids_communs, comp_communes
|
2024-01-21 13:14:04 +01:00
|
|
|
]
|
|
|
|
|
2024-01-27 10:45:17 +01:00
|
|
|
# Supprime tout ce qui n'est pas numérique
|
2024-02-18 19:24:03 +01:00
|
|
|
for col in notes_df.columns:
|
|
|
|
notes_df[col] = pd.to_numeric(notes_df[col], errors="coerce")
|
|
|
|
|
|
|
|
# Stocke les dfs
|
|
|
|
notes_dfs[sxtag_id] = notes_df
|
|
|
|
coeffs_dfs[sxtag_id] = coeffs_df
|
2024-01-27 10:45:17 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
"""Réunit les notes sous forme d'un cube etudids x competences x semestres"""
|
|
|
|
sxtag_x_etudids_x_comps = [notes_dfs[fid].values for fid in notes_dfs]
|
|
|
|
notes_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
"""Réunit les coeffs sous forme d'un cube etudids x competences x semestres"""
|
|
|
|
sxtag_x_etudids_x_comps = [coeffs_dfs[fid].values for fid in notes_dfs]
|
|
|
|
coeffs_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
return notes_etudids_x_comps_x_sxtag, coeffs_etudids_x_comps_x_sxtag
|
2024-01-21 13:14:04 +01:00
|
|
|
|
2020-09-26 16:19:37 +02:00
|
|
|
def do_taglist(self):
|
2024-02-18 19:24:03 +01:00
|
|
|
"""Synthétise les tags à partir des Sxtags aggrégés
|
2024-01-21 13:14:04 +01:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
Une liste de tags triés par ordre alphabétique
|
2020-09-26 16:19:37 +02:00
|
|
|
"""
|
2024-01-21 13:14:04 +01:00
|
|
|
tags = []
|
2024-02-18 19:24:03 +01:00
|
|
|
for frmsem_id in self.sxstags:
|
|
|
|
tags.extend(self.sxstags[frmsem_id].tags_sorted)
|
2024-02-15 17:05:03 +01:00
|
|
|
pe_affichage.pe_print(f"* Tags : {', '.join(tags)}")
|
2024-01-21 13:14:04 +01:00
|
|
|
return sorted(set(tags))
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
def do_complist(self):
|
|
|
|
"""Synthétise les compétences à partir des Sxtags aggrégés"""
|
|
|
|
competences = []
|
2024-02-18 19:50:49 +01:00
|
|
|
for sxtag_id, sxtag in self.sxstags.items():
|
|
|
|
comp = list(sxtag.moyennes_tags["but"].matrice_notes.columns)
|
2024-02-18 19:24:03 +01:00
|
|
|
competences.extend(comp)
|
|
|
|
return sorted(set(competences))
|
|
|
|
|
|
|
|
|
|
|
|
def compute_notes_competences(
|
|
|
|
set_cube: np.array, coeff_cube: np.array, etudids: list, competences: list
|
|
|
|
):
|
|
|
|
"""Calcule:
|
|
|
|
* la moyenne par compétences à un tag donné sur plusieurs semestres (partant du set_cube).
|
|
|
|
* la somme des coeffs à utiliser pour la moyenne générale.
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2024-01-21 18:05:00 +01:00
|
|
|
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles
|
|
|
|
|
|
|
|
*Remarque* : Adaptation de moy_ue.compute_ue_moys_apc au cas des moyennes de tag
|
|
|
|
par aggrégat de plusieurs semestres.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
set_cube: notes moyennes aux modules ndarray
|
2024-02-18 19:24:03 +01:00
|
|
|
(etuds x UEs|compétences x sxtags), des floats avec des NaN
|
|
|
|
coeffs_cube: somme des coeffs impliqués dans la moyennes
|
2024-01-21 18:05:00 +01:00
|
|
|
etudids: liste des étudiants (dim. 0 du cube)
|
2024-02-18 19:24:03 +01:00
|
|
|
competences: list
|
2024-01-21 18:05:00 +01:00
|
|
|
tags: liste des tags (dim. 1 du cube)
|
|
|
|
Returns:
|
|
|
|
Un DataFrame avec pour columns les moyennes par tags,
|
|
|
|
et pour rows les etudid
|
|
|
|
"""
|
2024-02-18 19:24:03 +01:00
|
|
|
nb_etuds, nb_comps, nb_semestres = set_cube.shape
|
2024-01-21 18:05:00 +01:00
|
|
|
assert nb_etuds == len(etudids)
|
2024-02-18 19:24:03 +01:00
|
|
|
assert nb_comps == len(competences)
|
2024-01-21 18:05:00 +01:00
|
|
|
|
|
|
|
# Quelles entrées du cube contiennent des notes ?
|
|
|
|
mask = ~np.isnan(set_cube)
|
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
# Enlève les NaN du cube de notes pour les entrées manquantes
|
2024-01-21 18:05:00 +01:00
|
|
|
set_cube_no_nan = np.nan_to_num(set_cube, nan=0.0)
|
2024-02-18 19:24:03 +01:00
|
|
|
coeffs_cube_no_nan = no.nan_to_num(coeff_cube, nan=0.0)
|
2024-01-21 18:05:00 +01:00
|
|
|
|
|
|
|
# Les moyennes par tag
|
|
|
|
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
|
|
|
etud_moy_tag = np.sum(set_cube_no_nan, axis=2) / np.sum(mask, axis=2)
|
2024-02-18 19:24:03 +01:00
|
|
|
# La somme des coeffs
|
|
|
|
coeff_tag = np.sum(coeffs_cube_no_nan, axis=2)
|
2024-01-21 18:05:00 +01:00
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
# Le dataFrame des notes moyennes
|
2024-01-21 18:05:00 +01:00
|
|
|
etud_moy_tag_df = pd.DataFrame(
|
|
|
|
etud_moy_tag,
|
|
|
|
index=etudids, # les etudids
|
2024-02-18 19:24:03 +01:00
|
|
|
columns=competences, # les competences
|
2024-01-21 18:05:00 +01:00
|
|
|
)
|
2024-02-02 17:16:07 +01:00
|
|
|
etud_moy_tag_df.fillna(np.nan)
|
|
|
|
|
2024-02-18 19:24:03 +01:00
|
|
|
coeffs_df = pd.DataFrame(coeff_tag, index=etudids, columns=competences)
|
|
|
|
coeffs_df.fillna(np.nan)
|
|
|
|
|
|
|
|
return etud_moy_tag_df, coeffs_df
|