From 462ca6a0695adea799b015520116447a04c4d92f Mon Sep 17 00:00:00 2001 From: alan Date: Tue, 17 Dec 2024 17:24:41 +0100 Subject: [PATCH] Modification du contenu des emails de notifications (template HTML) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajoute un template HTML avec style unifié pour tous les emails de notifications - Inclut les informations de l'expéditeur et le lien vers la fiche - Styles CSS appliqués directement dans les balises HTML pour maximiser la compatibilité avec les différents clients email (cf. score Mailtrap) - Dans les tests de notifications, renomme les variables pour éviter l'ambiguïté entre le contenu du message et l'objet Message lui-même : * 'message' en 'content' pour le contenu textuel * 'instance' en 'message_obj' pour l'objet Message - Ajout de tests pour notification AC et compte rendu Ticket Notion : https://www.notion.so/incubateur-masa/Mail-au-format-HTML-pour-respecter-les-maquettes-150de24614be80d0921bd8463ded61d8?pvs=4 PR : https://github.com/betagouv/seves/pull/551 --- core/notifications.py | 87 ++++++++++--------- sv/tests/test_notifications.py | 151 ++++++++++++++++++++++++--------- 2 files changed, 159 insertions(+), 79 deletions(-) diff --git a/core/notifications.py b/core/notifications.py index c539df2a..75f99d74 100644 --- a/core/notifications.py +++ b/core/notifications.py @@ -1,56 +1,65 @@ from post_office.mail import send +from post_office.models import EmailTemplate from core.constants import MUS_STRUCTURE from core.models import Message, Contact from django.conf import settings -def _send_message(recipients: list[str], copy: list[str], subject, message, instance): - message += ( - f"\n\n Pour voir la fiche concernée par cette notification, consultez SEVES : " - f'{settings.ROOT_URL}{instance.content_object.get_absolute_url()}' +def _send_message(recipients: list[str], copy: list[str], subject: str, content: str, message_obj: Message): + template, _ = EmailTemplate.objects.update_or_create( + name="seves_email_template", + defaults={ + "subject": f"Sèves - {message_obj.content_object.numero} - {message_obj.message_type} - {subject}", + "html_content": """ + + +
+

{{ content }}

+

{{ message_obj.sender.agent.prenom }} {{ message_obj.sender.agent.nom }}

+

{{ message_obj.sender.agent.structure }}

+

Consulter la fiche dans Sèves : {{ fiche_url }}

+
+ + """, + }, ) send( recipients=recipients, cc=copy, sender="no-reply@beta.gouv.fr", - subject=f"SEVES - {subject}", - html_message=message, + template=template, + context={ + "message_obj": message_obj, + "content": content, + "fiche_url": f"{settings.ROOT_URL}{message_obj.content_object.get_absolute_url()}", + }, ) -def notify_message(instance: Message): +def notify_message(message_obj: Message): recipients, copy = [], [] - message, subject = None, None - if instance.message_type == Message.MESSAGE: - subject = instance.title - message = f"Bonjour,\n Vous avez reçu un message sur SEVES dont voici le contenu : \n {instance.content}" - recipients = [r.email for r in instance.recipients.all()] - copy = [r.email for r in instance.recipients_copy.all()] - elif instance.message_type == Message.COMPTE_RENDU: - subject = instance.title - message = f"Bonjour,\n Vous avez reçu un compte rendu sur demande d'intervention sur SEVES dont voici le contenu : \n {instance.content}" - recipients = [r.email for r in instance.recipients.all()] - elif instance.message_type == Message.DEMANDE_INTERVENTION: - subject = instance.title - message = "Bonjour,\n Vous avez reçu un message sur SEVES." - recipients = [r.email for r in instance.recipients.structures_only()] - copy = [r.email for r in instance.recipients_copy.structures_only()] - elif instance.message_type == Message.POINT_DE_SITUATION: - subject = instance.title - message = "Bonjour,\n Vous avez reçu un nouveau point de suivi sur SEVES." - recipients = [c.email for c in instance.content_object.contacts.agents_only()] - elif instance.message_type == Message.FIN_SUIVI: - subject = instance.title - message = "Bonjour,\n Vous avez reçu un nouveau point de suivi sur SEVES." - recipients = instance.content_object.contacts.agents_only().filter(agent__structure__niveau2=MUS_STRUCTURE) - recipients = [r.email for r in recipients] - elif instance.message_type == Message.NOTIFICATION_AC: - subject = instance.title - message = ( - f"Bonjour,\n une personne a déclarée la fiche {instance.content_object.numero} à l'administration centrale." - ) - recipients = [Contact.objects.get_mus().email, Contact.objects.get_bsv().email] + content = message_obj.content - if recipients and message: - _send_message(recipients, copy, subject=subject, message=message, instance=instance) + match message_obj.message_type: + case Message.MESSAGE: + recipients = [r.email for r in message_obj.recipients.all()] + copy = [r.email for r in message_obj.recipients_copy.all()] + case Message.COMPTE_RENDU: + recipients = [r.email for r in message_obj.recipients.all()] + case Message.DEMANDE_INTERVENTION: + recipients = [r.email for r in message_obj.recipients.structures_only()] + copy = [r.email for r in message_obj.recipients_copy.structures_only()] + case Message.POINT_DE_SITUATION: + recipients = [c.email for c in message_obj.content_object.contacts.agents_only()] + case Message.FIN_SUIVI: + recipients = message_obj.content_object.contacts.agents_only().filter( + agent__structure__niveau2=MUS_STRUCTURE + ) + recipients = [r.email for r in recipients] + case Message.NOTIFICATION_AC: + content = f"Bonjour,\nLa fiche {message_obj.content_object.numero} vient d'être déclarée à l'administration centrale." + recipients = [Contact.objects.get_mus().email, Contact.objects.get_bsv().email] + + if recipients and content: + _send_message(recipients, copy, subject=message_obj.title, content=content, message_obj=message_obj) diff --git a/sv/tests/test_notifications.py b/sv/tests/test_notifications.py index d2ff14a4..40450268 100644 --- a/sv/tests/test_notifications.py +++ b/sv/tests/test_notifications.py @@ -1,6 +1,8 @@ import pytest +from django.utils import html from model_bakery import baker +from core.constants import AC_STRUCTURE, MUS_STRUCTURE, BSV_STRUCTURE from core.models import Structure, Agent, Contact, Message from core.notifications import notify_message @@ -28,16 +30,16 @@ def test_notification_message(mailoutbox, fiche_detection): notify_message(message) assert len(mailoutbox) == 1 - message = mailoutbox[0] - assert message.subject == "SEVES - TITLE" - assert ( - message.body - == f"Bonjour,\n Vous avez reçu un message sur SEVES dont voici le contenu : \n My message \n Thanks\n\n Pour voir la fiche concernée par cette notification, consultez SEVES : " - f'http://testserver.com/sv/fiches-detection/{fiche_detection.pk}/' - ) - assert message.from_email == "no-reply@beta.gouv.fr" - assert set(message.to) == {contact_1.email, contact_2.email} - assert set(message.cc) == {contact_3.email, contact_4.email} + mail = mailoutbox[0] + assert mail.subject == f"Sèves - {fiche_detection.numero} - {message.message_type} - {message.title}" + assert message.content in mail.body + assert message.sender.agent.prenom in mail.body + assert message.sender.agent.nom in mail.body + assert str(message.sender.agent.structure) in mail.body + assert fiche_detection.get_absolute_url() in mail.body + assert mail.from_email == "no-reply@beta.gouv.fr" + assert set(mail.to) == {contact_1.email, contact_2.email} + assert set(mail.cc) == {contact_3.email, contact_4.email} @pytest.mark.django_db @@ -63,16 +65,16 @@ def test_notification_demande_intervention(mailoutbox, fiche_detection): notify_message(message) assert len(mailoutbox) == 1 - message = mailoutbox[0] - assert message.subject == "SEVES - TITLE" - assert ( - message.body - == f"Bonjour,\n Vous avez reçu un message sur SEVES.\n\n Pour voir la fiche concernée par cette notification, consultez SEVES : " - f'http://testserver.com/sv/fiches-detection/{fiche_detection.pk}/' - ) - assert message.from_email == "no-reply@beta.gouv.fr" - assert set(message.to) == {structure_1.email} - assert set(message.cc) == {structure_2.email} + mail = mailoutbox[0] + assert mail.subject == f"Sèves - {fiche_detection.numero} - {message.message_type} - {message.title}" + assert message.content in mail.body + assert message.sender.agent.prenom in mail.body + assert message.sender.agent.nom in mail.body + assert str(message.sender.agent.structure) in mail.body + assert fiche_detection.get_absolute_url() in mail.body + assert mail.from_email == "no-reply@beta.gouv.fr" + assert set(mail.to) == {structure_1.email} + assert set(mail.cc) == {structure_2.email} @pytest.mark.django_db @@ -95,22 +97,22 @@ def test_notification_point_de_situation(mailoutbox, fiche_detection): notify_message(message) assert len(mailoutbox) == 1 - message = mailoutbox[0] - assert message.subject == "SEVES - TITLE" - assert ( - message.body - == f"Bonjour,\n Vous avez reçu un nouveau point de suivi sur SEVES.\n\n Pour voir la fiche concernée par cette notification, consultez SEVES : " - f'http://testserver.com/sv/fiches-detection/{fiche_detection.pk}/' - ) - assert message.from_email == "no-reply@beta.gouv.fr" - assert set(message.to) == {agent_1.email} - assert set(message.cc) == set() + mail = mailoutbox[0] + assert mail.subject == f"Sèves - {fiche_detection.numero} - {message.message_type} - {message.title}" + assert message.content in mail.body + assert message.sender.agent.prenom in mail.body + assert message.sender.agent.nom in mail.body + assert str(message.sender.agent.structure) in mail.body + assert fiche_detection.get_absolute_url() in mail.body + assert mail.from_email == "no-reply@beta.gouv.fr" + assert set(mail.to) == {agent_1.email} + assert set(mail.cc) == set() @pytest.mark.django_db def test_notification_fin_de_suivi(mailoutbox, fiche_detection): sender = baker.make(Contact, agent=baker.make(Agent)) - agent_1 = baker.make(Contact, agent=baker.make(Agent, structure__niveau2="MUS")) + agent_1 = baker.make(Contact, agent=baker.make(Agent, structure__niveau2=MUS_STRUCTURE)) agent_2 = baker.make(Contact, agent=baker.make(Agent, structure__niveau2="FOO")) structure_1 = baker.make(Contact, structure=baker.make(Structure)) @@ -126,13 +128,82 @@ def test_notification_fin_de_suivi(mailoutbox, fiche_detection): notify_message(message) assert len(mailoutbox) == 1 - message = mailoutbox[0] - assert message.subject == "SEVES - TITLE" - assert ( - message.body - == f"Bonjour,\n Vous avez reçu un nouveau point de suivi sur SEVES.\n\n Pour voir la fiche concernée par cette notification, consultez SEVES : " - f'http://testserver.com/sv/fiches-detection/{fiche_detection.pk}/' + mail = mailoutbox[0] + assert mail.subject == f"Sèves - {fiche_detection.numero} - {message.message_type} - {message.title}" + assert message.content in mail.body + assert message.sender.agent.prenom in mail.body + assert message.sender.agent.nom in mail.body + assert str(message.sender.agent.structure) in mail.body + assert fiche_detection.get_absolute_url() in mail.body + assert mail.from_email == "no-reply@beta.gouv.fr" + assert set(mail.to) == {agent_1.email} + assert set(mail.cc) == set() + + +def test_notification_notification_ac(mailoutbox, fiche_detection): + sender = baker.make(Contact, agent=baker.make(Agent)) + agent_1 = baker.make(Contact, agent=baker.make(Agent)) + structure_1 = baker.make(Contact, structure=baker.make(Structure)) + contact_mus = baker.make(Contact, structure=baker.make(Structure, niveau1=AC_STRUCTURE, niveau2=MUS_STRUCTURE)) + contact_bsv = baker.make(Contact, structure=baker.make(Structure, niveau1=AC_STRUCTURE, niveau2=BSV_STRUCTURE)) + + message = Message.objects.create( + title="TITLE", + content="My message \n Thanks", + sender=sender, + message_type=Message.NOTIFICATION_AC, + content_object=fiche_detection, + ) + message.recipients.set([agent_1, structure_1]) + + notify_message(message) + + assert len(mailoutbox) == 1 + mail = mailoutbox[0] + + assert mail.subject == f"Sèves - {fiche_detection.numero} - {message.message_type} - {message.title}" + + expected_content = f"Bonjour,\nLa fiche {fiche_detection.numero} vient d'être déclarée à l'administration centrale." + assert html.escape(expected_content) in mail.body + assert message.content not in mail.body + + assert message.sender.agent.prenom in mail.body + assert message.sender.agent.nom in mail.body + assert str(message.sender.agent.structure) in mail.body + assert fiche_detection.get_absolute_url() in mail.body + + assert mail.from_email == "no-reply@beta.gouv.fr" + assert set(mail.to) == {contact_mus.email, contact_bsv.email} + assert set(mail.cc) == set() + + +def test_notification_compte_rendu(mailoutbox, fiche_detection): + sender = baker.make(Contact, agent=baker.make(Agent)) + contact_mus = baker.make(Contact, structure=baker.make(Structure, niveau1=AC_STRUCTURE, niveau2=MUS_STRUCTURE)) + contact_bsv = baker.make(Contact, structure=baker.make(Structure, niveau1=AC_STRUCTURE, niveau2=BSV_STRUCTURE)) + + message = Message.objects.create( + title="TITLE", + content="My message \n Thanks", + sender=sender, + message_type=Message.COMPTE_RENDU, + content_object=fiche_detection, ) - assert message.from_email == "no-reply@beta.gouv.fr" - assert set(message.to) == {agent_1.email} - assert set(message.cc) == set() + message.recipients.set([contact_mus, contact_bsv]) + + notify_message(message) + + assert len(mailoutbox) == 1 + mail = mailoutbox[0] + + assert mail.subject == f"Sèves - {fiche_detection.numero} - {message.message_type} - {message.title}" + + assert message.content in mail.body + assert message.sender.agent.prenom in mail.body + assert message.sender.agent.nom in mail.body + assert str(message.sender.agent.structure) in mail.body + assert fiche_detection.get_absolute_url() in mail.body + + assert mail.from_email == "no-reply@beta.gouv.fr" + assert set(mail.to) == {contact_mus.email, contact_bsv.email} + assert set(mail.cc) == set()