Projet

Général

Profil

0002-misc-allow-operators-for-filter_by-internal-id-statu.patch

Lauréline Guérin, 10 octobre 2022 13:58

Télécharger (10,4 ko)

Voir les différences:

Subject: [PATCH 2/2] misc: allow operators for |filter_by internal-id/status
 fields (#68943)

 tests/test_formdata.py | 71 ++++++++++++++++++++++++++++++++++++++++--
 wcs/variables.py       | 67 ++++++++++++++++++++++++++-------------
 2 files changed, 114 insertions(+), 24 deletions(-)
tests/test_formdata.py
1528 1528
        .count
1529 1529
        == 1
1530 1530
    )
1531
    tmpl = Template(
1532
        '{{form_objects|filter_by:"user"|equal|filter_value:form_user|filter_by:"foo_foo"|filter_value:"foo"|count}}'
1533
    )
1534
    assert tmpl.render(context) == '1'
1535
    for operator in ['not_equal', 'less_than', 'less_than_or_equal', 'greater_than', 'greater_than_or_equal']:
1536
        pub.loggederror_class.wipe()
1537
        tmpl = Template('{{form_objects|filter_by:"user"|%s|filter_value:"foo"|count}}' % operator)
1538
        assert tmpl.render(context) == '0'
1539
        assert pub.loggederror_class.count() == 1
1540
        logged_error = pub.loggederror_class.select(order_by='id')[0]
1541
        assert logged_error.summary == 'Invalid operator "%s" for filter "user"' % operator
1531 1542

  
1532 1543
    # test |current_user
1533 1544
    pub.get_request()._user = ()  # reset cache
......
1576 1587
    # test |filter_by_internal_id
1577 1588
    context = pub.substitutions.get_context_variables(mode='lazy')
1578 1589
    for tpl in ['filter_by_internal_id', 'filter_by:"internal_id"|filter_value']:
1590
        pub.loggederror_class.wipe()
1579 1591
        tmpl = Template('{{form_objects|%s:"%s"|count}}' % (tpl, finished_formdata.id))
1580 1592
        assert tmpl.render(context) == '1'
1581 1593
        tmpl = Template('{{form_objects|%s:"%s"|count}}' % (tpl, '0'))
1582 1594
        assert tmpl.render(context) == '0'
1583 1595
        tmpl = Template('{{form_objects|%s:"%s"|count}}' % (tpl, 'invalid value'))
1584 1596
        assert tmpl.render(context) == '0'
1585
        assert pub.loggederror_class.count() == 4
1586
        logged_error = pub.loggederror_class.select(order_by='id')[3]
1597
        assert pub.loggederror_class.count() == 1
1598
        logged_error = pub.loggederror_class.select(order_by='id')[0]
1587 1599
        assert logged_error.summary == 'Invalid value "invalid value" for filter "internal_id"'
1588 1600

  
1589 1601
    # test |filter_by_number
......
1592 1604
        assert tmpl.render(context) == '1'
1593 1605
        tmpl = Template('{{form_objects|%s:"%s"|count}}' % (tpl, 'invalid value'))
1594 1606
        assert tmpl.render(context) == '0'
1607
    tmpl = Template(
1608
        '{{form_objects|filter_by:"number"|equal|filter_value:"%s"|count}}'
1609
        % finished_formdata.get_display_id()
1610
    )
1611
    assert tmpl.render(context) == '1'
1612
    for operator in ['not_equal', 'less_than', 'less_than_or_equal', 'greater_than', 'greater_than_or_equal']:
1613
        pub.loggederror_class.wipe()
1614
        tmpl = Template(
1615
            '{{form_objects|filter_by:"number"|%s|filter_value:"%s"|count}}'
1616
            % (operator, finished_formdata.get_display_id())
1617
        )
1618
        assert tmpl.render(context) == '0'
1619
        assert pub.loggederror_class.count() == 1
1620
        logged_error = pub.loggederror_class.select(order_by='id')[0]
1621
        assert logged_error.summary == 'Invalid operator "%s" for filter "number"' % operator
1595 1622

  
1596 1623
    # test |is_empty
1597 1624
    tmpl = Template('{{form_objects|pending|is_empty}}')
......
3573 3600
                '5': 'a@localhost' if i % 2 else 'b@localhost',
3574 3601
            }
3575 3602
        formdata.just_created()
3576
        formdata.jump_status('new')
3603
        if i % 3:
3604
            formdata.jump_status('finished')
3577 3605
        formdata.store()
3578 3606

  
3579 3607
    context = pub.substitutions.get_context_variables(mode='lazy')
......
3770 3798
        logged_error = pub.loggederror_class.select(order_by='id')[0]
3771 3799
        assert logged_error.summary == 'Operator filter is not allowed for exclude_value filter'
3772 3800

  
3801
    # internal_id
3802
    params = [
3803
        ('equal', '1', '1'),
3804
        ('equal', '01', '1'),
3805
        ('not_equal', '1', '10'),
3806
        ('less_than', '1', '0'),
3807
        ('less_than_or_equal', '1', '1'),
3808
        ('greater_than', '1', '10'),
3809
        ('greater_than', '10', '1'),
3810
        ('greater_than_or_equal', '1', '11'),
3811
    ]
3812
    for operator, value, result in params:
3813
        tmpl = Template(
3814
            '{{forms|objects:"test"|filter_by:"internal_id"|%s|filter_value:"%s"|count}}' % (operator, value)
3815
        )
3816
        assert tmpl.render(context) == result
3817

  
3818
    # status
3819
    params = [
3820
        ('equal', 'Just Submitted', '4'),
3821
        ('equal', 'Finished', '7'),
3822
        ('equal', 'Unknown', '0'),
3823
        ('not_equal', 'Finished', '4'),
3824
    ]
3825
    for operator, value, result in params:
3826
        tmpl = Template(
3827
            '{{forms|objects:"test"|filter_by:"status"|%s|filter_value:"%s"|count}}' % (operator, value)
3828
        )
3829
        assert tmpl.render(context) == result
3830
    for operator in ['less_than', 'less_than_or_equal', 'greater_than', 'greater_than_or_equal']:
3831
        pub.loggederror_class.wipe()
3832
        tmpl = Template('{{forms|objects:"test"|filter_by:"status"|%s|filter_value:"plop"|count}}' % operator)
3833
        assert tmpl.render(context) == '0'
3834
        assert pub.loggederror_class.count() == 1
3835
        logged_error = pub.loggederror_class.select(order_by='id')[0]
3836
        assert logged_error.summary == 'Invalid operator "%s" for filter "status"' % operator
3837

  
3773 3838

  
3774 3839
def test_formdata_filtering_on_block_fields(pub):
3775 3840
    NamedDataSource.wipe()
wcs/variables.py
116 116
        return self.filter_by_user(get_request().user)
117 117

  
118 118
    def filter_by_user(self, user, op='eq'):
119
        if op not in ['eq']:
120
            get_publisher().record_error(
121
                _('Invalid operator "%s" for filter "%s"') % (self.get_operator_name(op), self.pending_attr),
122
                formdata=self._formdata,
123
            )
124
            return self.none()
119 125
        if isinstance(user, str):
120 126
            user = get_publisher().user_class.lookup_by_string(user)
121 127
        if not user:
......
123 129
        return self._clone(self._criterias + [Equal('user_id', str(user.id))])
124 130

  
125 131
    def filter_by_status(self, status, op='eq'):
132
        criteria_class = self.get_operator_class(op)
133
        if op not in ['eq', 'ne']:
134
            get_publisher().record_error(
135
                _('Invalid operator "%s" for filter "%s"') % (self.get_operator_name(op), self.pending_attr),
136
                formdata=self._formdata,
137
            )
138
            return self.none()
126 139
        for wfs in self._formdef.workflow.possible_status:
127 140
            if wfs.name == status:
128 141
                wf_status = 'wf-%s' % wfs.id
129
                return self._clone(self._criterias + [Equal('status', wf_status)])
142
                return self._clone(self._criterias + [criteria_class('status', wf_status)])
130 143
        return self.none()
131 144

  
132 145
    def with_custom_view(self, custom_view_slug):
......
203 216
                formdata=self._formdata,
204 217
            )
205 218
            return self.none()
206
        return self._clone(self._criterias + [Equal('id', str(value))])
219
        criteria_class = self.get_operator_class(op)
220
        return self._clone(self._criterias + [criteria_class('id', str(value))])
207 221

  
208 222
    def filter_by_number(self, value, op='eq'):
223
        if op not in ['eq']:
224
            get_publisher().record_error(
225
                _('Invalid operator "%s" for filter "%s"') % (self.get_operator_name(op), self.pending_attr),
226
                formdata=self._formdata,
227
            )
228
            return self.none()
209 229
        return self._clone(self._criterias + [Equal('id_display', str(value))])
210 230

  
211 231
    def get_fields(self, key):
......
230 250
            return operators
231 251
        return None
232 252

  
253
    def get_operator_class(self, op):
254
        operators_mapping = {
255
            'eq': Equal,
256
            'ne': NotEqual,
257
            'lt': Less,
258
            'lte': LessOrEqual,
259
            'gt': Greater,
260
            'gte': GreaterOrEqual,
261
        }
262
        return operators_mapping[op]
263

  
264
    def get_operator_name(self, op):
265
        operator_names_mapping = {
266
            'eq': 'equal',
267
            'ne': 'not_equal',
268
            'lt': 'less_than',
269
            'lte': 'less_than_or_equal',
270
            'gt': 'greater_than',
271
            'gte': 'greater_than_or_equal',
272
        }
273
        return operator_names_mapping[op]
274

  
233 275
    def apply_filter_value(self, value, exclude=False):
234 276
        assert self.pending_attr
235 277
        op = 'ne' if exclude else getattr(self, 'pending_op', 'eq')
......
265 307

  
266 308
        from wcs import sql
267 309

  
268
        operators_mapping = {
269
            'eq': Equal,
270
            'ne': NotEqual,
271
            'lt': Less,
272
            'lte': LessOrEqual,
273
            'gt': Greater,
274
            'gte': GreaterOrEqual,
275
        }
276
        operator_names_mapping = {
277
            'eq': 'equal',
278
            'ne': 'not_equal',
279
            'lt': 'less_than',
280
            'lte': 'less_than_or_equal',
281
            'gt': 'greater_than',
282
            'gte': 'greater_than_or_equal',
283
        }
284

  
285 310
        criterias = []
286 311
        for field in fields:
287 312
            field_id = sql.get_field_id(field)
......
303 328
                if op not in operators:
304 329
                    get_publisher().record_error(
305 330
                        _('Invalid operator "%s" for filter "%s"')
306
                        % (operator_names_mapping[op], self.pending_attr),
331
                        % (self.get_operator_name(op), self.pending_attr),
307 332
                        formdata=self._formdata,
308 333
                    )
309 334
                    return self.none()
310
                criteria_class = operators_mapping[op]
335
                criteria_class = self.get_operator_class(op)
311 336
                if field.type in ['string', 'item', 'items']:
312 337
                    try:
313 338
                        # cast to integer so it can be used with numerical operators
314
-