Projet

Général

Profil

0001-json-export-content-of-uploads-7254.patch

Thomas Noël, 26 juin 2015 18:48

Télécharger (8,97 ko)

Voir les différences:

Subject: [PATCH] json: export content of uploads (#7254)

 help/fr/api-get.page         | 50 ++++++++++++++++++++++++++++++++++++++++++++
 tests/test_api.py            | 19 +++++++++++++----
 wcs/backoffice/management.py |  2 +-
 wcs/fields.py                |  8 ++++++-
 wcs/formdata.py              | 10 ++++++---
 wcs/qommon/form.py           |  6 ++++++
 6 files changed, 86 insertions(+), 9 deletions(-)
help/fr/api-get.page
71 71
}
72 72
</code>
73 73

  
74
<p>
75
Seuls les champs ayant un <em>nom de variable</em> sont exportés dans <code>fields</code>.
76
</p>
77

  
74 78
<note>
75 79
 <p>
76 80
  Il est bien sûr nécessaire de disposer des autorisations nécessaires pour
......
102 106
<section id="datatypes">
103 107
<title>Types de données</title>
104 108

  
109
<p>
110
Les données d'un formulaire sont placées dans le champs <code>fields</code> de
111
la réponse. Les champs de type simple tels que « Texte », « Texte long » ou
112
« Courriel » sont vus en tant que chaîne de caractères :
113
</p>
114

  
115
  <code mime="application/json">
116
    (...)
117
    "fields": {
118
        "email": "marc@example.net",
119
        "nom": "L.",
120
        "prenom": "Marc"
121
    },
122
    (...)
123
  </code>
124

  
125
<section>
126
<title>Représentation d'un champ « Fichier »</title>
127

  
128
<p>
129
Les champs de type « Fichier » sont exportés selon le schéma suivant :
130
</p>
131

  
132
  <code mime="application/json">
133
    (...)
134
    "fields": {
135
        "photo": {
136
            "filename": "exemple.txt",
137
            "content_type": "text/plain",
138
            "content": "Q2VjaSBuJ2VzdCBwYXMgdW4gZXhlbXBsZS4="
139
        }
140
    },
141
    (...)
142
  </code>
143

  
144
<p>
145
La valeur de <code>content</code> est le contenu du fichier, encodé en base64.
146
</p>
147

  
148
</section>
149

  
105 150
</section>
106 151

  
107 152
<section id="listing">
......
184 229
       https://www.example.net/api/forms/inscriptions/list?full=on</input>
185 230
</screen>
186 231

  
232
<p>
233
À noter que pour ne pas alourdir l'export en mode <code>full=on</code>, les
234
champs de type « Fichier » ne sont pas exportés.
235
</p>
236

  
187 237
</section>
188 238

  
189 239

  
tests/test_api.py
10 10
import time
11 11

  
12 12
from quixote import cleanup, get_publisher
13
from wcs.qommon.form import PicklableUpload
13 14
from wcs.users import User
14 15
from wcs.roles import Role
15 16
from wcs.formdef import FormDef
......
242 243
    formdef.fields = [
243 244
        fields.StringField(id='0', label='foobar', varname='foobar'),
244 245
        fields.StringField(id='1', label='foobar2'),
245
        fields.DateField(id='2', label='foobar3', varname='date'),]
246
        fields.DateField(id='2', label='foobar3', varname='date'),
247
        fields.FileField(id='3', label='foobar4', varname='file'),]
246 248
    formdef.store()
247 249

  
248 250
    formdata = formdef.data_class()()
249 251
    date = time.strptime('2014-01-20', '%Y-%m-%d')
250
    formdata.data = {'0': 'foo@localhost', '1': 'xxx', '2': date}
252
    upload = PicklableUpload('test.txt', 'text/plain', 'ascii')
253
    upload.receive(['base64me'])
254
    formdata.data = {'0': 'foo@localhost', '1': 'xxx', '2': date, '3': upload}
251 255
    formdata.user_id = local_user.id
252 256
    formdata.just_created()
253 257
    formdata.store()
254 258

  
255 259
    resp = get_app(pub).get(sign_uri('/test/%s/' % formdata.id, user=local_user))
256 260
    assert 'last_update_time' in resp.json
261
    assert len(resp.json['fields']) == 3 # foobar2 has no varname, not in json
257 262
    assert resp.json['user']['name'] == local_user.name
258 263
    assert resp.json['fields']['foobar'] == 'foo@localhost'
259 264
    assert resp.json['fields']['date'] == '2014-01-20'
260
    assert len(resp.json['fields']) == 2 # foobar2 has no varname, not in json
265
    assert resp.json['fields']['file']['content'] == 'YmFzZTY0bWU=' # base64('base64me')
266
    assert resp.json['fields']['file']['filename'] == 'test.txt'
267
    assert resp.json['fields']['file']['content_type'] == 'text/plain'
261 268

  
262 269
def test_myspace_forms(local_user):
263 270
    FormDef.wipe()
......
318 325
        fields.StringField(id='0', label='foobar', varname='foobar'),
319 326
        fields.ItemField(id='1', label='foobar3', varname='foobar3', type='item',
320 327
            items=['foo', 'bar', 'baz']),
328
        fields.FileField(id='2', label='foobar4', varname='file'),
321 329
        ]
322 330
    formdef.store()
323 331

  
......
327 335
    for i in range(30):
328 336
        formdata = data_class()
329 337
        date = time.strptime('2014-01-20', '%Y-%m-%d')
330
        formdata.data = {'0': 'FOO BAR %d' % i}
338
        upload = PicklableUpload('test.txt', 'text/plain', 'ascii')
339
        upload.receive(['base64me'])
340
        formdata.data = {'0': 'FOO BAR %d' % i, '2': upload}
331 341
        if i%4 == 0:
332 342
            formdata.data['1'] = 'foo'
333 343
            formdata.data['1_display'] = 'foo'
......
363 373
    assert len(resp.json) == 30
364 374
    assert 'receipt_time' in resp.json[0]
365 375
    assert 'fields' in resp.json[0]
376
    assert 'file' not in resp.json[0]['fields'] # no file export in full lists
366 377

  
367 378
    # check filtered results
368 379
    resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-foobar3=foo', user=local_user))
wcs/backoffice/management.py
861 861
            selected_filter, user=user, query=query, criterias=criterias,
862 862
            order_by=order_by)
863 863
        if get_request().form.get('full') == 'on':
864
            output = [json.loads(filled.export_to_json()) for filled in items]
864
            output = [json.loads(filled.export_to_json(include_files=False)) for filled in items]
865 865
        else:
866 866
            output = [{'id': filled.id,
867 867
                'url': filled.get_url(),
wcs/fields.py
17 17
import time
18 18
import random
19 19
import re
20
import base64
20 21
import xml.etree.ElementTree as ET
21 22

  
22 23
from quixote import get_request, get_publisher
......
709 710
        return ['%s' % value]
710 711

  
711 712
    def get_json_value(self, value):
712
        return {'field_id': self.id, 'filename': value.base_filename}
713
        return {
714
            'field_id': self.id,
715
            'filename': value.base_filename,
716
            'content_type': value.content_type or 'application/octet-stream',
717
            'content': base64.b64encode(value.get_content())
718
        }
713 719

  
714 720
    def perform_more_widget_changes(self, form, kwargs, edit = True):
715 721
        if not edit:
wcs/formdata.py
28 28
from qommon.substitution import Substitutions
29 29

  
30 30
from roles import Role
31
from fields import FileField
31 32

  
32 33

  
33 34
def get_dict_with_varnames(fields, data, formdata=None, varnames_only=False):
......
87 88
            del d[k]
88 89

  
89 90

  
90
def get_json_dict(fields, data):
91
def get_json_dict(fields, data, include_files=True):
91 92
    new_data = {}
92 93
    for field in fields:
93 94
        if not field.varname: # exports only named fields
94 95
            continue
96
        if not include_files and isinstance(field, FileField):
97
            continue
95 98
        if data is not None:
96 99
            value = data.get(field.id)
97 100
            if value and hasattr(field, 'get_json_value'):
......
527 530
                evo.parts = None
528 531
        self.store()
529 532

  
530
    def export_to_json(self):
533
    def export_to_json(self, include_files=True):
531 534
        data = {}
532 535
        data['id'] = '%s/%s' % (self.formdef.url_name, self.id)
533 536
        data['display_id'] = self.get_display_id()
......
553 556
        if user:
554 557
            data['user'] = {'id': user.id, 'name': user.display_name}
555 558

  
556
        data['fields'] = get_json_dict(self.formdef.fields, self.data)
559
        data['fields'] = get_json_dict(self.formdef.fields, self.data,
560
                include_files=include_files)
557 561

  
558 562
        data['workflow'] = {}
559 563
        wf_status = self.get_visible_status()
wcs/qommon/form.py
687 687
        # quack like UploadedFile
688 688
        return self.get_file_pointer()
689 689

  
690
    def get_content(self):
691
        if hasattr(self, 'qfilename'):
692
            filename = os.path.join(get_publisher().app_dir, 'uploads', self.qfilename)
693
            return file(filename).read()
694
        return None
695

  
690 696

  
691 697
class EmailWidget(StringWidget):
692 698
    HTML_TYPE = 'email'
693
-