From b7c8e48907030051a49d9976ab4befad1b8aa2e0 Mon Sep 17 00:00:00 2001 From: Manuela Sabatino Date: Thu, 28 Nov 2024 18:05:03 +0000 Subject: [PATCH] update tests with locale and handle the quota error --- .gitignore | 1 + README.md | 28 ++++++------ tests/test_w3w.py | 92 +++++++++++++++++++++++++++++++++------- what3words/version.py | 2 +- what3words/what3words.py | 33 +++++++++++--- 5 files changed, 119 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index 286d518..8689a00 100644 --- a/.gitignore +++ b/.gitignore @@ -235,3 +235,4 @@ pyvenv.cfg pip-selfcheck.json # End of https://www.toptal.com/developers/gitignore/api/python,macos,venv,git +.vscode/* \ No newline at end of file diff --git a/README.md b/README.md index ff6cc55..cb9faaa 100644 --- a/README.md +++ b/README.md @@ -88,33 +88,33 @@ Retrieves a list of the currently loaded and available 3 word address languages. The returned payload from the `available-languages` method is described in the [what3words REST API documentation](https://docs.what3words.com/api/v3/#available-languages). -## isPossible3wa +## is_possible_3wa This method takes a string as a parameter and returns whether the string is in the format of a 3WA (eg “filled.count.soap”). Return type is boolean. NOTE: Does not check if it is an actual existing 3WA. ``` -isPossible3wa(“filled.count.soap”) returns True -isPossible3wa(“not a 3wa”) returns False -isPossible3wa(“not.3wa address”) returns False +is_possible_3wa(“filled.count.soap”) returns True +is_possible_3wa(“not a 3wa”) returns False +is_possible_3wa(“not.3wa address”) returns False ``` -## findPossible3wa +## find_possible_3wa This method takes a string as a parameter and searches the string for any possible instances of a 3WA - e.g. "leave in my porch at word.word.word." Likely to be the main method that is called on the delivery notes. Returns an array of matched items. Returns an empty array if no matches are found. NOTE: Does not check if it is an actual existing 3WA. ``` -findPossible3wa(“Please leave by my porch at filled.count.soap”) will return [‘filled.count.soap’] -findPossible3wa(“Please leave by my porch at filled.count.soap or deed.tulip.judge”) will return [‘filled.count.soap’, ‘deed.tulip.judge’] -findPossible3wa(“Please leave by my porch at”) will return [] +find_possible_3wa(“Please leave by my porch at filled.count.soap”) will return [‘filled.count.soap’] +find_possible_3wa(“Please leave by my porch at filled.count.soap or deed.tulip.judge”) will return [‘filled.count.soap’, ‘deed.tulip.judge’] +find_possible_3wa(“Please leave by my porch at”) will return [] ``` -## isValid3wa +## is_valid_3wa -This method takes a string as a parameter and first passes it through the W3W regex filter (akin to calling isPossible3wa() on the string) and then calls the W3W api to verify it is a real 3WA. +This method takes a string as a parameter and first passes it through the W3W regex filter (akin to calling is_possible_3wa() on the string) and then calls the W3W api to verify it is a real 3WA. ``` -isValid3wa(“filled.count.soap”) returns True -isValid3wa(“filled.count.”) returns False -isValid3wa(“python.is.cool”) returns False +is_valid_3wa(“filled.count.soap”) returns True +is_valid_3wa(“filled.count.”) returns False +is_valid_3wa(“python.is.cool”) returns False ``` ## Code examples @@ -160,7 +160,7 @@ Anyone and everyone is welcome to contribute. # Revision History - +* `v3.4.0` 28/11/24 - Support locale for the autosuggest and update the readme file * `v3.3.0` 30/09/24 - Support locale, update regex, format and tests * `v3.2.0` 08/03/22 - Added regex functions * `v3.1.1` 04/10/19 - Fix bugs related to setting default language value, and autosuggest input-type diff --git a/tests/test_w3w.py b/tests/test_w3w.py index 96026fd..d6b2dbc 100644 --- a/tests/test_w3w.py +++ b/tests/test_w3w.py @@ -36,7 +36,8 @@ def test_convert_to_coordinates(self): def test_locale_with_convert_to_coordinates(self): # Test conversion to coordinates with a valid 3-word address and locale - result = self.geocoder.convert_to_coordinates(addr) + result = self.geocoder.convert_to_coordinates("напомена.илузија.дирљив") + print(result) if "error" in result: print(f"API error code: {result['error']['code']}") # Handle the Quota Exceeded error specifically for this function @@ -47,7 +48,7 @@ def test_locale_with_convert_to_coordinates(self): ) else: self.assertEqual(result["language"], "oo") - self.assertEqual(result["locale"], "oo_la") + self.assertEqual(result["locale"], "oo_cy") self.assertEqual(result["coordinates"]["lat"], lat) self.assertEqual(result["coordinates"]["lng"], lng) @@ -77,6 +78,35 @@ def test_locale_with_convert_to_3wa(self): self.assertEqual(result["coordinates"]["lat"], lat) self.assertEqual(result["coordinates"]["lng"], lng) + def test_locale_with_autosuggest(self): + # Test autosuggest with a valid partial 3-word address and a specific locale + partial_address = "напомена.илузија.дирљи" + locale = "oo_cy" + + result = self.geocoder.autosuggest(partial_address, locale=locale, language=locale) + print(result) + + if "error" in result: + print(f"API error code: {result['error']['code']}") + # Handle the Quota Exceeded error specifically for this function + self.assertEqual( + result["error"]["code"], + "QuotaExceeded", + "Expected QuotaExceeded error for Autosuggest with locale", + ) + else: + # Validate the locale and language in the response + self.assertIn("suggestions", result, "Expected 'suggestions' in the response") + self.assertGreater(len(result["suggestions"]), 0, "Expected at least one suggestion") + + # Check the language and locale of the first suggestion + first_suggestion = result["suggestions"][0] + self.assertEqual(first_suggestion["language"], "oo", "Language does not match the expected locale") + self.assertTrue( + first_suggestion["words"].startswith(partial_address), + "First suggestion does not match the input partial address" + ) + def test_available_languages(self): # Test available languages result = self.geocoder.available_languages() @@ -132,36 +162,64 @@ def test_autosuggest(self): ) def test_grid_section(self): - # Test grid section functionality with a bounding box sw = Coordinates(52.208867, 0.117540) ne = Coordinates(52.207988, 0.116126) bb = BoundingBox(sw, ne) - result = self.geocoder.grid_section(bb) - self.assertIsNotNone(result["lines"]) + print(result) # Debugging output - def test_invalid_address(self): - # Test invalid address - invalid_addr = "invalid.address.test" - result = self.geocoder.convert_to_coordinates(invalid_addr) if "error" in result: - print(f"API error code: {result['error']['code']}") - # Handle the Quota Exceeded error specifically for this function self.assertEqual( result["error"]["code"], "QuotaExceeded", - "Expected QuotaExceeded error for Convert to 3wa", + "Expected QuotaExceeded error due to plan limitations or exceeded quota.", ) else: - self.assertEqual(result["error"]["code"], "BadWords") + self.assertIn("lines", result, "Expected 'lines' in the response") + self.assertIsNotNone(result["lines"]) + + def test_invalid_address(self): + invalid_addr = "invalid.address" + result = self.geocoder.convert_to_coordinates(invalid_addr) + print(result) # Debugging output + + if "error" in result: + if result["error"]["code"] == "QuotaExceeded": + self.assertEqual( + result["error"]["code"], + "QuotaExceeded", + "Expected QuotaExceeded error due to exceeded quota or restricted API key.", + ) + else: + self.assertEqual( + result["error"]["code"], + "BadWords", + "Expected BadWords error for invalid address.", + ) + else: + self.fail("Expected an error in the response, but none was returned.") def test_invalid_coordinates(self): - # Test invalid coordinates to 3-word address conversion invalid_lat = 100.0 invalid_lng = 200.0 result = self.geocoder.convert_to_3wa(Coordinates(invalid_lat, invalid_lng)) - self.assertIn("error", result) - self.assertEqual(result["error"]["code"], "BadCoordinates") + print(result) # Debugging output + + if "error" in result: + if result["error"]["code"] == "QuotaExceeded": + self.assertEqual( + result["error"]["code"], + "QuotaExceeded", + "Expected QuotaExceeded error due to plan limitations or exceeded quota.", + ) + else: + self.assertEqual( + result["error"]["code"], + "BadCoordinates", + "Expected BadCoordinates error for invalid coordinates.", + ) + else: + self.fail("Expected an error in the response but got none.") def test_partial_autosuggest(self): # Test partial autosuggest functionality @@ -211,5 +269,7 @@ def test_did_you_mean(self): self.assertFalse(self.geocoder.did_you_mean(invalid_input)) + + if __name__ == "__main__": unittest.main() diff --git a/what3words/version.py b/what3words/version.py index 88c513e..903a158 100644 --- a/what3words/version.py +++ b/what3words/version.py @@ -1 +1 @@ -__version__ = "3.3.0" +__version__ = "3.4.0" diff --git a/what3words/what3words.py b/what3words/what3words.py index 91e6527..21464ac 100644 --- a/what3words/what3words.py +++ b/what3words/what3words.py @@ -38,12 +38,16 @@ def convert_to_coordinates( Convert a 3 word address into coordinates. :param words: A 3 word address as a string :param format: Return data format type; can be 'json' (default) or 'geojson' + :param locale: A supported locale as an ISO 639-1 2 letter code :return: Response as a dictionary """ params = {"words": words, "format": format, "locale": locale} if locale: params["locale"] = locale - return self._request("/convert-to-coordinates", params) + response = self._request("/convert-to-coordinates", params) + if "error" in response: + return {"error": response["error"]} + return response def convert_to_3wa( self, @@ -57,6 +61,7 @@ def convert_to_3wa( :param coordinates: Coordinates object :param format: Return data format type; can be 'json' (default) or 'geojson' :param language: A supported 3 word address language as an ISO 639-1 2 letter code. Defaults to self.language + :param locale: A supported locale as an ISO 639-1 2 letter code :return: Response as a dictionary """ params = { @@ -67,7 +72,11 @@ def convert_to_3wa( } if locale: params["locale"] = locale - return self._request("/convert-to-3wa", params) + + response = self._request("/convert-to-3wa", params) + if "error" in response: + return {"error": response["error"]} + return response def grid_section(self, bounding_box: "BoundingBox", format: str = "json") -> Dict: """ @@ -80,14 +89,20 @@ def grid_section(self, bounding_box: "BoundingBox", format: str = "json") -> Dic "bounding-box": f"{bounding_box.sw.lat},{bounding_box.sw.lng},{bounding_box.ne.lat},{bounding_box.ne.lng}", "format": format, } - return self._request("/grid-section", params) + response = self._request("/grid-section", params) + if "error" in response: + return {"error": response["error"]} + return response def available_languages(self) -> Dict: """ Retrieve a list of available 3 word languages. :return: Response as a dictionary """ - return self._request("/available-languages") + response = self._request("/available-languages") + if "error" in response: + return {"error": response["error"]} + return response def autosuggest( self, @@ -102,6 +117,7 @@ def autosuggest( input_type: Optional[str] = None, language: Optional[str] = None, prefer_land: Optional[bool] = None, + locale: Optional[str] = None, ) -> Dict: """ Returns a list of 3 word addresses based on user input and other parameters. @@ -116,6 +132,7 @@ def autosuggest( :param input_type: Specify voice input mode :param language: A supported 3 word address language as an ISO 639-1 2 letter code :param prefer_land: Makes autosuggest prefer results on land to those in the sea + :param locale: A supported locale as an ISO 639-1 2 letter code :return: Response as a dictionary """ params = {"input": input, "language": language or self.language} @@ -143,8 +160,12 @@ def autosuggest( params["input-type"] = input_type if prefer_land is not None: params["prefer-land"] = str(prefer_land).lower() - - return self._request("/autosuggest", params) + if locale: + params["locale"] = locale + response = self._request("/autosuggest", params) + if "error" in response: + return {"error": response["error"]} + return response def default_language(self, lang: Optional[str] = None) -> str: """