profile: format phone numbers at cell-rendering time (#72769) #23

Merged
pmarillonnet merged 1 commits from wip/72769-profile-cell-phone-number-formatting into main 2023-01-06 12:20:41 +01:00
6 changed files with 61 additions and 1 deletions

View File

@ -24,6 +24,7 @@ from django.utils.translation import gettext_lazy as _
from combo.data.library import register_cell_class
from combo.data.models import JsonCellBase
from combo.profile import utils as profile_utils
class Profile(models.Model):
@ -61,6 +62,8 @@ class ProfileCell(JsonCellBase):
if value:
if attribute['kind'] in ('birthdate', 'date'):
value = parse_date(value)
if attribute['kind'] == 'phone_number':
value = profile_utils.get_formatted_phone(value)
extra_context['profile_fields'][attribute['name']]['value'] = value
else:
extra_context['error'] = 'unknown user'

View File

@ -14,8 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import phonenumbers
from django.conf import settings
from django.contrib.auth.models import User
from phonenumbers.phonenumberutil import region_code_for_country_code
if 'mellon' in settings.INSTALLED_APPS:
from mellon.models import UserSAMLIdentifier
@ -46,3 +48,29 @@ def get_user_from_name_id(name_id, raise_on_missing=False):
if raise_on_missing:
raise User.DoesNotExist()
return ProxiedUser(name_id=name_id)
def get_formatted_phone(value, country_code=None):
if country_code is None:
country_code = settings.DEFAULT_COUNTRY_CODE
Outdated
Review

On devrait quand même poser un « DEFAULT_COUNTRY_CODE = '33" » dans combo/settings.py (pour ne pas avoir de code qui fasse appel à un settings inexistant, combo devrait pouvoir tourner "sans hobo").

On devrait quand même poser un « DEFAULT_COUNTRY_CODE = '33" » dans combo/settings.py (pour ne pas avoir de code qui fasse appel à un settings inexistant, combo devrait pouvoir tourner "sans hobo").
region_code = 'ZZ' # phonenumbers' default value for unknown regions
try:
region_code = region_code_for_country_code(int(country_code))
except ValueError:
pass
if region_code == 'ZZ':
return value
try:
pn = phonenumbers.parse(value, region_code)
except phonenumbers.NumberParseException:
return value
if not phonenumbers.is_valid_number(pn):
return value
if country_code == str(pn.country_code):
return phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.NATIONAL)
return phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
Outdated
Review

phonenumbers.PhoneNumberFormat.INTERNATIONAL serait plus joli (+221 77 643 93 28)

phonenumbers.PhoneNumberFormat.INTERNATIONAL serait plus joli (+221 77 643 93 28)

Ok, je n’avais pas réalisé que ces deux formats différaient, my bad. C’est corrigé et c’est mieux ainsi, en effet.

Ok, je n’avais pas réalisé que ces deux formats différaient, my bad. C’est corrigé et c’est mieux ainsi, en effet.

View File

@ -377,6 +377,9 @@ CATEGORIES_CELL_ENABLED = False
# and enable others
CHART_FILTERS_CELL_ENABLED = True
# default country code for phonenumbers' user phone parsing
DEFAULT_COUNTRY_CODE = '33'
def debug_show_toolbar(request):
from debug_toolbar.middleware import show_toolbar as dt_show_toolbar # pylint: disable=import-error

View File

@ -181,6 +181,7 @@ setup(
'pywebpush',
'pygal',
'lxml',
'phonenumbers',
],
zip_safe=False,
cmdclass={

View File

@ -105,6 +105,12 @@ USER_PROFILE_CONFIG = {
'label': 'Birth Date',
'user_visible': True,
},
{
'name': 'phone',
'kind': 'phone_number',
'label': 'Phone',
'user_visible': True,
},
]
}
@ -121,3 +127,5 @@ REST_FRAMEWORK = {
'rest_framework.authentication.BasicAuthentication',
]
}
DEFAULT_COUNTRY_CODE = '33'

View File

@ -20,7 +20,11 @@ def test_profile_cell(requests_get, app, admin_user):
cell = ProfileCell(page=page, order=0)
cell.save()
data = {'first_name': 'Foo', 'birthdate': '2018-08-10'}
data = {
'first_name': 'Foo',
'birthdate': '2018-08-10',
'phone': '+33612345678',
}
requests_get.return_value = mock.Mock(content=json.dumps(data), json=lambda: data, status_code=200)
admin_user.get_name_id = lambda: '123456'
@ -28,4 +32,17 @@ def test_profile_cell(requests_get, app, admin_user):
context = cell.get_cell_extra_context({'synchronous': True, 'selected_user': admin_user})
assert context['profile_fields']['first_name']['value'] == 'Foo'
assert context['profile_fields']['birthdate']['value'] == datetime.date(2018, 8, 10)
assert context['profile_fields']['phone']['value'] == '06 12 34 56 78'
assert requests_get.call_args[0][0] == 'http://example.org/api/users/123456/'
# foreign number remains in its international representation
data['phone'] = '+221 33 889 00 00' # Dakar landline number
requests_get.return_value = mock.Mock(content=json.dumps(data), json=lambda: data, status_code=200)
context = cell.get_cell_extra_context({'synchronous': True, 'selected_user': admin_user})
Outdated
Review

Ajouter un test qui montre qu'un numéro genre en +221 s'affiche bien avec son indicatif international ?

Ajouter un test qui montre qu'un numéro genre en +221 s'affiche bien avec son indicatif international ?
assert context['profile_fields']['phone']['value'] == '+221 33 889 00 00' # international representation
# erroneous number is not parsed at all
data['phone'] = '+336a23c5678'
requests_get.return_value = mock.Mock(content=json.dumps(data), json=lambda: data, status_code=200)
context = cell.get_cell_extra_context({'synchronous': True, 'selected_user': admin_user})
assert context['profile_fields']['phone']['value'] == '+336a23c5678'