diff --git a/neon_api_proxy/services/map_maker_api.py b/neon_api_proxy/services/map_maker_api.py index 7cd6c10..db32ccf 100644 --- a/neon_api_proxy/services/map_maker_api.py +++ b/neon_api_proxy/services/map_maker_api.py @@ -59,6 +59,7 @@ def handle_query(self, **kwargs) -> dict: :param kwargs: 'lat' - optional str latitude 'lon' - optional str longitude + 'lang_code' - optional language code to request results in 'address' - optional string address/place to resolve :return: dict containing `status_code`, `content`, `encoding` from URL response @@ -66,7 +67,7 @@ def handle_query(self, **kwargs) -> dict: lat = kwargs.get("lat") lon = kwargs.get("lon", kwargs.get("lng")) address = kwargs.get('address') - + lang = kwargs.get('lang_code', "en") if not (address or (lat and lon)): # Missing data for lookup return {"status_code": -1, @@ -83,27 +84,49 @@ def handle_query(self, **kwargs) -> dict: if lat and lon: # Lookup address for coordinates try: - response = self._query_reverse(float(lat), float(lon)) + response = self._query_reverse(float(lat), float(lon), lang) except ValueError as e: return {"status_code": -1, "content": repr(e), "encoding": None} else: # Lookup coordinates for search term/address - response = self._query_geocode(address) + response = self._query_geocode(address, lang) self._last_query = time() + resp_lang = response.headers.get('Content-Language') + if resp_lang != lang: + # TODO: Translate? + LOG.warning(f"Response not translated to {lang}") return {"status_code": response.status_code, "content": response.content, "encoding": response.encoding} - def _query_geocode(self, address: str) -> Response: - query_str = urllib.parse.urlencode({"q": address, + def _translate_response(self, response: Response, lang: str) -> Response: + import json + resp_lang = response.headers.get('Content-Language') + if lang == resp_lang: + LOG.warning(f"Requested translation to the same language ({lang})") + return response + address = json.loads(response.content.decode(response.encoding))['address'] + for key, val in address.items(): + address[key] = str(val) + + def _get_translation(self, to_translate: str, in_lang: str, out_lang: str) -> str: + from neon_utils.hana_utils import request_backend + # TODO + return to_translate + + def _query_geocode(self, address: str, lang: str) -> Response: + self.session.headers["Content-Language"] = lang + query_str = urllib.parse.urlencode({"q": address, "lang": lang, "api_key": self._api_key}) request_url = f"{self.geocode_url}?{query_str}" return self.get_with_cache_timeout(request_url, self.cache_timeout) - def _query_reverse(self, lat: float, lon: float): + def _query_reverse(self, lat: float, lon: float, lang: str): + self.session.headers["Content-Language"] = lang query_str = urllib.parse.urlencode({"lat": lat, "lon": lon, + "lang": lang, "api_key": self._api_key}) request_url = f"{self.reverse_url}?{query_str}" return self.get_with_cache_timeout(request_url, self.cache_timeout) diff --git a/tests/test_map_maker_api.py b/tests/test_map_maker_api.py index 20d3a7c..ca7335f 100644 --- a/tests/test_map_maker_api.py +++ b/tests/test_map_maker_api.py @@ -70,6 +70,16 @@ def test_geocode_lookup(self): self.assertAlmostEqual(float(valid_location['lon']), -115.17, delta=0.02) + # Test language + valid_es_location = self.api.handle_query(address=VALID_ADDRESS_2, + lang_code="es") + self.assertEqual(valid_es_location['status_code'], 200) + self.assertEqual(valid_es_location["encoding"].lower(), "utf-8") + es_location = json.loads(valid_es_location["content"])[0] + self.assertEqual(valid_location['lat'], es_location['lat'], es_location) + self.assertEqual(valid_location['lon'], es_location['lon'], es_location) + # self.assertNotEqual(valid_location, es_location) + invalid_response = self.api.handle_query(address=INVALID_ADDRESS) self.assertEqual(invalid_response['status_code'], -1) @@ -81,6 +91,19 @@ def test_reverse_lookup(self): self.assertEqual(valid_location['state'], "Washington", valid_location) self.assertEqual(valid_location['town'], "Renton", valid_location) + # Test language + valid_german = self.api.handle_query(lat=51.233334, lon=6.783333, + lang_code="de") + self.assertEqual(json.loads(valid_german["content"])['address']['city'], + "Düsseldorf") + + valid_es_location = self.api.handle_query(lat=VALID_LAT, lon=VALID_LON, + lang_code="es") + self.assertEqual(valid_es_location['status_code'], 200) + self.assertEqual(valid_es_location["encoding"].lower(), "utf-8") + es_location = json.loads(valid_es_location["content"])['address'] + self.assertNotEqual(valid_location, es_location) + invalid_response = self.api.handle_query(lat=VALID_LAT, lon=None) self.assertEqual(invalid_response['status_code'], -1) diff --git a/version_bump.py b/version_bump.py deleted file mode 100644 index b107127..0000000 --- a/version_bump.py +++ /dev/null @@ -1,54 +0,0 @@ -# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework -# All trademark and other rights reserved by their respective owners -# Copyright 2008-2025 Neongecko.com Inc. -# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds, -# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo -# BSD-3 License -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from this -# software without specific prior written permission. -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import fileinput -from os.path import join, dirname - -with open(join(dirname(__file__), "version.py"), "r", encoding="utf-8") as v: - for line in v.readlines(): - if line.startswith("__version__"): - if '"' in line: - version = line.split('"')[1] - else: - version = line.split("'")[1] - -if "a" not in version: - parts = version.split('.') - parts[-1] = str(int(parts[-1]) + 1) - version = '.'.join(parts) - version = f"{version}a0" -else: - post = version.split("a")[1] - new_post = int(post) + 1 - version = version.replace(f"a{post}", f"a{new_post}") - -for line in fileinput.input(join(dirname(__file__), "version.py"), inplace=True): - if line.startswith("__version__"): - print(f"__version__ = \"{version}\"") - else: - print(line.rstrip('\n'))