cards: add status operator support to custom card data sources (#65248)

This commit is contained in:
Frédéric Péters 2022-05-14 12:44:39 +02:00
parent 613dec5d33
commit 7be05db469
3 changed files with 105 additions and 4 deletions

View File

@ -5897,6 +5897,97 @@ def test_item_field_from_custom_view_on_cards(pub):
assert formdef.data_class().select()[0].data['0_display'] == 'Xattr%sY' % baz_id
def test_item_field_from_custom_view_on_cards_filter_status(pub):
pub.role_class.wipe()
pub.custom_view_class.wipe()
user = create_user(pub)
role = pub.role_class(name='xxx')
role.store()
user.roles = [role.id]
user.is_admin = True
user.store()
formdef = create_formdef()
formdef.data_class().wipe()
card_workflow = CardDef.get_default_workflow()
st1 = card_workflow.add_status('Status1', 'st1')
card_workflow.id = None
card_workflow.store()
CardDef.wipe()
carddef = CardDef()
carddef.workflow_id = card_workflow.id
carddef.name = 'items'
carddef.digest_templates = {'default': '{{form_var_attr}}'}
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.fields = [
fields.ItemField(id='0', type='item', label='item', varname='item', items=['foo', 'bar', 'baz']),
fields.StringField(id='1', type='string', label='string', varname='attr'),
]
carddef.store()
carddef.data_class().wipe()
for i, value in enumerate(['foo', 'bar', 'baz']):
carddata = carddef.data_class()()
carddata.data = {
'0': value,
'0_display': value,
'1': 'attr%s' % (i + 1),
}
carddata.just_created()
carddata.store()
carddata.jump_status(st1.id)
carddata.store()
# create custom view
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/backoffice/data/items/')
resp.forms['listing-settings']['filter-status'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter-operator'].value = 'ne'
resp.forms['listing-settings']['filter'].value = 'st1'
resp = resp.forms['listing-settings'].submit()
assert resp.pyquery('tbody tr').length == 2
resp.forms['save-custom-view']['title'] = 'as data source'
resp.forms['save-custom-view']['visibility'] = 'datasource'
resp = resp.forms['save-custom-view'].submit()
custom_view = pub.custom_view_class.select()[0]
assert custom_view.filters == {'filter-operator': 'ne', 'filter': 'st1', 'filter-status': 'on'}
# use custom view as source
ds = {'type': 'carddef:%s:%s' % (carddef.url_name, custom_view.slug)}
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 len(resp.form['f0'].options) == 2
assert {x[2] for x in resp.form['f0'].options} == {'attr1', 'attr2'}
custom_view.filters['filter-operator'] = 'eq'
custom_view.store()
resp = get_app(pub).get('/test/')
assert [x[2] for x in resp.form['f0'].options] == ['attr3']
custom_view.filters['filter'] = 'all'
custom_view.store()
resp = get_app(pub).get('/test/')
assert len(resp.form['f0'].options) == 3
custom_view.filters['filter'] = 'all'
custom_view.filters['filter-operator'] = 'ne'
custom_view.store()
resp = get_app(pub).get('/test/')
assert len(resp.form['f0'].options) == 1
assert [x[2] for x in resp.form['f0'].options] == ['---']
@pytest.mark.parametrize('filter_value', ['{{ "foo" }}', 'foo'])
def test_items_field_from_custom_view_on_cards(pub, filter_value):
pub.role_class.wipe()

View File

@ -1767,7 +1767,7 @@ class FormPage(Directory):
def get_filter_operator_from_query(self):
default_filter_operator = 'eq'
if self.view:
default_filter_operator = self.view.filters.get('filter-operator', 'eq')
default_filter_operator = self.view.get_status_filter_operator()
operator = get_request().form.get('filter-operator') or default_filter_operator
if operator not in ['eq', 'ne']:
raise RequestError('Invalid operator "%s" for "filter-operator"' % operator)

View File

@ -23,7 +23,7 @@ from quixote import get_publisher
from wcs.carddef import CardDef
from wcs.formdef import FormDef
from wcs.qommon.misc import simplify
from wcs.qommon.storage import Contains, Equal, StorableObject
from wcs.qommon.storage import Contains, Equal, NotContains, StorableObject
from .qommon.misc import xml_node_text
@ -144,6 +144,9 @@ class CustomView(StorableObject):
def get_filter(self):
return self.filters.get('filter')
def get_status_filter_operator(self):
return self.filters.get('filter-operator', 'eq')
def get_filters_dict(self):
return self.filters
@ -166,7 +169,11 @@ class CustomView(StorableObject):
)
selected_filter = self.get_filter()
if selected_filter and selected_filter != 'all':
selected_status_filter_operator = self.get_status_filter_operator()
if selected_filter and selected_filter == 'all':
if selected_status_filter_operator == 'ne':
criterias.append(Equal('status', '_none'))
elif selected_filter:
if selected_filter == 'pending':
applied_filters = ['wf-%s' % x.id for x in formdef.workflow.get_not_endpoint_status()]
elif selected_filter == 'done':
@ -174,7 +181,10 @@ class CustomView(StorableObject):
else:
applied_filters = ['wf-%s' % selected_filter]
if applied_filters:
criterias.append(Contains('status', applied_filters))
if selected_status_filter_operator == 'eq':
criterias.append(Contains('status', applied_filters))
elif selected_status_filter_operator == 'ne':
criterias.append(NotContains('status', applied_filters))
return criterias