From b4bcc8f0820b128294a5a37e85462df9d87d718e Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 23 Feb 2016 15:17:23 +0100 Subject: [PATCH] api: handle submit of forms with date, file and map fields (#10059) --- help/fr/api-fill.page | 49 ++++++++++++++++++++++++++++++++++++++++++++++ tests/test_api.py | 54 ++++++++++++++++++++++++++++++++++++++------------- wcs/api.py | 7 +++++++ wcs/fields.py | 25 ++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 13 deletions(-) diff --git a/help/fr/api-fill.page b/help/fr/api-fill.page index 98bf6de..522f991 100644 --- a/help/fr/api-fill.page +++ b/help/fr/api-fill.page @@ -46,6 +46,55 @@ différentes pages définies dans le formulaire (mode flux).

+ Les champs de type simple tels que « Texte », « Texte long » ou + « Courriel » sont des chaînes de caractères. +

+ +

+ Les champs de type « Date » sont des chaînes de caractères au format + ISO-8601, i.e. YYYY-MM-DD. +

+ +

+ Les champs de type « Fichier » » sont des dictionnaires contenant les clés + suivantes. +

+ + + filename le nom du fichier + content le contenu du fichier encodé en base64 + + +

+ Les champs de type « Fichier » » sont des dictionnaires contenant les clés + suivantes. +

+ + + + filename le nom du fichier en chaîne de caractère + + + content le contenu du fichier en chaîne de caractère encodé en + base64 + + + +

+ Les champs de type « Carte » » sont des dictionnaires contenant les clés + suivantes. +

+ + + + lat la latitute en nombre décimal + + + lon la longitude en nombre décimal + + + +

L'attribut meta est optionnel et contient une série de paramètres supplémentaires concernant le formulaire.

diff --git a/tests/test_api.py b/tests/test_api.py index e4e9f0e..99a9476 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -440,6 +440,9 @@ def test_formdef_submit_with_varname(pub, local_user): data_source={'type': 'foobar'}), fields.ItemField(id='2', label='foobar2', varname='foobar2', data_source={'type': 'foobar_jsonp'}), + fields.DateField(id='3', label='foobar3', varname='date'), + fields.FileField(id='4', label='foobar4', varname='file'), + fields.MapField(id='5', label='foobar5', varname='map'), ] formdef.store() data_class = formdef.data_class() @@ -451,19 +454,30 @@ def test_formdef_submit_with_varname(pub, local_user): signed_url = sign_url('http://example.net/api/formdefs/test/submit' + '?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), '1234') url = signed_url[len('http://example.net'):] - resp = get_app(pub).post_json(url, - {'data': - {'foobar0': 'xxx', - 'foobar1': '1', - 'foobar1_structured': { - 'id': '1', - 'text': 'foo', - 'more': 'XXX', - }, - 'foobar2': 'bar', - 'foobar2_raw': '10', - } - }) + payload = { + 'data': + { + 'foobar0': 'xxx', + 'foobar1': '1', + 'foobar1_structured': { + 'id': '1', + 'text': 'foo', + 'more': 'XXX', + }, + 'foobar2': 'bar', + 'foobar2_raw': '10', + 'date': '1970-01-01', + 'file': { + 'filename': 'test.txt', + 'content': base64.b64encode('test'), + }, + 'map': { + 'lat': 1.5, + 'lon': 2.25, + }, + } + } + resp = get_app(pub).post_json(url, payload) assert resp.json['err'] == 0 assert data_class.get(resp.json['data']['id']).status == 'wf-new' assert data_class.get(resp.json['data']['id']).user_id == str(local_user.id) @@ -473,6 +487,20 @@ def test_formdef_submit_with_varname(pub, local_user): assert data_class.get(resp.json['data']['id']).data['1_structured'] == source[0] assert data_class.get(resp.json['data']['id']).data['2'] == '10' assert data_class.get(resp.json['data']['id']).data['2_display'] == 'bar' + assert data_class.get(resp.json['data']['id']).data['3'] == time.struct_time((1970, 1, 1, 0, 0, + 0, 3, 1, -1)) + + assert data_class.get(resp.json['data']['id']).data['4'].orig_filename == 'test.txt' + assert data_class.get(resp.json['data']['id']).data['4'].get_content() == 'test' + assert data_class.get(resp.json['data']['id']).data['5'] == '1.5;2.25' + # test bijectivity + assert (formdef.fields[3].get_json_value(data_class.get(resp.json['data']['id']).data['3']) == + payload['data']['date']) + for k in payload['data']['file']: + data = data_class.get(resp.json['data']['id']).data['4'] + assert formdef.fields[4].get_json_value(data)[k] == payload['data']['file'][k] + assert (formdef.fields[5].get_json_value(data_class.get(resp.json['data']['id']).data['5']) == + payload['data']['map']) data_class.wipe() diff --git a/wcs/api.py b/wcs/api.py index 3617452..6fb723c 100644 --- a/wcs/api.py +++ b/wcs/api.py @@ -243,6 +243,13 @@ class ApiFormdefDirectory(Directory): data[field.id] = data.pop(field.varname) if field.store_structured_value and structured in data: data['%s_structured' % field.id] = data.pop(structured) + # parse special fields + for field in self.formdef.fields: + if not hasattr(field, 'from_json_value'): + continue + if not field.id in data: + continue + data[field.id] = field.from_json_value(data[field.id]) formdata.data = json_input['data'] meta = json_input.get('meta') or {} if meta.get('backoffice-submission'): diff --git a/wcs/fields.py b/wcs/fields.py index e0e4395..570c169 100644 --- a/wcs/fields.py +++ b/wcs/fields.py @@ -763,6 +763,19 @@ class FileField(WidgetField): 'content': base64.b64encode(value.get_content()) } + def from_json_value(self, value): + if 'filename' in value and 'content' in value: + content = base64.b64decode(value['content']) + content_type = value.get('content_type', 'application/octet-stream') + if content_type.startswith('text/'): + charset = 'utf-8' + else: + charset = None + upload = PicklableUpload(value['filename'], content_type, charset) + upload.receive([content]) + return upload + return None + def perform_more_widget_changes(self, form, kwargs, edit = True): if not edit: value = get_request().get_field(self.field_key) @@ -915,6 +928,12 @@ class DateField(WidgetField): except TypeError: return '' + def from_json_value(self, value): + try: + return time.strptime(value, '%Y-%m-%d') + except (TypeError, ValueError): + return None + register_field_class(DateField) @@ -1756,6 +1775,12 @@ class MapField(WidgetField): return None return {'lat': lat, 'lon': lon} + def from_json_value(self, value): + if 'lat' in value and 'lon' in value: + return '%s;%s' % (float(value['lat']), float(value['lon'])) + else: + return None + register_field_class(MapField) -- 2.1.4