ScoDoc/app/pe/pe_semtag.py

325 lines
12 KiB
Python
Raw Normal View History

2020-09-26 16:19:37 +02:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
2023-12-31 23:04:06 +01:00
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
2020-09-26 16:19:37 +02:00
#
# 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
"""
2024-02-08 22:09:11 +01:00
import pandas as pd
import app.pe.pe_etudiant
2024-02-08 22:09:11 +01:00
from app import db, ScoValueError
from app import comp
2024-01-20 16:34:38 +01:00
from app.comp.res_sem import load_formsemestre_results
2024-01-25 17:17:01 +01:00
from app.models import FormSemestre
from app.models.moduleimpls import ModuleImpl
2024-02-08 22:09:11 +01:00
import app.pe.pe_affichage as pe_affichage
from app.pe.pe_tabletags import TableTag, MoyenneTag
from app.scodoc import sco_tag_module
2024-01-20 16:34:38 +01:00
from app.scodoc.codes_cursus import UE_SPORT
2020-09-26 16:19:37 +02:00
class SemestreTag(TableTag):
2024-02-08 22:09:11 +01:00
"""
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):
2024-01-20 16:34:38 +01:00
"""
Args:
formsemestre_id: Identifiant du ``FormSemestre`` sur lequel il se base
2020-09-26 16:19:37 +02:00
"""
TableTag.__init__(self)
2024-01-25 17:17:01 +01:00
# Le semestre
2024-01-20 16:34:38 +01:00
self.formsemestre_id = formsemestre_id
self.formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
2020-09-26 16:19:37 +02:00
# Le nom du semestre taggué
self.nom = self.get_repr()
# Les résultats du semestre
2024-01-20 16:34:38 +01:00
self.nt = load_formsemestre_results(self.formsemestre)
2020-09-26 16:19:37 +02:00
# Les étudiants
2024-01-20 16:34:38 +01:00
self.etuds = self.nt.etuds
self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds}
self.etudids = list(self.etudiants.keys())
2020-09-26 16:19:37 +02:00
# Les notes, les modules implémentés triés, les étudiants, les coeffs,
# récupérés notamment de py:mod:`res_but`
2024-01-20 16:34:38 +01:00
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
2024-01-20 16:34:38 +01:00
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
2024-02-03 10:46:14 +01:00
tags_personnalises = get_synthese_tags_personnalises_semestre(
self.nt.formsemestre
)
noms_tags_perso = sorted(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
2024-02-08 22:09:11 +01:00
self.tags = noms_tags_perso + noms_tags_auto
2024-02-03 10:46:14 +01:00
"""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))
2024-02-08 22:09:11 +01:00
liste_intersection = "\n".join(
[f"<li><code>{tag}</code></li>" 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 <em>{self.formsemestre.titre_annee()}</em>
contient le{s} tag{s} réservé{s} suivant :
<ul>
{liste_intersection}
</ul>
2024-02-08 22:09:11 +01:00
Modifiez votre programme de formation pour le{s} supprimer.
Il{s} ser{'ont' if s else 'a'} automatiquement à vos documents de poursuites d'études.
"""
2024-02-08 22:09:11 +01:00
raise ScoValueError(message)
2024-01-20 16:34:38 +01:00
ues_hors_sport = [ue for ue in self.ues if ue.type != UE_SPORT]
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
2024-01-20 16:34:38 +01:00
self.moyennes_tags = {}
2024-02-03 10:46:14 +01:00
for tag in tags_personnalises:
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
moy_ues_tag = self.compute_moy_ues_tag(tag, tags_personnalises)
moy_gen_tag = self.compute_moy_gen_tag(moy_ues_tag)
self.moyennes_tags[tag] = MoyenneTag(
tag, ues_hors_sport, moy_ues_tag, moy_gen_tag
)
2024-01-20 16:34:38 +01:00
# Ajoute les d'UE 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", [], None, moy_gen_but, )
# Ajoute les moyennes par UEs (et donc par compétence) + la moyenne générale (but)
df_ues = pd.DataFrame({ue.id: self.nt.etud_moy_ue[ue.id] for ue in ues_hors_sport},
index = self.etudids)
# moy_ues = self.nt.etud_moy_ue[ue_id]
2024-01-20 16:34:38 +01:00
moy_gen_but = self.nt.etud_moy_gen
self.moyennes_tags["but"] = MoyenneTag("but", ues_hors_sport, df_ues, moy_gen_but)
2024-02-03 10:46:14 +01:00
self.tags_sorted = self.get_all_tags()
"""Tags (personnalisés+compétences) par ordre alphabétique"""
2020-09-26 16:19:37 +02:00
# Synthétise l'ensemble des moyennes dans un dataframe
2024-02-03 10:46:14 +01:00
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)
2020-09-26 16:19:37 +02:00
def compute_moy_ues_tag(self, tag: str, tags_infos: dict) -> pd.Series:
"""Calcule la moyenne par UE des étudiants pour le tag indiqué,
2024-02-03 10:46:14 +01:00
pour ce SemestreTag, en ayant connaissance des informations sur
les tags (dictionnaire donnant les coeff de repondération)
2020-09-26 16:19:37 +02:00
2024-01-20 16:34:38 +01:00
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.
2020-09-26 16:19:37 +02:00
Force ou non le calcul de la moyenne lorsque des notes sont manquantes.
2024-02-03 10:46:14 +01:00
Returns:
La série des moyennes
2020-09-26 16:19:37 +02:00
"""
2024-02-08 22:09:11 +01:00
# Adaptation du mask de calcul des moyennes au tag visé
2024-01-20 16:34:38 +01:00
modimpls_mask = [
modimpl.module.ue.type != UE_SPORT
for modimpl in self.formsemestre.modimpls_sorted
]
2024-02-08 22:09:11 +01:00
# Désactive tous les modules qui ne sont pas pris en compte pour ce tag
2024-01-20 16:34:38 +01:00
for i, modimpl in enumerate(self.formsemestre.modimpls_sorted):
2024-02-03 10:46:14 +01:00
if modimpl.moduleimpl_id not in tags_infos[tag]:
2024-01-20 16:34:38 +01:00
modimpls_mask[i] = False
2024-02-08 22:09:11 +01:00
# Applique la pondération des coefficients
2024-01-20 16:34:38 +01:00
modimpl_coefs_ponderes_df = self.modimpl_coefs_df.copy()
2024-02-03 10:46:14 +01:00
for modimpl_id in tags_infos[tag]:
ponderation = tags_infos[tag][modimpl_id]["ponderation"]
2024-01-20 16:34:38 +01:00
modimpl_coefs_ponderes_df[modimpl_id] *= ponderation
2024-02-08 22:09:11 +01:00
# Calcule les moyennes pour le tag visé dans chaque UE (dataframe etudid x ues)#
moyennes_ues_tag = comp.moy_ue.compute_ue_moys_apc(
2024-01-20 16:34:38 +01:00
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,
)
return moyennes_ues_tag
2024-01-20 16:34:38 +01:00
def compute_moy_gen_tag(self, moy_ues_tag: pd.DataFrame) -> pd.Series:
"""Calcule la moyenne générale (toutes UE confondus)
pour le tag considéré, en les pondérant par les crédits ECTS.
"""
2024-02-08 22:09:11 +01:00
# Les ects
2024-01-20 16:34:38 +01:00
ects = self.ues_inscr_parcours_df.fillna(0.0) * [
ue.ects for ue in self.ues if ue.type != UE_SPORT
]
2020-09-26 16:19:37 +02:00
2024-02-08 22:09:11 +01:00
# 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(
moy_ues_tag,
2024-01-20 16:34:38 +01:00
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
2020-09-26 16:19:37 +02:00
2024-01-16 15:51:22 +01:00
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"]
2024-01-20 16:34:38 +01:00
def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
2024-01-20 16:34:38 +01:00
"""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)).
2024-01-20 16:34:38 +01:00
Args:
formsemestre: Le formsemestre à la base de la recherche des tags
Return:
Un dictionnaire de tags
2024-01-20 16:34:38 +01:00
"""
synthese_tags = {}
2024-02-08 22:09:11 +01:00
# Instance des modules du semestre
2024-01-20 16:34:38 +01:00
modimpls = formsemestre.modimpls_sorted
for modimpl in modimpls:
modimpl_id = modimpl.id
2024-02-08 22:09:11 +01:00
# Liste des tags pour le module concerné
2024-01-20 16:34:38 +01:00
tags = sco_tag_module.module_tag_list(modimpl.module.id)
2024-02-08 22:09:11 +01:00
# Traitement des tags recensés, chacun pouvant étant de la forme
# "mathématiques", "théorie", "pe:0", "maths:2"
2024-01-20 16:34:38 +01:00
for tag in tags:
2024-02-08 22:09:11 +01:00
# Extraction du nom du tag et du coeff de pondération
2024-01-20 16:34:38 +01:00
(tagname, ponderation) = sco_tag_module.split_tagname_coeff(tag)
2024-02-08 22:09:11 +01:00
# Ajout d'une clé pour le tag
2024-01-20 16:34:38 +01:00
if tagname not in synthese_tags:
synthese_tags[tagname] = {}
2024-02-08 22:09:11 +01:00
# Ajout du module (modimpl) au tagname considéré
2024-01-20 16:34:38 +01:00
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.
2024-02-08 22:09:11 +01:00
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:
2024-02-08 22:09:11 +01:00
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