# -*- 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 Fri Sep 9 09:15:05 2016 @author: barasc """ import pandas as pd import app.pe.pe_etudiant from app import db, ScoValueError from app import comp from app.comp.res_sem import load_formsemestre_results from app.models import FormSemestre from app.models.moduleimpls import ModuleImpl import app.pe.pe_affichage as pe_affichage from app.pe.pe_tabletags import TableTag, MoyenneTag from app.scodoc import sco_tag_module from app.scodoc.codes_cursus import UE_SPORT class SemestreTag(TableTag): """ Un SemestreTag représente les résultats des étudiants à un semestre, en donnant accès aux moyennes par tag. Il s'appuie principalement sur FormSemestre et sur ResultatsSemestreBUT. """ def __init__(self, formsemestre_id: int): """ Args: formsemestre_id: Identifiant du ``FormSemestre`` sur lequel il se base """ TableTag.__init__(self) # Le semestre self.formsemestre_id = formsemestre_id self.formsemestre = FormSemestre.get_formsemestre(formsemestre_id) # Le nom du semestre taggué self.nom = self.get_repr() # Les résultats du semestre self.nt = load_formsemestre_results(self.formsemestre) # Les étudiants self.etuds = self.nt.etuds self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds} # Les notes, les modules implémentés triés, les étudiants, les coeffs, # récupérés notamment de py:mod:`res_but` self.sem_cube = self.nt.sem_cube self.modimpls_sorted = self.nt.formsemestre.modimpls_sorted self.modimpl_coefs_df = self.nt.modimpl_coefs_df # Les inscriptions au module et les dispenses d'UE self.modimpl_inscr_df = self.nt.modimpl_inscr_df self.ues = self.nt.ues self.ues_inscr_parcours_df = self.nt.load_ues_inscr_parcours() self.dispense_ues = self.nt.dispense_ues # Les tags : ## Saisis par l'utilisateur tags_personnalises = get_synthese_tags_personnalises_semestre( self.nt.formsemestre ) noms_tags_perso = list(set(tags_personnalises.keys())) ## Déduit des compétences dict_ues_competences = get_noms_competences_from_ues(self.nt.formsemestre) noms_tags_comp = list(set(dict_ues_competences.values())) noms_tags_auto = ["but"] + noms_tags_comp self.tags = noms_tags_perso + noms_tags_auto """Tags du semestre taggué""" ## Vérifie l'unicité des tags if len(set(self.tags)) != len(self.tags): intersection = list(set(noms_tags_perso) & set(noms_tags_auto)) liste_intersection = "\n".join( [f"
  • {tag}
  • " for tag in intersection] ) s = "s" if len(intersection) > 0 else "" message = f"""Erreur dans le module PE : Un des tags saisis dans votre programme de formation fait parti des tags réservés. En particulier, votre semestre {self.formsemestre.titre_annee()} contient le{s} tag{s} réservé{s} suivant : Modifiez votre programme de formation pour le{s} supprimer. Il{s} ser{'ont' if s else 'a'} automatiquement à vos documents de poursuites d'études. """ raise ScoValueError(message) # Calcul des moyennes & les classements de chaque étudiant à chaque tag self.moyennes_tags = {} for tag in tags_personnalises: # pe_affichage.pe_print(f" -> Traitement du tag {tag}") moy_gen_tag = self.compute_moyenne_tag(tag, tags_personnalises) self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_tag) # Ajoute les moyennes générales de BUT pour le semestre considéré moy_gen_but = self.nt.etud_moy_gen self.moyennes_tags["but"] = MoyenneTag("but", moy_gen_but) # Ajoute les moyennes par compétence for ue_id, competence in dict_ues_competences.items(): if competence not in self.moyennes_tags: moy_ue = self.nt.etud_moy_ue[ue_id] self.moyennes_tags[competence] = MoyenneTag(competence, moy_ue) self.tags_sorted = self.get_all_tags() """Tags (personnalisés+compétences) par ordre alphabétique""" # Synthétise l'ensemble des moyennes dans un dataframe self.notes = self.df_notes() """Dataframe synthétique des notes par tag""" pe_affichage.pe_print( f" => Traitement des tags {', '.join(self.tags_sorted)}" ) def get_repr(self): """Nom affiché pour le semestre taggué""" return app.pe.pe_etudiant.nom_semestre_etape(self.formsemestre, avec_fid=True) def compute_moyenne_tag(self, tag: str, tags_infos: dict) -> pd.Series: """Calcule la moyenne des étudiants pour le tag indiqué, pour ce SemestreTag, en ayant connaissance des informations sur les tags (dictionnaire donnant les coeff de repondération) Sont pris en compte les modules implémentés associés au tag, avec leur éventuel coefficient de **repondération**, en utilisant les notes chargées pour ce SemestreTag. Force ou non le calcul de la moyenne lorsque des notes sont manquantes. Returns: La série des moyennes """ # Adaptation du mask de calcul des moyennes au tag visé modimpls_mask = [ modimpl.module.ue.type != UE_SPORT for modimpl in self.formsemestre.modimpls_sorted ] # Désactive tous les modules qui ne sont pas pris en compte pour ce tag for i, modimpl in enumerate(self.formsemestre.modimpls_sorted): if modimpl.moduleimpl_id not in tags_infos[tag]: modimpls_mask[i] = False # Applique la pondération des coefficients modimpl_coefs_ponderes_df = self.modimpl_coefs_df.copy() for modimpl_id in tags_infos[tag]: ponderation = tags_infos[tag][modimpl_id]["ponderation"] modimpl_coefs_ponderes_df[modimpl_id] *= ponderation # Calcule les moyennes pour le tag visé dans chaque UE (dataframe etudid x ues)# moyennes_ues_tag = comp.moy_ue.compute_ue_moys_apc( self.sem_cube, self.etuds, self.formsemestre.modimpls_sorted, self.modimpl_inscr_df, modimpl_coefs_ponderes_df, modimpls_mask, self.dispense_ues, block=self.formsemestre.block_moyennes, ) # Les ects ects = self.ues_inscr_parcours_df.fillna(0.0) * [ ue.ects for ue in self.ues if ue.type != UE_SPORT ] # Calcule la moyenne générale dans le semestre (pondérée par le ECTS) moy_gen_tag = comp.moy_sem.compute_sem_moys_apc_using_ects( moyennes_ues_tag, ects, formation_id=self.formsemestre.formation_id, skip_empty_ues=True, ) return moy_gen_tag def get_moduleimpl(modimpl_id) -> dict: """Renvoie l'objet modimpl dont l'id est modimpl_id""" modimpl = db.session.get(ModuleImpl, modimpl_id) if modimpl: return modimpl return None def get_moy_ue_from_nt(nt, etudid, modimpl_id) -> float: """Renvoie la moyenne de l'UE d'un etudid dans laquelle se trouve le module de modimpl_id """ # ré-écrit modimpl = get_moduleimpl(modimpl_id) # le module ue_status = nt.get_etud_ue_status(etudid, modimpl.module.ue.id) if ue_status is None: return None return ue_status["moy"] def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre): """Etant données les implémentations des modules du semestre (modimpls), synthétise les tags renseignés dans le programme pédagogique & associés aux modules du semestre, en les associant aux modimpls qui les concernent (modimpl_id) et aucoeff et pondération fournie avec le tag (par défaut 1 si non indiquée)). Args: formsemestre: Le formsemestre à la base de la recherche des tags Return: Un dictionnaire de tags """ synthese_tags = {} # Instance des modules du semestre modimpls = formsemestre.modimpls_sorted for modimpl in modimpls: modimpl_id = modimpl.id # Liste des tags pour le module concerné tags = sco_tag_module.module_tag_list(modimpl.module.id) # Traitement des tags recensés, chacun pouvant étant de la forme # "mathématiques", "théorie", "pe:0", "maths:2" for tag in tags: # Extraction du nom du tag et du coeff de pondération (tagname, ponderation) = sco_tag_module.split_tagname_coeff(tag) # Ajout d'une clé pour le tag if tagname not in synthese_tags: synthese_tags[tagname] = {} # Ajout du module (modimpl) au tagname considéré synthese_tags[tagname][modimpl_id] = { "modimpl": modimpl, # les données sur le module # "coeff": modimpl.module.coefficient, # le coeff du module dans le semestre "ponderation": ponderation, # la pondération demandée pour le tag sur le module # "module_code": modimpl.module.code, # le code qui doit se retrouver à l'identique dans des ue capitalisee # "ue_id": modimpl.module.ue.id, # les données sur l'ue # "ue_code": modimpl.module.ue.ue_code, # "ue_acronyme": modimpl.module.ue.acronyme, } return synthese_tags def get_noms_competences_from_ues(formsemestre: FormSemestre) -> dict[int, str]: """Partant d'un formsemestre, extrait le nom des compétences associés à (ou aux) parcours des étudiants du formsemestre. Ignore les UEs non associées à un niveau de compétence. Args: formsemestre: Un FormSemestre Returns: Dictionnaire {ue_id: nom_competence} lisant tous les noms des compétences en les raccrochant à leur ue """ # Les résultats du semestre nt = load_formsemestre_results(formsemestre) noms_competences = {} for ue in nt.ues: if ue.niveau_competence and ue.type != UE_SPORT: # ?? inutilisé ordre = ue.niveau_competence.ordre nom = ue.niveau_competence.competence.titre noms_competences[ue.ue_id] = f"comp. {nom}" return noms_competences