0001-solis-try-to-convert-files-to-PDF-21997.patch
debian/control | ||
---|---|---|
26 | 26 |
python-cmislib (>= 0.5), python-cmislib (< 0.6), |
27 | 27 |
python-lxml, |
28 | 28 |
python-dateutil, |
29 |
python-pyproj |
|
29 |
python-pyproj, |
|
30 |
python-pil |
|
30 | 31 |
Recommends: python-soappy, python-phpserialize |
31 | 32 |
Description: Uniform access to multiple data sources and services (Python module) |
32 | 33 |
passerelle/apps/solis/models.py | ||
---|---|---|
18 | 18 |
import json |
19 | 19 |
import re |
20 | 20 |
import unicodedata |
21 |
from PIL import Image |
|
22 |
from StringIO import StringIO |
|
21 | 23 | |
22 | 24 |
from django.db import models |
23 | 25 |
from django.template.loader import get_template |
... | ... | |
62 | 64 |
return dict_ |
63 | 65 | |
64 | 66 | |
67 |
def convert_to_pdf(content, content_type): |
|
68 |
content_type = content_type.lower() |
|
69 |
if content_type == 'application/pdf': |
|
70 |
return content |
|
71 |
if content_type.startswith('image/'): |
|
72 |
image = Image.open(StringIO(content)) |
|
73 |
if image.mode != 'RGB': |
|
74 |
# PDF cannot handle alpha (RGBA) |
|
75 |
image = image.convert('RGB') |
|
76 |
out = StringIO() |
|
77 |
image.save(out, format='PDF') |
|
78 |
return out.getvalue() |
|
79 |
raise ValueError('cannot convert %s to PDF' % content_type) |
|
80 | ||
81 | ||
65 | 82 |
def keystore_upload_to(instance, filename): |
66 | 83 |
return '%s/%s/keystore/%s' % (instance.get_connector_slug(), instance.id, filename) |
67 | 84 | |
... | ... | |
302 | 319 |
# handle specific file: and del: keys |
303 | 320 |
files = [] |
304 | 321 |
delete_keys = [] |
322 |
files_failed_pdf_conversion = [] |
|
305 | 323 |
for key, value in payload.items(): |
306 | 324 |
# extract files from payload, to send them before the request |
307 | 325 |
if key.startswith('file:'): |
308 |
if (isinstance(value, dict) and 'content' in value and 'content_type' in value): |
|
309 |
filename = key[5:] |
|
310 |
binary_content = base64.b64decode(value['content']) |
|
311 |
files.append(('files', (filename, binary_content, value['content_type']))) |
|
312 | 326 |
delete_keys.append(key) |
327 |
if value is None: |
|
328 |
continue |
|
329 |
filename = key[5:] |
|
330 |
if (isinstance(value, dict) and 'content' in value and 'content_type' in value): |
|
331 |
content = base64.b64decode(value['content']) |
|
332 |
content_type = value['content_type'] |
|
333 |
try: |
|
334 |
pdf_content = convert_to_pdf(content, content_type) |
|
335 |
except: |
|
336 |
files_failed_pdf_conversion.append(filename) |
|
337 |
else: |
|
338 |
files.append(('files', (filename, pdf_content, 'application.pdf'))) |
|
339 |
else: |
|
340 |
files_failed_pdf_conversion.append(filename) |
|
313 | 341 |
# Solis doesn't accept somes values or dict-of-values if there are empty |
314 | 342 |
# (for example is there is not "conjoint"): remove all these keys if a |
315 | 343 |
# specific "del:key_prefix":true entry exists (for example "del:conjoint") |
... | ... | |
340 | 368 |
integration_data['uidPiecesJointes'] = sendfiles.get('id') |
341 | 369 | |
342 | 370 |
response = self.request('asg/apa/integrationDemandeApa', data=integration_data) |
343 |
return {'data': response, 'sendfiles': sendfiles} |
|
371 |
return { |
|
372 |
'data': response, |
|
373 |
'files_sent': sendfiles, |
|
374 |
'files_failed_pdf_conversion': files_failed_pdf_conversion |
|
375 |
} |
|
344 | 376 | |
345 | 377 |
@endpoint(name='referential', perm='can_access', |
346 | 378 |
pattern=r'^(?P<module>[\w-]+)/(?P<name>[\w-]+)/$', |
setup.py | ||
---|---|---|
101 | 101 |
'feedparser', |
102 | 102 |
'lxml', |
103 | 103 |
'python-dateutil', |
104 |
'Pillow', |
|
104 | 105 |
], |
105 | 106 |
cmdclass={ |
106 | 107 |
'build': build, |
tests/test_solis.py | ||
---|---|---|
507 | 507 |
assert requests_post.call_args[1]['json']['demandeApa']['conjoint']['nom'] == 'Conjnom' |
508 | 508 |
assert resp.json['err'] == 0 |
509 | 509 | |
510 |
# add a file
|
|
510 |
# add files
|
|
511 | 511 |
requests_post.reset_mock() |
512 | 512 |
requests_post.side_effect = [ |
513 |
utils.FakedResponse(content='{"id": "foo", "nbFichiersAcceptes": 1}', status_code=200),
|
|
513 |
utils.FakedResponse(content='{"id": "foo", "nbFichiersAcceptes": 3}', status_code=200),
|
|
514 | 514 |
utils.FakedResponse(content='', status_code=204)] |
515 | 515 |
demande['file:etat_civil_001.pdf'] = { |
516 |
'content': 'Y29pbg==',
|
|
516 |
'content': 'JVBERmZha2U=',
|
|
517 | 517 |
'content_type': 'application/pdf', |
518 | 518 |
'filename': 'whatever.pdf', |
519 | 519 |
} |
520 |
demande['file:etat_civil_002.pdf'] = None |
|
520 |
demande['file:etat_civil_002.pdf'] = { |
|
521 |
# jpeg, will be converted to PDF |
|
522 |
'content': '/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCw' |
|
523 |
'kJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=', |
|
524 |
'content_type': 'image/jpeg', |
|
525 |
'filename': 'image.jpg', |
|
526 |
} |
|
527 |
demande['file:etat_civil_003.pdf'] = { |
|
528 |
# transparent png (RGBA), will be converted to RGB and then PDF |
|
529 |
'content': 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQ' |
|
530 |
'ABDQottAAAAABJRU5ErkJggg==', |
|
531 |
'content_type': 'image/png', |
|
532 |
'filename': 'image.png', |
|
533 |
} |
|
534 |
demande['file:etat_civil_004.pdf'] = { |
|
535 |
'content': 'Y29pbg==', # bad content, conversion will fail |
|
536 |
'content_type': 'image/png', |
|
537 |
'filename': 'image.png', |
|
538 |
} |
|
539 |
demande['file:etat_civil_005.pdf'] = { |
|
540 |
'content': 'Y29pbg==', |
|
541 |
'content_type': 'video/mp4', # not a image, cannot convert |
|
542 |
'filename': 'video.mp4', |
|
543 |
} |
|
544 |
demande['file:etat_civil_006.pdf'] = { |
|
545 |
'content_type': 'video/mp4', # no content, cannot convert |
|
546 |
} |
|
547 |
demande['file:etat_civil_007.pdf'] = None |
|
521 | 548 |
resp = app.post_json(url, params=demande, status=200) |
549 |
assert requests_post.call_count == 2 # post files + demandeApa |
|
550 |
sent_files = requests_post.call_args_list[0][1]['files'] |
|
551 |
assert len(sent_files) == 3 |
|
552 |
for file_ in sent_files: |
|
553 |
assert file_[1][1].startswith('%PDF') |
|
522 | 554 |
# file entries are removed from demandeApa JSON dict |
523 | 555 |
assert 'file:etat_civil_001.pdf' not in requests_post.call_args[1]['json']['demandeApa'] |
524 | 556 |
assert 'file:etat_civil_002.pdf' not in requests_post.call_args[1]['json']['demandeApa'] |
525 | 557 |
assert resp.json['err'] == 0 |
526 | 558 |
assert resp.json['data'] is None |
527 |
assert resp.json['sendfiles'] == {'id': 'foo', 'nbFichiersAcceptes': 1} |
|
559 |
assert resp.json['files_sent'] == {'id': 'foo', 'nbFichiersAcceptes': 3} |
|
560 |
assert set(resp.json['files_failed_pdf_conversion']) == set(['etat_civil_004.pdf', |
|
561 |
'etat_civil_005.pdf', |
|
562 |
'etat_civil_006.pdf']) |
|
528 | 563 | |
529 | 564 |
# invalid inputs |
530 | 565 |
requests_post.reset_mock() |
531 |
- |