2021-12-05 20:21:51 +01:00
|
|
|
##############################################################################
|
|
|
|
# ScoDoc
|
2023-12-31 23:04:06 +01:00
|
|
|
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
|
2021-12-05 20:21:51 +01:00
|
|
|
# See LICENSE
|
|
|
|
##############################################################################
|
|
|
|
|
2021-12-26 19:15:47 +01:00
|
|
|
"""Génération bulletin BUT
|
|
|
|
"""
|
|
|
|
|
2022-03-14 08:05:31 +01:00
|
|
|
import collections
|
2021-12-05 20:21:51 +01:00
|
|
|
import datetime
|
2024-04-11 01:45:25 +02:00
|
|
|
import pandas as pd
|
2022-03-15 22:24:52 +01:00
|
|
|
import numpy as np
|
2022-08-07 19:56:25 +02:00
|
|
|
from flask import g, has_request_context, url_for
|
2021-12-05 20:21:51 +01:00
|
|
|
|
2023-07-11 06:57:38 +02:00
|
|
|
from app import db
|
2024-04-11 01:45:25 +02:00
|
|
|
from app.comp.moy_mod import ModuleImplResults
|
2022-02-14 23:21:42 +01:00
|
|
|
from app.comp.res_but import ResultatsSemestreBUT
|
2024-04-11 01:45:25 +02:00
|
|
|
from app.models import Evaluation, FormSemestre, Identite, ModuleImpl
|
2022-05-18 20:43:01 +02:00
|
|
|
from app.models.groups import GroupDescr
|
2022-03-15 21:50:37 +01:00
|
|
|
from app.models.ues import UniteEns
|
2022-02-21 19:25:38 +01:00
|
|
|
from app.scodoc import sco_bulletins, sco_utils as scu
|
2021-12-17 00:25:45 +01:00
|
|
|
from app.scodoc import sco_bulletins_json
|
2022-02-14 23:21:42 +01:00
|
|
|
from app.scodoc import sco_bulletins_pdf
|
2023-02-12 13:36:47 +01:00
|
|
|
from app.scodoc import codes_cursus
|
2022-05-18 20:43:01 +02:00
|
|
|
from app.scodoc import sco_groups
|
2021-12-05 21:54:04 +01:00
|
|
|
from app.scodoc import sco_preferences
|
2023-02-12 13:36:47 +01:00
|
|
|
from app.scodoc.codes_cursus import UE_SPORT, DEF
|
2023-10-13 22:25:44 +02:00
|
|
|
from app.scodoc.sco_exceptions import ScoValueError
|
2021-12-26 19:15:47 +01:00
|
|
|
from app.scodoc.sco_utils import fmt_note
|
2021-12-05 20:21:51 +01:00
|
|
|
|
2021-12-24 00:08:25 +01:00
|
|
|
|
2022-02-09 23:22:00 +01:00
|
|
|
class BulletinBUT:
|
2021-12-24 00:08:25 +01:00
|
|
|
"""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.
|
|
|
|
"""
|
|
|
|
|
2022-02-09 23:22:00 +01:00
|
|
|
def __init__(self, formsemestre: FormSemestre):
|
|
|
|
""" """
|
|
|
|
self.res = ResultatsSemestreBUT(formsemestre)
|
2022-02-14 23:21:42 +01:00
|
|
|
self.prefs = sco_preferences.SemPreferences(formsemestre.id)
|
2022-02-09 23:22:00 +01:00
|
|
|
|
2021-12-05 20:21:51 +01:00
|
|
|
def etud_ue_mod_results(self, etud, ue, modimpls) -> dict:
|
|
|
|
"dict synthèse résultats dans l'UE pour les modules indiqués"
|
2022-02-09 23:22:00 +01:00
|
|
|
res = self.res
|
2021-12-05 20:21:51 +01:00
|
|
|
d = {}
|
2022-02-09 23:22:00 +01:00
|
|
|
etud_idx = res.etud_index[etud.id]
|
2022-01-29 22:45:39 +01:00
|
|
|
if ue.type != UE_SPORT:
|
2022-02-09 23:22:00 +01:00
|
|
|
ue_idx = res.modimpl_coefs_df.index.get_loc(ue.id)
|
|
|
|
etud_moy_module = res.sem_cube[etud_idx] # module x UE
|
2021-12-26 19:15:47 +01:00
|
|
|
for modimpl in modimpls:
|
2022-02-09 23:22:00 +01:00
|
|
|
if res.modimpl_inscr_df[modimpl.id][etud.id]: # si inscrit
|
2022-01-29 22:45:39 +01:00
|
|
|
if ue.type != UE_SPORT:
|
2022-02-09 23:22:00 +01:00
|
|
|
coef = res.modimpl_coefs_df[modimpl.id][ue.id]
|
2022-01-29 22:45:39 +01:00
|
|
|
if coef > 0:
|
|
|
|
d[modimpl.module.code] = {
|
|
|
|
"id": modimpl.id,
|
|
|
|
"coef": coef,
|
|
|
|
"moyenne": fmt_note(
|
|
|
|
etud_moy_module[
|
2022-02-09 23:22:00 +01:00
|
|
|
res.modimpl_coefs_df.columns.get_loc(modimpl.id)
|
2022-01-29 22:45:39 +01:00
|
|
|
][ue_idx]
|
|
|
|
),
|
|
|
|
}
|
|
|
|
# else: # modules dans UE bonus sport
|
|
|
|
# d[modimpl.module.code] = {
|
|
|
|
# "id": modimpl.id,
|
|
|
|
# "coef": "",
|
|
|
|
# "moyenne": "?x?",
|
|
|
|
# }
|
2021-12-05 20:21:51 +01:00
|
|
|
return d
|
|
|
|
|
2022-05-18 20:43:01 +02:00
|
|
|
def etud_ue_results(
|
|
|
|
self,
|
|
|
|
etud: Identite,
|
|
|
|
ue: UniteEns,
|
|
|
|
decision_ue: dict,
|
|
|
|
etud_groups: list[GroupDescr] = None,
|
|
|
|
) -> dict:
|
|
|
|
"""dict synthèse résultats UE
|
|
|
|
etud_groups : liste des groupes, pour affichage du rang.
|
2022-09-04 23:19:13 +02:00
|
|
|
Si UE sport et étudiant non inscrit, renvoie dict vide.
|
2022-05-18 20:43:01 +02:00
|
|
|
"""
|
2022-02-09 23:22:00 +01:00
|
|
|
res = self.res
|
2022-03-15 21:50:37 +01:00
|
|
|
|
2022-12-01 13:00:14 +01:00
|
|
|
if (etud.id, ue.id) in self.res.dispense_ues:
|
|
|
|
return {}
|
|
|
|
|
2022-09-04 23:19:13 +02:00
|
|
|
if ue.type == UE_SPORT:
|
|
|
|
modimpls_spo = [
|
|
|
|
modimpl
|
|
|
|
for modimpl in res.formsemestre.modimpls_sorted
|
|
|
|
if modimpl.module.ue.type == UE_SPORT
|
|
|
|
]
|
|
|
|
# L'étudiant est-il inscrit à l'un des modules de l'UE bonus ?
|
|
|
|
if not any(res.modimpl_inscr_df.loc[etud.id][[m.id for m in modimpls_spo]]):
|
|
|
|
return {}
|
|
|
|
|
2021-12-05 20:21:51 +01:00
|
|
|
d = {
|
|
|
|
"id": ue.id,
|
2022-01-29 22:59:40 +01:00
|
|
|
"titre": ue.titre,
|
2021-12-10 01:54:11 +01:00
|
|
|
"numero": ue.numero,
|
2022-01-25 10:45:13 +01:00
|
|
|
"type": ue.type,
|
|
|
|
"color": ue.color,
|
2021-12-05 20:21:51 +01:00
|
|
|
"competence": None, # XXX TODO lien avec référentiel
|
2022-01-25 10:45:13 +01:00
|
|
|
"moyenne": None,
|
|
|
|
# Le bonus sport appliqué sur cette UE
|
2024-02-24 16:49:41 +01:00
|
|
|
"bonus": (
|
|
|
|
fmt_note(res.bonus_ues[ue.id][etud.id])
|
|
|
|
if res.bonus_ues is not None and ue.id in res.bonus_ues
|
|
|
|
else fmt_note(0.0)
|
|
|
|
),
|
2022-03-10 01:24:37 +01:00
|
|
|
"malus": fmt_note(res.malus[ue.id][etud.id]),
|
2022-05-18 20:43:01 +02:00
|
|
|
"capitalise": None, # "AAAA-MM-JJ" TODO #sco93
|
2022-02-09 23:22:00 +01:00
|
|
|
"ressources": self.etud_ue_mod_results(etud, ue, res.ressources),
|
|
|
|
"saes": self.etud_ue_mod_results(etud, ue, res.saes),
|
2021-12-05 20:21:51 +01:00
|
|
|
}
|
2022-03-15 21:50:37 +01:00
|
|
|
if self.prefs["bul_show_ects"]:
|
|
|
|
d["ECTS"] = {
|
|
|
|
"acquis": decision_ue.get("ects", 0.0),
|
|
|
|
"total": ue.ects or 0.0, # float même si non renseigné
|
|
|
|
}
|
2022-01-25 10:45:13 +01:00
|
|
|
if ue.type != UE_SPORT:
|
2022-02-14 23:21:42 +01:00
|
|
|
if self.prefs["bul_show_ue_rangs"]:
|
2022-02-09 23:22:00 +01:00
|
|
|
rangs, effectif = res.ue_rangs[ue.id]
|
2022-02-06 18:40:00 +01:00
|
|
|
rang = rangs[etud.id]
|
|
|
|
else:
|
|
|
|
rang, effectif = "", 0
|
2022-01-25 10:45:13 +01:00
|
|
|
d["moyenne"] = {
|
2022-02-09 23:22:00 +01:00
|
|
|
"value": fmt_note(res.etud_moy_ue[ue.id][etud.id]),
|
|
|
|
"min": fmt_note(res.etud_moy_ue[ue.id].min()),
|
|
|
|
"max": fmt_note(res.etud_moy_ue[ue.id].max()),
|
|
|
|
"moy": fmt_note(res.etud_moy_ue[ue.id].mean()),
|
2022-02-06 18:40:00 +01:00
|
|
|
"rang": rang,
|
2022-02-06 18:29:22 +01:00
|
|
|
"total": effectif, # nb etud avec note dans cette UE
|
2022-05-18 20:43:01 +02:00
|
|
|
"groupes": {},
|
2022-01-25 10:45:13 +01:00
|
|
|
}
|
2022-05-18 20:43:01 +02:00
|
|
|
if self.prefs["bul_show_ue_rangs"]:
|
|
|
|
for group in etud_groups:
|
|
|
|
if group.partition.bul_show_rank:
|
|
|
|
rang, effectif = self.res.get_etud_ue_rang(
|
|
|
|
ue.id, etud.id, group.id
|
|
|
|
)
|
|
|
|
d["moyenne"]["groupes"][group.id] = {
|
|
|
|
"value": rang,
|
|
|
|
"total": effectif,
|
|
|
|
}
|
2022-09-04 23:19:13 +02:00
|
|
|
else: # UE BONUS
|
|
|
|
d["modules"] = self.etud_mods_results(etud, modimpls_spo)
|
2022-01-26 11:51:13 +01:00
|
|
|
# ceci suppose que l'on a une seule UE bonus,
|
|
|
|
# en tous cas elles auront la même description
|
|
|
|
d["bonus_description"] = self.etud_bonus_description(etud.id)
|
2022-09-04 23:19:13 +02:00
|
|
|
|
2021-12-05 20:21:51 +01:00
|
|
|
return d
|
|
|
|
|
2022-09-04 11:20:25 +02:00
|
|
|
def etud_ues_capitalisees(self, etud: Identite) -> dict:
|
|
|
|
"""dict avec les UE capitalisees. la clé est l'acronyme d'UE, qui ne
|
|
|
|
peut donc être capitalisée qu'une seule fois (on prend la meilleure)"""
|
2022-09-04 22:45:34 +02:00
|
|
|
if not etud.id in self.res.validations.ue_capitalisees.index:
|
2022-09-04 11:20:25 +02:00
|
|
|
return {} # aucune capitalisation
|
|
|
|
d = {}
|
|
|
|
for _, ue_capitalisee in self.res.validations.ue_capitalisees.loc[
|
2022-09-05 13:54:35 +02:00
|
|
|
[etud.id]
|
2022-09-04 11:20:25 +02:00
|
|
|
].iterrows():
|
2023-02-12 13:36:47 +01:00
|
|
|
if codes_cursus.code_ue_validant(ue_capitalisee.code):
|
2023-07-11 06:57:38 +02:00
|
|
|
ue = db.session.get(UniteEns, ue_capitalisee.ue_id) # XXX cacher ?
|
2022-09-04 11:20:25 +02:00
|
|
|
# déjà capitalisé ? montre la meilleure
|
2022-09-08 15:10:39 +02:00
|
|
|
if ue.acronyme in d:
|
|
|
|
moy_cap = d[ue.acronyme]["moyenne_num"] or 0.0
|
|
|
|
if (not isinstance(moy_cap, float)) or (
|
|
|
|
(ue_capitalisee.moy_ue or 0.0) < moy_cap
|
|
|
|
):
|
|
|
|
continue # skip this duplicate UE
|
2022-09-04 11:20:25 +02:00
|
|
|
|
|
|
|
d[ue.acronyme] = {
|
|
|
|
"id": ue.id,
|
|
|
|
"ue_code": ue_capitalisee.ue_code,
|
|
|
|
"titre": ue.titre,
|
|
|
|
"numero": ue.numero,
|
|
|
|
"type": ue.type,
|
|
|
|
"color": ue.color,
|
2022-09-08 15:10:39 +02:00
|
|
|
"moyenne": fmt_note(ue_capitalisee.moy_ue), # arrondi en str
|
|
|
|
"moyenne_num": fmt_note(ue_capitalisee.moy_ue, keep_numeric=True),
|
2022-09-04 11:20:25 +02:00
|
|
|
"is_external": ue_capitalisee.is_external,
|
|
|
|
"date_capitalisation": ue_capitalisee.event_date,
|
|
|
|
"formsemestre_id": ue_capitalisee.formsemestre_id,
|
2024-02-24 16:49:41 +01:00
|
|
|
"bul_orig_url": (
|
|
|
|
url_for(
|
|
|
|
"notes.formsemestre_bulletinetud",
|
|
|
|
scodoc_dept=g.scodoc_dept,
|
|
|
|
etudid=etud.id,
|
|
|
|
formsemestre_id=ue_capitalisee.formsemestre_id,
|
|
|
|
)
|
|
|
|
if ue_capitalisee.formsemestre_id
|
|
|
|
else None
|
|
|
|
),
|
2023-03-18 21:56:08 +01:00
|
|
|
"ressources": {}, # sans détail en BUT
|
|
|
|
"saes": {},
|
2022-09-04 11:20:25 +02:00
|
|
|
}
|
2022-09-04 22:45:34 +02:00
|
|
|
if self.prefs["bul_show_ects"]:
|
|
|
|
d[ue.acronyme]["ECTS"] = {
|
|
|
|
"acquis": ue.ects or 0.0, # toujours validée ici
|
|
|
|
"total": ue.ects or 0.0, # float même si non renseigné
|
|
|
|
}
|
2022-09-04 11:20:25 +02:00
|
|
|
return d
|
|
|
|
|
2022-03-07 21:49:11 +01:00
|
|
|
def etud_mods_results(self, etud, modimpls, version="long") -> dict:
|
2021-12-05 20:21:51 +01:00
|
|
|
"""dict synthèse résultats des modules indiqués,
|
2022-03-07 21:49:11 +01:00
|
|
|
avec évaluations de chacun (sauf si version == "short")
|
|
|
|
"""
|
2022-02-09 23:22:00 +01:00
|
|
|
res = self.res
|
2021-12-05 20:21:51 +01:00
|
|
|
d = {}
|
2021-12-18 12:16:49 +01:00
|
|
|
# etud_idx = self.etud_index[etud.id]
|
2021-12-26 19:15:47 +01:00
|
|
|
for modimpl in modimpls:
|
2021-12-18 12:16:49 +01:00
|
|
|
# mod_idx = self.modimpl_coefs_df.columns.get_loc(mi.id)
|
2021-12-16 00:03:24 +01:00
|
|
|
# # 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,
|
|
|
|
# )
|
2022-02-10 14:34:16 +01:00
|
|
|
# except RuntimeWarning:
|
|
|
|
# # all nans in np.nanmean (sur certains etuds sans notes valides)
|
2021-12-16 00:03:24 +01:00
|
|
|
# pass
|
|
|
|
# try:
|
|
|
|
# moy_indicative_mod = np.nanmean(self.sem_cube[etud_idx, mod_idx])
|
|
|
|
# except RuntimeWarning: # all nans in np.nanmean
|
|
|
|
# pass
|
2022-02-09 23:22:00 +01:00
|
|
|
modimpl_results = res.modimpls_results[modimpl.id]
|
|
|
|
if res.modimpl_inscr_df[modimpl.id][etud.id]: # si inscrit
|
2022-01-09 22:33:08 +01:00
|
|
|
d[modimpl.module.code] = {
|
|
|
|
"id": modimpl.id,
|
2024-04-06 12:33:07 +02:00
|
|
|
"titre": modimpl.module.titre_str(),
|
2022-01-09 22:33:08 +01:00
|
|
|
"code_apogee": modimpl.module.code_apogee,
|
2024-02-24 16:49:41 +01:00
|
|
|
"url": (
|
|
|
|
url_for(
|
|
|
|
"notes.moduleimpl_status",
|
|
|
|
scodoc_dept=g.scodoc_dept,
|
|
|
|
moduleimpl_id=modimpl.id,
|
|
|
|
)
|
|
|
|
if has_request_context()
|
|
|
|
else "na"
|
|
|
|
),
|
2022-01-09 21:48:58 +01:00
|
|
|
"moyenne": {
|
2022-02-10 14:34:16 +01:00
|
|
|
# # moyenne indicative de module: moyenne des UE,
|
|
|
|
# # ignorant celles sans notes (nan)
|
2022-01-09 21:48:58 +01:00
|
|
|
# "value": fmt_note(moy_indicative_mod),
|
|
|
|
# "min": fmt_note(moyennes_etuds.min()),
|
|
|
|
# "max": fmt_note(moyennes_etuds.max()),
|
|
|
|
# "moy": fmt_note(moyennes_etuds.mean()),
|
|
|
|
},
|
2024-02-24 16:49:41 +01:00
|
|
|
"evaluations": (
|
2024-04-11 01:45:25 +02:00
|
|
|
self.etud_list_modimpl_evaluations(
|
|
|
|
etud, modimpl, modimpl_results, version
|
|
|
|
)
|
2024-02-24 16:49:41 +01:00
|
|
|
if version != "short"
|
|
|
|
else []
|
|
|
|
),
|
2022-01-09 21:48:58 +01:00
|
|
|
}
|
2021-12-05 20:21:51 +01:00
|
|
|
return d
|
|
|
|
|
2024-04-11 01:45:25 +02:00
|
|
|
def etud_list_modimpl_evaluations(
|
|
|
|
self,
|
|
|
|
etud: Identite,
|
|
|
|
modimpl: ModuleImpl,
|
|
|
|
modimpl_results: ModuleImplResults,
|
|
|
|
version: str,
|
|
|
|
) -> list[dict]:
|
|
|
|
"""Liste des résultats aux évaluations de ce modimpl à montrer pour cet étudiant"""
|
|
|
|
evaluation: Evaluation
|
|
|
|
eval_results = []
|
|
|
|
for evaluation in modimpl.evaluations:
|
|
|
|
if (
|
|
|
|
(evaluation.visibulletin or version == "long")
|
|
|
|
and (evaluation.id in modimpl_results.evaluations_etat)
|
|
|
|
and (
|
|
|
|
modimpl_results.evaluations_etat[evaluation.id].is_complete
|
|
|
|
or self.prefs["bul_show_all_evals"]
|
|
|
|
)
|
|
|
|
):
|
|
|
|
eval_notes = self.res.modimpls_results[modimpl.id].evals_notes[
|
|
|
|
evaluation.id
|
|
|
|
]
|
|
|
|
|
|
|
|
if (evaluation.evaluation_type == Evaluation.EVALUATION_NORMALE) or (
|
|
|
|
not np.isnan(eval_notes[etud.id])
|
|
|
|
):
|
|
|
|
eval_results.append(
|
|
|
|
self.etud_eval_results(etud, evaluation, eval_notes)
|
|
|
|
)
|
|
|
|
return eval_results
|
|
|
|
|
|
|
|
def etud_eval_results(
|
|
|
|
self, etud: Identite, evaluation: Evaluation, eval_notes: pd.DataFrame
|
|
|
|
) -> dict:
|
2021-12-05 20:21:51 +01:00
|
|
|
"dict resultats d'un étudiant à une évaluation"
|
2021-12-26 19:15:47 +01:00
|
|
|
# eval_notes est une pd.Series avec toutes les notes des étudiants inscrits
|
2021-12-11 10:56:40 +01:00
|
|
|
notes_ok = eval_notes.where(eval_notes > scu.NOTES_ABSENCE).dropna()
|
2024-04-11 01:45:25 +02:00
|
|
|
modimpls_evals_poids = self.res.modimpls_evals_poids[evaluation.moduleimpl_id]
|
2022-03-14 08:05:31 +01:00
|
|
|
try:
|
2022-11-27 19:14:37 +01:00
|
|
|
etud_ues_ids = self.res.etud_ues_ids(etud.id)
|
2022-03-14 08:05:31 +01:00
|
|
|
poids = {
|
2024-04-11 01:45:25 +02:00
|
|
|
ue.acronyme: modimpls_evals_poids[ue.id][evaluation.id]
|
2022-03-16 15:44:54 +01:00
|
|
|
for ue in self.res.ues
|
2022-11-27 19:14:37 +01:00
|
|
|
if (ue.type != UE_SPORT) and (ue.id in etud_ues_ids)
|
2022-03-14 08:05:31 +01:00
|
|
|
}
|
|
|
|
except KeyError:
|
|
|
|
poids = collections.defaultdict(lambda: 0.0)
|
2021-12-05 20:21:51 +01:00
|
|
|
d = {
|
2024-04-11 01:45:25 +02:00
|
|
|
"id": evaluation.id,
|
2024-02-24 16:49:41 +01:00
|
|
|
"coef": (
|
2024-04-11 01:45:25 +02:00
|
|
|
fmt_note(evaluation.coefficient)
|
|
|
|
if evaluation.evaluation_type == Evaluation.EVALUATION_NORMALE
|
2024-02-24 16:49:41 +01:00
|
|
|
else None
|
|
|
|
),
|
2024-04-11 01:45:25 +02:00
|
|
|
"date_debut": (
|
|
|
|
evaluation.date_debut.isoformat() if evaluation.date_debut else None
|
|
|
|
),
|
|
|
|
"date_fin": (
|
|
|
|
evaluation.date_fin.isoformat() if evaluation.date_fin else None
|
|
|
|
),
|
|
|
|
"description": evaluation.description,
|
|
|
|
"evaluation_type": evaluation.evaluation_type,
|
2024-02-25 16:58:59 +01:00
|
|
|
"note": (
|
|
|
|
{
|
|
|
|
"value": fmt_note(
|
|
|
|
eval_notes[etud.id],
|
2024-04-11 01:45:25 +02:00
|
|
|
note_max=evaluation.note_max,
|
2024-02-25 16:58:59 +01:00
|
|
|
),
|
2024-04-11 01:45:25 +02:00
|
|
|
"min": fmt_note(notes_ok.min(), note_max=evaluation.note_max),
|
|
|
|
"max": fmt_note(notes_ok.max(), note_max=evaluation.note_max),
|
|
|
|
"moy": fmt_note(notes_ok.mean(), note_max=evaluation.note_max),
|
2024-02-25 16:58:59 +01:00
|
|
|
}
|
2024-04-11 01:45:25 +02:00
|
|
|
if not evaluation.is_blocked()
|
2024-02-25 16:58:59 +01:00
|
|
|
else {}
|
|
|
|
),
|
2022-09-18 16:53:00 +02:00
|
|
|
"poids": poids,
|
2024-02-24 16:49:41 +01:00
|
|
|
"url": (
|
|
|
|
url_for(
|
|
|
|
"notes.evaluation_listenotes",
|
|
|
|
scodoc_dept=g.scodoc_dept,
|
2024-04-11 01:45:25 +02:00
|
|
|
evaluation_id=evaluation.id,
|
2024-02-24 16:49:41 +01:00
|
|
|
)
|
|
|
|
if has_request_context()
|
|
|
|
else "na"
|
|
|
|
),
|
2023-08-25 17:58:57 +02:00
|
|
|
# deprecated (supprimer avant #sco9.7)
|
2024-04-11 01:45:25 +02:00
|
|
|
"date": (
|
|
|
|
evaluation.date_debut.isoformat() if evaluation.date_debut else None
|
|
|
|
),
|
2024-02-24 16:49:41 +01:00
|
|
|
"heure_debut": (
|
2024-04-11 01:45:25 +02:00
|
|
|
evaluation.date_debut.time().isoformat("minutes")
|
|
|
|
if evaluation.date_debut
|
|
|
|
else None
|
|
|
|
),
|
|
|
|
"heure_fin": (
|
|
|
|
evaluation.date_fin.time().isoformat("minutes")
|
|
|
|
if evaluation.date_fin
|
|
|
|
else None
|
2024-02-24 16:49:41 +01:00
|
|
|
),
|
2021-12-05 20:21:51 +01:00
|
|
|
}
|
|
|
|
return d
|
|
|
|
|
2022-01-26 11:51:13 +01:00
|
|
|
def etud_bonus_description(self, etudid):
|
|
|
|
"""description du bonus affichée dans la section "UE bonus"."""
|
2022-02-09 23:22:00 +01:00
|
|
|
res = self.res
|
|
|
|
if res.bonus_ues is None or res.bonus_ues.shape[1] == 0:
|
2022-01-26 11:51:13 +01:00
|
|
|
return ""
|
|
|
|
|
2022-02-09 23:22:00 +01:00
|
|
|
bonus_vect = res.bonus_ues.loc[etudid]
|
2022-01-26 11:51:13 +01:00
|
|
|
if bonus_vect.nunique() > 1:
|
|
|
|
# détail UE par UE
|
|
|
|
details = [
|
|
|
|
f"{fmt_note(bonus_vect[ue.id])} sur {ue.acronyme}"
|
2022-02-09 23:22:00 +01:00
|
|
|
for ue in res.ues
|
2022-03-14 09:44:31 +01:00
|
|
|
if ue.type != UE_SPORT
|
2022-06-25 23:22:20 +02:00
|
|
|
and res.modimpls_in_ue(ue, etudid)
|
2022-02-09 23:22:00 +01:00
|
|
|
and ue.id in res.bonus_ues
|
2022-01-29 23:36:07 +01:00
|
|
|
and bonus_vect[ue.id] > 0.0
|
2022-01-26 11:51:13 +01:00
|
|
|
]
|
|
|
|
if details:
|
|
|
|
return "Bonus de " + ", ".join(details)
|
|
|
|
else:
|
|
|
|
return "" # aucun bonus
|
|
|
|
else:
|
|
|
|
return f"Bonus de {fmt_note(bonus_vect.iloc[0])}"
|
|
|
|
|
2022-02-14 23:21:42 +01:00
|
|
|
def bulletin_etud(
|
2022-03-07 21:49:11 +01:00
|
|
|
self,
|
|
|
|
etud: Identite,
|
|
|
|
force_publishing=False,
|
|
|
|
version="long",
|
2022-02-14 23:21:42 +01:00
|
|
|
) -> dict:
|
|
|
|
"""Le bulletin de l'étudiant dans ce semestre: dict pour la version JSON / HTML.
|
2022-03-07 21:49:11 +01:00
|
|
|
- version:
|
|
|
|
"long", "selectedevals": toutes les infos (notes des évaluations)
|
|
|
|
"short" : ne descend pas plus bas que les modules.
|
|
|
|
|
2022-02-14 23:21:42 +01:00
|
|
|
- Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai
|
2024-03-01 12:03:19 +01:00
|
|
|
(bulletins non publiés sur la passerelle).
|
2022-01-25 13:24:08 +01:00
|
|
|
"""
|
2023-12-06 20:40:55 +01:00
|
|
|
if version not in scu.BULLETINS_VERSIONS_BUT:
|
|
|
|
raise ScoValueError("bulletin_etud: version de bulletin demandée invalide")
|
2022-02-09 23:22:00 +01:00
|
|
|
res = self.res
|
2023-09-01 14:38:41 +02:00
|
|
|
formsemestre = res.formsemestre
|
2021-12-05 20:21:51 +01:00
|
|
|
d = {
|
|
|
|
"version": "0",
|
|
|
|
"type": "BUT",
|
|
|
|
"date": datetime.datetime.utcnow().isoformat() + "Z",
|
2022-01-25 13:24:08 +01:00
|
|
|
"publie": not formsemestre.bul_hide_xml,
|
2024-01-30 10:54:00 +01:00
|
|
|
"etat_inscription": etud.inscription_etat(formsemestre.id),
|
2021-12-05 20:21:51 +01:00
|
|
|
"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,
|
2022-02-14 23:21:42 +01:00
|
|
|
"options": sco_preferences.bulletin_option_affichage(
|
2023-01-12 13:49:05 +01:00
|
|
|
formsemestre, self.prefs
|
2022-02-14 23:21:42 +01:00
|
|
|
),
|
2021-12-05 20:21:51 +01:00
|
|
|
}
|
2024-01-30 10:54:00 +01:00
|
|
|
published = (not formsemestre.bul_hide_xml) or force_publishing
|
|
|
|
if not published or d["etat_inscription"] is False:
|
2022-01-25 13:24:08 +01:00
|
|
|
return d
|
2022-01-26 00:11:04 +01:00
|
|
|
|
2024-01-30 10:54:00 +01:00
|
|
|
nb_inscrits = self.res.get_inscriptions_counts()[scu.INSCRIT]
|
|
|
|
if formsemestre.formation.referentiel_competence is None:
|
|
|
|
etud_ues_ids = {ue.id for ue in res.ues if res.modimpls_in_ue(ue, etud.id)}
|
|
|
|
else:
|
|
|
|
etud_ues_ids = res.etud_ues_ids(etud.id)
|
|
|
|
|
2024-03-01 12:40:05 +01:00
|
|
|
nbabsnj, nbabsjust, nbabs = formsemestre.get_abs_count(etud.id)
|
2022-05-18 20:43:01 +02:00
|
|
|
etud_groups = sco_groups.get_etud_formsemestre_groups(
|
|
|
|
etud, formsemestre, only_to_show=True
|
|
|
|
)
|
2021-12-16 12:41:37 +01:00
|
|
|
semestre_infos = {
|
2021-12-17 00:25:45 +01:00
|
|
|
"etapes": [str(x.etape_apo) for x in formsemestre.etapes if x.etape_apo],
|
2021-12-16 12:41:37 +01:00
|
|
|
"date_debut": formsemestre.date_debut.isoformat(),
|
|
|
|
"date_fin": formsemestre.date_fin.isoformat(),
|
2022-02-09 23:22:00 +01:00
|
|
|
"annee_universitaire": formsemestre.annee_scolaire_str(),
|
2021-12-16 12:41:37 +01:00
|
|
|
"numero": formsemestre.semestre_id,
|
2022-02-02 22:22:56 +01:00
|
|
|
"inscription": "", # inutilisé mais nécessaire pour le js de Seb.
|
2022-05-18 20:43:01 +02:00
|
|
|
"groupes": [group.to_dict() for group in etud_groups],
|
2022-03-15 21:50:37 +01:00
|
|
|
}
|
|
|
|
if self.prefs["bul_show_abs"]:
|
|
|
|
semestre_infos["absences"] = {
|
2024-03-01 12:40:05 +01:00
|
|
|
"injustifie": nbabsnj,
|
2022-01-26 00:11:04 +01:00
|
|
|
"total": nbabs,
|
2023-08-19 16:17:21 +02:00
|
|
|
"metrique": {
|
|
|
|
"H.": "Heure(s)",
|
|
|
|
"J.": "Journée(s)",
|
|
|
|
"1/2 J.": "1/2 Jour.",
|
|
|
|
}.get(sco_preferences.get_preference("assi_metrique")),
|
2022-03-15 21:50:37 +01:00
|
|
|
}
|
2023-02-19 23:06:44 +01:00
|
|
|
decisions_ues = self.res.get_etud_decisions_ue(etud.id) or {}
|
2022-03-15 21:50:37 +01:00
|
|
|
if self.prefs["bul_show_ects"]:
|
2022-07-18 17:48:10 +02:00
|
|
|
ects_tot = res.etud_ects_tot_sem(etud.id)
|
|
|
|
ects_acquis = res.get_etud_ects_valides(etud.id, decisions_ues)
|
2022-03-15 21:50:37 +01:00
|
|
|
semestre_infos["ECTS"] = {"acquis": ects_acquis, "total": ects_tot}
|
2022-06-30 15:43:48 +02:00
|
|
|
if sco_preferences.get_preference("bul_show_decision", formsemestre.id):
|
|
|
|
semestre_infos.update(
|
2022-07-08 18:09:45 +02:00
|
|
|
sco_bulletins_json.dict_decision_jury(etud, formsemestre)
|
2022-06-30 15:43:48 +02:00
|
|
|
)
|
2024-01-30 10:54:00 +01:00
|
|
|
if d["etat_inscription"] == scu.INSCRIT:
|
2022-03-15 22:24:52 +01:00
|
|
|
# moyenne des moyennes générales du semestre
|
|
|
|
semestre_infos["notes"] = {
|
|
|
|
"value": fmt_note(res.etud_moy_gen[etud.id]),
|
|
|
|
"min": fmt_note(res.etud_moy_gen.min()),
|
|
|
|
"moy": fmt_note(res.etud_moy_gen.mean()),
|
|
|
|
"max": fmt_note(res.etud_moy_gen.max()),
|
|
|
|
}
|
|
|
|
if self.prefs["bul_show_rangs"] and not np.isnan(res.etud_moy_gen[etud.id]):
|
2022-05-18 20:43:01 +02:00
|
|
|
# classement wrt moyenne générale, indicatif
|
2022-03-15 22:24:52 +01:00
|
|
|
semestre_infos["rang"] = {
|
|
|
|
"value": res.etud_moy_gen_ranks[etud.id],
|
|
|
|
"total": nb_inscrits,
|
2022-05-18 20:43:01 +02:00
|
|
|
"groupes": {},
|
2022-03-15 22:24:52 +01:00
|
|
|
}
|
2022-05-18 20:43:01 +02:00
|
|
|
# Rangs par groupes
|
|
|
|
for group in etud_groups:
|
|
|
|
if group.partition.bul_show_rank:
|
|
|
|
rang, effectif = self.res.get_etud_rang_group(etud.id, group.id)
|
|
|
|
semestre_infos["rang"]["groupes"][group.id] = {
|
|
|
|
"value": rang,
|
|
|
|
"total": effectif,
|
|
|
|
}
|
2022-03-15 22:24:52 +01:00
|
|
|
else:
|
|
|
|
semestre_infos["rang"] = {
|
|
|
|
"value": "-",
|
|
|
|
"total": nb_inscrits,
|
2022-05-18 20:43:01 +02:00
|
|
|
"groupes": {},
|
2022-03-15 22:24:52 +01:00
|
|
|
}
|
2021-12-16 12:41:37 +01:00
|
|
|
d.update(
|
|
|
|
{
|
2022-03-07 21:49:11 +01:00
|
|
|
"ressources": self.etud_mods_results(
|
|
|
|
etud, res.ressources, version=version
|
|
|
|
),
|
|
|
|
"saes": self.etud_mods_results(etud, res.saes, version=version),
|
2024-10-06 15:24:50 +02:00
|
|
|
"ues_capitalisees": (
|
|
|
|
self.etud_ues_capitalisees(etud)
|
|
|
|
if sco_preferences.get_preference(
|
|
|
|
"bul_show_ue_cap_but", formsemestre.id
|
|
|
|
)
|
|
|
|
else {}
|
|
|
|
),
|
2021-12-16 12:41:37 +01:00
|
|
|
"semestre": semestre_infos,
|
|
|
|
},
|
|
|
|
)
|
2022-09-04 23:19:13 +02:00
|
|
|
d_ues = {}
|
|
|
|
for ue in res.ues:
|
|
|
|
# si l'UE comporte des modules auxquels on est inscrit:
|
|
|
|
if (ue.type == UE_SPORT) or ue.id in etud_ues_ids:
|
|
|
|
ue_r = self.etud_ue_results(
|
|
|
|
etud,
|
|
|
|
ue,
|
|
|
|
decision_ue=decisions_ues.get(ue.id, {}),
|
|
|
|
etud_groups=etud_groups,
|
|
|
|
)
|
2022-09-14 21:44:38 +02:00
|
|
|
if ue_r: # exclu UE sport sans inscriptions
|
2022-09-04 23:19:13 +02:00
|
|
|
d_ues[ue.acronyme] = ue_r
|
|
|
|
d["ues"] = d_ues
|
2022-09-04 11:20:25 +02:00
|
|
|
|
2021-12-16 12:41:37 +01:00
|
|
|
else:
|
|
|
|
semestre_infos.update(
|
|
|
|
{
|
|
|
|
"notes": {
|
|
|
|
"value": "DEM",
|
|
|
|
"min": "",
|
|
|
|
"moy": "",
|
|
|
|
"max": "",
|
|
|
|
},
|
2022-01-16 23:47:52 +01:00
|
|
|
"rang": {"value": "DEM", "total": nb_inscrits},
|
2021-12-16 12:41:37 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
d.update(
|
|
|
|
{
|
|
|
|
"semestre": semestre_infos,
|
|
|
|
"ressources": {},
|
|
|
|
"saes": {},
|
|
|
|
"ues": {},
|
2023-01-09 21:04:25 +01:00
|
|
|
"ues_capitalisees": {},
|
2021-12-16 12:41:37 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2021-12-05 20:21:51 +01:00
|
|
|
return d
|
2022-02-14 23:21:42 +01:00
|
|
|
|
2022-03-10 09:28:59 +01:00
|
|
|
def bulletin_etud_complet(self, etud: Identite, version="long") -> dict:
|
2022-03-05 12:47:08 +01:00
|
|
|
"""Bulletin dict complet avec toutes les infos pour les bulletins BUT pdf
|
2023-03-18 21:56:08 +01:00
|
|
|
(pas utilisé pour json/html)
|
2022-03-05 12:47:08 +01:00
|
|
|
Résultat compatible avec celui de sco_bulletins.formsemestre_bulletinetud_dict
|
|
|
|
"""
|
2023-09-01 14:38:41 +02:00
|
|
|
d = self.bulletin_etud(etud, version=version, force_publishing=True)
|
2022-02-21 19:25:38 +01:00
|
|
|
d["etudid"] = etud.id
|
|
|
|
d["etud"] = d["etudiant"]
|
|
|
|
d["etud"]["nomprenom"] = etud.nomprenom
|
2023-03-13 06:39:36 +01:00
|
|
|
d["etud"]["etat_civil"] = etud.etat_civil
|
2022-02-21 19:25:38 +01:00
|
|
|
d.update(self.res.sem)
|
2022-03-05 12:47:08 +01:00
|
|
|
etud_etat = self.res.get_etud_etat(etud.id)
|
2024-01-17 23:52:14 +01:00
|
|
|
d["filigranne"] = sco_bulletins_pdf.get_filigranne_apc(
|
|
|
|
etud_etat, self.prefs, etud.id, res=self.res
|
2022-02-14 23:21:42 +01:00
|
|
|
)
|
2022-03-05 12:47:08 +01:00
|
|
|
if etud_etat == scu.DEMISSION:
|
|
|
|
d["demission"] = "(Démission)"
|
|
|
|
elif etud_etat == DEF:
|
|
|
|
d["demission"] = "(Défaillant)"
|
|
|
|
else:
|
|
|
|
d["demission"] = ""
|
|
|
|
|
2022-02-21 19:25:38 +01:00
|
|
|
# --- Absences
|
2024-03-01 12:40:05 +01:00
|
|
|
_, d["nbabsjust"], d["nbabs"] = self.res.formsemestre.get_abs_count(etud.id)
|
2022-03-05 12:47:08 +01:00
|
|
|
|
|
|
|
# --- Decision Jury
|
2023-09-14 12:08:20 +02:00
|
|
|
infos, _ = sco_bulletins.etud_descr_situation_semestre(
|
2022-03-05 12:47:08 +01:00
|
|
|
etud.id,
|
2023-03-08 22:56:11 +01:00
|
|
|
self.res.formsemestre,
|
2023-09-21 10:20:19 +02:00
|
|
|
fmt="html",
|
2022-03-05 12:47:08 +01:00
|
|
|
show_date_inscr=self.prefs["bul_show_date_inscr"],
|
|
|
|
show_decisions=self.prefs["bul_show_decision"],
|
|
|
|
show_uevalid=self.prefs["bul_show_uevalid"],
|
|
|
|
show_mention=self.prefs["bul_show_mention"],
|
|
|
|
)
|
|
|
|
d.update(infos)
|
2022-02-21 19:25:38 +01:00
|
|
|
# --- Rangs
|
2024-04-06 12:33:07 +02:00
|
|
|
d["rang_nt"] = (
|
|
|
|
f"{d['semestre']['rang']['value']} / {d['semestre']['rang']['total']}"
|
|
|
|
)
|
2022-02-21 19:25:38 +01:00
|
|
|
d["rang_txt"] = "Rang " + d["rang_nt"]
|
|
|
|
|
2022-03-05 12:47:08 +01:00
|
|
|
d.update(sco_bulletins.make_context_dict(self.res.formsemestre, d["etud"]))
|
|
|
|
|
2022-02-21 19:25:38 +01:00
|
|
|
return d
|