wbmailing 2.2.1__tar.gz

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 (64) hide show
  1. wbmailing-2.2.1/.gitignore +181 -0
  2. wbmailing-2.2.1/PKG-INFO +5 -0
  3. wbmailing-2.2.1/pyproject.toml +29 -0
  4. wbmailing-2.2.1/wbmailing/__init__.py +1 -0
  5. wbmailing-2.2.1/wbmailing/admin.py +74 -0
  6. wbmailing-2.2.1/wbmailing/apps.py +14 -0
  7. wbmailing-2.2.1/wbmailing/backend.py +131 -0
  8. wbmailing-2.2.1/wbmailing/celery.py +0 -0
  9. wbmailing-2.2.1/wbmailing/dynamic_preferences_registry.py +35 -0
  10. wbmailing-2.2.1/wbmailing/factories.py +211 -0
  11. wbmailing-2.2.1/wbmailing/filters/__init__.py +8 -0
  12. wbmailing-2.2.1/wbmailing/filters/mailing_lists.py +84 -0
  13. wbmailing-2.2.1/wbmailing/filters/mails.py +74 -0
  14. wbmailing-2.2.1/wbmailing/locale/de/LC_MESSAGES/django.po +1110 -0
  15. wbmailing-2.2.1/wbmailing/management/__init__.py +22 -0
  16. wbmailing-2.2.1/wbmailing/migrations/0001_initial_squashed_squashed_0008_alter_mail_bcc_email_alter_mail_cc_email_and_more.py +649 -0
  17. wbmailing-2.2.1/wbmailing/migrations/0002_delete_mailingsettings.py +16 -0
  18. wbmailing-2.2.1/wbmailing/migrations/0003_alter_mailinglistsubscriberchangerequest_options.py +25 -0
  19. wbmailing-2.2.1/wbmailing/migrations/__init__.py +0 -0
  20. wbmailing-2.2.1/wbmailing/models/__init__.py +6 -0
  21. wbmailing-2.2.1/wbmailing/models/mailing_lists.py +386 -0
  22. wbmailing-2.2.1/wbmailing/models/mails.py +895 -0
  23. wbmailing-2.2.1/wbmailing/serializers/__init__.py +19 -0
  24. wbmailing-2.2.1/wbmailing/serializers/mailing_lists.py +209 -0
  25. wbmailing-2.2.1/wbmailing/serializers/mails.py +251 -0
  26. wbmailing-2.2.1/wbmailing/tasks.py +37 -0
  27. wbmailing-2.2.1/wbmailing/templates/email_base_template.html +291 -0
  28. wbmailing-2.2.1/wbmailing/templates/mailing/maintain_mail_subsciptions.html +7 -0
  29. wbmailing-2.2.1/wbmailing/templates/mailing/manage_mailing_list_subscriptions.html +26 -0
  30. wbmailing-2.2.1/wbmailing/templates/template.html +295 -0
  31. wbmailing-2.2.1/wbmailing/templates/test.html +294 -0
  32. wbmailing-2.2.1/wbmailing/templates/workbench.html +24 -0
  33. wbmailing-2.2.1/wbmailing/templatetags/__init__.py +0 -0
  34. wbmailing-2.2.1/wbmailing/templatetags/mailing_tags.py +22 -0
  35. wbmailing-2.2.1/wbmailing/tests/__init__.py +0 -0
  36. wbmailing-2.2.1/wbmailing/tests/conftest.py +30 -0
  37. wbmailing-2.2.1/wbmailing/tests/models/__init__.py +0 -0
  38. wbmailing-2.2.1/wbmailing/tests/models/test_mailing_lists.py +297 -0
  39. wbmailing-2.2.1/wbmailing/tests/models/test_mails.py +205 -0
  40. wbmailing-2.2.1/wbmailing/tests/signals.py +124 -0
  41. wbmailing-2.2.1/wbmailing/tests/test_serializers.py +28 -0
  42. wbmailing-2.2.1/wbmailing/tests/test_tasks.py +49 -0
  43. wbmailing-2.2.1/wbmailing/tests/test_viewsets.py +216 -0
  44. wbmailing-2.2.1/wbmailing/tests/tests.py +142 -0
  45. wbmailing-2.2.1/wbmailing/urls.py +90 -0
  46. wbmailing-2.2.1/wbmailing/viewsets/__init__.py +32 -0
  47. wbmailing-2.2.1/wbmailing/viewsets/analytics.py +110 -0
  48. wbmailing-2.2.1/wbmailing/viewsets/buttons/__init__.py +10 -0
  49. wbmailing-2.2.1/wbmailing/viewsets/buttons/mailing_lists.py +91 -0
  50. wbmailing-2.2.1/wbmailing/viewsets/buttons/mails.py +98 -0
  51. wbmailing-2.2.1/wbmailing/viewsets/display/__init__.py +16 -0
  52. wbmailing-2.2.1/wbmailing/viewsets/display/mailing_lists.py +175 -0
  53. wbmailing-2.2.1/wbmailing/viewsets/display/mails.py +318 -0
  54. wbmailing-2.2.1/wbmailing/viewsets/endpoints/__init__.py +8 -0
  55. wbmailing-2.2.1/wbmailing/viewsets/endpoints/mailing_lists.py +86 -0
  56. wbmailing-2.2.1/wbmailing/viewsets/endpoints/mails.py +51 -0
  57. wbmailing-2.2.1/wbmailing/viewsets/mailing_lists.py +320 -0
  58. wbmailing-2.2.1/wbmailing/viewsets/mails.py +425 -0
  59. wbmailing-2.2.1/wbmailing/viewsets/menu/__init__.py +5 -0
  60. wbmailing-2.2.1/wbmailing/viewsets/menu/mailing_lists.py +37 -0
  61. wbmailing-2.2.1/wbmailing/viewsets/menu/mails.py +25 -0
  62. wbmailing-2.2.1/wbmailing/viewsets/titles/__init__.py +17 -0
  63. wbmailing-2.2.1/wbmailing/viewsets/titles/mailing_lists.py +63 -0
  64. wbmailing-2.2.1/wbmailing/viewsets/titles/mails.py +55 -0
@@ -0,0 +1,181 @@
1
+ ~
2
+
3
+ # Docker volumes
4
+ volumes/
5
+ # Poetry auth file
6
+ auth.toml
7
+
8
+ media/*
9
+ media/
10
+ mediafiles/
11
+ mediafiles/*
12
+ test/*
13
+ staticfiles/*
14
+ staticfiles/
15
+ #
16
+ # Byte-compiled / optimized / DLL files
17
+ __pycache__/
18
+ *.py[cod]
19
+ *$py.class
20
+
21
+ # C extensions
22
+ *.so
23
+
24
+ # Distribution / packaging
25
+ .Python
26
+ build/
27
+ develop-eggs/
28
+ dist/
29
+ info/
30
+ downloads/
31
+ eggs/
32
+ .eggs/
33
+ lib/
34
+ lib64/
35
+ parts/
36
+ sdist/
37
+ var/
38
+ wheels/
39
+ share/python-wheels/
40
+ *.egg-info/
41
+ .installed.cfg
42
+ *.egg
43
+ MANIFEST
44
+
45
+ # PyInstaller
46
+ # Usually these files are written by a python script from a template
47
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
48
+ *.manifest
49
+ *.spec
50
+
51
+ # Installer logs
52
+ pip-log.txt
53
+ pip-delete-this-directory.txt
54
+
55
+ # Unit test / coverage reports
56
+ htmlcov/
57
+ .tox/
58
+ .nox/
59
+ .coverage
60
+ .coverage.*
61
+ .cache
62
+ .dccache
63
+ nosetests.xml
64
+ coverage.xml
65
+ *.cover
66
+ *.py,cover
67
+ .hypothesis/
68
+ .pytest_cache/
69
+ cover/
70
+ report.xml
71
+ */report.xml
72
+
73
+ # Translations
74
+ *.mo
75
+ *.pot
76
+
77
+ # Django stuff:
78
+ *.log
79
+ local_settings.py
80
+ *.sqlite3
81
+ db.sqlite3-journal
82
+
83
+ # Flask stuff:
84
+ instance/
85
+ .webassets-cache
86
+
87
+ # Scrapy stuff:
88
+ .scrapy
89
+
90
+ # Sphinx documentation
91
+ docs/_build/
92
+
93
+ # PyBuilder
94
+ .pybuilder/
95
+ target/
96
+
97
+ # Jupyter Notebook
98
+ .ipynb_checkpoints
99
+ *.ipynb
100
+ # IPython
101
+ profile_default/
102
+ ipython_config.py
103
+
104
+ # pyenv
105
+ # For a library or package, you might want to ignore these files since the code is
106
+ # intended to run in multiple environments; otherwise, check them in:
107
+ # .python-version
108
+
109
+ # pipenv
110
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
111
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
112
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
113
+ # install all needed dependencies.
114
+ #Pipfile.lock
115
+
116
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
117
+ __pypackages__/
118
+
119
+ # Celery stuff
120
+ celerybeat-schedule
121
+ celerybeat.pid
122
+
123
+ # SageMath parsed files
124
+ *.sage.py
125
+
126
+ # Environments
127
+ .env
128
+ .envrc
129
+ .venv
130
+ env/
131
+ venv/
132
+ ENV/
133
+ env.bak/
134
+ venv.bak/
135
+ .vscode/
136
+ .idea/
137
+ .idea.bkp/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+ crm/
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # Gitlab Runner
164
+ builds
165
+ builds/
166
+
167
+ # Integrator Office 365 : reverse proxy tunnel for outlook365
168
+ ngrok
169
+ */ngrok
170
+ /modules/**/system/
171
+
172
+ /modules/wbmailing/files/*
173
+ /modules/wbmailing/mailing/*
174
+
175
+ /projects/*/requirements.txt
176
+ public
177
+
178
+ # Ignore archive localization generated folder
179
+ backend/modules/**/archive/*
180
+ **/**/requirements.txt
181
+ CHANGELOG-*
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.3
2
+ Name: wbmailing
3
+ Version: 2.2.1
4
+ Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
5
+ Requires-Dist: wbcore
@@ -0,0 +1,29 @@
1
+ [project]
2
+ name = "wbmailing"
3
+ description = ""
4
+ authors = [{ name = "Christopher Wittlinger", email = "c.wittlinger@stainly.com"}]
5
+ dynamic = ["version"]
6
+
7
+ dependencies = [
8
+ "wbcore",
9
+ ]
10
+
11
+ [tool.uv.sources]
12
+ wbcore = { workspace = true }
13
+
14
+ [tool.uv]
15
+ package = true
16
+
17
+ [tool.hatch.version]
18
+ path = "../../pyproject.toml"
19
+
20
+ [tool.hatch.build.targets.sdist]
21
+ include = ["wbmailing/*"]
22
+
23
+ [tool.hatch.build.targets.wheel]
24
+ packages = ["wbmailing"]
25
+ only-packages = true
26
+
27
+ [build-system]
28
+ requires = ["hatchling"]
29
+ build-backend = "hatchling.build"
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
@@ -0,0 +1,74 @@
1
+ from django.contrib import admin
2
+ from django.utils.translation import gettext_lazy as _
3
+
4
+ from .models import (
5
+ Mail,
6
+ MailEvent,
7
+ MailingList,
8
+ MailingListEmailContactThroughModel,
9
+ MailingListSubscriberChangeRequest,
10
+ MailTemplate,
11
+ MassMail,
12
+ )
13
+
14
+
15
+ class MailingListEmailContactThroughInlineAdmin(admin.TabularInline):
16
+ model = MailingListEmailContactThroughModel
17
+ fk_name = "mailing_list"
18
+ autocomplete_fields = ["email_contact"]
19
+
20
+
21
+ @admin.register(MailingList)
22
+ class MailingListAdmin(admin.ModelAdmin):
23
+ autocomplete_fields = ["email_contacts"]
24
+ search_fields = ["title"]
25
+ inlines = [
26
+ MailingListEmailContactThroughInlineAdmin,
27
+ ]
28
+
29
+
30
+ @admin.register(MassMail)
31
+ class MassMailAdmin(admin.ModelAdmin):
32
+ autocomplete_fields = ["creator"]
33
+
34
+ def send_test_mail(self, request, queryset):
35
+ for mass_mail in queryset:
36
+ mass_mail.send_test_mail(request.user)
37
+
38
+ actions = [send_test_mail]
39
+
40
+
41
+ @admin.register(MailingListSubscriberChangeRequest)
42
+ class MailingListSubscriberChangeRequestAdmin(admin.ModelAdmin):
43
+ list_display = ("email_contact", "mailing_list", "status")
44
+ autocomplete_fields = ["email_contact", "requester"]
45
+
46
+
47
+ admin.site.register(MailEvent)
48
+
49
+
50
+ class MailEventInline(admin.TabularInline):
51
+ model = MailEvent
52
+ fields = ("timestamp", "event_type", "reject_reason", "recipient", "user_agent", "tags", "description")
53
+ readonly_fields = ("timestamp", "event_type", "reject_reason", "recipient", "user_agent", "tags", "description")
54
+ extra = 0
55
+ can_delete = False
56
+
57
+
58
+ @admin.register(Mail)
59
+ class MailAdmin(admin.ModelAdmin):
60
+ def send_mails(self, request, queryset):
61
+ for mail in queryset:
62
+ mail.resend()
63
+
64
+ send_mails.short_description = _("Send Emails")
65
+ actions = [send_mails]
66
+ search_fields = ["mass_mail__subject", "from_email", "to_email__address", "subject", "message_ids"]
67
+ autocomplete_fields = ["to_email", "cc_email", "bcc_email"]
68
+
69
+ inlines = [MailEventInline]
70
+
71
+
72
+ @admin.register(MailTemplate)
73
+ class MailTemplateAdmin(admin.ModelAdmin):
74
+ pass
@@ -0,0 +1,14 @@
1
+ from django.apps import AppConfig
2
+ from django.db.models.signals import post_migrate
3
+
4
+
5
+ class WbmailingConfig(AppConfig):
6
+ name = "wbmailing"
7
+
8
+ def ready(self) -> None:
9
+ from wbmailing.management import initialize_task
10
+
11
+ post_migrate.connect(
12
+ initialize_task,
13
+ dispatch_uid="wbmailing.initialize_task",
14
+ )
@@ -0,0 +1,131 @@
1
+ from datetime import timedelta
2
+
3
+ from anymail.backends.mailgun import EmailBackend as AnymailMailgunBackend
4
+ from anymail.backends.mailjet import EmailBackend as AnymailMailjetBackend
5
+ from anymail.backends.mandrill import EmailBackend as AnymailMandrillBackend
6
+ from anymail.backends.postmark import EmailBackend as AnymailPostmarkBackend
7
+ from anymail.backends.sendgrid import EmailBackend as AnymailSendgridBackend
8
+ from anymail.backends.sendinblue import EmailBackend as AnymailSendinblueBackend
9
+ from anymail.exceptions import AnymailError
10
+ from django.conf import settings
11
+ from django.core.mail.backends.console import EmailBackend as ConsoleBackend
12
+ from django.utils import timezone
13
+ from sentry_sdk import capture_message
14
+ from wbcore.utils.html import convert_html2text
15
+ from wbmailing.models import Mail, MailEvent
16
+
17
+
18
+ class SendMessagesMixin:
19
+ def _process_msg(self, message):
20
+ mail = message.mail if hasattr(message, "mail") else None
21
+ mass_mail = message.mass_mail if hasattr(message, "mass_mail") else None
22
+ if mail:
23
+ event_type = MailEvent.EventType.RESENT
24
+ else:
25
+ if not hasattr(message, "silent_mail") or (hasattr(message, "silent_mail") and not message.silent_mail):
26
+ mail = Mail.create_mail_from_mailmessage(message, user=getattr(message, "user", None))
27
+ event_type = MailEvent.EventType.CREATED
28
+ if mass_mail:
29
+ message.tags = [f"massmail-{mass_mail.id}"]
30
+ else:
31
+ message.tags = [f"mail-{mail.id}"]
32
+ if mail and mail.body:
33
+ # We reset the body text and html field with what might have been computed in create_mail_from_mailmessage
34
+ message.body = convert_html2text(mail.body)
35
+ message.alternatives = []
36
+ message.attach_alternative(mail.body, "text/html")
37
+ return event_type, mail
38
+
39
+ def send_messages(self, email_messages):
40
+ """
41
+ Sends one or more EmailMessage objects and returns the number of email
42
+ messages sent.
43
+ """
44
+ # This API is specified by Django's core BaseEmailBackend
45
+ # (so you can't change it to, e.g., return detailed status).
46
+ # Subclasses shouldn't need to override.
47
+ from wbmailing.models import MailEvent
48
+
49
+ num_sent = 0
50
+ if not email_messages:
51
+ return num_sent
52
+
53
+ created_session = self.open()
54
+
55
+ try:
56
+ for message in email_messages:
57
+ try:
58
+ event_type, mail = self._process_msg(message)
59
+ sent = self._send(message)
60
+ if mail:
61
+ MailEvent.objects.create(
62
+ mail=mail, event_type=event_type, timestamp=timezone.now() - timedelta(seconds=1)
63
+ )
64
+ mail.message_ids.append(message.anymail_status.message_id)
65
+ mail.last_send = timezone.now()
66
+ mail.save()
67
+ except AnymailError as e:
68
+ capture_message(e)
69
+ if self.fail_silently:
70
+ sent = False
71
+ else:
72
+ raise
73
+ if sent:
74
+ num_sent += 1
75
+ finally:
76
+ if created_session:
77
+ self.close()
78
+
79
+ return num_sent
80
+
81
+
82
+ class PostmarkEmailBackend(SendMessagesMixin, AnymailPostmarkBackend):
83
+ def send_messages(self, email_messages):
84
+ if settings.WBMAILING_POSTMARK_BROADCAST_STREAM_ID:
85
+ for msg in email_messages:
86
+ if hasattr(msg, "mass_mail") and msg.mass_mail:
87
+ msg.esp_extra = {"MessageStream": settings.WBMAILING_POSTMARK_BROADCAST_STREAM_ID}
88
+
89
+ return super().send_messages(email_messages)
90
+
91
+
92
+ class SendgridEmailBackend(SendMessagesMixin, AnymailSendgridBackend):
93
+ pass
94
+
95
+
96
+ class MailgunEmailBackend(SendMessagesMixin, AnymailMailgunBackend):
97
+ pass
98
+
99
+
100
+ class MailjetEmailBackend(SendMessagesMixin, AnymailMailjetBackend):
101
+ pass
102
+
103
+
104
+ class MandrillEmailBackend(SendMessagesMixin, AnymailMandrillBackend):
105
+ pass
106
+
107
+
108
+ class SendinblueEmailBackend(SendMessagesMixin, AnymailSendinblueBackend):
109
+ pass
110
+
111
+
112
+ class ConsoleEmailBackend(SendMessagesMixin, ConsoleBackend):
113
+ def send_messages(self, email_messages):
114
+ """Write all messages to the stream in a thread-safe way."""
115
+ if not email_messages:
116
+ return
117
+ msg_count = 0
118
+ with self._lock:
119
+ try:
120
+ stream_created = self.open()
121
+ for message in email_messages:
122
+ self._process_msg(message)
123
+ self.write_message(message)
124
+ self.stream.flush() # flush after each message
125
+ msg_count += 1
126
+ if stream_created:
127
+ self.close()
128
+ except Exception:
129
+ if not self.fail_silently:
130
+ raise
131
+ return msg_count
File without changes
@@ -0,0 +1,35 @@
1
+ from re import fullmatch
2
+
3
+ from django.forms import ValidationError
4
+ from django.utils.translation import gettext as _
5
+ from dynamic_preferences.preferences import Section
6
+ from dynamic_preferences.registries import global_preferences_registry
7
+ from dynamic_preferences.types import BooleanPreference, StringPreference
8
+
9
+ mailing_section = Section("wbmailing")
10
+
11
+
12
+ @global_preferences_registry.register
13
+ class DefaultSourceMailPreference(StringPreference):
14
+ section = mailing_section
15
+ name = "default_source_mail"
16
+ default = "info@stainly-bench.com"
17
+
18
+ verbose_name = _("Default Source Mail Preference")
19
+ help_text = _("The default address used to send emails from")
20
+
21
+ def validate(self, value):
22
+ if not fullmatch(r"[^@]+@[^@]+\.[^@]+", value):
23
+ raise ValidationError(_("Not a valid email format"))
24
+
25
+
26
+ @global_preferences_registry.register
27
+ class AutomaticallyApproveUnsubscriptionRequestFromHardBound(BooleanPreference):
28
+ section = mailing_section
29
+ name = "automatically_approve_unsubscription_request_from_hard_bounce"
30
+ default = False
31
+
32
+ verbose_name = _("Automatically approve unsubscription request from hard bounce")
33
+ help_text = _(
34
+ "Automatically approve unsubscription request from hard bounce received from the ESP tracking system"
35
+ )
@@ -0,0 +1,211 @@
1
+ import factory
2
+ import pytz
3
+ from wbcore.contrib.directory.factories import EmailContactFactory
4
+ from wbmailing.models import (
5
+ Mail,
6
+ MailEvent,
7
+ MailingList,
8
+ MailingListEmailContactThroughModel,
9
+ MailingListSubscriberChangeRequest,
10
+ MailTemplate,
11
+ MassMail,
12
+ )
13
+
14
+
15
+ class MailingListSubscriberChangeRequestFactory(factory.django.DjangoModelFactory):
16
+ class Meta:
17
+ model = MailingListSubscriberChangeRequest
18
+
19
+ expiration_date = factory.Faker("date_object")
20
+ status = MailingListSubscriberChangeRequest.Status.PENDING
21
+ type = MailingListSubscriberChangeRequest.Type.SUBSCRIBING
22
+ email_contact = factory.SubFactory("wbcore.contrib.directory.factories.EmailContactFactory")
23
+ mailing_list = factory.SubFactory("wbmailing.factories.MailingListFactory")
24
+ requester = factory.SubFactory("wbcore.contrib.authentication.factories.AuthenticatedPersonFactory")
25
+ approver = factory.SubFactory("wbcore.contrib.authentication.factories.AuthenticatedPersonFactory")
26
+ reason = factory.Faker("text", max_nb_chars=256)
27
+
28
+
29
+ class ApprovedMailingListSubscriberChangeRequest(MailingListSubscriberChangeRequestFactory):
30
+ status = MailingListSubscriberChangeRequest.Status.APPROVED
31
+
32
+
33
+ class MailingListFactory(factory.django.DjangoModelFactory):
34
+ class Meta:
35
+ model = MailingList
36
+
37
+ title = factory.Faker("text", max_nb_chars=64)
38
+ is_public = False
39
+
40
+ @factory.post_generation
41
+ def email_contacts(self, create, extracted, **kwargs):
42
+ if not create:
43
+ return
44
+ if extracted:
45
+ for email_contact in extracted:
46
+ MailingListEmailContactThroughModel.objects.create(
47
+ mailing_list=self,
48
+ email_contact=email_contact,
49
+ status=MailingListEmailContactThroughModel.Status.SUBSCRIBED,
50
+ )
51
+
52
+
53
+ class EmailContactMailingListFactory(MailingListFactory):
54
+ @factory.post_generation
55
+ def email_contacts(self, create, extracted, **kwargs):
56
+ mlscr = MailingListSubscriberChangeRequestFactory()
57
+ self.email_contacts.add(mlscr.email_contact)
58
+
59
+
60
+ class MailingListEmailContactFactory(EmailContactFactory):
61
+ @factory.post_generation
62
+ def subscriptions(self, create, extracted, **kwargs):
63
+ ml = MailingListFactory()
64
+ MailingListEmailContactThroughModel.objects.create(
65
+ mailing_list=ml, email_contact=self, status=MailingListEmailContactThroughModel.Status.SUBSCRIBED
66
+ )
67
+
68
+
69
+ class UnsubscribedMailingListEmailContactFactory(EmailContactFactory):
70
+ @factory.post_generation
71
+ def subscriptions(self, create, extracted, **kwargs):
72
+ ml = MailingListFactory()
73
+ MailingListEmailContactThroughModel.objects.create(
74
+ mailing_list=ml, email_contact=self, status=MailingListEmailContactThroughModel.Status.UNSUBSCRIBED
75
+ )
76
+
77
+
78
+ class MailingListEmailContactThroughModelFactory(factory.django.DjangoModelFactory):
79
+ email_contact = factory.SubFactory("wbcore.contrib.directory.factories.EmailContactFactory")
80
+ mailing_list = factory.SubFactory("wbmailing.factories.MailingListFactory")
81
+ status = MailingListEmailContactThroughModel.Status.SUBSCRIBED
82
+
83
+ class Meta:
84
+ model = MailingListEmailContactThroughModel
85
+
86
+
87
+ class MassMailFactory(factory.django.DjangoModelFactory):
88
+ class Meta:
89
+ model = MassMail
90
+
91
+ # status = #defaut = DRAFT
92
+ from_email = factory.Faker("email")
93
+ template = factory.SubFactory("wbmailing.factories.MailTemplateFactory")
94
+
95
+ @factory.post_generation
96
+ def mailing_lists(self, create, extracted, **kwargs):
97
+ if not create:
98
+ return
99
+ if extracted:
100
+ for mailing_list in extracted:
101
+ self.mailing_lists.add(mailing_list)
102
+
103
+ subject = factory.Faker("text", max_nb_chars=64)
104
+ body = factory.Faker("paragraph", nb_sentences=5)
105
+
106
+ @factory.post_generation
107
+ def attachments(self, create, extracted, **kwargs):
108
+ if not create:
109
+ return
110
+ if extracted:
111
+ for attachment in extracted:
112
+ self.attach_document(attachment)
113
+
114
+ # body_json = #JSONField(null=True, blank=True)
115
+ created = factory.Faker("date_time", tzinfo=pytz.utc)
116
+ creator = factory.SubFactory("wbcore.contrib.directory.factories.PersonFactory")
117
+ send_at = factory.Faker("date_time_between", start_date="now", end_date="+30y", tzinfo=pytz.utc)
118
+
119
+
120
+ class CustomMassMailFactory(MassMailFactory):
121
+ @factory.post_generation
122
+ def mailing_lists(self, create, extracted, **kwargs):
123
+ ml = MailingListFactory()
124
+ self.mailing_lists.add(ml)
125
+
126
+
127
+ class CustomMassMailEmailContactFactory(EmailContactFactory):
128
+ @factory.post_generation
129
+ def subscriptions(self, create, extracted, **kwargs):
130
+ ml = MailingListFactory.create()
131
+ ml.add_to_mailinglist(self)
132
+ MassMailFactory.create(mailing_lists=[ml])
133
+
134
+
135
+ class MailFactory(factory.django.DjangoModelFactory):
136
+ class Meta:
137
+ model = Mail
138
+
139
+ created = factory.Faker("date_time", tzinfo=pytz.utc)
140
+ last_send = factory.Faker("date_time", tzinfo=pytz.utc)
141
+ template = factory.SubFactory("wbmailing.factories.MailTemplateFactory")
142
+ message_ids = factory.List([factory.Faker("pystr")])
143
+ mass_mail = factory.SubFactory("wbmailing.factories.MassMailFactory")
144
+ from_email = factory.Faker("email")
145
+
146
+ @factory.post_generation
147
+ def to_email(self, create, extracted, **kwargs):
148
+ if not create:
149
+ return
150
+ if extracted:
151
+ for email in extracted:
152
+ self.to_email.add(email)
153
+
154
+ @factory.post_generation
155
+ def cc_email(self, create, extracted, **kwargs):
156
+ if not create:
157
+ return
158
+ if extracted:
159
+ for email in extracted:
160
+ self.cc_email.add(email)
161
+
162
+ @factory.post_generation
163
+ def bcc_email(self, create, extracted, **kwargs):
164
+ if not create:
165
+ return
166
+ if extracted:
167
+ for email in extracted:
168
+ self.bcc_email.add(email)
169
+
170
+ subject = factory.Faker("text", max_nb_chars=64)
171
+ body = factory.Faker("paragraph", nb_sentences=5)
172
+ # body_json = # JSONField(null=True, blank=True)
173
+
174
+ @factory.post_generation
175
+ def attachments(self, create, extracted, **kwargs):
176
+ if not create:
177
+ return
178
+ if extracted:
179
+ for attachment in extracted:
180
+ self.attach_document(attachment)
181
+
182
+
183
+ class ToEmailMailFactory(MailFactory):
184
+ @factory.post_generation
185
+ def to_email(self, create, extracted, **kwargs):
186
+ ec = EmailContactFactory.create()
187
+ self.to_email.add(ec)
188
+
189
+
190
+ class MailEventFactory(factory.django.DjangoModelFactory):
191
+ class Meta:
192
+ model = MailEvent
193
+
194
+ mail = factory.SubFactory("wbmailing.factories.MailFactory")
195
+ timestamp = factory.Faker("date_time", tzinfo=pytz.utc)
196
+ # event_type = # default=EventType.CREATED
197
+ reject_reason = "" # default=null # RejectReason.choices
198
+ description = factory.Faker("paragraph", nb_sentences=2)
199
+ recipient = factory.Faker("email")
200
+ click_url = factory.Faker("image_url")
201
+ ip = factory.Faker("ipv4")
202
+ user_agent = factory.Faker("first_name")
203
+ # raw_data = JSONField(default=dict, null=True, blank=True, verbose_name="Raw Data")
204
+
205
+
206
+ class MailTemplateFactory(factory.django.DjangoModelFactory):
207
+ class Meta:
208
+ model = MailTemplate
209
+
210
+ title = factory.Faker("text", max_nb_chars=64)
211
+ template = factory.Faker("paragraph", nb_sentences=5)
@@ -0,0 +1,8 @@
1
+ from .mailing_lists import (
2
+ EmailContactMailingListFilterSet,
3
+ MailingListEmailContactThroughModelModelFilterSet,
4
+ MailingListFilterSet,
5
+ MailingListSubscriberChangeRequestFilterSet,
6
+ MailStatusMassMailFilterSet,
7
+ )
8
+ from .mails import MailFilter, MassMailFilterSet