slthcore 0.4.5__tar.gz → 0.4.6__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 slthcore might be problematic. Click here for more details.
- {slthcore-0.4.5/slthcore.egg-info → slthcore-0.4.6}/PKG-INFO +1 -1
- {slthcore-0.4.5 → slthcore-0.4.6}/setup.py +1 -1
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/__init__.py +17 -2
- slthcore-0.4.6/slth/endpoints/whatsappnotification.py +52 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/factory.py +3 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/forms.py +4 -0
- slthcore-0.4.6/slth/management/commands/worker.py +28 -0
- slthcore-0.4.6/slth/migrations/0013_whatsappnotification.py +35 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/models.py +110 -25
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/notifications.py +12 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/serializer.py +2 -2
- slthcore-0.4.6/slth/static/images/placeholder.png +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/static/js/slth.min.js +6 -6
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/views.py +2 -12
- {slthcore-0.4.5 → slthcore-0.4.6/slthcore.egg-info}/PKG-INFO +1 -1
- {slthcore-0.4.5 → slthcore-0.4.6}/slthcore.egg-info/SOURCES.txt +3 -0
- slthcore-0.4.5/slth/management/commands/worker.py +0 -19
- {slthcore-0.4.5 → slthcore-0.4.6}/MANIFEST.in +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/setup.cfg +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/__init__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/apps.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/configure/__main__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/__main__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/__pycache__/__main__.cpython-312.pyc +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/.DS_Store +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/.gitignore +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/api/__init__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/api/asgi.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/api/endpoints/__init__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/api/models.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/api/settings.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/api/tests.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/api/urls.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/api/wsgi.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/application.yml +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/entrypoint.sh +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/manage.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/backend/requirements.txt +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/base.env +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/docker-compose.yml +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/frontend/package.json +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/frontend/src/main.jsx +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/frontend/vite.config.js +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/local.env +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/run.sh +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/selenium/run.sh +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/cmd/init/boilerplate/test.sh +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/components.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/db/__init__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/db/generic.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/db/models.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/auth.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/deletion.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/dev.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/email.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/job.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/log.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/profile.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/pushsubscription.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/report.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/role.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/task.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/timezone.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/endpoints/user.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/exceptions.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/management/__init__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/management/commands/__init__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/management/commands/integration_test.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/management/commands/sync.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/middleware/__init__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/middleware/timezone.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0001_initial.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0002_email_role_pushsubscription_error.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0004_alter_profile_photo.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0005_alter_profile_photo.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0006_user.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0007_deletion_log.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0008_alter_deletion_datetime_alter_log_datetime.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0009_remove_email_from_email_email_action_email_attempt_and_more.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0010_email_key_alter_email_action_alter_email_attempt_and_more.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0011_usertimezone.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/0012_timezone_remove_usertimezone_key_and_more.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/migrations/__init__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/oauth.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/pdf/__init__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/pdf/tests.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/permissions.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/printer.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/queryset.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/roles.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/selenium/__init__.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/selenium/browser.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/static/.DS_Store +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/static/css/.DS_Store +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/static/css/slth.css +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/static/js/index.min.js +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/static/js/react.min.js +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/statistics.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/tasks.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/templates/email.html +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/templates/index.html +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/templates/report.html +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/templates/service-worker.js +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/templates/signature.html +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/tests.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/threadlocal.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/tz.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/urls.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slth/utils.py +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slthcore.egg-info/dependency_links.txt +0 -0
- {slthcore-0.4.5 → slthcore-0.4.6}/slthcore.egg-info/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import io
|
|
2
|
+
import json
|
|
2
3
|
import inspect
|
|
3
4
|
from ..models import Log
|
|
4
5
|
from django.apps import apps
|
|
@@ -11,6 +12,7 @@ from django.http import JsonResponse, HttpResponse
|
|
|
11
12
|
from ..factory import FormFactory
|
|
12
13
|
from django.core.exceptions import ValidationError
|
|
13
14
|
from slth import forms
|
|
15
|
+
from slth.models import Role
|
|
14
16
|
from django.db.models import Model
|
|
15
17
|
from datetime import datetime
|
|
16
18
|
from django.template.loader import render_to_string
|
|
@@ -45,6 +47,7 @@ class ApiResponse(JsonResponse):
|
|
|
45
47
|
self["Access-Control-Allow-Headers"] = "*"
|
|
46
48
|
self["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS, PUT, DELETE, PATCH"
|
|
47
49
|
self["Access-Control-Max-Age"] = "600"
|
|
50
|
+
# print(json.dumps(args[0], indent=1, ensure_ascii=False))
|
|
48
51
|
|
|
49
52
|
|
|
50
53
|
class EnpointMetaclass(type):
|
|
@@ -105,7 +108,7 @@ class Endpoint(metaclass=EnpointMetaclass):
|
|
|
105
108
|
return True
|
|
106
109
|
for name in names:
|
|
107
110
|
if (
|
|
108
|
-
|
|
111
|
+
Role.objects
|
|
109
112
|
.filter(username=self.request.user.username, name=name)
|
|
110
113
|
.exists()
|
|
111
114
|
):
|
|
@@ -292,7 +295,16 @@ class Endpoint(metaclass=EnpointMetaclass):
|
|
|
292
295
|
return default if value is None else value
|
|
293
296
|
|
|
294
297
|
def get_verbose_name(self):
|
|
295
|
-
return self.get_metadata("verbose_name")
|
|
298
|
+
return self.get_metadata("verbose_name", self.get_default_verbose_name())
|
|
299
|
+
|
|
300
|
+
def get_submit_label(self):
|
|
301
|
+
return self.get_metadata("submit_label", "Enviar")
|
|
302
|
+
|
|
303
|
+
def get_submit_icon(self):
|
|
304
|
+
return self.get_metadata("submit_icon", "chevron-right")
|
|
305
|
+
|
|
306
|
+
def get_default_verbose_name(self):
|
|
307
|
+
return type(self).__name__
|
|
296
308
|
|
|
297
309
|
def get_icon(self):
|
|
298
310
|
return self.get_metadata("icon")
|
|
@@ -338,6 +350,9 @@ class ModelEndpoint(Endpoint):
|
|
|
338
350
|
|
|
339
351
|
def get_icon(self):
|
|
340
352
|
return super().get_icon() or getattr(self.model._meta, 'icon', None)
|
|
353
|
+
|
|
354
|
+
def get_default_verbose_name(self):
|
|
355
|
+
return type(self).__name__.replace(self.model.__name__, f' {self.model._meta.verbose_name}')
|
|
341
356
|
|
|
342
357
|
|
|
343
358
|
class QuerySetEndpoint(Generic[T], ModelEndpoint):
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from slth import endpoints
|
|
2
|
+
from ..models import WhatsappNotification
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WhatsappNotifications(endpoints.ListEndpoint[WhatsappNotification]):
|
|
6
|
+
def get(self):
|
|
7
|
+
return super().get().actions('whatsappnotification.send')
|
|
8
|
+
def check_permission(self):
|
|
9
|
+
return self.check_role()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Add(endpoints.AddEndpoint[WhatsappNotification]):
|
|
13
|
+
class Meta:
|
|
14
|
+
modal = True
|
|
15
|
+
icon = 'message'
|
|
16
|
+
|
|
17
|
+
def check_permission(self):
|
|
18
|
+
return self.check_role()
|
|
19
|
+
|
|
20
|
+
def get_verbose_name(self):
|
|
21
|
+
return 'Enviar Whatsapp'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Edit(endpoints.EditEndpoint[WhatsappNotification]):
|
|
25
|
+
def check_permission(self):
|
|
26
|
+
return self.check_role()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Delete(endpoints.DeleteEndpoint[WhatsappNotification]):
|
|
30
|
+
def check_permission(self):
|
|
31
|
+
return self.check_role()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class View(endpoints.ViewEndpoint[WhatsappNotification]):
|
|
35
|
+
def check_permission(self):
|
|
36
|
+
return self.check_role()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Send(endpoints.InstanceEndpoint[WhatsappNotification]):
|
|
40
|
+
class Meta:
|
|
41
|
+
icon = "chevron-right"
|
|
42
|
+
verbose_name = "Send"
|
|
43
|
+
|
|
44
|
+
def check_permission(self):
|
|
45
|
+
return self.check_role()
|
|
46
|
+
|
|
47
|
+
def get(self):
|
|
48
|
+
return self.formfactory().fields()
|
|
49
|
+
|
|
50
|
+
def post(self):
|
|
51
|
+
self.instance.send()
|
|
52
|
+
return super().post()
|
|
@@ -39,6 +39,7 @@ class FormFactory:
|
|
|
39
39
|
return self
|
|
40
40
|
|
|
41
41
|
def fields(self, *names, **values) -> 'FormFactory':
|
|
42
|
+
names = names + tuple(values.keys())
|
|
42
43
|
not_str = {name for name in names if not isinstance(name, str)}
|
|
43
44
|
if not_str:
|
|
44
45
|
self.fieldset('Dados Gerais', names)
|
|
@@ -126,6 +127,8 @@ class FormFactory:
|
|
|
126
127
|
form._dispose = self._dispose
|
|
127
128
|
form._image = self._image
|
|
128
129
|
form._autosubmit = self._autosubmit
|
|
130
|
+
form._submit_label = endpoint.get_submit_label()
|
|
131
|
+
form._submit_icon = endpoint.get_submit_icon()
|
|
129
132
|
for name in self._fieldlist:
|
|
130
133
|
if name not in form.fields:
|
|
131
134
|
form.fields[name] = getattr(endpoint, name)
|
|
@@ -168,6 +168,8 @@ class FormMixin:
|
|
|
168
168
|
info=self._info,
|
|
169
169
|
image=self._image,
|
|
170
170
|
autosubmit=self._autosubmit,
|
|
171
|
+
submit_label=self._submit_label,
|
|
172
|
+
submit_icon=self._submit_icon,
|
|
171
173
|
)
|
|
172
174
|
data.update(
|
|
173
175
|
controls=self.controller.controls, width=self.get_metadata("width", "100%")
|
|
@@ -528,6 +530,8 @@ class Form(DjangoForm, FormMixin):
|
|
|
528
530
|
self._method = "POST"
|
|
529
531
|
self._key = self._title.lower()
|
|
530
532
|
self._autosubmit = None
|
|
533
|
+
self._submit_label = "Enviar"
|
|
534
|
+
self._submit_icon = "chevron-right"
|
|
531
535
|
|
|
532
536
|
self.fieldsets = {}
|
|
533
537
|
self.fields = {}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from django.apps import apps
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
from django.core.management.commands.test import Command
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Command(Command):
|
|
8
|
+
def add_arguments(self, parser):
|
|
9
|
+
super().add_arguments(parser)
|
|
10
|
+
parser.add_argument("--preview", action="store_true", help="Preview")
|
|
11
|
+
|
|
12
|
+
def handle(self, *args, **options):
|
|
13
|
+
seconds = 10 if settings.DEBUG else 60
|
|
14
|
+
preview = options.get("preview")
|
|
15
|
+
try:
|
|
16
|
+
while True:
|
|
17
|
+
print('Checking whatsapp notifications...')
|
|
18
|
+
apps.get_model("slth", "whatsappnotification").objects.send(preview)
|
|
19
|
+
print('Checking email notifications...')
|
|
20
|
+
apps.get_model("slth", "email").objects.send(preview)
|
|
21
|
+
print('Checking push notifications...')
|
|
22
|
+
apps.get_model("slth", "pushnotification").objects.send(preview)
|
|
23
|
+
print('Checking jobs...')
|
|
24
|
+
apps.get_model("slth", "job").objects.execute(preview)
|
|
25
|
+
print(f'Sleeping for {seconds} seconds...')
|
|
26
|
+
time.sleep(seconds)
|
|
27
|
+
except KeyboardInterrupt:
|
|
28
|
+
pass
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Generated by Django 5.1.3 on 2025-01-23 14:16
|
|
2
|
+
|
|
3
|
+
import slth
|
|
4
|
+
import slth.db.models
|
|
5
|
+
from django.db import migrations, models
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
|
|
10
|
+
dependencies = [
|
|
11
|
+
('slth', '0012_timezone_remove_usertimezone_key_and_more'),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.CreateModel(
|
|
16
|
+
name='WhatsappNotification',
|
|
17
|
+
fields=[
|
|
18
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
19
|
+
('to', slth.db.models.CharField(max_length=255, verbose_name='To')),
|
|
20
|
+
('title', slth.db.models.CharField(max_length=255, verbose_name='Subject')),
|
|
21
|
+
('message', slth.db.models.TextField(verbose_name='Content')),
|
|
22
|
+
('url', slth.db.models.CharField(blank=True, max_length=255, null=True, verbose_name='URL')),
|
|
23
|
+
('send_at', slth.db.models.DateTimeField(blank=True, null=True, verbose_name='Send at')),
|
|
24
|
+
('sent_at', slth.db.models.DateTimeField(blank=True, null=True, verbose_name='Sent at')),
|
|
25
|
+
('attempt', slth.db.models.IntegerField(blank=True, default=0, verbose_name='Attempt')),
|
|
26
|
+
('error', slth.db.models.TextField(blank=True, null=True, verbose_name='Error')),
|
|
27
|
+
('key', slth.db.models.CharField(blank=True, db_index=True, max_length=255, null=True)),
|
|
28
|
+
],
|
|
29
|
+
options={
|
|
30
|
+
'verbose_name': 'Whatsapp Notification',
|
|
31
|
+
'verbose_name_plural': 'Whatsapp Notifications',
|
|
32
|
+
},
|
|
33
|
+
bases=(models.Model, slth.ModelMixin),
|
|
34
|
+
),
|
|
35
|
+
]
|
|
@@ -16,7 +16,7 @@ from django.utils.html import strip_tags
|
|
|
16
16
|
from django.core import serializers
|
|
17
17
|
from django.template.loader import render_to_string
|
|
18
18
|
from django.core.mail import EmailMultiAlternatives
|
|
19
|
-
from .notifications import send_push_web_notification
|
|
19
|
+
from .notifications import send_push_web_notification, send_whatsapp_notification
|
|
20
20
|
from .components import HtmlContent
|
|
21
21
|
from slth import APPLICATON
|
|
22
22
|
from django.utils import timezone
|
|
@@ -222,18 +222,19 @@ class JobManager(models.Manager):
|
|
|
222
222
|
task_runner = super().create(name=name or uuid1().hex, task=task, start=start)
|
|
223
223
|
return task_runner
|
|
224
224
|
|
|
225
|
-
def execute(self):
|
|
225
|
+
def execute(self, preview=False):
|
|
226
226
|
qs = self.filter(finish__isnull=True, attempt__lte=3, canceled=False)
|
|
227
227
|
qs = qs.filter(start__isnull=True) | qs.filter(start__lt=datetime.now())
|
|
228
228
|
for obj in qs:
|
|
229
229
|
try:
|
|
230
230
|
obj.task.job = obj
|
|
231
231
|
print(f'Executing job {obj.id}...')
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
232
|
+
if not preview:
|
|
233
|
+
obj.start = datetime.now()
|
|
234
|
+
obj.attempt += 1
|
|
235
|
+
obj.task.run()
|
|
236
|
+
obj.finish = datetime.now()
|
|
237
|
+
print(f'Job {obj.id} completed with success.')
|
|
237
238
|
except Exception as e:
|
|
238
239
|
traceback.print_exc()
|
|
239
240
|
obj.error = str(e)
|
|
@@ -243,7 +244,8 @@ class JobManager(models.Manager):
|
|
|
243
244
|
obj.finish = datetime.now()
|
|
244
245
|
print(f'Job {obj.id} completed with error ({obj.error}).')
|
|
245
246
|
finally:
|
|
246
|
-
|
|
247
|
+
if not preview:
|
|
248
|
+
obj.save()
|
|
247
249
|
|
|
248
250
|
|
|
249
251
|
class Job(models.Model):
|
|
@@ -280,18 +282,23 @@ class Job(models.Model):
|
|
|
280
282
|
|
|
281
283
|
class PushNotificationQuerySet(models.QuerySet):
|
|
282
284
|
def all(self):
|
|
283
|
-
return self
|
|
285
|
+
return self.fields('title', 'to', 'message', 'send_at', 'sent_at').search('title', 'message', 'to').filters('send_at__gte', 'send_at__lte', 'sent_at__gte', 'sent_at__lte').rows().order_by('-id')
|
|
284
286
|
|
|
285
287
|
def create(self, to, title, message, send_at=None, action=None, url=None, key=None):
|
|
286
|
-
|
|
288
|
+
if key:
|
|
289
|
+
self.filter(key=key, sent_at__isnull=False).delete()
|
|
290
|
+
notification = super().create(
|
|
287
291
|
to=to, title=title, message=message, send_at=send_at, url=url, key=key
|
|
288
292
|
)
|
|
293
|
+
if send_at and send_at <= datetime.now():
|
|
294
|
+
notification.send()
|
|
295
|
+
return notification
|
|
289
296
|
|
|
290
|
-
def send(self):
|
|
297
|
+
def send(self, preview=False):
|
|
291
298
|
qs = self.filter(attempt__lte=3, sent_at__isnull=True)
|
|
292
299
|
qs = qs.filter(send_at__isnull=True) | qs.filter(send_at__lte=datetime.now())
|
|
293
300
|
for obj in qs:
|
|
294
|
-
obj.send()
|
|
301
|
+
obj.send(preview=preview)
|
|
295
302
|
|
|
296
303
|
|
|
297
304
|
class PushNotification(models.Model):
|
|
@@ -316,19 +323,92 @@ class PushNotification(models.Model):
|
|
|
316
323
|
def __str__(self):
|
|
317
324
|
return f'Push Notification {self.title} to {self.to}'
|
|
318
325
|
|
|
319
|
-
def send(self):
|
|
326
|
+
def send(self, preview=False):
|
|
320
327
|
self.attempt = self.attempt + 1
|
|
321
328
|
try:
|
|
322
329
|
print(f'Sending notification "{self.title}" to "{self.to}"...')
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
330
|
+
if not preview:
|
|
331
|
+
send_push_web_notification(self.to, self.title, self.message, url=self.url)
|
|
332
|
+
self.sent_at = datetime.now()
|
|
333
|
+
print(f'Notification "{self.title}" to "{self.to}" sent with success.')
|
|
326
334
|
except Exception as e:
|
|
327
335
|
self.error = str(e)
|
|
328
336
|
print(f'Notification "{self.title}" to "{self.to}" failed during attempt {self.attempt}.')
|
|
329
337
|
traceback.print_exc()
|
|
330
338
|
finally:
|
|
331
|
-
|
|
339
|
+
if not preview:
|
|
340
|
+
super().save()
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
class WhatsappNotificationQuerySet(models.QuerySet):
|
|
344
|
+
def all(self):
|
|
345
|
+
return self.fields('title', 'to', 'message', 'send_at', 'sent_at').search('title', 'message', 'to').filters('send_at__gte', 'send_at__lte', 'sent_at__gte', 'sent_at__lte').rows().order_by('-id')
|
|
346
|
+
|
|
347
|
+
@meta('Enviadas')
|
|
348
|
+
def sent(self):
|
|
349
|
+
return self.filter(send_at__isnull=False)
|
|
350
|
+
|
|
351
|
+
@meta('Não-Enviadas')
|
|
352
|
+
def unsent(self):
|
|
353
|
+
return self.filter(send_at__isnull=True)
|
|
354
|
+
|
|
355
|
+
def create(self, to, title, message, send_at=None, url=None, key=None):
|
|
356
|
+
if key:
|
|
357
|
+
self.filter(key=key, sent_at__isnull=False).delete()
|
|
358
|
+
notification = super().create(
|
|
359
|
+
to=to, title=title, message=message, send_at=send_at, url=url, key=key
|
|
360
|
+
)
|
|
361
|
+
if send_at and send_at <= datetime.now():
|
|
362
|
+
notification.send()
|
|
363
|
+
return notification
|
|
364
|
+
|
|
365
|
+
def send(self, preview=False):
|
|
366
|
+
qs = self.filter(attempt__lte=3, sent_at__isnull=True)
|
|
367
|
+
qs = qs.filter(send_at__isnull=True) | qs.filter(send_at__lte=datetime.now())
|
|
368
|
+
for obj in qs:
|
|
369
|
+
obj.send(preview=preview)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
class WhatsappNotification(models.Model):
|
|
373
|
+
to = models.CharField(verbose_name='To')
|
|
374
|
+
title = models.CharField("Subject")
|
|
375
|
+
message = models.TextField("Content")
|
|
376
|
+
url = models.CharField("URL", null=True, blank=True)
|
|
377
|
+
|
|
378
|
+
send_at = models.DateTimeField("Send at", null=True, blank=True)
|
|
379
|
+
sent_at = models.DateTimeField("Sent at", null=True, blank=True)
|
|
380
|
+
attempt = models.IntegerField("Attempt", default=0, blank=True)
|
|
381
|
+
|
|
382
|
+
error = models.TextField("Error", null=True, blank=True)
|
|
383
|
+
key = models.CharField(max_length=255, null=True, blank=True, db_index=True)
|
|
384
|
+
|
|
385
|
+
class Meta:
|
|
386
|
+
verbose_name = 'Whatsapp Notification'
|
|
387
|
+
verbose_name_plural = 'Whatsapp Notifications'
|
|
388
|
+
|
|
389
|
+
objects = PushNotificationQuerySet()
|
|
390
|
+
|
|
391
|
+
def __str__(self):
|
|
392
|
+
return f'Push Notification {self.title} to {self.to}'
|
|
393
|
+
|
|
394
|
+
def send(self, preview=False):
|
|
395
|
+
self.attempt = self.attempt + 1
|
|
396
|
+
try:
|
|
397
|
+
print(f'Sending notification "{self.title}" to "{self.to}"...')
|
|
398
|
+
if not preview:
|
|
399
|
+
send_whatsapp_notification(self.to, self.title, self.message, url=self.url)
|
|
400
|
+
self.sent_at = datetime.now()
|
|
401
|
+
print(f'Notification "{self.title}" to "{self.to}" sent with success.')
|
|
402
|
+
except Exception as e:
|
|
403
|
+
self.error = str(e)
|
|
404
|
+
print(f'Notification "{self.title}" to "{self.to}" failed during attempt {self.attempt}.')
|
|
405
|
+
traceback.print_exc()
|
|
406
|
+
finally:
|
|
407
|
+
if not preview:
|
|
408
|
+
super().save()
|
|
409
|
+
|
|
410
|
+
def serializer(self):
|
|
411
|
+
return super().serializer().fieldset("Dados Gerais", ('subject', 'to', 'content', 'url', 'send_at'))
|
|
332
412
|
|
|
333
413
|
|
|
334
414
|
class EmailManager(models.Manager):
|
|
@@ -339,15 +419,18 @@ class EmailManager(models.Manager):
|
|
|
339
419
|
to = [to] if isinstance(to, str) else list(to)
|
|
340
420
|
if key:
|
|
341
421
|
self.filter(key=key, sent_at__isnull=False).delete()
|
|
342
|
-
|
|
422
|
+
email = super().create(
|
|
343
423
|
to=" ".join(to), subject=subject, content=content, send_at=send_at, action=action, url=url, key=key
|
|
344
424
|
)
|
|
425
|
+
if send_at and send_at <= datetime.now():
|
|
426
|
+
email.send()
|
|
427
|
+
return email
|
|
345
428
|
|
|
346
|
-
def send(self):
|
|
429
|
+
def send(self, preview=False):
|
|
347
430
|
qs = self.filter(attempt__lte=3, sent_at__isnull=True)
|
|
348
431
|
qs = qs.filter(send_at__isnull=True) | qs.filter(send_at__lte=datetime.now())
|
|
349
432
|
for obj in qs:
|
|
350
|
-
obj.send()
|
|
433
|
+
obj.send(preview=preview)
|
|
351
434
|
|
|
352
435
|
|
|
353
436
|
class Email(models.Model):
|
|
@@ -373,7 +456,7 @@ class Email(models.Model):
|
|
|
373
456
|
def __str__(self):
|
|
374
457
|
return self.subject
|
|
375
458
|
|
|
376
|
-
def send(self):
|
|
459
|
+
def send(self, preview=False):
|
|
377
460
|
to = [email.strip() for email in self.to.split()]
|
|
378
461
|
msg = EmailMultiAlternatives(self.subject, strip_tags(self.content), settings.DEFAULT_FROM_EMAIL, to)
|
|
379
462
|
html = render_to_string('email.html', dict(email=self, title=APPLICATON['title']))
|
|
@@ -381,15 +464,17 @@ class Email(models.Model):
|
|
|
381
464
|
self.attempt = self.attempt + 1
|
|
382
465
|
try:
|
|
383
466
|
print(f'Sending e-mail "{self.subject}" to "{self.to}"...')
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
467
|
+
if not preview:
|
|
468
|
+
msg.send(fail_silently=False)
|
|
469
|
+
self.sent_at = datetime.now()
|
|
470
|
+
print(f'E-mail "{self.subject}" to "{self.to}" sent with success.')
|
|
387
471
|
except Exception as e:
|
|
388
472
|
self.error = str(e)
|
|
389
473
|
print(f'Email "{self.subject}" to "{self.to}" failed during attempt {self.attempt}.')
|
|
390
474
|
traceback.print_exc()
|
|
391
475
|
finally:
|
|
392
|
-
|
|
476
|
+
if not preview:
|
|
477
|
+
super().save()
|
|
393
478
|
|
|
394
479
|
def formfactory(self):
|
|
395
480
|
return super().formfactory().fieldset(
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
+
import requests
|
|
3
4
|
from pywebpush import webpush
|
|
4
5
|
from slth import APPLICATON
|
|
5
6
|
|
|
7
|
+
|
|
6
8
|
def send_push_web_notification(user, title, message, url=None, icon=None):
|
|
7
9
|
icon = icon or APPLICATON['icon']
|
|
8
10
|
for subscription in user.pushsubscription_set.all():
|
|
@@ -14,3 +16,13 @@ def send_push_web_notification(user, title, message, url=None, icon=None):
|
|
|
14
16
|
vapid_claims={"sub": "mailto:admin@admin.com"}
|
|
15
17
|
)
|
|
16
18
|
print(data)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def send_whatsapp_notification(to, title, messsage, url=None):
|
|
22
|
+
api_url = 'https://whatsapp.aplicativo.click/send'
|
|
23
|
+
headers={"Content-Type": "application/json", "Authorization": "Token undefined"}
|
|
24
|
+
to = to.replace('(', '').replace(')', '').replace('-', '').replace(' ', '')
|
|
25
|
+
to = '55{}{}@c.us'.format(to[0:2], to[3:])
|
|
26
|
+
data = {"to": to, "message": "*{}*\n{}\n{}".format(title, messsage, url or "")}
|
|
27
|
+
response = requests.post(api_url, headers=headers, json=data)
|
|
28
|
+
response.status_code == 200
|
|
@@ -9,7 +9,7 @@ from django.db.models import Model, QuerySet, Manager
|
|
|
9
9
|
from django.utils.text import slugify
|
|
10
10
|
from .exceptions import JsonResponseException
|
|
11
11
|
from .utils import absolute_url, build_url
|
|
12
|
-
from .components import Image, FileLink, FileViewer
|
|
12
|
+
from .components import Image, FileLink, FileViewer, Banner
|
|
13
13
|
from django.db.models.fields.files import ImageFieldFile, FieldFile
|
|
14
14
|
|
|
15
15
|
|
|
@@ -36,7 +36,7 @@ def serialize(obj, primitive=False, request=None, logging=False):
|
|
|
36
36
|
return None
|
|
37
37
|
elif isinstance(obj, dict):
|
|
38
38
|
if request:
|
|
39
|
-
if isinstance(obj, Image):
|
|
39
|
+
if isinstance(obj, Image) or isinstance(obj, Banner):
|
|
40
40
|
if isinstance(obj['src'], ImageFieldFile):
|
|
41
41
|
obj['src'] = build_url(request, obj['src'].url) if obj['src'] else None
|
|
42
42
|
elif isinstance(obj, FileLink) or isinstance(obj, FileViewer):
|
|
Binary file
|