############################################################################## # ScoDoc # Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """Génération bulletin BUT """ import datetime from flask import url_for, g from app.scodoc import sco_utils as scu from app.scodoc import sco_bulletins_json from app.scodoc import sco_preferences from app.scodoc.sco_codes_parcours import UE_SPORT from app.scodoc.sco_utils import fmt_note from app.comp.res_but import ResultatsSemestreBUT class BulletinBUT(ResultatsSemestreBUT): """Génération du bulletin BUT. Cette classe génère des dictionnaires avec toutes les informations du bulletin, qui sont immédiatement traduisibles en JSON. """ def etud_ue_mod_results(self, etud, ue, modimpls) -> dict: "dict synthèse résultats dans l'UE pour les modules indiqués" d = {} etud_idx = self.etud_index[etud.id] ue_idx = self.modimpl_coefs_df.index.get_loc(ue.id) etud_moy_module = self.sem_cube[etud_idx] # module x UE for modimpl in modimpls: if self.modimpl_inscr_df[str(modimpl.id)][etud.id]: # si inscrit coef = self.modimpl_coefs_df[modimpl.id][ue.id] if coef > 0: d[modimpl.module.code] = { "id": modimpl.id, "coef": coef, "moyenne": fmt_note( etud_moy_module[ self.modimpl_coefs_df.columns.get_loc(modimpl.id) ][ue_idx] ), } return d def etud_ue_results(self, etud, ue): "dict synthèse résultats UE" d = { "id": ue.id, "numero": ue.numero, "type": ue.type, "ECTS": { "acquis": 0, # XXX TODO voir jury "total": ue.ects, }, "color": ue.color, "competence": None, # XXX TODO lien avec référentiel "moyenne": None, # Le bonus sport appliqué sur cette UE "bonus": self.bonus_ues[ue.id][etud.id] if self.bonus_ues is not None and ue.id in self.bonus_ues else 0.0, "malus": None, # XXX TODO voir ce qui est ici "capitalise": None, # "AAAA-MM-JJ" TODO "ressources": self.etud_ue_mod_results(etud, ue, self.ressources), "saes": self.etud_ue_mod_results(etud, ue, self.saes), } if ue.type != UE_SPORT: d["moyenne"] = { "value": fmt_note(self.etud_moy_ue[ue.id][etud.id]), "min": fmt_note(self.etud_moy_ue[ue.id].min()), "max": fmt_note(self.etud_moy_ue[ue.id].max()), "moy": fmt_note(self.etud_moy_ue[ue.id].mean()), } return d def etud_mods_results(self, etud, modimpls) -> dict: """dict synthèse résultats des modules indiqués, avec évaluations de chacun.""" d = {} # etud_idx = self.etud_index[etud.id] for modimpl in modimpls: # mod_idx = self.modimpl_coefs_df.columns.get_loc(mi.id) # # moyennes indicatives (moyennes de moyennes d'UE) # try: # moyennes_etuds = np.nan_to_num( # np.nanmean(self.sem_cube[:, mod_idx, :], axis=1), # copy=False, # ) # except RuntimeWarning: # all nans in np.nanmean (sur certains etuds sans notes valides) # pass # try: # moy_indicative_mod = np.nanmean(self.sem_cube[etud_idx, mod_idx]) # except RuntimeWarning: # all nans in np.nanmean # pass modimpl_results = self.modimpls_results[modimpl.id] if self.modimpl_inscr_df[str(modimpl.id)][etud.id]: # si inscrit d[modimpl.module.code] = { "id": modimpl.id, "titre": modimpl.module.titre, "code_apogee": modimpl.module.code_apogee, "url": url_for( "notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id, ), "moyenne": { # # moyenne indicative de module: moyenne des UE, ignorant celles sans notes (nan) # "value": fmt_note(moy_indicative_mod), # "min": fmt_note(moyennes_etuds.min()), # "max": fmt_note(moyennes_etuds.max()), # "moy": fmt_note(moyennes_etuds.mean()), }, "evaluations": [ self.etud_eval_results(etud, e) for e in modimpl.evaluations if e.visibulletin and modimpl_results.evaluations_etat[e.id].is_complete ], } return d def etud_eval_results(self, etud, e) -> dict: "dict resultats d'un étudiant à une évaluation" # eval_notes est une pd.Series avec toutes les notes des étudiants inscrits eval_notes = self.modimpls_results[e.moduleimpl_id].evals_notes[e.id] notes_ok = eval_notes.where(eval_notes > scu.NOTES_ABSENCE).dropna() d = { "id": e.id, "description": e.description, "date": e.jour.isoformat() if e.jour else None, "heure_debut": e.heure_debut.strftime("%H:%M") if e.heure_debut else None, "heure_fin": e.heure_fin.strftime("%H:%M") if e.heure_debut else None, "coef": e.coefficient, "poids": {p.ue.acronyme: p.poids for p in e.ue_poids}, "note": { "value": fmt_note( eval_notes[etud.id], note_max=e.note_max, ), "min": fmt_note(notes_ok.min()), "max": fmt_note(notes_ok.max()), "moy": fmt_note(notes_ok.mean()), }, "url": url_for( "notes.evaluation_listenotes", scodoc_dept=g.scodoc_dept, evaluation_id=e.id, ), } return d def bulletin_etud(self, etud, formsemestre, force_publishing=False) -> dict: """Le bulletin de l'étudiant dans ce semestre. Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai (bulletins non publiés). """ etat_inscription = etud.etat_inscription(formsemestre.id) nb_inscrits = self.get_inscriptions_counts()[scu.INSCRIT] published = (not formsemestre.bul_hide_xml) or force_publishing d = { "version": "0", "type": "BUT", "date": datetime.datetime.utcnow().isoformat() + "Z", "publie": not formsemestre.bul_hide_xml, "etudiant": etud.to_dict_bul(), "formation": { "id": formsemestre.formation.id, "acronyme": formsemestre.formation.acronyme, "titre_officiel": formsemestre.formation.titre_officiel, "titre": formsemestre.formation.titre, }, "formsemestre_id": formsemestre.id, "etat_inscription": etat_inscription, "options": sco_preferences.bulletin_option_affichage(formsemestre.id), } if not published: return d semestre_infos = { "etapes": [str(x.etape_apo) for x in formsemestre.etapes if x.etape_apo], "date_debut": formsemestre.date_debut.isoformat(), "date_fin": formsemestre.date_fin.isoformat(), "annee_universitaire": self.formsemestre.annee_scolaire_str(), "inscription": "TODO-MM-JJ", # XXX TODO "numero": formsemestre.semestre_id, "groupes": [], # XXX TODO "absences": { # XXX TODO "injustifie": -1, "total": -1, }, } semestre_infos.update( sco_bulletins_json.dict_decision_jury(etud.id, formsemestre.id) ) if etat_inscription == scu.INSCRIT: semestre_infos.update( { "notes": { # moyenne des moyennes générales du semestre "value": fmt_note(self.etud_moy_gen[etud.id]), "min": fmt_note(self.etud_moy_gen.min()), "moy": fmt_note(self.etud_moy_gen.mean()), "max": fmt_note(self.etud_moy_gen.max()), }, "rang": { # classement wrt moyenne général, indicatif "value": self.etud_moy_gen_ranks[etud.id], "total": nb_inscrits, }, }, ) d.update( { "ressources": self.etud_mods_results(etud, self.ressources), "saes": self.etud_mods_results(etud, self.saes), "ues": { ue.acronyme: self.etud_ue_results(etud, ue) for ue in self.ues }, "semestre": semestre_infos, }, ) else: semestre_infos.update( { "notes": { "value": "DEM", "min": "", "moy": "", "max": "", }, "rang": {"value": "DEM", "total": nb_inscrits}, } ) d.update( { "semestre": semestre_infos, "ressources": {}, "saes": {}, "ues": {}, } ) return d