move localization into add_result_details

This means that the locale now needs to be handed in into the search
functions already. At least search needs them for reranking.
This commit is contained in:
Sarah Hoffmann
2023-09-19 11:17:04 +02:00
parent 8106e67f14
commit 5762a5bc80
6 changed files with 61 additions and 60 deletions

View File

@@ -292,12 +292,6 @@ class SearchResults(List[SearchResult]):
May be empty when no result was found. May be empty when no result was found.
""" """
def localize(self, locales: Locales) -> None:
""" Apply the given locales to all results.
"""
for result in self:
result.localize(locales)
def _filter_geometries(row: SaRow) -> Dict[str, str]: def _filter_geometries(row: SaRow) -> Dict[str, str]:
return {k[9:]: v for k, v in row._mapping.items() # pylint: disable=W0212 return {k[9:]: v for k, v in row._mapping.items() # pylint: disable=W0212
@@ -459,6 +453,8 @@ async def add_result_details(conn: SearchConnection, results: List[BaseResultT],
log().comment('Query keywords') log().comment('Query keywords')
for result in results: for result in results:
await complete_keywords(conn, result) await complete_keywords(conn, result)
for result in results:
result.localize(details.locales)
def _result_row_to_address_row(row: SaRow) -> AddressLine: def _result_row_to_address_row(row: SaRow) -> AddressLine:

View File

@@ -17,6 +17,7 @@ from struct import unpack
from binascii import unhexlify from binascii import unhexlify
from nominatim.errors import UsageError from nominatim.errors import UsageError
from nominatim.api.localization import Locales
# pylint: disable=no-member,too-many-boolean-expressions,too-many-instance-attributes # pylint: disable=no-member,too-many-boolean-expressions,too-many-instance-attributes
@@ -386,7 +387,7 @@ TParam = TypeVar('TParam', bound='LookupDetails') # pylint: disable=invalid-name
@dataclasses.dataclass @dataclasses.dataclass
class LookupDetails: class LookupDetails:
""" Collection of parameters that define the amount of details """ Collection of parameters that define which kind of details are
returned with a lookup or details result. returned with a lookup or details result.
""" """
geometry_output: GeometryFormat = GeometryFormat.NONE geometry_output: GeometryFormat = GeometryFormat.NONE
@@ -413,6 +414,9 @@ class LookupDetails:
0.0 means the original geometry is kept. The higher the value, the 0.0 means the original geometry is kept. The higher the value, the
more the geometry gets simplified. more the geometry gets simplified.
""" """
locales: Locales = Locales()
""" Prefered languages for localization of results.
"""
@classmethod @classmethod
def from_kwargs(cls: Type[TParam], kwargs: Dict[str, Any]) -> TParam: def from_kwargs(cls: Type[TParam], kwargs: Dict[str, Any]) -> TParam:

View File

@@ -308,7 +308,8 @@ async def details_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) ->
keywords=params.get_bool('keywords', False), keywords=params.get_bool('keywords', False),
geometry_output = napi.GeometryFormat.GEOJSON geometry_output = napi.GeometryFormat.GEOJSON
if params.get_bool('polygon_geojson', False) if params.get_bool('polygon_geojson', False)
else napi.GeometryFormat.NONE else napi.GeometryFormat.NONE,
locales=locales
) )
if debug: if debug:
@@ -317,8 +318,6 @@ async def details_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) ->
if result is None: if result is None:
params.raise_error('No place with that OSM ID found.', status=404) params.raise_error('No place with that OSM ID found.', status=404)
result.localize(locales)
output = formatting.format_result(result, fmt, output = formatting.format_result(result, fmt,
{'locales': locales, {'locales': locales,
'group_hierarchy': params.get_bool('group_hierarchy', False), 'group_hierarchy': params.get_bool('group_hierarchy', False),
@@ -337,6 +336,7 @@ async def reverse_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) ->
details = params.parse_geometry_details(fmt) details = params.parse_geometry_details(fmt)
details['max_rank'] = helpers.zoom_to_rank(params.get_int('zoom', 18)) details['max_rank'] = helpers.zoom_to_rank(params.get_int('zoom', 18))
details['layers'] = params.get_layers() details['layers'] = params.get_layers()
details['locales'] = napi.Locales.from_accept_languages(params.get_accepted_languages())
result = await api.reverse(coord, **details) result = await api.reverse(coord, **details)
@@ -357,9 +357,6 @@ async def reverse_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) ->
'namedetails': params.get_bool('namedetails', False), 'namedetails': params.get_bool('namedetails', False),
'addressdetails': params.get_bool('addressdetails', True)} 'addressdetails': params.get_bool('addressdetails', True)}
if result:
result.localize(napi.Locales.from_accept_languages(params.get_accepted_languages()))
output = formatting.format_result(napi.ReverseResults([result] if result else []), output = formatting.format_result(napi.ReverseResults([result] if result else []),
fmt, fmt_options) fmt, fmt_options)
@@ -372,6 +369,7 @@ async def lookup_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) -> A
fmt = params.parse_format(napi.SearchResults, 'xml') fmt = params.parse_format(napi.SearchResults, 'xml')
debug = params.setup_debugging() debug = params.setup_debugging()
details = params.parse_geometry_details(fmt) details = params.parse_geometry_details(fmt)
details['locales'] = napi.Locales.from_accept_languages(params.get_accepted_languages())
places = [] places = []
for oid in (params.get('osm_ids') or '').split(','): for oid in (params.get('osm_ids') or '').split(','):
@@ -394,8 +392,6 @@ async def lookup_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) -> A
'namedetails': params.get_bool('namedetails', False), 'namedetails': params.get_bool('namedetails', False),
'addressdetails': params.get_bool('addressdetails', True)} 'addressdetails': params.get_bool('addressdetails', True)}
results.localize(napi.Locales.from_accept_languages(params.get_accepted_languages()))
output = formatting.format_result(results, fmt, fmt_options) output = formatting.format_result(results, fmt, fmt_options)
return params.build_response(output, num_results=len(results)) return params.build_response(output, num_results=len(results))
@@ -456,6 +452,8 @@ async def search_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) -> A
else: else:
details['layers'] = params.get_layers() details['layers'] = params.get_layers()
details['locales'] = napi.Locales.from_accept_languages(params.get_accepted_languages())
# unstructured query parameters # unstructured query parameters
query = params.get('q', None) query = params.get('q', None)
# structured query parameters # structured query parameters
@@ -480,8 +478,6 @@ async def search_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) -> A
except UsageError as err: except UsageError as err:
params.raise_error(str(err)) params.raise_error(str(err))
results.localize(napi.Locales.from_accept_languages(params.get_accepted_languages()))
if details['dedupe'] and len(results) > 1: if details['dedupe'] and len(results) > 1:
results = helpers.deduplicate_results(results, max_results) results = helpers.deduplicate_results(results, max_results)

View File

@@ -109,7 +109,8 @@ class APISearch:
'countries': args.countrycodes, 'countries': args.countrycodes,
'excluded': args.exclude_place_ids, 'excluded': args.exclude_place_ids,
'viewbox': args.viewbox, 'viewbox': args.viewbox,
'bounded_viewbox': args.bounded 'bounded_viewbox': args.bounded,
'locales': args.get_locales(api.config.DEFAULT_LANGUAGE)
} }
if args.query: if args.query:
@@ -124,9 +125,6 @@ class APISearch:
country=args.country, country=args.country,
**params) **params)
for result in results:
result.localize(args.get_locales(api.config.DEFAULT_LANGUAGE))
if args.dedupe and len(results) > 1: if args.dedupe and len(results) > 1:
results = deduplicate_results(results, args.limit) results = deduplicate_results(results, args.limit)
@@ -187,14 +185,14 @@ class APIReverse:
layers=args.get_layers(napi.DataLayer.ADDRESS | napi.DataLayer.POI), layers=args.get_layers(napi.DataLayer.ADDRESS | napi.DataLayer.POI),
address_details=True, # needed for display name address_details=True, # needed for display name
geometry_output=args.get_geometry_output(), geometry_output=args.get_geometry_output(),
geometry_simplification=args.polygon_threshold) geometry_simplification=args.polygon_threshold,
locales=args.get_locales(api.config.DEFAULT_LANGUAGE))
if args.format == 'debug': if args.format == 'debug':
print(loglib.get_and_disable()) print(loglib.get_and_disable())
return 0 return 0
if result: if result:
result.localize(args.get_locales(api.config.DEFAULT_LANGUAGE))
output = api_output.format_result( output = api_output.format_result(
napi.ReverseResults([result]), napi.ReverseResults([result]),
args.format, args.format,
@@ -249,10 +247,8 @@ class APILookup:
results = api.lookup(places, results = api.lookup(places,
address_details=True, # needed for display name address_details=True, # needed for display name
geometry_output=args.get_geometry_output(), geometry_output=args.get_geometry_output(),
geometry_simplification=args.polygon_threshold or 0.0) geometry_simplification=args.polygon_threshold or 0.0,
locales=args.get_locales(api.config.DEFAULT_LANGUAGE))
for result in results:
result.localize(args.get_locales(api.config.DEFAULT_LANGUAGE))
output = api_output.format_result( output = api_output.format_result(
results, results,
@@ -326,6 +322,7 @@ class APIDetails:
api = napi.NominatimAPI(args.project_dir) api = napi.NominatimAPI(args.project_dir)
locales = args.get_locales(api.config.DEFAULT_LANGUAGE)
result = api.details(place, result = api.details(place,
address_details=args.addressdetails, address_details=args.addressdetails,
linked_places=args.linkedplaces, linked_places=args.linkedplaces,
@@ -333,13 +330,11 @@ class APIDetails:
keywords=args.keywords, keywords=args.keywords,
geometry_output=napi.GeometryFormat.GEOJSON geometry_output=napi.GeometryFormat.GEOJSON
if args.polygon_geojson if args.polygon_geojson
else napi.GeometryFormat.NONE) else napi.GeometryFormat.NONE,
locales=locales)
if result: if result:
locales = args.get_locales(api.config.DEFAULT_LANGUAGE)
result.localize(locales)
output = api_output.format_result( output = api_output.format_result(
result, result,
'json', 'json',

View File

@@ -150,17 +150,20 @@ def test_lookup_placex_with_address_details(apiobj):
category=('highway', 'residential'), category=('highway', 'residential'),
names={'name': 'Street'}, extratags={}, names={'name': 'Street'}, extratags={},
admin_level=15, fromarea=True, isaddress=True, admin_level=15, fromarea=True, isaddress=True,
rank_address=26, distance=0.0), rank_address=26, distance=0.0,
local_name='Street'),
napi.AddressLine(place_id=1000, osm_object=('N', 3333), napi.AddressLine(place_id=1000, osm_object=('N', 3333),
category=('place', 'suburb'), category=('place', 'suburb'),
names={'name': 'Smallplace'}, extratags={}, names={'name': 'Smallplace'}, extratags={},
admin_level=13, fromarea=False, isaddress=True, admin_level=13, fromarea=False, isaddress=True,
rank_address=23, distance=0.0034), rank_address=23, distance=0.0034,
local_name='Smallplace'),
napi.AddressLine(place_id=1001, osm_object=('N', 3334), napi.AddressLine(place_id=1001, osm_object=('N', 3334),
category=('place', 'city'), category=('place', 'city'),
names={'name': 'Bigplace'}, extratags={}, names={'name': 'Bigplace'}, extratags={},
admin_level=15, fromarea=True, isaddress=True, admin_level=15, fromarea=True, isaddress=True,
rank_address=16, distance=0.0), rank_address=16, distance=0.0,
local_name='Bigplace'),
napi.AddressLine(place_id=None, osm_object=None, napi.AddressLine(place_id=None, osm_object=None,
category=('place', 'country_code'), category=('place', 'country_code'),
names={'ref': 'pl'}, extratags={}, names={'ref': 'pl'}, extratags={},
@@ -341,22 +344,26 @@ def test_lookup_osmline_with_address_details(apiobj):
category=('place', 'house_number'), category=('place', 'house_number'),
names={'ref': '2'}, extratags={}, names={'ref': '2'}, extratags={},
admin_level=None, fromarea=True, isaddress=True, admin_level=None, fromarea=True, isaddress=True,
rank_address=28, distance=0.0), rank_address=28, distance=0.0,
local_name='2'),
napi.AddressLine(place_id=332, osm_object=('W', 4), napi.AddressLine(place_id=332, osm_object=('W', 4),
category=('highway', 'residential'), category=('highway', 'residential'),
names={'name': 'Street'}, extratags={}, names={'name': 'Street'}, extratags={},
admin_level=15, fromarea=True, isaddress=True, admin_level=15, fromarea=True, isaddress=True,
rank_address=26, distance=0.0), rank_address=26, distance=0.0,
local_name='Street'),
napi.AddressLine(place_id=1000, osm_object=('N', 3333), napi.AddressLine(place_id=1000, osm_object=('N', 3333),
category=('place', 'suburb'), category=('place', 'suburb'),
names={'name': 'Smallplace'}, extratags={}, names={'name': 'Smallplace'}, extratags={},
admin_level=13, fromarea=False, isaddress=True, admin_level=13, fromarea=False, isaddress=True,
rank_address=23, distance=0.0034), rank_address=23, distance=0.0034,
local_name='Smallplace'),
napi.AddressLine(place_id=1001, osm_object=('N', 3334), napi.AddressLine(place_id=1001, osm_object=('N', 3334),
category=('place', 'city'), category=('place', 'city'),
names={'name': 'Bigplace'}, extratags={}, names={'name': 'Bigplace'}, extratags={},
admin_level=15, fromarea=True, isaddress=True, admin_level=15, fromarea=True, isaddress=True,
rank_address=16, distance=0.0), rank_address=16, distance=0.0,
local_name='Bigplace'),
napi.AddressLine(place_id=None, osm_object=None, napi.AddressLine(place_id=None, osm_object=None,
category=('place', 'country_code'), category=('place', 'country_code'),
names={'ref': 'pl'}, extratags={}, names={'ref': 'pl'}, extratags={},
@@ -441,22 +448,26 @@ def test_lookup_tiger_with_address_details(apiobj):
category=('place', 'house_number'), category=('place', 'house_number'),
names={'ref': '2'}, extratags={}, names={'ref': '2'}, extratags={},
admin_level=None, fromarea=True, isaddress=True, admin_level=None, fromarea=True, isaddress=True,
rank_address=28, distance=0.0), rank_address=28, distance=0.0,
local_name='2'),
napi.AddressLine(place_id=332, osm_object=('W', 4), napi.AddressLine(place_id=332, osm_object=('W', 4),
category=('highway', 'residential'), category=('highway', 'residential'),
names={'name': 'Street'}, extratags={}, names={'name': 'Street'}, extratags={},
admin_level=15, fromarea=True, isaddress=True, admin_level=15, fromarea=True, isaddress=True,
rank_address=26, distance=0.0), rank_address=26, distance=0.0,
local_name='Street'),
napi.AddressLine(place_id=1000, osm_object=('N', 3333), napi.AddressLine(place_id=1000, osm_object=('N', 3333),
category=('place', 'suburb'), category=('place', 'suburb'),
names={'name': 'Smallplace'}, extratags={}, names={'name': 'Smallplace'}, extratags={},
admin_level=13, fromarea=False, isaddress=True, admin_level=13, fromarea=False, isaddress=True,
rank_address=23, distance=0.0034), rank_address=23, distance=0.0034,
local_name='Smallplace'),
napi.AddressLine(place_id=1001, osm_object=('N', 3334), napi.AddressLine(place_id=1001, osm_object=('N', 3334),
category=('place', 'city'), category=('place', 'city'),
names={'name': 'Bigplace'}, extratags={}, names={'name': 'Bigplace'}, extratags={},
admin_level=15, fromarea=True, isaddress=True, admin_level=15, fromarea=True, isaddress=True,
rank_address=16, distance=0.0), rank_address=16, distance=0.0,
local_name='Bigplace'),
napi.AddressLine(place_id=None, osm_object=None, napi.AddressLine(place_id=None, osm_object=None,
category=('place', 'country_code'), category=('place', 'country_code'),
names={'ref': 'us'}, extratags={}, names={'ref': 'us'}, extratags={},
@@ -536,17 +547,20 @@ def test_lookup_postcode_with_address_details(apiobj):
category=('place', 'suburb'), category=('place', 'suburb'),
names={'name': 'Smallplace'}, extratags={}, names={'name': 'Smallplace'}, extratags={},
admin_level=13, fromarea=True, isaddress=True, admin_level=13, fromarea=True, isaddress=True,
rank_address=23, distance=0.0), rank_address=23, distance=0.0,
local_name='Smallplace'),
napi.AddressLine(place_id=1001, osm_object=('N', 3334), napi.AddressLine(place_id=1001, osm_object=('N', 3334),
category=('place', 'city'), category=('place', 'city'),
names={'name': 'Bigplace'}, extratags={}, names={'name': 'Bigplace'}, extratags={},
admin_level=15, fromarea=True, isaddress=True, admin_level=15, fromarea=True, isaddress=True,
rank_address=16, distance=0.0), rank_address=16, distance=0.0,
local_name='Bigplace'),
napi.AddressLine(place_id=None, osm_object=None, napi.AddressLine(place_id=None, osm_object=None,
category=('place', 'postcode'), category=('place', 'postcode'),
names={'ref': '34 425'}, extratags={}, names={'ref': '34 425'}, extratags={},
admin_level=None, fromarea=False, isaddress=True, admin_level=None, fromarea=False, isaddress=True,
rank_address=5, distance=0.0), rank_address=5, distance=0.0,
local_name='34 425'),
napi.AddressLine(place_id=None, osm_object=None, napi.AddressLine(place_id=None, osm_object=None,
category=('place', 'country_code'), category=('place', 'country_code'),
names={'ref': 'gb'}, extratags={}, names={'ref': 'gb'}, extratags={},

View File

@@ -67,7 +67,9 @@ class TestCliReverseCall:
result = napi.ReverseResult(napi.SourceTable.PLACEX, ('place', 'thing'), result = napi.ReverseResult(napi.SourceTable.PLACEX, ('place', 'thing'),
napi.Point(1.0, -3.0), napi.Point(1.0, -3.0),
names={'name':'Name', 'name:fr': 'Nom'}, names={'name':'Name', 'name:fr': 'Nom'},
extratags={'extra':'Extra'}) extratags={'extra':'Extra'},
locale_name='Name',
display_name='Name')
monkeypatch.setattr(napi.NominatimAPI, 'reverse', monkeypatch.setattr(napi.NominatimAPI, 'reverse',
lambda *args, **kwargs: result) lambda *args, **kwargs: result)
@@ -109,16 +111,6 @@ class TestCliReverseCall:
assert out['type'] == 'FeatureCollection' assert out['type'] == 'FeatureCollection'
def test_reverse_language(self, cli_call, tmp_path, capsys):
result = cli_call('reverse', '--project-dir', str(tmp_path),
'--lat', '34', '--lon', '34', '--lang', 'fr')
assert result == 0
out = json.loads(capsys.readouterr().out)
assert out['name'] == 'Nom'
class TestCliLookupCall: class TestCliLookupCall:
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
@@ -126,7 +118,9 @@ class TestCliLookupCall:
result = napi.SearchResult(napi.SourceTable.PLACEX, ('place', 'thing'), result = napi.SearchResult(napi.SourceTable.PLACEX, ('place', 'thing'),
napi.Point(1.0, -3.0), napi.Point(1.0, -3.0),
names={'name':'Name', 'name:fr': 'Nom'}, names={'name':'Name', 'name:fr': 'Nom'},
extratags={'extra':'Extra'}) extratags={'extra':'Extra'},
locale_name='Name',
display_name='Name')
monkeypatch.setattr(napi.NominatimAPI, 'lookup', monkeypatch.setattr(napi.NominatimAPI, 'lookup',
lambda *args, **kwargs: napi.SearchResults([result])) lambda *args, **kwargs: napi.SearchResults([result]))
@@ -150,9 +144,11 @@ class TestCliLookupCall:
]) ])
def test_search(cli_call, tmp_path, capsys, monkeypatch, endpoint, params): def test_search(cli_call, tmp_path, capsys, monkeypatch, endpoint, params):
result = napi.SearchResult(napi.SourceTable.PLACEX, ('place', 'thing'), result = napi.SearchResult(napi.SourceTable.PLACEX, ('place', 'thing'),
napi.Point(1.0, -3.0), napi.Point(1.0, -3.0),
names={'name':'Name', 'name:fr': 'Nom'}, names={'name':'Name', 'name:fr': 'Nom'},
extratags={'extra':'Extra'}) extratags={'extra':'Extra'},
locale_name='Name',
display_name='Name')
monkeypatch.setattr(napi.NominatimAPI, endpoint, monkeypatch.setattr(napi.NominatimAPI, endpoint,
lambda *args, **kwargs: napi.SearchResults([result])) lambda *args, **kwargs: napi.SearchResults([result]))