api: handle submit of forms with date, file and map fields (#10059)
This commit is contained in:
parent
9f9599afee
commit
efd1af0770
|
@ -44,6 +44,38 @@ différentes pages définies dans le formulaire (mode flux).
|
|||
nom de variable) et les valeurs le contenu de ces champs.
|
||||
</p>
|
||||
|
||||
<list>
|
||||
<item>
|
||||
<p>
|
||||
Les champs de type simple tels que « Texte », « Texte long » ou
|
||||
« Courriel » sont des chaînes de caractères.
|
||||
</p>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<p>
|
||||
Les champs de type « Date » sont des chaînes de caractères au format
|
||||
ISO-8601, i.e. <code>YYYY-MM-DD</code>.
|
||||
</p>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<p>
|
||||
Les champs de type « Fichier » sont des dictionnaires contenant les clés
|
||||
<code>filename</code> pour le nom de fichier et <code>content</code> pour le
|
||||
contenu de celui-ci, encodé en base64.
|
||||
</p>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<p>
|
||||
Les champs de type « Carte » sont des dictionnaires contenant les clés
|
||||
<code>lat</code> pour la latitute en nombre décimal et <code>lon</code>
|
||||
pour la longitude en nombre décimal.
|
||||
</p>
|
||||
</item>
|
||||
</list>
|
||||
|
||||
<p>
|
||||
L'attribut <code>meta</code> est optionnel et contient une série de
|
||||
paramètres supplémentaires concernant le formulaire.
|
||||
|
|
|
@ -439,6 +439,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()
|
||||
|
@ -450,19 +453,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)
|
||||
|
@ -472,6 +486,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()
|
||||
|
||||
|
@ -492,6 +520,7 @@ def test_categories(pub):
|
|||
formdef.fields = []
|
||||
formdef.keywords = 'mobile, test'
|
||||
formdef.store()
|
||||
formdef.data_class().wipe()
|
||||
|
||||
formdef = FormDef()
|
||||
formdef.name = 'test 2'
|
||||
|
@ -499,6 +528,7 @@ def test_categories(pub):
|
|||
formdef.fields = []
|
||||
formdef.keywords = 'foobar'
|
||||
formdef.store()
|
||||
formdef.data_class().wipe()
|
||||
|
||||
resp = get_app(pub).get('/api/categories/')
|
||||
resp2 = get_app(pub).get('/categories', headers={'Accept': 'application/json'})
|
||||
|
@ -517,6 +547,7 @@ def test_categories_formdefs(pub):
|
|||
formdef2.category_id = None
|
||||
formdef2.fields = []
|
||||
formdef2.store()
|
||||
formdef2.data_class().wipe()
|
||||
|
||||
resp = get_app(pub).get('/api/categories/category/formdefs/')
|
||||
resp2 = get_app(pub).get('/category/json')
|
||||
|
|
|
@ -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 = data
|
||||
meta = json_input.get('meta') or {}
|
||||
if meta.get('backoffice-submission'):
|
||||
|
|
|
@ -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,13 @@ 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 +1776,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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue