slthcore 0.4.9__tar.gz → 0.5.0__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.0}/PKG-INFO +1 -1
- {slthcore-0.4.9 → slthcore-0.5.0}/setup.py +1 -1
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/__init__.py +2 -2
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/application.py +78 -16
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/api/__init__.py +1 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/components.py +2 -2
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/__init__.py +28 -22
- slthcore-0.5.0/slth/endpoints/settings.py +60 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/user.py +6 -3
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/forms.py +12 -5
- slthcore-0.5.0/slth/integrations/deepseek/__init__.py +36 -0
- slthcore-0.5.0/slth/integrations/google/gemini.py +23 -0
- slthcore-0.5.0/slth/integrations/google/places.py +41 -0
- slthcore-0.5.0/slth/integrations/google/vision.py +18 -0
- slthcore-0.5.0/slth/integrations/justvoip/__init__.py +15 -0
- slthcore-0.5.0/slth/integrations/mercadopago/__init__.py +48 -0
- slthcore-0.5.0/slth/integrations/openai/chatgpt.py +30 -0
- slthcore-0.5.0/slth/integrations/viacep/__init__.py +6 -0
- slthcore-0.5.0/slth/integrations/whatsapp/__init__.py +9 -0
- slthcore-0.5.0/slth/management/commands/__init__.py +0 -0
- slthcore-0.5.0/slth/middleware/__init__.py +0 -0
- slthcore-0.5.0/slth/migrations/0014_settings.py +40 -0
- slthcore-0.5.0/slth/migrations/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/models.py +23 -2
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/oauth.py +10 -5
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/queryset.py +81 -26
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/serializer.py +91 -32
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/static/css/slth.css +14 -7
- slthcore-0.5.0/slth/static/js/slth.min.js +270 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/templates/index.html +7 -15
- {slthcore-0.4.9 → slthcore-0.5.0/slthcore.egg-info}/PKG-INFO +1 -1
- {slthcore-0.4.9 → slthcore-0.5.0}/slthcore.egg-info/SOURCES.txt +14 -0
- slthcore-0.4.9/slth/static/js/slth.min.js +0 -251
- {slthcore-0.4.9 → slthcore-0.5.0}/MANIFEST.in +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/setup.cfg +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/apps.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/configure/__main__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/__main__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/__pycache__/__main__.cpython-312.pyc +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/.DS_Store +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/.gitignore +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/api/asgi.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/api/endpoints/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/api/models.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/api/settings.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/api/tests.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/api/urls.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/api/wsgi.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/entrypoint.sh +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/manage.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/backend/requirements.txt +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/base.env +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/docker-compose.yml +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/frontend/package.json +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/frontend/src/main.jsx +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/frontend/vite.config.js +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/local.env +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/run.sh +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/selenium/run.sh +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/cmd/init/boilerplate/test.sh +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/db/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/db/generic.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/db/models.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/auth.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/deletion.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/dev.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/email.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/job.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/log.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/profile.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/pushsubscription.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/report.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/role.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/task.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/timezone.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/endpoints/whatsappnotification.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/exceptions.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/factory.py +0 -0
- {slthcore-0.4.9/slth/management → slthcore-0.5.0/slth/integrations}/__init__.py +0 -0
- {slthcore-0.4.9/slth/management/commands → slthcore-0.5.0/slth/integrations/google}/__init__.py +0 -0
- {slthcore-0.4.9/slth/middleware → slthcore-0.5.0/slth/integrations/openai}/__init__.py +0 -0
- {slthcore-0.4.9/slth/migrations → slthcore-0.5.0/slth/management}/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/management/commands/api.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/management/commands/integration_test.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/management/commands/sync.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/management/commands/worker.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/middleware/timezone.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0001_initial.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0002_email_role_pushsubscription_error.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0004_alter_profile_photo.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0005_alter_profile_photo.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0006_user.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0007_deletion_log.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0008_alter_deletion_datetime_alter_log_datetime.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0009_remove_email_from_email_email_action_email_attempt_and_more.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0010_email_key_alter_email_action_alter_email_attempt_and_more.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0011_usertimezone.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0012_timezone_remove_usertimezone_key_and_more.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/migrations/0013_whatsappnotification.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/notifications.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/pdf/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/pdf/tests.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/permissions.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/printer.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/roles.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/selenium/__init__.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/selenium/browser.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/static/.DS_Store +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/static/css/.DS_Store +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/static/images/placeholder.png +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/static/js/index.min.js +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/static/js/react.min.js +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/statistics.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/tasks.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/templates/email.html +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/templates/report.html +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/templates/service-worker.js +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/templates/signature.html +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/tests.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/threadlocal.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/tz.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/urls.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/utils.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slth/views.py +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/slthcore.egg-info/dependency_links.txt +0 -0
- {slthcore-0.4.9 → slthcore-0.5.0}/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,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:
|
|
@@ -157,7 +160,8 @@ class Endpoint(metaclass=EnpointMetaclass):
|
|
|
157
160
|
else:
|
|
158
161
|
self.cleaned_data = self.form.submit()
|
|
159
162
|
if self.form._message or self.form._redirect or self.form._dispose:
|
|
160
|
-
|
|
163
|
+
redirect = '.' if 'only' in self.request.GET else self.form._redirect
|
|
164
|
+
return Response(self.form._message, redirect, dispose=self.form._dispose)
|
|
161
165
|
else:
|
|
162
166
|
return self.post()
|
|
163
167
|
except ValidationError as e:
|
|
@@ -168,6 +172,8 @@ class Endpoint(metaclass=EnpointMetaclass):
|
|
|
168
172
|
data = self.form
|
|
169
173
|
elif isinstance(data, Form) or isinstance(data, ModelForm):
|
|
170
174
|
data = data.settitle(title)
|
|
175
|
+
elif isinstance(data, HttpResponse) or isinstance(data, StreamingHttpResponse):
|
|
176
|
+
raise ReadyResponseException(data)
|
|
171
177
|
elif self.request.method == "POST":# and not data:
|
|
172
178
|
return self.post()
|
|
173
179
|
return data
|
|
@@ -580,36 +586,36 @@ class Dashboard(Endpoint):
|
|
|
580
586
|
|
|
581
587
|
def get(self):
|
|
582
588
|
application = ApplicationConfig.get_instance()
|
|
583
|
-
serializer =
|
|
589
|
+
serializer = self.serializer()
|
|
590
|
+
if application.dashboard.actions:
|
|
591
|
+
serializer.actions(*application.dashboard.actions)
|
|
592
|
+
if application.dashboard.todo:
|
|
593
|
+
serializer.todo(*application.dashboard.todo)
|
|
594
|
+
if application.dashboard.top:
|
|
595
|
+
group = serializer.group("Top")
|
|
596
|
+
for name in application.dashboard.top:
|
|
597
|
+
cls = ENDPOINTS[name]
|
|
598
|
+
endpoint = cls.instantiate(self.request, self)
|
|
599
|
+
if endpoint.check_permission():
|
|
600
|
+
group.endpoint(cls)
|
|
601
|
+
group.parent()
|
|
584
602
|
if application.dashboard.boxes:
|
|
585
603
|
boxes = Boxes("Acesso Rápido")
|
|
586
604
|
for name in application.dashboard.boxes:
|
|
587
605
|
cls = ENDPOINTS[name]
|
|
588
606
|
endpoint = cls().contextualize(self.request)
|
|
589
607
|
if endpoint.check_permission():
|
|
590
|
-
icon = endpoint.get_icon() or "
|
|
608
|
+
icon = endpoint.get_icon() or "link"
|
|
591
609
|
label = endpoint.get_verbose_name()
|
|
592
610
|
url = build_url(self.request, cls.get_api_url())
|
|
593
611
|
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()
|
|
612
|
+
serializer.append(boxes)
|
|
605
613
|
if application.dashboard.center:
|
|
606
614
|
for name in application.dashboard.center:
|
|
607
615
|
cls = ENDPOINTS[name]
|
|
608
616
|
endpoint = cls.instantiate(self.request, self)
|
|
609
617
|
if endpoint.check_permission():
|
|
610
|
-
serializer.endpoint(
|
|
611
|
-
endpoint.get_verbose_name(), cls, wrap=False
|
|
612
|
-
)
|
|
618
|
+
serializer.endpoint(cls)
|
|
613
619
|
return serializer
|
|
614
620
|
|
|
615
621
|
def check_permission(self):
|
|
@@ -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)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import requests
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
from slth.integrations import mercadopago
|
|
8
|
+
id = mercadopago.payment("04770402414", "juca@mail.com", "pix", "Produto 01", 1)
|
|
9
|
+
mercadopago.approved(id)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
MERCADOPAGO_TOKEN = os.environ.get('MERCADOPAGO_TOKEN')
|
|
13
|
+
|
|
14
|
+
def payment_methods():
|
|
15
|
+
url = 'https://api.mercadopago.com/v1/payment_methods'
|
|
16
|
+
headers = {'Authorization': f'Bearer {MERCADOPAGO_TOKEN}'}
|
|
17
|
+
response = requests.get(url, headers=headers)
|
|
18
|
+
return [dict(id=method['id'], value=method['name']) for method in response.json()]
|
|
19
|
+
|
|
20
|
+
def payment(cpf, email, payment_method, description, amount):
|
|
21
|
+
url = 'https://api.mercadopago.com/v1/payments'
|
|
22
|
+
headers = {'Authorization': f'Bearer {MERCADOPAGO_TOKEN}'}
|
|
23
|
+
data = {
|
|
24
|
+
"description": description,
|
|
25
|
+
"external_reference": cpf,
|
|
26
|
+
"payer": {
|
|
27
|
+
"email": email,
|
|
28
|
+
"identification": {
|
|
29
|
+
"type": "CPF",
|
|
30
|
+
"number": cpf
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"payment_method_id": payment_method,
|
|
34
|
+
"transaction_amount": float(amount)
|
|
35
|
+
}
|
|
36
|
+
response = requests.post(url, headers=headers, json=data)
|
|
37
|
+
data = response.json()
|
|
38
|
+
try:
|
|
39
|
+
return data['id']
|
|
40
|
+
except KeyError:
|
|
41
|
+
raise Exception(response.text)
|
|
42
|
+
|
|
43
|
+
def approved(id):
|
|
44
|
+
url = f'https://api.mercadopago.com/v1/payments/{id}'
|
|
45
|
+
headers = {'Authorization': f'Bearer {MERCADOPAGO_TOKEN}'}
|
|
46
|
+
response = requests.get(url, headers=headers)
|
|
47
|
+
data = response.json()
|
|
48
|
+
return data['status'] == "approved"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
from slth.integrations.openai import chatgpt
|
|
6
|
+
chatgpt.ask("Qual a capital do Brasil?")
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
OPENAI_TOKEN = os.environ.get('OPENAI_TOKEN')
|
|
10
|
+
|
|
11
|
+
def ask(prompt, model="gpt-4"):
|
|
12
|
+
headers = {
|
|
13
|
+
"Authorization": "Bearer {}".format(OPENAI_TOKEN),
|
|
14
|
+
"Content-Type": "application/json"
|
|
15
|
+
}
|
|
16
|
+
data = {
|
|
17
|
+
"model": model, # Or "gpt-3.5-turbo"
|
|
18
|
+
"messages": [
|
|
19
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
|
20
|
+
{"role": "user", "content": prompt}
|
|
21
|
+
],
|
|
22
|
+
"temperature": 0.7
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=data)
|
|
26
|
+
data = response.json()
|
|
27
|
+
try:
|
|
28
|
+
return data['choices'][0]['message']['content']
|
|
29
|
+
except KeyError:
|
|
30
|
+
return Exception(response.text)
|