diff --git a/app/comp/res_but.py b/app/comp/res_but.py index d7fec78639..d86d10e798 100644 --- a/app/comp/res_but.py +++ b/app/comp/res_but.py @@ -28,6 +28,7 @@ class ResultatsSemestreBUT(NotesTableCompat): "modimpl_coefs_df", "modimpls_evals_poids", "sem_cube", + "ues_inscr_parcours_df", # inscriptions aux UE / parcours ) def __init__(self, formsemestre): @@ -55,6 +56,7 @@ class ResultatsSemestreBUT(NotesTableCompat): self.modimpls_results, ) = moy_ue.notes_sem_load_cube(self.formsemestre) self.modimpl_inscr_df = inscr_mod.df_load_modimpl_inscr(self.formsemestre) + self.ues_inscr_parcours_df = self.load_ues_inscr_parcours() self.modimpl_coefs_df, _, _ = moy_ue.df_load_modimpl_coefs( self.formsemestre, modimpls=self.formsemestre.modimpls_sorted ) @@ -108,6 +110,9 @@ class ResultatsSemestreBUT(NotesTableCompat): # Clippe toutes les moyennes d'UE dans [0,20] self.etud_moy_ue.clip(lower=0.0, upper=20.0, inplace=True) + # Nanifie les moyennes d'UE hors parcours pour chaque étudiant + self.etud_moy_ue *= self.ues_inscr_parcours_df + # Moyenne générale indicative: # (note: le bonus sport a déjà été appliqué aux moyennes d'UE, et impacte # donc la moyenne indicative) @@ -175,3 +180,45 @@ class ResultatsSemestreBUT(NotesTableCompat): i = self.modimpl_coefs_df.columns.get_loc(modimpl_id) j = self.modimpl_coefs_df.index.get_loc(ue_id) return self.sem_cube[:, i, j] + + def load_ues_inscr_parcours(self) -> pd.DataFrame: + """Chargement des inscriptions aux parcours et calcul de la + matrice d'inscriptions (etuds, ue). + S'il n'y pas de référentiel de compétence, donc pas de parcours, + on considère l'étudiant inscrit à toutes les ue. + La matrice avec ue ne comprend que les UE non bonus. + 1.0 si étudiant inscrit à l'UE, NaN sinon. + """ + etuds_parcours = { + inscr.etudid: inscr.parcour_id for inscr in self.formsemestre.inscriptions + } + ue_ids = [ue.id for ue in self.ues] + # matrice de 1, inscrits par défaut à toutes les UE: + ues_inscr_parcours_df = pd.DataFrame( + 1.0, index=etuds_parcours.keys(), columns=ue_ids, dtype=float + ) + if self.formsemestre.formation.referentiel_competence is None: + return ues_inscr_parcours_df + + ue_by_parcours = {} # parcours_id : {ue_id:0|1} + for parcour in self.formsemestre.formation.referentiel_competence.parcours: + ue_by_parcours[parcour.id] = { + ue.id: 1.0 + for ue in self.formsemestre.formation.query_ues_parcour( + parcour + ).filter_by(semestre_idx=self.formsemestre.semestre_id) + } + for etudid in etuds_parcours: + parcour = etuds_parcours[etudid] + if parcour is not None: + ues_inscr_parcours_df.loc[etudid] = ue_by_parcours[ + etuds_parcours[etudid] + ] + return ues_inscr_parcours_df + + def etud_ues(self, etudid: int) -> list[int]: + """Liste des id d'UE auxquelles l'étudiant est inscrit (sans bonus). + (surchargée en BUT pour prendre en compte les parcours) + """ + s = self.ues_inscr_parcours_df.loc[etudid] + return s.index[s.notna()] diff --git a/app/comp/res_common.py b/app/comp/res_common.py index 645f067e60..3c9eb810f9 100644 --- a/app/comp/res_common.py +++ b/app/comp/res_common.py @@ -112,6 +112,14 @@ class ResultatsSemestre(ResultatsCache): "dict { etudid : indice dans les inscrits }" return {e.id: idx for idx, e in enumerate(self.etuds)} + def etud_ues(self, etudid: int) -> list[int]: + """Liste des UE auxquelles l'etudiant est inscrit, sans bonus + (surchargée en BUT pour prendre en compte les parcours) + """ + # Pour les formations classiques, etudid n'est pas utilisé + # car tous les étudiants sont inscrits à toutes les UE + return [ue.id for ue in self.ues if ue.type != UE_SPORT] + def modimpl_notes(self, modimpl_id: int, ue_id: int) -> np.ndarray: """Les notes moyennes des étudiants du sem. à ce modimpl dans cette ue. Utile pour stats bottom tableau recap. @@ -622,9 +630,10 @@ class ResultatsSemestre(ResultatsCache): f"_{col_id}_target_attrs" ] = f""" title="{modimpl.module.titre} ({nom_resp})" """ modimpl_ids.add(modimpl.id) + nb_ues_etud_parcours = len(self.etud_ues(etudid)) ue_valid_txt = ( ue_valid_txt_html - ) = f"{nb_ues_validables}/{len(ues_sans_bonus)}" + ) = f"{nb_ues_validables}/{nb_ues_etud_parcours}" if nb_ues_warning: ue_valid_txt_html += " " + scu.EMO_WARNING add_cell( diff --git a/app/models/formations.py b/app/models/formations.py index df857d39cc..dc8ccb8015 100644 --- a/app/models/formations.py +++ b/app/models/formations.py @@ -62,7 +62,10 @@ class Formation(db.Model): return e def get_parcours(self): - """get l'instance de TypeParcours de cette formation""" + """get l'instance de TypeParcours de cette formation + (le TypeParcours définit le genre de formation, à ne pas confondre + avec les parcours du BUT). + """ return sco_codes_parcours.get_parcours_from_code(self.type_parcours) def get_titre_version(self) -> str: