From 6c3d13b3e16f5c2b8b9a8c8b29b9fef00bccd13b Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 9 Oct 2023 23:01:19 +0200 Subject: [PATCH] Assiduite: notifications mail. Fix #789 --- app/api/assiduites.py | 3 +- app/models/assiduites.py | 46 +++++++++++------------- app/scodoc/sco_abs_notification.py | 58 +++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 44 deletions(-) diff --git a/app/api/assiduites.py b/app/api/assiduites.py index e6cf873c6..1b8eec7aa 100644 --- a/app/api/assiduites.py +++ b/app/api/assiduites.py @@ -559,7 +559,7 @@ def _create_singular( data: dict, etud: Identite, ) -> tuple[int, object]: - """TODO: documenter""" + """TODO @iziram: documenter""" errors: list[str] = [] # -- vérifications de l'objet json -- @@ -630,6 +630,7 @@ def _create_singular( description=desc, user_id=current_user.id, external_data=external_data, + notify_mail=True, ) db.session.add(nouv_assiduite) diff --git a/app/models/assiduites.py b/app/models/assiduites.py index 3474318ac..766196c35 100644 --- a/app/models/assiduites.py +++ b/app/models/assiduites.py @@ -7,6 +7,7 @@ from app import db, log from app.models import ModuleImpl, Scolog from app.models.etudiants import Identite from app.auth.models import User +from app.scodoc import sco_abs_notification from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_utils import ( EtatAssiduite, @@ -130,13 +131,14 @@ class Assiduite(db.Model): user_id: int = None, est_just: bool = False, external_data: dict = None, + notify_mail=False, ) -> "Assiduite": """Créer une nouvelle assiduité pour l'étudiant""" # Vérification de non duplication des périodes assiduites: Query = etud.assiduites if is_period_conflicting(date_debut, date_fin, assiduites, Assiduite): raise ScoValueError( - "Duplication des assiduités (la période rentrée rentre en conflit avec une assiduité enregistrée)" + "Duplication: la période rentre en conflit avec une plage enregistrée" ) if not est_just: @@ -147,35 +149,25 @@ class Assiduite(db.Model): > 0 ) + moduleimpl_id = None if moduleimpl is not None: - # Vérification de l'existence du module pour l'étudiant + # Vérification de l'inscription de l'étudiant if moduleimpl.est_inscrit(etud): - nouv_assiduite = Assiduite( - date_debut=date_debut, - date_fin=date_fin, - etat=etat, - etudiant=etud, - moduleimpl_id=moduleimpl.id, - description=description, - entry_date=entry_date, - user_id=user_id, - est_just=est_just, - external_data=external_data, - ) + moduleimpl_id = moduleimpl.id else: raise ScoValueError("L'étudiant n'est pas inscrit au module") - else: - nouv_assiduite = Assiduite( - date_debut=date_debut, - date_fin=date_fin, - etat=etat, - etudiant=etud, - description=description, - entry_date=entry_date, - user_id=user_id, - est_just=est_just, - external_data=external_data, - ) + nouv_assiduite = Assiduite( + date_debut=date_debut, + date_fin=date_fin, + description=description, + entry_date=entry_date, + est_just=est_just, + etat=etat, + etudiant=etud, + external_data=external_data, + moduleimpl_id=moduleimpl_id, + user_id=user_id, + ) db.session.add(nouv_assiduite) log(f"create_assiduite: {etud.id} {nouv_assiduite}") Scolog.logdb( @@ -183,6 +175,8 @@ class Assiduite(db.Model): etudid=etud.id, msg=f"assiduité: {nouv_assiduite}", ) + if notify_mail and etat == EtatAssiduite.ABSENT: + sco_abs_notification.abs_notify(etud.id, nouv_assiduite.date_debut) return nouv_assiduite diff --git a/app/scodoc/sco_abs_notification.py b/app/scodoc/sco_abs_notification.py index 7ff95d772..cb51e7be5 100644 --- a/app/scodoc/sco_abs_notification.py +++ b/app/scodoc/sco_abs_notification.py @@ -40,17 +40,17 @@ from flask_mail import Message from app import db from app import email from app import log +from app.auth.models import User from app.models.absences import AbsenceNotification from app.models.events import Scolog from app.models.formsemestre import FormSemestre import app.scodoc.notesdb as ndb from app.scodoc import sco_etud from app.scodoc import sco_preferences -from app.scodoc import sco_users from app.scodoc import sco_utils as scu -def abs_notify(etudid, date): +def abs_notify(etudid: int, date: str | datetime.datetime): """Check if notifications are requested and send them Considère le nombre d'absence dans le semestre courant (s'il n'y a pas de semestre courant, ne fait rien, @@ -64,18 +64,28 @@ def abs_notify(etudid, date): nbabs, nbabsjust = sco_assiduites.get_assiduites_count_in_interval( etudid, - formsemestre.date_debut.isoformat(), - formsemestre.date_fin.isoformat(), - scu.translate_assiduites_metric( + metrique=scu.translate_assiduites_metric( sco_preferences.get_preference( "assi_metrique", formsemestre.formsemestre_id ) ), + date_debut=datetime.datetime.combine( + formsemestre.date_debut, datetime.datetime.min.time() + ), + date_fin=datetime.datetime.combine( + formsemestre.date_fin, datetime.datetime.min.time() + ), ) do_abs_notify(formsemestre, etudid, date, nbabs, nbabsjust) -def do_abs_notify(formsemestre: FormSemestre, etudid, date, nbabs, nbabsjust): +def do_abs_notify( + formsemestre: FormSemestre, + etudid: int, + date: str | datetime.datetime, + nbabs: int, + nbabsjust: int, +): """Given new counts of absences, check if notifications are requested and send them.""" # prefs fallback to global pref if sem is None: if formsemestre: @@ -138,8 +148,13 @@ def abs_notify_send(destinations, etudid, msg, nbabs, nbabsjust, formsemestre_id def abs_notify_get_destinations( - formsemestre: FormSemestre, prefs, etudid, date, nbabs, nbabsjust -) -> set: + formsemestre: FormSemestre, + prefs: dict, + etudid: int, + date: str | datetime.datetime, + nbabs: int, + nbabsjust: int, +) -> set[str]: """Returns set of destination emails to be notified""" destinations = [] # list of email address to notify @@ -165,9 +180,9 @@ def abs_notify_get_destinations( if prefs["abs_notify_respeval"]: mods = mod_with_evals_at_date(date, etudid) for mod in mods: - u = sco_users.user_info(mod["responsable_id"]) - if u["email"]: - destinations.append(u["email"]) + u: User = db.session.get(User, mod["responsable_id"]) + if u is not None and u.is_active and u.email: + destinations.append(u.email) # uniq destinations = set(destinations) @@ -267,9 +282,11 @@ def abs_notification_message( return msg -def retreive_current_formsemestre(etudid: int, cur_date) -> Optional[FormSemestre]: +def retreive_current_formsemestre( + etudid: int, cur_date: str | datetime.date +) -> Optional[FormSemestre]: """Get formsemestre dans lequel etudid est (ou était) inscrit a la date indiquée - date est une chaine au format ISO (yyyy-mm-dd) + date est une chaine au format ISO (yyyy-mm-dd) ou un datetime.date Result: FormSemestre ou None si pas inscrit à la date indiquée """ @@ -287,10 +304,17 @@ def retreive_current_formsemestre(etudid: int, cur_date) -> Optional[FormSemestr return formsemestre -def mod_with_evals_at_date(date_abs, etudid): +def mod_with_evals_at_date( + date_abs: str | datetime.datetime, etudid: int +) -> list[dict]: """Liste des moduleimpls avec des evaluations à la date indiquée""" - req = """SELECT m.id AS moduleimpl_id, m.* + req = """ + SELECT m.id AS moduleimpl_id, m.* FROM notes_moduleimpl m, notes_evaluation e, notes_moduleimpl_inscription i - WHERE m.id = e.moduleimpl_id AND e.moduleimpl_id = i.moduleimpl_id - AND i.etudid = %(etudid)s AND e.jour = %(date_abs)s""" + WHERE m.id = e.moduleimpl_id + AND e.moduleimpl_id = i.moduleimpl_id + AND i.etudid = %(etudid)s + AND e.date_debut <= %(date_abs)s + AND e.date_fin >= %(date_abs)s + """ return ndb.SimpleDictFetch(req, {"etudid": etudid, "date_abs": date_abs})