slthcore 0.0.4__tar.gz → 0.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of slthcore might be problematic. Click here for more details.

Files changed (78) hide show
  1. {slthcore-0.0.4/slthcore.egg-info → slthcore-0.0.6}/PKG-INFO +1 -1
  2. {slthcore-0.0.4 → slthcore-0.0.6}/setup.py +1 -1
  3. {slthcore-0.0.4 → slthcore-0.0.6}/slth/__init__.py +86 -1
  4. slthcore-0.0.6/slth/cmd/configure/__main__.py +88 -0
  5. {slthcore-0.0.4 → slthcore-0.0.6}/slth/components.py +3 -1
  6. slthcore-0.0.6/slth/db/generic.py +98 -0
  7. {slthcore-0.0.4 → slthcore-0.0.6}/slth/db/models.py +15 -0
  8. {slthcore-0.0.4 → slthcore-0.0.6}/slth/endpoints.py +165 -44
  9. {slthcore-0.0.4 → slthcore-0.0.6}/slth/factory.py +1 -1
  10. {slthcore-0.0.4 → slthcore-0.0.6}/slth/forms.py +8 -5
  11. slthcore-0.0.6/slth/migrations/0007_deletion_log.py +49 -0
  12. {slthcore-0.0.4 → slthcore-0.0.6}/slth/models.py +124 -1
  13. {slthcore-0.0.4 → slthcore-0.0.6}/slth/notifications.py +1 -0
  14. {slthcore-0.0.4 → slthcore-0.0.6}/slth/queryset.py +35 -25
  15. {slthcore-0.0.4 → slthcore-0.0.6}/slth/serializer.py +10 -4
  16. {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/js/slth.min.js +52 -14
  17. slthcore-0.0.6/slth/tasks.py +70 -0
  18. {slthcore-0.0.4 → slthcore-0.0.6}/slth/templates/index.html +2 -0
  19. {slthcore-0.0.4 → slthcore-0.0.6}/slth/tests.py +4 -2
  20. slthcore-0.0.6/slth/threadlocal.py +3 -0
  21. {slthcore-0.0.4 → slthcore-0.0.6}/slth/utils.py +7 -1
  22. {slthcore-0.0.4 → slthcore-0.0.6}/slth/views.py +12 -3
  23. {slthcore-0.0.4 → slthcore-0.0.6/slthcore.egg-info}/PKG-INFO +1 -1
  24. {slthcore-0.0.4 → slthcore-0.0.6}/slthcore.egg-info/SOURCES.txt +5 -0
  25. {slthcore-0.0.4 → slthcore-0.0.6}/MANIFEST.in +0 -0
  26. {slthcore-0.0.4 → slthcore-0.0.6}/setup.cfg +0 -0
  27. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/__main__.py +0 -0
  28. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/.DS_Store +0 -0
  29. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/.gitignore +0 -0
  30. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/__init__.py +0 -0
  31. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/asgi.py +0 -0
  32. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/endpoints.py +0 -0
  33. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/models.py +0 -0
  34. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/settings.py +0 -0
  35. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/tests.py +0 -0
  36. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/urls.py +0 -0
  37. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/wsgi.py +0 -0
  38. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/application.yml +0 -0
  39. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/entrypoint.sh +0 -0
  40. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/manage.py +0 -0
  41. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/requirements.txt +0 -0
  42. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/base.env +0 -0
  43. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/docker-compose.yml +0 -0
  44. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/frontend/package.json +0 -0
  45. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/frontend/src/main.jsx +0 -0
  46. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/frontend/vite.config.js +0 -0
  47. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/local.env +0 -0
  48. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/run.sh +0 -0
  49. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/selenium/run.sh +0 -0
  50. {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/test.sh +0 -0
  51. {slthcore-0.0.4 → slthcore-0.0.6}/slth/db/__init__.py +0 -0
  52. {slthcore-0.0.4 → slthcore-0.0.6}/slth/exceptions.py +0 -0
  53. {slthcore-0.0.4 → slthcore-0.0.6}/slth/management/__init__.py +0 -0
  54. {slthcore-0.0.4 → slthcore-0.0.6}/slth/management/commands/__init__.py +0 -0
  55. {slthcore-0.0.4 → slthcore-0.0.6}/slth/management/commands/integration_test.py +0 -0
  56. {slthcore-0.0.4 → slthcore-0.0.6}/slth/management/commands/sync.py +0 -0
  57. {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0001_initial.py +0 -0
  58. {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0002_email_role_pushsubscription_error.py +0 -0
  59. {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +0 -0
  60. {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0004_alter_profile_photo.py +0 -0
  61. {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0005_alter_profile_photo.py +0 -0
  62. {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0006_user.py +0 -0
  63. {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/__init__.py +0 -0
  64. {slthcore-0.0.4 → slthcore-0.0.6}/slth/oauth.py +0 -0
  65. {slthcore-0.0.4 → slthcore-0.0.6}/slth/permissions.py +0 -0
  66. {slthcore-0.0.4 → slthcore-0.0.6}/slth/roles.py +0 -0
  67. {slthcore-0.0.4 → slthcore-0.0.6}/slth/selenium/__init__.py +0 -0
  68. {slthcore-0.0.4 → slthcore-0.0.6}/slth/selenium/browser.py +0 -0
  69. {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/.DS_Store +0 -0
  70. {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/css/.DS_Store +0 -0
  71. {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/css/slth.css +0 -0
  72. {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/js/index.min.js +0 -0
  73. {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/js/react.min.js +0 -0
  74. {slthcore-0.0.4 → slthcore-0.0.6}/slth/statistics.py +0 -0
  75. {slthcore-0.0.4 → slthcore-0.0.6}/slth/templates/service-worker.js +0 -0
  76. {slthcore-0.0.4 → slthcore-0.0.6}/slth/urls.py +0 -0
  77. {slthcore-0.0.4 → slthcore-0.0.6}/slthcore.egg-info/dependency_links.txt +0 -0
  78. {slthcore-0.0.4 → slthcore-0.0.6}/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.0.4
3
+ Version: 0.0.6
4
4
  Summary: API generator based on yml file
5
5
  Home-page: https://github.com/brenokcc
6
6
  Author: Breno Silva
@@ -5,7 +5,7 @@ install_requires = []
5
5
 
6
6
  setup(
7
7
  name='slthcore',
8
- version='0.0.4',
8
+ version='0.0.6',
9
9
  packages=find_packages(),
10
10
  install_requires=install_requires,
11
11
  include_package_data=True,
@@ -1,18 +1,23 @@
1
1
  import re
2
2
  import os
3
3
  import yaml
4
+ import json
4
5
  import warnings
5
6
  from pathlib import Path
7
+ from datetime import datetime
6
8
  from django.apps import apps
7
9
  from django.db import models
8
10
  from .queryset import QuerySet
9
11
  from django.db.models import manager
10
- from .serializer import Serializer
12
+ from .serializer import Serializer, serialize
13
+ from django.db.models.deletion import Collector
11
14
  from .factory import FormFactory
12
15
  import django.db.models.options as options
13
16
  from django.db.models.base import ModelBase
14
17
  from django.core.exceptions import FieldDoesNotExist
15
18
  from django.utils.autoreload import autoreload_started
19
+ from django.core import serializers
20
+ from .threadlocal import tl
16
21
 
17
22
  warnings.filterwarnings('ignore', module='urllib3')
18
23
 
@@ -89,6 +94,83 @@ class ModelMixin(object):
89
94
 
90
95
  def formfactory(self) -> FormFactory:
91
96
  return FormFactory(self)
97
+
98
+ def pre_save(self):
99
+ pass
100
+
101
+ def post_save(self):
102
+ pass
103
+
104
+ def safe_delete(self, username=None):
105
+ from .models import Deletion
106
+ order = []
107
+ objects = []
108
+ collector = Collector('default')
109
+ qs = type(self).objects.filter(pk=self.pk)
110
+ collector.collect(qs)
111
+ for instances in collector.data.values():
112
+ for instance in instances:
113
+ if instance.__class__.__name__ not in ['Log'] and instance not in objects:
114
+ objects.append(instance)
115
+ if instance.__class__.__name__ not in order:
116
+ order.append(instance.__class__.__name__)
117
+ for qs in collector.fast_deletes:
118
+ for instance in qs:
119
+ if instance.__class__.__name__ not in ['Log'] and instance not in objects:
120
+ objects.append(instance)
121
+ if instance.__class__.__name__ not in order:
122
+ order.append(instance.__class__.__name__)
123
+ backup = json.dumps(dict(order=order, objects=serializers.serialize("python", objects)))
124
+ instance = '{}.{}:{}'.format(self._meta.app_label, self._meta.model_name, self.pk)
125
+ Deletion.objects.create(username=username, datetime=datetime.now(), instance=instance, backup=backup)
126
+ return self.delete()
127
+
128
+
129
+ def save_decorator(func):
130
+
131
+ def decorate(self, *args, **kwargs):
132
+ diff = {}
133
+ if self.pk:
134
+ action = 'edit'
135
+ obj = type(self).objects.filter(pk=self.pk).first()
136
+ if obj:
137
+ for field in self._meta.fields:
138
+ a = getattr(obj, field.name)
139
+ b = getattr(self, field.name)
140
+ if a != b:
141
+ diff[field.verbose_name] = (serialize(a), serialize(b))
142
+ else:
143
+ action = 'add'
144
+ for field in self._meta.fields:
145
+ b = getattr(self, field.name)
146
+ if b is not None:
147
+ diff[field.verbose_name] = (None, serialize(b))
148
+ func(self, *args, **kwargs)
149
+ if diff:
150
+ model = '{}.{}'.format(self._meta.app_label, self._meta.model_name)
151
+ log = dict(model=model, pk=self.pk, action=action, diff=diff)
152
+ context = getattr(tl, 'context', None)
153
+ if context:
154
+ context['logs'].append(log)
155
+
156
+ return decorate
157
+
158
+
159
+ def delete_decorator(func):
160
+ def decorate(self, *args, **kwargs):
161
+ diff = {}
162
+ for field in self._meta.fields:
163
+ a = getattr(self, field.name)
164
+ diff[field.verbose_name] = (a, None)
165
+ log = dict(model='{}.{}'.format(
166
+ self._meta.app_label, self._meta.model_name), action='delete', diff=diff
167
+ )
168
+ context = getattr(tl, 'context', None)
169
+ if context:
170
+ context['logs'].append(log)
171
+ func(self, *args, **kwargs)
172
+
173
+ return decorate
92
174
 
93
175
 
94
176
  ___new___ = ModelBase.__new__
@@ -109,6 +191,9 @@ def __new__(mcs, name, bases, attrs, **kwargs):
109
191
  cls = ___new___(mcs, name, bases, attrs, **kwargs)
110
192
  if cls._meta.proxy_for_model:
111
193
  PROXIED_MODELS.append(cls._meta.proxy_for_model)
194
+ if name == 'Model':
195
+ cls.save = save_decorator(cls.save)
196
+ cls.delete = delete_decorator(cls.delete)
112
197
  return cls
113
198
 
114
199
 
@@ -0,0 +1,88 @@
1
+ import os
2
+ import sys
3
+ import pathlib
4
+ from slth.utils import parse_string_template
5
+ from subprocess import Popen, PIPE
6
+
7
+
8
+ USAGE = 'USAGE: python -m slth.cmd.configure [nginx|systemctl]'
9
+
10
+ NGINX = '''server {
11
+ listen 80;
12
+ server_name {{ server_name }};
13
+ {% if ssl %}
14
+ listen 443 ssl;
15
+ ssl_certificate /etc/letsencrypt/live/{{ server_name }}/fullchain.pem;
16
+ ssl_certificate_key /etc/letsencrypt/live/{{ server_name }}/privkey.pem;
17
+ if ($scheme = http) { return 301 https://$server_name$request_uri; }
18
+ {% endif %}
19
+ location / {
20
+ proxy_pass http://127.0.0.1:{{ port }};
21
+ }
22
+ }'''
23
+
24
+ SERVICE_SCRIPT = '''#!/bin/bash
25
+ source .venv/bin/activate
26
+
27
+ export POSTGRES_HOST=localhost
28
+ export POSTGRES_DB={{ name }}
29
+
30
+ python manage.py sync
31
+ gunicorn api.wsgi -b 0.0.0.0:8000 -w 3 --log-level info --reload --timeout 3600
32
+ '''
33
+
34
+ SERVICE_CONTENT = '''[Unit]
35
+ Description={{ server_name }}
36
+
37
+ [Service]
38
+ User=www-data
39
+ WorkingDirectory={{ dirname }}
40
+ ExecStart=bash service.sh
41
+ Restart=always
42
+ RestartSec=3
43
+
44
+ [Install]
45
+ WantedBy=multi-user.target
46
+ '''
47
+
48
+ def execute(command):
49
+ process = Popen(command.split(), stdout=PIPE, stderr=PIPE)
50
+ stdout, stderr = process.communicate()
51
+ #print(stdout, stderr)
52
+
53
+ def print_content(file_path, content):
54
+ print()
55
+ print(file_path)
56
+ print('--------------------------')
57
+ print(content)
58
+ print('--------------------------')
59
+
60
+
61
+ dirname = pathlib.Path().resolve()
62
+ server_name = input('DOMAIN: ')
63
+ name = server_name.split('.')[0]
64
+
65
+ execute('createdb -U postgres {};'.format(name))
66
+
67
+ ssl = input('SSL [y|N]: ').lower() == 'y'
68
+ port = input('PORT [8000]: ') or '8000'
69
+ nginx_file_path = f'/etc/nginx/conf.d/{server_name}.conf'
70
+ nginx_file_content = parse_string_template(NGINX, server_name=server_name, ssl=ssl, port=port)
71
+ print_content(nginx_file_path, nginx_file_content)
72
+
73
+ service_script_path = os.path.join(dirname, 'service.sh')
74
+ service_script_content = parse_string_template(SERVICE_SCRIPT, name=name)
75
+ service_file_content = parse_string_template(SERVICE_CONTENT, server_name=server_name, dirname=dirname)
76
+ service_file_path = f'/etc/systemd/system/{server_name}.service'
77
+ print_content(service_script_path, service_script_content)
78
+ print_content(service_file_path, service_file_content)
79
+ save = input('SAVE [y|N]: ').lower() == 'y'
80
+ if save:
81
+ with open(nginx_file_path, 'w') as file:
82
+ file.write(nginx_file_content)
83
+ with open(service_script_path, 'w') as file:
84
+ file.write(service_script_content)
85
+ with open(service_file_path, 'w') as file:
86
+ file.write(service_file_content)
87
+
88
+ execute('systemctl daemon-reload')
@@ -289,15 +289,17 @@ class Scheduler(dict):
289
289
  def __init__(
290
290
  self,
291
291
  start_time=7,
292
- end_time=20,
292
+ end_time=23,
293
293
  chucks=2,
294
294
  start_day=None,
295
295
  days=14,
296
296
  single_selection=False,
297
297
  input_name="schedule",
298
298
  readonly=False,
299
+ title=None
299
300
  ):
300
301
  self["type"] = "scheduler"
302
+ self["title"] = title
301
303
  self["single_selection"] = single_selection
302
304
  self["input_name"] = input_name
303
305
  self["readonly"] = readonly
@@ -0,0 +1,98 @@
1
+ # -*- coding: utf-8 -*-
2
+ import datetime
3
+ import json
4
+ from decimal import Decimal
5
+
6
+ from django.apps import apps
7
+ from django.db.models import *
8
+ from django.db.models.query_utils import DeferredAttribute
9
+
10
+
11
+ class GenericModelWrapper(object):
12
+ def __init__(self, obj):
13
+ self._wrapped_obj = obj
14
+
15
+ def __getattr__(self, attr):
16
+ if attr == 'prepare_database_save':
17
+ raise AttributeError()
18
+ return getattr(self._wrapped_obj, attr)
19
+
20
+ def __setattr__(self, attr, value):
21
+ if attr == '_wrapped_obj':
22
+ super().__setattr__(attr, value)
23
+ elif self._wrapped_obj is not None:
24
+ self._wrapped_obj.__setattr__(attr, value._wrapped_obj)
25
+
26
+ def __str__(self):
27
+ return self._wrapped_obj.__str__()
28
+
29
+ def __repr__(self):
30
+ return self._wrapped_obj.__repr__()
31
+
32
+
33
+ class GenericValue(object):
34
+ def __init__(self, value):
35
+ self.value = value
36
+
37
+ def get_value(self):
38
+ if isinstance(self.value, str) and '::' in self.value:
39
+ value_type, value = self.value.split('::')
40
+ if '.' in value_type:
41
+ self.value = apps.get_model(value_type).objects.get(pk=value)
42
+ elif value_type == 'str':
43
+ self.value = value
44
+ elif value_type == 'int':
45
+ self.value = int(value)
46
+ elif value_type == 'Decimal':
47
+ self.value = Decimal(value)
48
+ elif value_type in ('date', 'datetime'):
49
+ self.value = datetime.datetime.strptime(value[0:10], '%Y-%m-%d')
50
+ elif value_type == 'float':
51
+ self.value = float(value)
52
+ elif value_type == 'bool':
53
+ self.value = value == 'True'
54
+ elif value_type == 'list':
55
+ self.value = json.loads(value)
56
+ return self.value
57
+
58
+ def dumps(self):
59
+ value = self.value
60
+ if value is not None:
61
+ if isinstance(value, Model):
62
+ value = GenericModelWrapper(value)
63
+ if isinstance(value, GenericModelWrapper):
64
+ return '{}.{}::{}'.format(
65
+ value._meta.app_label, value._meta.model_name, value.pk
66
+ )
67
+ if hasattr(value, 'model'):
68
+ value = list(value.values_list('pk', flat=True))
69
+ if isinstance(value, list):
70
+ value = json.dumps(value)
71
+ return '{}::{}'.format(type(value).__name__, value)
72
+ return None
73
+
74
+
75
+ class GenericFieldDescriptor(DeferredAttribute):
76
+ def __get__(self, instance, cls=None):
77
+ obj = super().__get__(instance, cls=cls)
78
+ if isinstance(obj.value, Model):
79
+ return GenericModelWrapper(obj.value)
80
+ return obj.get_value()
81
+
82
+ def __set__(self, instance, value):
83
+ instance.__dict__[self.field.attname] = GenericValue(value)
84
+
85
+
86
+ class GenericField(CharField):
87
+ descriptor_class = GenericFieldDescriptor
88
+
89
+ def __init__(self, *args, max_length=255, null=True, **kwargs):
90
+ super().__init__(*args, max_length=max_length, null=null, **kwargs)
91
+
92
+ def get_prep_value(self, value):
93
+ if value is not None:
94
+ if isinstance(value, GenericValue):
95
+ value = value.dumps()
96
+ else:
97
+ value = GenericValue(value).dumps()
98
+ return value
@@ -2,27 +2,34 @@ from uuid import uuid1
2
2
  from django.db.models import Model as DjangoModel
3
3
  from django.db.models import *
4
4
  from django.utils.translation import gettext_lazy as _
5
+ from . import generic
5
6
  from .. import ModelMixin
6
7
 
8
+ GenericField = generic.GenericField
9
+
7
10
  class CharField(CharField):
8
11
  def __init__(self, *args, **kwargs):
9
12
  self.mask = kwargs.pop('mask', None)
13
+ self.pick = kwargs.pop('pick', False)
10
14
  kwargs.setdefault('max_length', 255)
11
15
  super().__init__(*args, **kwargs)
12
16
 
13
17
  def formfield(self, *args, **kwargs):
14
18
  field = super().formfield(*args, **kwargs)
15
19
  field.mask = self.mask
20
+ field.pick = self.pick
16
21
  return field
17
22
 
18
23
  class IntegerField(IntegerField):
19
24
  def __init__(self, *args, **kwargs):
20
25
  self.mask = kwargs.pop('mask', None)
26
+ self.pick = kwargs.pop('pick', False)
21
27
  super().__init__(*args, **kwargs)
22
28
 
23
29
  def formfield(self, *args, **kwargs):
24
30
  field = super().formfield(*args, **kwargs)
25
31
  field.mask = self.mask
32
+ field.pick = self.pick
26
33
  return field
27
34
 
28
35
  class ForeignKey(ForeignKey):
@@ -138,3 +145,11 @@ class ImageField(ImageField):
138
145
  class Model(DjangoModel, ModelMixin):
139
146
  class Meta:
140
147
  abstract = True
148
+
149
+ class Filter:
150
+
151
+ def get_label(self):
152
+ return None
153
+
154
+ def choices(self, queryset):
155
+ return queryset