wbmailing 2.2.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of wbmailing might be problematic. Click here for more details.
- wbmailing/__init__.py +1 -0
- wbmailing/admin.py +74 -0
- wbmailing/apps.py +14 -0
- wbmailing/backend.py +131 -0
- wbmailing/celery.py +0 -0
- wbmailing/dynamic_preferences_registry.py +35 -0
- wbmailing/factories.py +211 -0
- wbmailing/filters/__init__.py +8 -0
- wbmailing/filters/mailing_lists.py +84 -0
- wbmailing/filters/mails.py +74 -0
- wbmailing/management/__init__.py +22 -0
- wbmailing/migrations/0001_initial_squashed_squashed_0008_alter_mail_bcc_email_alter_mail_cc_email_and_more.py +649 -0
- wbmailing/migrations/0002_delete_mailingsettings.py +16 -0
- wbmailing/migrations/0003_alter_mailinglistsubscriberchangerequest_options.py +25 -0
- wbmailing/migrations/__init__.py +0 -0
- wbmailing/models/__init__.py +6 -0
- wbmailing/models/mailing_lists.py +386 -0
- wbmailing/models/mails.py +895 -0
- wbmailing/serializers/__init__.py +19 -0
- wbmailing/serializers/mailing_lists.py +209 -0
- wbmailing/serializers/mails.py +251 -0
- wbmailing/tasks.py +37 -0
- wbmailing/templatetags/__init__.py +0 -0
- wbmailing/templatetags/mailing_tags.py +22 -0
- wbmailing/tests/__init__.py +0 -0
- wbmailing/tests/conftest.py +30 -0
- wbmailing/tests/models/__init__.py +0 -0
- wbmailing/tests/models/test_mailing_lists.py +297 -0
- wbmailing/tests/models/test_mails.py +205 -0
- wbmailing/tests/signals.py +124 -0
- wbmailing/tests/test_serializers.py +28 -0
- wbmailing/tests/test_tasks.py +49 -0
- wbmailing/tests/test_viewsets.py +216 -0
- wbmailing/tests/tests.py +142 -0
- wbmailing/urls.py +90 -0
- wbmailing/viewsets/__init__.py +32 -0
- wbmailing/viewsets/analytics.py +110 -0
- wbmailing/viewsets/buttons/__init__.py +10 -0
- wbmailing/viewsets/buttons/mailing_lists.py +91 -0
- wbmailing/viewsets/buttons/mails.py +98 -0
- wbmailing/viewsets/display/__init__.py +16 -0
- wbmailing/viewsets/display/mailing_lists.py +175 -0
- wbmailing/viewsets/display/mails.py +318 -0
- wbmailing/viewsets/endpoints/__init__.py +8 -0
- wbmailing/viewsets/endpoints/mailing_lists.py +86 -0
- wbmailing/viewsets/endpoints/mails.py +51 -0
- wbmailing/viewsets/mailing_lists.py +320 -0
- wbmailing/viewsets/mails.py +425 -0
- wbmailing/viewsets/menu/__init__.py +5 -0
- wbmailing/viewsets/menu/mailing_lists.py +37 -0
- wbmailing/viewsets/menu/mails.py +25 -0
- wbmailing/viewsets/titles/__init__.py +17 -0
- wbmailing/viewsets/titles/mailing_lists.py +63 -0
- wbmailing/viewsets/titles/mails.py +55 -0
- wbmailing-2.2.1.dist-info/METADATA +5 -0
- wbmailing-2.2.1.dist-info/RECORD +57 -0
- wbmailing-2.2.1.dist-info/WHEEL +5 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import plotly.graph_objects as go
|
|
3
|
+
from django.contrib.messages import info
|
|
4
|
+
from django.db.models import CharField, Count, OuterRef, Q, Subquery, Value
|
|
5
|
+
from django.db.models.functions import Coalesce
|
|
6
|
+
from django.shortcuts import get_object_or_404
|
|
7
|
+
from django.utils.functional import cached_property
|
|
8
|
+
from django.utils.translation import gettext
|
|
9
|
+
from rest_framework import filters, status
|
|
10
|
+
from rest_framework.decorators import action
|
|
11
|
+
from rest_framework.response import Response
|
|
12
|
+
from wbcore import viewsets
|
|
13
|
+
from wbcore.contrib.directory.models import EmailContact
|
|
14
|
+
from wbcore.filters import DjangoFilterBackend
|
|
15
|
+
from wbcore.utils.strings import format_number
|
|
16
|
+
from wbmailing import models, serializers
|
|
17
|
+
from wbmailing.filters import MailFilter, MailStatusMassMailFilterSet, MassMailFilterSet
|
|
18
|
+
from wbmailing.models import MailEvent
|
|
19
|
+
from wbmailing.viewsets.buttons import (
|
|
20
|
+
MailButtonConfig,
|
|
21
|
+
MailStatusMassMailButtonConfig,
|
|
22
|
+
MassMailButtonConfig,
|
|
23
|
+
)
|
|
24
|
+
from wbmailing.viewsets.display import (
|
|
25
|
+
MailDisplayConfig,
|
|
26
|
+
MailEventDisplayConfig,
|
|
27
|
+
MailStatusMassMailDisplayConfig,
|
|
28
|
+
MailTemplateDisplayConfig,
|
|
29
|
+
MassMailDisplayConfig,
|
|
30
|
+
)
|
|
31
|
+
from wbmailing.viewsets.endpoints import (
|
|
32
|
+
MailEventMailEndpointConfig,
|
|
33
|
+
MailEventMassMailEndpointConfig,
|
|
34
|
+
MailMailingListChartEndpointConfig,
|
|
35
|
+
MailStatusMassMailEndpointConfig,
|
|
36
|
+
)
|
|
37
|
+
from wbmailing.viewsets.titles import (
|
|
38
|
+
MailEventMailTitleConfig,
|
|
39
|
+
MailEventMassMailTitleConfig,
|
|
40
|
+
MailMailingListChartTitleConfig,
|
|
41
|
+
MailStatusMassMailTitleConfig,
|
|
42
|
+
MailTemplateTitleConfig,
|
|
43
|
+
MailTitleConfig,
|
|
44
|
+
MassMailTitleConfig,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MailTemplateRepresentationViewSet(viewsets.RepresentationViewSet):
|
|
49
|
+
IDENTIFIER = "wbmailing:mailtemplate"
|
|
50
|
+
|
|
51
|
+
filter_backends = (
|
|
52
|
+
filters.OrderingFilter,
|
|
53
|
+
filters.SearchFilter,
|
|
54
|
+
)
|
|
55
|
+
ordering_fields = ordering = ("title",)
|
|
56
|
+
search_fields = ("title",)
|
|
57
|
+
|
|
58
|
+
queryset = models.MailTemplate.objects.all()
|
|
59
|
+
serializer_class = serializers.MailTemplateRepresentationSerializer
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class MailRepresentationViewSet(viewsets.RepresentationViewSet):
|
|
63
|
+
IDENTIFIER = "wbmailing:mail"
|
|
64
|
+
queryset = models.Mail.objects.all()
|
|
65
|
+
serializer_class = serializers.MailRepresentationSerializer
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class MassMailRepresentationViewSet(viewsets.RepresentationViewSet):
|
|
69
|
+
IDENTIFIER = "wbmailing:massmail"
|
|
70
|
+
|
|
71
|
+
filter_backends = (
|
|
72
|
+
filters.OrderingFilter,
|
|
73
|
+
filters.SearchFilter,
|
|
74
|
+
)
|
|
75
|
+
ordering_fields = ("subject",)
|
|
76
|
+
search_fields = ("subject",)
|
|
77
|
+
queryset = models.MassMail.objects.all()
|
|
78
|
+
serializer_class = serializers.MassMailRepresentationSerializer
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class MailMailingListChartViewSet(viewsets.ChartViewSet):
|
|
82
|
+
IDENTIFIER = "mailing:maileventchart"
|
|
83
|
+
|
|
84
|
+
queryset = models.Mail.objects.all()
|
|
85
|
+
|
|
86
|
+
title_config_class = MailMailingListChartTitleConfig
|
|
87
|
+
endpoint_config_class = MailMailingListChartEndpointConfig
|
|
88
|
+
|
|
89
|
+
def get_plotly(self, queryset):
|
|
90
|
+
mass_mail_name_map = dict(models.MassMail.objects.values_list("id", "subject"))
|
|
91
|
+
fig = go.Figure()
|
|
92
|
+
if queryset.exists():
|
|
93
|
+
df = pd.DataFrame(queryset.values("mass_mail", "CLICKED", "DELIVERED", "OPENED"))
|
|
94
|
+
df["TOTAL"] = 1
|
|
95
|
+
df = df.groupby("mass_mail").sum()
|
|
96
|
+
total = df["TOTAL"]
|
|
97
|
+
del df["TOTAL"]
|
|
98
|
+
df["FAILED"] = total - df["DELIVERED"]
|
|
99
|
+
df.index.name = None
|
|
100
|
+
df = df.divide(total.values, axis=0)
|
|
101
|
+
df["title"] = df.index
|
|
102
|
+
df.title = df.title.map(mass_mail_name_map)
|
|
103
|
+
df.index = range(df.shape[0])
|
|
104
|
+
for col in df.columns:
|
|
105
|
+
if col != "title":
|
|
106
|
+
fig.add_trace(
|
|
107
|
+
go.Scatter(
|
|
108
|
+
x=df.index,
|
|
109
|
+
y=df[col],
|
|
110
|
+
mode="lines",
|
|
111
|
+
name=str(models.MailEvent.EventType[col].label),
|
|
112
|
+
marker_color=models.MailEvent.EventType.get_color(col),
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
fig.update_layout(
|
|
116
|
+
xaxis=dict(tickmode="array", tickvals=df.index, ticktext=df.title), yaxis=dict(tickformat=".1%")
|
|
117
|
+
)
|
|
118
|
+
return fig
|
|
119
|
+
|
|
120
|
+
def get_queryset(self):
|
|
121
|
+
return models.Mail.objects.filter(
|
|
122
|
+
mass_mail__isnull=False, mass_mail__mailing_lists=self.kwargs["mailing_list_id"]
|
|
123
|
+
).annotate(
|
|
124
|
+
CLICKED=Coalesce(
|
|
125
|
+
Subquery(
|
|
126
|
+
MailEvent.objects.filter(mail=OuterRef("pk"), event_type=MailEvent.EventType.CLICKED)
|
|
127
|
+
.annotate(c=Count("event_type"))
|
|
128
|
+
.values("c")[:1]
|
|
129
|
+
),
|
|
130
|
+
0,
|
|
131
|
+
),
|
|
132
|
+
OPENED=Coalesce(
|
|
133
|
+
Subquery(
|
|
134
|
+
MailEvent.objects.filter(mail=OuterRef("pk"), event_type=MailEvent.EventType.OPENED)
|
|
135
|
+
.annotate(c=Count("event_type"))
|
|
136
|
+
.values("c")[:1]
|
|
137
|
+
),
|
|
138
|
+
0,
|
|
139
|
+
),
|
|
140
|
+
DELIVERED=Coalesce(
|
|
141
|
+
Subquery(
|
|
142
|
+
MailEvent.objects.filter(mail=OuterRef("pk"), event_type=MailEvent.EventType.DELIVERED)
|
|
143
|
+
.annotate(c=Count("event_type"))
|
|
144
|
+
.values("c")[:1]
|
|
145
|
+
),
|
|
146
|
+
0,
|
|
147
|
+
),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class MassMailModelViewSet(viewsets.ModelViewSet):
|
|
152
|
+
queryset = models.MassMail.objects.select_related("creator", "template").prefetch_related(
|
|
153
|
+
"mailing_lists",
|
|
154
|
+
"excluded_mailing_lists",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
filterset_class = MassMailFilterSet
|
|
158
|
+
ordering_fields = (
|
|
159
|
+
"created",
|
|
160
|
+
"subject",
|
|
161
|
+
"from_email",
|
|
162
|
+
"template__title",
|
|
163
|
+
"creator__computed_str",
|
|
164
|
+
"sent",
|
|
165
|
+
"opened",
|
|
166
|
+
)
|
|
167
|
+
ordering = ("-created",)
|
|
168
|
+
search_fields = ("subject",)
|
|
169
|
+
|
|
170
|
+
display_config_class = MassMailDisplayConfig
|
|
171
|
+
title_config_class = MassMailTitleConfig
|
|
172
|
+
button_config_class = MassMailButtonConfig
|
|
173
|
+
|
|
174
|
+
def get_serializer_class(self):
|
|
175
|
+
if getattr(self, "action", None) == "list":
|
|
176
|
+
return serializers.MassMailListSerializer
|
|
177
|
+
return serializers.MassMailModelSerializer
|
|
178
|
+
|
|
179
|
+
@action(methods=["GET", "PATCH"], detail=True)
|
|
180
|
+
def sendtestmail(self, request, pk=None):
|
|
181
|
+
mass_mail = self.get_object()
|
|
182
|
+
email = request.POST.get("to_test_email", request.user.email)
|
|
183
|
+
|
|
184
|
+
mass_mail.send_test_mail(email)
|
|
185
|
+
|
|
186
|
+
return Response({"__notification": {"title": gettext("Mail sent.")}}, status=status.HTTP_200_OK)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class MailModelViewSet(viewsets.ModelViewSet):
|
|
190
|
+
queryset = models.Mail.objects.select_related(
|
|
191
|
+
"mass_mail",
|
|
192
|
+
"template",
|
|
193
|
+
).prefetch_related(
|
|
194
|
+
"to_email",
|
|
195
|
+
"cc_email",
|
|
196
|
+
"bcc_email",
|
|
197
|
+
)
|
|
198
|
+
serializer_class = serializers.MailModelSerializer
|
|
199
|
+
filterset_class = MailFilter
|
|
200
|
+
|
|
201
|
+
ordering_fields = ("created", "subject", "mass_mail__subject", "from_email", "last_send")
|
|
202
|
+
ordering = "-last_send"
|
|
203
|
+
search_fields = ("subject", "from_email", "mass_mail__subject", "to_email__address")
|
|
204
|
+
|
|
205
|
+
display_config_class = MailDisplayConfig
|
|
206
|
+
title_config_class = MailTitleConfig
|
|
207
|
+
button_config_class = MailButtonConfig
|
|
208
|
+
|
|
209
|
+
def get_queryset(self):
|
|
210
|
+
user = self.request.user
|
|
211
|
+
emails = user.profile.emails.values("address")
|
|
212
|
+
qs = super().get_queryset()
|
|
213
|
+
if not user.has_perm("wbmailing:view_all_mails") and not user.is_superuser:
|
|
214
|
+
qs = qs.annotate(
|
|
215
|
+
is_in_charge_to_email=Coalesce(
|
|
216
|
+
Subquery(
|
|
217
|
+
EmailContact.objects.filter(mail_to=OuterRef("pk"))
|
|
218
|
+
.filter(Q(entry__relationship_managers=user.profile) | Q(address__in=emails))
|
|
219
|
+
.values("mail_to")
|
|
220
|
+
.annotate(number_of_charges=Count("*"))
|
|
221
|
+
.values("number_of_charges")[:1]
|
|
222
|
+
),
|
|
223
|
+
0,
|
|
224
|
+
),
|
|
225
|
+
is_in_charge_cc_email=Coalesce(
|
|
226
|
+
Subquery(
|
|
227
|
+
EmailContact.objects.filter(mail_cc=OuterRef("pk"))
|
|
228
|
+
.filter(Q(entry__relationship_managers=user.profile) | Q(address__in=emails))
|
|
229
|
+
.values("mail_cc")
|
|
230
|
+
.annotate(number_of_charges=Count("*"))
|
|
231
|
+
.values("number_of_charges")[:1]
|
|
232
|
+
),
|
|
233
|
+
0,
|
|
234
|
+
),
|
|
235
|
+
is_in_charge_bcc_email=Coalesce(
|
|
236
|
+
Subquery(
|
|
237
|
+
EmailContact.objects.filter(mail_bcc=OuterRef("pk"))
|
|
238
|
+
.filter(Q(entry__relationship_managers=user.profile) | Q(address__in=emails))
|
|
239
|
+
.values("mail_bcc")
|
|
240
|
+
.annotate(number_of_charges=Count("*"))
|
|
241
|
+
.values("number_of_charges")[:1]
|
|
242
|
+
),
|
|
243
|
+
0,
|
|
244
|
+
),
|
|
245
|
+
).filter(
|
|
246
|
+
Q(is_in_charge_to_email__gt=0)
|
|
247
|
+
| Q(is_in_charge_cc_email__gt=0)
|
|
248
|
+
| Q(is_in_charge_bcc_email__gt=0)
|
|
249
|
+
| Q(mass_mail__isnull=False)
|
|
250
|
+
)
|
|
251
|
+
return self.annotate(qs)
|
|
252
|
+
|
|
253
|
+
@action(methods=["GET", "PATCH"], detail=True)
|
|
254
|
+
def resend(self, request, pk=None):
|
|
255
|
+
mail = self.get_object()
|
|
256
|
+
mail.resend()
|
|
257
|
+
return Response({"__notification": {"title": gettext("Mail sent.")}}, status=status.HTTP_200_OK)
|
|
258
|
+
|
|
259
|
+
@classmethod
|
|
260
|
+
def annotate(cls, qs):
|
|
261
|
+
return qs.annotate(
|
|
262
|
+
status=Coalesce(
|
|
263
|
+
Subquery(
|
|
264
|
+
models.MailEvent.objects.filter(mail=OuterRef("pk"))
|
|
265
|
+
.order_by("-timestamp")
|
|
266
|
+
.values("event_type")[:1]
|
|
267
|
+
),
|
|
268
|
+
None,
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class MailStatusMassMailModelViewSet(viewsets.ModelViewSet):
|
|
274
|
+
IDENTIFIER = "wbmailing:massmail"
|
|
275
|
+
|
|
276
|
+
queryset = EmailContact.objects.select_related("entry")
|
|
277
|
+
serializer_class = serializers.MailStatusMassMailModelSerializer
|
|
278
|
+
filterset_class = MailStatusMassMailFilterSet
|
|
279
|
+
|
|
280
|
+
ordering_fields = ("address", "entry__computed_str")
|
|
281
|
+
ordering = ("address",)
|
|
282
|
+
search_fields = ("address", "entry__computed_str")
|
|
283
|
+
|
|
284
|
+
display_config_class = MailStatusMassMailDisplayConfig
|
|
285
|
+
title_config_class = MailStatusMassMailTitleConfig
|
|
286
|
+
endpoint_config_class = MailStatusMassMailEndpointConfig
|
|
287
|
+
button_config_class = MailStatusMassMailButtonConfig
|
|
288
|
+
|
|
289
|
+
def add_messages(
|
|
290
|
+
self,
|
|
291
|
+
request,
|
|
292
|
+
queryset=None,
|
|
293
|
+
paginated_queryset=None,
|
|
294
|
+
instance=None,
|
|
295
|
+
initial=False,
|
|
296
|
+
):
|
|
297
|
+
df = pd.DataFrame(
|
|
298
|
+
MailEvent.objects.filter(mail__mass_mail=self.kwargs["mass_mail_id"]).values(
|
|
299
|
+
"event_type",
|
|
300
|
+
"recipient",
|
|
301
|
+
"timestamp",
|
|
302
|
+
),
|
|
303
|
+
columns=["event_type", "recipient", "timestamp"],
|
|
304
|
+
)
|
|
305
|
+
df = df.sort_values(by="timestamp", ascending=False).groupby("recipient").first().reset_index()
|
|
306
|
+
total_mails = df.shape[0]
|
|
307
|
+
if not df.empty and total_mails:
|
|
308
|
+
nb_created = df[df["event_type"] == MailEvent.EventType.CREATED.value].shape[0]
|
|
309
|
+
nb_queued = df[df["event_type"] == MailEvent.EventType.QUEUED.value].shape[0]
|
|
310
|
+
nb_opened = df[df["event_type"] == MailEvent.EventType.OPENED.value].shape[0]
|
|
311
|
+
nb_clicked = df[df["event_type"] == MailEvent.EventType.CLICKED.value].shape[0]
|
|
312
|
+
nb_delivered = df[df["event_type"] == MailEvent.EventType.DELIVERED.value].shape[0]
|
|
313
|
+
nb_received = nb_opened + nb_delivered + nb_clicked
|
|
314
|
+
msg = f"""
|
|
315
|
+
<p> Total Sent: {total_mails}</p>
|
|
316
|
+
<p> Total Received: {format_number(nb_received / total_mails):.2%}</p>
|
|
317
|
+
<ul>
|
|
318
|
+
<li> Only Created: {format_number(nb_created / total_mails):.2%} </li>
|
|
319
|
+
<li> Only Queued: {format_number(nb_queued / total_mails):.2%} </li>
|
|
320
|
+
<li> Delivered: {format_number(nb_delivered / total_mails):.2%} </li>
|
|
321
|
+
<li> Opened: {format_number(nb_opened / total_mails):.2%} </li>
|
|
322
|
+
<li> Clicked: {format_number(nb_clicked / total_mails):.2%} </li>
|
|
323
|
+
<li> Not Received: {format_number(1 - nb_received / total_mails):.2%} </li>
|
|
324
|
+
</ul>
|
|
325
|
+
"""
|
|
326
|
+
info(request, msg, extra_tags="auto_close=0")
|
|
327
|
+
|
|
328
|
+
@cached_property
|
|
329
|
+
def mass_mail(self):
|
|
330
|
+
return get_object_or_404(models.MassMail, id=self.kwargs["mass_mail_id"])
|
|
331
|
+
|
|
332
|
+
@cached_property
|
|
333
|
+
def included_mailing_list_ids(self):
|
|
334
|
+
return self.mass_mail.mailing_lists.values_list("id")
|
|
335
|
+
|
|
336
|
+
@cached_property
|
|
337
|
+
def excluded_mailing_list_ids(self):
|
|
338
|
+
return self.mass_mail.excluded_mailing_lists.values_list("id")
|
|
339
|
+
|
|
340
|
+
def get_queryset(self):
|
|
341
|
+
mass_mail = self.mass_mail
|
|
342
|
+
return (
|
|
343
|
+
models.MassMail.get_emails(self.included_mailing_list_ids, self.excluded_mailing_list_ids)
|
|
344
|
+
.annotate(
|
|
345
|
+
mail_id=Subquery(
|
|
346
|
+
models.Mail.objects.filter(mass_mail=mass_mail, to_email__id=OuterRef("id"))
|
|
347
|
+
.order_by("-created")
|
|
348
|
+
.values("id")[:1],
|
|
349
|
+
output_field=CharField(),
|
|
350
|
+
),
|
|
351
|
+
status=Coalesce(
|
|
352
|
+
Subquery(
|
|
353
|
+
models.MailEvent.objects.filter(recipient=OuterRef("address"), mail__mass_mail=mass_mail)
|
|
354
|
+
.order_by("-timestamp")
|
|
355
|
+
.values("event_type")[:1]
|
|
356
|
+
),
|
|
357
|
+
Value("NOT_SENT"),
|
|
358
|
+
),
|
|
359
|
+
)
|
|
360
|
+
.select_related("entry")
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
@action(methods=["PATCH"], detail=False)
|
|
364
|
+
def resendbouncedmails(self, request, mass_mail_id=None):
|
|
365
|
+
mass_mail = models.MassMail.objects.get(id=mass_mail_id)
|
|
366
|
+
unsent_emails = MailModelViewSet.annotate(mass_mail.mails.all()).filter(
|
|
367
|
+
status__in=[
|
|
368
|
+
models.MailEvent.EventType.BOUNCED,
|
|
369
|
+
models.MailEvent.EventType.REJECTED,
|
|
370
|
+
models.MailEvent.EventType.DEFERRED,
|
|
371
|
+
models.MailEvent.EventType.UNKNOWN,
|
|
372
|
+
]
|
|
373
|
+
)
|
|
374
|
+
for mail in unsent_emails.all():
|
|
375
|
+
mail.resend()
|
|
376
|
+
return Response(
|
|
377
|
+
{"__notification": {"title": gettext("Unsent emails resent with success")}}, status=status.HTTP_200_OK
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class MailTemplateModelViewSet(viewsets.ModelViewSet):
|
|
382
|
+
IDENTIFIER = "wbmailing:mailtemplate"
|
|
383
|
+
|
|
384
|
+
queryset = models.MailTemplate.objects.all()
|
|
385
|
+
serializer_class = serializers.MailTemplateModelSerializer
|
|
386
|
+
|
|
387
|
+
display_config_class = MailTemplateDisplayConfig
|
|
388
|
+
title_config_class = MailTemplateTitleConfig
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class MailEventModelViewSet(viewsets.ModelViewSet):
|
|
392
|
+
serializer_class = serializers.MailEventModelSerializer
|
|
393
|
+
filter_backends = (DjangoFilterBackend, filters.OrderingFilter)
|
|
394
|
+
queryset = MailEvent.objects.select_related("mail")
|
|
395
|
+
|
|
396
|
+
filterset_fields = {
|
|
397
|
+
"timestamp": ["gte", "exact", "lte"],
|
|
398
|
+
"event_type": ["exact"],
|
|
399
|
+
"reject_reason": ["exact"],
|
|
400
|
+
"recipient": ["exact"],
|
|
401
|
+
"click_url": ["exact", "icontains"],
|
|
402
|
+
"description": ["exact", "icontains"],
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
ordering_fields = ("timestamp",)
|
|
406
|
+
ordering = ("-timestamp",)
|
|
407
|
+
search_fields = ("description",)
|
|
408
|
+
|
|
409
|
+
display_config_class = MailEventDisplayConfig
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
class MailEventMailModelViewSet(MailEventModelViewSet):
|
|
413
|
+
endpoint_config_class = MailEventMailEndpointConfig
|
|
414
|
+
title_config_class = MailEventMailTitleConfig
|
|
415
|
+
|
|
416
|
+
def get_queryset(self):
|
|
417
|
+
return models.MailEvent.objects.filter(mail_id=self.kwargs["mail_id"])
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class MailEventMassMailMailModelViewSet(MailEventModelViewSet):
|
|
421
|
+
title_config_class = MailEventMassMailTitleConfig
|
|
422
|
+
endpoint_config_class = MailEventMassMailEndpointConfig
|
|
423
|
+
|
|
424
|
+
def get_queryset(self):
|
|
425
|
+
return models.MailEvent.objects.filter(mail__mass_mail=self.kwargs["mass_mail_id"])
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from django.utils.translation import gettext as _
|
|
2
|
+
from wbcore.menus import ItemPermission, MenuItem
|
|
3
|
+
from wbcore.permissions.shortcuts import is_internal_user
|
|
4
|
+
from wbmailing.models.mailing_lists import MailingListSubscriberChangeRequest
|
|
5
|
+
|
|
6
|
+
MAILINGLIST_MENUITEM = MenuItem(
|
|
7
|
+
label=_("Mailing Lists"),
|
|
8
|
+
endpoint="wbmailing:mailinglist-list",
|
|
9
|
+
permission=ItemPermission(
|
|
10
|
+
method=lambda request: is_internal_user(request.user), permissions=["wbmailing.view_mailinglist"]
|
|
11
|
+
),
|
|
12
|
+
add=MenuItem(
|
|
13
|
+
label=_("Create Mailing List"),
|
|
14
|
+
endpoint="wbmailing:mailinglist-list",
|
|
15
|
+
permission=ItemPermission(
|
|
16
|
+
method=lambda request: is_internal_user(request.user), permissions=["wbmailing.add_mailinglist"]
|
|
17
|
+
),
|
|
18
|
+
),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
MAILINGLISTSUBSCRIPTIONCHANGEREQUEST_MENUITEM = MenuItem(
|
|
22
|
+
label=_("Subscription Requests"),
|
|
23
|
+
endpoint="wbmailing:mailinglistsubscriberchangerequest-list",
|
|
24
|
+
endpoint_get_parameters={"status": MailingListSubscriberChangeRequest.Status.PENDING.value},
|
|
25
|
+
permission=ItemPermission(
|
|
26
|
+
method=lambda request: is_internal_user(request.user),
|
|
27
|
+
permissions=["wbmailing.view_mailinglistsubscriberchangerequest"],
|
|
28
|
+
),
|
|
29
|
+
add=MenuItem(
|
|
30
|
+
label=_("Create Subscription Request"),
|
|
31
|
+
endpoint="wbmailing:mailinglistsubscriberchangerequest-list",
|
|
32
|
+
permission=ItemPermission(
|
|
33
|
+
method=lambda request: is_internal_user(request.user),
|
|
34
|
+
permissions=["wbmailing.add_mailinglistsubscriberchangerequest"],
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from django.utils.translation import gettext as _
|
|
2
|
+
from wbcore.menus import ItemPermission, MenuItem
|
|
3
|
+
from wbcore.permissions.shortcuts import is_internal_user
|
|
4
|
+
|
|
5
|
+
MASSMAIL_MENUITEM = MenuItem(
|
|
6
|
+
label=_("Mass Mail"),
|
|
7
|
+
endpoint="wbmailing:massmail-list",
|
|
8
|
+
permission=ItemPermission(
|
|
9
|
+
method=lambda request: is_internal_user(request.user), permissions=["wbmailing.view_massmail"]
|
|
10
|
+
),
|
|
11
|
+
add=MenuItem(
|
|
12
|
+
label=_("Create Mass Mail"),
|
|
13
|
+
endpoint="wbmailing:massmail-list",
|
|
14
|
+
permission=ItemPermission(
|
|
15
|
+
method=lambda request: is_internal_user(request.user), permissions=["wbmailing.add_massmail"]
|
|
16
|
+
),
|
|
17
|
+
),
|
|
18
|
+
)
|
|
19
|
+
MAIL_MENUITEM = MenuItem(
|
|
20
|
+
label=_("Mails"),
|
|
21
|
+
endpoint="wbmailing:mail-list",
|
|
22
|
+
permission=ItemPermission(
|
|
23
|
+
method=lambda request: is_internal_user(request.user), permissions=["wbmailing.view_mail"]
|
|
24
|
+
),
|
|
25
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .mailing_lists import (
|
|
2
|
+
EmailContactMailingListTitleConfig,
|
|
3
|
+
MailingListEntryTitleConfig,
|
|
4
|
+
MailingListSubscriberChangeRequestTitleConfig,
|
|
5
|
+
MailingListSubscriberRequestEntryTitleConfig,
|
|
6
|
+
MailingListTitleConfig,
|
|
7
|
+
MailMailingListChartTitleConfig,
|
|
8
|
+
UnsubscribedEmailContactMailingListTitleConfig,
|
|
9
|
+
)
|
|
10
|
+
from .mails import (
|
|
11
|
+
MailEventMailTitleConfig,
|
|
12
|
+
MailEventMassMailTitleConfig,
|
|
13
|
+
MailStatusMassMailTitleConfig,
|
|
14
|
+
MailTemplateTitleConfig,
|
|
15
|
+
MailTitleConfig,
|
|
16
|
+
MassMailTitleConfig,
|
|
17
|
+
)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from django.utils.translation import gettext as _
|
|
2
|
+
from wbcore.contrib.directory.models import Entry
|
|
3
|
+
from wbcore.metadata.configs.titles import TitleViewConfig
|
|
4
|
+
from wbmailing import models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MailingListTitleConfig(TitleViewConfig):
|
|
8
|
+
def get_instance_title(self):
|
|
9
|
+
return _("Mailing List: {{title}}")
|
|
10
|
+
|
|
11
|
+
def get_list_title(self):
|
|
12
|
+
return _("Mailing Lists")
|
|
13
|
+
|
|
14
|
+
def get_create_title(self):
|
|
15
|
+
return _("New Mailing List")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MailingListSubscriberChangeRequestTitleConfig(TitleViewConfig):
|
|
19
|
+
def get_instance_title(self):
|
|
20
|
+
return _("Mailing List Subscriber Change Request")
|
|
21
|
+
|
|
22
|
+
def get_list_title(self):
|
|
23
|
+
return _("Mailing List Subscriber Change Requests")
|
|
24
|
+
|
|
25
|
+
def get_create_title(self):
|
|
26
|
+
return _("New Mailing List Subscriber Change Request")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class EmailContactMailingListTitleConfig(TitleViewConfig):
|
|
30
|
+
def get_instance_title(self):
|
|
31
|
+
return _("Email Contact: {{address}}")
|
|
32
|
+
|
|
33
|
+
def get_list_title(self):
|
|
34
|
+
mailinglist = models.MailingList.objects.get(id=self.view.kwargs["mailing_list_id"])
|
|
35
|
+
return _("Subscribed Emails for {}").format(mailinglist.title)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class MailMailingListChartTitleConfig(TitleViewConfig):
|
|
39
|
+
def get_list_title(self):
|
|
40
|
+
mailinglist = models.MailingList.objects.get(id=self.view.kwargs["mailing_list_id"])
|
|
41
|
+
return _("Mail Penetration for {}").format(mailinglist.title)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class UnsubscribedEmailContactMailingListTitleConfig(EmailContactMailingListTitleConfig):
|
|
45
|
+
def get_list_title(self):
|
|
46
|
+
mailinglist = models.MailingList.objects.get(id=self.view.kwargs["mailing_list_id"])
|
|
47
|
+
return _("Unsubscribed Emails for {}").format(mailinglist.title)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class MailingListEntryTitleConfig(MailingListTitleConfig):
|
|
51
|
+
def get_list_title(self):
|
|
52
|
+
entry = Entry.objects.get(id=self.view.kwargs["entry_id"])
|
|
53
|
+
return _("Subscribed Mailing Lists of {}").format(entry.computed_str)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class MailingListSubscriberRequestEntryTitleConfig(MailingListSubscriberChangeRequestTitleConfig):
|
|
57
|
+
def get_list_title(self):
|
|
58
|
+
entry = Entry.objects.get(id=self.view.kwargs["entry_id"])
|
|
59
|
+
return _("Mailing Subscription Requests for {}").format(entry.computed_str)
|
|
60
|
+
|
|
61
|
+
def get_create_title(self):
|
|
62
|
+
entry = Entry.objects.get(id=self.view.kwargs["entry_id"])
|
|
63
|
+
return _("New Mailing Subscription Request for {}").format(entry.computed_str)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from django.utils.translation import gettext as _
|
|
2
|
+
from wbcore.metadata.configs.titles import TitleViewConfig
|
|
3
|
+
from wbmailing import models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MassMailTitleConfig(TitleViewConfig):
|
|
7
|
+
def get_instance_title(self):
|
|
8
|
+
return _("Mass Mail: {{subject}}")
|
|
9
|
+
|
|
10
|
+
def get_list_title(self):
|
|
11
|
+
return _("Mass Mails")
|
|
12
|
+
|
|
13
|
+
def get_create_title(self):
|
|
14
|
+
return _("New Mass Mail")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MailTitleConfig(TitleViewConfig):
|
|
18
|
+
def get_instance_title(self):
|
|
19
|
+
return _("Mail: {{subject}}")
|
|
20
|
+
|
|
21
|
+
def get_list_title(self):
|
|
22
|
+
return _("Mails")
|
|
23
|
+
|
|
24
|
+
def get_create_title(self):
|
|
25
|
+
return _("Write E-Mail")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MailStatusMassMailTitleConfig(TitleViewConfig):
|
|
29
|
+
def get_instance_title(self):
|
|
30
|
+
return _("Mail: {{subject}}")
|
|
31
|
+
|
|
32
|
+
def get_list_title(self):
|
|
33
|
+
massmail = models.MassMail.objects.get(id=self.view.kwargs["mass_mail_id"])
|
|
34
|
+
return _("Mails' Status for {}").format(massmail.subject)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MailTemplateTitleConfig(TitleViewConfig):
|
|
38
|
+
def get_instance_title(self):
|
|
39
|
+
return _("Mail Template: {{title}}")
|
|
40
|
+
|
|
41
|
+
def get_list_title(self):
|
|
42
|
+
return _("Mail Templates")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MailEventMailTitleConfig(TitleViewConfig):
|
|
46
|
+
def get_list_title(self):
|
|
47
|
+
mail = models.Mail.objects.get(id=self.view.kwargs["mail_id"])
|
|
48
|
+
emails = ", ".join([email.address for email in mail.to_email.all()])
|
|
49
|
+
return _("Event for Mail {} to {} ({})").format(mail.subject, emails, mail.created)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class MailEventMassMailTitleConfig(TitleViewConfig):
|
|
53
|
+
def get_list_title(self):
|
|
54
|
+
massmail = models.MassMail.objects.get(id=self.view.kwargs["mass_mail_id"])
|
|
55
|
+
return _("Event for Mass Mail {}").format(str(massmail))
|