slthcore 0.1.4__tar.gz → 0.1.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.
Potentially problematic release.
This version of slthcore might be problematic. Click here for more details.
- {slthcore-0.1.4/slthcore.egg-info → slthcore-0.1.5}/PKG-INFO +1 -1
- {slthcore-0.1.4 → slthcore-0.1.5}/setup.py +1 -1
- slthcore-0.1.5/slth/pdf/__init__.py +177 -0
- slthcore-0.1.5/slth/pdf/tests.py +70 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/selenium/__init__.py +1 -0
- {slthcore-0.1.4 → slthcore-0.1.5/slthcore.egg-info}/PKG-INFO +1 -1
- {slthcore-0.1.4 → slthcore-0.1.5}/slthcore.egg-info/SOURCES.txt +2 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/MANIFEST.in +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/setup.cfg +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/__init__.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/configure/__main__.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/__main__.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/.DS_Store +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/.gitignore +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/api/__init__.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/api/asgi.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/api/endpoints.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/api/models.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/api/settings.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/api/tests.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/api/urls.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/api/wsgi.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/application.yml +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/entrypoint.sh +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/manage.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/backend/requirements.txt +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/base.env +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/docker-compose.yml +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/frontend/package.json +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/frontend/src/main.jsx +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/frontend/vite.config.js +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/local.env +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/run.sh +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/selenium/run.sh +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/cmd/init/boilerplate/test.sh +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/components.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/db/__init__.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/db/generic.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/db/models.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/endpoints.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/exceptions.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/factory.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/forms.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/management/__init__.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/management/commands/__init__.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/management/commands/integration_test.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/management/commands/sync.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/migrations/0001_initial.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/migrations/0002_email_role_pushsubscription_error.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/migrations/0004_alter_profile_photo.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/migrations/0005_alter_profile_photo.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/migrations/0006_user.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/migrations/0007_deletion_log.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/migrations/__init__.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/models.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/notifications.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/oauth.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/permissions.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/printer.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/queryset.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/roles.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/selenium/browser.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/serializer.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/static/.DS_Store +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/static/css/.DS_Store +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/static/css/slth.css +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/static/js/index.min.js +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/static/js/react.min.js +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/static/js/slth.min.js +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/statistics.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/tasks.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/templates/index.html +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/templates/report.html +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/templates/service-worker.js +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/templates/signature.html +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/tests.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/threadlocal.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/urls.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/utils.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slth/views.py +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slthcore.egg-info/dependency_links.txt +0 -0
- {slthcore-0.1.4 → slthcore-0.1.5}/slthcore.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# pip install --upgrade fpdf2
|
|
2
|
+
from fpdf import FPDF, FontFace
|
|
3
|
+
import hashlib
|
|
4
|
+
import base64
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import requests
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
import tempfile
|
|
10
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
11
|
+
from django.template.loader import render_to_string
|
|
12
|
+
from django.conf import settings
|
|
13
|
+
|
|
14
|
+
SIGNATURE_URL = 'https://assinatura-api.staging.iti.br/externo/v2/assinarPKCS7'
|
|
15
|
+
SIGNATURE_SIZE = 18944
|
|
16
|
+
BYTERANGE='[ ]'
|
|
17
|
+
|
|
18
|
+
STYLE = {
|
|
19
|
+
'h1': FontFace(color="#000000", size_pt=28, family='Helvetica'),
|
|
20
|
+
'h2': FontFace(color="#000000", size_pt=24, family='Helvetica'),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class PdfWriter:
|
|
24
|
+
def __init__(self):
|
|
25
|
+
self.pdf = FPDF()
|
|
26
|
+
self.pdf.add_page()
|
|
27
|
+
|
|
28
|
+
def render(self, template_name, context):
|
|
29
|
+
context.update(base_url=settings.SITE_URL)
|
|
30
|
+
html = render_to_string(template_name, context)
|
|
31
|
+
self.pdf.write_html(html, tag_styles=STYLE, font_family="Courier")
|
|
32
|
+
|
|
33
|
+
def save(self, path):
|
|
34
|
+
self.pdf.output(path)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class PdfSigner:
|
|
38
|
+
def __init__(self, path, signer):
|
|
39
|
+
self.path = path
|
|
40
|
+
self.signer = signer
|
|
41
|
+
self.xref = None
|
|
42
|
+
self.data = []
|
|
43
|
+
with open(path, 'rb') as fp:
|
|
44
|
+
self.content = fp.read()# + b'\r\n'
|
|
45
|
+
self.offset = len(self.content)
|
|
46
|
+
self.byterange = []
|
|
47
|
+
self.path = path.split('.')[0]
|
|
48
|
+
self.startxref = [int(x[9:].strip()) for x in re.findall(b'startxref[\n\r ]*\d+', self.content)]
|
|
49
|
+
self.objects = {}
|
|
50
|
+
for xref in re.findall(b'xref[\n\r\w\d ]*trailer', self.content[self.startxref[0]:]):
|
|
51
|
+
for offset in sum([[int(n) for n in re.findall(b'\d{10}', xref) if int(n)]], []):
|
|
52
|
+
self.objects[b' '.join(self.content[offset:offset + 15].split()[0:2]).decode()] = offset
|
|
53
|
+
self.trailer = self.content[self.startxref[-1]:].split(b'trailer')[1].split(b'startxref')[0].strip()
|
|
54
|
+
self.root = root = re.findall(b'/Root \d+ \d+ R', self.trailer)[-1][6:-2].decode()
|
|
55
|
+
self.size = int(re.findall(b'/Size \d+', self.trailer)[-1][5:].strip())
|
|
56
|
+
self.page()
|
|
57
|
+
|
|
58
|
+
def obj(self, key=None):
|
|
59
|
+
key = key or self.root
|
|
60
|
+
return self.content[self.objects[key]: self.objects[key] + self.content[self.objects[key]:].index(b'endobj') + 6].replace(b'\r', b'') + b'\n'
|
|
61
|
+
|
|
62
|
+
def next(self, i):
|
|
63
|
+
return max([int(k.split()[0]) for k in self.objects]) + i
|
|
64
|
+
|
|
65
|
+
def add(self, data):
|
|
66
|
+
offset = str(self.offset).rjust(10, '0')
|
|
67
|
+
self.offset += len(data)
|
|
68
|
+
self.data.append(data)
|
|
69
|
+
if self.xref is None:
|
|
70
|
+
self.xref = ['xref', '0 1', '0000000000 65535 f\r']
|
|
71
|
+
self.xref.append('{} 1'.format(data.split()[0]))
|
|
72
|
+
self.xref.append('{} 00000 n\r'.format(offset))
|
|
73
|
+
|
|
74
|
+
def page(self):
|
|
75
|
+
for key, offset in self.objects.items():
|
|
76
|
+
content = self.content[offset:offset + 500]
|
|
77
|
+
if b'/Page' in content and b'/Pages' not in content:
|
|
78
|
+
return key
|
|
79
|
+
raise BaseException('No page was found in the PDF')
|
|
80
|
+
|
|
81
|
+
def finalize(self):
|
|
82
|
+
xrefstart = str(self.offset)
|
|
83
|
+
data = '\n'.join(self.xref)
|
|
84
|
+
data += '\ntrailer\n<<\n/Size {}\n/Root {} R\n/Prev {}\n>>\n'.format(
|
|
85
|
+
(self.size + len(self.xref) - 5), self.root, self.startxref[-1]
|
|
86
|
+
)
|
|
87
|
+
data += 'startxref\n'
|
|
88
|
+
data += xrefstart
|
|
89
|
+
data += '\n%%EOF'
|
|
90
|
+
self.offset += len(data)
|
|
91
|
+
self.data.append(data)
|
|
92
|
+
|
|
93
|
+
def hash(self, n=0):
|
|
94
|
+
w, h = 900, 200
|
|
95
|
+
left = n * 200
|
|
96
|
+
signer = self.signer.split(':')[0]
|
|
97
|
+
stamp_file_path = self.stamp(signer)
|
|
98
|
+
print(stamp_file_path)
|
|
99
|
+
self.add(self.obj().decode().replace('>>', '/AcroForm <<\n/Fields [{} 0 R]\n/SigFlags 3\n>>\n>>'.format(self.next(1))))
|
|
100
|
+
self.add(self.obj(self.page()).decode().replace('/Page', '/Page /Annots [{} 0 R]'.format(self.next(1))))
|
|
101
|
+
a = "{} 0 obj\n<<\n/FT /Sig\n/Type /Annot\n/Subtype /Widget\n/F 132\n/T (Signature1)\n/V {} 0 R\n/P {} R\n/Rect [{} 0 {} {}]\n/AP {} 0 R\n>>\nendobj\n".format(
|
|
102
|
+
self.next(1), self.next(2), self.page(), w/10*2+left, left, h/10*2, self.next(3))
|
|
103
|
+
b = "{} 0 obj\n<<\n/Type /Sig\n/Filter /Adobe.PPKLite\n/SubFilter /adbe.pkcs7.detached\n/M ({})\n/Contents <{}>\n/ByteRange {}\n/Name ({})\n>>\nendobj\n".format(
|
|
104
|
+
self.next(2), datetime.now().strftime("D:%Y%m%d%H%M%S+00'00'"), '0' * SIGNATURE_SIZE, BYTERANGE, signer)
|
|
105
|
+
c = "{} 0 obj\n<<\n/N {} 0 R\n>>\nendobj\n".format(self.next(3), self.next(4))
|
|
106
|
+
stream = 'q\n{} 0 0 {} 0 0 cm\n/Img1 Do\nQ'.format(w, h)
|
|
107
|
+
d = "{} 0 obj\n<</Type/XObject/Subtype/Form/Resources<</XObject<</Img1 {} 0 R>>>>/BBox[0 0 {} {}]/Length {}>>\nstream\n{}\nendstream\nendobj\n".format(self.next(4), self.next(5), w, h, len(stream), stream)
|
|
108
|
+
x = base64.a85encode(open(stamp_file_path, 'rb').read()).decode()
|
|
109
|
+
e = "{} 0 obj\n<<\n/BitsPerComponent 8 /ColorSpace /DeviceRGB /Filter [ /ASCII85Decode /DCTDecode ] /Length {} /Subtype /Image /Type /XObject /Width {} /Height {} /Resources<< >> >>\nstream\n{}\nendstream\nendobj\n".format(self.next(5), len(x), w, h, x)
|
|
110
|
+
self.add(a)
|
|
111
|
+
i = self.offset + b.index('/Contents') + 10
|
|
112
|
+
j = i + SIGNATURE_SIZE + 2
|
|
113
|
+
self.add(b)
|
|
114
|
+
self.add(c)
|
|
115
|
+
self.add(d)
|
|
116
|
+
self.add(e)
|
|
117
|
+
self.finalize()
|
|
118
|
+
self.byterange = [0, i, j, self.length() - j]
|
|
119
|
+
self.data[3] = self.data[3].replace(
|
|
120
|
+
BYTERANGE, '[{} {} {} {}]'.format(*self.byterange).ljust(len(BYTERANGE), ' ')
|
|
121
|
+
)
|
|
122
|
+
for data in self.data:
|
|
123
|
+
self.content += data.encode()
|
|
124
|
+
i, j, x, y = self.byterange
|
|
125
|
+
content = self.content[i:i+j] + self.content[x:x+y]
|
|
126
|
+
hash = base64.b64encode(hashlib.sha256(content).digest()).decode()
|
|
127
|
+
return hash
|
|
128
|
+
|
|
129
|
+
def length(self):
|
|
130
|
+
i = len(self.content)
|
|
131
|
+
for data in self.data:
|
|
132
|
+
i += len(data)
|
|
133
|
+
return i
|
|
134
|
+
|
|
135
|
+
def signed(self, name):
|
|
136
|
+
return '/Name ({})'.format(name).encode() in self.content
|
|
137
|
+
|
|
138
|
+
def stamp(self, name):
|
|
139
|
+
w, h = 900, 200
|
|
140
|
+
bgcolor = '#FFFFFF'
|
|
141
|
+
date = datetime.now()
|
|
142
|
+
img = Image.new('RGB', (w, h), (255, 255, 255))
|
|
143
|
+
img1 = ImageDraw.Draw(img)
|
|
144
|
+
img1.rectangle([(3, 3), (w - 3, h - 3)], fill=bgcolor, outline=bgcolor)
|
|
145
|
+
font = ImageFont.truetype(os.path.join('/Users/breno/Documents/Workspace/suap', 'comum', 'static/comum/font/MicrosoftSansSerif.ttf'), 25)
|
|
146
|
+
bold = ImageFont.truetype(os.path.join('/Users/breno/Documents/Workspace/suap', 'comum', 'static/comum/font/MicrosoftSansSerifBold.ttf'), 25)
|
|
147
|
+
x, y = 380, 10
|
|
148
|
+
img1.text((x, y), 'DOCUMENTO ASSINADO DIGITALMENTE', (0, 0, 0), font=font)
|
|
149
|
+
y += 45
|
|
150
|
+
img1.text((x, y), name, (0, 0, 0), font=bold)
|
|
151
|
+
y += 35
|
|
152
|
+
img1.text((x, y), 'Data/Hora: {}'.format(date.strftime('%d/%m/%Y %H:%M:%S')), (0, 0, 0), font=font)
|
|
153
|
+
y += 35
|
|
154
|
+
img1.text((x, y), 'Verifique em https://verificador.iti.br', (0, 0, 0), font=font)
|
|
155
|
+
y += 35
|
|
156
|
+
tmp = tempfile.NamedTemporaryFile(suffix='.jpeg', delete=False)
|
|
157
|
+
logo = Image.open(os.path.join(os.path.dirname(__file__), 'images', 'icp-brasil.png'))
|
|
158
|
+
img.paste(logo.resize((int(logo.width*0.6), int(logo.height*0.6))), (5, 10))
|
|
159
|
+
img = img.convert("RGB")
|
|
160
|
+
img.save(tmp.name, format='jpeg')
|
|
161
|
+
return tmp.name
|
|
162
|
+
|
|
163
|
+
def sign(self, path=None):
|
|
164
|
+
signature = self.sign_hash(self.hash())
|
|
165
|
+
path = path or '{}-signed.pdf'.format(self.path)
|
|
166
|
+
with open(path, 'wb') as fp:
|
|
167
|
+
content = self.content
|
|
168
|
+
if signature:
|
|
169
|
+
i, j, x, y = self.byterange
|
|
170
|
+
content = self.content[i:i + j] + b'<' + base64.b64decode(signature).hex().ljust(SIGNATURE_SIZE, '0').encode() + b'>' + self.content[x:x + y]
|
|
171
|
+
fp.write(content)
|
|
172
|
+
|
|
173
|
+
def sign_hash(self, hash):
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
#curl -X POST 'https://verificador.staging.iti.br/report' --header 'Content-Type: multipart/form-data' --form 'report_type="json"' --form 'signature_files[]=@"hello-assinado.pdf"' --form 'detached_files[]=""'
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from django.test import TestCase
|
|
2
|
+
from . import PdfWriter, VidaasPdfSigner
|
|
3
|
+
|
|
4
|
+
HTML = """
|
|
5
|
+
<h1>Big title</h1>
|
|
6
|
+
<dl>
|
|
7
|
+
<dt>Description title</dt>
|
|
8
|
+
<dd>Description Detail</dd>
|
|
9
|
+
</dl>
|
|
10
|
+
<section>
|
|
11
|
+
<h2>Section title</h2>
|
|
12
|
+
<p><b>Hello</b> world. <u>I am</u> <i>tired</i>.</p>
|
|
13
|
+
<p><a href="https://github.com/py-pdf/fpdf2">py-pdf/fpdf2 GitHub repo</a></p>
|
|
14
|
+
<p align="right">right aligned text</p>
|
|
15
|
+
<p>i am a paragraph <br>in two parts.</p>
|
|
16
|
+
<font color="#00ff00"><p>hello in green</p></font>
|
|
17
|
+
<font size="7"><p>hello small</p></font>
|
|
18
|
+
<font face="helvetica"><p>hello helvetica</p></font>
|
|
19
|
+
<font face="times"><p>hello times</p></font>
|
|
20
|
+
</section>
|
|
21
|
+
<section>
|
|
22
|
+
<h2>Other section title</h2>
|
|
23
|
+
<ul type="circle">
|
|
24
|
+
<li>unordered</li>
|
|
25
|
+
<li>list</li>
|
|
26
|
+
<li>items</li>
|
|
27
|
+
</ul>
|
|
28
|
+
<ol start="3" type="i">
|
|
29
|
+
<li>ordered</li>
|
|
30
|
+
<li>list</li>
|
|
31
|
+
<li>items</li>
|
|
32
|
+
</ol>
|
|
33
|
+
<br>
|
|
34
|
+
<br>
|
|
35
|
+
<pre>i am preformatted text.</pre>
|
|
36
|
+
<br>
|
|
37
|
+
<blockquote>hello blockquote</blockquote>
|
|
38
|
+
<table width="50%">
|
|
39
|
+
<thead>
|
|
40
|
+
<tr>
|
|
41
|
+
<th width="30%">ID</th>
|
|
42
|
+
<th width="70%">Name</th>
|
|
43
|
+
</tr>
|
|
44
|
+
</thead>
|
|
45
|
+
<tbody>
|
|
46
|
+
<tr>
|
|
47
|
+
<td>1</td>
|
|
48
|
+
<td>Alice</td>
|
|
49
|
+
</tr>
|
|
50
|
+
<tr>
|
|
51
|
+
<td>2</td>
|
|
52
|
+
<td>Bob</td>
|
|
53
|
+
</tr>
|
|
54
|
+
</tbody>
|
|
55
|
+
</table>
|
|
56
|
+
</section>
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
class AnimalTestCase(TestCase):
|
|
60
|
+
def setUp(self):
|
|
61
|
+
return super().setUp()
|
|
62
|
+
|
|
63
|
+
def test_pdf(self):
|
|
64
|
+
writter = PdfWriter(HTML)
|
|
65
|
+
writter.save('test4.pdf')
|
|
66
|
+
signer = VidaasPdfSigner('test4.pdf', 'Carlos Breno Pereira Silva:04770402414')
|
|
67
|
+
signer.sign()
|
|
68
|
+
|
|
69
|
+
def tearDown(self):
|
|
70
|
+
return super().tearDown()
|
|
@@ -78,6 +78,7 @@ class SeleniumTestCase(LiveServerTestCase):
|
|
|
78
78
|
if os.path.exists(fixture_path):
|
|
79
79
|
call_command("loaddata", fixture_path)
|
|
80
80
|
settings.DEBUG = True
|
|
81
|
+
settings.SITE_URL = cls.live_server_url
|
|
81
82
|
|
|
82
83
|
def create_superuser(self, username, password):
|
|
83
84
|
if not User.objects.filter(username=username).exists():
|
|
@@ -61,6 +61,8 @@ slth/migrations/0005_alter_profile_photo.py
|
|
|
61
61
|
slth/migrations/0006_user.py
|
|
62
62
|
slth/migrations/0007_deletion_log.py
|
|
63
63
|
slth/migrations/__init__.py
|
|
64
|
+
slth/pdf/__init__.py
|
|
65
|
+
slth/pdf/tests.py
|
|
64
66
|
slth/selenium/__init__.py
|
|
65
67
|
slth/selenium/browser.py
|
|
66
68
|
slth/static/.DS_Store
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{slthcore-0.1.4 → slthcore-0.1.5}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|