django-codenerix-email 4.0.39__py2.py3-none-any.whl → 4.0.41__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.
@@ -1,4 +1,4 @@
1
- __version__ = "4.0.39"
1
+ __version__ = "4.0.41"
2
2
 
3
3
  __authors__ = [
4
4
  "Juan Miguel Taboada Godoy <juanmi@juanmitaboada.com>",
codenerix_email/forms.py CHANGED
@@ -94,7 +94,7 @@ class EmailMessageForm(GenModelForm):
94
94
  return [
95
95
  (
96
96
  _("Details"),
97
- 6,
97
+ 4,
98
98
  ["uuid", 3],
99
99
  ["created", 3],
100
100
  ["updated", 3],
@@ -102,20 +102,27 @@ class EmailMessageForm(GenModelForm):
102
102
  ["eto", 3],
103
103
  ["subject", 3],
104
104
  ["opened", 3],
105
- ["content_subtype", 3],
106
105
  ),
107
106
  (
108
107
  _("System"),
109
- 6,
108
+ 4,
110
109
  ["sending", 3],
111
110
  ["sent", 3],
112
111
  ["error", 3],
113
- ["retries", 3],
114
- ["priority", 3],
115
112
  ["log", 3],
113
+ ["content_subtype", 3],
116
114
  ["unsubscribe_url", 3],
117
115
  ["headers", 3],
118
116
  ),
117
+ (
118
+ _("Status"),
119
+ 4,
120
+ ["retries", 3],
121
+ ["priority", 3],
122
+ ["bounces_total", 3],
123
+ ["bounces_soft", 3],
124
+ ["bounces_hard", 3],
125
+ ),
119
126
  (
120
127
  _("Body"),
121
128
  12,
@@ -1,4 +1,5 @@
1
1
  import re
2
+ import codecs
2
3
  from textwrap import dedent
3
4
  from argparse import RawTextHelpFormatter
4
5
 
@@ -144,6 +145,24 @@ class Command(BaseCommand):
144
145
  "--rewrite", action="store_true", help="Rewrite existing"
145
146
  )
146
147
 
148
+ def validate_encoding(self, encoding: str | None) -> str:
149
+ """
150
+ Validates and returns a safe encoding name.
151
+ """
152
+ # If an encoding is specified, verify it's valid
153
+ if encoding:
154
+ try:
155
+ # Attempt to look up the encoding to see if it's known
156
+ codecs.lookup(encoding)
157
+ except LookupError:
158
+ # If the lookup fails, the encoding is unknown
159
+ # Fall back to a safe default like 'latin-1'
160
+ encoding = "latin-1"
161
+ else:
162
+ # If no encoding is specified, use a default
163
+ encoding = "utf-8"
164
+ return encoding
165
+
147
166
  def handle(self, *args, **options):
148
167
  # Get configuration
149
168
  self.silent = options["silent"]
@@ -328,10 +347,15 @@ class Command(BaseCommand):
328
347
  # Parse the email
329
348
  msg = message_from_bytes(raw_email)
330
349
 
331
- # Extract subject, efrom, eto & eid
350
+ # Extract subject
332
351
  subject, encoding = decode_header(msg["Subject"])[0]
352
+
353
+ # Decode subject if it's bytes
333
354
  if isinstance(subject, bytes):
334
- subject = subject.decode(encoding or "utf-8")
355
+ charset = self.validate_encoding(encoding)
356
+ subject = subject.decode(charset, "ignore")
357
+
358
+ # Extract other headers
335
359
  efrom = msg.get("From")
336
360
  eto = msg.get("To")
337
361
  eid = msg.get("Message-ID")
@@ -353,22 +377,31 @@ class Command(BaseCommand):
353
377
  for part in msg.walk():
354
378
  content_type = part.get_content_type()
355
379
  if content_type == "text/plain" and not body_plain:
380
+ charset = self.validate_encoding(
381
+ part.get_content_charset()
382
+ )
356
383
  body_plain = part.get_payload(
357
384
  decode=True
358
385
  ).decode(
359
- part.get_content_charset() or "utf-8",
386
+ charset,
360
387
  "ignore",
361
388
  )
362
389
  elif content_type == "text/html" and not body_html:
390
+ charset = self.validate_encoding(
391
+ part.get_content_charset()
392
+ )
363
393
  body_html = part.get_payload(
364
394
  decode=True
365
395
  ).decode(
366
- part.get_content_charset() or "utf-8",
396
+ charset,
367
397
  "ignore",
368
398
  )
369
399
  else:
400
+ charset = self.validate_encoding(
401
+ msg.get_content_charset()
402
+ )
370
403
  body_plain = msg.get_payload(decode=True).decode(
371
- msg.get_content_charset() or "utf-8",
404
+ charset,
372
405
  "ignore",
373
406
  )
374
407
 
@@ -416,9 +449,10 @@ class Command(BaseCommand):
416
449
  headers = {}
417
450
  for header, value in msg.items():
418
451
  decoded_value, encoding = decode_header(value)[0]
452
+ charset = self.validate_encoding(encoding)
419
453
  if isinstance(decoded_value, bytes):
420
454
  decoded_value = decoded_value.decode(
421
- encoding or "utf-8", "ignore"
455
+ charset, "ignore"
422
456
  )
423
457
  headers[header] = decoded_value
424
458
 
@@ -453,7 +487,8 @@ class Command(BaseCommand):
453
487
  if self.verbose:
454
488
  self.stdout.write(
455
489
  self.style.SUCCESS(
456
- f"Deleted email with IMAP ID: {imap_id}"
490
+ "Deleted email with "
491
+ f"IMAP ID: {imap_id}"
457
492
  )
458
493
  )
459
494
 
@@ -487,6 +522,10 @@ class Command(BaseCommand):
487
522
  # Save the received email
488
523
  email_received.save()
489
524
 
525
+ # Recalculate the email status if linked to a sent email
526
+ if email_message:
527
+ email_message.recalculate_bounces()
528
+
490
529
  # Count created or overwritten
491
530
  if overwriting:
492
531
  overwrite_count += 1
@@ -543,7 +582,7 @@ class Command(BaseCommand):
543
582
  )
544
583
  )
545
584
  elif getattr(settings, "IMAP_EMAIL_SEEN", True):
546
- # Mark the message as read otherwise (to avoid reprocessing)
585
+ # Mark the message as read otherwise (avoids reprocessing)
547
586
  server.add_flags(imap_id, [b"\\Seen"])
548
587
 
549
588
  return (created_count, overwrite_count)
@@ -591,7 +630,9 @@ class Command(BaseCommand):
591
630
  headers_payload = part.get_payload(decode=True)
592
631
  if isinstance(headers_payload, bytes):
593
632
  # Decode using the specified charset
594
- charset = part.get_content_charset() or "utf-8"
633
+ charset = self.validate_encoding(
634
+ part.get_content_charset()
635
+ )
595
636
  headers_text = headers_payload.decode(
596
637
  charset, errors="ignore"
597
638
  )
@@ -615,7 +656,9 @@ class Command(BaseCommand):
615
656
  payload = part.get_payload(decode=True)
616
657
  if isinstance(payload, bytes):
617
658
  # Decode using the specified charset
618
- charset = part.get_content_charset() or "utf-8"
659
+ charset = self.validate_encoding(
660
+ part.get_content_charset()
661
+ )
619
662
  body_text += payload.decode(
620
663
  charset, errors="ignore"
621
664
  )
@@ -626,7 +669,9 @@ class Command(BaseCommand):
626
669
  payload = msg.get_payload(decode=True)
627
670
  if isinstance(payload, bytes):
628
671
  # Decode using the specified charset
629
- charset = msg.get_content_charset() or "utf-8"
672
+ charset = self.validate_encoding(
673
+ msg.get_content_charset()
674
+ )
630
675
  body_text = payload.decode(
631
676
  charset, errors="ignore"
632
677
  )
@@ -0,0 +1,28 @@
1
+ # Generated by Django 5.2.7 on 2025-10-14 05:10
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('codenerix_email', '0014_emailreceived'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='emailmessage',
15
+ name='bounces_hard',
16
+ field=models.PositiveIntegerField(default=0, verbose_name='Hard bounces'),
17
+ ),
18
+ migrations.AddField(
19
+ model_name='emailmessage',
20
+ name='bounces_soft',
21
+ field=models.PositiveIntegerField(default=0, verbose_name='Soft bounces'),
22
+ ),
23
+ migrations.AddField(
24
+ model_name='emailmessage',
25
+ name='bounces_total',
26
+ field=models.PositiveIntegerField(default=0, verbose_name='Total bounces'),
27
+ ),
28
+ ]
codenerix_email/models.py CHANGED
@@ -114,6 +114,34 @@ class EmailMessage(CodenerixModel):
114
114
  _("Unsubscribe URL"), blank=True, null=True
115
115
  )
116
116
  headers = models.JSONField(_("Headers"), blank=True, null=True)
117
+ bounces_soft = models.PositiveIntegerField(
118
+ _("Soft bounces"), blank=False, null=False, default=0
119
+ )
120
+ bounces_hard = models.PositiveIntegerField(
121
+ _("Hard bounces"), blank=False, null=False, default=0
122
+ )
123
+ bounces_total = models.PositiveIntegerField(
124
+ _("Total bounces"), blank=False, null=False, default=0
125
+ )
126
+
127
+ def recalculate_bounces(self):
128
+ bounces_soft = self.receiveds.filter(bounce_type=BOUNCE_SOFT).count()
129
+ bounces_hard = self.receiveds.filter(bounce_type=BOUNCE_HARD).count()
130
+ bounces_total = self.receiveds.filter(
131
+ bounce_type__isnull=False
132
+ ).count()
133
+ changed = False
134
+ if self.bounces_soft != bounces_soft:
135
+ self.bounces_soft = bounces_soft
136
+ changed = True
137
+ if self.bounces_hard != bounces_hard:
138
+ self.bounces_hard = bounces_hard
139
+ changed = True
140
+ if self.bounces_total != bounces_total:
141
+ self.bounces_total = bounces_total
142
+ changed = True
143
+ if changed:
144
+ self.save()
117
145
 
118
146
  def __fields__(self, info):
119
147
  fields = []
@@ -127,6 +155,7 @@ class EmailMessage(CodenerixModel):
127
155
  # fields.append(("efrom", _("From")))
128
156
  fields.append(("eto", _("To")))
129
157
  fields.append(("subject", _("Subject")))
158
+ fields.append(("bounces_total", _("Bounces")))
130
159
  fields.append(("retries", _("Retries")))
131
160
  fields.append(("next_retry", _("Next retry")))
132
161
  fields.append(("pk", _("ID")))
@@ -169,7 +198,6 @@ class EmailMessage(CodenerixModel):
169
198
  return {
170
199
  "sent": (_("Sent"), lambda x: mailstatus(x), mailoptions),
171
200
  "uuid": (_("UUID"), lambda x: Q(uuid__icontains=x), "input"),
172
- "priority": (_("Priority"), lambda x: Q(priority=x), "input"),
173
201
  "opened": (
174
202
  _("Opened"),
175
203
  lambda x: ~Q(opened__isnull=x),
@@ -177,25 +205,12 @@ class EmailMessage(CodenerixModel):
177
205
  ),
178
206
  # "efrom": (_("From"), lambda x: Q(efrom__icontains=x), "input"),
179
207
  "eto": (_("To"), lambda x: Q(eto__icontains=x), "input"),
180
- "retries": (_("Retries"), lambda x: Q(retries=x), "input"),
181
208
  "pk": (_("ID"), lambda x: Q(pk=x), "input"),
182
209
  }
183
210
 
184
211
  def __unicode__(self):
185
212
  return "{} ({})".format(self.eto, self.pk)
186
213
 
187
- @property
188
- def bounces_soft(self):
189
- return self.receiveds.filter(bounce_type=BOUNCE_SOFT).count()
190
-
191
- @property
192
- def bounces_hard(self):
193
- return self.receiveds.filter(bounce_type=BOUNCE_HARD).count()
194
-
195
- @property
196
- def bounces_total(self):
197
- return self.receiveds.filter(bounce_type__isnull=False).count()
198
-
199
214
  def clean(self):
200
215
  if not isinstance(self.headers, dict):
201
216
  raise ValidationError(_("HEADERS must be a Dictionary"))
@@ -14,7 +14,7 @@
14
14
  </td>
15
15
  <td>{{row.eto|codenerix}}</td>
16
16
  <td><nobr>{{row.subject|codenerix}}</nobr></td>
17
- <td class="text-nowrap">{{row.bounces_total_count|codenerix}}</td>
17
+ <td class="text-nowrap">{{row.bounces_total|codenerix}}</td>
18
18
  <td><center>{{row.retries|codenerix}}</center></td>
19
19
  <td class="text-nowrap">{{row.next_retry|codenerix}}</td>
20
20
  <td><center>{{row.pk|codenerix}}</center></td>
codenerix_email/views.py CHANGED
@@ -28,7 +28,7 @@ from django.shortcuts import get_object_or_404
28
28
  from django.utils.translation import gettext as _
29
29
  from django.conf import settings
30
30
  from django.http import HttpRequest, Http404
31
- from django.db.models import Q, Count
31
+ from django.db.models import Q
32
32
 
33
33
  from codenerix.multiforms import MultiForm # type: ignore
34
34
  from codenerix.views import ( # type: ignore
@@ -40,15 +40,12 @@ from codenerix.views import ( # type: ignore
40
40
  GenDelete,
41
41
  GenDetail,
42
42
  GenDetailModal,
43
- SearchFilters,
44
43
  )
45
44
  from codenerix_email.models import (
46
45
  EmailTemplate,
47
46
  EmailMessage,
48
47
  EmailReceived,
49
48
  MODELS,
50
- BOUNCE_SOFT,
51
- BOUNCE_HARD,
52
49
  )
53
50
  from codenerix_email.forms import (
54
51
  EmailTemplateForm,
@@ -106,7 +103,7 @@ class EmailFollow(View):
106
103
  # Return an image of 1x1 pixel
107
104
  return HttpResponse(
108
105
  base64.b64decode(
109
- "R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
106
+ "R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" # noqa: E501
110
107
  ),
111
108
  content_type="image/gif",
112
109
  )
@@ -186,90 +183,6 @@ class EmailMessageList(GenList):
186
183
  "menu": ["codenerix_email", "emailmessages"],
187
184
  "bread": [_("Emails"), _("Email Messages")],
188
185
  }
189
- annotations = {
190
- # "bounces_soft_count": Count(
191
- # "receiveds__pk", filter=Q(receiveds__bounce_type=BOUNCE_SOFT)
192
- # ),
193
- # "bounces_hard_count": Count(
194
- # "receiveds__pk", filter=Q(receiveds__bounce_type=BOUNCE_HARD)
195
- # ),
196
- "bounces_total_count": Count(
197
- "receiveds__pk", filter=Q(receiveds__bounce_type__isnull=False)
198
- ),
199
- }
200
-
201
- def __fields__(self, info):
202
- fields = []
203
- fields.append(("sending", None))
204
- fields.append(("error", None))
205
- fields.append(("sent", _("Send")))
206
- fields.append(("priority", _("Priority")))
207
- fields.append(("created", _("Created")))
208
- fields.append(("updated", _("Updated")))
209
- fields.append(("opened", _("Opened")))
210
- # fields.append(("efrom", _("From")))
211
- fields.append(("eto", _("To")))
212
- fields.append(("subject", _("Subject")))
213
- fields.append(("bounces_total_count", _("Bounces")))
214
- # fields.append(("bounces_soft_count", _("Soft bounces")))
215
- # fields.append(("bounces_hard_count", _("Hard bounces")))
216
- fields.append(("retries", _("Retries")))
217
- fields.append(("next_retry", _("Next retry")))
218
- fields.append(("pk", _("ID")))
219
- fields.append(("uuid", _("UUID")))
220
- fields.append(("unsubscribe_url", _("Unsubscribe")))
221
- fields.append(("content_subtype", _("Content Subtype")))
222
- return fields
223
-
224
- def __searchF__(self, info): # noqa: N802
225
- def mailstatus(x):
226
- if x == "D":
227
- return Q(error=False, sent=True)
228
- elif x == "P":
229
- return Q(error=False, sent=False, sending=False)
230
- elif x == "S":
231
- return Q(error=False, sent=False, sending=True)
232
- elif x == "E":
233
- return Q(error=True)
234
- else:
235
- return Q()
236
-
237
- mailoptions = [
238
- ("D", _("Sent")), # Sent - Done
239
- ("P", _("Pending")), # Pending - Pending
240
- ("S", _("Sending")), # Sending - Sending
241
- ("E", _("Error")), # Error - Error
242
- ]
243
-
244
- return {
245
- "sent": (_("Sent"), lambda x: mailstatus(x), mailoptions),
246
- "uuid": (_("UUID"), lambda x: Q(uuid__icontains=x), "input"),
247
- "priority": (_("Priority"), lambda x: Q(priority=x), "input"),
248
- "opened": (
249
- _("Opened"),
250
- lambda x: ~Q(opened__isnull=x),
251
- [(True, _("Yes")), (False, _("No"))],
252
- ),
253
- # "efrom": (_("From"), lambda x: Q(efrom__icontains=x), "input"),
254
- "eto": (_("To"), lambda x: Q(eto__icontains=x), "input"),
255
- "retries": (_("Retries"), lambda x: Q(retries=x), "input"),
256
- "pk": (_("ID"), lambda x: Q(pk=x), "input"),
257
- "bounces_total_count": (
258
- _("Bounces"),
259
- SearchFilters.number("bounces_total_count"),
260
- "input",
261
- ),
262
- "bounces_soft_count": (
263
- _("Soft bounces"),
264
- SearchFilters.number("bounces_soft_count"),
265
- "input",
266
- ),
267
- "bounces_hard_count": (
268
- _("Hard bounces"),
269
- SearchFilters.number("bounces_hard_count"),
270
- "input",
271
- ),
272
- }
273
186
 
274
187
 
275
188
  class EmailMessageCreate(GenCreate):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-codenerix-email
3
- Version: 4.0.39
3
+ Version: 4.0.41
4
4
  Summary: Codenerix Email is a module that enables CODENERIX to set send emails in a general manner.
5
5
  Home-page: https://github.com/codenerix/django-codenerix-email
6
6
  Author: Juan Miguel Taboada Godoy <juanmi@juanmitaboada.com>, Juan Soler Ruiz <soleronline@gmail.com>
@@ -1,13 +1,13 @@
1
- codenerix_email/__init__.py,sha256=jy3doviOd16K4l2OxN9UBbjhtzx2zjQmwaHgbqXosnw,149
1
+ codenerix_email/__init__.py,sha256=fvnPvF-O5l4qg-_eeRjKHdMZDzfLHFHFDi1l8fzeLQ4,149
2
2
  codenerix_email/admin.py,sha256=w259UKFk_opGEl6PJjYHXWAHQ_8emgqmiixKT5Rid4A,1180
3
3
  codenerix_email/apps.py,sha256=WXqu1XQibDDyCvvQYt2JbTK4GIpW8BNv5DCbRJS2mmk,149
4
- codenerix_email/forms.py,sha256=38byLGxg1MOLAY1kAYChxYZj64tSgyfRvDcSbIOdV0I,5521
5
- codenerix_email/models.py,sha256=e1BqaycD0sVTYwpeUlow50ijyW55Da-9kQDXaABxZrw,25311
4
+ codenerix_email/forms.py,sha256=P5w-ebxLR1V10RFDqO_R5vvZV-vY6SLTzVb1OLdFZE0,5710
5
+ codenerix_email/models.py,sha256=z0tcaOy1LCs8QBelJmn5O9Fn3GAS3_bQT59RBi-Otm4,25948
6
6
  codenerix_email/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  codenerix_email/test_settings.py,sha256=A9WT-2MMuqvW5Mrd3lkmjrf0xJZmJl3McOEeyJug1Xc,633
8
8
  codenerix_email/urls.py,sha256=M760qDSVV3EoY9aIPdnk8DtUv5tyWO-FQWdody4KTeM,3996
9
9
  codenerix_email/urls_frontend.py,sha256=DG5WS_fGQJC9sezNebPwG-E_WtUWy3Kwz4VUxh82xuc,873
10
- codenerix_email/views.py,sha256=9HXoWbO9ZMhmpIqTWAr9P65HJTluh-NPsl24JgQcgcA,10078
10
+ codenerix_email/views.py,sha256=7j9lIUJ8TMZlFx6GzDZ_R9QC6Krt__z3Dfr0ZO_IcKE,6707
11
11
  codenerix_email/.mypy_cache/.gitignore,sha256=amnaZw0RUw038PDP3HvtMLeOpkNOJPenMgi5guKdMiw,34
12
12
  codenerix_email/.mypy_cache/CACHEDIR.TAG,sha256=8cE6_FVTWMkDOw8fMKqhd_6IvaQPS4okWYQA1UeHatw,190
13
13
  codenerix_email/.mypy_cache/3.10/@plugins_snapshot.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
@@ -827,8 +827,8 @@ codenerix_email/.mypy_cache/3.10/zoneinfo/_common.data.json,sha256=e4xbNKL_yQ5h5
827
827
  codenerix_email/.mypy_cache/3.10/zoneinfo/_common.meta.json,sha256=5K19XWobpSjKwiv7bZFZRcxsEfoh7b-FhYcmqB-2Iic,1737
828
828
  codenerix_email/.mypy_cache/3.10/zoneinfo/_tzpath.data.json,sha256=CFx7Q1XfUhhuNX69prkxyirG8rfvEDCNgEHWQigKC_A,5632
829
829
  codenerix_email/.mypy_cache/3.10/zoneinfo/_tzpath.meta.json,sha256=d1HJ_xFBI1orlZSVhH0gHWLI-dJG3zY-ZOlctOl62yU,1765
830
- codenerix_email/__pycache__/__init__.cpython-310.pyc,sha256=jR8Y-ZqrC_AzgwY3EjpVl3TOkhtTgWepEoei20YfoQw,313
831
- codenerix_email/__pycache__/__init__.cpython-311.pyc,sha256=VHHEae6bOdMZRv4BoHz1g5HaZMN7_WmD3aXcpvu2mAY,337
830
+ codenerix_email/__pycache__/__init__.cpython-310.pyc,sha256=15V6lHDQ0IWV_UqdkRd4uPnweplUuyHbpl8lpW1wUTI,313
831
+ codenerix_email/__pycache__/__init__.cpython-311.pyc,sha256=UWUl2oUBod7dR46Nr9F955JjBzb5Hw5FLg0DZAJ3XNk,337
832
832
  codenerix_email/__pycache__/__init__.cpython-35.pyc,sha256=dl9lYAgrokJptUj3JAhiqTlX7d_CbncOxZeTc1USc88,308
833
833
  codenerix_email/__pycache__/__init__.cpython-37.pyc,sha256=5d1CeFU5DrfnwrRpvSw1bHvLN9hoHXjUA3ln3rXCDo8,306
834
834
  codenerix_email/__pycache__/__init__.cpython-39.pyc,sha256=0c6KWU_eOTlF5l9fNWv8l41b0LcfVQNUsqDvJTv2YyU,300
@@ -840,11 +840,11 @@ codenerix_email/__pycache__/apps.cpython-310.pyc,sha256=PzDChfGmY_KnNkDrqSdEAQbl
840
840
  codenerix_email/__pycache__/apps.cpython-311.pyc,sha256=U51a_9uWzb1nkGnGLqZL5X4d-LBMi83MiPMlUtBYobo,560
841
841
  codenerix_email/__pycache__/apps.cpython-39.pyc,sha256=d5Hd-YMSDmSzyxDmQ_nwXjcEcHASDm6MxFblb1TATf4,439
842
842
  codenerix_email/__pycache__/forms.cpython-310.pyc,sha256=-oq5NlQGH7VkMUUMB4lXmBoUhkTVO14AOIZetrpgqck,4004
843
- codenerix_email/__pycache__/forms.cpython-311.pyc,sha256=zNvq_Xc_bdUsG59tq9OWUiy5hoyZ035z4E5fEovr4NE,5854
843
+ codenerix_email/__pycache__/forms.cpython-311.pyc,sha256=jUjgCTZFkAE2iTueIoG0mZ7CunsFX4EH2zh2OhLA_-c,5999
844
844
  codenerix_email/__pycache__/forms.cpython-35.pyc,sha256=z1aCJ2d8yyKJMuf5aVz0mGE6Nqi9bjU7HyDZPKxpUWc,3233
845
845
  codenerix_email/__pycache__/forms.cpython-39.pyc,sha256=NORLA0i3bmWx-mUn3wh3JtObuR7UYKZemSU6Cl5gHM8,2988
846
- codenerix_email/__pycache__/models.cpython-310.pyc,sha256=tJ-ff7rjubUvp21PUTwvWd5LAKZzRCOXzXJO8-aGtTc,18097
847
- codenerix_email/__pycache__/models.cpython-311.pyc,sha256=_ketRfcsx3QB9uplTrFGkqs_D69iM_lwL_UYziboJBc,36756
846
+ codenerix_email/__pycache__/models.cpython-310.pyc,sha256=glpYvUi4CpWxUfW8zUzo60woiHa_YdvIz5MS6SFnbns,17995
847
+ codenerix_email/__pycache__/models.cpython-311.pyc,sha256=p5V6pZ7oTWjBFJNlexW7mM29QnJVMnc3wCn5fk4A7Lo,36820
848
848
  codenerix_email/__pycache__/models.cpython-35.pyc,sha256=oGheSKlh8Ttc6bB-qZ1JY5_6RySM9M6GY5-GCATxfes,9481
849
849
  codenerix_email/__pycache__/models.cpython-39.pyc,sha256=ugyNNDG3k6rqsunwlcUIWADaLNvJ8snCgQwMZu9Ahac,8849
850
850
  codenerix_email/__pycache__/test_settings.cpython-310.pyc,sha256=HH5CsPxLlq7_jW4ZCGBc1KzcV4HNQcI6YTh0wcz_F0w,715
@@ -855,7 +855,7 @@ codenerix_email/__pycache__/urls.cpython-39.pyc,sha256=EZTAIjMTQLfB4lVdxaQrspy93
855
855
  codenerix_email/__pycache__/urls_frontend.cpython-310.pyc,sha256=byQ8k-Hcfft-DzwWVvO5CEt_DBazuNPK_lNXkDV8HhY,381
856
856
  codenerix_email/__pycache__/urls_frontend.cpython-311.pyc,sha256=GmCqHFwOVkzllal3pnLe5BYg1Rj5SAm5WTGKQrfzVGk,498
857
857
  codenerix_email/__pycache__/views.cpython-310.pyc,sha256=vB2BqnTPYMYxiihKQAr6DZZ1-7SoGfC7dE41JHp-jPI,8707
858
- codenerix_email/__pycache__/views.cpython-311.pyc,sha256=Z8OEWBF0DIzhN8vtyNKk2gXhg5uukALDpHCxaDbjMGA,15763
858
+ codenerix_email/__pycache__/views.cpython-311.pyc,sha256=urp3a_ATSfAza2O4A-D_Vnf_zfTMzRO6jG9BxwR8MZA,10251
859
859
  codenerix_email/__pycache__/views.cpython-35.pyc,sha256=ZiDircDZ5B_MEFZu2H4ca7OZop10EyZF2PRR6JRgUGc,3988
860
860
  codenerix_email/__pycache__/views.cpython-39.pyc,sha256=SV_-RTnh7w_7oMUkD0mUWhSs5lqMuw-HUGU40hV56YY,3657
861
861
  codenerix_email/locale/en/LC_MESSAGES/django.po,sha256=NzL-kG6Z7lVnp_YDsUOMz-HAKqC37J-2elkU_Ce1zL8,7198
@@ -868,7 +868,7 @@ codenerix_email/management/__pycache__/__init__.cpython-35.pyc,sha256=sBoEWs6zdI
868
868
  codenerix_email/management/__pycache__/__init__.cpython-39.pyc,sha256=uPXklfliVd3b8pLOJQT9ZeKcqmJMrGychvt68BsPulY,168
869
869
  codenerix_email/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
870
870
  codenerix_email/management/commands/email_test.py,sha256=SPsiq0s48sEX_G3piNHq-c1gDOH800H2zTvM19ei2LY,5605
871
- codenerix_email/management/commands/emails_recv.py,sha256=4QfIQ2M97T4Y3o7BR1d4nA8UF8bqe81wCMUpiooSzxs,35872
871
+ codenerix_email/management/commands/emails_recv.py,sha256=6MRABJzVSvOcGzz7qH-1_q9U3o8yNGBVlPB6lkvYlfU,37562
872
872
  codenerix_email/management/commands/emails_send.py,sha256=scCFklro4WVMYm-1ataSjUMsPT-Ie5u_DdA55CQcTCQ,7944
873
873
  codenerix_email/management/commands/recv_emails.py,sha256=aXmhdXlamiNxRpMIDSKBXUBhkOcwi5l_Pme7jSQUCME,273
874
874
  codenerix_email/management/commands/send_emails.py,sha256=a1MnpvZKAEFdXNfmI5oFUkVxy4PZ1AjaJS6GH90zeD0,273
@@ -1634,6 +1634,7 @@ codenerix_email/migrations/0011_alter_emailmessage_content_subtype_and_more.py,s
1634
1634
  codenerix_email/migrations/0012_emailmessage_unsubscribe_url.py,sha256=tPQt1W2LJcXMvt_ecB-2npu7wQ-Og29M99ZJjukYnWA,501
1635
1635
  codenerix_email/migrations/0013_emailmessage_headers.py,sha256=1T0lryajjfXhnMgU-aqJUW-CKDvVe6zLrYu_FygcFE4,441
1636
1636
  codenerix_email/migrations/0014_emailreceived.py,sha256=U--Zhb9JkihuL3w-lNnhCfWT1dkVbgEWBHE3RHXtAnA,3406
1637
+ codenerix_email/migrations/0015_emailmessage_bounces_hard_emailmessage_bounces_soft_and_more.py,sha256=68A8DtX_77s0KwhocOuyUyExkx3AMndAEnYahdok3rE,835
1637
1638
  codenerix_email/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1638
1639
  codenerix_email/migrations/__pycache__/0001_initial.cpython-310.pyc,sha256=Sm54OKfGHUMvSVgbXiFAe9E2d85HqlLoMEaBEBP53mE,2005
1639
1640
  codenerix_email/migrations/__pycache__/0001_initial.cpython-311.pyc,sha256=KsQcRgyLOFbYfAHj9C5i7uQNWt9_wMJg2MOwpOa1B10,3852
@@ -1681,14 +1682,15 @@ codenerix_email/migrations/__pycache__/0013_emailmessage_headers.cpython-310.pyc
1681
1682
  codenerix_email/migrations/__pycache__/0013_emailmessage_headers.cpython-311.pyc,sha256=-fFwK7UWVLCbaVh3ej4EQbee44wVC3hBezdgW875ncg,891
1682
1683
  codenerix_email/migrations/__pycache__/0014_emailreceived.cpython-310.pyc,sha256=cN48X4p05m2tZafTP1pHgQOcoz9qrROwGqII6G1FjsQ,1923
1683
1684
  codenerix_email/migrations/__pycache__/0014_emailreceived.cpython-311.pyc,sha256=0FC9aa_p8Ojxq0IISCbJbmNlySYNr-UkI1W1Y6b6jIU,2924
1685
+ codenerix_email/migrations/__pycache__/0015_emailmessage_bounces_hard_emailmessage_bounces_soft_and_more.cpython-311.pyc,sha256=-4y6CXG8s9cn_rpvQcWvxi1-farfomE6yIf6Dy1kBos,1231
1684
1686
  codenerix_email/migrations/__pycache__/__init__.cpython-310.pyc,sha256=PdaaABMdLCzCMnJmxaylT_jnLb8l7rr6n5C4CGDX37A,151
1685
1687
  codenerix_email/migrations/__pycache__/__init__.cpython-311.pyc,sha256=RbbUUEhcJ_eAVNFAdNbkjv60hyF7BsnNOlti5xplQsY,195
1686
1688
  codenerix_email/migrations/__pycache__/__init__.cpython-35.pyc,sha256=2g70xiMW6oJNkIpRM-0Dr5h7AUac-3xyCXPONxp9BBw,147
1687
1689
  codenerix_email/migrations/__pycache__/__init__.cpython-39.pyc,sha256=qNj2NH0YvoWPnCKxkVZPsEFsbM05y7t1njMskNISdVQ,168
1688
- codenerix_email/static/codenerix_email/emailmessages_rows.html,sha256=NyZpKPSHAAIywJX2ncS0H2bkqOVtMUwAdbYmkC4dKmk,2202
1690
+ codenerix_email/static/codenerix_email/emailmessages_rows.html,sha256=cA2ru5ti9WnPhhvFNEpI2tTR7gZzUYXWdzngRKU6a7k,2196
1689
1691
  codenerix_email/static/codenerix_email/emailreceiveds_rows.html,sha256=u9DXVdzKhx9WlJgcK_cT2DfGp9bGIKTBn3LDQplB4g4,2032
1690
- django_codenerix_email-4.0.39.dist-info/LICENSE,sha256=IXMIpi75XsrJt1Sznt4EftT9c_4X0C9eqK4tHhH8H48,11339
1691
- django_codenerix_email-4.0.39.dist-info/METADATA,sha256=et_AOcJOU0_8V_DL_Br12lR3UkKeJEI06F1mH-A9o2k,2676
1692
- django_codenerix_email-4.0.39.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
1693
- django_codenerix_email-4.0.39.dist-info/top_level.txt,sha256=lljSA0iKE_UBEM5gIrGQwioC_i8Jjnp-aR1LFElENgw,16
1694
- django_codenerix_email-4.0.39.dist-info/RECORD,,
1692
+ django_codenerix_email-4.0.41.dist-info/LICENSE,sha256=IXMIpi75XsrJt1Sznt4EftT9c_4X0C9eqK4tHhH8H48,11339
1693
+ django_codenerix_email-4.0.41.dist-info/METADATA,sha256=EZuTjqR4JMJ9UnNCudgk6mNmFnIlBjZng24g08-k_JA,2676
1694
+ django_codenerix_email-4.0.41.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
1695
+ django_codenerix_email-4.0.41.dist-info/top_level.txt,sha256=lljSA0iKE_UBEM5gIrGQwioC_i8Jjnp-aR1LFElENgw,16
1696
+ django_codenerix_email-4.0.41.dist-info/RECORD,,