Skip to content

Commit 3addac3

Browse files
authored
feat(Emails): avoir les logs d'envois des TemplateTransactional (#1337)
1 parent 6d48f7f commit 3addac3

File tree

11 files changed

+208
-66
lines changed

11 files changed

+208
-66
lines changed

lemarche/conversations/admin.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from django.contrib import admin
22
from django.http import HttpResponseRedirect
33

4-
from lemarche.conversations.models import Conversation, TemplateTransactional
4+
from lemarche.conversations.models import Conversation, TemplateTransactional, TemplateTransactionalSendLog
55
from lemarche.utils.admin.admin_site import admin_site
66
from lemarche.utils.fields import pretty_print_readonly_jsonfield_to_table
77
from lemarche.www.conversations.tasks import send_first_email_from_conversation
@@ -144,3 +144,22 @@ class TemplateTransactionalAdmin(admin.ModelAdmin):
144144
("Paramètres d'envoi", {"fields": ("mailjet_id", "brevo_id", "source", "is_active")}),
145145
("Dates", {"fields": ("created_at", "updated_at")}),
146146
)
147+
148+
149+
@admin.register(TemplateTransactionalSendLog, site=admin_site)
150+
class TemplateTransactionalSendLogAdmin(admin.ModelAdmin):
151+
list_display = ["id", "template_transactional", "content_type", "created_at"]
152+
list_filter = [("content_type", admin.RelatedOnlyFieldListFilter)]
153+
search_fields = ["id", "template_transactional"]
154+
search_help_text = "Cherche sur les champs : ID, Template transactionnel"
155+
156+
readonly_fields = [field.name for field in TemplateTransactionalSendLog._meta.fields]
157+
158+
def has_add_permission(self, request):
159+
return False
160+
161+
def has_change_permission(self, request, obj=None):
162+
return False
163+
164+
def has_delete_permission(self, request, obj=None):
165+
return False
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Generated by Django 4.2.13 on 2024-07-15 10:43
2+
3+
import django.db.models.deletion
4+
import django.utils.timezone
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
dependencies = [
10+
("contenttypes", "0002_remove_content_type_name"),
11+
("conversations", "0015_remove_templatetransactional_email_from_email_and_more"),
12+
]
13+
14+
operations = [
15+
migrations.CreateModel(
16+
name="TemplateTransactionalSendLog",
17+
fields=[
18+
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
19+
("object_id", models.PositiveBigIntegerField(blank=True, null=True)),
20+
("extra_data", models.JSONField(default=dict, editable=False, verbose_name="Données complémentaires")),
21+
(
22+
"created_at",
23+
models.DateTimeField(default=django.utils.timezone.now, verbose_name="Date de création"),
24+
),
25+
("updated_at", models.DateTimeField(auto_now=True, verbose_name="Date de modification")),
26+
(
27+
"content_type",
28+
models.ForeignKey(
29+
blank=True,
30+
null=True,
31+
on_delete=django.db.models.deletion.CASCADE,
32+
to="contenttypes.contenttype",
33+
),
34+
),
35+
(
36+
"template_transactional",
37+
models.ForeignKey(
38+
null=True,
39+
on_delete=django.db.models.deletion.SET_NULL,
40+
related_name="send_logs",
41+
to="conversations.templatetransactional",
42+
verbose_name="Template transactionnel",
43+
),
44+
),
45+
],
46+
options={
47+
"verbose_name": "Template transactionnel: logs d'envois",
48+
"verbose_name_plural": "Templates transactionnels: logs d'envois",
49+
},
50+
),
51+
]

lemarche/conversations/models.py

+41-18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from uuid import uuid4
33

44
from django.conf import settings
5+
from django.contrib.contenttypes.fields import GenericForeignKey
6+
from django.contrib.contenttypes.models import ContentType
57
from django.db import IntegrityError, models
68
from django.db.models import Func, IntegerField, Q
79
from django.utils import timezone
@@ -254,6 +256,9 @@ def get_template_id(self):
254256
return self.brevo_id
255257
return None
256258

259+
def create_send_log(self, **kwargs):
260+
TemplateTransactionalSendLog.objects.create(template_transactional=self, **kwargs)
261+
257262
def send_transactional_email(
258263
self,
259264
recipient_email,
@@ -264,23 +269,41 @@ def send_transactional_email(
264269
from_name=settings.DEFAULT_FROM_NAME,
265270
):
266271
if self.is_active:
272+
args = {
273+
"template_id": self.get_template_id,
274+
"recipient_email": recipient_email,
275+
"recipient_name": recipient_name,
276+
"variables": variables,
277+
"subject": subject,
278+
"from_email": from_email,
279+
"from_name": from_name,
280+
}
267281
if self.source == conversation_constants.SOURCE_MAILJET:
268-
api_mailjet.send_transactional_email_with_template(
269-
template_id=self.get_template_id,
270-
recipient_email=recipient_email,
271-
recipient_name=recipient_name,
272-
variables=variables,
273-
subject=subject,
274-
from_email=from_email,
275-
from_name=from_name,
276-
)
282+
result = api_mailjet.send_transactional_email_with_template(**args)
277283
elif self.source == conversation_constants.SOURCE_BREVO:
278-
api_brevo.send_transactional_email_with_template(
279-
template_id=self.get_template_id,
280-
recipient_email=recipient_email,
281-
recipient_name=recipient_name,
282-
variables=variables,
283-
subject=subject,
284-
from_email=from_email,
285-
from_name=from_name,
286-
)
284+
result = api_brevo.send_transactional_email_with_template(**args)
285+
# create log
286+
self.create_send_log(extra_data={"source": self.source, "variables": args, "response": result()})
287+
288+
289+
class TemplateTransactionalSendLog(models.Model):
290+
template_transactional = models.ForeignKey(
291+
"conversations.TemplateTransactional",
292+
verbose_name="Template transactionnel",
293+
on_delete=models.SET_NULL,
294+
null=True,
295+
related_name="send_logs",
296+
)
297+
298+
content_type = models.ForeignKey(ContentType, blank=True, null=True, on_delete=models.CASCADE)
299+
object_id = models.PositiveBigIntegerField(blank=True, null=True)
300+
content_object = GenericForeignKey("content_type", "object_id")
301+
302+
extra_data = models.JSONField(verbose_name="Données complémentaires", editable=False, default=dict)
303+
304+
created_at = models.DateTimeField(verbose_name="Date de création", default=timezone.now)
305+
updated_at = models.DateTimeField(verbose_name="Date de modification", auto_now=True)
306+
307+
class Meta:
308+
verbose_name = "Template transactionnel: logs d'envois"
309+
verbose_name_plural = "Templates transactionnels: logs d'envois"

lemarche/siaes/models.py

+1
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,7 @@ class Siae(models.Model):
871871
"Nombre de besoins intéressés", help_text=RECALCULATED_FIELD_HELP_TEXT, default=0
872872
)
873873
logs = models.JSONField(verbose_name="Logs historiques", editable=False, default=list)
874+
transactional_send_logs = GenericRelation("conversations.TemplateTransactionalSendLog", related_query_name="siae")
874875
source = models.CharField(
875876
max_length=20, choices=siae_constants.SOURCE_CHOICES, default=siae_constants.SOURCE_STAFF_C4_CREATED
876877
)
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
La structure {{ siae_name|safe }} souhaite répondre en co-traitance
1+
La structure {{ SIAE_NAME|safe }} souhaite répondre en co-traitance
22

3-
Titre : {{ tender_title|safe }}
4-
Type : {{ tender_kind|safe }}
5-
Contact email de l'ESI: {{ siae_contact_email|safe }}
6-
SIRET : {{ siae_siret|safe }}
3+
Titre : {{ TENDER_TITLE|safe }}
4+
Type : {{ TENDER_KIND|safe }}
5+
Contact email de l'ESI: {{ SIAE_CONTACT_EMAIL|safe }}
6+
SIRET : {{ SIAE_SIRET|safe }}
77

8-
Lien dans l'admin : {{ tender_admin_url }}
8+
Lien dans l'admin : {{ TENDER_ADMIN_URL }}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
Dépôt de besoin : ajout d'un nouveau {{ tender_kind|safe }}
1+
Dépôt de besoin : ajout d'un nouveau {{ TENDER_KIND|safe }}
22

3-
titre : {{ tender_title|safe }}
4-
type : {{ tender_kind|safe }}
5-
lieu d'intervention : {{ tender_location|safe }}
6-
date de clôture des réponses : {{ tender_deadline_date|safe }}
7-
contact : {{ tender_author_full_name|safe }}
8-
entreprise : {{ tender_author_company|safe }}
9-
si le Marché n'existait pas, auriez-vous consulté des prestataires inclusifs ? : {{ tender_scale_marche_useless|safe }}
10-
status : {{ tender_status|safe }}
11-
source: {{ tender_source|safe }}
3+
titre : {{ TENDER_TITLE|safe }}
4+
type : {{ TENDER_KIND|safe }}
5+
lieu d'intervention : {{ TENDER_LOCATION|safe }}
6+
date de clôture des réponses : {{ TENDER_DEADLINE_DATE|safe }}
7+
contact : {{ TENDER_AUTHOR_FULL_NAME|safe }}
8+
entreprise : {{ TENDER_AUTHOR_COMPANY|safe }}
9+
si le Marché n'existait pas, auriez-vous consulté des prestataires inclusifs ? : {{ TENDER_SCALE_MARCHE_USELESS|safe }}
10+
status : {{ TENDER_STATUS|safe }}
11+
source: {{ TENDER_SOURCE|safe }}
1212

13-
Lien dans l'admin : {{ tender_admin_url }}
13+
Lien dans l'admin : {{ TENDER_ADMIN_URL }}

lemarche/users/models.py

+1
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ class User(AbstractUser):
275275
"Date de dernière visite sur la page 'besoins'", blank=True, null=True
276276
)
277277
extra_data = models.JSONField(verbose_name="Données complémentaires", editable=False, default=dict)
278+
transactional_send_logs = GenericRelation("conversations.TemplateTransactionalSendLog", related_query_name="user")
278279

279280
# is_active, is_staff, is_superuser
280281

lemarche/utils/apis/api_brevo.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,8 @@ def send_transactional_email_with_template(
343343
send_smtp_email = sib_api_v3_sdk.SendSmtpEmail(**data)
344344
response = api_instance.send_transac_email(send_smtp_email)
345345
logger.info("Brevo: send transactional email with template")
346-
return response
346+
# {'message_id': '<[email protected]>', 'message_ids': None}
347+
return response.to_dict()
347348
except ApiException as e:
348349
print(f"Exception when calling SMTPApi->send_transac_email: {e}")
349350
else:

lemarche/utils/apis/api_mailjet.py

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ def send_transactional_email_with_template(
127127
response = client.post(SEND_URL, json=data)
128128
response.raise_for_status()
129129
logger.info("Mailjet: send transactional email with template")
130+
# {'Messages': [{'Status': 'success', 'CustomID': '', 'To': [{'Email': '<recipient_email>', 'MessageUUID': '<uuid>', 'MessageID': <id>, 'MessageHref': 'https://api.mailjet.com/v3/REST/message/<id>'}], 'Cc': [], 'Bcc': []}]} # noqa
130131
return response.json()
131132
except requests.exceptions.HTTPError as e:
132133
logger.error("Error while fetching `%s`: %s", e.request.url, e)

lemarche/www/dashboard_siaes/tasks.py

+18
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ def send_siae_user_request_email_to_assignee(siae_user_request):
1717
recipient_name = siae_user_request.assignee.full_name
1818

1919
variables = {
20+
"ASSIGNEE_ID": siae_user_request.assignee.id,
2021
"ASSIGNEE_FULL_NAME": siae_user_request.assignee.full_name,
22+
"INITIATIOR_ID": siae_user_request.initiator.id,
2123
"INITIATOR_FULL_NAME": siae_user_request.initiator.full_name,
24+
"SIAE_ID": siae_user_request.siae.id,
2225
"SIAE_NAME": siae_user_request.siae.name_display,
2326
"SIAE_USERS_URL": f"https://{get_domain_url()}{reverse_lazy('dashboard_siaes:siae_users', args=[siae_user_request.siae.slug])}", # noqa
2427
}
@@ -57,8 +60,11 @@ def send_siae_user_request_response_email_to_initiator(siae_user_request):
5760
recipient_name = siae_user_request.initiator.full_name
5861

5962
variables = {
63+
"ASSIGNEE_ID": siae_user_request.assignee.id,
6064
"ASSIGNEE_FULL_NAME": siae_user_request.assignee.full_name,
65+
"INITIATIOR_ID": siae_user_request.initiator.id,
6166
"INITIATOR_FULL_NAME": siae_user_request.initiator.full_name,
67+
"SIAE_ID": siae_user_request.siae.id,
6268
"SIAE_NAME": siae_user_request.siae.name_display,
6369
"DASHBOARD_URL": f"https://{get_domain_url()}{reverse_lazy('dashboard:home')}",
6470
}
@@ -99,8 +105,11 @@ def send_siae_user_request_reminder_3_days_email_to_assignee(siae_user_request):
99105
recipient_name = siae_user_request.assignee.full_name
100106

101107
variables = {
108+
"ASSIGNEE_ID": siae_user_request.assignee.id,
102109
"ASSIGNEE_FULL_NAME": siae_user_request.assignee.full_name,
110+
"INITIATIOR_ID": siae_user_request.initiator.id,
103111
"INITIATOR_FULL_NAME": siae_user_request.initiator.full_name,
112+
"SIAE_ID": siae_user_request.siae.id,
104113
"SIAE_NAME": siae_user_request.siae.name_display,
105114
"SIAE_USERS_URL": f"https://{get_domain_url()}{reverse_lazy('dashboard_siaes:siae_users', args=[siae_user_request.siae.slug])}", # noqa
106115
}
@@ -131,8 +140,11 @@ def send_siae_user_request_reminder_3_days_email_to_initiator(siae_user_request)
131140
recipient_name = siae_user_request.initiator.full_name
132141

133142
variables = {
143+
"ASSIGNEE_ID": siae_user_request.assignee.id,
134144
"ASSIGNEE_FULL_NAME": siae_user_request.assignee.full_name,
145+
"INITIATIOR_ID": siae_user_request.initiator.id,
135146
"INITIATOR_FULL_NAME": siae_user_request.initiator.full_name,
147+
"SIAE_ID": siae_user_request.siae.id,
136148
"SIAE_NAME": siae_user_request.siae.name_display,
137149
}
138150

@@ -172,8 +184,11 @@ def send_siae_user_request_reminder_8_days_email_to_assignee(siae_user_request):
172184
recipient_name = siae_user_request.assignee.full_name
173185

174186
variables = {
187+
"ASSIGNEE_ID": siae_user_request.assignee.id,
175188
"ASSIGNEE_FULL_NAME": siae_user_request.assignee.full_name,
189+
"INITIATIOR_ID": siae_user_request.initiator.id,
176190
"INITIATOR_FULL_NAME": siae_user_request.initiator.full_name,
191+
"SIAE_ID": siae_user_request.siae.id,
177192
"SIAE_NAME": siae_user_request.siae.name_display,
178193
"SIAE_USERS_URL": f"https://{get_domain_url()}{reverse_lazy('dashboard_siaes:siae_users', args=[siae_user_request.siae.slug])}", # noqa
179194
}
@@ -204,8 +219,11 @@ def send_siae_user_request_reminder_8_days_email_to_initiator(siae_user_request)
204219
recipient_name = siae_user_request.initiator.full_name
205220

206221
variables = {
222+
"ASSIGNEE_ID": siae_user_request.assignee.id,
207223
"ASSIGNEE_FULL_NAME": siae_user_request.assignee.full_name,
224+
"INITIATIOR_ID": siae_user_request.initiator.id,
208225
"INITIATOR_FULL_NAME": siae_user_request.initiator.full_name,
226+
"SIAE_ID": siae_user_request.siae.id,
209227
"SIAE_NAME": siae_user_request.siae.name_display,
210228
"SUPPORT_URL": f"https://{get_domain_url()}{reverse_lazy('pages:contact')}?siret={siae_user_request.siae.siret}", # noqa
211229
}

0 commit comments

Comments
 (0)