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.

Files changed (57) hide show
  1. wbmailing/__init__.py +1 -0
  2. wbmailing/admin.py +74 -0
  3. wbmailing/apps.py +14 -0
  4. wbmailing/backend.py +131 -0
  5. wbmailing/celery.py +0 -0
  6. wbmailing/dynamic_preferences_registry.py +35 -0
  7. wbmailing/factories.py +211 -0
  8. wbmailing/filters/__init__.py +8 -0
  9. wbmailing/filters/mailing_lists.py +84 -0
  10. wbmailing/filters/mails.py +74 -0
  11. wbmailing/management/__init__.py +22 -0
  12. wbmailing/migrations/0001_initial_squashed_squashed_0008_alter_mail_bcc_email_alter_mail_cc_email_and_more.py +649 -0
  13. wbmailing/migrations/0002_delete_mailingsettings.py +16 -0
  14. wbmailing/migrations/0003_alter_mailinglistsubscriberchangerequest_options.py +25 -0
  15. wbmailing/migrations/__init__.py +0 -0
  16. wbmailing/models/__init__.py +6 -0
  17. wbmailing/models/mailing_lists.py +386 -0
  18. wbmailing/models/mails.py +895 -0
  19. wbmailing/serializers/__init__.py +19 -0
  20. wbmailing/serializers/mailing_lists.py +209 -0
  21. wbmailing/serializers/mails.py +251 -0
  22. wbmailing/tasks.py +37 -0
  23. wbmailing/templatetags/__init__.py +0 -0
  24. wbmailing/templatetags/mailing_tags.py +22 -0
  25. wbmailing/tests/__init__.py +0 -0
  26. wbmailing/tests/conftest.py +30 -0
  27. wbmailing/tests/models/__init__.py +0 -0
  28. wbmailing/tests/models/test_mailing_lists.py +297 -0
  29. wbmailing/tests/models/test_mails.py +205 -0
  30. wbmailing/tests/signals.py +124 -0
  31. wbmailing/tests/test_serializers.py +28 -0
  32. wbmailing/tests/test_tasks.py +49 -0
  33. wbmailing/tests/test_viewsets.py +216 -0
  34. wbmailing/tests/tests.py +142 -0
  35. wbmailing/urls.py +90 -0
  36. wbmailing/viewsets/__init__.py +32 -0
  37. wbmailing/viewsets/analytics.py +110 -0
  38. wbmailing/viewsets/buttons/__init__.py +10 -0
  39. wbmailing/viewsets/buttons/mailing_lists.py +91 -0
  40. wbmailing/viewsets/buttons/mails.py +98 -0
  41. wbmailing/viewsets/display/__init__.py +16 -0
  42. wbmailing/viewsets/display/mailing_lists.py +175 -0
  43. wbmailing/viewsets/display/mails.py +318 -0
  44. wbmailing/viewsets/endpoints/__init__.py +8 -0
  45. wbmailing/viewsets/endpoints/mailing_lists.py +86 -0
  46. wbmailing/viewsets/endpoints/mails.py +51 -0
  47. wbmailing/viewsets/mailing_lists.py +320 -0
  48. wbmailing/viewsets/mails.py +425 -0
  49. wbmailing/viewsets/menu/__init__.py +5 -0
  50. wbmailing/viewsets/menu/mailing_lists.py +37 -0
  51. wbmailing/viewsets/menu/mails.py +25 -0
  52. wbmailing/viewsets/titles/__init__.py +17 -0
  53. wbmailing/viewsets/titles/mailing_lists.py +63 -0
  54. wbmailing/viewsets/titles/mails.py +55 -0
  55. wbmailing-2.2.1.dist-info/METADATA +5 -0
  56. wbmailing-2.2.1.dist-info/RECORD +57 -0
  57. wbmailing-2.2.1.dist-info/WHEEL +5 -0
@@ -0,0 +1,318 @@
1
+ from typing import Optional
2
+
3
+ from django.utils.translation import gettext as _
4
+ from wbcore.contrib.color.enums import WBColor
5
+ from wbcore.enums import Unit
6
+ from wbcore.metadata.configs import display as dp
7
+ from wbcore.metadata.configs.display.instance_display.display import Display
8
+ from wbcore.metadata.configs.display.instance_display.layouts.inlines import Inline
9
+ from wbcore.metadata.configs.display.instance_display.layouts.layouts import Layout
10
+ from wbcore.metadata.configs.display.instance_display.operators import default
11
+ from wbcore.metadata.configs.display.instance_display.pages import Page
12
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
13
+ create_simple_display,
14
+ create_simple_section,
15
+ )
16
+ from wbcore.metadata.configs.display.instance_display.styles import Style
17
+ from wbcore.metadata.configs.display.instance_display.utils import repeat_field
18
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
19
+ from wbmailing import models
20
+
21
+
22
+ class MassMailDisplayConfig(DisplayViewConfig):
23
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
24
+ return dp.ListDisplay(
25
+ fields=[
26
+ dp.Field(key="subject", label=_("Subject")),
27
+ dp.Field(key="from_email", label=_("From")),
28
+ dp.Field(key="template", label=_("Template")),
29
+ dp.Field(key="mailing_lists", label=_("Mailing Lists")),
30
+ dp.Field(key="excluded_mailing_lists", label=_("Excluded Mailing Lists")),
31
+ dp.Field(key="created", label=_("Created")),
32
+ dp.Field(key="creator", label=_("Creator")),
33
+ dp.Field(key="send_at", label=_("Send At")),
34
+ ],
35
+ legends=[
36
+ dp.Legend(
37
+ key="status",
38
+ items=[
39
+ dp.LegendItem(
40
+ icon=WBColor.GREEN_LIGHT.value,
41
+ label=models.MassMail.Status.SENT.label,
42
+ value=models.MassMail.Status.SENT,
43
+ ),
44
+ dp.LegendItem(
45
+ icon=WBColor.GREEN.value,
46
+ label=models.MassMail.Status.SEND_LATER.label,
47
+ value=models.MassMail.Status.SEND_LATER,
48
+ ),
49
+ dp.LegendItem(
50
+ icon=WBColor.YELLOW_LIGHT.value,
51
+ label=models.MassMail.Status.PENDING.label,
52
+ value=models.MassMail.Status.PENDING,
53
+ ),
54
+ dp.LegendItem(
55
+ icon=WBColor.BLUE_LIGHT.value,
56
+ label=models.MassMail.Status.DRAFT.label,
57
+ value=models.MassMail.Status.DRAFT,
58
+ ),
59
+ dp.LegendItem(
60
+ icon=WBColor.RED_LIGHT.value,
61
+ label=models.MassMail.Status.DENIED.label,
62
+ value=models.MassMail.Status.DENIED,
63
+ ),
64
+ ],
65
+ )
66
+ ],
67
+ formatting=[
68
+ dp.Formatting(
69
+ column="status",
70
+ formatting_rules=[
71
+ dp.FormattingRule(
72
+ style={"backgroundColor": WBColor.GREEN_LIGHT.value},
73
+ condition=("==", models.MassMail.Status.SENT),
74
+ ),
75
+ dp.FormattingRule(
76
+ style={"backgroundColor": WBColor.GREEN.value},
77
+ condition=("==", models.MassMail.Status.SEND_LATER),
78
+ ),
79
+ dp.FormattingRule(
80
+ style={"backgroundColor": WBColor.YELLOW_LIGHT.value},
81
+ condition=("==", models.MassMail.Status.PENDING),
82
+ ),
83
+ dp.FormattingRule(
84
+ style={"backgroundColor": WBColor.BLUE_LIGHT.value},
85
+ condition=("==", models.MassMail.Status.DRAFT),
86
+ ),
87
+ dp.FormattingRule(
88
+ style={"backgroundColor": WBColor.RED_LIGHT.value},
89
+ condition=("==", models.MassMail.Status.DENIED),
90
+ ),
91
+ ],
92
+ )
93
+ ],
94
+ )
95
+
96
+ def get_instance_display(self) -> Display:
97
+ display = Display(
98
+ pages=[
99
+ Page(
100
+ title="Home",
101
+ layouts={
102
+ default(): Layout(
103
+ grid_template_areas=[
104
+ ["status", "status"],
105
+ ["subject", "subject"],
106
+ ["from_email", "template"],
107
+ ["mailing_lists", "excluded_mailing_lists"],
108
+ ["body", "body"],
109
+ ["attachment_url", "attachment_url"],
110
+ ]
111
+ )
112
+ },
113
+ ),
114
+ Page(
115
+ title="Analytics",
116
+ layouts={
117
+ default(): Layout(
118
+ grid_template_areas=[
119
+ ["mailstatus_barchart", "mailclick_barchart"],
120
+ ["region_barchart", "region_barchart"],
121
+ ["clients_barchart", "country_barchart"],
122
+ ],
123
+ grid_template_rows=[Style.px(600), Style.px(600), Style.px(600)],
124
+ inlines=[
125
+ Inline(key="mailstatus_barchart", endpoint="mailstatus_barchart"),
126
+ Inline(key="mailclick_barchart", endpoint="mailclick_barchart"),
127
+ Inline(key="clients_barchart", endpoint="clients_barchart"),
128
+ Inline(key="country_barchart", endpoint="country_barchart"),
129
+ Inline(key="region_barchart", endpoint="region_barchart"),
130
+ ],
131
+ )
132
+ },
133
+ ),
134
+ Page(
135
+ title="Events",
136
+ layouts={
137
+ default(): Layout(
138
+ grid_template_areas=[["events_key"]],
139
+ inlines=[Inline(key="events_key", endpoint="events")],
140
+ grid_template_columns=[
141
+ "minmax(min-content, 1fr)",
142
+ ],
143
+ grid_auto_rows=Style.MIN_CONTENT,
144
+ ),
145
+ },
146
+ ),
147
+ ]
148
+ )
149
+
150
+ return display
151
+
152
+
153
+ EVENTTYPE_LEGENDS = dp.Legend(
154
+ key="status",
155
+ items=[
156
+ dp.LegendItem(
157
+ icon=WBColor.GREY.value,
158
+ label=models.MailEvent.EventType.CREATED.label,
159
+ value=models.MailEvent.EventType.CREATED,
160
+ ),
161
+ dp.LegendItem(
162
+ icon=WBColor.BLUE_LIGHT.value,
163
+ label=models.MailEvent.EventType.QUEUED.label,
164
+ value=models.MailEvent.EventType.QUEUED,
165
+ ),
166
+ dp.LegendItem(
167
+ icon=WBColor.BLUE_DARK.value,
168
+ label=models.MailEvent.EventType.UNKNOWN.label,
169
+ value=models.MailEvent.EventType.UNKNOWN,
170
+ ),
171
+ dp.LegendItem(
172
+ icon=WBColor.YELLOW_LIGHT.value,
173
+ label=models.MailEvent.EventType.DELIVERED.label,
174
+ value=models.MailEvent.EventType.DELIVERED,
175
+ ),
176
+ dp.LegendItem(
177
+ icon=WBColor.GREEN_LIGHT.value,
178
+ label=models.MailEvent.EventType.OPENED.label,
179
+ value=models.MailEvent.EventType.OPENED,
180
+ ),
181
+ dp.LegendItem(
182
+ icon=WBColor.GREEN.value,
183
+ label=models.MailEvent.EventType.CLICKED.label,
184
+ value=models.MailEvent.EventType.CLICKED,
185
+ ),
186
+ dp.LegendItem(
187
+ icon=WBColor.RED_LIGHT.value,
188
+ label=models.MailEvent.EventType.DEFERRED.label,
189
+ value=models.MailEvent.EventType.DEFERRED,
190
+ ),
191
+ dp.LegendItem(
192
+ icon=WBColor.RED.value,
193
+ label=models.MailEvent.EventType.BOUNCED.label,
194
+ value=models.MailEvent.EventType.BOUNCED,
195
+ ),
196
+ dp.LegendItem(
197
+ icon=WBColor.RED_DARK.value,
198
+ label=models.MailEvent.EventType.REJECTED.label,
199
+ value=models.MailEvent.EventType.REJECTED,
200
+ ),
201
+ ],
202
+ )
203
+
204
+ EVENTTYPE_FORMATTING = dp.Formatting(
205
+ column="status",
206
+ formatting_rules=[
207
+ dp.FormattingRule(
208
+ style={"backgroundColor": WBColor.BLUE_LIGHT.value},
209
+ condition=("==", models.MailEvent.EventType.QUEUED),
210
+ ),
211
+ dp.FormattingRule(
212
+ style={"backgroundColor": WBColor.GREEN.value},
213
+ condition=("==", models.MailEvent.EventType.CLICKED),
214
+ ),
215
+ dp.FormattingRule(
216
+ style={"backgroundColor": WBColor.GREEN_LIGHT.value},
217
+ condition=("==", models.MailEvent.EventType.OPENED),
218
+ ),
219
+ dp.FormattingRule(
220
+ style={"backgroundColor": WBColor.YELLOW_LIGHT.value},
221
+ condition=("==", models.MailEvent.EventType.DELIVERED),
222
+ ),
223
+ dp.FormattingRule(
224
+ style={"backgroundColor": WBColor.RED.value},
225
+ condition=("==", models.MailEvent.EventType.BOUNCED),
226
+ ),
227
+ dp.FormattingRule(
228
+ style={"backgroundColor": WBColor.RED_LIGHT.value},
229
+ condition=("==", models.MailEvent.EventType.DEFERRED),
230
+ ),
231
+ dp.FormattingRule(
232
+ style={"backgroundColor": WBColor.GREY.value},
233
+ condition=("==", models.MailEvent.EventType.CREATED),
234
+ ),
235
+ dp.FormattingRule(
236
+ style={"backgroundColor": WBColor.RED_DARK.value},
237
+ condition=("==", models.MailEvent.EventType.REJECTED),
238
+ ),
239
+ ],
240
+ )
241
+
242
+
243
+ class MailDisplayConfig(DisplayViewConfig):
244
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
245
+ return dp.ListDisplay(
246
+ fields=[
247
+ dp.Field(key="mass_mail", label=_("Mass Mail")),
248
+ dp.Field(key="subject", label=_("Subject")),
249
+ dp.Field(key="from_email", label=_("From")),
250
+ dp.Field(key="to_email", label=_("To")),
251
+ dp.Field(key="last_send", label=_("Last Sent")),
252
+ dp.Field(key="created", label=_("Created")),
253
+ dp.Field(key="status", label=_("Latest Status")),
254
+ ],
255
+ legends=[EVENTTYPE_LEGENDS],
256
+ formatting=[EVENTTYPE_FORMATTING],
257
+ )
258
+
259
+ def get_instance_display(self) -> Display:
260
+ fields = [
261
+ [repeat_field(2, "subject")],
262
+ ["from_email", "to_email"],
263
+ ["cc_email", "bcc_email"],
264
+ ["created", "."],
265
+ ]
266
+ instance = None
267
+ try:
268
+ instance = self.view.get_object()
269
+ except AssertionError:
270
+ pass
271
+
272
+ if instance and instance.mass_mail:
273
+ fields.append([repeat_field(2, "mass_mail")])
274
+ else:
275
+ fields.append([repeat_field(2, "template")])
276
+ fields.append([repeat_field(2, "body")])
277
+ fields.append([repeat_field(2, "mailevent_section")])
278
+ return create_simple_display(
279
+ fields, [create_simple_section("mailevent_section", _("Events"), [["mailevent"]], "mailevent")]
280
+ )
281
+
282
+
283
+ class MailStatusMassMailDisplayConfig(DisplayViewConfig):
284
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
285
+ return dp.ListDisplay(
286
+ fields=[
287
+ dp.Field(key="entry", label=_("Recipient"), width=Unit.PIXEL(350)),
288
+ dp.Field(key="address", label=_("Mail Address"), width=Unit.PIXEL(200)),
289
+ dp.Field(key="status", label=_("Latest Status"), width=Unit.PIXEL(200)),
290
+ ],
291
+ legends=[EVENTTYPE_LEGENDS],
292
+ formatting=[EVENTTYPE_FORMATTING],
293
+ )
294
+
295
+
296
+ class MailTemplateDisplayConfig(DisplayViewConfig):
297
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
298
+ return dp.ListDisplay(fields=[dp.Field(key="title", label=_("Title"))])
299
+
300
+ def get_instance_display(self) -> Display:
301
+ return create_simple_display([["title"], ["template"]])
302
+
303
+
304
+ class MailEventDisplayConfig(DisplayViewConfig):
305
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
306
+ return dp.ListDisplay(
307
+ fields=[
308
+ dp.Field(key="timestamp", label=_("Date")),
309
+ dp.Field(key="mail", label=_("Mail")) if "mail_id" not in self.view.kwargs else (),
310
+ dp.Field(key="event_type", label=_("Type")),
311
+ dp.Field(key="reject_reason", label=_("Rejection Reason")),
312
+ dp.Field(key="recipient", label=_("Recipient")),
313
+ dp.Field(key="click_url", label=_("URL")),
314
+ dp.Field(key="ip", label=_("IP")),
315
+ dp.Field(key="user_agent", label=_("User Agent")),
316
+ dp.Field(key="description", label=_("Description")),
317
+ ]
318
+ )
@@ -0,0 +1,8 @@
1
+ from .mailing_lists import (
2
+ EmailContactMailingListEndpointConfig,
3
+ MailingListEntryEndpointConfig,
4
+ MailingListSubscriberRequestEntryEndpointConfig,
5
+ MailingListSubscriberRequestMailingListEndpointConfig,
6
+ MailMailingListChartEndpointConfig,
7
+ )
8
+ from .mails import MailEventMailEndpointConfig, MailStatusMassMailEndpointConfig, MailEventMassMailEndpointConfig
@@ -0,0 +1,86 @@
1
+ from rest_framework.reverse import reverse
2
+ from wbcore.metadata.configs.endpoints import EndpointViewConfig
3
+
4
+
5
+ class MailingListSubscriberRequestMailingListEndpointConfig(EndpointViewConfig):
6
+ def get_endpoint(self, **kwargs):
7
+ return reverse(
8
+ "wbmailing:mailing_list-mailinglistsubscriberchangerequest-list",
9
+ args=[self.view.kwargs["mailing_list_id"]],
10
+ request=self.request,
11
+ )
12
+
13
+
14
+ class EmailContactMailingListEndpointConfig(EndpointViewConfig):
15
+ def get_endpoint(self, **kwargs):
16
+ return None
17
+
18
+ def get_list_endpoint(self, **kwargs):
19
+ return reverse(
20
+ "wbmailing:mailing_list-email_contacts-list",
21
+ args=[self.view.kwargs["mailing_list_id"]],
22
+ request=self.request,
23
+ )
24
+
25
+ def get_create_endpoint(self, **kwargs):
26
+ return reverse(
27
+ "wbmailing:mailing_list-mailinglistsubscriberchangerequest-list",
28
+ args=[self.view.kwargs["mailing_list_id"]],
29
+ request=self.request,
30
+ )
31
+
32
+
33
+ class MailingListEntryEndpointConfig(EndpointViewConfig):
34
+ PK_FIELD = "mailing_list"
35
+
36
+ def get_list_endpoint(self, **kwargs):
37
+ return reverse(
38
+ "wbmailing:entry-mailinglist-list",
39
+ args=[self.view.kwargs["entry_id"]],
40
+ request=self.request,
41
+ )
42
+
43
+ def get_instance_endpoint(self, **kwargs):
44
+ return reverse(
45
+ "wbmailing:mailinglist-list",
46
+ args=[],
47
+ request=self.request,
48
+ )
49
+
50
+ def get_delete_endpoint(self, **kwargs):
51
+ return None
52
+
53
+ def get_create_endpoint(self, **kwargs):
54
+ if getattr(self.view, "primary_email", None):
55
+ return reverse(
56
+ "wbmailing:entry-mailinglistsubscriberchangerequest-list",
57
+ args=[self.view.kwargs["entry_id"]],
58
+ request=self.request,
59
+ )
60
+ return None
61
+
62
+
63
+ class MailMailingListChartEndpointConfig(EndpointViewConfig):
64
+ def get_endpoint(self, **kwargs):
65
+ return reverse(
66
+ "wbmailing:mailing_list-maileventchart-list",
67
+ args=[self.view.kwargs["mailing_list_id"]],
68
+ request=self.request,
69
+ )
70
+
71
+
72
+ class MailingListSubscriberRequestEntryEndpointConfig(EndpointViewConfig):
73
+ def get_instance_endpoint(self, **kwargs):
74
+ if getattr(self.view, "primary_email", None):
75
+ return super().get_instance_endpoint(**kwargs)
76
+ return None
77
+
78
+ def get_create_endpoint(self, **kwargs):
79
+ return self.get_instance_endpoint(**kwargs)
80
+
81
+ def get_list_endpoint(self, **kwargs):
82
+ return reverse(
83
+ "wbmailing:entry-mailinglistsubscriberchangerequest-list",
84
+ args=[self.view.kwargs["entry_id"]],
85
+ request=self.request,
86
+ )
@@ -0,0 +1,51 @@
1
+ from rest_framework.reverse import reverse
2
+ from wbcore.metadata.configs.endpoints import EndpointViewConfig
3
+ from wbmailing.models import Mail
4
+
5
+
6
+ class MailStatusMassMailEndpointConfig(EndpointViewConfig):
7
+ PK_FIELD = "mail_id"
8
+
9
+ def get_endpoint(self, **kwargs):
10
+ return None
11
+
12
+ def get_list_endpoint(self, **kwargs):
13
+ return reverse(
14
+ "wbmailing:massmail-mailstatus-list",
15
+ args=[self.view.kwargs["mass_mail_id"]],
16
+ request=self.request,
17
+ )
18
+
19
+ def get_instance_endpoint(self, **kwargs):
20
+ endpoint = reverse(
21
+ "wbmailing:mail-list",
22
+ )
23
+ if self.instance and (mass_mail_id := self.view.kwargs.get("mass_mail_id", None)):
24
+ obj = self.view.get_object()
25
+ mail = Mail.objects.filter(mass_mail=mass_mail_id, to_email=obj).latest("created")
26
+ return f"{endpoint}{mail.id}"
27
+ return endpoint
28
+
29
+
30
+ class MailEventMailEndpointConfig(EndpointViewConfig):
31
+ def get_endpoint(self, **kwargs):
32
+ return None
33
+
34
+ def get_list_endpoint(self, **kwargs):
35
+ return reverse(
36
+ "wbmailing:mail-mailevent-list",
37
+ args=[self.view.kwargs["mail_id"]],
38
+ request=self.request,
39
+ )
40
+
41
+
42
+ class MailEventMassMailEndpointConfig(EndpointViewConfig):
43
+ def get_endpoint(self, **kwargs):
44
+ return None
45
+
46
+ def get_list_endpoint(self, **kwargs):
47
+ return reverse(
48
+ "wbmailing:massmail-mailevent-list",
49
+ args=[self.view.kwargs["mass_mail_id"]],
50
+ request=self.request,
51
+ )