diff --git a/app/comp/moy_sem.py b/app/comp/moy_sem.py index 3701f3dd0..20d9752ce 100644 --- a/app/comp/moy_sem.py +++ b/app/comp/moy_sem.py @@ -100,7 +100,7 @@ def compute_sem_moys_apc_using_ects( 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 + """Calcul rangs à partir d'une série ("vecteur") de notes (index etudid, valeur numérique) en tenant compte des ex-aequos. Result: couple (tuple) diff --git a/app/comp/res_but.py b/app/comp/res_but.py index 880f1b8ed..c6d99fb95 100644 --- a/app/comp/res_but.py +++ b/app/comp/res_but.py @@ -273,7 +273,7 @@ class ResultatsSemestreBUT(NotesTableCompat): return s.index[s.notna()] def etud_parcours_ues_ids(self, etudid: int) -> set[int]: - """Ensemble les id des UEs que l'étudiant doit valider dans ce semestre compte tenu + """Ensemble des id des UEs que l'étudiant doit valider dans ce semestre compte tenu du parcours dans lequel il est inscrit. Se base sur le parcours dans ce semestre, et le référentiel de compétences. Note: il n'est pas nécessairement inscrit à toutes ces UEs. diff --git a/app/pe/pe_etudiant.py b/app/pe/pe_etudiant.py index c459752f2..f4fc3fff5 100644 --- a/app/pe/pe_etudiant.py +++ b/app/pe/pe_etudiant.py @@ -85,16 +85,12 @@ class EtudiantsJuryPE: self.abandons_ids = {} """Les etudids des étudiants redoublants/réorientés""" - def find_etudiants(self, formation_id: int): + def find_etudiants(self): """Liste des étudiants à prendre en compte dans le jury PE, en les recherchant - de manière automatique par rapport à leur année de diplomation ``annee_diplome`` - dans la formation ``formation_id``. XXX TODO voir si on garde formation_id qui n'est pas utilisé ici + de manière automatique par rapport à leur année de diplomation ``annee_diplome``. Les données obtenues sont stockées dans les attributs de EtudiantsJuryPE. - - formation_id: L'identifiant de la formation (inutilisé) - *Remarque* : ex: JuryPE.get_etudiants_in_jury() """ cosemestres = pe_comp.get_cosemestres_diplomants(self.annee_diplome, None) diff --git a/app/pe/pe_interclasstag.py b/app/pe/pe_interclasstag.py index ffb2a03a4..b4da44cf1 100644 --- a/app/pe/pe_interclasstag.py +++ b/app/pe/pe_interclasstag.py @@ -59,8 +59,9 @@ class AggregatInterclasseTag(TableTag): for etudid in self.diplomes_ids: self.suivi[etudid] = trajectoires_jury_pe.suivi[etudid][nom_aggregat] - """Les tags""" + self.tags_sorted = self.do_taglist() + """Liste des tags (triés par ordre alphabétique)""" # Construit la matrice de notes self.notes = self.compute_notes_matrice() @@ -69,15 +70,7 @@ class AggregatInterclasseTag(TableTag): self.moyennes_tags = {} for tag in self.tags_sorted: moy_gen_tag = self.notes[tag] - class_gen_tag = moy_sem.comp_ranks_series(moy_gen_tag)[1] # en int - self.moyennes_tags[tag] = { - "notes": moy_gen_tag, - "classements": class_gen_tag, - "min": moy_gen_tag.min(), - "max": moy_gen_tag.max(), - "moy": moy_gen_tag.mean(), - "nb_inscrits": len(moy_gen_tag), - } + self.moyennes_tags[tag] = self.comp_moy_et_stat(moy_gen_tag) # Est significatif ? (aka a-t-il des tags et des notes) self.significatif = len(self.tags_sorted) > 0 @@ -125,4 +118,7 @@ class AggregatInterclasseTag(TableTag): etudids_communs, tags_communs ] + # Force les nan + df.fillna(np.nan) + return df diff --git a/app/pe/pe_jury.py b/app/pe/pe_jury.py index 7c45d4c87..55752ff4a 100644 --- a/app/pe/pe_jury.py +++ b/app/pe/pe_jury.py @@ -83,7 +83,7 @@ class JuryPE(object): # leur affichage dans les avis latex # ------------------------------------------------------------------------------------------------------------------ - def __init__(self, diplome, formation_id): + def __init__(self, diplome): """ Création d'une table PE sur la base d'un semestre selectionné. De ce semestre est déduit : 1. l'année d'obtention du DUT, @@ -98,19 +98,16 @@ class JuryPE(object): self.diplome = diplome "L'année du diplome" - self.formation_id = formation_id - "La formation associée au diplome" - self.nom_export_zip = f"Jury_PE_{self.diplome}" "Nom du zip où ranger les fichiers générés" # Chargement des étudiants à prendre en compte dans le jury pe_affichage.pe_print( f"""*** Recherche et chargement des étudiants diplômés en { - self.diplome} pour la formation {self.formation_id}""" + self.diplome}""" ) self.etudiants = EtudiantsJuryPE(self.diplome) # Les infos sur les étudiants - self.etudiants.find_etudiants(self.formation_id) + self.etudiants.find_etudiants() self.diplomes_ids = self.etudiants.diplomes_ids self.zipdata = io.BytesIO() @@ -610,7 +607,7 @@ def get_dict_synthese_aggregat( note = TableTag.get_note_for_df(bilan, etudid) # Statistiques sur le groupe - if note != np.nan: + if not pd.isna(note) and note != np.nan: # Les moyennes de cette trajectoire donnees |= { (descr, "", "note"): note, @@ -640,7 +637,7 @@ def get_dict_synthese_aggregat( if tag in interclassement_taggue.moyennes_tags: bilan = interclassement_taggue.moyennes_tags[tag] - if note != np.nan: + if not pd.isna(note) and note != np.nan: nom_stat_promo = f"{NOM_STAT_PROMO} {diplome}" donnees |= { diff --git a/app/pe/pe_semtag.py b/app/pe/pe_semtag.py index 0b8a62385..165a49b04 100644 --- a/app/pe/pe_semtag.py +++ b/app/pe/pe_semtag.py @@ -35,9 +35,12 @@ Created on Fri Sep 9 09:15:05 2016 @author: barasc """ +import numpy as np + import app.pe.pe_etudiant from app import db, log from app.comp import res_sem, moy_ue, moy_sem +from app.comp.moy_sem import comp_ranks_series from app.comp.res_compat import NotesTableCompat from app.comp.res_sem import load_formsemestre_results from app.models import FormSemestre @@ -49,8 +52,8 @@ import app.pe.pe_affichage as pe_affichage from app.pe.pe_tabletags import TableTag, TAGS_RESERVES import pandas as pd -class SemestreTag(TableTag): +class SemestreTag(TableTag): def __init__(self, formsemestre_id: int): """ Un SemestreTag représente les résultats des étudiants à un semestre, en donnant @@ -89,47 +92,50 @@ class SemestreTag(TableTag): self.ues_inscr_parcours_df = self.nt.load_ues_inscr_parcours() self.dispense_ues = self.nt.dispense_ues - # Les tags (en supprimant les tags réservés) - self.tags = get_synthese_tags_semestre(self.nt.formsemestre) - for tag in TAGS_RESERVES: - if tag in self.tags: - del self.tags[tag] + # Les tags : + ## Saisis par l'utilisateur + self.tags_personnalises = get_synthese_tags_personnalises_semestre( + self.nt.formsemestre + ) + ## Déduit des compétences + self.tags_competences = get_noms_competences_from_ues(self.nt.formsemestre) + + # Supprime les doublons dans les tags + tags_reserves = TAGS_RESERVES + list(self.tags_competences.values()) + for tag in self.tags_personnalises: + if tag in tags_reserves: + del self.tags_personnalises[tag] + pe_affichage.pe_print(f"Supprime le tag {tag}") # Calcul des moyennes & les classements de chaque étudiant à chaque tag self.moyennes_tags = {} - for tag in self.tags: + for tag in self.tags_personnalises: # pe_affichage.pe_print(f" -> Traitement du tag {tag}") moy_gen_tag = self.compute_moyenne_tag(tag) - class_gen_tag = moy_sem.comp_ranks_series(moy_gen_tag)[1] # en int - self.moyennes_tags[tag] = { - "notes": moy_gen_tag, - "classements": class_gen_tag, - "min": moy_gen_tag.min(), - "max": moy_gen_tag.max(), - "moy": moy_gen_tag.mean(), - "nb_inscrits": len(moy_gen_tag), - } + self.moyennes_tags[tag] = self.comp_moy_et_stat(moy_gen_tag) # Ajoute les moyennes générales de BUT pour le semestre considéré moy_gen_but = self.nt.etud_moy_gen - class_gen_but = self.nt.etud_moy_gen_ranks_int - self.moyennes_tags["but"] = { - "notes": moy_gen_but, - "classements": class_gen_but, - "min": moy_gen_but.min(), - "max": moy_gen_but.max(), - "moy": moy_gen_but.mean(), - "nb_inscrits": len(moy_gen_but), - } + moy_gen_but = pd.to_numeric(moy_gen_but, errors="coerce") + self.moyennes_tags["but"] = self.comp_moy_et_stat(moy_gen_but) + + # Ajoute les moyennes par compétence + for ue_id, competence in self.tags_competences.items(): + moy_ue = self.nt.etud_moy_ue[ue_id] + self.moyennes_tags[competence] = self.comp_moy_et_stat(moy_ue) # Synthétise l'ensemble des moyennes dans un dataframe - self.tags_sorted = sorted(self.moyennes_tags) # les tags par ordre alphabétique + self.tags_sorted = sorted( + self.moyennes_tags + ) # les tags (personnalisés+compétences) par ordre alphabétique self.notes = ( self.df_notes() ) # Le dataframe synthétique des notes (=moyennes par tag) - pe_affichage.pe_print(f" => Traitement des tags {', '.join(self.tags_sorted)}") + pe_affichage.pe_print( + f" => Traitement des tags {', '.join(self.tags_sorted)}" + ) def get_repr(self): """Nom affiché pour le semestre taggué""" @@ -157,13 +163,13 @@ class SemestreTag(TableTag): """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 self.tags[tag]: + if modimpl.moduleimpl_id not in self.tags_personnalises[tag]: modimpls_mask[i] = False """Applique la pondération des coefficients""" modimpl_coefs_ponderes_df = self.modimpl_coefs_df.copy() - for modimpl_id in self.tags[tag]: - ponderation = self.tags[tag][modimpl_id]["ponderation"] + for modimpl_id in self.tags_personnalises[tag]: + ponderation = self.tags_personnalises[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)""" @@ -193,95 +199,6 @@ class SemestreTag(TableTag): return moy_gen_tag - def get_noteEtCoeff_modimpl(self, modimpl_id, etudid, profondeur=2): - """Renvoie un couple donnant la note et le coeff normalisé d'un étudiant à un module d'id modimpl_id. - La note et le coeff sont extraits : - 1) soit des données du semestre en normalisant le coefficient par rapport à la somme des coefficients des modules du semestre, - 2) soit des données des UE précédemment capitalisées, en recherchant un module de même CODE que le modimpl_id proposé, - le coefficient normalisé l'étant alors par rapport au total des coefficients du semestre auquel appartient l'ue capitalisée - - TODO:: A rependre si nécessaire - """ - - def get_ue_capitalisees(etudid) -> list[dict]: - """Renvoie la liste des capitalisation effectivement capitalisées par un étudiant""" - if etudid in self.nt.validations.ue_capitalisees.index: - return self.nt.validations.ue_capitalisees.loc[[etudid]].to_dict("records") - return [] - - (note, coeff_norm) = (None, None) - - modimpl = get_moduleimpl(modimpl_id) # Le module considéré - if modimpl == None or profondeur < 0: - return (None, None) - - # Y-a-t-il eu capitalisation d'UE ? - ue_capitalisees = get_ue_capitalisees( - etudid - ) # les ue capitalisées des étudiants - ue_capitalisees_id = { - ue_cap["ue_id"] for ue_cap in ue_capitalisees - } # les id des ue capitalisées - - # Si le module ne fait pas partie des UE capitalisées - if modimpl.module.ue.id not in ue_capitalisees_id: - note = self.nt.get_etud_mod_moy(modimpl_id, etudid) # lecture de la note - coeff = modimpl.module.coefficient or 0.0 # le coeff (! non compatible BUT) - coeff_norm = ( - coeff / self.somme_coeffs if self.somme_coeffs != 0 else 0 - ) # le coeff normalisé - - # Si le module fait partie d'une UE capitalisée - elif len(ue_capitalisees) > 0: - moy_ue_actuelle = get_moy_ue_from_nt( - self.nt, etudid, modimpl_id - ) # la moyenne actuelle - # A quel semestre correspond l'ue capitalisée et quelles sont ses notes ? - fids_prec = [ - ue_cap["formsemestre_id"] - for ue_cap in ue_capitalisees - if ue_cap["ue_code"] == modimpl.module.ue.ue_code - ] # and ue['semestre_id'] == semestre_id] - if len(fids_prec) > 0: - # => le formsemestre_id du semestre dont vient la capitalisation - fid_prec = fids_prec[0] - # Lecture des notes de ce semestre - # le tableau de note du semestre considéré: - formsemestre_prec = FormSemestre.get_formsemestre(fid_prec) - nt_prec: NotesTableCompat = res_sem.load_formsemestre_results( - formsemestre_prec - ) - - # Y-a-t-il un module équivalent c'est à dire correspondant au même code (le module_id n'étant pas significatif en cas de changement de PPN) - - modimpl_prec = [ - modi - for modi in nt_prec.formsemestre.modimpls_sorted - if modi.module.code == modimpl.module.code - ] - if len(modimpl_prec) > 0: # si une correspondance est trouvée - modprec_id = modimpl_prec[0].id - moy_ue_capitalisee = get_moy_ue_from_nt(nt_prec, etudid, modprec_id) - if ( - moy_ue_capitalisee is None - ) or moy_ue_actuelle >= moy_ue_capitalisee: # on prend la meilleure ue - note = self.nt.get_etud_mod_moy( - modimpl_id, etudid - ) # lecture de la note - coeff = modimpl.module.coefficient # le coeff - # nota: self.somme_coeffs peut être None - coeff_norm = ( - coeff / self.somme_coeffs if self.somme_coeffs else 0 - ) # le coeff normalisé - else: - semtag_prec = SemestreTag(nt_prec, nt_prec.sem) - (note, coeff_norm) = semtag_prec.get_noteEtCoeff_modimpl( - modprec_id, etudid, profondeur=profondeur - 1 - ) # lecture de la note via le semtag associé au modimpl capitalisé - - # Sinon - pas de notes à prendre en compte - return (note, coeff_norm) - def get_moduleimpl(modimpl_id) -> dict: """Renvoie l'objet modimpl dont l'id est modimpl_id""" @@ -308,21 +225,13 @@ def get_moy_ue_from_nt(nt, etudid, modimpl_id) -> float: return ue_status["moy"] -# ----------------------------------------------------------------------------- -def get_synthese_tags_semestre(formsemestre: FormSemestre): +def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre): """Etant données les implémentations des modules du semestre (modimpls), - synthétise les tags les concernant (tags saisis dans le programme pédagogique) + 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)). - { tagname1: { modimpl_id1: { 'module_id': ..., - 'coeff': ..., - 'coeff_norm': ..., - 'ponderation': ..., - 'module_code': ..., - 'ue_xxx': ...}, - } - } Args: formsemestre: Le formsemestre à la base de la recherche des tags @@ -364,3 +273,26 @@ def get_synthese_tags_semestre(formsemestre: FormSemestre): } 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. + + 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.type != UE_SPORT: + ordre = ue.niveau_competence.ordre + nom = ue.niveau_competence.competence.titre + noms_competences[ue.ue_id] = f"comp. {nom}" + return noms_competences diff --git a/app/pe/pe_tabletags.py b/app/pe/pe_tabletags.py index 9df18c013..610e5693a 100644 --- a/app/pe/pe_tabletags.py +++ b/app/pe/pe_tabletags.py @@ -40,6 +40,8 @@ Created on Thu Sep 8 09:36:33 2016 import datetime import numpy as np +from app.comp.moy_sem import comp_ranks_series +from app.pe import pe_affichage from app.scodoc import sco_utils as scu import pandas as pd @@ -104,6 +106,44 @@ class TableTag(object): return df.to_csv(sep=";") + def comp_moy_et_stat(self, notes: pd.Series) -> dict: + """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. + + Partant des notes, sont calculés les classements (en ne tenant compte + que des notes non nulles). + + Args: + notes: Une série de notes (avec des éventuels NaN) + + Returns: + Un dictionnaire stockant les notes, les classements, le min, + le max, la moyenne, le nb de notes (donc d'inscrits) + """ + # Supprime d'éventuels chaines de caractères dans les notes + notes = pd.to_numeric(notes, errors="coerce") + + # Les indices des ... et les notes non nulles/pertinentes + indices = notes.notnull() + notes_non_nulles = notes[indices] + + # Les classements sur les notes non nulles + (_, class_gen_ue_non_nul) = comp_ranks_series(notes_non_nulles) + + # Les classements (toutes notes confondues, avec NaN si pas de notes) + class_gen_ue = pd.Series(np.nan, index=notes.index, dtype="Int64") + class_gen_ue[indices] = class_gen_ue_non_nul[indices] + + synthese = { + "notes": notes, + "classements": class_gen_ue, + "min": notes.min(), + "max": notes.max(), + "moy": notes.mean(), + "nb_inscrits": len(indices), + } + return synthese + @classmethod def get_min_for_df(cls, bilan: dict) -> float: """Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné, @@ -127,12 +167,15 @@ class TableTag(object): """Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné, renvoie le classement ramené au nombre d'inscrits, pour un étudiant donné par son etudid""" - return f"{bilan['classements'].loc[etudid]}/{bilan['nb_inscrits']}" - + classement = bilan['classements'].loc[etudid] + if not pd.isna(classement): + return f"{classement}/{bilan['nb_inscrits']}" + else: + return pe_affichage.SANS_NOTE @classmethod def get_note_for_df(cls, bilan: dict, etudid: int): """Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné, renvoie la note (moyenne) pour un étudiant donné par son etudid""" - return round(bilan["notes"].loc[etudid], 2) \ No newline at end of file + return round(bilan["notes"].loc[etudid], 2) diff --git a/app/pe/pe_trajectoiretag.py b/app/pe/pe_trajectoiretag.py index 7fff55f5c..3b42d1586 100644 --- a/app/pe/pe_trajectoiretag.py +++ b/app/pe/pe_trajectoiretag.py @@ -47,11 +47,8 @@ from app.pe.pe_tabletags import TableTag class TrajectoireTag(TableTag): - def __init__( - self, - trajectoire: Trajectoire, - semestres_taggues: dict[int, SemestreTag] + self, trajectoire: Trajectoire, semestres_taggues: dict[int, SemestreTag] ): """Calcule les moyennes par tag d'une combinaison de semestres (trajectoires), identifiée par un nom d'aggrégat (par ex: '3S') et @@ -71,11 +68,13 @@ class TrajectoireTag(TableTag): # Le nom de la trajectoire tagguée (identique à la trajectoire) self.nom = self.get_repr() - """Le formsemestre terminal et les semestres aggrégés""" self.formsemestre_terminal = trajectoire.semestre_final + """Le formsemestre terminal""" + # Les résultats du formsemestre terminal nt = load_formsemestre_results(self.formsemestre_terminal) self.semestres_aggreges = trajectoire.semestres_aggreges + """Les semestres aggrégés""" self.semestres_tags_aggreges = {} """Les semestres tags associés aux semestres aggrégés""" @@ -87,32 +86,25 @@ class TrajectoireTag(TableTag): """Les étudiants (état civil + cursus connu)""" self.etuds = nt.etuds + # assert self.etuds == trajectoire.suivi # manque-t-il des étudiants ? self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds} - """Les tags extraits de tous les semestres""" self.tags_sorted = self.do_taglist() + """Tags extraits de tous les semestres""" - """Construit le cube de notes""" self.notes_cube = self.compute_notes_cube() + """Cube de notes""" - """Calcul les moyennes par tag sous forme d'un dataframe""" etudids = list(self.etudiants.keys()) self.notes = compute_tag_moy(self.notes_cube, etudids, self.tags_sorted) + """Calcul les moyennes par tag sous forme d'un dataframe""" - """Synthétise les moyennes/classements par tag""" self.moyennes_tags = {} + """Synthétise les moyennes/classements par tag (qu'ils soient personnalisé ou de compétences)""" for tag in self.tags_sorted: moy_gen_tag = self.notes[tag] - class_gen_tag = moy_sem.comp_ranks_series(moy_gen_tag)[1] # en int - self.moyennes_tags[tag] = { - "notes": moy_gen_tag, - "classements": class_gen_tag, - "min": moy_gen_tag.min(), - "max": moy_gen_tag.max(), - "moy": moy_gen_tag.mean(), - "nb_inscrits": len(moy_gen_tag), - } + self.moyennes_tags[tag] = self.comp_moy_et_stat(moy_gen_tag) def get_repr(self, verbose=False) -> str: """Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle @@ -123,11 +115,11 @@ class TrajectoireTag(TableTag): """Construit le cube de notes (etudid x tags x semestre_aggregé) nécessaire au calcul des moyennes de l'aggrégat """ - nb_tags = len(self.tags_sorted) - nb_etudiants = len(self.etuds) - nb_semestres = len(self.semestres_tags_aggreges) + # nb_tags = len(self.tags_sorted) + # nb_etudiants = len(self.etuds) + # nb_semestres = len(self.semestres_tags_aggreges) - """Index du cube (etudids -> dim 0, tags -> dim 1)""" + # Index du cube (etudids -> dim 0, tags -> dim 1) etudids = [etud.etudid for etud in self.etuds] tags = self.tags_sorted semestres_id = list(self.semestres_tags_aggreges.keys()) @@ -135,17 +127,17 @@ class TrajectoireTag(TableTag): dfs = {} for frmsem_id in semestres_id: - """Partant d'un dataframe vierge""" + # Partant d'un dataframe vierge df = pd.DataFrame(np.nan, index=etudids, columns=tags) - """Charge les notes du semestre tag""" + # Charge les notes du semestre tag notes = self.semestres_tags_aggreges[frmsem_id].notes - """Les étudiants & les tags commun au dataframe final et aux notes du semestre)""" + # Les étudiants & les tags commun au dataframe final et aux notes du semestre) etudids_communs = df.index.intersection(notes.index) tags_communs = df.columns.intersection(notes.columns) - """Injecte les notes par tag""" + # Injecte les notes par tag df.loc[etudids_communs, tags_communs] = notes.loc[ etudids_communs, tags_communs ] @@ -154,7 +146,7 @@ class TrajectoireTag(TableTag): for col in df.columns: df[col] = pd.to_numeric(df[col], errors="coerce") - """Stocke le df""" + # Stocke le df dfs[frmsem_id] = df """Réunit les notes sous forme d'un cube etdids x tags x semestres""" @@ -175,8 +167,6 @@ class TrajectoireTag(TableTag): return sorted(set(tags)) - - def compute_tag_moy(set_cube: np.array, etudids: list, tags: list): """Calcul de la moyenne par tag sur plusieurs semestres. La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles @@ -214,4 +204,6 @@ def compute_tag_moy(set_cube: np.array, etudids: list, tags: list): columns=tags, # les tags ) + etud_moy_tag_df.fillna(np.nan) + return etud_moy_tag_df diff --git a/app/pe/pe_view.py b/app/pe/pe_view.py index 7b3a4211d..8279ff611 100644 --- a/app/pe/pe_view.py +++ b/app/pe/pe_view.py @@ -78,7 +78,7 @@ def pe_view_sem_recap(formsemestre_id: int): sco=ScoData(formsemestre=formsemestre), ) - jury = pe_jury.JuryPE(annee_diplome, formsemestre.formation.formation_id) + jury = pe_jury.JuryPE(annee_diplome) if not jury.diplomes_ids: flash("aucun étudiant à considérer !") return redirect( diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index e86dc3551..2fd6a13b8 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -882,7 +882,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
""" )