From 7be05db4694dc6af7fc69300d0ae271914635d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sat, 14 May 2022 12:44:39 +0200 Subject: [PATCH] cards: add status operator support to custom card data sources (#65248) --- tests/form_pages/test_all.py | 91 ++++++++++++++++++++++++++++++++++++ wcs/backoffice/management.py | 2 +- wcs/custom_views.py | 16 +++++-- 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/tests/form_pages/test_all.py b/tests/form_pages/test_all.py index b2c821bac..6fd2dda72 100644 --- a/tests/form_pages/test_all.py +++ b/tests/form_pages/test_all.py @@ -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() diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py index 490453b00..7d017da16 100644 --- a/wcs/backoffice/management.py +++ b/wcs/backoffice/management.py @@ -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) diff --git a/wcs/custom_views.py b/wcs/custom_views.py index c1a005764..b7f07a2b0 100644 --- a/wcs/custom_views.py +++ b/wcs/custom_views.py @@ -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