slthcore 0.6.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. slthcore-0.6.5/MANIFEST.in +4 -0
  2. slthcore-0.6.5/PKG-INFO +16 -0
  3. slthcore-0.6.5/setup.cfg +4 -0
  4. slthcore-0.6.5/setup.py +27 -0
  5. slthcore-0.6.5/slth/__init__.py +270 -0
  6. slthcore-0.6.5/slth/application.py +252 -0
  7. slthcore-0.6.5/slth/apps.py +44 -0
  8. slthcore-0.6.5/slth/cmd/configure/__main__.py +88 -0
  9. slthcore-0.6.5/slth/cmd/init/__main__.py +24 -0
  10. slthcore-0.6.5/slth/cmd/init/__pycache__/__main__.cpython-312.pyc +0 -0
  11. slthcore-0.6.5/slth/cmd/init/boilerplate/.DS_Store +0 -0
  12. slthcore-0.6.5/slth/cmd/init/boilerplate/.gitignore +9 -0
  13. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/api/__init__.py +28 -0
  14. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/api/asgi.py +16 -0
  15. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/api/endpoints/__init__.py +1 -0
  16. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/api/models.py +1 -0
  17. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/api/settings.py +175 -0
  18. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/api/tests.py +8 -0
  19. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/api/urls.py +14 -0
  20. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/api/wsgi.py +16 -0
  21. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/entrypoint.sh +3 -0
  22. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/manage.py +22 -0
  23. slthcore-0.6.5/slth/cmd/init/boilerplate/backend/requirements.txt +2 -0
  24. slthcore-0.6.5/slth/cmd/init/boilerplate/base.env +15 -0
  25. slthcore-0.6.5/slth/cmd/init/boilerplate/docker-compose.yml +64 -0
  26. slthcore-0.6.5/slth/cmd/init/boilerplate/frontend/package.json +22 -0
  27. slthcore-0.6.5/slth/cmd/init/boilerplate/frontend/src/main.jsx +11 -0
  28. slthcore-0.6.5/slth/cmd/init/boilerplate/frontend/vite.config.js +22 -0
  29. slthcore-0.6.5/slth/cmd/init/boilerplate/local.env +3 -0
  30. slthcore-0.6.5/slth/cmd/init/boilerplate/run.sh +3 -0
  31. slthcore-0.6.5/slth/cmd/init/boilerplate/selenium/run.sh +2 -0
  32. slthcore-0.6.5/slth/cmd/init/boilerplate/test.sh +4 -0
  33. slthcore-0.6.5/slth/components.py +484 -0
  34. slthcore-0.6.5/slth/db/__init__.py +3 -0
  35. slthcore-0.6.5/slth/db/generic.py +98 -0
  36. slthcore-0.6.5/slth/db/models.py +179 -0
  37. slthcore-0.6.5/slth/endpoints/__init__.py +671 -0
  38. slthcore-0.6.5/slth/endpoints/auth.py +90 -0
  39. slthcore-0.6.5/slth/endpoints/deletion.py +21 -0
  40. slthcore-0.6.5/slth/endpoints/dev.py +18 -0
  41. slthcore-0.6.5/slth/endpoints/email.py +34 -0
  42. slthcore-0.6.5/slth/endpoints/job.py +22 -0
  43. slthcore-0.6.5/slth/endpoints/log.py +7 -0
  44. slthcore-0.6.5/slth/endpoints/profile.py +37 -0
  45. slthcore-0.6.5/slth/endpoints/pushsubscription.py +48 -0
  46. slthcore-0.6.5/slth/endpoints/report.py +16 -0
  47. slthcore-0.6.5/slth/endpoints/role.py +18 -0
  48. slthcore-0.6.5/slth/endpoints/settings.py +60 -0
  49. slthcore-0.6.5/slth/endpoints/task.py +12 -0
  50. slthcore-0.6.5/slth/endpoints/timezone.py +11 -0
  51. slthcore-0.6.5/slth/endpoints/user.py +63 -0
  52. slthcore-0.6.5/slth/endpoints/whatsappnotification.py +52 -0
  53. slthcore-0.6.5/slth/exceptions.py +11 -0
  54. slthcore-0.6.5/slth/factory.py +146 -0
  55. slthcore-0.6.5/slth/forms.py +791 -0
  56. slthcore-0.6.5/slth/geo/__init__.py +16 -0
  57. slthcore-0.6.5/slth/integrations/__init__.py +0 -0
  58. slthcore-0.6.5/slth/integrations/deepseek/__init__.py +36 -0
  59. slthcore-0.6.5/slth/integrations/google/__init__.py +0 -0
  60. slthcore-0.6.5/slth/integrations/google/gemini.py +23 -0
  61. slthcore-0.6.5/slth/integrations/google/places.py +41 -0
  62. slthcore-0.6.5/slth/integrations/google/vision.py +18 -0
  63. slthcore-0.6.5/slth/integrations/justvoip/__init__.py +15 -0
  64. slthcore-0.6.5/slth/integrations/mercadopago/__init__.py +48 -0
  65. slthcore-0.6.5/slth/integrations/openai/__init__.py +0 -0
  66. slthcore-0.6.5/slth/integrations/openai/chatgpt.py +30 -0
  67. slthcore-0.6.5/slth/integrations/viacep/__init__.py +6 -0
  68. slthcore-0.6.5/slth/integrations/whatsapp/__init__.py +9 -0
  69. slthcore-0.6.5/slth/management/__init__.py +0 -0
  70. slthcore-0.6.5/slth/management/commands/__init__.py +0 -0
  71. slthcore-0.6.5/slth/management/commands/api.py +90 -0
  72. slthcore-0.6.5/slth/management/commands/integration_test.py +27 -0
  73. slthcore-0.6.5/slth/management/commands/sync.py +38 -0
  74. slthcore-0.6.5/slth/management/commands/worker.py +28 -0
  75. slthcore-0.6.5/slth/middleware/__init__.py +0 -0
  76. slthcore-0.6.5/slth/middleware/timezone.py +15 -0
  77. slthcore-0.6.5/slth/migrations/0001_initial.py +41 -0
  78. slthcore-0.6.5/slth/migrations/0002_email_role_pushsubscription_error.py +78 -0
  79. slthcore-0.6.5/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +23 -0
  80. slthcore-0.6.5/slth/migrations/0004_alter_profile_photo.py +18 -0
  81. slthcore-0.6.5/slth/migrations/0005_alter_profile_photo.py +19 -0
  82. slthcore-0.6.5/slth/migrations/0006_user.py +33 -0
  83. slthcore-0.6.5/slth/migrations/0007_deletion_log.py +49 -0
  84. slthcore-0.6.5/slth/migrations/0008_alter_deletion_datetime_alter_log_datetime.py +24 -0
  85. slthcore-0.6.5/slth/migrations/0009_remove_email_from_email_email_action_email_attempt_and_more.py +43 -0
  86. slthcore-0.6.5/slth/migrations/0010_email_key_alter_email_action_alter_email_attempt_and_more.py +103 -0
  87. slthcore-0.6.5/slth/migrations/0011_usertimezone.py +31 -0
  88. slthcore-0.6.5/slth/migrations/0012_timezone_remove_usertimezone_key_and_more.py +37 -0
  89. slthcore-0.6.5/slth/migrations/0013_whatsappnotification.py +35 -0
  90. slthcore-0.6.5/slth/migrations/0014_settings.py +40 -0
  91. slthcore-0.6.5/slth/migrations/0015_auto_20250412_1126.py +27 -0
  92. slthcore-0.6.5/slth/migrations/__init__.py +0 -0
  93. slthcore-0.6.5/slth/models.py +672 -0
  94. slthcore-0.6.5/slth/notifications.py +29 -0
  95. slthcore-0.6.5/slth/oauth.py +62 -0
  96. slthcore-0.6.5/slth/pdf/__init__.py +182 -0
  97. slthcore-0.6.5/slth/pdf/tests.py +70 -0
  98. slthcore-0.6.5/slth/permissions.py +56 -0
  99. slthcore-0.6.5/slth/printer.py +71 -0
  100. slthcore-0.6.5/slth/queryset.py +570 -0
  101. slthcore-0.6.5/slth/roles.py +103 -0
  102. slthcore-0.6.5/slth/selenium/__init__.py +244 -0
  103. slthcore-0.6.5/slth/selenium/browser.py +300 -0
  104. slthcore-0.6.5/slth/serializer.py +434 -0
  105. slthcore-0.6.5/slth/static/.DS_Store +0 -0
  106. slthcore-0.6.5/slth/static/css/.DS_Store +0 -0
  107. slthcore-0.6.5/slth/static/css/slth.css +98 -0
  108. slthcore-0.6.5/slth/static/images/placeholder.png +0 -0
  109. slthcore-0.6.5/slth/static/js/index.min.js +1 -0
  110. slthcore-0.6.5/slth/static/js/react.min.js +40 -0
  111. slthcore-0.6.5/slth/static/js/slth.min.js +270 -0
  112. slthcore-0.6.5/slth/statistics.py +191 -0
  113. slthcore-0.6.5/slth/storage/__init__.py +99 -0
  114. slthcore-0.6.5/slth/storage/s3.py +383 -0
  115. slthcore-0.6.5/slth/tasks.py +59 -0
  116. slthcore-0.6.5/slth/templates/email.html +155 -0
  117. slthcore-0.6.5/slth/templates/index.html +76 -0
  118. slthcore-0.6.5/slth/templates/report.html +23 -0
  119. slthcore-0.6.5/slth/templates/service-worker.js +39 -0
  120. slthcore-0.6.5/slth/templates/signature.html +45 -0
  121. slthcore-0.6.5/slth/tests.py +179 -0
  122. slthcore-0.6.5/slth/threadlocal.py +3 -0
  123. slthcore-0.6.5/slth/timezone.py +33 -0
  124. slthcore-0.6.5/slth/urls.py +31 -0
  125. slthcore-0.6.5/slth/utils.py +46 -0
  126. slthcore-0.6.5/slth/views.py +86 -0
  127. slthcore-0.6.5/slthcore.egg-info/PKG-INFO +16 -0
  128. slthcore-0.6.5/slthcore.egg-info/SOURCES.txt +128 -0
  129. slthcore-0.6.5/slthcore.egg-info/dependency_links.txt +1 -0
  130. slthcore-0.6.5/slthcore.egg-info/top_level.txt +1 -0
@@ -0,0 +1,4 @@
1
+ include requirements.txt
2
+ recursive-include slth/static *.*
3
+ recursive-include slth/templates *.*
4
+ recursive-include slth/cmd *.*
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.1
2
+ Name: slthcore
3
+ Version: 0.6.5
4
+ Summary: API generator based on yml file
5
+ Home-page: https://github.com/brenokcc
6
+ Author: Breno Silva
7
+ Author-email: brenokcc@yahoo.com.br
8
+ License: BSD License
9
+ Classifier: Environment :: Web Environment
10
+ Classifier: Framework :: Django
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: BSD License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Topic :: Internet :: WWW/HTTP
16
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,27 @@
1
+ from setuptools import find_packages, setup
2
+
3
+ install_requires = []
4
+
5
+ setup(
6
+ name='slthcore',
7
+ version='0.6.5',
8
+ packages=find_packages(),
9
+ install_requires=install_requires,
10
+ include_package_data=True,
11
+ license='BSD License',
12
+ description='API generator based on yml file',
13
+ long_description='',
14
+ url='https://github.com/brenokcc',
15
+ author='Breno Silva',
16
+ author_email='brenokcc@yahoo.com.br',
17
+ classifiers=[
18
+ 'Environment :: Web Environment',
19
+ 'Framework :: Django',
20
+ 'Intended Audience :: Developers',
21
+ 'License :: OSI Approved :: BSD License',
22
+ 'Operating System :: OS Independent',
23
+ 'Programming Language :: Python',
24
+ 'Topic :: Internet :: WWW/HTTP',
25
+ 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
26
+ ],
27
+ )
@@ -0,0 +1,270 @@
1
+ import json
2
+ import warnings
3
+ from pathlib import Path
4
+ from datetime import datetime, date
5
+ from django.db import models
6
+ from .queryset import QuerySet
7
+ from django.db.models import manager
8
+ from .serializer import Serializer, serialize
9
+ from django.db.models.deletion import Collector
10
+ from .factory import FormFactory
11
+ import django.db.models.options as options
12
+ from django.db.models.base import ModelBase
13
+ from django.db.models import Model
14
+ from django.core.exceptions import FieldDoesNotExist
15
+ from django.core.exceptions import ObjectDoesNotExist
16
+ from django.utils.autoreload import autoreload_started
17
+ from django.core import serializers
18
+ from django.utils import autoreload
19
+ from .threadlocal import tl
20
+ from django.apps import apps as django_apps
21
+
22
+ from decimal import Decimal
23
+
24
+ warnings.filterwarnings('ignore', module='urllib3')
25
+
26
+ FILENAME = 'application.yml'
27
+ ENDPOINTS = {}
28
+ PROXIED_MODELS = []
29
+ THREADS = []
30
+
31
+ class JSONEncoder(json.JSONEncoder):
32
+ def default(self, obj):
33
+ if isinstance(obj, Decimal):
34
+ return dict(type='decimal', value=float(obj))
35
+ elif isinstance(obj, datetime):
36
+ return dict(type='datetime', value=obj.isoformat())
37
+ elif isinstance(obj, date):
38
+ return dict(type='date', value=obj.isoformat())
39
+ elif isinstance(obj, QuerySet):
40
+ return dict(type='queryset', value=obj.dump())
41
+ elif isinstance(obj, Model):
42
+ return dict(type='object', model=obj._meta.label.lower(), pk=obj.pk)
43
+ return json.JSONEncoder.default(self, obj)
44
+
45
+
46
+ class JSONDecoder(json.JSONDecoder):
47
+
48
+ def __init__(self, *args, **kwargs):
49
+ super().__init__(object_hook=self.object_hook, *args, **kwargs)
50
+
51
+ def object_hook(self, obj):
52
+ type = obj.get('type')
53
+ if type:
54
+ if type == 'decimal':
55
+ return Decimal(obj['value'])
56
+ elif type == 'datetime':
57
+ return datetime.fromisoformat(obj['value'])
58
+ elif type == 'date':
59
+ return date.fromisoformat(obj['value'])
60
+ elif type == 'queryset':
61
+ return QuerySet.load(obj['value'])
62
+ elif type == 'object':
63
+ return django_apps.get_model(obj['model']).objects.get(pk=obj['pk'])
64
+ return obj
65
+
66
+
67
+ def dumps(data, indent=1, ensure_ascii=False):
68
+ return json.dumps(data, indent=indent, ensure_ascii=ensure_ascii, cls=JSONEncoder)
69
+
70
+
71
+ def loads(data):
72
+ return json.loads(data, cls=JSONDecoder)
73
+
74
+
75
+ class BaseManager(manager.BaseManager):
76
+ def get_queryset(self):
77
+ return super().get_queryset()
78
+
79
+ def all(self):
80
+ return self.get_queryset().all()
81
+
82
+ def __call__(self, model):
83
+ return django_apps.get_model(model)
84
+
85
+
86
+ class Manager(BaseManager.from_queryset(QuerySet)):
87
+ pass
88
+
89
+
90
+ models.QuerySet = QuerySet
91
+ models.Manager = Manager
92
+ setattr(options, 'DEFAULT_NAMES', options.DEFAULT_NAMES + ('icon', 'search_fields'))
93
+
94
+
95
+
96
+ class ModelMixin(object):
97
+
98
+ @classmethod
99
+ def get_field(cls, lookup):
100
+ model = cls
101
+ attrs = lookup.split('__')
102
+ while attrs:
103
+ attr_name = attrs.pop(0)
104
+ if attrs: # go deeper
105
+ field = model._meta.get_field(attr_name)
106
+ model = field.related_model
107
+ else:
108
+ try:
109
+ return model._meta.get_field(attr_name)
110
+ except FieldDoesNotExist:
111
+ pass
112
+ return None
113
+
114
+ def getroles(self, username_lookup='username'):
115
+ roles = getattr(self, '_roles', None)
116
+ if roles is None:
117
+ obj = self
118
+ for attr_name in username_lookup.split('__'):
119
+ obj = getattr(obj, attr_name)
120
+ roles = django_apps.get_model('api.role').objects.filter(username=obj)
121
+ setattr(self, '_roles', roles)
122
+ return roles
123
+
124
+ def getuser(self, username_lookup):
125
+ obj = self
126
+ for attr_name in username_lookup.split('__'):
127
+ obj = getattr(obj, attr_name)
128
+ return django_apps.get_model('auth.user').objects.get(username=obj)
129
+
130
+ def serializer(self) -> Serializer:
131
+ return Serializer(self)
132
+
133
+ def formfactory(self) -> FormFactory:
134
+ return FormFactory(self)
135
+
136
+ def pre_save(self):
137
+ pass
138
+
139
+ def post_save(self):
140
+ pass
141
+
142
+ def safe_delete(self, username=None):
143
+ from .models import Deletion
144
+ order = []
145
+ objects = []
146
+ collector = Collector('default')
147
+ qs = type(self).objects.filter(pk=self.pk)
148
+ collector.collect(qs)
149
+ for instances in collector.data.values():
150
+ for instance in instances:
151
+ if instance.__class__.__name__ not in ['Log'] and instance not in objects:
152
+ objects.append(instance)
153
+ if instance.__class__.__name__ not in order:
154
+ order.append(instance.__class__.__name__)
155
+ for qs in collector.fast_deletes:
156
+ for instance in qs:
157
+ if instance.__class__.__name__ not in ['Log'] and instance not in objects:
158
+ objects.append(instance)
159
+ if instance.__class__.__name__ not in order:
160
+ order.append(instance.__class__.__name__)
161
+ backup = dumps(dict(order=order, objects=serializers.serialize("python", objects)))
162
+ instance = '{}.{}:{}'.format(self._meta.app_label, self._meta.model_name, self.pk)
163
+ Deletion.objects.create(username=username, datetime=datetime.now(), instance=instance, backup=backup)
164
+ return self.delete()
165
+
166
+
167
+ def save_decorator(func):
168
+
169
+ def decorate(self, *args, **kwargs):
170
+ diff = {}
171
+ if self.pk:
172
+ action = 'edit'
173
+ obj = type(self).objects.filter(pk=self.pk).first()
174
+ if obj:
175
+ for field in self._meta.fields:
176
+ a = getattr(obj, field.name)
177
+ try:
178
+ b = getattr(self, field.name)
179
+ except ObjectDoesNotExist:
180
+ b = None
181
+ if a != b:
182
+ diff[field.verbose_name] = (serialize(a), serialize(b))
183
+ else:
184
+ action = 'add'
185
+ for field in self._meta.fields:
186
+ try:
187
+ b = getattr(self, field.name)
188
+ except ObjectDoesNotExist:
189
+ b = None
190
+ if b is not None:
191
+ diff[field.verbose_name] = (None, serialize(b))
192
+ func(self, *args, **kwargs)
193
+ if diff:
194
+ model = '{}.{}'.format(self._meta.app_label, self._meta.model_name)
195
+ log = dict(model=model, pk=self.pk, action=action, diff=diff)
196
+ context = getattr(tl, 'context', None)
197
+ if context:
198
+ context['logs'].append(log)
199
+
200
+ return decorate
201
+
202
+
203
+ def delete_decorator(func):
204
+ def decorate(self, *args, **kwargs):
205
+ diff = {}
206
+ for field in self._meta.fields:
207
+ a = getattr(self, field.name)
208
+ diff[field.verbose_name] = (a, None)
209
+ log = dict(model='{}.{}'.format(
210
+ self._meta.app_label, self._meta.model_name), action='delete', diff=diff
211
+ )
212
+ context = getattr(tl, 'context', None)
213
+ if context:
214
+ context['logs'].append(log)
215
+ func(self, *args, **kwargs)
216
+
217
+ return decorate
218
+
219
+
220
+ ___new___ = ModelBase.__new__
221
+
222
+
223
+ def __new__(mcs, name, bases, attrs, **kwargs):
224
+ if attrs['__module__'] != '__fake__':
225
+ # See .db.models.Manager
226
+ if 'objects' in attrs and isinstance(attrs['objects'], QuerySet):
227
+ queryset_class = attrs['objects']
228
+ attrs.update(objects=BaseManager.from_queryset(type(queryset_class))())
229
+ # Defining the objects Manager using .db.models.QuerySet
230
+ if 'objects' not in attrs and not all(['objects' in dir(cls) for cls in bases]):
231
+ attrs.update(objects=BaseManager.from_queryset(QuerySet)())
232
+
233
+ if ModelMixin not in bases:
234
+ bases = bases + (ModelMixin, )
235
+ cls = ___new___(mcs, name, bases, attrs, **kwargs)
236
+ if cls._meta.proxy_for_model:
237
+ PROXIED_MODELS.append(cls._meta.proxy_for_model)
238
+ if name == 'Model':
239
+ cls.save = save_decorator(cls.save)
240
+ cls.delete = delete_decorator(cls.delete)
241
+ return cls
242
+
243
+
244
+ ModelBase.__new__ = __new__
245
+
246
+
247
+ def api_watchdog(sender, **kwargs):
248
+ sender.extra_files.add(Path('application.yml'))
249
+
250
+ autoreload_started.connect(api_watchdog)
251
+
252
+
253
+ def meta(verbose_name=None):
254
+ def decorate(function):
255
+ function.verbose_name = verbose_name
256
+ return function
257
+ return decorate
258
+
259
+
260
+ original_trigger_reload = autoreload.trigger_reload
261
+
262
+ def trigger_reload(filename):
263
+ print('Stoping sloth thread....')
264
+ for thread in THREADS:
265
+ thread.stop()
266
+ thread.join()
267
+ print('Thread stopped!')
268
+ original_trigger_reload(filename)
269
+
270
+ autoreload.trigger_reload = trigger_reload
@@ -0,0 +1,252 @@
1
+
2
+ from slth import ENDPOINTS
3
+ from .utils import build_url
4
+ from django.conf import settings
5
+ from django.apps import apps
6
+
7
+ APPLICATION_CLASS = None
8
+
9
+
10
+ class Style():
11
+ def __init__(self, name, color="black", background="inherite", border="none"):
12
+ self.name = name
13
+ self.update(color=color, background=background, border=border)
14
+
15
+ def update(self, color=None, background=None, border=None):
16
+ if color:
17
+ self.color = color
18
+ if background:
19
+ self.background = background
20
+ if border:
21
+ self.border = border
22
+
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):
54
+ def __init__(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")
87
+
88
+
89
+ class Groups(dict):
90
+ def add(self, **kwargs):
91
+ self.update(**kwargs)
92
+
93
+ class Menu(dict):
94
+ def add(self, arg):
95
+ self.update(arg)
96
+
97
+ def process(self, request):
98
+ items = []
99
+
100
+ def get_item(k, v):
101
+ if isinstance(v, dict):
102
+ icon, label = k.split(":") if ":" in k else (None, k)
103
+ subitems = []
104
+ for k1, v1 in v.items():
105
+ subitem = get_item(k1, v1)
106
+ if subitem:
107
+ subitems.append(subitem)
108
+ if subitems:
109
+ return dict(dict(icon=icon, label=label, items=subitems))
110
+ else:
111
+ cls = ENDPOINTS.get(v)
112
+ if cls:
113
+ endpoint = cls().instantiate(request, None)
114
+ if endpoint.check_permission() and endpoint.contribute("menu"):
115
+ icon, label = k.split(":") if ":" in k else (None, k)
116
+ url = build_url(request, cls.get_api_url())
117
+ return dict(dict(label=label, url=url, icon=icon))
118
+
119
+ for k, v in self.items():
120
+ item = get_item(k, v)
121
+ if item:
122
+ items.append(item)
123
+
124
+ return items
125
+
126
+ class Oauth(list):
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))
129
+
130
+ def serialize(self):
131
+ data = []
132
+ for provider in self:
133
+ redirect_uri = "{}{}".format(settings.SITE_URL, provider['redirect_uri'])
134
+ authorize_url = '{}?response_type=code&client_id={}&redirect_uri={}'.format(
135
+ provider['authorize_url'], provider['client_id'], redirect_uri
136
+ )
137
+ if provider.get('scope'):
138
+ authorize_url = '{}&scope={}'.format(authorize_url, provider.get('scope'))
139
+ data.append(dict(label=f'Entrar com {provider["name"]}', url=authorize_url))
140
+ return data
141
+
142
+ class List(list):
143
+
144
+ def add(self, *names):
145
+ super().extend(names)
146
+
147
+
148
+ class Dashboard():
149
+ def __init__(self):
150
+ self.actions:List = List()
151
+ self.toolbar:List = List()
152
+ self.todo:List = List()
153
+ self.top:List = List()
154
+ self.center:List = List()
155
+ self.boxes:List = List()
156
+ self.search:List = List()
157
+ self.usermenu:List = List()
158
+ self.adder:List = List()
159
+ self.tools:List = List()
160
+ self.settings:List = List()
161
+ self.index = "dashboard"
162
+
163
+
164
+ class Theme:
165
+ def __init__(self):
166
+ self.light = Light()
167
+ self.dark = Dark()
168
+
169
+
170
+ class ApplicationMetaclass(type):
171
+
172
+ def __new__(mcs, name, bases, attrs):
173
+ global APPLICATION_CLASS
174
+ cls = super().__new__(mcs, name, bases, attrs)
175
+ APPLICATION_CLASS = cls
176
+ return cls
177
+
178
+
179
+ class Application(metaclass=ApplicationMetaclass):
180
+
181
+ def __init__(self):
182
+ self.lang = "pt-br"
183
+ self.title = "Sloth"
184
+ self.subtitle = "Take your time!"
185
+ self.icon = "/static/images/logo.png"
186
+ self.logo = "/static/images/logo.png"
187
+ self.brand = None
188
+ self.version = "0.0.1"
189
+ self.oauth:Oauth = Oauth()
190
+ self.groups:Groups = Groups()
191
+ self.menu:Menu = Menu()
192
+ self.dashboard = Dashboard()
193
+ self.theme:Theme = Theme()
194
+ self.sponsors:List = List()
195
+
196
+ def load(self):
197
+ pass
198
+
199
+ def serialize(self, request):
200
+ icon = build_url(request, self.icon)
201
+ logo = build_url(request, self.brand or self.logo)
202
+ title = self.title if self.brand is None else None
203
+ if request.user.is_authenticated:
204
+ name = request.user.first_name
205
+ user = request.user.username.split()[0].split("@")[0]
206
+ profile = apps.get_model("slth", "profile").objects.filter(user=request.user).first()
207
+ photo = profile and profile.photo and build_url(request, profile.photo.url) or None
208
+ else:
209
+ name = user = profile = photo = None
210
+
211
+ def contribute(endpoint_list, endpoint_name):
212
+ cls = ENDPOINTS[endpoint_name]
213
+ endpoint = cls().instantiate(request, None)
214
+ if endpoint.check_permission() and endpoint.contribute(entrypoint):
215
+ name = endpoint.get_verbose_name()
216
+ url = build_url(request, cls.get_api_url())
217
+ modal = cls.get_metadata("modal", False)
218
+ icon = cls.get_metadata("icon", None)
219
+ endpoint_list.append(dict(name=name, url=url, modal=modal, icon=icon))
220
+
221
+ endpoints = {"actions": [], "usermenu": [], "adder": [], "settings": [], "tools": [], "toolbar": []}
222
+ for entrypoint in endpoints:
223
+ for endpoint_name in getattr(self.dashboard, entrypoint):
224
+ contribute(endpoints[entrypoint], endpoint_name)
225
+
226
+ top = []
227
+ for endpoint_name in self.dashboard.top:
228
+ contribute(top, endpoint_name)
229
+
230
+ return dict(
231
+ type="application",
232
+ icon=icon,
233
+ navbar=dict(
234
+ type="navbar", title=title, subtitle=self.subtitle, logo=logo, user=user, name=name, **endpoints
235
+ ),
236
+ menu=dict(
237
+ type="menu", items=self.menu.process(request), user=user, image=photo
238
+ ),
239
+ footer=dict(
240
+ type="footer", version=self.version
241
+ ),
242
+ top=top,
243
+ oauth=self.oauth.serialize(),
244
+ sponsors=self.sponsors
245
+ )
246
+
247
+ @staticmethod
248
+ def get_instance():
249
+ instance = APPLICATION_CLASS()
250
+ instance.load()
251
+ return instance
252
+
@@ -0,0 +1,44 @@
1
+ import os
2
+ import time
3
+ from datetime import datetime
4
+ from django.conf import settings
5
+ from django.apps import AppConfig
6
+ from django.utils import autoreload
7
+ from django.apps import apps
8
+ import threading
9
+ from . import THREADS
10
+
11
+
12
+
13
+
14
+ class Thread(threading.Thread):
15
+ def __init__(self):
16
+ super().__init__()
17
+ self._stop_event = threading.Event()
18
+
19
+ def run(self):
20
+ while not self._stop_event.is_set():
21
+ # print('.')
22
+ apps.get_model("slth", "email").objects.send()
23
+ apps.get_model("slth", "pushnotification").objects.send()
24
+ apps.get_model("slth", "job").objects.execute()
25
+ for i in range(0, 10):
26
+ if self._stop_event.is_set():
27
+ break
28
+ time.sleep(1)
29
+
30
+ def stop(self):
31
+ print("Stopping e-mail thread...")
32
+ self._stop_event.set()
33
+
34
+
35
+ class AppConfig(AppConfig):
36
+ name = 'slth'
37
+
38
+ def ready(self):
39
+ settings.SLOTH = True
40
+ if os.environ.get("RUN_MAIN"):
41
+ print('Starting sloth thread...')
42
+ thread = Thread()
43
+ thread.start()
44
+ THREADS.append(thread)