fields: implement block field json serialization (#68964) #24
|
@ -14,6 +14,7 @@ from quixote import get_publisher
|
|||
from wcs import fields, qommon
|
||||
from wcs.api_access import ApiAccess
|
||||
from wcs.api_utils import sign_url
|
||||
from wcs.blocks import BlockDef
|
||||
from wcs.categories import Category
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.formdef import FormDef
|
||||
|
@ -21,7 +22,7 @@ from wcs.qommon.http_request import HTTPRequest
|
|||
from wcs.qommon.upload_storage import PicklableUpload
|
||||
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef
|
||||
|
||||
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app
|
||||
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login
|
||||
from .utils import sign_uri
|
||||
|
||||
|
||||
|
@ -965,3 +966,145 @@ def test_formdef_submit_structured(pub, local_user):
|
|||
}
|
||||
|
||||
data_class.wipe()
|
||||
|
||||
|
||||
def test_formdef_submit_structured_with_block_field(pub, local_user):
|
||||
BlockDef.wipe()
|
||||
block = BlockDef()
|
||||
block.name = 'foobar'
|
||||
block.fields = [
|
||||
fields.ItemField(
|
||||
id='0',
|
||||
label='foobar',
|
||||
varname='foobar',
|
||||
data_source={
|
||||
'type': 'json',
|
||||
'value': 'http://datasource.com',
|
||||
},
|
||||
),
|
||||
fields.ItemField(
|
||||
id='1',
|
||||
label='foobar2',
|
||||
varname='foobar2',
|
||||
data_source={
|
||||
'type': 'json',
|
||||
'value': 'http://datasource.com/{{form_var_foobar_foo}}',
|
||||
},
|
||||
),
|
||||
]
|
||||
block.store()
|
||||
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'test'
|
||||
formdef.fields = [
|
||||
fields.BlockField(id='0', label='test', varname='blockdata', type='block:foobar', max_items=3),
|
||||
]
|
||||
formdef.store()
|
||||
data_class = formdef.data_class()
|
||||
|
||||
def url():
|
||||
signed_url = sign_url(
|
||||
'http://example.net/api/formdefs/test/submit'
|
||||
'?format=json&orig=coucou&email=%s' % urllib.parse.quote(local_user.email),
|
||||
'1234',
|
||||
)
|
||||
return signed_url[len('http://example.net') :]
|
||||
|
||||
with responses.RequestsMock() as rsps:
|
||||
json_data = {
|
||||
"data": [
|
||||
{"id": 0, "text": "zéro", "foo": "bar"},
|
||||
{"id": 2, "text": "deux", "foo": "bar2"},
|
||||
]
|
||||
}
|
||||
rsps.get('http://datasource.com', json=json_data)
|
||||
rsps.get('http://datasource.com/bar', json=json_data)
|
||||
resp = get_app(pub).post_json(
|
||||
url(),
|
||||
{'data': {'0': [{'foobar': '0', 'foobar2': '2'}]}},
|
||||
)
|
||||
assert len(rsps.calls) == 2
|
||||
assert rsps.calls[0].request.url == 'http://datasource.com/'
|
||||
assert rsps.calls[1].request.url == 'http://datasource.com/bar'
|
||||
|
||||
formdata = data_class.get(resp.json['data']['id'])
|
||||
assert formdata.status == 'wf-new'
|
||||
blockdata = formdata.data['0']['data'][0]
|
||||
assert blockdata['0'] == '0'
|
||||
assert blockdata['0_display'] == 'zéro'
|
||||
assert blockdata['0_structured'] == {
|
||||
'id': 0,
|
||||
'text': 'zéro',
|
||||
'foo': 'bar',
|
||||
}
|
||||
assert blockdata['1'] == '2'
|
||||
assert blockdata['1_display'] == 'deux'
|
||||
assert blockdata['1_structured'] == {
|
||||
'id': 2,
|
||||
'text': 'deux',
|
||||
'foo': 'bar2',
|
||||
}
|
||||
|
||||
data_class.wipe()
|
||||
|
||||
|
||||
def test_formdef_import_export_block(pub, admin_user):
|
||||
BlockDef.wipe()
|
||||
block = BlockDef()
|
||||
block.name = 'foobar'
|
||||
block.fields = [
|
||||
fields.StringField(id='0', label='Foo', varname='foo'),
|
||||
fields.ItemField(id='1', label='Test', type='item', data_source={'type': 'foobar'}, varname='bar'),
|
||||
fields.StringField(id='2', label='Unnamed', required=False),
|
||||
fields.DateField(id='3', label='Date', type='date'),
|
||||
]
|
||||
block.store()
|
||||
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'test'
|
||||
formdef.fields = [
|
||||
fields.BlockField(id='0', label='test', varname='blockdata', type='block:foobar', max_items=3),
|
||||
]
|
||||
formdef.store()
|
||||
|
||||
formdef.data_class().wipe()
|
||||
formdata = formdef.data_class()()
|
||||
formdata.data = {
|
||||
'0': {
|
||||
'data': [
|
||||
{
|
||||
'0': 'plop',
|
||||
'1': '1',
|
||||
'1_display': 'foo',
|
||||
'1_structured': 'XXX',
|
||||
'2': 'yop',
|
||||
'3': time.strptime('2020-04-24', '%Y-%m-%d'),
|
||||
},
|
||||
{
|
||||
'0': 'hop',
|
||||
'1': '2',
|
||||
'1_display': 'bar',
|
||||
'1_structured': 'YYY',
|
||||
'2': None,
|
||||
'3': time.strptime('2020-04-24', '%Y-%m-%d'),
|
||||
},
|
||||
],
|
||||
'schema': {'0': 'string', '1': 'item', '2': 'string', '3': 'date'},
|
||||
},
|
||||
'0_display': 'test',
|
||||
}
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/api/forms/test/%s/' % formdata.id, status=200)
|
||||
|
||||
formdata_export = resp.json
|
||||
formdef.data_class().wipe()
|
||||
|
||||
resp = app.post_json('/api/formdefs/test/submit?orig=coucou', formdata_export)
|
||||
|
||||
new_formdata = formdef.data_class().select()[0]
|
||||
assert new_formdata.data == formdata.data
|
||||
|
|
|
@ -605,6 +605,15 @@ def test_backoffice_cards_import_data_from_json(pub):
|
|||
),
|
||||
}
|
||||
|
||||
BlockDef.wipe()
|
||||
block = BlockDef()
|
||||
block.name = 'foobar'
|
||||
block.fields = [
|
||||
fields.StringField(id='1', label='Foo', varname='foo'),
|
||||
fields.ItemField(id='2', label='List', varname='bar', data_source=data_source),
|
||||
]
|
||||
block.store()
|
||||
|
||||
CardDef.wipe()
|
||||
carddef = CardDef()
|
||||
carddef.name = 'test'
|
||||
|
@ -614,6 +623,7 @@ def test_backoffice_cards_import_data_from_json(pub):
|
|||
fields.ItemField(id='3', label='List', varname='item', data_source=data_source),
|
||||
fields.FileField(id='4', label='File', varname='file'),
|
||||
fields.DateField(id='5', label='Date', varname='date'),
|
||||
fields.BlockField(id='6', label='Block', varname='blockdata', type='block:foobar', max_items=3),
|
||||
]
|
||||
carddef.workflow_roles = {'_editor': user.roles[0]}
|
||||
carddef.backoffice_submission_roles = user.roles
|
||||
|
@ -636,6 +646,7 @@ def test_backoffice_cards_import_data_from_json(pub):
|
|||
'content': base64.encodebytes(b'%PDF-1.4 ...').decode(),
|
||||
},
|
||||
'date': '2022-07-19',
|
||||
'blockdata': [{'foo': 'another string', 'bar': '2'}],
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -661,6 +672,15 @@ def test_backoffice_cards_import_data_from_json(pub):
|
|||
'url': 'http://example.net/api/cards/test/1/download?hash=73491aa2c82f099162e376a1edfabd2043b8825262b9ab6fd974a36f0024c47c',
|
||||
},
|
||||
'date': '2022-07-19',
|
||||
'blockdata': 'foobar',
|
||||
'blockdata_raw': [
|
||||
{
|
||||
'bar': 'deux',
|
||||
'bar_raw': '2',
|
||||
'bar_structured': {'id': '2', 'more': 'bar', 'text': 'deux'},
|
||||
'foo': 'another string',
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import itertools
|
||||
import types
|
||||
import uuid
|
||||
import xml.etree.ElementTree as ET
|
||||
from contextlib import contextmanager
|
||||
|
@ -24,6 +25,7 @@ from quixote.html import htmltag, htmltext
|
|||
|
||||
from . import data_sources, fields
|
||||
from .categories import BlockCategory
|
||||
from .formdata import FormData
|
||||
from .qommon import _, misc
|
||||
from .qommon.form import CompositeWidget, WidgetList
|
||||
from .qommon.storage import StorableObject
|
||||
|
@ -246,6 +248,12 @@ class BlockDef(StorableObject):
|
|||
for field in self.fields or []:
|
||||
yield from field.i18n_scan(base_location=location)
|
||||
|
||||
def get_all_fields(self):
|
||||
return self.fields
|
||||
|
||||
def data_class(self):
|
||||
return types.ClassType('fake_formdata', (FormData,), {'_formdef': self})
|
||||
|
||||
|
||||
class BlockSubWidget(CompositeWidget):
|
||||
template_name = 'qommon/forms/widgets/block_sub.html'
|
||||
|
|
|
@ -3986,13 +3986,19 @@ class BlockField(WidgetField):
|
|||
self.block.fields,
|
||||
formdata=kwargs.get('formdata'),
|
||||
include_files=kwargs.get('include_file_content'),
|
||||
include_unnamed_fields=True,
|
||||
)
|
||||
)
|
||||
return result
|
||||
|
||||
def from_json_value(self, value):
|
||||
# TODO: add support for blockfield in json import
|
||||
return None
|
||||
from wcs.api import posted_json_data_to_formdata_data
|
||||
|
||||
result = []
|
||||
for subvalue_data in value or []:
|
||||
result.append(posted_json_data_to_formdata_data(self.block, subvalue_data))
|
||||
|
||||
return {'data': result, 'schema': {x.id: x.key for x in self.block.fields}}
|
||||
|
||||
def get_opendocument_node_value(self, value, formdata=None, **kwargs):
|
||||
node = ET.Element('{%s}span' % OD_NS['text'])
|
||||
|
|
Loading…
Reference in New Issue
Je verrais bien un test qui fasse le roundtrip export/import et assure que c'est ok. Là c'est un peu trop éparpillé avec juste dans tests/api/test_formdata.py::test_formdata une rapide vérification de ce qu'on reçoit en représentation json.
Yes, ajouté