general: add support for cards as data source (#35089)

This commit is contained in:
Frédéric Péters 2019-08-13 09:54:29 +02:00
parent f7f14c0095
commit 73da9459bd
4 changed files with 155 additions and 14 deletions

View File

@ -1248,7 +1248,7 @@ def test_form_edit_field_advanced(pub):
resp.body.index('<label for="form_data_source">Data Source</label>')
# start filling the "data source" field
resp.forms[0]['data_source$type'] = 'JSON URL'
resp.forms[0]['data_source$type'] = 'json'
resp.forms[0]['data_source$value'] = 'http://example.net'
resp = resp.forms[0].submit('submit')
resp = resp.follow()
@ -1379,6 +1379,72 @@ def test_form_edit_item_field(pub):
assert resp.location == 'http://example.net/backoffice/forms/1/fields/#itemId_1'
assert FormDef.get(1).fields[0].items == ['XXX']
def test_form_edit_item_field_data_source(pub):
create_superuser(pub)
create_role()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [fields.ItemField(id='1', label='1st field', type='item')]
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/1/fields/1/')
assert resp.form['data_source$type'].options == [
(u'None', True, u'None'),
(u'json', False, u'JSON URL'),
(u'jsonp', False, u'JSONP URL'),
(u'python', False, u'Python Expression')
]
resp = resp.form.submit('submit').follow()
data_source = NamedDataSource(name='Foobar')
data_source.data_source = {'type': 'formula', 'value': '[]'}
data_source.store()
resp = app.get('/backoffice/forms/1/fields/1/')
assert resp.form['data_source$type'].options == [
(u'None', True, u'None'),
(u'foobar', False, u'Foobar'),
(u'json', False, u'JSON URL'),
(u'jsonp', False, u'JSONP URL'),
(u'python', False, u'Python Expression')
]
resp.form['data_source$type'].value = 'foobar'
resp = resp.form.submit('submit').follow()
assert FormDef.get(formdef.id).fields[0].data_source == {'type': 'foobar'}
carddef = CardDef()
carddef.name = 'Baz'
carddef.store()
resp = app.get('/backoffice/forms/1/fields/1/')
assert resp.form['data_source$type'].options == [
(u'None', False, u'None'),
(u'foobar', True, u'Foobar'),
(u'json', False, u'JSON URL'),
(u'jsonp', False, u'JSONP URL'),
(u'python', False, u'Python Expression')
]
carddef.digest_template = 'plop'
carddef.store()
resp = app.get('/backoffice/forms/1/fields/1/')
assert resp.form['data_source$type'].options == [
(u'None', False, u'None'),
(u'carddef:%s' % carddef.url_name, False, u'Baz'),
(u'foobar', True, u'Foobar'),
(u'json', False, u'JSON URL'),
(u'jsonp', False, u'JSONP URL'),
(u'python', False, u'Python Expression')
]
resp.form['data_source$type'].value = 'carddef:%s' % carddef.url_name
resp = resp.form.submit('submit').follow()
assert FormDef.get(formdef.id).fields[0].data_source == {'type': 'carddef:%s' % carddef.url_name}
def test_form_edit_items_field(pub):
create_superuser(pub)
create_role()
@ -4584,7 +4650,7 @@ def test_data_sources_new(pub):
resp = resp.click('New Data Source')
resp.forms[0]['name'] = 'a new data source'
resp.forms[0]['description'] = 'description of the data source'
resp.forms[0]['data_source$type'] = 'Python Expression'
resp.forms[0]['data_source$type'] = 'python'
resp = resp.forms[0].submit('data_source$apply')
resp.forms[0]['data_source$value'] = repr(
[{'id': '1', 'text': 'un'}, {'id': '2', 'text': 'deux'}])
@ -4605,7 +4671,7 @@ def test_data_sources_new(pub):
resp = resp.click('New Data Source')
resp.forms[0]['name'] = 'an other data source'
resp.forms[0]['description'] = 'description of the data source'
resp.forms[0]['data_source$type'] = 'Python Expression'
resp.forms[0]['data_source$type'] = 'python'
resp = resp.forms[0].submit('data_source$apply')
resp.forms[0]['data_source$value'] = repr(
[{'id': '1', 'text': 'un'}, {'id': '2', 'text': 'deux'}])

View File

@ -23,6 +23,7 @@ from quixote.http_request import Upload as QuixoteUpload
from wcs.qommon.emails import docutils
from wcs.qommon.form import UploadedFile
from wcs.qommon.ident.password_accounts import PasswordAccount
from wcs.carddef import CardDef
from wcs.formdef import FormDef
from wcs.workflows import (Workflow, EditableWorkflowStatusItem,
DisplayMessageWorkflowStatusItem, WorkflowBackofficeFieldsFormDef,
@ -4886,6 +4887,46 @@ def test_form_page_profile_verified_radio_item_prefill(pub):
assert 'Check values then click submit.' in resp.body
assert resp.form['f0'].value == 'foo@localhost' # it is reverted
def test_item_field_from_cards(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
carddef = CardDef()
carddef.name = 'items'
carddef.digest_template = '{{form_var_name}}'
carddef.fields = [
fields.StringField(id='0', label='string', varname='name'),
fields.StringField(id='1', label='string', varname='attr'),
]
carddef.store()
for i, value in enumerate(['foo', 'bar', 'baz']):
carddata = carddef.data_class()()
carddata.data = {
'0': value,
'1': 'attr%s' % i,
}
carddata.just_created()
carddata.store()
ds = {'type': 'carddef:%s' % carddef.url_name}
formdef.fields = [fields.ItemField(id='0', label='string', type='item',
data_source=ds, display_disabled_items=True)]
formdef.store()
resp = get_app(pub).get('/test/')
assert resp.form['f0'].options == [
(u'2', False, u'bar'),
(u'3', False, u'baz'),
(u'1', False, u'foo'),
]
resp.form['f0'] = '2'
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submit
assert formdef.data_class().select()[0].data['0'] == '2'
assert formdef.data_class().select()[0].data['0_display'] == 'bar'
assert formdef.data_class().select()[0].data['0_structured']['name'] == 'bar'
def test_item_field_with_disabled_items(http_requests, pub):
user = create_user(pub)
formdef = create_formdef()

View File

@ -29,3 +29,16 @@ class CardData(FormData):
self._formdef = None
return self._formdef
formdef = property(get_formdef)
def get_data_source_structured_item(self):
item = {
'id': self.id,
'text': self.digest,
}
for field in self.formdef.get_all_fields():
if not field.varname:
continue
value = self.data and self.data.get(field.id)
if isinstance(value, basestring):
item[field.varname] = value
return item

View File

@ -55,14 +55,21 @@ class DataSourceSelectionWidget(CompositeWidget):
if not value:
value = {}
options = [('none', _('None')),
('formula', _('Python Expression')),
('json', _('JSON URL'))]
if allow_jsonp:
options.append(('jsonp', _('JSONP URL')))
options = []
if allow_named_sources:
options.extend([(x.slug, x.name) for x in
NamedDataSource.select(order_by='name')])
options.extend([(x.slug, x.name, x.slug) for x in NamedDataSource.select()])
from wcs.carddef import CardDef
options.extend([
('carddef:%s' % x.url_name, x.name, 'carddef:%s' % x.url_name)
for x in CardDef.select(lightweight=True, ignore_errors=True)
if x.digest_template])
options.sort(key=lambda x: qommon.misc.simplify(x[1]))
options.insert(0, (None, _('None'), None))
options.append(('json', _('JSON URL'), 'json'))
if allow_jsonp:
options.append(('jsonp', _('JSONP URL'), 'jsonp'))
options.append(('formula', _('Python Expression'), 'python'))
self.add(SingleSelectWidget, 'type', options=options, value=value.get('type'),
attrs={'data-dynamic-display-parent': 'true'})
@ -73,10 +80,7 @@ class DataSourceSelectionWidget(CompositeWidget):
self.add(StringWidget, 'value', value=value.get('value'), size=80,
attrs={'data-dynamic-display-child-of': 'data_source$type',
'data-dynamic-display-value-in': '|'.join(
[_('Python Expression'),
_('JSON URL'),
_('JSONP URL')])})
'data-dynamic-display-value-in': 'json|jsonp|python'})
self._parsed = False
@ -112,6 +116,17 @@ def get_items(data_source, include_disabled=False, mode=None):
def get_structured_items(data_source, mode=None):
cache_duration = 0
if data_source.get('type') and data_source.get('type').startswith('carddef:'):
# cards
from wcs.carddef import CardDef
carddef = CardDef.get_by_urlname(data_source['type'][8:])
items = [x.get_data_source_structured_item()
for x in carddef.data_class().select()
if not x.is_draft()]
items.sort(key=lambda x: qommon.misc.simplify(x['text']))
return items
if data_source.get('type') not in ('json', 'jsonp', 'formula'):
# named data source
named_data_source = NamedDataSource.get_by_slug(data_source['type'])
@ -214,6 +229,8 @@ def get_real(data_source):
ds_type = data_source.get('type')
if ds_type in ('json', 'jsonp', 'formula'):
return data_source
if ds_type and ds_type.startswith('carddef:'):
return data_source
return NamedDataSource.get_by_slug(ds_type).data_source
@ -225,6 +242,10 @@ def get_object(data_source):
named_data_source = NamedDataSource()
named_data_source.data_source = data_source
return named_data_source
if ds_type.startswith('carddef:'):
named_data_source = NamedDataSource()
named_data_source.data_source = data_source
return named_data_source
return NamedDataSource.get_by_slug(ds_type)