slthcore 0.0.5__tar.gz → 0.0.7__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 (83) hide show
  1. {slthcore-0.0.5/slthcore.egg-info → slthcore-0.0.7}/PKG-INFO +1 -1
  2. {slthcore-0.0.5 → slthcore-0.0.7}/setup.py +1 -1
  3. {slthcore-0.0.5 → slthcore-0.0.7}/slth/components.py +41 -15
  4. {slthcore-0.0.5 → slthcore-0.0.7}/slth/db/models.py +4 -0
  5. {slthcore-0.0.5 → slthcore-0.0.7}/slth/endpoints.py +66 -3
  6. slthcore-0.0.7/slth/exceptions.py +11 -0
  7. {slthcore-0.0.5 → slthcore-0.0.7}/slth/factory.py +6 -0
  8. {slthcore-0.0.5 → slthcore-0.0.7}/slth/forms.py +28 -5
  9. {slthcore-0.0.5 → slthcore-0.0.7}/slth/models.py +1 -1
  10. {slthcore-0.0.5 → slthcore-0.0.7}/slth/notifications.py +1 -0
  11. slthcore-0.0.7/slth/printer.py +70 -0
  12. {slthcore-0.0.5 → slthcore-0.0.7}/slth/queryset.py +2 -0
  13. {slthcore-0.0.5 → slthcore-0.0.7}/slth/serializer.py +1 -1
  14. slthcore-0.0.7/slth/static/js/slth.min.js +236 -0
  15. {slthcore-0.0.5 → slthcore-0.0.7}/slth/templates/index.html +2 -0
  16. slthcore-0.0.7/slth/templates/report.html +23 -0
  17. slthcore-0.0.7/slth/templates/signature.html +45 -0
  18. {slthcore-0.0.5 → slthcore-0.0.7}/slth/tests.py +4 -2
  19. {slthcore-0.0.5 → slthcore-0.0.7}/slth/views.py +6 -2
  20. {slthcore-0.0.5 → slthcore-0.0.7/slthcore.egg-info}/PKG-INFO +1 -1
  21. {slthcore-0.0.5 → slthcore-0.0.7}/slthcore.egg-info/SOURCES.txt +3 -0
  22. slthcore-0.0.5/slth/exceptions.py +0 -6
  23. slthcore-0.0.5/slth/static/js/slth.min.js +0 -225
  24. {slthcore-0.0.5 → slthcore-0.0.7}/MANIFEST.in +0 -0
  25. {slthcore-0.0.5 → slthcore-0.0.7}/setup.cfg +0 -0
  26. {slthcore-0.0.5 → slthcore-0.0.7}/slth/__init__.py +0 -0
  27. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/configure/__main__.py +0 -0
  28. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/__main__.py +0 -0
  29. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/.DS_Store +0 -0
  30. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/.gitignore +0 -0
  31. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/__init__.py +0 -0
  32. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/asgi.py +0 -0
  33. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/endpoints.py +0 -0
  34. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/models.py +0 -0
  35. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/settings.py +0 -0
  36. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/tests.py +0 -0
  37. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/urls.py +0 -0
  38. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/wsgi.py +0 -0
  39. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/application.yml +0 -0
  40. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/entrypoint.sh +0 -0
  41. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/manage.py +0 -0
  42. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/requirements.txt +0 -0
  43. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/base.env +0 -0
  44. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/docker-compose.yml +0 -0
  45. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/frontend/package.json +0 -0
  46. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/frontend/src/main.jsx +0 -0
  47. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/frontend/vite.config.js +0 -0
  48. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/local.env +0 -0
  49. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/run.sh +0 -0
  50. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/selenium/run.sh +0 -0
  51. {slthcore-0.0.5 → slthcore-0.0.7}/slth/cmd/init/boilerplate/test.sh +0 -0
  52. {slthcore-0.0.5 → slthcore-0.0.7}/slth/db/__init__.py +0 -0
  53. {slthcore-0.0.5 → slthcore-0.0.7}/slth/db/generic.py +0 -0
  54. {slthcore-0.0.5 → slthcore-0.0.7}/slth/management/__init__.py +0 -0
  55. {slthcore-0.0.5 → slthcore-0.0.7}/slth/management/commands/__init__.py +0 -0
  56. {slthcore-0.0.5 → slthcore-0.0.7}/slth/management/commands/integration_test.py +0 -0
  57. {slthcore-0.0.5 → slthcore-0.0.7}/slth/management/commands/sync.py +0 -0
  58. {slthcore-0.0.5 → slthcore-0.0.7}/slth/migrations/0001_initial.py +0 -0
  59. {slthcore-0.0.5 → slthcore-0.0.7}/slth/migrations/0002_email_role_pushsubscription_error.py +0 -0
  60. {slthcore-0.0.5 → slthcore-0.0.7}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +0 -0
  61. {slthcore-0.0.5 → slthcore-0.0.7}/slth/migrations/0004_alter_profile_photo.py +0 -0
  62. {slthcore-0.0.5 → slthcore-0.0.7}/slth/migrations/0005_alter_profile_photo.py +0 -0
  63. {slthcore-0.0.5 → slthcore-0.0.7}/slth/migrations/0006_user.py +0 -0
  64. {slthcore-0.0.5 → slthcore-0.0.7}/slth/migrations/0007_deletion_log.py +0 -0
  65. {slthcore-0.0.5 → slthcore-0.0.7}/slth/migrations/__init__.py +0 -0
  66. {slthcore-0.0.5 → slthcore-0.0.7}/slth/oauth.py +0 -0
  67. {slthcore-0.0.5 → slthcore-0.0.7}/slth/permissions.py +0 -0
  68. {slthcore-0.0.5 → slthcore-0.0.7}/slth/roles.py +0 -0
  69. {slthcore-0.0.5 → slthcore-0.0.7}/slth/selenium/__init__.py +0 -0
  70. {slthcore-0.0.5 → slthcore-0.0.7}/slth/selenium/browser.py +0 -0
  71. {slthcore-0.0.5 → slthcore-0.0.7}/slth/static/.DS_Store +0 -0
  72. {slthcore-0.0.5 → slthcore-0.0.7}/slth/static/css/.DS_Store +0 -0
  73. {slthcore-0.0.5 → slthcore-0.0.7}/slth/static/css/slth.css +0 -0
  74. {slthcore-0.0.5 → slthcore-0.0.7}/slth/static/js/index.min.js +0 -0
  75. {slthcore-0.0.5 → slthcore-0.0.7}/slth/static/js/react.min.js +0 -0
  76. {slthcore-0.0.5 → slthcore-0.0.7}/slth/statistics.py +0 -0
  77. {slthcore-0.0.5 → slthcore-0.0.7}/slth/tasks.py +0 -0
  78. {slthcore-0.0.5 → slthcore-0.0.7}/slth/templates/service-worker.js +0 -0
  79. {slthcore-0.0.5 → slthcore-0.0.7}/slth/threadlocal.py +0 -0
  80. {slthcore-0.0.5 → slthcore-0.0.7}/slth/urls.py +0 -0
  81. {slthcore-0.0.5 → slthcore-0.0.7}/slth/utils.py +0 -0
  82. {slthcore-0.0.5 → slthcore-0.0.7}/slthcore.egg-info/dependency_links.txt +0 -0
  83. {slthcore-0.0.5 → slthcore-0.0.7}/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.5
3
+ Version: 0.0.7
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.5',
8
+ version='0.0.7',
9
9
  packages=find_packages(),
10
10
  install_requires=install_requires,
11
11
  include_package_data=True,
@@ -170,9 +170,10 @@ class Table(dict):
170
170
 
171
171
 
172
172
  class TemplateContent(dict):
173
- def __init__(self, name, context):
173
+ def __init__(self, name, context, autoreload=None):
174
174
  self["type"] = "html"
175
175
  self["content"] = render_to_string(name, context)
176
+ self["autoreload"] = autoreload * 1000 if autoreload else None
176
177
 
177
178
 
178
179
  class Banner(dict):
@@ -284,11 +285,11 @@ class Grid(dict):
284
285
 
285
286
 
286
287
  class Scheduler(dict):
287
- INTERVALS = {1: ["00"], 2: ["00", "30"]}
288
+ INTERVALS = {1: ["00"], 2: ["00", "30"], 3: ["00", "20", "40"]}
288
289
 
289
290
  def __init__(
290
291
  self,
291
- start_time=7,
292
+ start_time=0,
292
293
  end_time=23,
293
294
  chucks=2,
294
295
  start_day=None,
@@ -296,19 +297,37 @@ class Scheduler(dict):
296
297
  single_selection=False,
297
298
  input_name="schedule",
298
299
  readonly=False,
300
+ title=None,
301
+ watch=[],
302
+ url=None,
303
+ selectable=None,
304
+ weekly=False
299
305
  ):
300
306
  self["type"] = "scheduler"
307
+ self["title"] = title
301
308
  self["single_selection"] = single_selection
302
309
  self["input_name"] = input_name
303
310
  self["readonly"] = readonly
304
- self.end_day = start_day or datetime.now()
305
- self.end_day = datetime(self.end_day.year, self.end_day.month, self.end_day.day)
311
+ self["watch"] = watch
312
+ self["url"] = url
313
+ self["selectable"] = ['{} {}'.format(obj.strftime("%d/%m/%Y"), obj.strftime("%H:%M")) for obj in selectable] if selectable is not None else None
314
+ self["weekly"] = weekly
315
+ if weekly:
316
+ self.end_day = datetime.now()
317
+ while self.end_day.weekday() > 0:
318
+ self.end_day = self.end_day - timedelta(days=1)
319
+ days = 7
320
+ else:
321
+ self.end_day = start_day or datetime.now()
322
+ self.end_day = datetime(self.end_day.year, self.end_day.month, self.end_day.day)
306
323
  self.times = []
307
324
  for hour in range(start_time, end_time + 1):
308
325
  for minute in Scheduler.INTERVALS[chucks]:
309
326
  self.times.append("{}:{}".format(str(hour).rjust(2, "0"), minute))
310
327
  self.days = []
311
328
  for n in range(0, days):
329
+ if n == 0:
330
+ self.start_day = self.end_day
312
331
  self.days.append(self.end_day.strftime("%d/%m/%Y"))
313
332
  self.end_day = self.end_day + timedelta(days=1)
314
333
  self.end_day = datetime(self.end_day.year, self.end_day.month, self.end_day.day, 23, 59, 59)
@@ -327,13 +346,20 @@ class Scheduler(dict):
327
346
  row.append(self["slots"][day][time])
328
347
  self["matrix"].append(row)
329
348
 
330
- def append(self, date_time, text, icon='check'):
331
- day = date_time.strftime("%d/%m/%Y")
332
- time = date_time.strftime("%H:%M")
333
- value = dict(text=text, icon=icon)
334
- self["slots"][day][time] = value
335
- j = self.days.index(day) + 1 if day in self.days else -1
336
- x = self.times.index(time) + 1 if time in self.times else -1
337
- self["matrix"][x][j] = value
338
-
339
-
349
+ def append(self, date_time, text=None, icon='check'):
350
+ if date_time.strftime("%d/%m/%Y") in self.days:
351
+ day = date_time.strftime("%d/%m/%Y")
352
+ time = date_time.strftime("%H:%M")
353
+ value = dict(text=text, icon=icon)
354
+ self["slots"][day][time] = value
355
+ j = self.days.index(day) + 1 if day in self.days else -1
356
+ x = self.times.index(time) + 1 if time in self.times else -1
357
+ self["matrix"][x][j] = value
358
+
359
+ def append_weekday(self, weekday, hour, minute):
360
+ date_time = self.start_day
361
+ while date_time < self.end_day + timedelta(days=-1):
362
+ if date_time.weekday() == int(weekday):
363
+ self.append(datetime(date_time.year, date_time.month, date_time.day, hour, minute, 0))
364
+ date_time += timedelta(days=1)
365
+
@@ -10,22 +10,26 @@ GenericField = generic.GenericField
10
10
  class CharField(CharField):
11
11
  def __init__(self, *args, **kwargs):
12
12
  self.mask = kwargs.pop('mask', None)
13
+ self.pick = kwargs.pop('pick', False)
13
14
  kwargs.setdefault('max_length', 255)
14
15
  super().__init__(*args, **kwargs)
15
16
 
16
17
  def formfield(self, *args, **kwargs):
17
18
  field = super().formfield(*args, **kwargs)
18
19
  field.mask = self.mask
20
+ field.pick = self.pick
19
21
  return field
20
22
 
21
23
  class IntegerField(IntegerField):
22
24
  def __init__(self, *args, **kwargs):
23
25
  self.mask = kwargs.pop('mask', None)
26
+ self.pick = kwargs.pop('pick', False)
24
27
  super().__init__(*args, **kwargs)
25
28
 
26
29
  def formfield(self, *args, **kwargs):
27
30
  field = super().formfield(*args, **kwargs)
28
31
  field.mask = self.mask
32
+ field.pick = self.pick
29
33
  return field
30
34
 
31
35
  class ForeignKey(ForeignKey):
@@ -1,7 +1,9 @@
1
+ import io
1
2
  import json
2
3
  import types
3
4
  import inspect
4
-
5
+ import requests
6
+ from weasyprint import HTML
5
7
  from .models import Token, Role, Log, Deletion
6
8
  from django.apps import apps
7
9
  from typing import TypeVar, Generic
@@ -9,13 +11,14 @@ from django.core.cache import cache
9
11
  from django.conf import settings
10
12
  from django.utils.text import slugify
11
13
  from django.db import models
12
- from django.http import JsonResponse
14
+ from django.http import JsonResponse, HttpResponse
13
15
  from django.views.decorators.csrf import csrf_exempt
14
16
  from .factory import FormFactory
15
17
  from django.core.exceptions import ValidationError
16
18
  from slth import forms
17
19
  from django.db.models import Model
18
20
  from datetime import datetime
21
+ from django.template.loader import render_to_string
19
22
  from django.contrib.auth import authenticate
20
23
  from .forms import ModelForm, Form
21
24
  from .serializer import serialize, Serializer
@@ -27,8 +30,9 @@ from .components import (
27
30
  Response,
28
31
  Boxes,
29
32
  IconSet,
33
+ TemplateContent,
30
34
  )
31
- from .exceptions import JsonResponseException
35
+ from .exceptions import JsonResponseException, ReadyResponseException
32
36
  from .utils import build_url, append_url
33
37
  from .models import PushSubscription, Profile, User, Log
34
38
  from slth.queryset import QuerySet
@@ -148,6 +152,29 @@ class Endpoint(metaclass=EnpointMetaclass):
148
152
 
149
153
  def redirect(self, url):
150
154
  raise JsonResponseException(dict(type="redirect", url=url))
155
+
156
+ def render(self, data, template=None, pdf=False, autoreload=None):
157
+ base_url='http://localhost:8000'
158
+ data.update(base_url=base_url)
159
+ if template is None:
160
+ template = '{}.html'.format(template or self.__class__.__name__.lower())
161
+ if pdf:
162
+ buffer = io.BytesIO()
163
+ if isinstance(template, str):
164
+ templates = template,
165
+ else:
166
+ templates = template
167
+ pages = []
168
+ for template in templates:
169
+ html = render_to_string(template, data)
170
+ if pdf:
171
+ doc = HTML(string=html).render()
172
+ pages.extend(doc.pages)
173
+ new_doc = doc.copy(pages=pages)
174
+ new_doc.write_pdf(buffer, base_url=base_url, stylesheets=[])
175
+ buffer.seek(0)
176
+ raise ReadyResponseException(HttpResponse(buffer, content_type='application/pdf'))
177
+ return TemplateContent(template, data, autoreload=autoreload)
151
178
 
152
179
  def getform(self, form):
153
180
  return form
@@ -309,11 +336,26 @@ class Endpoint(metaclass=EnpointMetaclass):
309
336
  tl.context.update(model=model, pk=pk or instance.pk)
310
337
  Log.objects.create(data=tl.context)
311
338
 
339
+ def absolute_url(self, relative_url):
340
+ return "{}://{}{}".format(self.request.META.get('X-Forwarded-Proto', self.request.scheme), self.request.get_host(), relative_url)
341
+
342
+
312
343
  class PublicEndpoint(Endpoint):
313
344
  def check_permission(self):
314
345
  return True
315
346
 
316
347
 
348
+ class ReportEndpoint(Endpoint):
349
+
350
+ def process(self):
351
+ data = super().process()
352
+ template = '{}.html'.format(self.__class__.__name__.lower())
353
+ html = render_to_string(template, data)
354
+ headers = {'Content-type': 'text/html'}
355
+ response = requests.post('http://weasyprint.aplicativo.click/pdf', headers=headers, data=html)
356
+ raise ReadyResponseException(HttpResponse(response.content, content_type='application/pdf'))
357
+
358
+
317
359
  class ModelEndpoint(Endpoint):
318
360
  def __init__(self):
319
361
  self.model = self.__orig_bases__[0].__args__[0]
@@ -418,6 +460,27 @@ class ChildEndpoint(Endpoint):
418
460
 
419
461
  def check_permission(self):
420
462
  return True
463
+
464
+ class RelationEndpoint(Generic[T], ModelEndpoint):
465
+ def __init__(self):
466
+ super().__init__()
467
+ self.instance = self.model()
468
+
469
+ def get(self) -> FormFactory:
470
+ return self.formfactory()
471
+
472
+ def get_instance(self):
473
+ return self.instance
474
+
475
+ def formfactory(self):
476
+ return super().formfactory(self.instance)
477
+
478
+ @classmethod
479
+ def is_child(cls):
480
+ return True
481
+
482
+ def check_permission(self):
483
+ return True
421
484
 
422
485
 
423
486
  class Add(ChildEndpoint):
@@ -0,0 +1,11 @@
1
+
2
+ class JsonResponseException(Exception):
3
+
4
+ def __init__(self, data, *args, **kwargs):
5
+ super().__init__(*args, **kwargs)
6
+ self.data = data
7
+
8
+ class ReadyResponseException(Exception):
9
+ def __init__(self, response, *args, **kwargs):
10
+ super().__init__(*args, **kwargs)
11
+ self.response = response
@@ -18,6 +18,7 @@ class FormFactory:
18
18
  self._redirect = None
19
19
  self._message = None
20
20
  self._dispose = False
21
+ self._image = None
21
22
 
22
23
  def _append_field(self, field_name):
23
24
  if ':' in field_name:
@@ -25,6 +26,10 @@ class FormFactory:
25
26
  self._actions[field_name] = action_name
26
27
  self._fieldlist.append(field_name)
27
28
  return field_name
29
+
30
+ def image(self, image) -> 'FormFactory':
31
+ self._image = image
32
+ return self
28
33
 
29
34
  def fields(self, *names, **values) -> 'FormFactory':
30
35
  not_str = {name for name in names if not isinstance(name, str)}
@@ -112,6 +117,7 @@ class FormFactory:
112
117
  form._message = self._message
113
118
  form._redirect = self._redirect
114
119
  form._dispose = self._dispose
120
+ form._image = self._image
115
121
  for name in self._fieldlist:
116
122
  if name not in form.fields:
117
123
  form.fields[name] = getattr(endpoint, name)
@@ -166,6 +166,7 @@ class FormMixin:
166
166
  style=self.get_metadata("style"),
167
167
  url=absolute_url(self.request),
168
168
  info=self._info,
169
+ image=self._image,
169
170
  )
170
171
  data.update(
171
172
  controls=self.controller.controls, width=self.get_metadata("width", "100%")
@@ -337,21 +338,21 @@ class FormMixin:
337
338
  for word in ("password", "senha"):
338
339
  if word in name:
339
340
  data.update(type="password")
341
+ pick = getattr(field, "pick", False)
340
342
  if isinstance(field, CharField) or isinstance(field, IntegerField):
341
343
  mask = getattr(field, "mask", None)
342
344
  if mask:
343
345
  data.update(mask=mask)
344
- if isinstance(field, CharField) and isinstance(field.widget, Textarea):
346
+ if isinstance(field.widget, Textarea):
345
347
  data.update(type="textarea")
346
348
  if ftype == "decimal":
347
349
  data.update(mask="decimal")
348
350
  elif ftype == "scheduler":
349
351
  data.update(scheduler=field.scheduler)
350
- elif ftype == "choice":
352
+ elif ftype == "choice" or pick:
351
353
  if name in self.request.GET and not choices_field_name:
352
354
  data.update(type="hidden", value=self.request.GET[name])
353
355
  else:
354
- pick = getattr(field, "pick", False)
355
356
  if choices_field_name == fname or (isinstance(field.choices, ModelChoiceIterator) and not pick):
356
357
  if choices_field_name == fname:
357
358
  qs = field.choices.queryset
@@ -466,6 +467,13 @@ class FormMixin:
466
467
  if field_name not in inline_fields
467
468
  }
468
469
  )
470
+ for attr_name in dir(self._endpoint):
471
+ if attr_name.startswith('clean_'):
472
+ try:
473
+ getattr(self._endpoint, attr_name)(data)
474
+ except ValidationError as e:
475
+ fieldname = attr_name.replace('clean_', '')
476
+ raise JsonResponseException(dict(type="error", text="Por favor, corrija os erros.", errors={fieldname: ''.join(e.messages)}))
469
477
  with transaction.atomic():
470
478
  self.cleaned_data = data
471
479
  if isinstance(self, DjangoModelForm):
@@ -506,6 +514,7 @@ class Form(DjangoForm, FormMixin):
506
514
  self._display = []
507
515
  self._endpoint = endpoint
508
516
  self._info = None
517
+ self._image = None
509
518
  self._message = None
510
519
  self._redirect = None
511
520
  self._dispose = False
@@ -540,6 +549,7 @@ class ModelForm(DjangoModelForm, FormMixin):
540
549
  self._display = []
541
550
  self._endpoint = endpoint
542
551
  self._info = None
552
+ self._image = None
543
553
  self._message = None
544
554
  self._redirect = None
545
555
  self._dispose = False
@@ -568,6 +578,9 @@ class ModelForm(DjangoModelForm, FormMixin):
568
578
  kwargs.update(files=self.request.FILES or None)
569
579
  super().__init__(instance=instance, **kwargs)
570
580
 
581
+ def is_valid(self):
582
+ valid = super().is_valid()
583
+ return valid
571
584
 
572
585
  class InlineFormField(Field):
573
586
  def __init__(self, *args, form=None, min=1, max=3, **kwargs):
@@ -691,7 +704,7 @@ class SchedulerField(CharField):
691
704
  self.scheduler = scheduler or Scheduler()
692
705
  super().__init__(*args, **kwargs)
693
706
 
694
- def clean(self, value):
707
+ def to_python(self, value):
695
708
  values = dict(select=[], deselect=[])
696
709
  if value:
697
710
  data = json.loads(value)
@@ -699,7 +712,17 @@ class SchedulerField(CharField):
699
712
  for date, hour in data[key]:
700
713
  data_string = "{} {}".format(date, hour)
701
714
  values[key].append(datetime.datetime.strptime(data_string, "%d/%m/%Y %H:%M"))
702
- return values
715
+ if self.scheduler["single_selection"]:
716
+ return values['select'][0] if values['select'] else None
717
+ else:
718
+ return values
719
+
720
+ def clean(self, value):
721
+ value = self.to_python(value)
722
+ if self.scheduler["single_selection"]:
723
+ if self.required and value is None:
724
+ raise ValidationError('Este campo é obrigatório.')
725
+ return value
703
726
 
704
727
 
705
728
  FIELD_TYPES = {
@@ -95,7 +95,7 @@ class Role(models.Model):
95
95
  return APPLICATON['groups'].get(self.name, self.name)
96
96
 
97
97
  def get_scope_value(self):
98
- return apps.get_model(self.model).objects.get(pk=self.value) if self.model else None
98
+ return apps.get_model(self.model).objects.filter(pk=self.value).first() if self.model else None
99
99
 
100
100
  @meta('Descrição')
101
101
  def get_description(self):
@@ -6,6 +6,7 @@ from slth import APPLICATON
6
6
  def send_push_web_notification(user, title, message, url=None, icon=None):
7
7
  icon = icon or APPLICATON['icon']
8
8
  for subscription in user.pushsubscription_set.all():
9
+ print(subscription.user, subscription.device, subscription.data, title, message, url)
9
10
  data = webpush(
10
11
  subscription_info=subscription.data,
11
12
  data=json.dumps(dict(title=title, message=message, url=url, icon=icon)),
@@ -0,0 +1,70 @@
1
+ import io
2
+ import qrcode
3
+ import base64
4
+ from datetime import datetime
5
+ from weasyprint import HTML
6
+ from django.conf import settings
7
+ from django.template.loader import render_to_string
8
+
9
+
10
+ class Signature:
11
+ def __init__(self, date, validation_url, verify_code=None, auth_code=None):
12
+ self.date = date
13
+ if validation_url.startswith('http'):
14
+ self.validation_url = validation_url
15
+ else:
16
+ self.validation_url = '{}{}'.format(settings.SITE_URL, validation_url)
17
+ self.verify_code = verify_code
18
+ self.auth_code = auth_code
19
+ self.signers = []
20
+ self.qrcode = qrcode_base64(self.validation_url)
21
+
22
+ def add_signer(self, identifier, signature_date=None):
23
+ self.signers.append((identifier, signature_date))
24
+
25
+
26
+ def qrcode_base64(texto):
27
+ qr = qrcode.QRCode(version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4)
28
+ qr.add_data(texto)
29
+ qr.make(fit=True)
30
+ img = qr.make_image(fill_color="black", back_color="white")
31
+ buffered =io.BytesIO()
32
+ img.save(buffered, format="JPEG")
33
+ img_str = base64.b64encode(buffered.getvalue()).decode()
34
+ return img_str
35
+
36
+ def image_base64(image_bytes):
37
+ return 'data:image/png;base64, {}'.format(base64.b64encode(image_bytes).decode())
38
+
39
+
40
+ def to_pdf(data, template, file_name=None, signature=None):
41
+ templates = []
42
+ buffer = io.BytesIO()
43
+ templates.append(template)
44
+ if signature:
45
+ data.update(signature=signature)
46
+ templates.append('signature.html')
47
+ pages = []
48
+ for template in templates:
49
+ data.update(page=len(pages)+1)
50
+ html = render_to_string(template, data)
51
+ doc = HTML(string=html).render()
52
+ pages.extend(doc.pages)
53
+ doc = doc.copy(pages=pages) if len(templates) > 1 else doc
54
+ doc.write_pdf(file_name or buffer, base_url=settings.SITE_URL, stylesheets=[])
55
+ buffer.seek(0)
56
+ return buffer
57
+
58
+ def test():
59
+ data = dict(lines=range(50))
60
+ signature = Signature(date=datetime.now(), validation_url='/', verify_code=123, auth_code=123)
61
+ signature.add_signer('Carlos Silva', datetime.now())
62
+ signature.add_signer('Juca Silva', None)
63
+ to_pdf(data, 'report.html', '/Users/breno/Downloads/a.pdf', signature=signature)
64
+
65
+ def test2():
66
+ data = dict(lines=range(50))
67
+ signature = Signature(date=datetime.now(), validation_url='https://validar.iti.gov.br/')
68
+ signature.add_signer('Carlos Silva (075.803.982-90)', None)
69
+ signature.add_signer('Juca Silva (075.803.982-90)', None)
70
+ to_pdf(data, 'report.html', '/Users/breno/Downloads/a.pdf', signature=signature)
@@ -22,6 +22,7 @@ class QuerySet(models.QuerySet):
22
22
 
23
23
  def __init__(self, *args, **kwargs):
24
24
  self.metadata = {}
25
+ self.instance = None
25
26
  self.request = None
26
27
  super().__init__(*args, **kwargs)
27
28
 
@@ -36,6 +37,7 @@ class QuerySet(models.QuerySet):
36
37
 
37
38
  def _clone(self):
38
39
  qs = super()._clone()
40
+ qs.instance = self.instance
39
41
  qs.request = self.request
40
42
  for k, v in self.metadata.items():
41
43
  v = self.metadata[k]
@@ -288,7 +288,7 @@ class Serializer:
288
288
  value = attr().filter()
289
289
  else:
290
290
  value = attr.filter()
291
-
291
+ value.instance = self.obj
292
292
  path = self.path + [key]
293
293
  if lazy:
294
294
  data = dict(type='queryset', title=title, key=key)