Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
3 changed files with 89 additions and 54 deletions
Showing only changes of commit 267dbb6460 - Show all commits

View File

@ -9,7 +9,7 @@
from flask import g from flask import g
from app import log from app import log
PE_DEBUG = False PE_DEBUG = True
# On stocke les logs PE dans g.scodoc_pe_log # On stocke les logs PE dans g.scodoc_pe_log
@ -22,14 +22,15 @@ def pe_start_log() -> list[str]:
def pe_print(*a): def pe_print(*a):
"Log (or print in PE_DEBUG mode) and store in g" "Log (or print in PE_DEBUG mode) and store in g"
lines = getattr(g, "scodoc_pe_log")
if lines is None:
lines = pe_start_log()
msg = " ".join(a)
lines.append(msg)
if PE_DEBUG: if PE_DEBUG:
msg = " ".join(a)
print(msg) print(msg)
else: else:
lines = getattr(g, "scodoc_pe_log")
if lines is None:
lines = pe_start_log()
msg = " ".join(a)
lines.append(msg)
log(msg) log(msg)

View File

@ -76,6 +76,7 @@ class SemestreTag(TableTag):
# Les étudiants # Les étudiants
self.etuds = self.nt.etuds self.etuds = self.nt.etuds
self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds} self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds}
self.etudids = list(self.etudiants.keys())
# Les notes, les modules implémentés triés, les étudiants, les coeffs, # Les notes, les modules implémentés triés, les étudiants, les coeffs,
# récupérés notamment de py:mod:`res_but` # récupérés notamment de py:mod:`res_but`
@ -94,12 +95,12 @@ class SemestreTag(TableTag):
tags_personnalises = get_synthese_tags_personnalises_semestre( tags_personnalises = get_synthese_tags_personnalises_semestre(
self.nt.formsemestre self.nt.formsemestre
) )
noms_tags_perso = list(set(tags_personnalises.keys())) noms_tags_perso = sorted(list(set(tags_personnalises.keys())))
## Déduit des compétences ## Déduit des compétences
dict_ues_competences = get_noms_competences_from_ues(self.nt.formsemestre) # dict_ues_competences = get_noms_competences_from_ues(self.nt.formsemestre)
noms_tags_comp = list(set(dict_ues_competences.values())) # noms_tags_comp = list(set(dict_ues_competences.values()))
noms_tags_auto = ["but"] + noms_tags_comp noms_tags_auto = ["but"] # + noms_tags_comp
self.tags = noms_tags_perso + noms_tags_auto self.tags = noms_tags_perso + noms_tags_auto
"""Tags du semestre taggué""" """Tags du semestre taggué"""
@ -122,23 +123,31 @@ class SemestreTag(TableTag):
""" """
raise ScoValueError(message) raise ScoValueError(message)
ues_hors_sport = [ue for ue in self.ues if ue.type != UE_SPORT]
# Calcul des moyennes & les classements de chaque étudiant à chaque tag # Calcul des moyennes & les classements de chaque étudiant à chaque tag
self.moyennes_tags = {} self.moyennes_tags = {}
for tag in tags_personnalises: for tag in tags_personnalises:
# pe_affichage.pe_print(f" -> Traitement du tag {tag}") # pe_affichage.pe_print(f" -> Traitement du tag {tag}")
moy_gen_tag = self.compute_moyenne_tag(tag, tags_personnalises) moy_ues_tag = self.compute_moy_ues_tag(tag, tags_personnalises)
self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_tag) moy_gen_tag = self.compute_moy_gen_tag(moy_ues_tag)
# Ajoute les moyennes générales de BUT pour le semestre considéré self.moyennes_tags[tag] = MoyenneTag(
tag, ues_hors_sport, moy_ues_tag, moy_gen_tag
)
# 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]
moy_gen_but = self.nt.etud_moy_gen moy_gen_but = self.nt.etud_moy_gen
self.moyennes_tags["but"] = MoyenneTag("but", moy_gen_but) self.moyennes_tags["but"] = MoyenneTag("but", ues_hors_sport, df_ues, 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() self.tags_sorted = self.get_all_tags()
"""Tags (personnalisés+compétences) par ordre alphabétique""" """Tags (personnalisés+compétences) par ordre alphabétique"""
@ -156,8 +165,8 @@ class SemestreTag(TableTag):
"""Nom affiché pour le semestre taggué""" """Nom affiché pour le semestre taggué"""
return app.pe.pe_etudiant.nom_semestre_etape(self.formsemestre, avec_fid=True) 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: def compute_moy_ues_tag(self, tag: str, tags_infos: dict) -> pd.Series:
"""Calcule la moyenne des étudiants pour le tag indiqué, """Calcule la moyenne par UE des étudiants pour le tag indiqué,
pour ce SemestreTag, en ayant connaissance des informations sur pour ce SemestreTag, en ayant connaissance des informations sur
les tags (dictionnaire donnant les coeff de repondération) les tags (dictionnaire donnant les coeff de repondération)
@ -199,7 +208,12 @@ class SemestreTag(TableTag):
self.dispense_ues, self.dispense_ues,
block=self.formsemestre.block_moyennes, block=self.formsemestre.block_moyennes,
) )
return moyennes_ues_tag
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.
"""
# Les ects # Les ects
ects = self.ues_inscr_parcours_df.fillna(0.0) * [ ects = self.ues_inscr_parcours_df.fillna(0.0) * [
ue.ects for ue in self.ues if ue.type != UE_SPORT ue.ects for ue in self.ues if ue.type != UE_SPORT
@ -207,7 +221,7 @@ class SemestreTag(TableTag):
# Calcule la moyenne générale dans le semestre (pondérée par le ECTS) # 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_gen_tag = comp.moy_sem.compute_sem_moys_apc_using_ects(
moyennes_ues_tag, moy_ues_tag,
ects, ects,
formation_id=self.formsemestre.formation_id, formation_id=self.formsemestre.formation_id,
skip_empty_ues=True, skip_empty_ues=True,

View File

@ -42,19 +42,27 @@ import numpy as np
from app import ScoValueError from app import ScoValueError
from app.comp.moy_sem import comp_ranks_series from app.comp.moy_sem import comp_ranks_series
from app.models import UniteEns
from app.pe import pe_affichage from app.pe import pe_affichage
from app.pe.pe_affichage import SANS_NOTE from app.pe.pe_affichage import SANS_NOTE
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
import pandas as pd import pandas as pd
from app.scodoc.codes_cursus import UE_SPORT
TAGS_RESERVES = ["but"] TAGS_RESERVES = ["but"]
class MoyenneTag: class MoyenneTag:
def __init__(self, tag: str, notes: pd.Series): def __init__(
self,
tag: str,
ues: list[UniteEns],
notes_ues: pd.DataFrame,
notes_gen: pd.Series,
):
"""Classe centralisant la synthèse des moyennes/classements d'une série """Classe centralisant la synthèse des moyennes/classements d'une série
d'étudiants à un tag donné, en stockant un dictionnaire : d'étudiants à un tag donné, en stockant :
`` ``
{ {
@ -69,16 +77,26 @@ class MoyenneTag:
Args: Args:
tag: Un tag tag: Un tag
note: Une série de notes (moyenne) sous forme d'un pd.Series() ues: La liste des UEs ayant servie au calcul de la moyenne
notes_ues: Les moyennes (etudid x ues) aux différentes UEs et pour le tag
notes_gen: Une série de notes (moyenne) sous forme d'un pd.Series() (toutes UEs confondues)
""" """
self.tag = tag self.tag = tag
"""Le tag associé à la moyenne""" """Le tag associé à la moyenne"""
self.etudids = list(notes.index) # calcul à venir self.etudids = list(notes_gen.index) # calcul à venir
"""Les id des étudiants""" """Les id des étudiants"""
self.inscrits_ids = notes[notes.notnull()].index.to_list() self.ues: list[UniteEns] = ues
"""Les id des étudiants dont la moyenne est non nulle""" """Les UEs sur lesquelles sont calculées les moyennes"""
self.df: pd.DataFrame = self.comp_moy_et_stat(notes) self.df_ues: dict[int, pd.DataFrame] = {}
"""Le dataframe retraçant les moyennes/classements/statistiques""" """Les dataframes retraçant les moyennes/classements/statistiques des étudiants aux UEs"""
for ue in self.ues: # if ue.type != UE_SPORT:
notes = notes_ues[ue.id]
self.df_ues[ue.id] = self.comp_moy_et_stat(notes)
self.inscrits_ids = notes_gen[notes_gen.notnull()].index.to_list()
"""Les id des étudiants dont la moyenne générale est non nulle"""
self.df_gen: pd.DataFrame = self.comp_moy_et_stat(notes_gen)
"""Le dataframe retraçant les moyennes/classements/statistiques général"""
self.synthese = self.to_dict() self.synthese = self.to_dict()
"""La synthèse (dictionnaire) des notes/classements/statistiques""" """La synthèse (dictionnaire) des notes/classements/statistiques"""
@ -88,7 +106,8 @@ class MoyenneTag:
def comp_moy_et_stat(self, notes: pd.Series) -> dict: def comp_moy_et_stat(self, notes: pd.Series) -> dict:
"""Calcule et structure les données nécessaires au PE pour une série """Calcule et structure les données nécessaires au PE pour une série
de notes (souvent une moyenne par tag) dans un dictionnaire spécifique. de notes (pouvant être une moyenne d'un tag à une UE ou une moyenne générale
d'un tag) dans un dictionnaire spécifique.
Partant des notes, sont calculés les classements (en ne tenant compte Partant des notes, sont calculés les classements (en ne tenant compte
que des notes non nulles). que des notes non nulles).
@ -121,64 +140,65 @@ class MoyenneTag:
# Les nb d'étudiants & nb d'inscrits # Les nb d'étudiants & nb d'inscrits
df["nb_etuds"] = len(self.etudids) df["nb_etuds"] = len(self.etudids)
df.loc[self.inscrits_ids, "nb_inscrits"] = len(self.inscrits_ids) # Les étudiants dont la note n'est pas nulle
inscrits_ids = notes[notes.notnull()].index.to_list()
df.loc[inscrits_ids, "nb_inscrits"] = len(inscrits_ids)
# Le classement des inscrits # Le classement des inscrits
notes_non_nulles = notes[self.inscrits_ids] notes_non_nulles = notes[inscrits_ids]
(class_str, class_int) = comp_ranks_series(notes_non_nulles) (class_str, class_int) = comp_ranks_series(notes_non_nulles)
df.loc[self.inscrits_ids, "classement"] = class_int df.loc[inscrits_ids, "classement"] = class_int
# Le rang (classement/nb_inscrit) # Le rang (classement/nb_inscrit)
df["rang"] = df["rang"].astype(str) df["rang"] = df["rang"].astype(str)
df.loc[self.inscrits_ids, "rang"] = ( df.loc[inscrits_ids, "rang"] = (
df.loc[self.inscrits_ids, "classement"].astype(int).astype(str) df.loc[inscrits_ids, "classement"].astype(int).astype(str)
+ "/" + "/"
+ df.loc[self.inscrits_ids, "nb_inscrits"].astype(int).astype(str) + df.loc[inscrits_ids, "nb_inscrits"].astype(int).astype(str)
) )
# Les stat (des inscrits) # Les stat (des inscrits)
df.loc[self.inscrits_ids, "min"] = notes.min() df.loc[inscrits_ids, "min"] = notes.min()
df.loc[self.inscrits_ids, "max"] = notes.max() df.loc[inscrits_ids, "max"] = notes.max()
df.loc[self.inscrits_ids, "moy"] = notes.mean() df.loc[inscrits_ids, "moy"] = notes.mean()
return df return df
def to_dict(self) -> dict: def to_dict(self) -> dict:
"""Renvoie un dictionnaire de synthèse des moyennes/classements/statistiques""" """Renvoie un dictionnaire de synthèse des moyennes/classements/statistiques"""
synthese = { synthese = {
"notes": self.df["note"], "notes": self.df_gen["note"],
"classements": self.df["classement"], "classements": self.df_gen["classement"],
"min": self.df["min"].mean(), "min": self.df_gen["min"].mean(),
"max": self.df["max"].mean(), "max": self.df_gen["max"].mean(),
"moy": self.df["moy"].mean(), "moy": self.df_gen["moy"].mean(),
"nb_inscrits": self.df["nb_inscrits"].mean(), "nb_inscrits": self.df_gen["nb_inscrits"].mean(),
} }
return synthese return synthese
def get_notes(self): def get_notes(self):
"""Série des notes, arrondies à 2 chiffres après la virgule""" """Série des notes, arrondies à 2 chiffres après la virgule"""
return self.df["note"].round(2) return self.df_gen["note"].round(2)
def get_rangs_inscrits(self) -> pd.Series: def get_rangs_inscrits(self) -> pd.Series:
"""Série des rangs classement/nbre_inscrit""" """Série des rangs classement/nbre_inscrit"""
return self.df["rang"] return self.df_gen["rang"]
def get_min(self) -> pd.Series: def get_min(self) -> pd.Series:
"""Série des min""" """Série des min"""
return self.df["min"].round(2) return self.df_gen["min"].round(2)
def get_max(self) -> pd.Series: def get_max(self) -> pd.Series:
"""Série des max""" """Série des max"""
return self.df["max"].round(2) return self.df_gen["max"].round(2)
def get_moy(self) -> pd.Series: def get_moy(self) -> pd.Series:
"""Série des moy""" """Série des moy"""
return self.df["moy"].round(2) return self.df_gen["moy"].round(2)
def get_note_for_df(self, etudid: int): def get_note_for_df(self, etudid: int):
"""Note d'un étudiant donné par son etudid""" """Note d'un étudiant donné par son etudid"""
return round(self.df["note"].loc[etudid], 2) return round(self.df_gen["note"].loc[etudid], 2)
def get_min_for_df(self) -> float: def get_min_for_df(self) -> float:
"""Min renseigné pour affichage dans un df""" """Min renseigné pour affichage dans un df"""
@ -195,7 +215,7 @@ class MoyenneTag:
def get_class_for_df(self, etudid: int) -> str: def get_class_for_df(self, etudid: int) -> str:
"""Classement ramené au nombre d'inscrits, """Classement ramené au nombre d'inscrits,
pour un étudiant donné par son etudid""" pour un étudiant donné par son etudid"""
classement = self.df["rang"].loc[etudid] classement = self.df_gen["rang"].loc[etudid]
if not pd.isna(classement): if not pd.isna(classement):
return classement return classement
else: else: