122 lines
5.2 KiB
Python
122 lines
5.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import _
|
|
from odoo.exceptions import UserError
|
|
|
|
import logging
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
_phonenumbers_lib_warning = False
|
|
|
|
|
|
try:
|
|
import phonenumbers
|
|
|
|
def phone_parse(number, country_code):
|
|
try:
|
|
phone_nbr = phonenumbers.parse(number, region=country_code or None, keep_raw_input=True)
|
|
except phonenumbers.phonenumberutil.NumberParseException as e:
|
|
raise UserError(_('Unable to parse %(phone)s: %(error)s', phone=number, error=str(e)))
|
|
|
|
if not phonenumbers.is_possible_number(phone_nbr):
|
|
raise UserError(_('Impossible number %s: probably invalid number of digits.', number))
|
|
if not phonenumbers.is_valid_number(phone_nbr):
|
|
# Force format with international to force metadata to apply
|
|
formatted_intl = phonenumbers.format_number(phone_nbr, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
|
|
phone_nbr_intl = phonenumbers.parse(formatted_intl, region=country_code or None, keep_raw_input=True)
|
|
if not phonenumbers.is_valid_number(phone_nbr_intl):
|
|
raise UserError(_('Invalid number %s: probably incorrect prefix.', number))
|
|
return phone_nbr_intl
|
|
|
|
return phone_nbr
|
|
|
|
def phone_format(number, country_code, country_phone_code, force_format='INTERNATIONAL', raise_exception=True):
|
|
""" Format the given phone number according to the localisation and international options.
|
|
:param number: number to convert
|
|
:param country_code: the ISO country code in two chars
|
|
:type country_code: str
|
|
:param country_phone_code: country dial in codes, defined by the ITU-T (Ex: 32 for Belgium)
|
|
:type country_phone_code: int
|
|
:param force_format: stringified version of format globals (see
|
|
https://github.com/daviddrysdale/python-phonenumbers/blob/dev/python/phonenumbers/phonenumberutil.py)
|
|
'E164' = 0
|
|
'INTERNATIONAL' = 1
|
|
'NATIONAL' = 2
|
|
'RFC3966' = 3
|
|
:type force_format: str
|
|
:rtype: str
|
|
"""
|
|
try:
|
|
phone_nbr = phone_parse(number, country_code)
|
|
except (phonenumbers.phonenumberutil.NumberParseException, UserError) as e:
|
|
if raise_exception:
|
|
raise
|
|
else:
|
|
return number
|
|
if force_format == 'E164':
|
|
phone_fmt = phonenumbers.PhoneNumberFormat.E164
|
|
elif force_format == 'RFC3966':
|
|
phone_fmt = phonenumbers.PhoneNumberFormat.RFC3966
|
|
elif force_format == 'INTERNATIONAL' or phone_nbr.country_code != country_phone_code:
|
|
phone_fmt = phonenumbers.PhoneNumberFormat.INTERNATIONAL
|
|
else:
|
|
phone_fmt = phonenumbers.PhoneNumberFormat.NATIONAL
|
|
return phonenumbers.format_number(phone_nbr, phone_fmt)
|
|
|
|
except ImportError:
|
|
|
|
def phone_parse(number, country_code):
|
|
return False
|
|
|
|
def phone_format(number, country_code, country_phone_code, force_format='INTERNATIONAL', raise_exception=True):
|
|
global _phonenumbers_lib_warning
|
|
if not _phonenumbers_lib_warning:
|
|
_logger.info(
|
|
"The `phonenumbers` Python module is not installed, contact numbers will not be "
|
|
"verified. Please install the `phonenumbers` Python module."
|
|
)
|
|
_phonenumbers_lib_warning = True
|
|
return number
|
|
|
|
|
|
def phone_sanitize_numbers(numbers, country_code, country_phone_code, force_format='E164'):
|
|
""" Given a list of numbers, return parsezd and sanitized information
|
|
|
|
:return dict: {number: {
|
|
'sanitized': sanitized and formated number or False (if cannot format)
|
|
'code': 'empty' (number was a void string), 'invalid' (error) or False (sanitize ok)
|
|
'msg': error message when 'invalid'
|
|
}}
|
|
"""
|
|
if not isinstance(numbers, (list)):
|
|
raise NotImplementedError()
|
|
result = dict.fromkeys(numbers, False)
|
|
for number in numbers:
|
|
if not number:
|
|
result[number] = {'sanitized': False, 'code': 'empty', 'msg': False}
|
|
continue
|
|
try:
|
|
stripped = number.strip()
|
|
sanitized = phone_format(
|
|
stripped, country_code, country_phone_code,
|
|
force_format=force_format, raise_exception=True)
|
|
except Exception as e:
|
|
result[number] = {'sanitized': False, 'code': 'invalid', 'msg': str(e)}
|
|
else:
|
|
result[number] = {'sanitized': sanitized, 'code': False, 'msg': False}
|
|
return result
|
|
|
|
|
|
def phone_sanitize_numbers_w_record(numbers, record, country=False, record_country_fname='country_id', force_format='E164'):
|
|
if not isinstance(numbers, (list)):
|
|
raise NotImplementedError()
|
|
if not country:
|
|
if record and record_country_fname and hasattr(record, record_country_fname) and record[record_country_fname]:
|
|
country = record[record_country_fname]
|
|
elif record:
|
|
country = record.env.company.country_id
|
|
country_code = country.code if country else None
|
|
country_phone_code = country.phone_code if country else None
|
|
return phone_sanitize_numbers(numbers, country_code, country_phone_code, force_format=force_format)
|