profile: format phone numbers at cell-rendering time (#72769)
gitea-wip/combo/pipeline/pr-main This commit looks good Details

This commit is contained in:
Paul Marillonnet 2023-01-03 14:56:43 +01:00
parent e1046fa0b8
commit 16e6fc8711
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
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)

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})
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'