diff --git a/passerelle/apps/base_adresse/models.py b/passerelle/apps/base_adresse/models.py index d913c5df..bf70d596 100644 --- a/passerelle/apps/base_adresse/models.py +++ b/passerelle/apps/base_adresse/models.py @@ -125,10 +125,24 @@ class BaseAdresse(AddressResource): 'Prioritize results according to coordinates. "lat" parameter must also be present.' ) }, + 'type': { + 'description': _( + 'Type of address to return, housenumber, street, locality, municipality or all. Default is all.' + ) + }, }, ) def addresses( - self, request, id=None, q=None, zipcode='', citycode=None, lat=None, lon=None, page_limit=5 + self, + request, + id=None, + q=None, + zipcode='', + citycode=None, + lat=None, + lon=None, + page_limit=5, + type=None, ): if id is not None: return self.get_by_id(request, id=id, citycode=citycode) @@ -156,6 +170,8 @@ class BaseAdresse(AddressResource): if self.latitude and self.longitude or lat and lon: query_args['lat'] = lat or self.latitude query_args['lon'] = lon or self.longitude + if type in ('housenumber', 'street', 'locality', 'municipality'): + query_args['type'] = type query = urlencode(query_args) url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) @@ -167,7 +183,8 @@ class BaseAdresse(AddressResource): result = [] - for feature in result_response.json().get('features'): + features = result_response.json().get('features') + for feature in features: if not feature['geometry']['type'] == 'Point': continue # skip unknown data = self.format_address_data(feature) @@ -177,7 +194,6 @@ class BaseAdresse(AddressResource): ) if not created: address.update_timestamp() - return {'data': result} def get_by_id(self, request, id, citycode=None): @@ -236,13 +252,25 @@ class BaseAdresse(AddressResource): 'Prioritize results according to coordinates. "lon" parameter must be present.' ) }, + 'type': { + 'description': _( + 'Type of address to return, housenumber, street, locality, municipality or all. Default is all.' + ) + }, }, ) - def search(self, request, q, zipcode='', citycode=None, lat=None, lon=None, **kwargs): + def search(self, request, q, zipcode='', citycode=None, lat=None, lon=None, type=None, **kwargs): if kwargs.get('format', 'json') != 'json': raise NotImplementedError() result = self.addresses( - request, q=q, zipcode=zipcode, citycode=citycode, lat=lat, lon=lon, page_limit=1 + request, + q=q, + zipcode=zipcode, + citycode=citycode, + lat=lat, + lon=lon, + page_limit=1, + type=type, ) return result['data'] @@ -251,15 +279,23 @@ class BaseAdresse(AddressResource): parameters={ 'lat': {'description': _('Latitude'), 'example_value': 48.833708}, 'lon': {'description': _('Longitude'), 'example_value': 2.323349}, + 'type': { + 'description': _( + 'Type of address to return, housenumber, street, locality, municipality or all. Default is all.' + ) + }, }, ) - def reverse(self, request, lat, lon, **kwargs): + def reverse(self, request, lat, lon, type=None, **kwargs): if kwargs.get('format', 'json') != 'json': raise NotImplementedError() scheme, netloc, path, params, query, fragment = urlparse.urlparse(self.service_url) path = urlparse.urljoin(path, 'reverse/') - query = urlencode({'lat': lat, 'lon': lon}) + query_dict = {'lat': lat, 'lon': lon} + if type in ('housenumber', 'street', 'locality', 'municipality'): + query_dict['type'] = type + query = urlencode(query_dict) url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) try: diff --git a/tests/test_base_adresse.py b/tests/test_base_adresse.py index 0fe86d14..61eda666 100644 --- a/tests/test_base_adresse.py +++ b/tests/test_base_adresse.py @@ -211,6 +211,21 @@ def test_base_adresse_search(mocked_get, app, base_adresse): assert data['display_name'] == 'Rue Roger Halope 49000 Angers' +@mock.patch('passerelle.utils.Request.get') +def test_base_adresse_search_type_housenumber(mocked_get, app, base_adresse): + endpoint = tests.utils.generic_endpoint_url('base-adresse', 'search', slug=base_adresse.slug) + mocked_get.return_value = tests.utils.FakedResponse(content=FAKED_CONTENT, status_code=200) + app.get(endpoint, params={'q': 'plop'}, status=200) + assert 'type=' not in mocked_get.call_args[0][0] + + for type in ['housenumber', 'street', 'locality', 'municipality']: + app.get(endpoint, params={'q': 'plop', 'type': type}, status=200) + assert f'type={type}' in mocked_get.call_args[0][0] + + app.get(endpoint, params={'q': 'plop', 'type': 'foo'}, status=200) + assert 'type=foo' not in mocked_get.call_args[0][0] + + @mock.patch('passerelle.utils.Request.get') def test_base_adresse_search_limit_to_200(mocked_get, app, base_adresse): endpoint = tests.utils.generic_endpoint_url('base-adresse', 'search', slug=base_adresse.slug) @@ -366,6 +381,18 @@ def test_base_adresse_reverse_path(mocked_get, app, base_adresse): assert mocked_get.call_args[0][0].startswith('http://example.net/path/reverse/?') +@mock.patch('passerelle.utils.Request.get') +def test_base_adresse_reverse_type_housenumber(mocked_get, app, base_adresse): + mocked_get.return_value = tests.utils.FakedResponse(content=json.dumps({'features': []}), status_code=200) + app.get('/base-adresse/%s/reverse?lon=-0.593775&lat=47.474633' % base_adresse.slug) + assert 'type=' not in mocked_get.call_args[0][0] + app.get('/base-adresse/%s/reverse?lon=-0.593775&lat=47.474633&type=truc' % base_adresse.slug) + assert 'type=' not in mocked_get.call_args[0][0] + for type in ['housenumber', 'street', 'locality', 'municipality']: + app.get(f'/base-adresse/%s/reverse?lon=-0.593775&lat=47.474633&type={type}' % base_adresse.slug) + assert f'type={type}' in mocked_get.call_args[0][0] + + @mock.patch('passerelle.utils.Request.get') def test_base_adresse_reverse_api_timeout(mocked_get, app, base_adresse): mocked_get.side_effect = ConnectionError('Remote end closed connection without response')