Projet

Général

Profil

0001-solis-try-to-convert-files-to-PDF-21997.patch

Thomas Noël, 20 février 2018 15:52

Télécharger (7,24 ko)

Voir les différences:

Subject: [PATCH] solis: try to convert files to PDF (#21997)

 debian/control                  |  3 ++-
 passerelle/apps/solis/models.py | 32 +++++++++++++++++++++++++++++---
 setup.py                        |  1 +
 tests/test_solis.py             | 40 +++++++++++++++++++++++++++++++++++-----
 4 files changed, 67 insertions(+), 9 deletions(-)
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 326
                if (isinstance(value, dict) and 'content' in value and 'content_type' in value):
309 327
                    filename = key[5:]
310
                    binary_content = base64.b64decode(value['content'])
311
                    files.append(('files', (filename, binary_content, value['content_type'])))
328
                    try:
329
                        pdf_content = convert_to_pdf(base64.b64decode(value['content']),
330
                                                 value['content_type'])
331
                        files.append(('files', (filename, pdf_content, 'application.pdf')))
332
                    except:
333
                        files_failed_pdf_conversion.append(filename)
312 334
                delete_keys.append(key)
313 335
            # Solis doesn't accept somes values or dict-of-values if there are empty
314 336
            # (for example is there is not "conjoint"): remove all these keys if a
......
340 362
            integration_data['uidPiecesJointes'] = sendfiles.get('id')
341 363

  
342 364
        response = self.request('asg/apa/integrationDemandeApa', data=integration_data)
343
        return {'data': response, 'sendfiles': sendfiles}
365
        return {
366
            'data': response,
367
            'files_sent': sendfiles,
368
            'files_failed_pdf_conversion': files_failed_pdf_conversion
369
        }
344 370

  
345 371
    @endpoint(name='referential', perm='can_access',
346 372
              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'] = None
521 545
        resp = app.post_json(url, params=demande, status=200)
546
        assert requests_post.call_count == 2  # post files + demandeApa
547
        sent_files = requests_post.call_args_list[0][1]['files']
548
        assert len(sent_files) == 3
549
        for file_ in sent_files:
550
            assert file_[1][1].startswith('%PDF')
522 551
        # file entries are removed from demandeApa JSON dict
523 552
        assert 'file:etat_civil_001.pdf' not in requests_post.call_args[1]['json']['demandeApa']
524 553
        assert 'file:etat_civil_002.pdf' not in requests_post.call_args[1]['json']['demandeApa']
525 554
        assert resp.json['err'] == 0
526 555
        assert resp.json['data'] is None
527
        assert resp.json['sendfiles'] == {'id': 'foo', 'nbFichiersAcceptes': 1}
556
        assert resp.json['files_sent'] == {'id': 'foo', 'nbFichiersAcceptes': 3}
557
        assert set(resp.json['files_failed_pdf_conversion']) == set(['etat_civil_004.pdf', 'etat_civil_005.pdf'])
528 558

  
529 559
        # invalid inputs
530 560
        requests_post.reset_mock()
531
-