attribute_kinds: normalize local phone numbers prefix (#48546)

This commit is contained in:
Benjamin Dauvergne 2020-11-13 21:58:20 +01:00
parent 0c705e7922
commit 3d1fbb6af2
6 changed files with 99 additions and 9 deletions

View File

@ -348,6 +348,9 @@ default_settings = dict(
A2_DUPLICATES_BIRTHDATE_BONUS=Setting(
default=0.3,
definition='Bonus in case of birthdate match (no bonus is 0, max is 1).'),
A2_LOCAL_PHONE_PREFIXES=Setting(
default=['+33', '0033', '0'],
definition='List of known prefix for local numbers, they will be normalized to the last one'),
)
app_settings = AppSettings(default_settings)

View File

@ -145,9 +145,16 @@ validate_phone_number = RegexValidator(
message=_('Phone number can start with a + and must contain only digits.'))
def clean_number(number):
def clean_phone_number(number):
cleaned_number = re.sub(r'[-.\s/]', '', number)
validate_phone_number(cleaned_number)
# normalize local prefixes
local_phone_prefixes = app_settings.A2_LOCAL_PHONE_PREFIXES
if local_phone_prefixes:
for prefix in local_phone_prefixes[:-1]:
if cleaned_number.startswith(prefix):
cleaned_number = local_phone_prefixes[-1] + cleaned_number[len(prefix):]
break
return cleaned_number
@ -161,7 +168,7 @@ class PhoneNumberField(forms.CharField):
def clean(self, value):
if value not in self.empty_values:
value = clean_number(value)
value = clean_phone_number(value)
return value
@ -169,7 +176,7 @@ class PhoneNumberDRFField(serializers.CharField):
default_validators = [validate_phone_number]
def to_internal_value(self, data):
return clean_number(super().to_internal_value(data))
return clean_phone_number(super().to_internal_value(data))
validate_fr_postcode = RegexValidator(

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-11-13 21:15
from __future__ import unicode_literals
from django.db import migrations
from django.core.exceptions import ValidationError
from authentic2.attribute_kinds import clean_phone_number
def noop(apps, schema_editor):
pass
def clean_phone_numbers(apps, schema_editor):
Attribute = apps.get_model('authentic2', 'Attribute')
AttributeValue = apps.get_model('authentic2', 'AttributeValue')
at_ids = Attribute.all_objects.filter(kind='phone_number').values_list('id')
for atv in AttributeValue.all_objects.filter(attribute__id__in=at_ids):
try:
new_content = clean_phone_number(atv.content)
except ValidationError:
pass
else:
if atv.content != new_content:
atv.content = new_content
atv.save()
class Migration(migrations.Migration):
dependencies = [
('custom_user', '0021_set_unusable_password'),
]
operations = [
migrations.RunPython(clean_phone_numbers, noop),
]

View File

@ -1945,8 +1945,22 @@ def test_phone_normalization_ok(settings, app, admin):
'last_name': 'Doe',
}
resp = app.post_json('/api/users/', headers=headers, params=payload, status=201)
assert resp.json['phone'] == '+33499985643'
assert User.objects.get(username='janedoe').attributes.phone == '+33499985643'
assert resp.json['phone'] == '0499985643'
assert User.objects.get(username='janedoe').attributes.phone == '0499985643'
def test_phone_normalization_ok_other_prefix(settings, app, admin):
headers = basic_authorization_header(admin)
Attribute.objects.create(name='phone', label='phone', kind='phone_number')
payload = {
'username': 'janedoe',
'phone': ' 00334-99 98.56/43',
'first_name': 'Jane',
'last_name': 'Doe',
}
resp = app.post_json('/api/users/', headers=headers, params=payload, status=201)
assert resp.json['phone'] == '0499985643'
assert User.objects.get(username='janedoe').attributes.phone == '0499985643'
def test_phone_normalization_nok(settings, app, admin):

View File

@ -222,11 +222,11 @@ def test_phone_number(db, app, admin, mailoutbox, settings):
assert response.pyquery.find('.form-field-error #id_phone_number')
form = response.form
form.set('phone_number', '12345')
form.set('phone_number', '+33499999999')
form.set('password1', '12345abcdA')
form.set('password2', '12345abcdA')
response = form.submit().follow()
assert qs.get().attributes.phone_number == '12345'
assert qs.get().attributes.phone_number == '0499999999'
qs.delete()
url = register_john()
@ -234,11 +234,11 @@ def test_phone_number(db, app, admin, mailoutbox, settings):
form = response.form
form.set('first_name', 'John')
form.set('last_name', 'Doe')
form.set('phone_number', '+12345')
form.set('phone_number', '0033499999999')
form.set('password1', '12345abcdA')
form.set('password2', '12345abcdA')
response = form.submit().follow()
assert qs.get().attributes.phone_number == '+12345'
assert qs.get().attributes.phone_number == '0499999999'
qs.delete()
url = register_john()

View File

@ -14,6 +14,7 @@
# 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 itertools
import mock
from django.contrib.auth.models import AbstractUser
@ -63,3 +64,32 @@ def test_migration_custom_user_0021_set_unusable_password(transactional_db, migr
User = new_apps.get_model('custom_user', 'User')
user = User.objects.get()
assert not AbstractUser.has_usable_password(user)
def test_migration_custom_user_0022_clean_phone_numbers(transactional_db, migration):
from authentic2.models import Attribute, AttributeValue
from authentic2.custom_user.models import User
migration.before([('custom_user', '0021_set_unusable_password')])
Attribute.objects.create(name='phone', label='phone', kind='phone_number')
Attribute.objects.create(name='mobile', label='mobile', kind='phone_number')
numbers1, numbers2 = itertools.tee(itertools.cycle(['+33699999999', '0033999999999', '0199999999']))
next(numbers2)
for i, number1, number2 in zip(range(3), numbers1, numbers2):
user = User.objects.create()
user.attributes.phone = number1
user.attributes.mobile = number2
migration.apply([('custom_user', '0022_clean_phone_numbers')])
assert (
list(AttributeValue.objects.filter(attribute__name='phone').order_by('id').values_list('content', flat=True))
== ['0699999999', '0999999999', '0199999999']
)
assert (
list(AttributeValue.objects.filter(attribute__name='mobile').order_by('id').values_list('content', flat=True))
== ['0999999999', '0199999999', '0699999999']
)