ScoDoc/app/pe/pe_tabletags.py

182 lines
6.5 KiB
Python

# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# 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 datetime
import numpy as np
from app.comp.moy_sem import comp_ranks_series
from app.pe import pe_affichage
from app.scodoc import sco_utils as scu
import pandas as pd
TAGS_RESERVES = ["but"]
class TableTag(object):
def __init__(self):
"""Classe centralisant différentes méthodes communes aux
SemestreTag, TrajectoireTag, AggregatInterclassTag
"""
pass
# -----------------------------------------------------------------------------------------------------------
def get_all_tags(self):
"""Liste des tags de la table, triée par ordre alphabétique
Returns:
Liste de tags triés par ordre alphabétique
"""
return sorted(self.moyennes_tags.keys())
def df_moyennes_et_classements(self):
"""Renvoie un dataframe listant toutes les moyennes,
et les classements des étudiants pour tous les tags
"""
etudiants = self.etudiants
df = pd.DataFrame.from_dict(etudiants, orient="index", columns=["nom"])
for tag in self.get_all_tags():
df = df.join(self.moyennes_tags[tag]["notes"].rename(f"Moy {tag}"))
df = df.join(self.moyennes_tags[tag]["classements"].rename(f"Class {tag}"))
return df
def df_notes(self):
"""Renvoie un dataframe (etudid x tag) listant toutes les moyennes par tags
Returns:
Un dataframe etudids x tag (avec tag par ordre alphabétique)
"""
tags = self.get_all_tags()
if tags:
dict_series = {tag: self.moyennes_tags[tag]["notes"] for tag in tags}
df = pd.DataFrame(dict_series)
return df
else:
return None
def str_tagtable(self):
"""Renvoie une chaine de caractère listant toutes les moyennes,
les rangs des étudiants pour tous les tags."""
etudiants = self.etudiants
df = pd.DataFrame.from_dict(etudiants, orient="index", columns=["nom"])
for tag in self.get_all_tags():
df = df.join(self.moyennes_tags[tag]["notes"].rename(f"moy {tag}"))
df = df.join(self.moyennes_tags[tag]["classements"].rename(f"class {tag}"))
return df.to_csv(sep=";")
def comp_moy_et_stat(self, notes: pd.Series) -> dict:
"""Calcule et structure les données nécessaires au PE pour une série
de notes (souvent une moyenne par tag) dans un dictionnaire spécifique.
Partant des notes, sont calculés les classements (en ne tenant compte
que des notes non nulles).
Args:
notes: Une série de notes (avec des éventuels NaN)
Returns:
Un dictionnaire stockant les notes, les classements, le min,
le max, la moyenne, le nb de notes (donc d'inscrits)
"""
# Supprime d'éventuels chaines de caractères dans les notes
notes = pd.to_numeric(notes, errors="coerce")
# Les indices des ... et les notes non nulles/pertinentes
indices = notes.notnull()
notes_non_nulles = notes[indices]
# Les classements sur les notes non nulles
(_, class_gen_ue_non_nul) = comp_ranks_series(notes_non_nulles)
# Les classements (toutes notes confondues, avec NaN si pas de notes)
class_gen_ue = pd.Series(np.nan, index=notes.index, dtype="Int64")
class_gen_ue[indices] = class_gen_ue_non_nul[indices]
synthese = {
"notes": notes,
"classements": class_gen_ue,
"min": notes.min(),
"max": notes.max(),
"moy": notes.mean(),
"nb_inscrits": len(indices),
}
return synthese
@classmethod
def get_min_for_df(cls, bilan: dict) -> float:
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
revoie le min renseigné pour affichage dans un df"""
return round(bilan["min"], 2)
@classmethod
def get_max_for_df(cls, bilan: dict) -> float:
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
renvoie le max renseigné pour affichage dans un df"""
return round(bilan["max"], 2)
@classmethod
def get_moy_for_df(cls, bilan: dict) -> float:
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
renvoie la moyenne renseignée pour affichage dans un df"""
return round(bilan["moy"], 2)
@classmethod
def get_class_for_df(cls, bilan: dict, etudid: int) -> str:
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
renvoie le classement ramené au nombre d'inscrits,
pour un étudiant donné par son etudid"""
classement = bilan['classements'].loc[etudid]
if not pd.isna(classement):
return f"{classement}/{bilan['nb_inscrits']}"
else:
return pe_affichage.SANS_NOTE
@classmethod
def get_note_for_df(cls, bilan: dict, etudid: int):
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
renvoie la note (moyenne)
pour un étudiant donné par son etudid"""
return round(bilan["notes"].loc[etudid], 2)