diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index a1e67d338..0fe70efa3 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -1097,10 +1097,12 @@ def mail_bulletin(formsemestre_id, infos, pdfdata, filename, recipient_addr): hea = "" if sco_preferences.get_preference("bul_mail_list_abs"): - hea += "\n\n" + "(LISTE D'ABSENCES NON DISPONIBLE)" # XXX TODO-ASSIDUITE - # sco_abs_views.ListeAbsEtud( - # etud["etudid"], with_evals=False, format="text" - # ) + from app.views.assiduites import generate_bul_list + + etud_identite: Identite = Identite.get_etud(etud["etudid"]) + form_semestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id) + hea += "\n\n" + hea += generate_bul_list(etud_identite, form_semestre) subject = f"""Relevé de notes de {etud["nomprenom"]}""" recipients = [recipient_addr] diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index 1acfc5dab..e7514ed80 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -1750,6 +1750,17 @@ class BasePreferences: "category": "bul_mail", }, ), + ( + "bul_mail_list_abs_nb", + { + "initvalue": 10, + "title": "Nombre maximum de dates par catégorie", + "explanation": "dans la liste des absences dans le mail envoyant le bulletin de notes (catégories : abs,abs_just, retard,justificatifs)", + "type": "int", + "size": 3, + "category": "bul_mail", + }, + ), ( "bul_mail_contact_addr", { diff --git a/app/templates/assiduites/widgets/liste_assiduites_mail.j2 b/app/templates/assiduites/widgets/liste_assiduites_mail.j2 new file mode 100644 index 000000000..33254d652 --- /dev/null +++ b/app/templates/assiduites/widgets/liste_assiduites_mail.j2 @@ -0,0 +1,16 @@ +<=== Assiduité ===> +--- Absences non justifiées {{stats.absent[metric] - stats.absent.justifie[metric]}} ({{metrique}}) --- +{% for assi in abs_nj %}- Absence non just. {{assi.date}} +{% endfor %} + +--- Absences justifiées {{stats.absent.justifie[metric]}} ({{metrique}}) --- +{% for assi in abs_j %}- Absence just. {{assi.date}} +{% endfor %} + +--- Retard {{stats.retard[metric]}} ({{metrique}}) --- +{% for assi in retards %}- Retard {{assi.date}} +{% endfor %} + +--- Justificatif --- +{% for justi in justifs %}- Justificatif {{justi.date}} {{justi.raison}} : {{justi.etat}} +{% endfor %} \ No newline at end of file diff --git a/app/views/assiduites.py b/app/views/assiduites.py index b037b10ac..654873b0a 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -16,6 +16,7 @@ from app.models import ( Identite, ScoDocSiteConfig, Assiduite, + Justificatif, Departement, FormSemestreInscription, Evaluation, @@ -1082,7 +1083,7 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None): scodoc_dept=g.scodoc_dept, duplication="oui", ) - raise ScoValueError(msg, dest) + raise ScoValueError(msg, dest) from see db.session.add(assiduite_unique) db.session.commit() @@ -1098,9 +1099,81 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None): ) +def generate_bul_list(etud: Identite, semestre: FormSemestre) -> str: + """Génère la liste des assiduités d'un étudiant pour le bulletin mail""" + metrique: str = scu.translate_assiduites_metric( + sco_preferences.get_preference("assi_metrique", formsemestre_id=semestre.id), + ) + + max_nb: int = int( + sco_preferences.get_preference( + "bul_mail_list_abs_nb", formsemestre_id=semestre.id + ) + ) + + assiduites = scass.filter_by_formsemestre( + etud.assiduites, Assiduite, semestre + ).order_by(Assiduite.entry_date.desc()) + justificatifs = scass.filter_by_formsemestre( + etud.justificatifs, Justificatif, semestre + ).order_by(Justificatif.entry_date.desc()) + + stats: dict = scass.get_assiduites_stats( + assiduites, metric=metrique, filtered={"split": True} + ) + + abs_j: list[str] = [ + {"date": _get_date_str(assi.date_debut, assi.date_fin)} + for assi in assiduites + if assi.etat == scu.EtatAssiduite.ABSENT and assi.est_just is True + ] + abs_nj: list[str] = [ + {"date": _get_date_str(assi.date_debut, assi.date_fin)} + for assi in assiduites + if assi.etat == scu.EtatAssiduite.ABSENT and assi.est_just is False + ] + retards: list[str] = [ + {"date": _get_date_str(assi.date_debut, assi.date_fin)} + for assi in assiduites + if assi.etat == scu.EtatAssiduite.RETARD + ] + + justifs: list[dict[str, str]] = [ + { + "date": _get_date_str(justi.date_debut, justi.date_fin), + "raison": "" if justi.raison is None else justi.raison, + "etat": { + scu.EtatJustificatif.VALIDE: "justificatif valide", + scu.EtatJustificatif.NON_VALIDE: "justificatif invalide", + scu.EtatJustificatif.ATTENTE: "justificatif en attente de validation", + scu.EtatJustificatif.MODIFIE: "justificatif ayant été modifié", + }.get(justi.etat), + } + for justi in justificatifs + ] + + return render_template( + "assiduites/widgets/liste_assiduites_mail.j2", + abs_j=abs_j[:max_nb], + abs_nj=abs_nj[:max_nb], + retards=retards[:max_nb], + justifs=justifs[:max_nb], + stats=stats, + metrique=scu.translate_assiduites_metric(metrique, short=True, inverse=False), + metric=metrique, + ) + + # --- Fonctions internes --- +def _get_date_str(deb: datetime.datetime, fin: datetime.datetime) -> str: + if deb.date() == fin.date(): + temps = deb.strftime("%d/%m/%Y %H:%M").split(" ") + [fin.strftime("%H:%M")] + return f"le {temps[0]} de {temps[1]} à {temps[2]}" + return f'du {deb.strftime("%d/%m/%Y %H:%M")} au {fin.strftime("%d/%m/%Y %H:%M")}' + + def _get_days_between_dates(deb: str, fin: str): if deb is None or fin is None: return "null"