Projet

Général

Profil

0001-csvdatasource-remove-advanced-lookup-filters-13748.patch

Serghei Mihai, 13 novembre 2018 16:36

Télécharger (6,95 ko)

Voir les différences:

Subject: [PATCH] csvdatasource: remove advanced lookup filters (#13748)

 passerelle/apps/csvdatasource/models.py | 31 +++++++++++-----------
 passerelle/apps/csvdatasource/views.py  | 31 +++++-----------------
 tests/test_csv_datasource.py            | 35 +------------------------
 3 files changed, 23 insertions(+), 74 deletions(-)
passerelle/apps/csvdatasource/models.py
35 35
from passerelle.utils.jsonresponse import APIError
36 36
from passerelle.utils.api import endpoint
37 37

  
38
import lookups
39

  
40 38
identifier_re = re.compile(r"^[^\d\W]\w*\Z", re.UNICODE)
41 39

  
42 40

  
......
242 240
            for data in self.get_cached_rows(initial=False):
243 241
                yield data
244 242

  
245
    def get_data(self, filters=None):
243
    def get_data(self, filters={}, case_insensitive=False):
246 244
        titles = [t.strip() for t in self.columns_keynames.split(',')]
247 245

  
248
        # validate filters (appropriate columns must exist)
249
        if filters:
250
            for filter_key in filters.keys():
251
                if not filter_key.split(lookups.DELIMITER)[0] in titles:
252
                    del filters[filter_key]
246
        for filter_key in filters.keys():
247
            # allow 'q' filter
248
            if filter_key == 'q':
249
                continue
250
            if filter_key not in titles:
251
                del filters[filter_key]
253 252

  
254 253
        rows = self.get_cached_rows()
255 254
        data = []
256 255

  
257 256
        # build a generator of all filters
258
        def filters_generator(filters, titles):
257
        def filters_generator(filters, case_insensitive):
259 258
            if not filters:
260 259
                return
261 260
            for key, value in filters.items():
262
                try:
263
                    key, op = key.split(lookups.DELIMITER)
264
                except (ValueError,):
265
                    op = 'eq'
266
                yield lookups.get_lookup(op, key, value)
261
                def operation(key, value, case_insensitive):
262
                    if key == 'q':
263
                        return lambda x: value.lower() in x['text'].lower()
264
                    if case_insensitive:
265
                        return lambda x: value.lower() == x[key].lower()
266
                    return lambda x: value == x[key]
267
                yield operation(key, value, case_insensitive)
267 268

  
268 269
        # apply filters to data
269 270
        def super_filter(filters, data):
......
272 273
            return data
273 274

  
274 275
        data = list(super_filter(
275
            filters_generator(filters, titles), rows
276
            filters_generator(filters, case_insensitive), rows
276 277
        ))
277 278

  
278 279
        return data
passerelle/apps/csvdatasource/views.py
34 34
    def _filters_builder(self, request):
35 35
        filters = {}
36 36
        obj = self.get_object()
37

  
38
        params = request.GET
39

  
40
        case_insensitive = 'case-insensitive' in params
41
        query = params.get('q', None)
37
        query = request.GET.get('q', None)
42 38

  
43 39
        if query:
44
            if case_insensitive:
45
                filters['text__icontains'] = query.lower()
46
            else:
47
                filters['text__contains'] = query
40
            filters['q'] = query
48 41

  
49 42
        # builds filters according to csv file header
50 43
        for column_title in [t.strip() for t in obj.columns_keynames.split(',') if t]:
51
            match = filter(
52
                (lambda ct: lambda x: x.startswith(ct))(column_title), params.keys()
53
            )
54
            for key in match:
55
                if case_insensitive:
56
                    filters[key + '__ieq'] = params[key].lower()
57
                else:
58
                    filters[key] = params[key]
59

  
60
        if 'text' in filters:
61
            if case_insensitive:
62
                filters['text__ieq'] = filters['text'].lower()
63
            else:
64
                filters['text__eq'] = filters['text']
65
            filters.pop('text')
44
            if column_title in params.keys():
45
                filters[column_title] = params[column_title]
66 46

  
67 47
        return filters
68 48

  
......
72 52
    def get(self, request, *args, **kwargs):
73 53
        obj = self.get_object()
74 54
        filters = self._filters_builder(request)
75
        return {'data': obj.get_data(filters)}
55
        case_insensitive = 'case-insensitive' in request.GET
56
        return {'data': obj.get_data(filters, case_insensitive)}
76 57

  
77 58

  
78 59
class NewQueryView(CreateView):
tests/test_csv_datasource.py
234 234
    assert result[0]['text'] == 'Eliot'
235 235
    assert len(result) == 1
236 236

  
237
def test_advanced_filters(client, setup, filetype):
238
    csvdata, url = setup(filename=filetype, data=get_file_content(filetype))
239
    filters = {'id__gt':20, 'id__lt': 40}
240
    resp = client.get(url, filters)
241
    result = parse_response(resp)
242
    assert len(result) == 3
243
    for stuff in result:
244
        assert stuff['id'] in ('22', '36', '38')
245

  
246
def test_advanced_filters_combo(client, setup, filetype):
247
    csvdata, url = setup(filename=filetype, data=get_file_content(filetype))
248
    filters = {
249
        'id__ge': '20',
250
        'id__lt': '40',
251
        'fam__gt': '234',
252
        'fam__le': '235',
253
        'fname__icontains': 'Sandra'
254
    }
255
    resp = client.get(url, filters)
256
    result = parse_response(resp)
257
    assert len(result) == 1
258
    assert result[0]['id'] == '22'
259
    assert result[0]['lname'] == 'MARTIN'
260

  
261
def test_unknown_operator(client, setup, filetype):
262
    csvdata, url = setup(filename=filetype, data=get_file_content(filetype))
263
    filters = {'id__whatever': '25', 'fname__icontains':'Eliot'}
264
    resp = client.get(url, filters)
265
    result = json.loads(resp.content)
266
    assert result['err'] == 1
267
    assert result['err_class'] == 'passerelle.apps.csvdatasource.lookups.InvalidOperatorError'
268
    assert result['err_desc'] == 'whatever is not a valid operator'
269

  
270 237

  
271 238
def test_dialect(client, setup):
272 239
    csvdata, url = setup(data=data)
......
280 247
    }
281 248

  
282 249
    assert expected == csvdata.dialect_options
283
    filters = {'id__gt': '20', 'id__lt': '40', 'fname__icontains': 'Sandra'}
250
    filters = {'id': '22', 'fname': 'Sandra'}
284 251
    resp = client.get(url, filters)
285 252
    result = parse_response(resp)
286 253
    assert len(result) == 1
287
-