# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2021 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 d'UE """ import numpy as np import pandas as pd from app import db from app import models from app.models import UniteEns, Module, ModuleImpl, ModuleUECoef from app.comp import moy_mod from app.models.formsemestre import FormSemestre from app.scodoc import sco_codes_parcours def df_load_module_coefs(formation_id: int, semestre_idx: int) -> pd.DataFrame: """Charge les coefs des modules de la formation pour le semestre indiqué. Ces coefs lient les modules à chaque UE. Résultat: (module_coefs_df, ues, modules) DataFrame rows = UEs, columns = modules, value = coef. Considère toutes les UE (sauf sport) et modules du semestre. Les coefs non définis (pas en base) sont mis à zéro. Si semestre_idx None, prend toutes les UE de la formation. """ ues = UniteEns.query.filter_by(formation_id=formation_id).filter( UniteEns.type != sco_codes_parcours.UE_SPORT ) modules = Module.query.filter_by(formation_id=formation_id) if semestre_idx is not None: ues = ues.filter_by(semestre_idx=semestre_idx) modules = modules.filter_by(semestre_id=semestre_idx) ues = ues.all() modules = modules.all() ue_ids = [ue.id for ue in ues] module_ids = [module.id for module in modules] module_coefs_df = pd.DataFrame(columns=module_ids, index=ue_ids, dtype=float) for mod_coef in ( db.session.query(ModuleUECoef) .filter(UniteEns.formation_id == formation_id) .filter(ModuleUECoef.ue_id == UniteEns.id) ): module_coefs_df[mod_coef.module_id][mod_coef.ue_id] = mod_coef.coef module_coefs_df.fillna(value=0, inplace=True) return module_coefs_df, ues, modules def df_load_modimpl_coefs(formsemestre: models.FormSemestre) -> pd.DataFrame: """Charge les coefs des modules du formsemestre indiqué. Comme df_load_module_coefs mais prend seulement les UE et modules du formsemestre. Résultat: (module_coefs_df, ues, modules) DataFrame rows = UEs, columns = modimpl, value = coef. """ ues = formsemestre.query_ues().all() ue_ids = [x.id for x in ues] modimpls = formsemestre.modimpls.all() modimpl_ids = [x.id for x in modimpls] mod2impl = {m.module.id: m.id for m in modimpls} modimpl_coefs_df = pd.DataFrame(columns=modimpl_ids, index=ue_ids, dtype=float) mod_coefs = ( db.session.query(ModuleUECoef) .filter(ModuleUECoef.module_id == ModuleImpl.module_id) .filter(ModuleImpl.formsemestre_id == formsemestre.id) ) for mod_coef in mod_coefs: modimpl_coefs_df[mod2impl[mod_coef.module_id]][mod_coef.ue_id] = mod_coef.coef modimpl_coefs_df.fillna(value=0, inplace=True) return modimpl_coefs_df, ues, modimpls def notes_sem_assemble_cube(modimpls_notes: list[pd.DataFrame]) -> np.ndarray: """Réuni les notes moyennes des modules du semestre en un "cube" modimpls_notes : liste des moyennes de module (DataFrames rendus par compute_module_moy, (etud x UE)) Resultat: ndarray (etud x module x UE) """ modimpls_notes_arr = [df.values for df in modimpls_notes] modimpls_notes = np.stack(modimpls_notes_arr) # passe de (mod x etud x ue) à (etud x mod x UE) return modimpls_notes.swapaxes(0, 1) def notes_sem_load_cube(formsemestre_id): """Calcule le cube des notes du semestre (charge toutes les notes, calcule les moyenne des modules et assemble le cube) Resultat: ndarray (etuds x modimpls x UEs) """ formsemestre = FormSemestre.query.get(formsemestre_id) modimpls_notes = [] for modimpl in formsemestre.modimpls: evals_notes, evaluations = moy_mod.df_load_modimpl_notes(modimpl.id) evals_poids, ues = moy_mod.df_load_evaluations_poids(modimpl.id) etuds_moy_module = moy_mod.compute_module_moy( evals_notes, evals_poids, evaluations ) modimpls_notes.append(etuds_moy_module) return notes_sem_assemble_cube(modimpls_notes) def compute_ue_moys( sem_cube: np.array, etuds: list, modimpls: list, ues: list, module_inscr_df: pd.DataFrame, module_coefs_df: pd.DataFrame, ) -> pd.DataFrame: """Calcul de la moyenne d'UE La moyenne d'UE est un nombre (note/20), ou NI ou NA ou ERR NI non inscrit à (au moins un) module de cette UE NA pas de notes disponibles ERR erreur dans une formule utilisateur. [XXX pas encore gérées ici] sem_cube: notes moyennes aux modules ndarray (etuds x modimpls x UEs) (floats avec des NaN) etuds : lites des étudiants (dim. 0 du cube) modimpls : liste des modules à considérer (dim. 1 du cube) ues : liste des UE (dim. 2 du cube) module_inscr_df: matrice d'inscription du semestre (etud x modimpl) module_coefs_df: matrice coefficients (UE x modimpl) Resultat: DataFrame columns UE, rows etudid """ nb_etuds, nb_modules, nb_ues = sem_cube.shape assert len(etuds) == nb_etuds assert len(modimpls) == nb_modules assert len(ues) == nb_ues assert module_inscr_df.shape[0] == nb_etuds assert module_inscr_df.shape[1] == nb_modules assert module_coefs_df.shape[0] == nb_ues assert module_coefs_df.shape[1] == nb_modules module_inscr = module_inscr_df.values modules_coefs = module_coefs_df.values # # version non vectorisée sur les etuds: etud_moy_ue = np.zeros((nb_etuds, nb_ues)) for i in range(nb_etuds): coefs = module_inscr[i] * modules_coefs etud_moy_ue[i] = (sem_cube[i].transpose() * coefs).sum(axis=1) / coefs.sum( axis=1 ) return pd.DataFrame( etud_moy_ue, index=module_inscr_df.index, columns=module_coefs_df.index )