339 lines
14 KiB
Python
339 lines
14 KiB
Python
|
# -*- mode: python -*-
|
||
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
##############################################################################
|
||
|
#
|
||
|
# Gestion scolarite IUT
|
||
|
#
|
||
|
# Copyright (c) 1999 - 2020 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 notes_table
|
||
|
import datetime
|
||
|
import codecs
|
||
|
|
||
|
|
||
|
class TableTag:
|
||
|
"""
|
||
|
Classe mémorisant les moyennes des étudiants à différents tag et permettant de calculer les rangs et les statistiques :
|
||
|
- nom : Nom représentatif des données de la Table
|
||
|
- inscrlist : Les étudiants inscrits dans le TagTag avec leur information de la forme :
|
||
|
{ etudid : dictionnaire d'info extrait de Scodoc, ...}
|
||
|
- taglist : Liste triée des noms des tags
|
||
|
- resultats : Dictionnaire donnant les notes-moyennes de chaque étudiant par tag et la somme commulée
|
||
|
des coeff utilisées dans le calcul de la moyenne pondérée, sous la forme :
|
||
|
{ tag : { etudid: (note_moy, somme_coeff_norm),
|
||
|
...}
|
||
|
- rangs : Dictionnaire donnant les rang par tag de chaque étudiant de la forme :
|
||
|
{ tag : {etudid: rang, ...} }
|
||
|
- nbinscrits : Nombre d'inscrits dans le semestre (pas de distinction entre les tags)
|
||
|
- statistiques : Dictionnaire donnant les stastitiques (moyenne, min, max) des résultats par tag de la forme :
|
||
|
{ tag : (moy, min, max), ...}
|
||
|
|
||
|
"""
|
||
|
|
||
|
def __init__(self, nom=""):
|
||
|
self.nom = nom
|
||
|
self.inscrlist = []
|
||
|
self.identdict = {}
|
||
|
self.taglist = []
|
||
|
|
||
|
self.resultats = {}
|
||
|
self.rangs = {}
|
||
|
self.statistiques = {}
|
||
|
|
||
|
# *****************************************************************************************************************
|
||
|
# Accesseurs
|
||
|
# *****************************************************************************************************************
|
||
|
|
||
|
# -----------------------------------------------------------------------------------------------------------
|
||
|
def get_moy_from_resultats(self, tag, etudid):
|
||
|
"""Renvoie la moyenne obtenue par un étudiant à un tag donné au regard du format de self.resultats"""
|
||
|
return (
|
||
|
self.resultats[tag][etudid][0]
|
||
|
if tag in self.resultats and etudid in self.resultats[tag]
|
||
|
else None
|
||
|
)
|
||
|
|
||
|
# -----------------------------------------------------------------------------------------------------------
|
||
|
def get_rang_from_resultats(self, tag, etudid):
|
||
|
"""Renvoie le rang à un tag d'un étudiant au regard du format de self.resultats"""
|
||
|
return (
|
||
|
self.rangs[tag][etudid]
|
||
|
if tag in self.resultats and etudid in self.resultats[tag]
|
||
|
else None
|
||
|
)
|
||
|
|
||
|
# -----------------------------------------------------------------------------------------------------------
|
||
|
def get_coeff_from_resultats(self, tag, etudid):
|
||
|
"""Renvoie la somme des coeffs de pondération normalisée utilisés dans le calcul de la moyenne à un tag d'un étudiant
|
||
|
au regard du format de self.resultats.
|
||
|
"""
|
||
|
return (
|
||
|
self.resultats[tag][etudid][1]
|
||
|
if tag in self.resultats and etudid in self.resultats[tag]
|
||
|
else None
|
||
|
)
|
||
|
|
||
|
# -----------------------------------------------------------------------------------------------------------
|
||
|
def get_all_tags(self):
|
||
|
"""Renvoie la liste des tags du semestre triée par ordre alphabétique"""
|
||
|
# return self.taglist
|
||
|
return sorted(self.resultats.keys())
|
||
|
|
||
|
# -----------------------------------------------------------------------------------------------------------
|
||
|
def get_nbinscrits(self):
|
||
|
"""Renvoie le nombre d'inscrits"""
|
||
|
return len(self.inscrlist)
|
||
|
|
||
|
# -----------------------------------------------------------------------------------------------------------
|
||
|
def get_moy_from_stats(self, tag):
|
||
|
""" Renvoie la moyenne des notes calculées pour d'un tag donné"""
|
||
|
return self.statistiques[tag][0] if tag in self.statistiques else None
|
||
|
|
||
|
def get_min_from_stats(self, tag):
|
||
|
""" Renvoie la plus basse des notes calculées pour d'un tag donné"""
|
||
|
return self.statistiques[tag][1] if tag in self.statistiques else None
|
||
|
|
||
|
def get_max_from_stats(self, tag):
|
||
|
""" Renvoie la plus haute des notes calculées pour d'un tag donné"""
|
||
|
return self.statistiques[tag][2] if tag in self.statistiques else None
|
||
|
|
||
|
# -----------------------------------------------------------------------------------------------------------
|
||
|
# La structure des données mémorisées pour chaque tag dans le dictionnaire de synthèse
|
||
|
# d'un jury PE
|
||
|
FORMAT_DONNEES_ETUDIANTS = (
|
||
|
"note",
|
||
|
"coeff",
|
||
|
"rang",
|
||
|
"nbinscrits",
|
||
|
"moy",
|
||
|
"max",
|
||
|
"min",
|
||
|
)
|
||
|
|
||
|
def get_resultatsEtud(self, tag, etudid):
|
||
|
"""Renvoie un tuple (note, coeff, rang, nb_inscrit, moy, min, max) synthétisant les résultats d'un étudiant
|
||
|
à un tag donné. None sinon"""
|
||
|
return (
|
||
|
self.get_moy_from_resultats(tag, etudid),
|
||
|
self.get_coeff_from_resultats(tag, etudid),
|
||
|
self.get_rang_from_resultats(tag, etudid),
|
||
|
self.get_nbinscrits(),
|
||
|
self.get_moy_from_stats(tag),
|
||
|
self.get_min_from_stats(tag),
|
||
|
self.get_max_from_stats(tag),
|
||
|
)
|
||
|
|
||
|
# return self.tag_stats[tag]
|
||
|
# else :
|
||
|
# return self.pe_stats
|
||
|
|
||
|
# *****************************************************************************************************************
|
||
|
# Ajout des notes
|
||
|
# *****************************************************************************************************************
|
||
|
|
||
|
# -----------------------------------------------------------------------------------------------------------
|
||
|
def add_moyennesTag(self, tag, listMoyEtCoeff):
|
||
|
"""
|
||
|
Mémorise les moyennes, les coeffs de pondération et les etudid dans resultats
|
||
|
avec calcul du rang
|
||
|
:param tag: Un tag
|
||
|
:param listMoyEtCoeff: Une liste donnant [ (moy, coeff, etudid) ]
|
||
|
"""
|
||
|
# ajout des moyennes au dictionnaire résultat
|
||
|
if listMoyEtCoeff:
|
||
|
self.resultats[tag] = {
|
||
|
etudid: (moyenne, somme_coeffs)
|
||
|
for (moyenne, somme_coeffs, etudid) in listMoyEtCoeff
|
||
|
}
|
||
|
|
||
|
# Calcule les rangs
|
||
|
lesMoyennesTriees = sorted(
|
||
|
listMoyEtCoeff, reverse=True, key=lambda col: col[0]
|
||
|
) # triées
|
||
|
self.rangs[tag] = notes_table.comp_ranks(lesMoyennesTriees) # les rangs
|
||
|
|
||
|
# calcul des stats
|
||
|
self.comp_stats_d_un_tag(tag)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
# *****************************************************************************************************************
|
||
|
# Méthodes dévolues aux calculs de statistiques (min, max, moy) sur chaque moyenne taguée
|
||
|
# *****************************************************************************************************************
|
||
|
|
||
|
def comp_stats_d_un_tag(self, tag):
|
||
|
"""
|
||
|
Calcule la moyenne generale, le min, le max pour un tag donné,
|
||
|
en ne prenant en compte que les moyennes significatives. Mémorise le resultat dans
|
||
|
self.statistiques
|
||
|
"""
|
||
|
stats = ("-NA-", "-", "-")
|
||
|
if tag not in self.resultats:
|
||
|
return stats
|
||
|
|
||
|
notes = [
|
||
|
self.get_moy_from_resultats(tag, etudid) for etudid in self.resultats[tag]
|
||
|
] # les notes du tag
|
||
|
notes_valides = [
|
||
|
note for note in notes if isinstance(note, float) and note != None
|
||
|
]
|
||
|
nb_notes_valides = len(notes_valides)
|
||
|
if nb_notes_valides > 0:
|
||
|
(moy, coeff) = moyenne_ponderee_terme_a_terme(notes_valides, force=True)
|
||
|
self.statistiques[tag] = (moy, max(notes_valides), min(notes_valides))
|
||
|
|
||
|
# ************************************************************************
|
||
|
# Méthodes dévolues aux affichages -> a revoir
|
||
|
# ************************************************************************
|
||
|
def str_resTag_d_un_etudiant(self, tag, etudid, delim=";"):
|
||
|
"""Renvoie une chaine de caractères (valable pour un csv)
|
||
|
décrivant la moyenne et le rang d'un étudiant, pour un tag donné ;
|
||
|
"""
|
||
|
if tag not in self.get_all_tags() or etudid not in self.resultats[tag]:
|
||
|
return ""
|
||
|
|
||
|
moystr = TableTag.str_moytag(
|
||
|
self.get_moy_from_resultats(tag, etudid),
|
||
|
self.get_rang_from_resultats(tag, etudid),
|
||
|
self.get_nbinscrits(),
|
||
|
delim=delim,
|
||
|
)
|
||
|
return moystr
|
||
|
|
||
|
def str_res_d_un_etudiant(self, etudid, delim=";"):
|
||
|
"""Renvoie sur une ligne les résultats d'un étudiant à tous les tags (par ordre alphabétique). """
|
||
|
return delim.join(
|
||
|
[self.str_resTag_d_un_etudiant(tag, etudid) for tag in self.get_all_tags()]
|
||
|
)
|
||
|
|
||
|
# -----------------------------------------------------------------------
|
||
|
def str_moytag(cls, moyenne, rang, nbinscrit, delim=";"):
|
||
|
"""Renvoie une chaine de caractères représentant une moyenne (float ou string) et un rang
|
||
|
pour différents formats d'affichage : HTML, debug ligne de commande, csv"""
|
||
|
moystr = (
|
||
|
"%2.2f%s%s%s%d" % (moyenne, delim, rang, delim, nbinscrit)
|
||
|
if isinstance(moyenne, float)
|
||
|
else str(moyenne) + delim + str(rang) + delim + str(nbinscrit)
|
||
|
)
|
||
|
return moystr
|
||
|
|
||
|
str_moytag = classmethod(str_moytag)
|
||
|
# -----------------------------------------------------------------------
|
||
|
|
||
|
def str_tagtable(self, delim=";", decimal_sep=","):
|
||
|
"""Renvoie une chaine de caractère listant toutes les moyennes, les rangs des étudiants pour tous les tags. """
|
||
|
entete = ["etudid", "nom", "prenom"]
|
||
|
for tag in self.get_all_tags():
|
||
|
entete += [titre + "_" + tag for titre in ["note", "rang", "nb_inscrit"]]
|
||
|
chaine = delim.join(entete) + "\n"
|
||
|
|
||
|
for etudid in self.identdict:
|
||
|
descr = delim.join(
|
||
|
[
|
||
|
etudid,
|
||
|
self.identdict[etudid]["nom"],
|
||
|
self.identdict[etudid]["prenom"],
|
||
|
]
|
||
|
)
|
||
|
descr += delim + self.str_res_d_un_etudiant(etudid, delim)
|
||
|
chaine += descr + "\n"
|
||
|
|
||
|
# Ajout des stats ... à faire
|
||
|
|
||
|
if decimal_sep != ".":
|
||
|
return chaine.replace(".", decimal_sep)
|
||
|
else:
|
||
|
return chaine
|
||
|
|
||
|
|
||
|
# ************************************************************************
|
||
|
# Fonctions diverses
|
||
|
# ************************************************************************
|
||
|
|
||
|
|
||
|
# *********************************************
|
||
|
def moyenne_ponderee_terme_a_terme(notes, coeffs=None, force=False):
|
||
|
"""
|
||
|
Calcule la moyenne pondérée d'une liste de notes avec d'éventuels coeffs de pondération.
|
||
|
Renvoie le résultat sous forme d'un tuple (moy, somme_coeff)
|
||
|
|
||
|
La liste de notes contient soit : 1) des valeurs numériques 2) des strings "-NA-" (pas de notes) ou "-NI-" (pas inscrit)
|
||
|
ou "-c-" ue capitalisée, 3) None.
|
||
|
Le paramètre force indique si le calcul de la moyenne doit être forcée ou non, c'est à
|
||
|
dire s'il y a ou non omission des notes non numériques (auquel cas la moyenne est calculée sur les
|
||
|
notes disponibles) ; sinon renvoie (None, None).
|
||
|
"""
|
||
|
# Vérification des paramètres d'entrée
|
||
|
if not isinstance(notes, list) or (
|
||
|
coeffs != None and not isinstance(coeffs, list) and len(coeffs) != len(notes)
|
||
|
):
|
||
|
raise ValueError("Erreur de paramètres dans moyenne_ponderee_terme_a_terme")
|
||
|
|
||
|
# Récupération des valeurs des paramètres d'entrée
|
||
|
coeffs = [1] * len(notes) if coeffs == None else coeffs
|
||
|
|
||
|
# S'il n'y a pas de notes
|
||
|
if not notes: # Si notes = []
|
||
|
return (None, None)
|
||
|
|
||
|
notesValides = [
|
||
|
(1 if isinstance(note, float) or isinstance(note, int) else 0) for note in notes
|
||
|
] # Liste indiquant les notes valides
|
||
|
if force == True or (
|
||
|
force == False and sum(notesValides) == len(notes)
|
||
|
): # Si on force le calcul de la moyenne ou qu'on ne le force pas et qu'on a le bon nombre de notes
|
||
|
(moyenne, ponderation) = (0.0, 0.0)
|
||
|
for i in range(len(notes)):
|
||
|
if notesValides[i]:
|
||
|
moyenne += coeffs[i] * notes[i]
|
||
|
ponderation += coeffs[i]
|
||
|
return (
|
||
|
(moyenne / (ponderation * 1.0), ponderation)
|
||
|
if ponderation != 0
|
||
|
else (None, 0)
|
||
|
)
|
||
|
else: # Si on ne force pas le calcul de la moyenne
|
||
|
return (None, None)
|
||
|
|
||
|
|
||
|
# -------------------------------------------------------------------------------------------
|
||
|
def conversionDate_StrToDate(date_fin):
|
||
|
""" Conversion d'une date fournie sous la forme d'une chaine de caractère de
|
||
|
type 'jj/mm/aaaa' en un objet date du package datetime.
|
||
|
Fonction servant au tri des semestres par date
|
||
|
"""
|
||
|
(d, m, y) = [int(x) for x in date_fin.split("/")]
|
||
|
date_fin_dst = datetime.date(y, m, d)
|
||
|
return date_fin_dst
|