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.

Files changed (130) hide show
  1. {slthcore-0.4.9/slthcore.egg-info → slthcore-0.5.1}/PKG-INFO +1 -1
  2. {slthcore-0.4.9 → slthcore-0.5.1}/setup.py +1 -1
  3. {slthcore-0.4.9 → slthcore-0.5.1}/slth/__init__.py +2 -2
  4. {slthcore-0.4.9 → slthcore-0.5.1}/slth/application.py +78 -16
  5. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/__init__.py +1 -0
  6. {slthcore-0.4.9 → slthcore-0.5.1}/slth/components.py +2 -2
  7. {slthcore-0.4.9 → slthcore-0.5.1}/slth/db/models.py +3 -12
  8. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/__init__.py +32 -23
  9. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/auth.py +5 -2
  10. slthcore-0.5.1/slth/endpoints/settings.py +60 -0
  11. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/user.py +6 -3
  12. {slthcore-0.4.9 → slthcore-0.5.1}/slth/forms.py +12 -5
  13. slthcore-0.5.1/slth/integrations/deepseek/__init__.py +36 -0
  14. slthcore-0.5.1/slth/integrations/google/gemini.py +23 -0
  15. slthcore-0.5.1/slth/integrations/google/places.py +41 -0
  16. slthcore-0.5.1/slth/integrations/google/vision.py +18 -0
  17. slthcore-0.5.1/slth/integrations/justvoip/__init__.py +15 -0
  18. slthcore-0.5.1/slth/integrations/mercadopago/__init__.py +48 -0
  19. slthcore-0.5.1/slth/integrations/openai/chatgpt.py +30 -0
  20. slthcore-0.5.1/slth/integrations/viacep/__init__.py +6 -0
  21. slthcore-0.5.1/slth/integrations/whatsapp/__init__.py +9 -0
  22. slthcore-0.5.1/slth/management/commands/__init__.py +0 -0
  23. slthcore-0.5.1/slth/middleware/__init__.py +0 -0
  24. slthcore-0.5.1/slth/middleware/timezone.py +15 -0
  25. slthcore-0.5.1/slth/migrations/0014_settings.py +40 -0
  26. slthcore-0.5.1/slth/migrations/0015_auto_20250412_1126.py +27 -0
  27. slthcore-0.5.1/slth/migrations/__init__.py +0 -0
  28. {slthcore-0.4.9 → slthcore-0.5.1}/slth/models.py +26 -4
  29. {slthcore-0.4.9 → slthcore-0.5.1}/slth/oauth.py +10 -5
  30. {slthcore-0.4.9 → slthcore-0.5.1}/slth/queryset.py +81 -26
  31. {slthcore-0.4.9 → slthcore-0.5.1}/slth/serializer.py +95 -33
  32. {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/css/slth.css +14 -7
  33. slthcore-0.5.1/slth/static/js/slth.min.js +270 -0
  34. {slthcore-0.4.9 → slthcore-0.5.1}/slth/templates/index.html +7 -15
  35. slthcore-0.5.1/slth/timezone.py +33 -0
  36. {slthcore-0.4.9 → slthcore-0.5.1/slthcore.egg-info}/PKG-INFO +1 -1
  37. {slthcore-0.4.9 → slthcore-0.5.1}/slthcore.egg-info/SOURCES.txt +16 -1
  38. slthcore-0.4.9/slth/middleware/timezone.py +0 -28
  39. slthcore-0.4.9/slth/static/js/slth.min.js +0 -251
  40. slthcore-0.4.9/slth/tz.py +0 -13
  41. {slthcore-0.4.9 → slthcore-0.5.1}/MANIFEST.in +0 -0
  42. {slthcore-0.4.9 → slthcore-0.5.1}/setup.cfg +0 -0
  43. {slthcore-0.4.9 → slthcore-0.5.1}/slth/apps.py +0 -0
  44. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/configure/__main__.py +0 -0
  45. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/__main__.py +0 -0
  46. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/__pycache__/__main__.cpython-312.pyc +0 -0
  47. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/.DS_Store +0 -0
  48. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/.gitignore +0 -0
  49. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/asgi.py +0 -0
  50. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/endpoints/__init__.py +0 -0
  51. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/models.py +0 -0
  52. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/settings.py +0 -0
  53. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/tests.py +0 -0
  54. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/urls.py +0 -0
  55. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/api/wsgi.py +0 -0
  56. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/entrypoint.sh +0 -0
  57. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/manage.py +0 -0
  58. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/backend/requirements.txt +0 -0
  59. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/base.env +0 -0
  60. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/docker-compose.yml +0 -0
  61. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/frontend/package.json +0 -0
  62. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/frontend/src/main.jsx +0 -0
  63. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/frontend/vite.config.js +0 -0
  64. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/local.env +0 -0
  65. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/run.sh +0 -0
  66. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/selenium/run.sh +0 -0
  67. {slthcore-0.4.9 → slthcore-0.5.1}/slth/cmd/init/boilerplate/test.sh +0 -0
  68. {slthcore-0.4.9 → slthcore-0.5.1}/slth/db/__init__.py +0 -0
  69. {slthcore-0.4.9 → slthcore-0.5.1}/slth/db/generic.py +0 -0
  70. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/deletion.py +0 -0
  71. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/dev.py +0 -0
  72. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/email.py +0 -0
  73. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/job.py +0 -0
  74. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/log.py +0 -0
  75. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/profile.py +0 -0
  76. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/pushsubscription.py +0 -0
  77. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/report.py +0 -0
  78. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/role.py +0 -0
  79. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/task.py +0 -0
  80. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/timezone.py +0 -0
  81. {slthcore-0.4.9 → slthcore-0.5.1}/slth/endpoints/whatsappnotification.py +0 -0
  82. {slthcore-0.4.9 → slthcore-0.5.1}/slth/exceptions.py +0 -0
  83. {slthcore-0.4.9 → slthcore-0.5.1}/slth/factory.py +0 -0
  84. {slthcore-0.4.9/slth/management → slthcore-0.5.1/slth/integrations}/__init__.py +0 -0
  85. {slthcore-0.4.9/slth/management/commands → slthcore-0.5.1/slth/integrations/google}/__init__.py +0 -0
  86. {slthcore-0.4.9/slth/middleware → slthcore-0.5.1/slth/integrations/openai}/__init__.py +0 -0
  87. {slthcore-0.4.9/slth/migrations → slthcore-0.5.1/slth/management}/__init__.py +0 -0
  88. {slthcore-0.4.9 → slthcore-0.5.1}/slth/management/commands/api.py +0 -0
  89. {slthcore-0.4.9 → slthcore-0.5.1}/slth/management/commands/integration_test.py +0 -0
  90. {slthcore-0.4.9 → slthcore-0.5.1}/slth/management/commands/sync.py +0 -0
  91. {slthcore-0.4.9 → slthcore-0.5.1}/slth/management/commands/worker.py +0 -0
  92. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0001_initial.py +0 -0
  93. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0002_email_role_pushsubscription_error.py +0 -0
  94. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +0 -0
  95. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0004_alter_profile_photo.py +0 -0
  96. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0005_alter_profile_photo.py +0 -0
  97. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0006_user.py +0 -0
  98. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0007_deletion_log.py +0 -0
  99. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0008_alter_deletion_datetime_alter_log_datetime.py +0 -0
  100. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0009_remove_email_from_email_email_action_email_attempt_and_more.py +0 -0
  101. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0010_email_key_alter_email_action_alter_email_attempt_and_more.py +0 -0
  102. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0011_usertimezone.py +0 -0
  103. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0012_timezone_remove_usertimezone_key_and_more.py +0 -0
  104. {slthcore-0.4.9 → slthcore-0.5.1}/slth/migrations/0013_whatsappnotification.py +0 -0
  105. {slthcore-0.4.9 → slthcore-0.5.1}/slth/notifications.py +0 -0
  106. {slthcore-0.4.9 → slthcore-0.5.1}/slth/pdf/__init__.py +0 -0
  107. {slthcore-0.4.9 → slthcore-0.5.1}/slth/pdf/tests.py +0 -0
  108. {slthcore-0.4.9 → slthcore-0.5.1}/slth/permissions.py +0 -0
  109. {slthcore-0.4.9 → slthcore-0.5.1}/slth/printer.py +0 -0
  110. {slthcore-0.4.9 → slthcore-0.5.1}/slth/roles.py +0 -0
  111. {slthcore-0.4.9 → slthcore-0.5.1}/slth/selenium/__init__.py +0 -0
  112. {slthcore-0.4.9 → slthcore-0.5.1}/slth/selenium/browser.py +0 -0
  113. {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/.DS_Store +0 -0
  114. {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/css/.DS_Store +0 -0
  115. {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/images/placeholder.png +0 -0
  116. {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/js/index.min.js +0 -0
  117. {slthcore-0.4.9 → slthcore-0.5.1}/slth/static/js/react.min.js +0 -0
  118. {slthcore-0.4.9 → slthcore-0.5.1}/slth/statistics.py +0 -0
  119. {slthcore-0.4.9 → slthcore-0.5.1}/slth/tasks.py +0 -0
  120. {slthcore-0.4.9 → slthcore-0.5.1}/slth/templates/email.html +0 -0
  121. {slthcore-0.4.9 → slthcore-0.5.1}/slth/templates/report.html +0 -0
  122. {slthcore-0.4.9 → slthcore-0.5.1}/slth/templates/service-worker.js +0 -0
  123. {slthcore-0.4.9 → slthcore-0.5.1}/slth/templates/signature.html +0 -0
  124. {slthcore-0.4.9 → slthcore-0.5.1}/slth/tests.py +0 -0
  125. {slthcore-0.4.9 → slthcore-0.5.1}/slth/threadlocal.py +0 -0
  126. {slthcore-0.4.9 → slthcore-0.5.1}/slth/urls.py +0 -0
  127. {slthcore-0.4.9 → slthcore-0.5.1}/slth/utils.py +0 -0
  128. {slthcore-0.4.9 → slthcore-0.5.1}/slth/views.py +0 -0
  129. {slthcore-0.4.9 → slthcore-0.5.1}/slthcore.egg-info/dependency_links.txt +0 -0
  130. {slthcore-0.4.9 → slthcore-0.5.1}/slthcore.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: slthcore
3
- Version: 0.4.9
3
+ Version: 0.5.1
4
4
  Summary: API generator based on yml file
5
5
  Home-page: https://github.com/brenokcc
6
6
  Author: Breno Silva
@@ -4,7 +4,7 @@ install_requires = []
4
4
 
5
5
  setup(
6
6
  name='slthcore',
7
- version='0.4.9',
7
+ version='0.5.1',
8
8
  packages=find_packages(),
9
9
  install_requires=install_requires,
10
10
  include_package_data=True,
@@ -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
- class Theme():
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.primary:Style = Style("#1351b4")
26
- self.secondary:Style = Style("#071e41")
27
- self.auxiliary:Style = Style("#2670e8")
28
- self.highlight:Style = Style("#0c326f")
29
-
30
- self.info:Style = Style("#1351b4", "#d4e5ff")
31
- self.success:Style = Style("#1351b4")
32
- self.warning:Style = Style("#fff5c2")
33
- self.danger:Style = Style("#e52207")
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, user_logout_url, user_scope, user_create, user_username, user_email):
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, user_logout_url=user_logout_url, user_scope=user_scope, user_create=user_create, user_username=user_username, user_email=user_email))
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=self.title, subtitle=self.subtitle, logo=logo, user=user, **endpoints
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
@@ -13,6 +13,7 @@ class ApiApplication(Application):
13
13
  "pushsubscription.pushsubscriptions",
14
14
  "job.jobs",
15
15
  "deletion.deletions",
16
+ "settings.settings",
16
17
  "auth.logout",
17
18
  )
18
19
  self.dashboard.boxes.add("user.users")
@@ -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 self.get_current_timezone().localize(value).astimezone(timezone.get_default_timezone()).replace(tzinfo=None) if value else None
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 pytz.timezone(timezone.get_default_timezone_name()).localize(value).astimezone(self.get_current_timezone()).replace(tzinfo=None) if value else None
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
- # print(json.dumps(args[0], indent=1, ensure_ascii=False))
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
- return Response(message="Ação realizada com sucesso")
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
- new_doc.write_pdf(buffer, base_url=base_url, stylesheets=[])
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
- return Response(self.form._message, self.form._redirect, dispose=self.form._dispose)
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 = Serializer(request=self.request)
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 "check"
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("Acesso Rápido", boxes)
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
- timezone_instance = TimeZone.objects.get_or_create(name=current_timezone.__str__())[0]
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.key = current_timezone.__str__()
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
- pass
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
- pass
21
+ class Meta:
22
+ icon = "user-pen"
21
23
 
22
24
 
23
25
  class Delete(endpoints.DeleteEndpoint[User]):
24
- pass
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
- return self.field_value(field_name, self.form.request.GET.get(field_name, default))
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)