django-codenerix-email 4.0.10__tar.gz → 4.0.11__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 (85) hide show
  1. {django-codenerix-email-4.0.10/django_codenerix_email.egg-info → django-codenerix-email-4.0.11}/PKG-INFO +1 -1
  2. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/__init__.py +1 -1
  3. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/forms.py +21 -6
  4. django-codenerix-email-4.0.11/codenerix_email/management/commands/__pycache__/__init__.cpython-311.pyc +0 -0
  5. django-codenerix-email-4.0.11/codenerix_email/management/commands/__pycache__/send_emails.cpython-311.pyc +0 -0
  6. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/management/commands/send_emails.py +93 -44
  7. django-codenerix-email-4.0.11/codenerix_email/migrations/0010_emailmessage_content_subtype_and_more.py +35 -0
  8. django-codenerix-email-4.0.11/codenerix_email/migrations/0011_alter_emailmessage_content_subtype_and_more.py +33 -0
  9. django-codenerix-email-4.0.11/codenerix_email/migrations/__pycache__/0010_emailmessage_content_subtype_and_more.cpython-311.pyc +0 -0
  10. django-codenerix-email-4.0.11/codenerix_email/migrations/__pycache__/0011_alter_emailmessage_content_subtype_and_more.cpython-311.pyc +0 -0
  11. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/models.py +204 -45
  12. django-codenerix-email-4.0.11/codenerix_email/static/codenerix_email/partials/emailmessages_rows.html +29 -0
  13. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/views.py +2 -1
  14. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11/django_codenerix_email.egg-info}/PKG-INFO +1 -1
  15. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/django_codenerix_email.egg-info/SOURCES.txt +6 -0
  16. django-codenerix-email-4.0.10/codenerix_email/static/codenerix_email/partials/emailmessages_rows.html +0 -18
  17. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/LICENSE +0 -0
  18. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/MANIFEST.in +0 -0
  19. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/README.rst +0 -0
  20. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/admin.py +0 -0
  21. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/apps.py +0 -0
  22. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/management/__init__.py +0 -0
  23. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/management/__pycache__/__init__.cpython-310.pyc +0 -0
  24. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/management/__pycache__/__init__.cpython-311.pyc +0 -0
  25. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/management/__pycache__/__init__.cpython-35.pyc +0 -0
  26. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/management/__pycache__/__init__.cpython-39.pyc +0 -0
  27. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/management/commands/__init__.py +0 -0
  28. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/management/commands/test_email.py +0 -0
  29. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/0001_initial.py +0 -0
  30. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/0002_auto_20170502_1043.py +0 -0
  31. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/0003_auto_20170921_1206.py +0 -0
  32. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/0004_auto_20171108_1628.py +0 -0
  33. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/0005_emailmessage_retries.py +0 -0
  34. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/0006_emailmessage_error.py +0 -0
  35. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/0007_emailmessage_next_retry.py +0 -0
  36. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/0008_auto_20171201_0928.py +0 -0
  37. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/0009_emailmessage_opened_emailmessage_uuid.py +0 -0
  38. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__init__.py +0 -0
  39. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0001_initial.cpython-310.pyc +0 -0
  40. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0001_initial.cpython-311.pyc +0 -0
  41. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0001_initial.cpython-35.pyc +0 -0
  42. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0001_initial.cpython-39.pyc +0 -0
  43. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0002_auto_20170502_1043.cpython-310.pyc +0 -0
  44. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0002_auto_20170502_1043.cpython-311.pyc +0 -0
  45. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0002_auto_20170502_1043.cpython-35.pyc +0 -0
  46. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0002_auto_20170502_1043.cpython-39.pyc +0 -0
  47. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0003_auto_20170921_1206.cpython-310.pyc +0 -0
  48. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0003_auto_20170921_1206.cpython-311.pyc +0 -0
  49. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0003_auto_20170921_1206.cpython-35.pyc +0 -0
  50. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0003_auto_20170921_1206.cpython-39.pyc +0 -0
  51. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0004_auto_20171108_1628.cpython-310.pyc +0 -0
  52. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0004_auto_20171108_1628.cpython-311.pyc +0 -0
  53. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0004_auto_20171108_1628.cpython-35.pyc +0 -0
  54. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0004_auto_20171108_1628.cpython-39.pyc +0 -0
  55. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0005_emailmessage_retries.cpython-310.pyc +0 -0
  56. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0005_emailmessage_retries.cpython-311.pyc +0 -0
  57. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0005_emailmessage_retries.cpython-35.pyc +0 -0
  58. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0005_emailmessage_retries.cpython-39.pyc +0 -0
  59. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0006_emailmessage_error.cpython-310.pyc +0 -0
  60. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0006_emailmessage_error.cpython-311.pyc +0 -0
  61. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0006_emailmessage_error.cpython-35.pyc +0 -0
  62. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0006_emailmessage_error.cpython-39.pyc +0 -0
  63. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0007_emailmessage_next_retry.cpython-310.pyc +0 -0
  64. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0007_emailmessage_next_retry.cpython-311.pyc +0 -0
  65. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0007_emailmessage_next_retry.cpython-35.pyc +0 -0
  66. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0007_emailmessage_next_retry.cpython-39.pyc +0 -0
  67. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0008_auto_20171201_0928.cpython-310.pyc +0 -0
  68. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0008_auto_20171201_0928.cpython-311.pyc +0 -0
  69. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0008_auto_20171201_0928.cpython-35.pyc +0 -0
  70. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0008_auto_20171201_0928.cpython-39.pyc +0 -0
  71. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0009_delete_emailtemplatetexten.cpython-311.pyc +0 -0
  72. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0009_emailmessage_opened_emailmessage_uuid.cpython-311.pyc +0 -0
  73. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/0010_emailmessage_opened_emailmessage_uuid_and_more.cpython-311.pyc +0 -0
  74. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/__init__.cpython-310.pyc +0 -0
  75. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
  76. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/__init__.cpython-35.pyc +0 -0
  77. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/migrations/__pycache__/__init__.cpython-39.pyc +0 -0
  78. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/urls.py +0 -0
  79. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/codenerix_email/urls_frontend.py +0 -0
  80. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/django_codenerix_email.egg-info/dependency_links.txt +0 -0
  81. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/django_codenerix_email.egg-info/not-zip-safe +0 -0
  82. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/django_codenerix_email.egg-info/requires.txt +0 -0
  83. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/django_codenerix_email.egg-info/top_level.txt +0 -0
  84. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/setup.cfg +0 -0
  85. {django-codenerix-email-4.0.10 → django-codenerix-email-4.0.11}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-codenerix-email
3
- Version: 4.0.10
3
+ Version: 4.0.11
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,4 +1,4 @@
1
- __version__ = "4.0.10"
1
+ __version__ = "4.0.11"
2
2
 
3
3
  __authors__ = [
4
4
  "Juan Miguel Taboada Godoy <juanmi@juanmitaboada.com>",
@@ -36,9 +36,18 @@ class EmailTemplateForm(GenModelForm):
36
36
  (
37
37
  _("Details"),
38
38
  12,
39
- ["cid", 3],
40
- ["efrom", 9],
41
- )
39
+ (
40
+ None,
41
+ 3,
42
+ ["cid", 12],
43
+ ["content_subtype", 12],
44
+ ),
45
+ (
46
+ None,
47
+ 9,
48
+ ["efrom", 9],
49
+ ),
50
+ ),
42
51
  ]
43
52
 
44
53
  @staticmethod
@@ -81,20 +90,26 @@ class EmailMessageForm(GenModelForm):
81
90
  _("Details"),
82
91
  6,
83
92
  ["uuid", 3],
93
+ ["created", 3],
94
+ ["updated", 3],
84
95
  ["efrom", 3],
85
96
  ["eto", 3],
86
97
  ["subject", 3],
87
- ["priority", 3],
98
+ ["opened", 3],
99
+ ),
100
+ (
101
+ _("System"),
102
+ 6,
88
103
  ["sending", 3],
89
104
  ["sent", 3],
90
105
  ["error", 3],
91
106
  ["retries", 3],
92
- ["opened", 3],
107
+ ["priority", 3],
93
108
  ["log", 3],
94
109
  ),
95
110
  (
96
111
  _("Body"),
97
- 6,
112
+ 12,
98
113
  ["body", 3],
99
114
  ),
100
115
  ]
@@ -70,29 +70,58 @@ class Command(BaseCommand, Debugger):
70
70
  default=False,
71
71
  help="Clear the sending status to all the Queue",
72
72
  )
73
+ # Named (optional) arguments
74
+ parser.add_argument(
75
+ "--verbose",
76
+ action="store_true",
77
+ dest="verbose",
78
+ default=False,
79
+ help="Enable verbose mode",
80
+ )
81
+ # Named (optional) arguments
82
+ parser.add_argument(
83
+ "--now",
84
+ action="store_true",
85
+ dest="now",
86
+ default=False,
87
+ help="Send now, do not wait the retry time",
88
+ )
89
+ # Named (optional) arguments
90
+ parser.add_argument(
91
+ "--all",
92
+ action="store_true",
93
+ dest="all",
94
+ default=False,
95
+ help="Send all, do not do on buckets",
96
+ )
73
97
 
74
98
  def handle(self, *args, **options):
75
99
 
100
+ # Get user configuration
101
+ daemon = bool(options["daemon"] or options["d"])
102
+ clear = bool(options["clear"] or options["c"])
103
+ bucket_size = getattr(settings, "CLIENT_EMAIL_BUCKETS", 10)
104
+ verbose = bool(options.get("verbose", False))
105
+ sendnow = bool(options.get("now", False))
106
+ doall = bool(options.get("all", False))
107
+
76
108
  # Autoconfigure Debugger
77
109
  self.set_name("CODENERIX-EMAIL")
78
110
  self.set_debug()
79
111
 
80
- # Get user configuration
81
- daemon = bool(options["daemon"] or options["d"])
82
- clear = bool(options["clear"] or options["c"])
83
- BUCKET_SIZE = getattr(settings, "CLIENT_EMAIL_BUCKETS", 10)
84
- if daemon:
85
- self.debug(
86
- "Starting command in DAEMON mode with a queue of {} emails".format(
87
- BUCKET_SIZE
88
- ),
89
- color="cyan",
90
- )
91
- else:
92
- self.debug(
93
- "Starting a queue of {} emails".format(BUCKET_SIZE),
94
- color="blue",
95
- )
112
+ # Daemon
113
+ if verbose:
114
+ if daemon:
115
+ self.debug(
116
+ "Starting command in DAEMON mode with a "
117
+ f"queue of {bucket_size} emails",
118
+ color="cyan",
119
+ )
120
+ else:
121
+ self.debug(
122
+ "Starting a queue of {} emails".format(bucket_size),
123
+ color="blue",
124
+ )
96
125
 
97
126
  # In if requested set sending status for all the list to False
98
127
  if clear:
@@ -110,8 +139,20 @@ class Command(BaseCommand, Debugger):
110
139
  sent=False,
111
140
  sending=False,
112
141
  error=False,
113
- next_retry__lte=timezone.now(),
114
- ).order_by("priority", "next_retry")[0 : BUCKET_SIZE + 1]
142
+ )
143
+
144
+ # If we do not have to send now we have to wait for the next retry
145
+ if not sendnow:
146
+ emails = emails.filter(
147
+ next_retry__lte=timezone.now(),
148
+ )
149
+
150
+ # Order emails by priority and next retry
151
+ emails = emails.order_by("priority", "next_retry")
152
+
153
+ # Send in buckets if we are not doing them all
154
+ if not doall:
155
+ emails = emails[0 : bucket_size + 1]
115
156
 
116
157
  # Check if there are emails to process
117
158
  if emails:
@@ -126,20 +167,22 @@ class Command(BaseCommand, Debugger):
126
167
 
127
168
  # For each email
128
169
  for email in emails:
129
- self.debug(
130
- "Sending to {}".format(email.eto),
131
- color="white",
132
- tail=False,
133
- )
134
-
135
- # Check if we have connection
136
- if not connection:
170
+ if verbose:
137
171
  self.debug(
138
- " - Connecting",
139
- color="yellow",
140
- head=False,
172
+ "Sending to {}".format(email.eto),
173
+ color="white",
141
174
  tail=False,
142
175
  )
176
+
177
+ # Check if we have connection
178
+ if not connection:
179
+ if verbose:
180
+ self.debug(
181
+ " - Connecting",
182
+ color="yellow",
183
+ head=False,
184
+ tail=False,
185
+ )
143
186
  connection = email.connect()
144
187
 
145
188
  # Send the email
@@ -154,20 +197,26 @@ class Command(BaseCommand, Debugger):
154
197
  email.log = error
155
198
  email.save()
156
199
  self.error(error)
157
- if email.sent:
158
- self.debug(" -> SENT", color="green", head=False)
159
- else:
160
- self.debug(
161
- " -> ERROR", color="red", head=False, tail=False
162
- )
163
- self.debug(
164
- " ({} retries left)".format(
165
- getattr(settings, "CLIENT_EMAIL_RETRIES", 10)
166
- - email.retries
167
- ),
168
- color="cyan",
169
- head=False,
170
- )
200
+ if verbose:
201
+ if email.sent:
202
+ self.debug(" -> SENT", color="green", head=False)
203
+ else:
204
+ self.debug(
205
+ " -> ERROR",
206
+ color="red",
207
+ head=False,
208
+ tail=False,
209
+ )
210
+ self.debug(
211
+ " ({} retries left)".format(
212
+ getattr(
213
+ settings, "CLIENT_EMAIL_RETRIES", 10
214
+ )
215
+ - email.retries
216
+ ),
217
+ color="cyan",
218
+ head=False,
219
+ )
171
220
 
172
221
  # Delete all that have been sent
173
222
  if not getattr(settings, "CLIENT_EMAIL_HISTORY", True):
@@ -184,7 +233,7 @@ class Command(BaseCommand, Debugger):
184
233
  self.debug("Exited by user request!", color="green")
185
234
  break
186
235
 
187
- else:
236
+ elif verbose:
188
237
  # No emails to send
189
238
  self.debug(
190
239
  "No emails to be sent at this moment in the queue!",
@@ -0,0 +1,35 @@
1
+ # Generated by Django 5.0.6 on 2024-06-04 10:55
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("codenerix_email", "0009_emailmessage_opened_emailmessage_uuid"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="emailmessage",
15
+ name="content_subtype",
16
+ field=models.CharField(
17
+ blank=True,
18
+ choices=[("plain", "Plain"), ("html", "HTML")],
19
+ default="html",
20
+ max_length=256,
21
+ verbose_name="Content Subtype",
22
+ ),
23
+ ),
24
+ migrations.AddField(
25
+ model_name="emailtemplate",
26
+ name="content_subtype",
27
+ field=models.CharField(
28
+ blank=True,
29
+ choices=[("plain", "Plain"), ("html", "HTML")],
30
+ default="html",
31
+ max_length=256,
32
+ verbose_name="Content Subtype",
33
+ ),
34
+ ),
35
+ ]
@@ -0,0 +1,33 @@
1
+ # Generated by Django 5.0.6 on 2024-06-04 11:05
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("codenerix_email", "0010_emailmessage_content_subtype_and_more"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="emailmessage",
15
+ name="content_subtype",
16
+ field=models.CharField(
17
+ choices=[("plain", "Plain"), ("html", "HTML Web")],
18
+ default="html",
19
+ max_length=256,
20
+ verbose_name="Content Subtype",
21
+ ),
22
+ ),
23
+ migrations.AlterField(
24
+ model_name="emailtemplate",
25
+ name="content_subtype",
26
+ field=models.CharField(
27
+ choices=[("plain", "Plain"), ("html", "HTML Web")],
28
+ default="html",
29
+ max_length=256,
30
+ verbose_name="Content Subtype",
31
+ ),
32
+ ),
33
+ ]
@@ -17,6 +17,8 @@
17
17
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
18
  # See the License for the specific language governing permissions and
19
19
  # limitations under the License.
20
+ #
21
+ # type: ignore
20
22
 
21
23
  import re
22
24
  import ssl
@@ -29,14 +31,22 @@ from django.db import models
29
31
  from django.template import Context, Template
30
32
  from django.core.exceptions import ValidationError
31
33
  from django.conf import settings
34
+ from django.db.models import Q
32
35
 
33
- from codenerix.models import CodenerixModel # type: ignore
36
+ from codenerix.models import CodenerixModel
34
37
  from codenerix_lib.debugger import Debugger
35
- from codenerix.lib.genmail import ( # type: ignore # noqa: N817
38
+ from codenerix.lib.genmail import ( # noqa: N817
36
39
  EmailMessage as EM,
37
40
  get_connection,
38
41
  )
39
- from codenerix.fields import WysiwygAngularField # type: ignore
42
+ from codenerix.fields import WysiwygAngularField
43
+
44
+ CONTENT_SUBTYPE_PLAIN = "plain"
45
+ CONTENT_SUBTYPE_HTML = "html"
46
+ CONTENT_SUBTYPES = (
47
+ (CONTENT_SUBTYPE_PLAIN, _("Plain")),
48
+ (CONTENT_SUBTYPE_HTML, _("HTML Web")),
49
+ )
40
50
 
41
51
 
42
52
  class EmailMessage(CodenerixModel, Debugger):
@@ -74,24 +84,78 @@ class EmailMessage(CodenerixModel, Debugger):
74
84
  opened = models.DateTimeField(
75
85
  _("Opened"), null=True, blank=True, default=None
76
86
  )
87
+ content_subtype = models.CharField(
88
+ _("Content Subtype"),
89
+ max_length=256,
90
+ choices=CONTENT_SUBTYPES,
91
+ blank=False,
92
+ null=False,
93
+ default=CONTENT_SUBTYPE_HTML,
94
+ )
77
95
 
78
96
  def __fields__(self, info):
79
97
  fields = []
80
- fields.append(("sending", None, 100))
81
- fields.append(("error", None, 100))
82
- fields.append(("sent", _("Send"), 100))
83
- fields.append(("priority", _("Priority"), 100))
84
- fields.append(("uuid", _("UUID"), 100))
85
- fields.append(("created", _("Created"), 100))
86
- fields.append(("opened", _("Opened"), 100))
87
- fields.append(("efrom", _("From"), 100))
88
- fields.append(("eto", _("To"), 100))
89
- fields.append(("subject", _("Subject"), 100))
90
- fields.append(("retries", _("Retries"), 100))
91
- fields.append(("next_retry", _("Next retry"), 100))
92
- fields.append(("pk", _("ID"), 100))
98
+ fields.append(("sending", None))
99
+ fields.append(("error", None))
100
+ fields.append(("sent", _("Send")))
101
+ fields.append(("priority", _("Priority")))
102
+ fields.append(("updated", _("Updated")))
103
+ fields.append(("opened", _("Opened")))
104
+ fields.append(("efrom", _("From")))
105
+ fields.append(("eto", _("To")))
106
+ fields.append(("subject", _("Subject")))
107
+ fields.append(("retries", _("Retries")))
108
+ fields.append(("next_retry", _("Next retry")))
109
+ fields.append(("pk", _("ID")))
110
+ fields.append(("content_subtype", _("Content Subtype")))
111
+ fields.append(("uuid", _("UUID")))
93
112
  return fields
94
113
 
114
+ def __searchQ__(self, info, search): # noqa: N802
115
+ answer = super().__searchQ__(info, search)
116
+ answer["uuid"] = Q(uuid__icontains=search)
117
+ answer["priority"] = Q(priority=search)
118
+ answer["efrom"] = Q(efrom__icontains=search)
119
+ answer["eto"] = Q(eto__icontains=search)
120
+ answer["retries"] = Q(retries=search)
121
+ answer["pk"] = Q(pk=search)
122
+ return answer
123
+
124
+ def __searchF__(self, info): # noqa: N802
125
+ def mailstatus(x):
126
+ if x == "D":
127
+ return Q(error=False, sent=True)
128
+ elif x == "P":
129
+ return Q(error=False, sent=False, sending=False)
130
+ elif x == "S":
131
+ return Q(error=False, sent=False, sending=True)
132
+ elif x == "E":
133
+ return Q(error=True)
134
+ else:
135
+ return Q()
136
+
137
+ mailoptions = [
138
+ ("D", _("Sent")), # Sent - Done
139
+ ("P", _("Pending")), # Pending - Pending
140
+ ("S", _("Sending")), # Sending - Sending
141
+ ("E", _("Error")), # Error - Error
142
+ ]
143
+
144
+ return {
145
+ "sent": (_("Sent"), lambda x: mailstatus(x), mailoptions),
146
+ "uuid": (_("UUID"), lambda x: Q(uuid__icontains=x), "input"),
147
+ "priority": (_("Priority"), lambda x: Q(priority=x), "input"),
148
+ "opened": (
149
+ _("Opened"),
150
+ lambda x: ~Q(opened__isnull=x),
151
+ [(True, _("Yes")), (False, _("No"))],
152
+ ),
153
+ "efrom": (_("From"), lambda x: Q(efrom__icontains=x), "input"),
154
+ "eto": (_("To"), lambda x: Q(eto__icontains=x), "input"),
155
+ "retries": (_("Retries"), lambda x: Q(retries=x), "input"),
156
+ "pk": (_("ID"), lambda x: Q(pk=x), "input"),
157
+ }
158
+
95
159
  def __unicode__(self):
96
160
  return "{} ({})".format(self.eto, self.pk)
97
161
 
@@ -100,9 +164,63 @@ class EmailMessage(CodenerixModel, Debugger):
100
164
  self.opened = timezone.now()
101
165
  self.save()
102
166
 
103
- def connect(self, legacy=False):
167
+ @classmethod
168
+ def process_queue(
169
+ cls, connection=None, legacy=False, silent=True, debug=False
170
+ ):
104
171
  """
105
- This class will return a connection instance, you can disconnect it with connection.close()
172
+ This method will process all the emails in the queue
173
+ """
174
+
175
+ # Process queue
176
+ emails = cls.objects.filter(
177
+ sent=False, error=False, next_retry__lte=timezone.now()
178
+ )
179
+
180
+ # Do we have to send emails
181
+ if emails.count():
182
+
183
+ # Get connection if not connected yet
184
+ if connection is None:
185
+
186
+ # Connect
187
+ connection = cls.internal_connect(legacy)
188
+
189
+ # Configure them as sending
190
+ emails.update(sending=True)
191
+
192
+ # Send them
193
+ for email in emails.order_by("priority"):
194
+ try:
195
+ email.send(
196
+ connection=connection,
197
+ legacy=legacy,
198
+ silent=silent,
199
+ debug=debug,
200
+ )
201
+ except Exception as e:
202
+
203
+ # Los the error into this email
204
+ if email.log is None:
205
+ email.log = ""
206
+ email.log += f"Error: {e}\n"
207
+ email.sending = False
208
+ email.save()
209
+ email.warning(f"Error at EmailMessage<{{email.pk}}>: {e}")
210
+
211
+ # Set all emails to not sending, since we are stopping now
212
+ emails.update(sending=False)
213
+
214
+ # Re-raise the exception
215
+ raise
216
+
217
+ emails.update(sending=False)
218
+
219
+ @classmethod
220
+ def internal_connect(cls, legacy=False):
221
+ """
222
+ This class will return a connection instance, you can disconnect it
223
+ with connection.close()
106
224
  """
107
225
 
108
226
  if not legacy:
@@ -119,15 +237,24 @@ class EmailMessage(CodenerixModel, Debugger):
119
237
  use_tls = settings.EMAIL_USE_TLS
120
238
 
121
239
  # Remember last connection data
122
- self.__connect_info = {"host": host, "port": port, "use_tls": use_tls}
240
+ conect_info = {"host": host, "port": port, "use_tls": use_tls}
123
241
  # Get connection
124
- return get_connection(
125
- host=host,
126
- port=port,
127
- username=username,
128
- password=password,
129
- use_tls=use_tls,
242
+ return (
243
+ get_connection(
244
+ host=host,
245
+ port=port,
246
+ username=username,
247
+ password=password,
248
+ use_tls=use_tls,
249
+ ),
250
+ conect_info,
251
+ )
252
+
253
+ def connect(self, legacy=False):
254
+ (connection, self.__connect_info) = EmailMessage.internal_connect(
255
+ legacy
130
256
  )
257
+ return connection
131
258
 
132
259
  def send(
133
260
  self,
@@ -135,7 +262,7 @@ class EmailMessage(CodenerixModel, Debugger):
135
262
  legacy=False,
136
263
  silent=True,
137
264
  debug=False,
138
- content_subtype="plain",
265
+ content_subtype=None,
139
266
  ):
140
267
 
141
268
  # Autoconfigure Debugger
@@ -143,6 +270,16 @@ class EmailMessage(CodenerixModel, Debugger):
143
270
  self.set_name("EmailMessage")
144
271
  self.set_debug()
145
272
 
273
+ # Warn about subtype
274
+ if content_subtype:
275
+ self.warning(
276
+ _(
277
+ "Programming ERROR: You are using content_subtype, this "
278
+ "value has been DEPRECATED and will be remove in future "
279
+ "versions."
280
+ )
281
+ )
282
+
146
283
  # Get connection if not connected yet
147
284
  if connection is None:
148
285
  # Connect
@@ -155,6 +292,7 @@ class EmailMessage(CodenerixModel, Debugger):
155
292
  self.set_name("EmailMessage->{}".format(self.eto))
156
293
 
157
294
  # Manually open the connection
295
+ error = None
158
296
  try:
159
297
  connection.open()
160
298
  except (
@@ -163,8 +301,6 @@ class EmailMessage(CodenerixModel, Debugger):
163
301
  TimeoutError,
164
302
  ) as e:
165
303
  connection = None
166
- if self.log is None:
167
- self.log = ""
168
304
  exceptiontxt = str(type(e)).split(".")[-1].split("'")[0]
169
305
  ci = getattr(self, "__connect_info", {})
170
306
  error = "{}: {} [HOST={}:{} TLS={}]\n".format(
@@ -175,12 +311,14 @@ class EmailMessage(CodenerixModel, Debugger):
175
311
  ci.get("use_tls", "-"),
176
312
  )
177
313
  self.warning(error)
178
- self.log += error
179
- # We will not retry anymore
314
+ if self.log is None:
315
+ self.log = ""
316
+ self.log += f"{error}\n"
317
+ # We will not retry anymore (for now)
180
318
  self.sending = False
181
319
  # We make lower this email's priority
182
320
  self.priority += 1
183
- # Set new retry
321
+ # Set we just made a new retry
184
322
  self.retries += 1
185
323
  self.next_retry = timezone.now() + timezone.timedelta(
186
324
  seconds=getattr(
@@ -204,7 +342,7 @@ class EmailMessage(CodenerixModel, Debugger):
204
342
  [self.eto],
205
343
  connection=connection,
206
344
  )
207
- email.content_subtype = content_subtype
345
+ email.content_subtype = self.content_subtype
208
346
  for at in self.attachments.all():
209
347
  with open(at.path) as f:
210
348
  email.attach(at.filename, f.read(), at.mime)
@@ -212,6 +350,7 @@ class EmailMessage(CodenerixModel, Debugger):
212
350
  # send list emails
213
351
  retries = 1
214
352
  while retries + 1:
353
+ error = None
215
354
  try:
216
355
  if connection.send_messages([email]):
217
356
  # We are done
@@ -219,27 +358,33 @@ class EmailMessage(CodenerixModel, Debugger):
219
358
  self.sending = False
220
359
  break
221
360
  except ssl.SSLError as e:
222
- error = "SSLError: {}\n".format(e)
361
+ error = f"SSLError: {e}\n"
223
362
  self.warning(error)
224
- self.log += error
363
+ if self.log is None:
364
+ self.log = ""
365
+ self.log += f"{error}\n"
225
366
  except smtplib.SMTPServerDisconnected as e:
226
- error = "SMTPServerDisconnected: {}\n".format(e)
367
+ error = f"SMTPServerDisconnected: {e}\n"
227
368
  self.warning(error)
228
- self.log += error
369
+ if self.log is None:
370
+ self.log = ""
371
+ self.log += f"{error}\n"
229
372
  except smtplib.SMTPException as e:
230
- error = "SMTPException: {}\n".format(e)
373
+ error = f"SMTPException: {e}\n"
231
374
  self.warning(error)
232
- self.log += error
375
+ if self.log is None:
376
+ self.log = ""
377
+ self.log += f"{error}\n"
233
378
  finally:
234
379
  # One chance less
235
380
  retries -= 1
236
381
  # Check if this is the last try
237
382
  if not retries:
238
- # We will not retry anymore
383
+ # We will not retry anymore (fow now)
239
384
  self.sending = False
240
385
  # We make lower this email's priority
241
386
  self.priority += 1
242
- # Set new retry
387
+ # Set we just made a new retry
243
388
  self.retries += 1
244
389
  self.next_retry = (
245
390
  timezone.now()
@@ -293,12 +438,21 @@ class EmailTemplate(CodenerixModel):
293
438
  _("CID"), unique=True, max_length=30, blank=False, null=False
294
439
  )
295
440
  efrom = models.TextField(_("From"), blank=True, null=False)
441
+ content_subtype = models.CharField(
442
+ _("Content Subtype"),
443
+ max_length=256,
444
+ choices=CONTENT_SUBTYPES,
445
+ blank=False,
446
+ null=False,
447
+ default=CONTENT_SUBTYPE_HTML,
448
+ )
296
449
 
297
450
  def __fields__(self, info):
298
451
  fields = []
299
452
  fields.append(("pk", _("PK"), 100))
300
453
  fields.append(("cid", _("CID"), 100))
301
454
  fields.append(("efrom", _("From"), 100))
455
+ fields.append(("content_subtype", _("Content Subtype"), 100))
302
456
  return fields
303
457
 
304
458
  def __str__(self):
@@ -313,7 +467,8 @@ class EmailTemplate(CodenerixModel):
313
467
  Usages:
314
468
  EmailTemplate.get('PACO', ctx) => EmailMessage(): le falta el eto
315
469
  > cid: PACO
316
- EmailTemplate.get(pk='PACO', context=ctx) => EmailMessage(): le falta el eto
470
+ EmailTemplate.get(pk='PACO', context=ctx) => EmailMessage(): le
471
+ falta el eto
317
472
  > pk: PACO (we don't have CID)
318
473
  """
319
474
  if cid:
@@ -337,6 +492,7 @@ class EmailTemplate(CodenerixModel):
337
492
  )
338
493
  e.body = Template(getattr(self, lang).body).render(Context(context))
339
494
  e.efrom = Template(self.efrom).render(Context(context))
495
+ e.content_subtype = self.content_subtype
340
496
  return e
341
497
 
342
498
  def clean(self):
@@ -345,7 +501,8 @@ class EmailTemplate(CodenerixModel):
345
501
  if len(re.findall(r"[A-Za-z0-9]+", self.cid)) != 1:
346
502
  raise ValidationError(
347
503
  _(
348
- "CID can contains only number and letters with no spaces"
504
+ "CID can contains only number and letters with "
505
+ "no spaces"
349
506
  )
350
507
  )
351
508
 
@@ -378,8 +535,10 @@ for info in MODELS:
378
535
  field = info[0]
379
536
  model = info[1]
380
537
  for lang_code in settings.LANGUAGES_DATABASES:
381
- query = "class {}Text{}(GenText):\n".format(model, lang_code)
382
- query += " {} = models.OneToOneField({}, on_delete=models.CASCADE, blank=False, null=False, related_name='{}')\n".format(
383
- field, model, lang_code.lower()
538
+ query = f"class {model}Text{lang_code}(GenText):\n"
539
+ query += (
540
+ f" {field} = models.OneToOneField({model}, "
541
+ f"on_delete=models.CASCADE, blank=False, null=False, "
542
+ f"related_name='{lang_code.lower()}')\n"
384
543
  )
385
544
  exec(query)
@@ -0,0 +1,29 @@
1
+ <td>
2
+ <center>
3
+ <span class='fa fa-refresh fa-spin fa-fw text-info' ng-if='!row.error && row.sending' alt='{{data.meta.gentranslate.sending}}...' title='{{data.meta.gentranslate.sending}}...'></span>
4
+ <span class='fa fa-envelope-o text-success' ng-if='!row.error && !row.sending && row.sent' alt='{{data.meta.gentranslate.sent}}' title='{{data.meta.gentranslate.sent}}'></span>
5
+ <span class='fa fa-envelope-open-o' ng-if='!row.error && !row.sending && !row.sent' alt='{{data.meta.gentranslate.waiting}}' title='{{data.meta.gentranslate.waiting}}'></span>
6
+ <span ng-if='row.error' alt='{{data.meta.gentranslate.notsent}}' title='{{data.meta.gentranslate.notsent}}' codenerix-html-compile="false|codenerix"></span>
7
+ </center>
8
+ </td>
9
+ <td><center>{{row.priority|codenerix}}</center></td>
10
+ <td class="text-nowrap">{{row.updated|codenerix}}</td>
11
+ <td>
12
+ <span title="{{row.opened}}" codenerix-html-compile="row.opened|codenerix:'bool'"><span>
13
+ </td>
14
+ <td>{{row.efrom|codenerix}}</td>
15
+ <td>{{row.eto|codenerix}}</td>
16
+ <td>{{row.subject|codenerix}}</td>
17
+ <td><center>{{row.retries|codenerix}}</center></td>
18
+ <td class="text-nowrap">{{row.next_retry|codenerix}}</td>
19
+ <td><center>{{row.pk|codenerix}}</center></td>
20
+ <td>
21
+ <center>
22
+ <button class="btn" title="{{row.content_subtype|codenerix}}">
23
+ <i ng-show="row.content_subtype=='plain'" class="fa fa-file-text-o" aria-hidden="true"></i>
24
+ <i ng-show="row.content_subtype=='html'" class="fa fa-code" aria-hidden="true"></i>
25
+ <i ng-hide="(row.content_subtype=='plain') || (row.content_subtype=='html')"class="fa fa-question-circle-o" aria-hidden="true"></i>
26
+ </button>
27
+ </center>
28
+ </td>
29
+ <td>{{row.uuid|codenerix}}</td>
@@ -144,7 +144,8 @@ class EmailTemplateDelete(GenDelete):
144
144
  class EmailMessageList(GenList):
145
145
  model = EmailMessage
146
146
  show_details = True
147
- default_ordering = ["-created"]
147
+ search_filter_button = True
148
+ default_ordering = ["-updated"]
148
149
  static_partial_row = "codenerix_email/partials/emailmessages_rows.html"
149
150
  gentranslate = {
150
151
  "sending": _("Sending"),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-codenerix-email
3
- Version: 4.0.10
3
+ Version: 4.0.11
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>
@@ -18,6 +18,8 @@ codenerix_email/management/__pycache__/__init__.cpython-39.pyc
18
18
  codenerix_email/management/commands/__init__.py
19
19
  codenerix_email/management/commands/send_emails.py
20
20
  codenerix_email/management/commands/test_email.py
21
+ codenerix_email/management/commands/__pycache__/__init__.cpython-311.pyc
22
+ codenerix_email/management/commands/__pycache__/send_emails.cpython-311.pyc
21
23
  codenerix_email/migrations/0001_initial.py
22
24
  codenerix_email/migrations/0002_auto_20170502_1043.py
23
25
  codenerix_email/migrations/0003_auto_20170921_1206.py
@@ -27,6 +29,8 @@ codenerix_email/migrations/0006_emailmessage_error.py
27
29
  codenerix_email/migrations/0007_emailmessage_next_retry.py
28
30
  codenerix_email/migrations/0008_auto_20171201_0928.py
29
31
  codenerix_email/migrations/0009_emailmessage_opened_emailmessage_uuid.py
32
+ codenerix_email/migrations/0010_emailmessage_content_subtype_and_more.py
33
+ codenerix_email/migrations/0011_alter_emailmessage_content_subtype_and_more.py
30
34
  codenerix_email/migrations/__init__.py
31
35
  codenerix_email/migrations/__pycache__/0001_initial.cpython-310.pyc
32
36
  codenerix_email/migrations/__pycache__/0001_initial.cpython-311.pyc
@@ -62,7 +66,9 @@ codenerix_email/migrations/__pycache__/0008_auto_20171201_0928.cpython-35.pyc
62
66
  codenerix_email/migrations/__pycache__/0008_auto_20171201_0928.cpython-39.pyc
63
67
  codenerix_email/migrations/__pycache__/0009_delete_emailtemplatetexten.cpython-311.pyc
64
68
  codenerix_email/migrations/__pycache__/0009_emailmessage_opened_emailmessage_uuid.cpython-311.pyc
69
+ codenerix_email/migrations/__pycache__/0010_emailmessage_content_subtype_and_more.cpython-311.pyc
65
70
  codenerix_email/migrations/__pycache__/0010_emailmessage_opened_emailmessage_uuid_and_more.cpython-311.pyc
71
+ codenerix_email/migrations/__pycache__/0011_alter_emailmessage_content_subtype_and_more.cpython-311.pyc
66
72
  codenerix_email/migrations/__pycache__/__init__.cpython-310.pyc
67
73
  codenerix_email/migrations/__pycache__/__init__.cpython-311.pyc
68
74
  codenerix_email/migrations/__pycache__/__init__.cpython-35.pyc
@@ -1,18 +0,0 @@
1
- <td>
2
- <span class='fa fa-refresh fa-spin fa-fw text-info' ng-if='!row.error && row.sending' alt='{{data.meta.gentranslate.sending}}...' title='{{data.meta.gentranslate.sending}}...'></span>
3
- <span class='fa fa-envelope-o text-success' ng-if='!row.error && !row.sending && row.sent' alt='{{data.meta.gentranslate.sent}}' title='{{data.meta.gentranslate.sent}}'></span>
4
- <span class='fa fa-envelope-open-o' ng-if='!row.error && !row.sending && !row.sent' alt='{{data.meta.gentranslate.waiting}}' title='{{data.meta.gentranslate.waiting}}'></span>
5
- <span ng-if='row.error' alt='{{data.meta.gentranslate.notsent}}' title='{{data.meta.gentranslate.notsent}}' codenerix-html-compile="false|codenerix"></span>
6
- </td>
7
- <td>{{row.priority|codenerix}}</td>
8
- <td>{{row.uuid|codenerix}}</td>
9
- <td class="text-nowrap">{{row.created|codenerix}}</td>
10
- <td>
11
- <span title="{{row.opened}}" codenerix-html-compile="row.opened|codenerix:'bool'"><span>
12
- </td>
13
- <td>{{row.efrom|codenerix}}</td>
14
- <td>{{row.eto|codenerix}}</td>
15
- <td>{{row.subject|codenerix}}</td>
16
- <td>{{row.retries|codenerix}}</td>
17
- <td class="text-nowrap">{{row.next_retry|codenerix}}</td>
18
- <td>{{row.pk|codenerix}}</td>