ScoDoc/app/comp/moy_sem.py
2023-07-11 11:35:54 +02:00

140 lines
5.0 KiB
Python

# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2023 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
#
##############################################################################
"""Fonctions de calcul des moyennes de semestre (indicatives dans le BUT)
"""
import numpy as np
import pandas as pd
from flask import flash, g, url_for
from markupsafe import Markup
from app import db
from app.models.formations import Formation
def compute_sem_moys_apc_using_coefs(
etud_moy_ue_df: pd.DataFrame, modimpl_coefs_df: pd.DataFrame
) -> pd.Series:
"""Calcule les moyennes générales indicatives de tous les étudiants
= moyenne des moyennes d'UE, pondérée par la somme de leurs coefs
etud_moy_ue_df: DataFrame, colonnes ue_id, lignes etudid
modimpl_coefs_df: DataFrame, colonnes moduleimpl_id, lignes UE (sans ue bonus)
Result: panda Series, index etudid, valeur float (moyenne générale)
"""
moy_gen = (etud_moy_ue_df * modimpl_coefs_df.values.sum(axis=1)).sum(
axis=1
) / modimpl_coefs_df.values.sum()
return moy_gen
def compute_sem_moys_apc_using_ects(
etud_moy_ue_df: pd.DataFrame,
ects_df: pd.DataFrame,
formation_id=None,
skip_empty_ues=False,
) -> pd.Series:
"""Calcule les moyennes générales indicatives de tous les étudiants
= moyenne des moyennes d'UE, pondérée par leurs ECTS.
etud_moy_ue_df: DataFrame, colonnes ue_id, lignes etudid
ects: DataFrame, col. ue_id, lignes etudid, valeur float ou None
Si skip_empty_ues: ne compte pas les UE non notées.
Sinon (par défaut), une UE non notée compte comme zéro.
Result: panda Series, index etudid, valeur float (moyenne générale)
"""
try:
if skip_empty_ues:
# annule les coefs des UE sans notes (NaN)
ects = np.where(etud_moy_ue_df.isna(), 0.0, ects_df.to_numpy())
else:
ects = ects_df.to_numpy()
# ects est maintenant un array nb_etuds x nb_ues
moy_gen = (etud_moy_ue_df * ects).sum(axis=1) / ects.sum(axis=1)
except TypeError:
if None in ects:
formation = db.session.get(Formation, formation_id)
flash(
Markup(
f"""Calcul moyenne générale impossible: ECTS des UE manquants !<br>
(formation: <a href="{url_for("notes.ue_table",
scodoc_dept=g.scodoc_dept, formation_id=formation_id)}">{formation.get_titre_version()}</a>)"""
)
)
moy_gen = pd.Series(np.NaN, index=etud_moy_ue_df.index)
else:
raise
return moy_gen
def comp_ranks_series(notes: pd.Series) -> tuple[pd.Series, pd.Series]:
"""Calcul rangs à partir d'une séries ("vecteur") de notes (index etudid, valeur
numérique) en tenant compte des ex-aequos.
Result: couple (tuple)
Series { etudid : rang:str } où rang est une chaine decrivant le rang,
Series { etudid : rang:int } le rang comme un nombre
"""
if (notes is None) or (len(notes) == 0):
return (pd.Series([], dtype=object), pd.Series([], dtype=int))
notes = notes.sort_values(ascending=False) # Serie, tri par ordre décroissant
rangs_str = pd.Series("", index=notes.index, dtype=str) # le rang est une chaîne
# le rang numérique pour tris:
rangs_int = pd.Series(0, index=notes.index, dtype=int)
N = len(notes)
nb_ex = 0 # nb d'ex-aequo consécutifs en cours
notes_i = notes.iat
for i, etudid in enumerate(notes.index):
# test ex-aequo
if i < (N - 1):
next = notes_i[i + 1]
else:
next = None
val = notes_i[i]
if nb_ex:
rangs_int[etudid] = i + 1 - nb_ex
srang = "%d ex" % (i + 1 - nb_ex)
if val == next:
nb_ex += 1
else:
nb_ex = 0
else:
if val == next:
rangs_int[etudid] = i + 1 - nb_ex
srang = "%d ex" % (i + 1 - nb_ex)
nb_ex = 1
else:
rangs_int[etudid] = i + 1
srang = "%d" % (i + 1)
rangs_str[etudid] = srang
assert rangs_int.dtype == int
return rangs_str, rangs_int