backoffice: allow fields of BlockField in csv import for cards (#72799) #91
|
@ -617,6 +617,123 @@ def test_backoffice_cards_import_data_csv_no_backoffice_fields(pub):
|
|||
assert carddef.data_class().count() == 2
|
||||
|
||||
|
||||
def test_backoffice_cards_import_data_csv_blockfield(pub):
|
||||
user = create_user(pub)
|
||||
|
||||
BlockDef.wipe()
|
||||
block = BlockDef()
|
||||
block.name = 'foobar'
|
||||
block.fields = [
|
||||
fields.StringField(id='1', label='Foo', varname='foo'),
|
||||
fields.StringField(id='2', label='Bar', varname='bar'),
|
||||
]
|
||||
block.digest_template = '{{ block_var_foo }}'
|
||||
block.store()
|
||||
|
||||
CardDef.wipe()
|
||||
carddef = CardDef()
|
||||
carddef.backoffice_submission_roles = user.roles
|
||||
carddef.name = 'test'
|
||||
carddef.fields = [
|
||||
fields.StringField(id='1', label='String'),
|
||||
fields.BlockField(id='2', label='Block', varname='blockdata', type='block:foobar', max_items=2),
|
||||
]
|
||||
carddef.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
sample_resp = app.get('/backoffice/data/test/data-sample-csv')
|
||||
assert sample_resp.text.splitlines()[0] == '"String","Block"'
|
||||
assert (
|
||||
sample_resp.text.splitlines()[1]
|
||||
== '"value","will be ignored - type Field Block (foobar) not supported"'
|
||||
)
|
||||
|
||||
# block is required, error
|
||||
data = b'''\
|
||||
"String","Block"
|
||||
"test","item1"
|
||||
"test","item2"
|
||||
'''
|
||||
resp = app.get('/backoffice/data/test/import-file')
|
||||
resp.forms[0]['file'] = Upload('test.csv', data, 'text/csv')
|
||||
resp = resp.forms[0].submit()
|
||||
assert 'Block is required but cannot be filled from CSV.' in resp
|
||||
|
||||
# block is not required
|
||||
carddef.fields[1].required = False
|
||||
carddef.store()
|
||||
resp = app.get('/backoffice/data/test/import-file')
|
||||
resp.forms[0]['file'] = Upload('test.csv', data, 'text/csv')
|
||||
resp = resp.forms[0].submit().follow()
|
||||
assert carddef.data_class().count() == 2
|
||||
carddata1, carddata2 = carddef.data_class().select(order_by='id')
|
||||
assert carddata1.data == {'1': 'test', '2': None, '2_display': None}
|
||||
assert carddata2.data == {'1': 'test', '2': None, '2_display': None}
|
||||
|
||||
# required, but max_items = 1
|
||||
carddef.fields[1].required = True
|
||||
carddef.fields[1].max_items = 1
|
||||
carddef.store()
|
||||
sample_resp = app.get('/backoffice/data/test/data-sample-csv')
|
||||
assert sample_resp.text.splitlines()[0] == '"String","Block - Foo","Block - Bar"'
|
||||
assert sample_resp.text.splitlines()[1] == '"value","value","value"'
|
||||
data = b'''\
|
||||
"String","Block - Foo","Block - Bar"
|
||||
"test","foo1","bar1"
|
||||
'''
|
||||
resp = app.get('/backoffice/data/test/import-file')
|
||||
resp.forms[0]['file'] = Upload('test.csv', data, 'text/csv')
|
||||
resp = resp.forms[0].submit().follow()
|
||||
assert carddef.data_class().count() == 3
|
||||
carddata1, carddata2, carddata3 = carddef.data_class().select(order_by='id')
|
||||
assert carddata1.data == {'1': 'test', '2': None, '2_display': None}
|
||||
assert carddata2.data == {'1': 'test', '2': None, '2_display': None}
|
||||
assert carddata3.data == {
|
||||
'1': 'test',
|
||||
'2': {'data': [{'1': 'foo1', '2': 'bar1'}], 'schema': {'1': 'string', '2': 'string'}},
|
||||
'2_display': 'foo1',
|
||||
}
|
||||
|
||||
# not required, with another BlockField
|
||||
carddef.fields[1].required = False
|
||||
carddef.fields.append(
|
||||
fields.BlockField(id='3', label='Block2', varname='blockdata2', type='block:foobar', max_items=1)
|
||||
)
|
||||
carddef.store()
|
||||
|
||||
sample_resp = app.get('/backoffice/data/test/data-sample-csv')
|
||||
assert (
|
||||
sample_resp.text.splitlines()[0]
|
||||
== '"String","Block - Foo","Block - Bar","Block2 - Foo","Block2 - Bar"'
|
||||
)
|
||||
assert sample_resp.text.splitlines()[1] == '"value","value","value","value","value"'
|
||||
data = b'''\
|
||||
"String","Block - Foo","Block - Bar","Block2 - Foo","Block2 - Bar"
|
||||
"test","foo2","","foo","bar"
|
||||
'''
|
||||
resp = app.get('/backoffice/data/test/import-file')
|
||||
resp.forms[0]['file'] = Upload('test.csv', data, 'text/csv')
|
||||
resp = resp.forms[0].submit().follow()
|
||||
assert carddef.data_class().count() == 4
|
||||
carddata1, carddata2, carddata3, carddata4 = carddef.data_class().select(order_by='id')
|
||||
assert carddata1.data == {'1': 'test', '2': None, '2_display': None, '3': None, '3_display': None}
|
||||
assert carddata2.data == {'1': 'test', '2': None, '2_display': None, '3': None, '3_display': None}
|
||||
assert carddata3.data == {
|
||||
'1': 'test',
|
||||
'2': {'data': [{'1': 'foo1', '2': 'bar1'}], 'schema': {'1': 'string', '2': 'string'}},
|
||||
'2_display': 'foo1',
|
||||
'3': None,
|
||||
'3_display': None,
|
||||
}
|
||||
assert carddata4.data == {
|
||||
'1': 'test',
|
||||
'2': {'data': [{'1': 'foo2'}], 'schema': {'1': 'string'}},
|
||||
'2_display': 'foo2',
|
||||
'3': {'data': [{'1': 'foo', '2': 'bar'}], 'schema': {'1': 'string', '2': 'string'}},
|
||||
'3_display': 'foo',
|
||||
}
|
||||
|
||||
|
||||
def test_backoffice_cards_import_data_from_json(pub):
|
||||
user = create_user(pub)
|
||||
|
||||
|
|
|
@ -48,7 +48,21 @@ def get_import_csv_fields(carddef):
|
|||
data['_user'] = value
|
||||
|
||||
# skip non-data fields
|
||||
csv_fields = [x for x in (carddef.fields or []) if isinstance(x, fields.WidgetField)]
|
||||
csv_fields = []
|
||||
for field in carddef.iter_fields(include_block_fields=True, with_backoffice_fields=False):
|
||||
if not isinstance(field, fields.WidgetField):
|
||||
continue
|
||||
if field.key == 'block' and field.max_items == 1:
|
||||
# ignore BlockField if only one item
|
||||
continue
|
||||
block_field = getattr(field, 'block_field', None)
|
||||
if block_field:
|
||||
if block_field.max_items > 1:
|
||||
# ignore fields of BlockField if more than one item
|
||||
continue
|
||||
# complete field label
|
||||
field.label = '%s - %s' % (block_field.label, field.label)
|
||||
csv_fields.append(field)
|
||||
if carddef.user_support == 'optional':
|
||||
return [UserField()] + csv_fields
|
||||
return csv_fields
|
||||
|
@ -416,6 +430,7 @@ class ImportFromCsvAfterJob(AfterJob):
|
|||
for csv_line in self.kwargs['data_lines']:
|
||||
data_instance = carddata_class()
|
||||
data_instance.data = {}
|
||||
block_data = {}
|
||||
|
||||
for i, field in enumerate(carddef_fields):
|
||||
value = csv_line[i].strip()
|
||||
|
@ -425,7 +440,21 @@ class ImportFromCsvAfterJob(AfterJob):
|
|||
# skip unsupported field types
|
||||
if field.convert_value_from_str is None:
|
||||
continue
|
||||
field.set_value(data_instance.data, field.convert_value_from_str(value))
|
||||
block_field = getattr(field, 'block_field', None)
|
||||
if not block_field:
|
||||
field.set_value(data_instance.data, field.convert_value_from_str(value))
|
||||
continue
|
||||
|
||||
# field in a BlockField
|
||||
if not block_data.get(block_field.id):
|
||||
block_data[block_field.id] = {'data': [{}], 'schema': {}, 'block_field': block_field}
|
||||
field.set_value(block_data[block_field.id]['data'][0], field.convert_value_from_str(value))
|
||||
block_data[block_field.id]['schema'][field.id] = field.key
|
||||
|
||||
# fill BlockFields
|
||||
for data in block_data.values():
|
||||
block_field = data.pop('block_field')
|
||||
block_field.set_value(data_instance.data, data)
|
||||
|
||||
user_value = data_instance.data.pop('_user', None)
|
||||
data_instance.user = self.user_lookup(user_value)
|
||||
|
|
|
@ -503,7 +503,7 @@ class FormDef(StorableObject):
|
|||
def get_all_fields(self):
|
||||
return (self.fields or []) + self.workflow.get_backoffice_fields()
|
||||
|
||||
def iter_fields(self, include_block_fields=False):
|
||||
def iter_fields(self, include_block_fields=False, with_backoffice_fields=True):
|
||||
def _iter_fields(fields, block_field=None):
|
||||
for field in fields:
|
||||
# add contextual_id/contextual_varname attributes
|
||||
|
@ -531,7 +531,11 @@ class FormDef(StorableObject):
|
|||
yield from _iter_fields(field.block.fields, block_field=field)
|
||||
field._block = None # reset cache
|
||||
|
||||
yield from _iter_fields(self.get_all_fields())
|
||||
if with_backoffice_fields:
|
||||
fields = self.get_all_fields()
|
||||
else:
|
||||
fields = self.fields or []
|
||||
yield from _iter_fields(fields)
|
||||
|
||||
def get_widget_fields(self):
|
||||
return [field for field in self.fields or [] if isinstance(field, fields.WidgetField)]
|
||||
|
|
Loading…
Reference in New Issue