From 80320ab7a023fe45ae08a033e24f462abfef99f0 Mon Sep 17 00:00:00 2001 From: Thomas Tracy Date: Thu, 20 Feb 2020 10:52:46 -0500 Subject: [PATCH] Add phone_number field to UserProfile model (#23132) Adds a simple nullable field to the UserProfile model. The only validation done it to make sure any character saved in that field is a digit and not a letter. We do not distiguish on a model level if the phone number is international or not. --- .../0030_userprofile_phone_number.py | 21 ++++++++++++++++ common/djangoapps/student/models.py | 6 +++-- .../tests/test_user_profile_properties.py | 24 ++++++++++++++++++- 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 common/djangoapps/student/migrations/0030_userprofile_phone_number.py diff --git a/common/djangoapps/student/migrations/0030_userprofile_phone_number.py b/common/djangoapps/student/migrations/0030_userprofile_phone_number.py new file mode 100644 index 0000000000..7b7873f1ad --- /dev/null +++ b/common/djangoapps/student/migrations/0030_userprofile_phone_number.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-02-18 18:36 +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('student', '0029_add_data_researcher'), + ] + + operations = [ + migrations.AddField( + model_name='userprofile', + name='phone_number', + field=models.CharField(blank=True, max_length=50, null=True, validators=[django.core.validators.RegexValidator(message='Phone number can only contain numbers.', regex='^\\+?1?\\d*$')]), + ), + ] diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 7d088c355b..e4bfc9398d 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -31,7 +31,7 @@ from django.contrib.auth.signals import user_logged_in, user_logged_out from django.contrib.sites.models import Site from django.core.cache import cache from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist -from django.core.validators import FileExtensionValidator +from django.core.validators import FileExtensionValidator, RegexValidator from django.db import IntegrityError, models from django.db.models import Count, Q, Index from django.db.models.signals import post_save, pre_save @@ -431,7 +431,7 @@ class UserProfile(models.Model): MITx fall prototype. .. pii: Contains many PII fields. Retired in AccountRetirementView. - .. pii_types: name, location, birth_date, gender, biography + .. pii_types: name, location, birth_date, gender, biography, phone_number .. pii_retirement: local_api """ # cache key format e.g user..profile.country = 'SG' @@ -503,6 +503,8 @@ class UserProfile(models.Model): allow_certificate = models.BooleanField(default=1) bio = models.CharField(blank=True, null=True, max_length=3000, db_index=False) profile_image_uploaded_at = models.DateTimeField(null=True, blank=True) + phone_regex = RegexValidator(regex=r'^\+?1?\d*$', message="Phone number can only contain numbers.") + phone_number = models.CharField(validators=[phone_regex], blank=True, null=True, max_length=50) @property def has_profile_image(self): diff --git a/common/djangoapps/student/tests/test_user_profile_properties.py b/common/djangoapps/student/tests/test_user_profile_properties.py index 77bcf25127..9400e93523 100644 --- a/common/djangoapps/student/tests/test_user_profile_properties.py +++ b/common/djangoapps/student/tests/test_user_profile_properties.py @@ -5,6 +5,7 @@ import datetime import ddt from django.core.cache import cache +from django.core.exceptions import ValidationError from openedx.core.djangolib.testing.utils import CacheIsolationTestCase from student.models import UserProfile @@ -13,7 +14,7 @@ from student.tests.factories import UserFactory @ddt.ddt class UserProfilePropertiesTest(CacheIsolationTestCase): - """Unit tests for age, gender_display, and level_of_education_display properties .""" + """Unit tests for age, gender_display, phone_number, and level_of_education_display properties .""" password = "test" @@ -105,3 +106,24 @@ class UserProfilePropertiesTest(CacheIsolationTestCase): self.assertNotEqual(cache.get(cache_key), country) self.assertIsNone(cache.get(cache_key)) + + def test_phone_number_can_only_contain_digits(self): + # validating the profile will fail, because there are letters + # in the phone number + self.profile.phone_number = 'abc' + self.assertRaises(ValidationError, self.profile.full_clean) + # fail if mixed digits/letters + self.profile.phone_number = '1234gb' + self.assertRaises(ValidationError, self.profile.full_clean) + # fail if whitespace + self.profile.phone_number = ' 123' + self.assertRaises(ValidationError, self.profile.full_clean) + # fail with special characters + self.profile.phone_number = '123!@#$%^&*' + self.assertRaises(ValidationError, self.profile.full_clean) + # valid phone number + self.profile.phone_number = '123456789' + try: + self.profile.full_clean() + except ValidationError: + self.fail("This phone number should be valid.")