Files
odoo_source/addons/base_geolocalize/models/base_geocoder.py
T
Saurabh Choraria 2a9d0ce128 [FIX] base_geolocalize: update log when request to openstreetmap fails
When the user will not get an HTTP response as 200 while retrieving the location
using OpenStreetMap Nominatim service, the logger error will occur.

The logger is updated to use the 'warning' level instead of the 'error' level.
This change reflects a less severe logging level for cases where a request to
OpenStreetMap fails.

sentry-4151622143

closes odoo/odoo#125835

X-original-commit: a73a65efc0daa702b6a3fd40ad63d2ef39ace302
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2023-06-21 15:53:15 +02:00

161 lines
6.6 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import requests
import logging
from odoo import api, fields, models, tools, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class GeoProvider(models.Model):
_name = "base.geo_provider"
_description = "Geo Provider"
tech_name = fields.Char(string="Technical Name")
name = fields.Char()
class GeoCoder(models.AbstractModel):
"""
Abstract class used to call Geolocalization API and convert addresses
into GPS coordinates.
"""
_name = "base.geocoder"
_description = "Geo Coder"
@api.model
def _get_provider(self):
prov_id = self.env['ir.config_parameter'].sudo().get_param('base_geolocalize.geo_provider')
if prov_id:
provider = self.env['base.geo_provider'].browse(int(prov_id))
if not prov_id or not provider.exists():
provider = self.env['base.geo_provider'].search([], limit=1)
return provider
@api.model
def geo_query_address(self, street=None, zip=None, city=None, state=None, country=None):
""" Converts address fields into a valid string for querying
geolocation APIs.
:param street: street address
:param zip: zip code
:param city: city
:param state: state
:param country: country
:return: formatted string
"""
provider = self._get_provider().tech_name
if hasattr(self, '_geo_query_address_' + provider):
# Makes the transformation defined for provider
return getattr(self, '_geo_query_address_' + provider)(street, zip, city, state, country)
else:
# By default, join the non-empty parameters
return self._geo_query_address_default(street=street, zip=zip, city=city, state=state, country=country)
@api.model
def geo_find(self, addr, **kw):
"""Use a location provider API to convert an address string into a latitude, longitude tuple.
Here we use Openstreetmap Nominatim by default.
:param addr: Address string passed to API
:return: (latitude, longitude) or None if not found
"""
provider = self._get_provider().tech_name
try:
service = getattr(self, '_call_' + provider)
result = service(addr, **kw)
except AttributeError:
raise UserError(_(
'Provider %s is not implemented for geolocation service.'
) % provider)
except UserError:
raise
except Exception:
_logger.debug('Geolocalize call failed', exc_info=True)
result = None
return result
@api.model
def _call_openstreetmap(self, addr, **kw):
"""
Use Openstreemap Nominatim service to retrieve location
:return: (latitude, longitude) or None if not found
"""
if not addr:
_logger.info('invalid address given')
return None
url = 'https://nominatim.openstreetmap.org/search'
try:
headers = {'User-Agent': 'Odoo (http://www.odoo.com/contactus)'}
response = requests.get(url, headers=headers, params={'format': 'json', 'q': addr})
_logger.info('openstreetmap nominatim service called')
if response.status_code != 200:
_logger.warning('Request to openstreetmap failed.\nCode: %s\nContent: %s', response.status_code, response.content)
result = response.json()
except Exception as e:
self._raise_query_error(e)
geo = result[0]
return float(geo['lat']), float(geo['lon'])
@api.model
def _call_googlemap(self, addr, **kw):
""" Use google maps API. It won't work without a valid API key.
:return: (latitude, longitude) or None if not found
"""
apikey = self.env['ir.config_parameter'].sudo().get_param('base_geolocalize.google_map_api_key')
if not apikey:
raise UserError(_(
"API key for GeoCoding (Places) required.\n"
"Visit https://developers.google.com/maps/documentation/geocoding/get-api-key for more information."
))
url = "https://maps.googleapis.com/maps/api/geocode/json"
params = {'sensor': 'false', 'address': addr, 'key': apikey}
if kw.get('force_country'):
params['components'] = 'country:%s' % kw['force_country']
try:
result = requests.get(url, params).json()
except Exception as e:
self._raise_query_error(e)
try:
if result['status'] == 'ZERO_RESULTS':
return None
if result['status'] != 'OK':
_logger.debug('Invalid Gmaps call: %s - %s',
result['status'], result.get('error_message', ''))
error_msg = _('Unable to geolocate, received the error:\n%s'
'\n\nGoogle made this a paid feature.\n'
'You should first enable billing on your Google account.\n'
'Then, go to Developer Console, and enable the APIs:\n'
'Geocoding, Maps Static, Maps Javascript.\n') % result.get('error_message')
raise UserError(error_msg)
geo = result['results'][0]['geometry']['location']
return float(geo['lat']), float(geo['lng'])
except (KeyError, ValueError):
_logger.debug('Unexpected Gmaps API answer %s', result.get('error_message', ''))
return None
@api.model
def _geo_query_address_default(self, street=None, zip=None, city=None, state=None, country=None):
address_list = [
street,
("%s %s" % (zip or '', city or '')).strip(),
state,
country
]
address_list = [item for item in address_list if item]
return tools.ustr(', '.join(address_list))
@api.model
def _geo_query_address_googlemap(self, street=None, zip=None, city=None, state=None, country=None):
# put country qualifier in front, otherwise GMap gives wrong# results
# e.g. 'Congo, Democratic Republic of the' => 'Democratic Republic of the Congo'
if country and ',' in country and (
country.endswith(' of') or country.endswith(' of the')):
country = '{1} {0}'.format(*country.split(',', 1))
return self._geo_query_address_default(street=street, zip=zip, city=city, state=state, country=country)
def _raise_query_error(self, error):
raise UserError(_('Error with geolocation server:') + ' %s' % error)