wbmailing 1.58.1__tar.gz → 1.59.10__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.
Files changed (71) hide show
  1. {wbmailing-1.58.1 → wbmailing-1.59.10}/PKG-INFO +1 -2
  2. {wbmailing-1.58.1 → wbmailing-1.59.10}/pyproject.toml +0 -1
  3. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/backend.py +4 -7
  4. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/factories.py +3 -0
  5. wbmailing-1.59.10/wbmailing/migrations/0004_alter_mailinglistemailcontactthroughmodel_unique_together_and_more.py +22 -0
  6. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/models/mailing_lists.py +3 -1
  7. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/models/mails.py +9 -6
  8. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tasks.py +4 -5
  9. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tests/models/test_mails.py +7 -7
  10. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tests/test_tasks.py +3 -3
  11. {wbmailing-1.58.1 → wbmailing-1.59.10}/.gitignore +0 -0
  12. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/__init__.py +0 -0
  13. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/admin.py +0 -0
  14. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/apps.py +0 -0
  15. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/celery.py +0 -0
  16. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/dynamic_preferences_registry.py +0 -0
  17. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/filters/__init__.py +0 -0
  18. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/filters/mailing_lists.py +0 -0
  19. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/filters/mails.py +0 -0
  20. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/locale/de/LC_MESSAGES/django.mo +0 -0
  21. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/locale/de/LC_MESSAGES/django.po +0 -0
  22. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/locale/de/LC_MESSAGES/django.po.translated +0 -0
  23. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/locale/en/LC_MESSAGES/django.mo +0 -0
  24. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/locale/en/LC_MESSAGES/django.po +0 -0
  25. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/locale/fr/LC_MESSAGES/django.mo +0 -0
  26. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/locale/fr/LC_MESSAGES/django.po +0 -0
  27. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/management/__init__.py +0 -0
  28. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/migrations/0001_initial_squashed_squashed_0008_alter_mail_bcc_email_alter_mail_cc_email_and_more.py +0 -0
  29. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/migrations/0002_delete_mailingsettings.py +0 -0
  30. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/migrations/0003_alter_mailinglistsubscriberchangerequest_options.py +0 -0
  31. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/migrations/__init__.py +0 -0
  32. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/models/__init__.py +0 -0
  33. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/serializers/__init__.py +0 -0
  34. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/serializers/mailing_lists.py +0 -0
  35. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/serializers/mails.py +0 -0
  36. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/templates/email_base_template.html +0 -0
  37. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/templates/mailing/maintain_mail_subsciptions.html +0 -0
  38. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/templates/mailing/manage_mailing_list_subscriptions.html +0 -0
  39. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/templates/template.html +0 -0
  40. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/templates/test.html +0 -0
  41. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/templates/workbench.html +0 -0
  42. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/templatetags/__init__.py +0 -0
  43. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/templatetags/mailing_tags.py +0 -0
  44. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tests/__init__.py +0 -0
  45. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tests/conftest.py +0 -0
  46. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tests/models/__init__.py +0 -0
  47. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tests/models/test_mailing_lists.py +0 -0
  48. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tests/signals.py +0 -0
  49. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tests/test_serializers.py +0 -0
  50. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tests/test_viewsets.py +0 -0
  51. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/tests/tests.py +0 -0
  52. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/urls.py +0 -0
  53. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/__init__.py +0 -0
  54. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/analytics.py +0 -0
  55. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/buttons/__init__.py +0 -0
  56. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/buttons/mailing_lists.py +0 -0
  57. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/buttons/mails.py +0 -0
  58. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/display/__init__.py +0 -0
  59. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/display/mailing_lists.py +0 -0
  60. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/display/mails.py +0 -0
  61. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/endpoints/__init__.py +0 -0
  62. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/endpoints/mailing_lists.py +0 -0
  63. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/endpoints/mails.py +0 -0
  64. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/mailing_lists.py +0 -0
  65. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/mails.py +0 -0
  66. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/menu/__init__.py +0 -0
  67. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/menu/mailing_lists.py +0 -0
  68. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/menu/mails.py +0 -0
  69. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/titles/__init__.py +0 -0
  70. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/titles/mailing_lists.py +0 -0
  71. {wbmailing-1.58.1 → wbmailing-1.59.10}/wbmailing/viewsets/titles/mails.py +0 -0
@@ -1,6 +1,5 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbmailing
3
- Version: 1.58.1
3
+ Version: 1.59.10
4
4
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
5
- Requires-Dist: sentry-sdk==2.*
6
5
  Requires-Dist: wbcore
@@ -6,7 +6,6 @@ dynamic = ["version"]
6
6
 
7
7
  dependencies = [
8
8
  "wbcore",
9
- "sentry-sdk == 2.*",
10
9
  ]
11
10
 
12
11
  [tool.uv.sources]
@@ -1,20 +1,21 @@
1
+ import logging
1
2
  from datetime import timedelta
2
3
 
3
4
  from anymail.backends.mailgun import EmailBackend as AnymailMailgunBackend
4
5
  from anymail.backends.mailjet import EmailBackend as AnymailMailjetBackend
5
6
  from anymail.backends.mandrill import EmailBackend as AnymailMandrillBackend
6
7
  from anymail.backends.postmark import EmailBackend as AnymailPostmarkBackend
7
- from anymail.backends.sendgrid import EmailBackend as AnymailSendgridBackend
8
8
  from anymail.backends.sendinblue import EmailBackend as AnymailSendinblueBackend
9
9
  from anymail.exceptions import AnymailError
10
10
  from django.conf import settings
11
11
  from django.core.mail.backends.console import EmailBackend as ConsoleBackend
12
12
  from django.utils import timezone
13
- from sentry_sdk import capture_message
14
13
  from wbcore.utils.html import convert_html2text
15
14
 
16
15
  from wbmailing.models import Mail, MailEvent
17
16
 
17
+ logger = logging.getLogger("mailing")
18
+
18
19
 
19
20
  class SendMessagesMixin:
20
21
  def _process_msg(self, message):
@@ -66,7 +67,7 @@ class SendMessagesMixin:
66
67
  mail.last_send = timezone.now()
67
68
  mail.save()
68
69
  except AnymailError as e:
69
- capture_message(e)
70
+ logger.warning(e)
70
71
  if self.fail_silently:
71
72
  sent = False
72
73
  else:
@@ -90,10 +91,6 @@ class PostmarkEmailBackend(SendMessagesMixin, AnymailPostmarkBackend):
90
91
  return super().send_messages(email_messages)
91
92
 
92
93
 
93
- class SendgridEmailBackend(SendMessagesMixin, AnymailSendgridBackend):
94
- pass
95
-
96
-
97
94
  class MailgunEmailBackend(SendMessagesMixin, AnymailMailgunBackend):
98
95
  pass
99
96
 
@@ -34,6 +34,7 @@ class ApprovedMailingListSubscriberChangeRequest(MailingListSubscriberChangeRequ
34
34
  class MailingListFactory(factory.django.DjangoModelFactory):
35
35
  class Meta:
36
36
  model = MailingList
37
+ skip_postgeneration_save = True
37
38
 
38
39
  title = factory.Faker("text", max_nb_chars=64)
39
40
  is_public = False
@@ -88,6 +89,7 @@ class MailingListEmailContactThroughModelFactory(factory.django.DjangoModelFacto
88
89
  class MassMailFactory(factory.django.DjangoModelFactory):
89
90
  class Meta:
90
91
  model = MassMail
92
+ skip_postgeneration_save = True
91
93
 
92
94
  # status = #defaut = DRAFT
93
95
  from_email = factory.Faker("email")
@@ -136,6 +138,7 @@ class CustomMassMailEmailContactFactory(EmailContactFactory):
136
138
  class MailFactory(factory.django.DjangoModelFactory):
137
139
  class Meta:
138
140
  model = Mail
141
+ skip_postgeneration_save = True
139
142
 
140
143
  created = factory.Faker("date_time", tzinfo=pytz.utc)
141
144
  last_send = factory.Faker("date_time", tzinfo=pytz.utc)
@@ -0,0 +1,22 @@
1
+ # Generated by Django 5.2.9 on 2025-12-16 15:26
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('directory', '0014_alter_entry_relationship_managers_and_more'),
10
+ ('wbmailing', '0003_alter_mailinglistsubscriberchangerequest_options'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterUniqueTogether(
15
+ name='mailinglistemailcontactthroughmodel',
16
+ unique_together=set(),
17
+ ),
18
+ migrations.AddConstraint(
19
+ model_name='mailinglistemailcontactthroughmodel',
20
+ constraint=models.UniqueConstraint(fields=('mailing_list', 'email_contact'), name='unique_mailinglistcontact'),
21
+ ),
22
+ ]
@@ -235,7 +235,9 @@ class MailingListEmailContactThroughModel(models.Model):
235
235
  )
236
236
 
237
237
  class Meta:
238
- unique_together = ("mailing_list", "email_contact")
238
+ constraints = (
239
+ models.UniqueConstraint(name="unique_mailinglistcontact", fields=("mailing_list", "email_contact")),
240
+ )
239
241
 
240
242
  def __str__(self) -> str:
241
243
  return f"{self.mailing_list} - {self.email_contact}"
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import logging
2
3
  from datetime import timedelta
3
4
 
4
5
  from anymail.exceptions import AnymailRecipientsRefused
@@ -21,7 +22,6 @@ from django_fsm import FSMField, transition
21
22
  from dynamic_preferences.registries import global_preferences_registry
22
23
  from psycopg.types.range import TimestamptzRange
23
24
  from rest_framework.reverse import reverse
24
- from sentry_sdk import capture_message
25
25
  from wbcore.contrib.color.enums import WBColor
26
26
  from wbcore.contrib.directory.models import EmailContact
27
27
  from wbcore.contrib.documents.models import Document, DocumentType
@@ -35,9 +35,12 @@ from wbcore.metadata.configs.display.instance_display.shortcuts import (
35
35
  )
36
36
  from wbcore.models import WBModel
37
37
  from wbcore.utils.html import convert_html2text
38
+ from wbcore.workers import Queue
38
39
 
39
40
  from .mailing_lists import MailingListEmailContactThroughModel
40
41
 
42
+ logger = logging.getLogger("mailing")
43
+
41
44
 
42
45
  def can_administrate_mail(mail, user):
43
46
  return user.has_perm("wbmailing.can_administrate_mail")
@@ -307,7 +310,7 @@ class MassMail(DocumentMixin, WBModel):
307
310
  context["attachment_url"] = attachment_url
308
311
  if emails.exists():
309
312
  email_contact = emails.first()
310
- unsubscribe_url = f'{settings.BASE_ENDPOINT_URL}{reverse("wbmailing:manage_mailing_list_subscriptions", args=[email_contact.id])}'
313
+ unsubscribe_url = f"{settings.BASE_ENDPOINT_URL}{reverse('wbmailing:manage_mailing_list_subscriptions', args=[email_contact.id])}"
311
314
  context["unsubscribe"] = f"<a href={unsubscribe_url}>" + _("Unsubscribe</a>")
312
315
  entry = email_contact.entry
313
316
  if entry:
@@ -820,10 +823,10 @@ def handle_mail_tracking(sender, event, esp_name, **kwargs): # noqa: C901
820
823
  )
821
824
 
822
825
  else:
823
- capture_message(gettext("Received event but could not find related mail"))
826
+ logger.warning(f"Received event but could not find related mail: {event}")
824
827
 
825
828
 
826
- @shared_task
829
+ @shared_task(queue=Queue.HIGH_PRIORITY.value)
827
830
  def send_mail_as_task(
828
831
  subject=None,
829
832
  body=None,
@@ -847,7 +850,7 @@ def send_mail_as_task(
847
850
  msg.send()
848
851
 
849
852
 
850
- @shared_task
853
+ @shared_task(queue=Queue.DEFAULT.value)
851
854
  def send_mass_mail_as_task(mass_mail_id: int, email_address: str):
852
855
  mass_mail = MassMail.objects.get(id=mass_mail_id)
853
856
  try:
@@ -869,7 +872,7 @@ def send_mass_mail_as_task(mass_mail_id: int, email_address: str):
869
872
  )
870
873
 
871
874
 
872
- @shared_task
875
+ @shared_task(queue=Queue.DEFAULT.value)
873
876
  def send_mail_task(mass_mail_id):
874
877
  """
875
878
  MailingListSubscriberChangeRequest post_save signal: Automatically approve if user is superuser/manager
@@ -1,10 +1,9 @@
1
1
  from __future__ import absolute_import, unicode_literals
2
2
 
3
- from datetime import datetime
4
-
5
3
  from celery import shared_task
6
4
  from django.db.models import Q
7
5
  from django.utils import timezone
6
+ from wbcore.workers import Queue
8
7
 
9
8
  from wbmailing.models import (
10
9
  MailingListEmailContactThroughModel,
@@ -13,7 +12,7 @@ from wbmailing.models import (
13
12
  )
14
13
 
15
14
 
16
- @shared_task
15
+ @shared_task(queue=Queue.BACKGROUND.value)
17
16
  def check_and_remove_expired_mailinglist_subscription(date=None):
18
17
  """
19
18
  Shared tasks to expire contact in MailingList.
@@ -30,9 +29,9 @@ def check_and_remove_expired_mailinglist_subscription(date=None):
30
29
  request.save()
31
30
 
32
31
 
33
- @shared_task
32
+ @shared_task(queue=Queue.BACKGROUND.value)
34
33
  def periodic_send_mass_mail_as_tasks():
35
- mass_mails = MassMail.objects.filter(Q(status=MassMail.Status.SEND_LATER) & Q(send_at__lte=datetime.now()))
34
+ mass_mails = MassMail.objects.filter(Q(status=MassMail.Status.SEND_LATER) & Q(send_at__lte=timezone.now()))
36
35
  for mass_mail in mass_mails:
37
36
  mass_mail.send()
38
37
  mass_mail.save()
@@ -9,7 +9,7 @@ from wbcore.contrib.directory.factories import EmailContactFactory
9
9
  from wbcore.contrib.documents.factories import DocumentFactory
10
10
  from wbcore.test.utils import get_or_create_superuser
11
11
 
12
- from wbmailing.backend import SendgridEmailBackend
12
+ from wbmailing.backend import AnymailPostmarkBackend
13
13
  from wbmailing.factories import ToEmailMailFactory
14
14
  from wbmailing.models import MailEvent, MassMail
15
15
  from wbmailing.models.mails import (
@@ -153,20 +153,20 @@ class TestSpecificModels:
153
153
  esp_event=esp_event,
154
154
  )
155
155
  # sender(class) – The source of the event. (One of theanymail.webhook.*Viewclasses, but you generally won’t examine this parameter; it’s required by Django’s signalmechanism.
156
- tracking.send(sender=msg, event=mevent, esp_name="SendGrid")
156
+ tracking.send(sender=msg, event=mevent, esp_name="Postmark")
157
157
 
158
158
  if exits_message_id == "YES":
159
159
  assert MailEvent.objects.filter(mail=ml, recipient=mailevent.recipient).count() == 2
160
160
  else:
161
161
  assert MailEvent.objects.filter(mail=ml, recipient=mailevent.recipient).count() == 1
162
162
 
163
- @override_settings(EMAIL_BACKEND="anymail.backends.test.EmailBackend", ANYMAIL_SENDGRID_API_KEY="TEST")
164
- @patch("wbmailing.backend.SendgridEmailBackend._send")
163
+ @override_settings(EMAIL_BACKEND="anymail.backends.test.EmailBackend", ANYMAIL_POSTMARK_SERVER_TOKEN="TEST")
164
+ @patch("wbmailing.backend.AnymailPostmarkBackend._send")
165
165
  @pytest.mark.parametrize("resend, exit_document", [(False, False), (False, True), (True, False)])
166
- def test_sendgridemailbackend_send_messages(self, mock_send, resend, exit_document):
166
+ def test_emailbackend_send_messages(self, mock_send, resend, exit_document):
167
167
  request = APIRequestFactory().get("")
168
168
  request.user = get_or_create_superuser()
169
- num_sent = SendgridEmailBackend().send_messages(None)
169
+ num_sent = AnymailPostmarkBackend().send_messages(None)
170
170
  assert num_sent == 0
171
171
  doc1 = DocumentFactory()
172
172
  ml = ToEmailMailFactory(cc_email=(EmailContactFactory(),), attachments=(doc1,))
@@ -193,7 +193,7 @@ class TestSpecificModels:
193
193
  mock_send.return_value.status_code = 200
194
194
  mock_send.return_value.json.return_value = msg
195
195
 
196
- num_sent = SendgridEmailBackend().send_messages(email_messages)
196
+ num_sent = AnymailPostmarkBackend().send_messages(email_messages)
197
197
  mock_send.assert_called()
198
198
  assert mock_send.call_count == 1
199
199
  assert num_sent == 1
@@ -1,4 +1,4 @@
1
- from datetime import datetime, timedelta
1
+ from datetime import timedelta
2
2
  from unittest.mock import patch
3
3
 
4
4
  import pytest
@@ -40,11 +40,11 @@ class TestSpecificTasks:
40
40
  send_date = timezone.now() - timedelta(minutes=15)
41
41
  mass_mail_factory(status=MassMail.Status.SEND_LATER, send_at=send_date)
42
42
  assert (
43
- MassMail.objects.filter(Q(status=MassMail.Status.SEND_LATER) & Q(send_at__lte=datetime.now())).count() == 1
43
+ MassMail.objects.filter(Q(status=MassMail.Status.SEND_LATER) & Q(send_at__lte=timezone.now())).count() == 1
44
44
  )
45
45
  periodic_send_mass_mail_as_tasks()
46
46
  send_mail_task.assert_called()
47
47
  assert send_mail_task.call_count == 1
48
48
  assert (
49
- MassMail.objects.filter(Q(status=MassMail.Status.SEND_LATER) & Q(send_at__lte=datetime.now())).count() == 0
49
+ MassMail.objects.filter(Q(status=MassMail.Status.SEND_LATER) & Q(send_at__lte=timezone.now())).count() == 0
50
50
  )
File without changes