slthcore 0.4.9__tar.gz → 0.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of slthcore might be problematic. Click here for more details.
- {slthcore-0.4.9/slthcore.egg-info → slthcore-0.5.1}/PKG-INFO +1 -1
- {slthcore-0.4.9 → slthcore-0.5.1}/setup.py +1 -1
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/__init__.py +2 -2
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/application.py +78 -16
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/__init__.py +1 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/components.py +2 -2
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/db/models.py +3 -12
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/__init__.py +32 -23
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/auth.py +5 -2
- slthcore-0.5.1/slth/endpoints/settings.py +60 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/user.py +6 -3
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/forms.py +12 -5
- slthcore-0.5.1/slth/integrations/deepseek/__init__.py +36 -0
- slthcore-0.5.1/slth/integrations/google/gemini.py +23 -0
- slthcore-0.5.1/slth/integrations/google/places.py +41 -0
- slthcore-0.5.1/slth/integrations/google/vision.py +18 -0
- slthcore-0.5.1/slth/integrations/justvoip/__init__.py +15 -0
- slthcore-0.5.1/slth/integrations/mercadopago/__init__.py +48 -0
- slthcore-0.5.1/slth/integrations/openai/chatgpt.py +30 -0
- slthcore-0.5.1/slth/integrations/viacep/__init__.py +6 -0
- slthcore-0.5.1/slth/integrations/whatsapp/__init__.py +9 -0
- slthcore-0.5.1/slth/management/commands/__init__.py +0 -0
- slthcore-0.5.1/slth/middleware/__init__.py +0 -0
- slthcore-0.5.1/slth/middleware/timezone.py +15 -0
- slthcore-0.5.1/slth/migrations/0014_settings.py +40 -0
- slthcore-0.5.1/slth/migrations/0015_auto_20250412_1126.py +27 -0
- slthcore-0.5.1/slth/migrations/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/models.py +26 -4
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/oauth.py +10 -5
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/queryset.py +81 -26
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/serializer.py +95 -33
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/css/slth.css +14 -7
- slthcore-0.5.1/slth/static/js/slth.min.js +270 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/templates/index.html +7 -15
- slthcore-0.5.1/slth/timezone.py +33 -0
- {slthcore-0.4.9 → slthcore-0.5.1/slthcore.egg-info}/PKG-INFO +1 -1
- {slthcore-0.4.9 → slthcore-0.5.1}/slthcore.egg-info/SOURCES.txt +16 -1
- slthcore-0.4.9/slth/middleware/timezone.py +0 -28
- slthcore-0.4.9/slth/static/js/slth.min.js +0 -251
- slthcore-0.4.9/slth/tz.py +0 -13
- {slthcore-0.4.9 → slthcore-0.5.1}/MANIFEST.in +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/setup.cfg +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/apps.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/configure/__main__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/__main__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/__pycache__/__main__.cpython-312.pyc +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/.DS_Store +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/.gitignore +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/asgi.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/endpoints/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/models.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/settings.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/tests.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/urls.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/wsgi.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/entrypoint.sh +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/manage.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/requirements.txt +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/base.env +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/docker-compose.yml +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/frontend/package.json +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/frontend/src/main.jsx +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/frontend/vite.config.js +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/local.env +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/run.sh +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/selenium/run.sh +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/test.sh +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/db/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/db/generic.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/deletion.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/dev.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/email.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/job.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/log.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/profile.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/pushsubscription.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/report.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/role.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/task.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/timezone.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/whatsappnotification.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/exceptions.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/factory.py +0 -0
- {slthcore-0.4.9/slth/management → slthcore-0.5.1/slth/integrations}/__init__.py +0 -0
- {slthcore-0.4.9/slth/management/commands → slthcore-0.5.1/slth/integrations/google}/__init__.py +0 -0
- {slthcore-0.4.9/slth/middleware → slthcore-0.5.1/slth/integrations/openai}/__init__.py +0 -0
- {slthcore-0.4.9/slth/migrations → slthcore-0.5.1/slth/management}/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/management/commands/api.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/management/commands/integration_test.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/management/commands/sync.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/management/commands/worker.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0001_initial.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0002_email_role_pushsubscription_error.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0004_alter_profile_photo.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0005_alter_profile_photo.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0006_user.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0007_deletion_log.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0008_alter_deletion_datetime_alter_log_datetime.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0009_remove_email_from_email_email_action_email_attempt_and_more.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0010_email_key_alter_email_action_alter_email_attempt_and_more.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0011_usertimezone.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0012_timezone_remove_usertimezone_key_and_more.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0013_whatsappnotification.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/notifications.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/pdf/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/pdf/tests.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/permissions.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/printer.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/roles.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/selenium/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/selenium/browser.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/.DS_Store +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/css/.DS_Store +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/images/placeholder.png +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/js/index.min.js +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/js/react.min.js +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/statistics.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/tasks.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/templates/email.html +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/templates/report.html +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/templates/service-worker.js +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/templates/signature.html +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/tests.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/threadlocal.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/urls.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/utils.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slth/views.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slthcore.egg-info/dependency_links.txt +0 -0
- {slthcore-0.4.9 → slthcore-0.5.1}/slthcore.egg-info/top_level.txt +0 -0
|
@@ -64,8 +64,8 @@ class JSONDecoder(json.JSONDecoder):
|
|
|
64
64
|
return obj
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def dumps(data):
|
|
68
|
-
return json.dumps(data, cls=JSONEncoder)
|
|
67
|
+
def dumps(data, indent=1, ensure_ascii=False):
|
|
68
|
+
return json.dumps(data, indent=indent, ensure_ascii=ensure_ascii, cls=JSONEncoder)
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
def loads(data):
|
|
@@ -8,7 +8,8 @@ APPLICATION_CLASS = None
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class Style():
|
|
11
|
-
def __init__(self, color="black", background="inherite", border="none"):
|
|
11
|
+
def __init__(self, name, color="black", background="inherite", border="none"):
|
|
12
|
+
self.name = name
|
|
12
13
|
self.update(color=color, background=background, border=border)
|
|
13
14
|
|
|
14
15
|
def update(self, color=None, background=None, border=None):
|
|
@@ -19,18 +20,70 @@ class Style():
|
|
|
19
20
|
if border:
|
|
20
21
|
self.border = border
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
def to_css(self):
|
|
24
|
+
return f"""
|
|
25
|
+
--{self.name}-color: { self.color };
|
|
26
|
+
--{self.name}-border: { self.border };
|
|
27
|
+
--{self.name}-background: { self.background };
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
class ColorSchema:
|
|
31
|
+
def to_css(self):
|
|
32
|
+
css = []
|
|
33
|
+
css.append("<style>")
|
|
34
|
+
css.append(":root{")
|
|
35
|
+
css.append(f"--border-radius: {self.border_radius}px;")
|
|
36
|
+
css.append(self.default.to_css())
|
|
37
|
+
css.append(self.header.to_css())
|
|
38
|
+
css.append(self.footer.to_css())
|
|
39
|
+
css.append(self.fieldset.to_css())
|
|
40
|
+
css.append(self.input.to_css())
|
|
41
|
+
css.append(self.primary.to_css())
|
|
42
|
+
css.append(self.secondary.to_css())
|
|
43
|
+
css.append(self.auxiliary.to_css())
|
|
44
|
+
css.append(self.highlight.to_css())
|
|
45
|
+
css.append(self.info.to_css())
|
|
46
|
+
css.append(self.success.to_css())
|
|
47
|
+
css.append(self.warning.to_css())
|
|
48
|
+
css.append(self.danger.to_css())
|
|
49
|
+
css.append("</style>")
|
|
50
|
+
return "\n".join(css)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Light(ColorSchema):
|
|
24
54
|
def __init__(self):
|
|
25
|
-
self.
|
|
26
|
-
self.
|
|
27
|
-
self.
|
|
28
|
-
self.
|
|
29
|
-
|
|
30
|
-
self.
|
|
31
|
-
self.
|
|
32
|
-
self.
|
|
33
|
-
self.
|
|
55
|
+
self.border_radius = 0
|
|
56
|
+
self.default: Style = Style("default", color="#383838", background="#FFFFFF")
|
|
57
|
+
self.header: Style = Style("header", color="#383838", background="#FFFFFF")
|
|
58
|
+
self.footer: Style = Style("footer", color="#383838", background="#FFFFFF")
|
|
59
|
+
self.fieldset: Style = Style("fieldset", color="#383838", background="#FFFFFF")
|
|
60
|
+
self.input: Style = Style("input", border="solid 1px #d9d9d9", background="#FFFFFF")
|
|
61
|
+
self.primary:Style = Style("primary", color="#1351b4", background="#1351b4")
|
|
62
|
+
self.secondary:Style = Style("secondary", color="#071e41")
|
|
63
|
+
self.auxiliary:Style = Style("auxiliary", color="#2670e8", background="#f8f8f8")
|
|
64
|
+
self.highlight:Style = Style("hightlight", color="#0c326f")
|
|
65
|
+
self.info:Style = Style("info", color="#1351b4", background="#d4e5ff")
|
|
66
|
+
self.success:Style = Style("success", color="#ffffff", background="#1351b4")
|
|
67
|
+
self.warning:Style = Style("warning", color="#fff5c2")
|
|
68
|
+
self.danger:Style = Style("danger", color="#e52207")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Dark(ColorSchema):
|
|
72
|
+
def __init__(self):
|
|
73
|
+
self.border_radius = 3
|
|
74
|
+
self.default: Style = Style("default", color="#c3d0e5", background="#0D1117")
|
|
75
|
+
self.header: Style = Style("header", color="#383838", background="#FFFFFF")
|
|
76
|
+
self.footer: Style = Style("footer", color="#383838", background="#FFFFFF")
|
|
77
|
+
self.fieldset: Style = Style("fieldset", color="#91aad2", background="#262c35")
|
|
78
|
+
self.input: Style = Style("input", border="0", background="#0D1117")
|
|
79
|
+
self.primary:Style = Style("primary", color="#c3d0e5", background="#90C4F9")
|
|
80
|
+
self.secondary:Style = Style("secondary", color="#071e41")
|
|
81
|
+
self.auxiliary:Style = Style("auxiliary", color="#91aad2", background="#262c35")
|
|
82
|
+
self.highlight:Style = Style("hightlight", color="#0c326f")
|
|
83
|
+
self.info:Style = Style("info", color="#c3d0e5", background="#262c35")
|
|
84
|
+
self.success:Style = Style("success", color="#c3d0e5", background="#121f1a", border="1px solid #3b622b")
|
|
85
|
+
self.warning:Style = Style("warning", color="#fff5c2")
|
|
86
|
+
self.danger:Style = Style("danger", color="#e52207")
|
|
34
87
|
|
|
35
88
|
|
|
36
89
|
class Groups(dict):
|
|
@@ -71,8 +124,8 @@ class Menu(dict):
|
|
|
71
124
|
return items
|
|
72
125
|
|
|
73
126
|
class Oauth(list):
|
|
74
|
-
def add(self, name, client_id, client_secret, redirect_uri, authorize_url, access_token_url, user_data_url,
|
|
75
|
-
super().append(dict(name=name, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, authorize_url=authorize_url, access_token_url=access_token_url, user_data_url=user_data_url,
|
|
127
|
+
def add(self, name, client_id, client_secret, redirect_uri, authorize_url, access_token_url, user_data_url, user_username, user_email=None, user_scope=None, user_create=False, user_logout_url=None):
|
|
128
|
+
super().append(dict(name=name, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, authorize_url=authorize_url, access_token_url=access_token_url, user_data_url=user_data_url, user_username=user_username, user_email=user_email, user_scope=user_scope, user_create=user_create, user_logout_url=user_logout_url))
|
|
76
129
|
|
|
77
130
|
def serialize(self):
|
|
78
131
|
data = []
|
|
@@ -96,6 +149,7 @@ class Dashboard():
|
|
|
96
149
|
def __init__(self):
|
|
97
150
|
self.actions:List = List()
|
|
98
151
|
self.toolbar:List = List()
|
|
152
|
+
self.todo:List = List()
|
|
99
153
|
self.top:List = List()
|
|
100
154
|
self.center:List = List()
|
|
101
155
|
self.boxes:List = List()
|
|
@@ -107,6 +161,12 @@ class Dashboard():
|
|
|
107
161
|
self.index = "dashboard"
|
|
108
162
|
|
|
109
163
|
|
|
164
|
+
class Theme:
|
|
165
|
+
def __init__(self):
|
|
166
|
+
self.light = Light()
|
|
167
|
+
self.dark = Dark()
|
|
168
|
+
|
|
169
|
+
|
|
110
170
|
class ApplicationMetaclass(type):
|
|
111
171
|
|
|
112
172
|
def __new__(mcs, name, bases, attrs):
|
|
@@ -124,6 +184,7 @@ class Application(metaclass=ApplicationMetaclass):
|
|
|
124
184
|
self.subtitle = "Take your time!"
|
|
125
185
|
self.icon = "/static/images/logo.png"
|
|
126
186
|
self.logo = "/static/images/logo.png"
|
|
187
|
+
self.brand = None
|
|
127
188
|
self.version = "0.0.1"
|
|
128
189
|
self.oauth:Oauth = Oauth()
|
|
129
190
|
self.groups:Groups = Groups()
|
|
@@ -136,7 +197,8 @@ class Application(metaclass=ApplicationMetaclass):
|
|
|
136
197
|
|
|
137
198
|
def serialize(self, request):
|
|
138
199
|
icon = build_url(request, self.icon)
|
|
139
|
-
logo = build_url(request, self.logo)
|
|
200
|
+
logo = build_url(request, self.brand or self.logo)
|
|
201
|
+
title = self.title if self.brand is None else None
|
|
140
202
|
if request.user.is_authenticated:
|
|
141
203
|
user = request.user.username.split()[0].split("@")[0]
|
|
142
204
|
profile = apps.get_model("slth", "profile").objects.filter(user=request.user).first()
|
|
@@ -158,7 +220,7 @@ class Application(metaclass=ApplicationMetaclass):
|
|
|
158
220
|
type="application",
|
|
159
221
|
icon=icon,
|
|
160
222
|
navbar=dict(
|
|
161
|
-
type="navbar", title=
|
|
223
|
+
type="navbar", title=title, subtitle=self.subtitle, logo=logo, user=user, **endpoints
|
|
162
224
|
),
|
|
163
225
|
menu=dict(
|
|
164
226
|
type="menu", items=self.menu.process(request), user=user, image=photo
|
|
@@ -95,9 +95,9 @@ class Indicators(dict):
|
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
class Boxes(dict):
|
|
98
|
-
def __init__(self, title):
|
|
98
|
+
def __init__(self, title=None):
|
|
99
99
|
self["type"] = "boxes"
|
|
100
|
-
self["title"] = str(title)
|
|
100
|
+
self["title"] = str(title) if title else None
|
|
101
101
|
self["items"] = []
|
|
102
102
|
|
|
103
103
|
def append(self, icon, label, url, style=None):
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
from uuid import uuid1
|
|
2
|
-
import pytz
|
|
3
|
-
from datetime import datetime, timedelta
|
|
4
|
-
from django.utils import timezone
|
|
5
2
|
from django.db.models import Model as DjangoModel, fields as DjangoFields
|
|
6
3
|
from django.db.models import *
|
|
7
4
|
from django.utils.translation import gettext_lazy as _
|
|
8
5
|
from . import generic
|
|
9
6
|
from .. import ModelMixin
|
|
10
7
|
from slth import dumps, loads
|
|
8
|
+
from slth import timezone
|
|
11
9
|
|
|
12
10
|
GenericField = generic.GenericField
|
|
13
11
|
|
|
@@ -116,16 +114,10 @@ class TextField(TextField):
|
|
|
116
114
|
class DateTimeField(DateTimeField):
|
|
117
115
|
|
|
118
116
|
def get_db_prep_value(self, value, *args, **kwargs):
|
|
119
|
-
return
|
|
117
|
+
return timezone.get_current_timezone().localize(value).astimezone(timezone.get_default_timezone()).replace(tzinfo=None) if value else None
|
|
120
118
|
|
|
121
119
|
def from_db_value(self, value, *args, **kwargs):
|
|
122
|
-
return
|
|
123
|
-
|
|
124
|
-
def get_current_timezone(self):
|
|
125
|
-
current_timezone_name = timezone.get_current_timezone_name()
|
|
126
|
-
if current_timezone_name and current_timezone_name[0] in ('+', '-'):
|
|
127
|
-
current_timezone_name = "Etc/GMT{}".format(int(current_timezone_name))
|
|
128
|
-
return pytz.timezone(current_timezone_name)
|
|
120
|
+
return timezone.get_default_timezone().localize(value).astimezone(timezone.get_current_timezone()).replace(tzinfo=None) if value else None
|
|
129
121
|
|
|
130
122
|
|
|
131
123
|
class TaskFied(TextField):
|
|
@@ -154,7 +146,6 @@ class FileField(FileField):
|
|
|
154
146
|
|
|
155
147
|
def generate_filename(self, instance, filename):
|
|
156
148
|
filename = '{}.{}'.format(uuid1().hex, filename.split('.')[-1].lower())
|
|
157
|
-
print(filename)
|
|
158
149
|
return super().generate_filename(instance, filename)
|
|
159
150
|
|
|
160
151
|
class ImageField(ImageField):
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import io
|
|
2
|
+
import os
|
|
2
3
|
import inspect
|
|
3
4
|
from ..models import Log
|
|
4
5
|
from django.apps import apps
|
|
5
6
|
from typing import TypeVar, Generic
|
|
6
7
|
from django.core.cache import cache
|
|
7
|
-
from django.conf import settings
|
|
8
8
|
from django.utils.text import slugify
|
|
9
9
|
from django.db import models
|
|
10
|
-
from django.http import JsonResponse, HttpResponse
|
|
10
|
+
from django.http import JsonResponse, HttpResponse, StreamingHttpResponse
|
|
11
11
|
from ..factory import FormFactory
|
|
12
12
|
from django.core.exceptions import ValidationError
|
|
13
13
|
from slth import forms
|
|
@@ -27,7 +27,7 @@ from ..exceptions import JsonResponseException, ReadyResponseException
|
|
|
27
27
|
from ..utils import build_url, append_url
|
|
28
28
|
from ..models import Log, Job
|
|
29
29
|
from slth.queryset import QuerySet
|
|
30
|
-
from slth import ENDPOINTS
|
|
30
|
+
from slth import ENDPOINTS, dumps
|
|
31
31
|
from ..threadlocal import tl
|
|
32
32
|
from ..tasks import Task
|
|
33
33
|
|
|
@@ -42,7 +42,8 @@ class ApiResponse(JsonResponse):
|
|
|
42
42
|
self["Access-Control-Allow-Headers"] = "*"
|
|
43
43
|
self["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS, PUT, DELETE, PATCH"
|
|
44
44
|
self["Access-Control-Max-Age"] = "600"
|
|
45
|
-
|
|
45
|
+
x = args[0]
|
|
46
|
+
#os.system('clear'); print(dumps(args[0]))
|
|
46
47
|
|
|
47
48
|
|
|
48
49
|
class EnpointMetaclass(type):
|
|
@@ -91,7 +92,8 @@ class Endpoint(metaclass=EnpointMetaclass):
|
|
|
91
92
|
return self.formfactory().fields(*fields) if fields else {}
|
|
92
93
|
|
|
93
94
|
def post(self):
|
|
94
|
-
|
|
95
|
+
redirect = '.' if 'only' in self.request.GET else self.form._redirect
|
|
96
|
+
return Response(message="Ação realizada com sucesso", redirect=redirect)
|
|
95
97
|
|
|
96
98
|
def check_permission(self):
|
|
97
99
|
return self.request.user.is_superuser
|
|
@@ -115,6 +117,7 @@ class Endpoint(metaclass=EnpointMetaclass):
|
|
|
115
117
|
raise JsonResponseException(dict(type="redirect", url=url, autosubmit=self.request.GET.get('autosubmit')))
|
|
116
118
|
|
|
117
119
|
def render(self, data, template=None, pdf=False, autoreload=None):
|
|
120
|
+
from django.conf import settings
|
|
118
121
|
base_url=settings.SITE_URL
|
|
119
122
|
data.update(base_url=base_url)
|
|
120
123
|
if template is None:
|
|
@@ -133,7 +136,10 @@ class Endpoint(metaclass=EnpointMetaclass):
|
|
|
133
136
|
doc = HTML(string=html).render()
|
|
134
137
|
pages.extend(doc.pages)
|
|
135
138
|
new_doc = doc.copy(pages=pages)
|
|
136
|
-
|
|
139
|
+
try:
|
|
140
|
+
new_doc.write_pdf(buffer, base_url=base_url, stylesheets=[])
|
|
141
|
+
except Exception:
|
|
142
|
+
new_doc.write_pdf(buffer)
|
|
137
143
|
buffer.seek(0)
|
|
138
144
|
raise ReadyResponseException(HttpResponse(buffer, content_type='application/pdf'))
|
|
139
145
|
return TemplateContent(template, data, autoreload=autoreload)
|
|
@@ -157,7 +163,8 @@ class Endpoint(metaclass=EnpointMetaclass):
|
|
|
157
163
|
else:
|
|
158
164
|
self.cleaned_data = self.form.submit()
|
|
159
165
|
if self.form._message or self.form._redirect or self.form._dispose:
|
|
160
|
-
|
|
166
|
+
redirect = '.' if 'only' in self.request.GET else self.form._redirect
|
|
167
|
+
return Response(self.form._message, redirect, dispose=self.form._dispose)
|
|
161
168
|
else:
|
|
162
169
|
return self.post()
|
|
163
170
|
except ValidationError as e:
|
|
@@ -168,6 +175,8 @@ class Endpoint(metaclass=EnpointMetaclass):
|
|
|
168
175
|
data = self.form
|
|
169
176
|
elif isinstance(data, Form) or isinstance(data, ModelForm):
|
|
170
177
|
data = data.settitle(title)
|
|
178
|
+
elif isinstance(data, HttpResponse) or isinstance(data, StreamingHttpResponse):
|
|
179
|
+
raise ReadyResponseException(data)
|
|
171
180
|
elif self.request.method == "POST":# and not data:
|
|
172
181
|
return self.post()
|
|
173
182
|
return data
|
|
@@ -580,36 +589,36 @@ class Dashboard(Endpoint):
|
|
|
580
589
|
|
|
581
590
|
def get(self):
|
|
582
591
|
application = ApplicationConfig.get_instance()
|
|
583
|
-
serializer =
|
|
592
|
+
serializer = self.serializer()
|
|
593
|
+
if application.dashboard.actions:
|
|
594
|
+
serializer.actions(*application.dashboard.actions)
|
|
595
|
+
if application.dashboard.todo:
|
|
596
|
+
serializer.todo(*application.dashboard.todo)
|
|
597
|
+
if application.dashboard.top:
|
|
598
|
+
group = serializer.group("Top")
|
|
599
|
+
for name in application.dashboard.top:
|
|
600
|
+
cls = ENDPOINTS[name]
|
|
601
|
+
endpoint = cls.instantiate(self.request, self)
|
|
602
|
+
if endpoint.check_permission():
|
|
603
|
+
group.endpoint(cls)
|
|
604
|
+
group.parent()
|
|
584
605
|
if application.dashboard.boxes:
|
|
585
606
|
boxes = Boxes("Acesso Rápido")
|
|
586
607
|
for name in application.dashboard.boxes:
|
|
587
608
|
cls = ENDPOINTS[name]
|
|
588
609
|
endpoint = cls().contextualize(self.request)
|
|
589
610
|
if endpoint.check_permission():
|
|
590
|
-
icon = endpoint.get_icon() or "
|
|
611
|
+
icon = endpoint.get_icon() or "link"
|
|
591
612
|
label = endpoint.get_verbose_name()
|
|
592
613
|
url = build_url(self.request, cls.get_api_url())
|
|
593
614
|
boxes.append(icon, label, url)
|
|
594
|
-
serializer.append(
|
|
595
|
-
if application.dashboard.top:
|
|
596
|
-
group = serializer.group("Top")
|
|
597
|
-
for name in application.dashboard.top:
|
|
598
|
-
cls = ENDPOINTS[name]
|
|
599
|
-
endpoint = cls.instantiate(self.request, self)
|
|
600
|
-
if endpoint.check_permission():
|
|
601
|
-
group.endpoint(
|
|
602
|
-
endpoint.get_verbose_name(), cls, wrap=False
|
|
603
|
-
)
|
|
604
|
-
group.parent()
|
|
615
|
+
serializer.append(boxes)
|
|
605
616
|
if application.dashboard.center:
|
|
606
617
|
for name in application.dashboard.center:
|
|
607
618
|
cls = ENDPOINTS[name]
|
|
608
619
|
endpoint = cls.instantiate(self.request, self)
|
|
609
620
|
if endpoint.check_permission():
|
|
610
|
-
serializer.endpoint(
|
|
611
|
-
endpoint.get_verbose_name(), cls, wrap=False
|
|
612
|
-
)
|
|
621
|
+
serializer.endpoint(cls)
|
|
613
622
|
return serializer
|
|
614
623
|
|
|
615
624
|
def check_permission(self):
|
|
@@ -12,12 +12,15 @@ from django.utils import timezone
|
|
|
12
12
|
def login_response(user, redirect='/api/dashboard/'):
|
|
13
13
|
token = Token.objects.create(user=user)
|
|
14
14
|
current_timezone = timezone.get_current_timezone()
|
|
15
|
-
|
|
15
|
+
current_timezone_name = current_timezone.__str__()
|
|
16
|
+
timezone_instance = TimeZone.objects.filter(name=current_timezone_name).first()
|
|
17
|
+
if timezone_instance is None:
|
|
18
|
+
timezone_instance = TimeZone.objects.create(name=current_timezone_name)
|
|
16
19
|
user_timezone = UserTimeZone.objects.filter(user=user).first()
|
|
17
20
|
if user_timezone is None:
|
|
18
21
|
UserTimeZone.objects.create(user=user, timezone=timezone_instance)
|
|
19
22
|
else:
|
|
20
|
-
user_timezone.
|
|
23
|
+
user_timezone.timezone = timezone_instance
|
|
21
24
|
user_timezone.save()
|
|
22
25
|
return Response(
|
|
23
26
|
message="Bem-vindo!",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from slth import endpoints
|
|
2
|
+
from ..models import Settings as SettingsModel
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Settings(endpoints.ListEndpoint[SettingsModel]):
|
|
6
|
+
class Meta:
|
|
7
|
+
modal = False
|
|
8
|
+
verbose_name = 'Configurações'
|
|
9
|
+
|
|
10
|
+
def get(self):
|
|
11
|
+
return (
|
|
12
|
+
super().get()
|
|
13
|
+
.actions('settings.add', 'settings.view', 'settings.edit', 'settings.delete')
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Add(endpoints.AddEndpoint[SettingsModel]):
|
|
18
|
+
class Meta:
|
|
19
|
+
icon = 'plus'
|
|
20
|
+
verbose_name = 'Cadastrar Configuração'
|
|
21
|
+
|
|
22
|
+
def get(self):
|
|
23
|
+
return (
|
|
24
|
+
super().get()
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class View(endpoints.ViewEndpoint[SettingsModel]):
|
|
29
|
+
class Meta:
|
|
30
|
+
modal = False
|
|
31
|
+
icon = 'eye'
|
|
32
|
+
verbose_name = 'Visualizar Configuração'
|
|
33
|
+
|
|
34
|
+
def get(self):
|
|
35
|
+
return (
|
|
36
|
+
super().get()
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Edit(endpoints.EditEndpoint[SettingsModel]):
|
|
41
|
+
class Meta:
|
|
42
|
+
icon = 'pen'
|
|
43
|
+
verbose_name = 'Editar Configuração'
|
|
44
|
+
|
|
45
|
+
def get(self):
|
|
46
|
+
return (
|
|
47
|
+
super().get()
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Delete(endpoints.DeleteEndpoint[SettingsModel]):
|
|
52
|
+
class Meta:
|
|
53
|
+
icon = 'trash'
|
|
54
|
+
verbose_name = 'Excluir Configuração'
|
|
55
|
+
|
|
56
|
+
def get(self):
|
|
57
|
+
return (
|
|
58
|
+
super().get()
|
|
59
|
+
)
|
|
60
|
+
|
|
@@ -9,7 +9,8 @@ class Users(endpoints.ListEndpoint[User]):
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class Add(endpoints.AddEndpoint[User]):
|
|
12
|
-
|
|
12
|
+
class Meta:
|
|
13
|
+
icon = "user-plus"
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class View(endpoints.ViewEndpoint[User]):
|
|
@@ -17,11 +18,13 @@ class View(endpoints.ViewEndpoint[User]):
|
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class Edit(endpoints.EditEndpoint[User]):
|
|
20
|
-
|
|
21
|
+
class Meta:
|
|
22
|
+
icon = "user-pen"
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
class Delete(endpoints.DeleteEndpoint[User]):
|
|
24
|
-
|
|
26
|
+
class Meta:
|
|
27
|
+
icon = "user-minus"
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
class ChangePassword(endpoints.ChildInstanceEndpoint):
|
|
@@ -66,8 +66,15 @@ class FormController:
|
|
|
66
66
|
v = v.strftime("%Y-%m-%d")
|
|
67
67
|
self.controls["set"][k] = v
|
|
68
68
|
|
|
69
|
-
def get(self, field_name, default=None):
|
|
70
|
-
|
|
69
|
+
def get(self, field_name, *field_names, default=None):
|
|
70
|
+
value = self.field_value(field_name, self.form.request.GET.get(field_name, default))
|
|
71
|
+
if field_names:
|
|
72
|
+
values = [value]
|
|
73
|
+
for field_name in field_names:
|
|
74
|
+
values.append(self.field_value(field_name, self.form.request.GET.get(field_name, default)))
|
|
75
|
+
return values
|
|
76
|
+
else:
|
|
77
|
+
return value
|
|
71
78
|
|
|
72
79
|
def field_value(self, name, value, default=None):
|
|
73
80
|
if value is None:
|
|
@@ -276,7 +283,7 @@ class FormMixin:
|
|
|
276
283
|
max=field.max,
|
|
277
284
|
name=name,
|
|
278
285
|
count=len(instances),
|
|
279
|
-
label=label,
|
|
286
|
+
label=str(label),
|
|
280
287
|
required=required,
|
|
281
288
|
value=value,
|
|
282
289
|
)
|
|
@@ -333,10 +340,10 @@ class FormMixin:
|
|
|
333
340
|
data = dict(
|
|
334
341
|
type=ftype,
|
|
335
342
|
name=fname,
|
|
336
|
-
label=field.label,
|
|
343
|
+
label=str(field.label) if field.label else None,
|
|
337
344
|
required=field.required,
|
|
338
345
|
value=value,
|
|
339
|
-
help_text=field.help_text,
|
|
346
|
+
help_text=str(field.help_text),
|
|
340
347
|
mask=None,
|
|
341
348
|
)
|
|
342
349
|
data.update(**extra)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
from slth.integrations import deepseek
|
|
6
|
+
deepseek.ask("Em uma única palavra, qual a capital do Brasil?")
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
DEEPSEEK_TOKEN = os.environ.get("DEEPSEEK_TOKEN")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def ask(prompt, model="deepseek-chat"):
|
|
13
|
+
headers = {
|
|
14
|
+
"Authorization": "Bearer {}".format(DEEPSEEK_TOKEN),
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
}
|
|
17
|
+
data = {
|
|
18
|
+
"model": model,
|
|
19
|
+
"messages": [
|
|
20
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
|
21
|
+
{
|
|
22
|
+
"role": "user",
|
|
23
|
+
"content": prompt,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
"stream": False,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
response = requests.post(
|
|
30
|
+
"https://api.deepseek.com/chat/completions", headers=headers, json=data
|
|
31
|
+
)
|
|
32
|
+
data = response.json()
|
|
33
|
+
try:
|
|
34
|
+
return data["choices"][0]["message"]["content"]
|
|
35
|
+
except KeyError:
|
|
36
|
+
return Exception(response.text)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
from slth.integrations.google import gemini
|
|
6
|
+
gemini.ask("Qual a capital do Brasil?")
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
GEMINI_API_KEY = os.environ.get('GEMINI_API_KEY')
|
|
10
|
+
|
|
11
|
+
def ask(prompt):
|
|
12
|
+
data = {"contents": [{"parts":[{"text": prompt}]}]}
|
|
13
|
+
response = requests.post(
|
|
14
|
+
"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={}".format(
|
|
15
|
+
GEMINI_API_KEY
|
|
16
|
+
), json=data
|
|
17
|
+
)
|
|
18
|
+
data = response.json()
|
|
19
|
+
try:
|
|
20
|
+
return data["candidates"][0]["content"]["parts"][0]["text"].strip()
|
|
21
|
+
except KeyError or IndexError:
|
|
22
|
+
return Exception(response.text)
|
|
23
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
from slth.integrations.google import places
|
|
6
|
+
places.search("Natal Shopping, Natal, RN")
|
|
7
|
+
places.geolocation("ChIJj2AvPX__sgcRNr1SxD-1jdU")
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def search(description):
|
|
11
|
+
response = requests.get(
|
|
12
|
+
"https://maps.googleapis.com/maps/api/place/autocomplete/json?input={}&key={}".format(
|
|
13
|
+
description, os.environ['GOOGLE_TOKEN']
|
|
14
|
+
)
|
|
15
|
+
)
|
|
16
|
+
data = response.json()
|
|
17
|
+
try:
|
|
18
|
+
return [dict(id=prediction['place_id'], value=prediction['description']) for prediction in data['predictions']]
|
|
19
|
+
except KeyError:
|
|
20
|
+
return Exception(response.text)
|
|
21
|
+
|
|
22
|
+
def geolocation(id_or_description):
|
|
23
|
+
if ' ' in id_or_description:
|
|
24
|
+
data = search(id_or_description)
|
|
25
|
+
place_id = data[0]['id'] if data else None
|
|
26
|
+
else:
|
|
27
|
+
place_id = id_or_description
|
|
28
|
+
|
|
29
|
+
if place_id:
|
|
30
|
+
response = requests.get(
|
|
31
|
+
"https://maps.googleapis.com/maps/api/place/details/json?place_id={}&key={}".format(
|
|
32
|
+
place_id, os.environ['GOOGLE_TOKEN']
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
data = response.json()
|
|
36
|
+
try:
|
|
37
|
+
location = data['result']['geometry']['location']
|
|
38
|
+
return location['lat'], location['lng']
|
|
39
|
+
except KeyError:
|
|
40
|
+
return Exception(response.text)
|
|
41
|
+
return None
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
import base64
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def ocr(uri):
|
|
7
|
+
if uri.startswith('http'):
|
|
8
|
+
image = {"source": {"image_uri": uri}}
|
|
9
|
+
else:
|
|
10
|
+
with open(uri, 'rb') as file:
|
|
11
|
+
image = {"content": base64.b64encode(file.read()).decode()}
|
|
12
|
+
url = 'https://vision.googleapis.com/v1/images:annotate?key={}'.format(os.environ['GOOGLE_TOKEN'])
|
|
13
|
+
data = {"requests": [{"image": image, "features": {"type": "TEXT_DETECTION"}}]}
|
|
14
|
+
response = requests.post(url, json=data).json()
|
|
15
|
+
for item in response['responses']:
|
|
16
|
+
if item and 'fullTextAnnotation' in item:
|
|
17
|
+
return item['fullTextAnnotation']['text']
|
|
18
|
+
return None
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
JUSTVOIP_USERNAME = os.environ.get('JUSTVOIP_USERNAME')
|
|
5
|
+
JUSTVOIP_PASSWORD = os.environ.get('JUSTVOIP_PASSWORD')
|
|
6
|
+
JUSTVOIP_FROM = os.environ.get('JUSTVOIP_FROM')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def send(to, message):
|
|
10
|
+
to = '55{}'.format(to).replace('-', '').replace('(', '').replace(')', '').replace(' ', '')
|
|
11
|
+
url = 'https://www.justvoip.com/myaccount/sendsms.php?username={}&password={}&from={}&to={}&text={}'.format(
|
|
12
|
+
JUSTVOIP_USERNAME, JUSTVOIP_PASSWORD, JUSTVOIP_FROM, to, message
|
|
13
|
+
)
|
|
14
|
+
response = requests.get(url)
|
|
15
|
+
print(response.text)
|