slthcore 0.0.6__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.
- {slthcore-0.0.6/slthcore.egg-info → slthcore-0.0.7}/PKG-INFO +1 -1
- {slthcore-0.0.6 → slthcore-0.0.7}/setup.py +1 -1
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/components.py +40 -16
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/endpoints.py +41 -3
- slthcore-0.0.7/slth/exceptions.py +11 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/factory.py +6 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/forms.py +23 -2
- slthcore-0.0.7/slth/printer.py +70 -0
- slthcore-0.0.7/slth/static/js/slth.min.js +236 -0
- slthcore-0.0.7/slth/templates/report.html +23 -0
- slthcore-0.0.7/slth/templates/signature.html +45 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/views.py +6 -2
- {slthcore-0.0.6 → slthcore-0.0.7/slthcore.egg-info}/PKG-INFO +1 -1
- {slthcore-0.0.6 → slthcore-0.0.7}/slthcore.egg-info/SOURCES.txt +3 -0
- slthcore-0.0.6/slth/exceptions.py +0 -6
- slthcore-0.0.6/slth/static/js/slth.min.js +0 -225
- {slthcore-0.0.6 → slthcore-0.0.7}/MANIFEST.in +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/setup.cfg +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/__init__.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/configure/__main__.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/__main__.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/.DS_Store +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/.gitignore +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/__init__.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/asgi.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/endpoints.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/models.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/settings.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/tests.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/urls.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/api/wsgi.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/application.yml +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/entrypoint.sh +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/manage.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/backend/requirements.txt +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/base.env +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/docker-compose.yml +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/frontend/package.json +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/frontend/src/main.jsx +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/frontend/vite.config.js +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/local.env +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/run.sh +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/selenium/run.sh +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/cmd/init/boilerplate/test.sh +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/db/__init__.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/db/generic.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/db/models.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/management/__init__.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/management/commands/__init__.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/management/commands/integration_test.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/management/commands/sync.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/migrations/0001_initial.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/migrations/0002_email_role_pushsubscription_error.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/migrations/0004_alter_profile_photo.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/migrations/0005_alter_profile_photo.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/migrations/0006_user.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/migrations/0007_deletion_log.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/migrations/__init__.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/models.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/notifications.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/oauth.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/permissions.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/queryset.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/roles.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/selenium/__init__.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/selenium/browser.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/serializer.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/static/.DS_Store +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/static/css/.DS_Store +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/static/css/slth.css +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/static/js/index.min.js +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/static/js/react.min.js +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/statistics.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/tasks.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/templates/index.html +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/templates/service-worker.js +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/tests.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/threadlocal.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/urls.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slth/utils.py +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slthcore.egg-info/dependency_links.txt +0 -0
- {slthcore-0.0.6 → slthcore-0.0.7}/slthcore.egg-info/top_level.txt +0 -0
|
@@ -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=
|
|
292
|
+
start_time=0,
|
|
292
293
|
end_time=23,
|
|
293
294
|
chucks=2,
|
|
294
295
|
start_day=None,
|
|
@@ -296,21 +297,37 @@ class Scheduler(dict):
|
|
|
296
297
|
single_selection=False,
|
|
297
298
|
input_name="schedule",
|
|
298
299
|
readonly=False,
|
|
299
|
-
title=None
|
|
300
|
+
title=None,
|
|
301
|
+
watch=[],
|
|
302
|
+
url=None,
|
|
303
|
+
selectable=None,
|
|
304
|
+
weekly=False
|
|
300
305
|
):
|
|
301
306
|
self["type"] = "scheduler"
|
|
302
307
|
self["title"] = title
|
|
303
308
|
self["single_selection"] = single_selection
|
|
304
309
|
self["input_name"] = input_name
|
|
305
310
|
self["readonly"] = readonly
|
|
306
|
-
self
|
|
307
|
-
self
|
|
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)
|
|
308
323
|
self.times = []
|
|
309
324
|
for hour in range(start_time, end_time + 1):
|
|
310
325
|
for minute in Scheduler.INTERVALS[chucks]:
|
|
311
326
|
self.times.append("{}:{}".format(str(hour).rjust(2, "0"), minute))
|
|
312
327
|
self.days = []
|
|
313
328
|
for n in range(0, days):
|
|
329
|
+
if n == 0:
|
|
330
|
+
self.start_day = self.end_day
|
|
314
331
|
self.days.append(self.end_day.strftime("%d/%m/%Y"))
|
|
315
332
|
self.end_day = self.end_day + timedelta(days=1)
|
|
316
333
|
self.end_day = datetime(self.end_day.year, self.end_day.month, self.end_day.day, 23, 59, 59)
|
|
@@ -329,13 +346,20 @@ class Scheduler(dict):
|
|
|
329
346
|
row.append(self["slots"][day][time])
|
|
330
347
|
self["matrix"].append(row)
|
|
331
348
|
|
|
332
|
-
def append(self, date_time, text, icon='check'):
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
+
|
|
@@ -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
|
|
@@ -318,6 +345,17 @@ class PublicEndpoint(Endpoint):
|
|
|
318
345
|
return True
|
|
319
346
|
|
|
320
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
|
+
|
|
321
359
|
class ModelEndpoint(Endpoint):
|
|
322
360
|
def __init__(self):
|
|
323
361
|
self.model = self.__orig_bases__[0].__args__[0]
|
|
@@ -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%")
|
|
@@ -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
|
|
707
|
+
def to_python(self, value):
|
|
695
708
|
values = dict(select=[], deselect=[])
|
|
696
709
|
if value:
|
|
697
710
|
data = json.loads(value)
|
|
@@ -701,7 +714,15 @@ class SchedulerField(CharField):
|
|
|
701
714
|
values[key].append(datetime.datetime.strptime(data_string, "%d/%m/%Y %H:%M"))
|
|
702
715
|
if self.scheduler["single_selection"]:
|
|
703
716
|
return values['select'][0] if values['select'] else None
|
|
704
|
-
|
|
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
|
|
705
726
|
|
|
706
727
|
|
|
707
728
|
FIELD_TYPES = {
|
|
@@ -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)
|