misc: rely on phonenumbers library for is_french_mobile_phone_number (#72793) #1037
|
@ -864,6 +864,9 @@ def test_is_french_mobile_phone_number(pub):
|
|||
assert t.render({'number': '06.23.45.67.89'}) == 'True'
|
||||
assert t.render({'number': '0 6 2 3 45 67 89'}) == 'True'
|
||||
|
||||
assert t.render({'number': '+33 6 23 45 67 89'}) == 'True'
|
||||
assert t.render({'number': '+262 6 92 11 22 33'}) == 'True'
|
||||
|
||||
t = Template('{% if number|is_french_mobile_phone_number %}ok{% endif %}')
|
||||
assert t.render({'number': '06 23 45 67 8989'}) == ''
|
||||
assert t.render({'number': '06 23 45 67 89'}) == 'ok'
|
||||
|
|
|
@ -853,15 +853,19 @@ def is_ascii_digit(string_value):
|
|||
return string_value and all(x in '0123456789' for x in string_value)
|
||||
|
||||
|
||||
fpeters marked this conversation as resolved
Outdated
|
||||
def validate_phone_fr(string_value):
|
||||
if not re.match(r'^[0\+][\d\.\s]+$', string_value):
|
||||
def get_valid_phone_number(string_value, region_codes=None, country_codes=None):
|
||||
# get string_value as a valid phonenumber in default or specified region_codes,
|
||||
# with additional check against country_codes if given.
|
||||
#
|
||||
# region_codes are strings like BE for Belgium, RE for La Reunion, etc.
|
||||
# country_codes are numeric codes like 32 for Belgium, 262 for La Reunion, etc.
|
||||
|
||||
if not re.match(r'^[0\+][\(\)\d\.\s]+$', string_value or ''):
|
||||
# leading zero or +, then digits, dots, or spaces
|
||||
return False
|
||||
return None
|
||||
|
||||
french_country_codes = [33, 262, 508, 590, 594, 596]
|
||||
french_region_codes = [phonenumbers.region_code_for_country_code(x) for x in french_country_codes]
|
||||
|
||||
for region_code in french_region_codes:
|
||||
region_codes = region_codes or [get_publisher().get_phone_local_region_code()]
|
||||
for region_code in region_codes:
|
||||
pn = None
|
||||
try:
|
||||
pn = phonenumbers.parse(string_value, region_code)
|
||||
|
@ -869,65 +873,73 @@ def validate_phone_fr(string_value):
|
|||
continue
|
||||
if not phonenumbers.is_valid_number(pn):
|
||||
continue
|
||||
fpeters marked this conversation as resolved
Outdated
tnoel
commented
Pour éviter de rechercher l'info, je proposerais ici de rappeler la signification de ces 6 indicatifs:
éventuellement pour rappeler/sous-entendre que ça ne gère pas Nouvelle-Calédonie, Wallis-et-Futuna, Polynésie française et autres endroits qui ne sont pas partie du "plan de numérotation français". Pour éviter de rechercher l'info, je proposerais ici de rappeler la signification de ces 6 indicatifs:
```
country_codes = [
# France has 6 country codes in E.164 system :
33, # Metropolitan France (FR)
262, # Réunion (RE)
508, # Saint-Pierre-et-Miquelon (PM)
590, # Guadeloupe, Saint-Barthélemy, Saint-Martin (GP)
594, # Guyanne (GF)
596, # Martinique (MQ)
]
```
éventuellement pour rappeler/sous-entendre que ça ne gère pas Nouvelle-Calédonie, Wallis-et-Futuna, Polynésie française et autres endroits qui ne sont pas partie du "plan de numérotation français".
|
||||
if pn.country_code not in french_country_codes:
|
||||
if country_codes and pn.country_code not in country_codes:
|
||||
continue
|
||||
return True
|
||||
return False
|
||||
return pn
|
||||
return None
|
||||
|
||||
|
||||
def validate_mobile_phone_local(string_value):
|
||||
if not re.match(r'^[0\+][\d\.\s]+$', string_value):
|
||||
# leading zero or +, then digits, dots, or spaces
|
||||
return False
|
||||
region_code = get_publisher().get_phone_local_region_code()
|
||||
country_codes = []
|
||||
def get_french_country_and_region_codes():
|
||||
country_codes = [
|
||||
# France has 6 country codes in E.164 system :
|
||||
33, # Metropolitan France (FR)
|
||||
262, # Réunion (RE)
|
||||
508, # Saint-Pierre-et-Miquelon (PM)
|
||||
590, # Guadeloupe, Saint-Barthélemy, Saint-Martin (GP)
|
||||
594, # Guyanne (GF)
|
||||
596, # Martinique (MQ)
|
||||
]
|
||||
region_codes = [phonenumbers.region_code_for_country_code(x) for x in country_codes]
|
||||
local_region_code = get_publisher().get_phone_local_region_code()
|
||||
if local_region_code in region_codes:
|
||||
# set parsing preference to configured local region code
|
||||
region_codes = [local_region_code] + [x for x in region_codes if x != local_region_code]
|
||||
return country_codes, region_codes
|
||||
|
||||
|
||||
def validate_phone_fr(string_value):
|
||||
country_codes, region_codes = get_french_country_and_region_codes()
|
||||
pn = get_valid_phone_number(string_value, region_codes=region_codes, country_codes=country_codes)
|
||||
return bool(pn)
|
||||
|
||||
|
||||
def validate_mobile_phone_local(string_value, region_code=None):
|
||||
region_code = region_code or get_publisher().get_phone_local_region_code()
|
||||
if region_code == 'FR':
|
||||
country_codes = [33, 262, 508, 590, 594, 596]
|
||||
# in case of France extend list of country/region codes, so it's not just
|
||||
# metropolitan France.
|
||||
country_codes, region_codes = get_french_country_and_region_codes()
|
||||
else:
|
||||
country_codes = []
|
||||
for country_code, region_codes in phonenumbers.COUNTRY_CODE_TO_REGION_CODE.items():
|
||||
if region_code in region_codes:
|
||||
country_codes = [country_code]
|
||||
break
|
||||
pn = None
|
||||
try:
|
||||
pn = phonenumbers.parse(string_value, region_code)
|
||||
except phonenumbers.NumberParseException:
|
||||
return False
|
||||
region_codes = [region_code]
|
||||
pn = get_valid_phone_number(string_value, region_codes, country_codes)
|
||||
return bool(
|
||||
phonenumbers.is_valid_number(pn)
|
||||
pn
|
||||
and phonenumbers.number_type(pn)
|
||||
in (phonenumbers.PhoneNumberType.MOBILE, phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE)
|
||||
and (pn.country_code in country_codes or not country_codes)
|
||||
)
|
||||
|
||||
|
||||
def get_formatted_phone(number, country_code=None):
|
||||
if not country_code:
|
||||
country_code = get_publisher().get_phone_local_region_code()
|
||||
try:
|
||||
pn = phonenumbers.parse(number)
|
||||
except phonenumbers.NumberParseException:
|
||||
try:
|
||||
pn = phonenumbers.parse(number, country_code)
|
||||
except phonenumbers.NumberParseException:
|
||||
return number
|
||||
|
||||
if phonenumbers.is_valid_number(pn):
|
||||
def get_formatted_phone(number, region_code=None):
|
||||
if not region_code:
|
||||
region_code = get_publisher().get_phone_local_region_code()
|
||||
pn = get_valid_phone_number(number, [region_code])
|
||||
if pn:
|
||||
return phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.NATIONAL)
|
||||
else:
|
||||
return number
|
||||
|
||||
|
||||
def normalize_phone_number_for_fts(value):
|
||||
country_code = get_publisher().get_phone_local_region_code()
|
||||
try:
|
||||
pn = phonenumbers.parse(value)
|
||||
except phonenumbers.NumberParseException:
|
||||
try:
|
||||
pn = phonenumbers.parse(value, country_code)
|
||||
except phonenumbers.NumberParseException:
|
||||
return value
|
||||
return phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.E164)
|
||||
def normalize_phone_number_for_fts(number):
|
||||
pn = get_valid_phone_number(number)
|
||||
if pn:
|
||||
return phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.E164)
|
||||
else:
|
||||
return number
|
||||
|
||||
|
||||
def validate_siren(string_value):
|
||||
|
|
|
@ -59,7 +59,7 @@ from django.utils.timezone import is_naive, localtime, make_aware, make_naive
|
|||
from wcs.qommon import _, calendar, evalutils, upload_storage
|
||||
from wcs.qommon.admin.texts import TextsDirectory
|
||||
from wcs.qommon.humantime import seconds2humanduration
|
||||
from wcs.qommon.misc import parse_decimal, strip_some_tags, unlazy, validate_phone_fr
|
||||
from wcs.qommon.misc import parse_decimal, strip_some_tags, unlazy, validate_mobile_phone_local
|
||||
from wcs.qommon.template import TemplateError
|
||||
|
||||
register = template.Library()
|
||||
|
@ -1052,16 +1052,15 @@ def phonenumber_fr(value, separator=' '):
|
|||
|
||||
@register.filter
|
||||
def is_french_mobile_phone_number(value):
|
||||
# check the given value is a valid French mobile phone number (Metropolitan
|
||||
# or overseas, with support for local prefixes).
|
||||
value = unlazy(value)
|
||||
|
||||
if not value:
|
||||
return False
|
||||
|
||||
value = value.strip().replace(' ', '')
|
||||
if not validate_phone_fr(value):
|
||||
return False
|
||||
|
||||
return value.startswith('06') or value.startswith('07')
|
||||
return validate_mobile_phone_local(value, region_code='FR')
|
||||
|
||||
|
||||
@register.filter
|
||||
|
|
Loading…
Reference in New Issue
Dans les commentaires, quelque part, ici ou ailleurs, je verrais bien une copie de ce que tu as écrit à côté, parce que c'est utile de se le rappeler dans ce fratas de code: