wcs/tests/backoffice_pages/test_carddata.py

1263 lines
40 KiB
Python

import base64
import datetime
import json
import os
import uuid
import pytest
from webtest import Upload
from wcs import fields
from wcs.blocks import BlockDef
from wcs.carddef import CardDef
from wcs.categories import CardDefCategory
from wcs.formdef import FormDef
from wcs.qommon.http_request import HTTPRequest
from wcs.wf.form import WorkflowFormFieldsFormDef
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login
from .test_all import create_superuser, create_user
@pytest.fixture
def pub(emails):
pub = create_temporary_pub()
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
pub.set_app_dir(req)
pub.cfg['identification'] = {'methods': ['password']}
pub.cfg['language'] = {'language': 'en'}
pub.write_cfg()
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
fd.write(
'''
[api-secrets]
coucou = 1234
'''
)
return pub
def teardown_module(module):
clean_temporary_pub()
def test_admin_card_page(pub):
create_superuser(pub)
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = []
carddef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/data/foo/')
assert 'backoffice/cards/1/' in resp
assert 'backoffice/workflows/_carddef_default/' in resp
def test_carddata_management(pub):
CardDef.wipe()
user = create_user(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/forms')
assert not resp.pyquery('#sidepage-menu .icon-data')
# no Cards entry in menu, even for admin
user.is_admin = True
user.store()
resp = app.get('/backoffice/management/forms')
assert not resp.pyquery('#sidepage-menu .icon-data')
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.StringField(id='1', label='Test', type='string', varname='foo'),
fields.StringField(
id='2',
label='Condi',
type='string',
varname='bar',
required=True,
condition={'type': 'django', 'value': 'form_var_foo == "ok"'},
),
]
carddef.store()
carddef.data_class().wipe()
# Cards entry for global admin
resp = app.get('/backoffice/management/forms')
assert resp.pyquery('#sidepage-menu .icon-data')
assert 'Cards' in resp.text
resp = app.get('/backoffice/data/')
# Cards entry for section admin
user.is_admin = False
user.store()
pub.cfg['admin-permissions'] = {'cards': user.roles}
pub.write_cfg()
resp = app.get('/backoffice/management/forms')
assert resp.pyquery('#sidepage-menu .icon-data')
resp = app.get('/backoffice/data/')
# get back to being a normal user, no Cards entry
pub.cfg['admin-permissions'] = {}
pub.write_cfg()
user.is_admin = False
user.store()
resp = app.get('/backoffice/management/forms')
assert not resp.pyquery('#sidepage-menu .icon-data')
resp = app.get('/backoffice/data/', status=403)
# add specific roles
carddef.backoffice_submission_roles = user.roles
carddef.store()
resp = app.get('/backoffice/management/forms')
assert resp.pyquery('#sidepage-menu .icon-data')
resp = app.get('/backoffice/data/')
carddef.backoffice_submission_roles = None
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.store()
resp = app.get('/backoffice/management/forms')
assert resp.pyquery('#sidepage-menu .icon-data')
resp = app.get('/backoffice/data/')
resp = app.get('/backoffice/data/')
resp = resp.click('foo')
assert 'Add' not in resp.text
carddef.backoffice_submission_roles = user.roles
carddef.store()
resp = app.get('/backoffice/data/')
resp = resp.click('foo')
assert resp.text.count('<tr') == 1 # header
assert 'Add' in resp.text
resp = resp.click('Add')
resp.form['f1'] = 'blah'
live_url = resp.html.find('form').attrs['data-live-url']
assert '/backoffice/data/foo/add/live' in live_url
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert not live_resp.json['result']['2']['visible']
resp.form['f1'] = 'ok'
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
resp.form['f2'] = 'blah'
resp = resp.form.submit('submit')
assert resp.location.endswith('/backoffice/data/foo/1/')
resp = resp.follow()
assert 'Edit Card' in resp.text
assert 'Delete Card' in resp.text
carddata = carddef.data_class().select()[0]
assert carddata.data == {'1': 'ok', '2': 'blah'}
assert carddata.user_id is None
assert carddata.submission_agent_id == str(user.id)
assert carddata.evolution[0].who == str(user.id)
assert 'Original Submitter' not in resp.text
resp = app.get('/backoffice/data/')
resp = resp.click('foo')
assert resp.text.count('<tr') == 2 # header + row of data
resp = resp.click('Add')
resp = resp.form.submit('cancel')
assert resp.location.endswith('/backoffice/data/foo/')
def test_carddata_management_categories(pub):
user = create_user(pub)
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = []
carddef.backoffice_submission_roles = None
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.store()
carddef2 = CardDef()
carddef2.name = 'card title 2'
carddef2.fields = []
carddef2.backoffice_submission_roles = None
carddef2.workflow_roles = {'_editor': user.roles[0]}
carddef2.store()
CardDefCategory.wipe()
cat = CardDefCategory(name='Foo')
cat.store()
cat2 = CardDefCategory(name='Bar')
cat2.store()
app = login(get_app(pub))
resp = app.get('/backoffice/data/')
assert '<h3>Misc</h3>' not in resp.text
assert '<h3>Foo</h3>' not in resp.text
assert '<h3>Bar</h3>' not in resp.text
carddef.category = cat2
carddef.store()
resp = app.get('/backoffice/data/')
assert '<h3>Misc</h3>' in resp.text
assert '<h3>Foo</h3>' not in resp.text
assert '<h3>Bar</h3>' in resp.text
carddef2.category = cat
carddef2.store()
resp = app.get('/backoffice/data/')
assert '<h3>Misc</h3>' not in resp.text
assert '<h3>Foo</h3>' in resp.text
assert '<h3>Bar</h3>' in resp.text
def test_carddata_management_user_support(pub):
user = create_user(pub)
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = []
carddef.backoffice_submission_roles = user.roles
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/data/foo/add/')
assert 'Associated User' not in resp.text
assert 'user_id' not in resp.form.fields
carddef.user_support = 'foobar'
carddef.store()
resp = app.get('/backoffice/data/foo/add/')
assert 'Associated User' not in resp.text
assert 'user_id' not in resp.form.fields
carddef.user_support = 'optional'
carddef.store()
resp = app.get('/backoffice/data/foo/add/')
assert 'Associated User' in resp.text
assert 'user_id' in resp.form.fields
def test_studio_card_item_link(pub):
user = create_user(pub)
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.StringField(id='1', label='Test', type='string', varname='foo'),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.digest_templates = {'default': 'card {{ form_var_foo }}'}
carddef.store()
carddef.data_class().wipe()
card = carddef.data_class()()
card.data = {'1': 'plop'}
card.just_created()
card.store()
carddef2 = CardDef()
carddef2.name = 'bar'
carddef2.fields = [
fields.ItemField(id='1', label='Test', type='item', data_source={'type': 'carddef:foo', 'value': ''}),
]
carddef2.backoffice_submission_roles = user.roles
carddef2.workflow_roles = {'_editor': user.roles[0]}
carddef2.store()
carddef2.data_class().wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/data/')
resp = resp.click('bar')
resp = resp.click('Add')
resp.form['f1'] = card.id
resp = resp.form.submit('submit')
assert resp.location.endswith('/backoffice/data/bar/1/')
resp = resp.follow()
resp = resp.click('card plop')
assert '<div class="value">plop</div>' in resp
carddata = carddef2.data_class().get(1)
linked_cards = list(carddata.iter_target_datas())
assert len(linked_cards) == 1
assert linked_cards[0][0].formdef.url_name == carddef.url_name
# check with a custom view as data source
custom_view = pub.custom_view_class()
custom_view.title = 'shared custom test view'
custom_view.formdef = carddef
custom_view.visibility = 'any'
custom_view.columns = {'list': [{'id': 'id'}]}
custom_view.store()
carddef2.fields = [
fields.ItemField(
id='1',
label='Test',
type='item',
data_source={'type': 'carddef:foo:%s' % custom_view.slug, 'value': ''},
),
]
carddef2.store()
resp = app.get('/backoffice/data/bar/1/')
resp = resp.click('card plop')
carddata = carddef2.data_class().get(1)
linked_cards = list(carddata.iter_target_datas())
assert len(linked_cards) == 1
assert linked_cards[0][0].formdef.url_name == carddef.url_name
# link to a unknown carddef
carddef2.fields = [
fields.ItemField(
id='1', label='Test', type='item', data_source={'type': 'carddef:unknown', 'value': ''}
),
]
carddef2.store()
carddata = carddef2.data_class().get(1)
linked_cards = list(carddata.iter_target_datas())
assert len(linked_cards) == 1
assert linked_cards[0][0] == 'Linked object def by id unknown'
app = login(get_app(pub))
resp = app.get('/backoffice/data/')
resp = resp.click('bar')
resp = resp.click('Add') # no error
# look without access rights
carddef.backoffice_submission_roles = None
carddef.workflow_roles = {'_editor': None}
carddef.store()
resp = app.get('/backoffice/data/bar/1/')
with pytest.raises(IndexError):
resp.click('card plop')
def test_backoffice_cards_import_data_from_csv(pub):
user = create_user(pub)
data_source = {
'type': 'jsonvalue',
'value': json.dumps(
[{'id': '1', 'text': 'un', 'more': 'foo'}, {'id': '2', 'text': 'deux', 'more': 'bar'}]
),
}
CardDef.wipe()
carddef = CardDef()
carddef.name = 'test'
carddef.fields = [
fields.TableField(id='0', label='Table'),
fields.MapField(id='1', label='Map'),
fields.StringField(id='2', label='Test'),
fields.BoolField(id='3', label='Boolean'),
fields.ItemField(id='4', label='List', items=['item1', 'item2']),
fields.DateField(id='5', label='Date'),
fields.TitleField(id='6', label='Title', type='title'),
fields.FileField(id='7', label='File'),
fields.EmailField(id='8', label='Email'),
fields.TextField(id='9', label='Long'),
fields.ItemField(id='10', label='List2', data_source=data_source),
fields.ItemsField(id='11', label='Items', data_source=data_source, required=False),
]
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
resp = app.get(carddef.get_url())
assert 'Import data from a file' not in resp.text
resp = app.get(carddef.get_url() + 'import-file', status=403)
carddef.backoffice_submission_roles = user.roles
carddef.store()
resp = app.get(carddef.get_url())
resp = resp.click('Import data from a file')
assert 'Table, File are required but cannot be filled from CSV.' in resp
assert 'Download sample CSV file for this card' not in resp
carddef.fields[0].required = False
carddef.fields[7].required = False
carddef.store()
resp = app.get(carddef.get_url())
resp = resp.click('Import data from a file')
sample_resp = resp.click('Download sample CSV file for this card')
today = datetime.date.today()
assert sample_resp.text == (
'"Table","Map","Test","Boolean","List","Date","File","Email","Long","List2","Items"\r\n'
'"will be ignored - type Table not supported",'
'"%s",'
'"value",'
'"Yes",'
'"value",'
'"%s",'
'"will be ignored - type File Upload not supported",'
'"foo@example.com",'
'"value",'
'"value",'
'"id1|id2|..."\r\n' % (pub.get_default_position(), today)
)
# missing file
resp = resp.forms[0].submit()
assert '>required field<' in resp
resp.forms[0]['file'] = Upload('test.csv', b'\0', 'text/csv')
resp = resp.forms[0].submit()
assert 'Invalid file format.' in resp
resp.forms[0]['file'] = Upload('test.csv', b'', 'text/csv')
resp = resp.forms[0].submit()
assert 'Invalid CSV file.' in resp
resp.forms[0]['file'] = Upload('test.csv', b'Test,List,Date\ndata1,item1,invalid', 'text/csv')
resp = resp.forms[0].submit()
assert 'CSV file contains less columns than card fields.' in resp.text
data = [b'Table,Map,Test,Boolean,List,Date,File,Email,Long,List2,Items']
for i in range(1, 150):
data.append(
b'table,48.81;2.37,data%d ,%s,item%d,2020-01-%02d,filename-%d,test@localhost,"plop\nplop",1,1|2'
% (i, str(bool(i % 2)).encode('utf-8'), i, i % 31 + 1, i)
)
resp.forms[0]['file'] = Upload('test.csv', b'\n'.join(data), 'text/csv')
resp = resp.forms[0].submit().follow()
assert 'Importing data into cards' in resp
assert carddef.data_class().count() == 149
card1, card2 = carddef.data_class().select(order_by='id')[:2]
assert card1.data['1'] == '48.81;2.37'
assert card1.data['2'] == 'data1'
assert card1.data['3'] is True
assert card1.data['5'].tm_mday == 2
assert card1.data['9'] == 'plop\nplop'
assert card1.data['10'] == '1'
assert card1.data['10_display'] == 'un'
assert card1.data['10_structured'] == {'id': '1', 'text': 'un', 'more': 'foo'}
assert card1.data['11'] == ['1', '2']
assert card1.data['11_display'] == 'un, deux'
assert card1.data['11_structured'] == [
{'id': '1', 'text': 'un', 'more': 'foo'},
{'id': '2', 'text': 'deux', 'more': 'bar'},
]
assert card2.data['2'] == 'data2'
assert card2.data['3'] is False
assert card2.data['5'].tm_mday == 3
assert card2.submission_channel == 'file-import'
assert card2.submission_agent_id == str(user.id)
def test_backoffice_cards_import_data_csv_user_support(pub):
user = create_user(pub)
user.name_identifiers = [str(uuid.uuid4())]
user.store()
CardDef.wipe()
carddef = CardDef()
carddef.name = 'test'
carddef.fields = [
fields.ItemField(id='1', label='List', items=['item1', 'item2']),
]
carddef.user_support = 'optional'
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.backoffice_submission_roles = user.roles
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
sample_resp = app.get('/backoffice/data/test/data-sample-csv')
assert sample_resp.text == '"User (email or UUID)","List"\r\n"value","value"\r\n'
data = [
b'User,List',
b'%s,item1' % user.email.encode('utf-8'),
b'%s,item2' % user.nameid.encode('utf-8'),
b',item1',
b'foobar,item2',
b'foobar@mail.com,item2',
]
resp = app.get('/backoffice/data/test/import-file')
resp.forms[0]['file'] = Upload('test.csv', b'\n'.join(data), 'text/csv')
resp = resp.forms[0].submit().follow()
assert carddef.data_class().count() == 5
cards = carddef.data_class().select(order_by='id')
assert cards[0].user_id == str(user.id)
assert cards[1].user_id == str(user.id)
assert cards[2].user_id is None
assert cards[3].user_id is None
assert cards[4].user_id is None
# if no user support, user columns is ignored in import
carddef.user_support = None
carddef.store()
carddef.data_class().wipe()
data = [
b'User',
user.email.encode('utf-8'),
user.nameid.encode('utf-8'),
b'foobar',
b'foobar@mail.com',
]
resp = app.get('/backoffice/data/test/import-file')
resp.forms[0]['file'] = Upload('test.csv', b'\n'.join(data), 'text/csv')
resp = resp.forms[0].submit().follow()
assert carddef.data_class().count() == 4
cards = carddef.data_class().select(order_by='id')
assert [c.user_id for c in cards] == [None, None, None, None]
def test_backoffice_cards_import_data_csv_invalid_columns(pub):
user = create_user(pub)
CardDef.wipe()
carddef = CardDef()
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.backoffice_submission_roles = user.roles
carddef.name = 'test'
carddef.fields = [
fields.StringField(id='1', label='String1'),
fields.StringField(id='2', label='String2'),
fields.TextField(id='3', label='Text'),
]
carddef.store()
app = login(get_app(pub))
resp = app.get(carddef.get_url())
resp = resp.click('Import data from a file')
csv_data = '''String1,String2,Text
1,2,3
4,5,6
7,
8,9,10,11
12,13,14
'''
resp.forms[0]['file'] = Upload('test.csv', csv_data.encode('utf-8'), 'text/csv')
resp = resp.forms[0].submit()
assert 'CSV file contains lines with wrong number of columns.' in resp.text
assert '(line numbers 4, 5, 7)' in resp.text
csv_data += '\n' * 10
resp.forms[0]['file'] = Upload('test.csv', csv_data.encode('utf-8'), 'text/csv')
resp = resp.forms[0].submit()
assert 'CSV file contains lines with wrong number of columns.' in resp.text
assert '(line numbers 4, 5, 7, 8, 9 and more)' in resp.text
def test_backoffice_cards_import_data_csv_no_backoffice_fields(pub):
user = create_user(pub)
user.name_identifiers = [str(uuid.uuid4())]
user.store()
Workflow.wipe()
workflow = Workflow(name='form-title')
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(id='bo0', varname='foo_bovar', type='string', label='bo variable'),
]
CardDef.wipe()
carddef = CardDef()
carddef.name = 'test'
carddef.fields = [
fields.StringField(id='1', label='String'),
fields.ItemField(id='2', label='List', items=['item1', 'item2']),
]
carddef.backoffice_submission_roles = user.roles
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
sample_resp = app.get('/backoffice/data/test/data-sample-csv')
assert sample_resp.text.splitlines()[0] == '"String","List"'
data = b'''\
"String","List"
"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().follow()
assert carddef.data_class().count() == 2
def test_backoffice_cards_import_data_from_json(pub):
user = create_user(pub)
data_source = {
'type': 'jsonvalue',
'value': json.dumps(
[{'id': '1', 'text': 'un', 'more': 'foo'}, {'id': '2', 'text': 'deux', 'more': 'bar'}]
),
}
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'
carddef.fields = [
fields.StringField(id='1', label='Test', varname='string'),
fields.BoolField(id='2', label='Boolean', varname='bool'),
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
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
resp = app.get(carddef.get_url())
resp = resp.click('Import data from a file')
data = {
'data': [
{
'fields': {
'string': 'a string',
'bool': True,
'item': '1',
'file': {
'filename': 'test.txt',
'content_type': 'application/pdf',
'content': base64.encodebytes(b'%PDF-1.4 ...').decode(),
},
'date': '2022-07-19',
'blockdata': [{'foo': 'another string', 'bar': '2'}],
}
}
]
}
resp.forms[0]['file'] = Upload('test.json', json.dumps(data).encode(), 'application/json')
resp = resp.forms[0].submit()
assert '/backoffice/processing?job=' in resp.location
resp = resp.follow()
carddata_export = carddef.data_class().select()[0].get_json_export_dict()
assert carddata_export['workflow']['status']['id'] == 'recorded'
assert carddata_export['fields'] == {
'string': 'a string',
'bool': True,
'item': 'un',
'item_raw': '1',
'item_structured': {'id': '1', 'more': 'foo', 'text': 'un'},
'file': {
'field_id': '4',
'filename': 'test.txt',
'content_type': 'application/pdf',
'content': base64.encodebytes(b'%PDF-1.4 ...').decode().strip(),
'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',
}
],
}
def test_backoffice_cards_import_data_with_no_varname_from_json(pub):
user = create_user(pub)
CardDef.wipe()
carddef = CardDef()
carddef.name = 'test'
carddef.fields = [
fields.StringField(id='1', label='Test'),
]
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.backoffice_submission_roles = user.roles
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
resp = app.get(carddef.get_url())
resp = resp.click('Import data from a file')
data = {
'data': [
{
'fields': {
'_unnamed': {
'1': 'a string',
}
}
}
]
}
resp.forms[0]['file'] = Upload('test.json', json.dumps(data).encode(), 'application/json')
resp = resp.forms[0].submit()
assert '/backoffice/processing?job=' in resp.location
carddata = carddef.data_class().select()[0]
assert carddata.data['1'] == 'a string'
def test_backoffice_cards_import_status_from_json(pub):
user = create_user(pub)
workflow = CardDef.get_default_workflow()
workflow.id = None
st2 = workflow.add_status('status2')
workflow.store()
CardDef.wipe()
carddef = CardDef()
carddef.name = 'test'
carddef.fields = [
fields.StringField(id='1', label='Test', varname='string'),
]
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.workflow = workflow
carddef.backoffice_submission_roles = user.roles
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
resp = app.get(carddef.get_url())
data = {
'data': [
{
'fields': {
'string': 'a string',
},
'workflow': {
'status': {
'id': 'wf-%s' % st2.id,
}
},
}
]
}
resp = resp.click('Import data from a file')
resp.forms[0]['file'] = Upload('test.json', json.dumps(data).encode(), 'application/json')
resp = resp.forms[0].submit()
assert '/backoffice/processing?job=' in resp.location
carddata = carddef.data_class().select()[0]
assert carddata.status == 'wf-%s' % st2.id
def test_backoffice_cards_import_backoffice_fields_from_json(pub):
user = create_user(pub)
workflow = CardDef.get_default_workflow()
workflow.id = None
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='bo field 1', varname='bo_data'),
]
workflow.store()
CardDef.wipe()
carddef = CardDef()
carddef.name = 'test'
carddef.fields = [
fields.StringField(id='1', label='Test', varname='string'),
]
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.workflow = workflow
carddef.backoffice_submission_roles = user.roles
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
resp = app.get(carddef.get_url())
data = {
'data': [
{
'fields': {
'string': 'a string',
},
'workflow': {
'status': {
'id': 'recorded',
},
'fields': {
'bo_data': 'foo',
},
},
}
]
}
resp = resp.click('Import data from a file')
resp.forms[0]['file'] = Upload('test.json', json.dumps(data).encode(), 'application/json')
resp = resp.forms[0].submit()
assert '/backoffice/processing?job=' in resp.location
carddata = carddef.data_class().select()[0]
assert carddata.status == 'recorded'
assert carddata.data == {'1': 'a string', 'bo1': 'foo'}
def test_backoffice_cards_import_user_from_json(pub):
user = create_user(pub)
user2 = pub.user_class(name='card import')
user2.email = 'card-import@example.org'
user2.store()
CardDef.wipe()
carddef = CardDef()
carddef.name = 'test'
carddef.fields = [
fields.StringField(id='1', label='Test', varname='string'),
]
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.backoffice_submission_roles = user.roles
carddef.user_support = 'optional'
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
resp = app.get(carddef.get_url())
data = {
'data': [
{
'fields': {
'string': 'a string',
},
'user': {
'email': 'card-import@example.org',
},
}
]
}
resp = resp.click('Import data from a file')
resp.forms[0]['file'] = Upload('test.json', json.dumps(data).encode(), 'application/json')
resp = resp.forms[0].submit()
assert '/backoffice/processing?job=' in resp.location
carddata = carddef.data_class().select()[0]
assert str(carddata.user_id) == str(user2.id)
def test_backoffice_cards_wscall_failure_display(http_requests, pub):
user = create_user(pub)
Workflow.wipe()
workflow = Workflow(name='wscall')
workflow.roles = {
'_viewer': 'Viewer',
'_editor': 'Editor',
}
st1 = workflow.add_status('Recorded', 'recorded')
wscall = st1.add_action('webservice_call', id='_wscall')
wscall.varname = 'xxx'
wscall.url = 'http://remote.example.net/xml'
wscall.action_on_bad_data = ':stop'
wscall.record_errors = True
again = st1.add_action('choice', id='_again')
again.label = 'Again'
again.by = ['_editor']
again.status = st1.id
workflow.store()
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.StringField(id='1', label='Test', type='string', varname='foo'),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow_id = workflow.id
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.digest_templates = {'default': 'card {{ form_var_foo }}'}
carddef.store()
carddef.data_class().wipe()
carddata = carddef.data_class()()
carddata.data = {'1': 'plop'}
carddata.just_created()
carddata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/data/foo/%s/' % carddata.id)
assert 'Again' in resp.text
resp = resp.forms[0].submit('button_again')
resp = resp.follow()
assert 'Error during webservice call' in resp.text
assert pub.loggederror_class.count() == 1
assert pub.loggederror_class.select()[0].get_formdata().data == {'1': 'plop'}
def test_block_card_item_link(pub):
user = create_user(pub)
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.StringField(id='1', label='Test', type='string', varname='foo'),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.digest_templates = {'default': 'card {{ form_var_foo }}'}
carddef.store()
carddef.data_class().wipe()
card = carddef.data_class()()
card.data = {'1': 'plop'}
card.just_created()
card.store()
card2 = carddef.data_class()()
card2.data = {'1': 'plop2'}
card2.just_created()
card2.store()
BlockDef.wipe()
block = BlockDef()
block.name = 'foobar'
block.fields = [
fields.ItemField(id='1', label='Test', type='item', data_source={'type': 'carddef:foo', 'value': ''}),
]
block.store()
formdef = FormDef()
formdef.name = 'bar'
formdef.fields = [
fields.BlockField(id='1', label='test', type='block:foobar', max_items=3),
]
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub))
resp = app.get('/bar/')
resp.form['f1$element0$f1'].value = card.id
resp = resp.form.submit('f1$add_element')
resp.form['f1$element1$f1'].value = card2.id
resp = resp.form.submit('submit') # -> validation page
assert resp.form['f1$element0$f1'].value == str(card.id)
assert resp.form['f1$element0$f1_label'].value == 'card plop'
assert resp.form['f1$element1$f1'].value == str(card2.id)
assert resp.form['f1$element1$f1_label'].value == 'card plop2'
resp = resp.form.submit('submit') # -> final submit
resp = resp.follow()
assert '<div class="value">card plop</div>' in resp
assert '<div class="value">card plop2</div>' in resp
# check cards are links in backoffice
resp = app.get('/backoffice/management' + resp.request.path)
assert (
'<div class="value"><a href="http://example.net/backoffice/data/foo/%s/">card plop</a></div></div>'
% card.id
in resp
)
assert (
'<div class="value"><a href="http://example.net/backoffice/data/foo/%s/">card plop2</a></div></div>'
% card2.id
in resp
)
def test_carddata_create_user_selection(pub):
CardDef.wipe()
user = create_user(pub)
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = []
carddef.backoffice_submission_roles = user.roles
carddef.workflow_roles = {'_editor': None}
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
app.get('/backoffice/data/foo/', status=403)
resp = app.get('/backoffice/data/foo/add/')
assert 'Associated User' not in resp
carddef.user_support = 'optional'
carddef.store()
resp = app.get('/backoffice/data/foo/add/')
assert 'Associated User' in resp
# check looking up users works
assert app.get('/api/users/?q=').json['data']
resp.form['user_id'] = str(user.id) # happens via javascript
resp = resp.form.submit('submit') # -> submit
assert resp.location.endswith('backoffice/data/') # to cards index page
resp.follow()
assert str(carddef.data_class().select()[0].user_id) == str(user.id)
def test_carddata_edit_user_selection(pub):
CardDef.wipe()
user = create_user(pub)
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.StringField(id='1', label='Test', type='string', varname='foo'),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.store()
carddef.data_class().wipe()
carddata = carddef.data_class()()
carddata.data = {'1': 'plop'}
carddata.just_created()
carddata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/data/foo/%s/' % carddata.id)
assert 'Edit Card' in resp.text
resp = resp.form.submit('button_editable')
resp = resp.follow()
assert 'Associated User' not in resp
assert '/user-pending-forms' not in resp.text
carddef.user_support = 'optional'
carddef.store()
resp = app.get('/backoffice/data/foo/%s/' % carddata.id)
assert 'Edit Card' in resp.text
resp = resp.form.submit('button_editable')
resp = resp.follow()
assert 'Associated User' in resp
assert resp.pyquery('.submit-user-selection')
resp.form['user_id'] = str(user.id) # happens via javascript
resp = resp.form.submit('submit') # -> save changes
resp = resp.follow()
assert 'Associated User' in resp
assert carddef.data_class().get(carddata.id).user_id == str(user.id)
assert '/user-pending-forms' not in resp.text
def test_carddata_add_related(pub):
user = create_user(pub)
BlockDef.wipe()
block = BlockDef()
block.name = 'child'
block.fields = [
fields.ItemField(
id='1',
label='Child',
type='item',
data_source={'type': 'carddef:child'},
display_mode='autocomplete',
),
]
block.store()
CardDef.wipe()
family = CardDef()
family.name = 'Family'
family.fields = [
fields.ItemField(
id='1',
label='RL1',
type='item',
data_source={'type': 'carddef:adult'},
display_mode='autocomplete',
),
fields.ItemField(
id='2',
label='RL2',
type='item',
data_source={'type': 'carddef:adult'},
display_mode='autocomplete',
),
fields.BlockField(id='3', label='Children', type='block:child', max_items=42),
]
family.backoffice_submission_roles = user.roles
family.workflow_roles = {'_editor': user.roles[0]}
family.store()
family.data_class().wipe()
adult = CardDef()
adult.name = 'Adult'
adult.fields = [
fields.StringField(
id='1',
label='First name',
type='string',
),
fields.StringField(
id='2',
label='Last name',
type='string',
),
fields.ItemField(
id='3',
label='autocompletion test',
type='item',
display_mode='autocomplete',
items=['Foo', 'Bar', 'Three', 'Four', 'Five', 'Six'],
),
]
adult.backoffice_submission_roles = user.roles
adult.workflow_roles = {'_editor': user.roles[0]}
adult.store()
adult.data_class().wipe()
child = CardDef()
child.name = 'Child'
child.fields = [
fields.StringField(
id='1',
label='First name',
type='string',
),
fields.StringField(
id='2',
label='Last name',
type='string',
),
]
child.backoffice_submission_roles = user.roles
child.workflow_roles = {'_editor': user.roles[0]}
child.store()
child.data_class().wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/data/family/add/')
assert 'Add another RL1' in resp
assert 'Add another RL2' in resp
assert 'Add another Child' in resp
assert resp.text.count('/backoffice/data/adult/add/?_popup=1') == 2
assert '/backoffice/data/child/add/?_popup=1' in resp
resp_popup = app.get('/backoffice/data/adult/add/?_popup=1')
assert 'select2.min.js' in resp_popup.text
# no autocompletion for RL1
family.fields[0].display_mode = []
family.store()
resp = app.get('/backoffice/data/family/add/')
assert 'Add another RL1' not in resp
assert 'Add another RL2' in resp
assert 'Add another Child' in resp
assert resp.text.count('/backoffice/data/adult/add/?_popup=1') == 1
assert '/backoffice/data/child/add/?_popup=1' in resp
# user ha no creation rights on child
child.backoffice_submission_roles = None
child.store()
resp = app.get('/backoffice/data/family/add/')
assert 'Add another RL1' not in resp
assert 'Add another RL2' in resp
assert 'Add another Child' not in resp
assert resp.text.count('/backoffice/data/adult/add/?_popup=1') == 1
assert '/backoffice/data/child/add/?_popup=1' not in resp
def test_backoffice_card_global_interactive_action(pub):
user = create_user(pub)
workflow = CardDef.get_default_workflow()
workflow.id = None
action = workflow.add_global_action('FOOBAR')
display = action.add_action('displaymsg')
display.message = 'This is a message'
display.to = []
form_action = action.add_action('form')
form_action.varname = 'blah'
form_action.formdef = WorkflowFormFieldsFormDef(item=form_action)
form_action.formdef.fields.append(
fields.StringField(id='1', label='Test', varname='test', type='string', required=True)
)
register_comment = action.add_action('register-comment')
register_comment.comment = 'HELLO {{ form_workflow_form_blah_var_test }}'
trigger = action.triggers[0]
trigger.roles = [user.roles[0]]
workflow.store()
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = []
carddef.workflow_id = workflow.id
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.store()
carddef.data_class().wipe()
carddata = carddef.data_class()()
carddata.data = {}
carddata.just_created()
carddata.store()
app = login(get_app(pub))
resp = app.get(carddata.get_url(backoffice=True))
assert 'button-action-1' in resp.form.fields
resp = resp.form.submit('button-action-1')
resp = resp.follow() # -> error, empty action
resp = resp.follow() # -> back to form
assert 'Error: empty action' in resp.text
form_action.by = trigger.roles
workflow.store()
resp = app.get(carddata.get_url(backoffice=True))
resp = resp.form.submit('button-action-1')
resp = resp.follow()
assert 'This is a message' in resp.text
resp = resp.form.submit('submit')
assert resp.pyquery('#form_error_fblah_1').text() == 'required field'
resp.form['fblah_1'] = 'GLOBAL INTERACTIVE ACTION'
resp = resp.form.submit('submit')
assert resp.location == carddata.get_url(backoffice=True)
resp = resp.follow()
assert 'HELLO GLOBAL INTERACTIVE ACTION' in resp.text
def test_carddata_with_file(pub):
CardDef.wipe()
user = create_user(pub)
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.StringField(id='1', label='String', required=True),
fields.FileField(id='2', label='File'),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/data/foo/')
resp = resp.click('Add')
resp.forms[0]['f2$file'] = Upload('test.txt', b'hello world')
resp = resp.form.submit('submit') # if will fail as string field is required
# test file access
assert resp.click('test.txt').body == b'hello world'
resp.forms[0]['f1'] = 'plop'
resp = resp.form.submit('submit').follow() # -> submit
resp = resp.form.submit('button_editable')
resp = resp.follow()
assert resp.click('test.txt').body == b'hello world' # check tempfile is ok