From 920ad6743f23d91759cb720a07efd0c01a0e4e40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Fri, 8 Jul 2022 15:01:22 +0200
Subject: [PATCH] misc: |filter_value & |exclude_value with block fields
 (#58452)

---
 tests/test_formdata.py | 132 +++++++++++++++++++++++++++++++++++++++++
 wcs/variables.py       |  13 +++-
 2 files changed, 143 insertions(+), 2 deletions(-)

diff --git a/tests/test_formdata.py b/tests/test_formdata.py
index 8f89f6685..9199b13c1 100644
--- a/tests/test_formdata.py
+++ b/tests/test_formdata.py
@@ -3457,6 +3457,138 @@ def test_block_set_value_to_empty_string(pub):
     assert formdata.data.get('1_display') is None
 
 
+def test_formdata_filtering_on_block_fields(pub):
+    if not pub.is_using_postgresql():
+        pytest.skip('this requires SQL')
+
+    NamedDataSource.wipe()
+    data_source = NamedDataSource(name='foobar')
+    data_source.data_source = {
+        'type': 'formula',
+        'value': repr([{'id': '1', 'text': 'foo'}, {'id': '2', 'text': 'bar'}]),
+    }
+    data_source.store()
+
+    BlockDef.wipe()
+    block = BlockDef()
+    block.name = 'foobar'
+    block.fields = [
+        fields.StringField(id='1', label='String', type='string', varname='string'),
+        fields.ItemField(id='2', label='Item', type='item', data_source={'type': 'foobar'}, varname='item'),
+        fields.BoolField(id='3', label='Bool', type='bool', varname='bool'),
+        fields.DateField(id='4', label='Date', type='date', varname='date'),
+        fields.EmailField(id='5', label='Email', type='email', varname='email'),
+    ]
+    block.store()
+
+    FormDef.wipe()
+    formdef = FormDef()
+    formdef.name = 'test'
+    formdef.fields = [
+        fields.BlockField(id='0', label='Block Data', varname='blockdata', type='block:foobar', max_items=3),
+    ]
+    formdef.store()
+
+    data_class = formdef.data_class()
+    data_class.wipe()
+
+    for i in range(11):
+        formdata = data_class()
+        if i < 10:  # None values for the last one
+            formdata.data = {
+                '0': {
+                    'data': [
+                        {
+                            '1': 'plop%s' % i,
+                            '2': '1' if i % 2 else '2',
+                            '2_display': 'foo' if i % 2 else 'bar',
+                            '2_structured': 'XXX' if i % 2 else 'YYY',
+                            '3': bool(i % 2),
+                            '4': '2021-06-%02d' % (i + 1),
+                            '5': 'a@localhost' if i % 2 else 'b@localhost',
+                        },
+                    ],
+                    'schema': {},  # not important here
+                },
+                '0_display': 'hello',
+            }
+            if i == 0:
+                formdata.data['0']['data'].append(
+                    {
+                        '1': 'plop%s' % (i + 1),
+                        '2': '1',
+                        '2_display': 'foo',
+                        '2_structured': 'XXX',
+                        '3': True,
+                        '4': '2021-06-02',
+                        '5': 'a@localhost',
+                    },
+                )
+        formdata.just_created()
+        formdata.jump_status('new')
+        formdata.store()
+
+    context = pub.substitutions.get_context_variables(mode='lazy')
+
+    # string
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_string"|filter_value:"plop0"|count}}')
+    assert tmpl.render(context) == '1'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_string"|filter_value:"plop1"|count}}')
+    assert tmpl.render(context) == '2'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_string"|filter_value:"plop10"|count}}')
+    assert tmpl.render(context) == '0'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_string"|exclude_value:"plop0"|count}}')
+    assert tmpl.render(context) == '10'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_string"|exclude_value:"plop1"|count}}')
+    assert tmpl.render(context) == '9'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_string"|exclude_value:"plop10"|count}}')
+    assert tmpl.render(context) == '11'
+    # item
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_item"|filter_value:"1"|count}}')
+    assert tmpl.render(context) == '6'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_item"|filter_value:"2"|count}}')
+    assert tmpl.render(context) == '5'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_item"|filter_value:"3"|count}}')
+    assert tmpl.render(context) == '0'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_item"|exclude_value:"1"|count}}')
+    assert tmpl.render(context) == '5'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_item"|exclude_value:"2"|count}}')
+    assert tmpl.render(context) == '6'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_item"|exclude_value:"3"|count}}')
+    assert tmpl.render(context) == '11'
+    # bool
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_bool"|filter_value:"true"|count}}')
+    assert tmpl.render(context) == '6'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_bool"|filter_value:"false"|count}}')
+    assert tmpl.render(context) == '5'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_bool"|exclude_value:"true"|count}}')
+    assert tmpl.render(context) == '5'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_bool"|exclude_value:"false"|count}}')
+    assert tmpl.render(context) == '6'
+    # date
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_date"|filter_value:"2021-06-01"|count}}')
+    assert tmpl.render(context) == '1'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_date"|filter_value:"2021-06-02"|count}}')
+    assert tmpl.render(context) == '2'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_date"|exclude_value:"2021-06-01"|count}}')
+    assert tmpl.render(context) == '10'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_date"|exclude_value:"2021-06-02"|count}}')
+    assert tmpl.render(context) == '9'
+    # email
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_email"|filter_value:"a@localhost"|count}}')
+    assert tmpl.render(context) == '6'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_email"|filter_value:"b@localhost"|count}}')
+    assert tmpl.render(context) == '5'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_email"|filter_value:"c@localhost"|count}}')
+    assert tmpl.render(context) == '0'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_email"|exclude_value:"a@localhost"|count}}')
+    assert tmpl.render(context) == '5'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_email"|exclude_value:"b@localhost"|count}}')
+    assert tmpl.render(context) == '6'
+    tmpl = Template('{{forms|objects:"test"|filter_by:"blockdata_email"|exclude_value:"c@localhost"|count}}')
+    assert tmpl.render(context) == '11'
+
+
 def test_items_field_getlist(pub):
     NamedDataSource.wipe()
     data_source = NamedDataSource(name='foobar')
diff --git a/wcs/variables.py b/wcs/variables.py
index 5478434e5..071021c85 100644
--- a/wcs/variables.py
+++ b/wcs/variables.py
@@ -187,8 +187,14 @@ class LazyFormDefObjectsManager:
         return self._clone(self._criterias + [Equal('id_display', str(value))])
 
     def get_fields(self, key):
-        for field in self._formdef.get_all_fields():
-            if getattr(field, 'varname', None) == key:
+        for field in self._formdef.iter_fields(include_block_fields=True):
+            if getattr(field, 'block_field', None):
+                if field.key == 'items':
+                    # not yet
+                    continue
+                if not get_publisher().is_using_postgresql():
+                    continue
+            if field.contextual_varname == key:
                 yield field
 
     def get_field(self, key):
@@ -216,6 +222,9 @@ class LazyFormDefObjectsManager:
                     formdata=self._formdata,
                 )
                 return self.none()
+            if value is not None and fields[0].convert_value_to_str and get_publisher().is_using_postgresql():
+                # in block fields, we store strings
+                value = fields[0].convert_value_to_str(value)
 
         from wcs import sql
 
-- 
2.35.1

