opendatasoft: add sort field (#54442)

This commit is contained in:
Nicolas Roche 2021-06-25 19:24:46 +02:00
parent cac06da89e
commit 27380a6dd1
3 changed files with 138 additions and 17 deletions

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2021-06-25 17:01
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('opendatasoft', '0002_auto_20210625_1852'),
]
operations = [
migrations.AddField(
model_name='query',
name='sort',
field=models.CharField(
blank=True,
help_text='Sorts results by the specified field. A minus sign - may be used to perform an ascending sort.',
max_length=256,
verbose_name='Sort field',
),
),
]

View File

@ -64,23 +64,24 @@ class OpenDataSoft(BaseResource):
Query.objects.bulk_create(queries)
return instance
def call_search(self, dataset=None, text_template='', filter_expression='', id=None, q=None, limit=None):
def call_search(
self, dataset=None, text_template='', filter_expression='', sort=None, id=None, q=None, limit=None
):
scheme, netloc, path, params, query, fragment = urlparse.urlparse(self.service_url)
path = urlparse.urljoin(path, 'api/records/1.0/search/')
url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
params = {'dataset': dataset}
if id is not None:
query = 'recordid:%s' % id
else:
query = q
params = {
'dataset': dataset,
'q': query,
}
params['q'] = 'recordid:%s' % id
elif q is not None:
params['q'] = q
elif sort:
params['sort'] = sort
if self.api_key:
params.update({'apikey': self.api_key})
params['apikey'] = self.api_key
if limit:
params.update({'rows': limit})
params['rows'] = limit
params.update(urlparse.parse_qs(filter_expression))
result_response = self.requests.get(url, params=params)
@ -107,13 +108,16 @@ class OpenDataSoft(BaseResource):
parameters={
'dataset': {'description': _('Dataset')},
'text_template': {'description': _('Text template')},
'sort': {'description': _('Sort field')},
'id': {'description': _('Record identifier')},
'q': {'description': _('Full text query')},
'limit': {'description': _('Maximum items')},
},
)
def search(self, request, dataset=None, text_template='', id=None, q=None, limit=None, **kwargs):
result = self.call_search(dataset, text_template, '', id, q, limit)
def search(
self, request, dataset=None, text_template='', sort=None, id=None, q=None, limit=None, **kwargs
):
result = self.call_search(dataset, text_template, '', sort, id, q, limit)
return {'data': result}
@endpoint(
@ -153,6 +157,14 @@ class Query(BaseQuery):
help_text=_('Specify refine and exclude facet expressions separated lines'),
blank=True,
)
sort = models.CharField(
verbose_name=_('Sort field'),
help_text=_(
"Sorts results by the specified field. A minus sign - may be used to perform an ascending sort."
),
max_length=256,
blank=True,
)
delete_view = 'opendatasoft-query-delete'
edit_view = 'opendatasoft-query-edit'
@ -164,6 +176,7 @@ class Query(BaseQuery):
filter_expression='&'.join(
[x.strip() for x in str(self.filter_expression).splitlines() if x.strip()]
),
sort=self.sort,
id=kwargs.get('id'),
q=kwargs.get('q'),
limit=kwargs.get('limit'),

View File

@ -143,6 +143,7 @@ def query(connector):
description='Rechercher une adresse',
dataset='referentiel-adresse-test',
text_template='{{numero}} {{nom_rue}} {{nom_commun}}',
sort='-nom_rue',
filter_expression='''
refine.source=Ville et Eurométropole de Strasbourg
exclude.numero=42
@ -188,6 +189,42 @@ def test_search_empty_contents(mocked_get, app, connector):
assert resp.json['err_desc'] == "The query is invalid : Field 00 doesn't exist"
@mock.patch('passerelle.utils.Request.get')
def test_search(mocked_get, app, connector):
endpoint = utils.generic_endpoint_url('opendatasoft', 'search', slug=connector.slug)
assert endpoint == '/opendatasoft/my_connector/search'
params = {
'dataset': 'referentiel-adresse-test',
'text_template': '{{numero}} {{nom_rue}} {{nom_commun}}',
'sort': '-nom_rue',
'limit': 3,
}
mocked_get.return_value = utils.FakedResponse(content=FAKED_CONTENT_Q_SEARCH, status_code=200)
resp = app.get(endpoint, params=params, status=200)
assert mocked_get.call_args[1]['params'] == {
'apikey': 'my_secret',
'dataset': 'referentiel-adresse-test',
'sort': '-nom_rue',
'rows': '3',
}
assert not resp.json['err']
assert len(resp.json['data']) == 3
# check order is kept
assert [x['id'] for x in resp.json['data']] == [
'e00cf6161e52a4c8fe510b2b74d4952036cb3473',
'7cafcd5c692773e8b863587b2d38d6be82e023d8',
'0984a5e1745701f71c91af73ce764e1f7132e0ff',
]
# check text results
assert [x['text'] for x in resp.json['data']] == [
"33 RUE DE L'AUBEPINE Strasbourg",
"19 RUE DE L'AUBEPINE Lipsheim",
"29 RUE DE L'AUBEPINE Strasbourg",
]
# check additional attributes
assert [x['numero'] for x in resp.json['data']] == ['33', '19', '29']
@mock.patch('passerelle.utils.Request.get')
def test_search_using_q(mocked_get, app, connector):
endpoint = utils.generic_endpoint_url('opendatasoft', 'search', slug=connector.slug)
@ -195,11 +232,18 @@ def test_search_using_q(mocked_get, app, connector):
params = {
'dataset': 'referentiel-adresse-test',
'text_template': '{{numero}} {{nom_rue}} {{nom_commun}}',
'sort': '-nom_rue',
'q': "rue de l'aubepine",
'limit': 3,
}
mocked_get.return_value = utils.FakedResponse(content=FAKED_CONTENT_Q_SEARCH, status_code=200)
resp = app.get(endpoint, params=params, status=200)
assert mocked_get.call_args[1]['params'] == {
'apikey': 'my_secret',
'dataset': 'referentiel-adresse-test',
'q': "rue de l'aubepine",
'rows': '3',
}
assert not resp.json['err']
assert len(resp.json['data']) == 3
# check order is kept
@ -229,10 +273,49 @@ def test_search_using_id(mocked_get, app, connector):
}
mocked_get.return_value = utils.FakedResponse(content=FAKED_CONTENT_ID_SEARCH, status_code=200)
resp = app.get(endpoint, params=params, status=200)
assert mocked_get.call_args[1]['params'] == {
'apikey': 'my_secret',
'dataset': 'referentiel-adresse-test',
'q': 'recordid:7cafcd5c692773e8b863587b2d38d6be82e023d8',
}
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['text'] == "19 RUE DE L'AUBEPINE Lipsheim"
@mock.patch('passerelle.utils.Request.get')
def test_query_q(mocked_get, app, query):
endpoint = '/opendatasoft/my_connector/q/my_query/'
params = {
'limit': 3,
}
mocked_get.return_value = utils.FakedResponse(content=FAKED_CONTENT_Q_SEARCH, status_code=200)
resp = app.get(endpoint, params=params, status=200)
assert mocked_get.call_args[1]['params'] == {
'apikey': 'my_secret',
'dataset': 'referentiel-adresse-test',
'refine.source': ['Ville et Eurométropole de Strasbourg'],
'exclude.numero': ['42', '43'],
'sort': '-nom_rue',
'rows': '3',
}
assert not resp.json['err']
assert len(resp.json['data']) == 3
# check order is kept
assert [x['id'] for x in resp.json['data']] == [
'e00cf6161e52a4c8fe510b2b74d4952036cb3473',
'7cafcd5c692773e8b863587b2d38d6be82e023d8',
'0984a5e1745701f71c91af73ce764e1f7132e0ff',
]
# check text results
assert [x['text'] for x in resp.json['data']] == [
"33 RUE DE L'AUBEPINE Strasbourg",
"19 RUE DE L'AUBEPINE Lipsheim",
"29 RUE DE L'AUBEPINE Strasbourg",
]
# check additional attributes
assert [x['numero'] for x in resp.json['data']] == ['33', '19', '29']
@mock.patch('passerelle.utils.Request.get')
def test_query_q_using_q(mocked_get, app, query):
endpoint = '/opendatasoft/my_connector/q/my_query/'
@ -243,12 +326,12 @@ def test_query_q_using_q(mocked_get, app, query):
mocked_get.return_value = utils.FakedResponse(content=FAKED_CONTENT_Q_SEARCH, status_code=200)
resp = app.get(endpoint, params=params, status=200)
assert mocked_get.call_args[1]['params'] == {
'dataset': 'referentiel-adresse-test',
'q': "rue de l'aubepine",
'apikey': 'my_secret',
'rows': '3',
'dataset': 'referentiel-adresse-test',
'refine.source': ['Ville et Eurométropole de Strasbourg'],
'exclude.numero': ['42', '43'],
'q': "rue de l'aubepine",
'rows': '3',
}
assert not resp.json['err']
assert len(resp.json['data']) == 3
@ -277,11 +360,11 @@ def test_query_q_using_id(mocked_get, app, query):
mocked_get.return_value = utils.FakedResponse(content=FAKED_CONTENT_ID_SEARCH, status_code=200)
resp = app.get(endpoint, params=params, status=200)
assert mocked_get.call_args[1]['params'] == {
'dataset': 'referentiel-adresse-test',
'q': 'recordid:7cafcd5c692773e8b863587b2d38d6be82e023d8',
'apikey': 'my_secret',
'dataset': 'referentiel-adresse-test',
'refine.source': ['Ville et Eurométropole de Strasbourg'],
'exclude.numero': ['42', '43'],
'q': 'recordid:7cafcd5c692773e8b863587b2d38d6be82e023d8',
}
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['text'] == "19 RUE DE L'AUBEPINE Lipsheim"