Extended PhoneNumber class to have format attributes
This commit is contained in:
@@ -11,6 +11,52 @@ from sqlalchemy.sql.expression import desc, asc
|
|||||||
from sqlalchemy import types
|
from sqlalchemy import types
|
||||||
|
|
||||||
|
|
||||||
|
class PhoneNumber(phonenumbers.phonenumber.PhoneNumber):
|
||||||
|
'''
|
||||||
|
Extends a PhoneNumber class from `Python phonenumbers library`_. Adds
|
||||||
|
different phone number formats to attributes, so they can be easily used
|
||||||
|
in templates. Phone number validation method is also implemented.
|
||||||
|
|
||||||
|
Takes the raw phone number and country code as params and parses them
|
||||||
|
into a PhoneNumber object.
|
||||||
|
|
||||||
|
.. _Python phonenumbers library:
|
||||||
|
https://github.com/daviddrysdale/python-phonenumbers
|
||||||
|
|
||||||
|
:param raw_number:
|
||||||
|
String representation of the phone number.
|
||||||
|
:param country_code:
|
||||||
|
Country code of the phone number.
|
||||||
|
'''
|
||||||
|
def __init__(self, raw_number, country_code=None):
|
||||||
|
self._phone_number = phonenumbers.parse(raw_number, country_code)
|
||||||
|
super(PhoneNumber, self).__init__(
|
||||||
|
country_code=self._phone_number.country_code,
|
||||||
|
national_number=self._phone_number.national_number,
|
||||||
|
extension=self._phone_number.extension,
|
||||||
|
italian_leading_zero=self._phone_number.italian_leading_zero,
|
||||||
|
raw_input=self._phone_number.raw_input,
|
||||||
|
country_code_source=self._phone_number.country_code_source,
|
||||||
|
preferred_domestic_carrier_code=
|
||||||
|
self._phone_number.preferred_domestic_carrier_code
|
||||||
|
)
|
||||||
|
self.national = phonenumbers.format_number(
|
||||||
|
self._phone_number,
|
||||||
|
phonenumbers.PhoneNumberFormat.NATIONAL
|
||||||
|
)
|
||||||
|
self.international = phonenumbers.format_number(
|
||||||
|
self._phone_number,
|
||||||
|
phonenumbers.PhoneNumberFormat.INTERNATIONAL
|
||||||
|
)
|
||||||
|
self.e164 = phonenumbers.format_number(
|
||||||
|
self._phone_number,
|
||||||
|
phonenumbers.PhoneNumberFormat.E164
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_valid_number(self):
|
||||||
|
return phonenumbers.is_valid_number(self._phone_number)
|
||||||
|
|
||||||
|
|
||||||
class PhoneNumberType(types.TypeDecorator):
|
class PhoneNumberType(types.TypeDecorator):
|
||||||
"""
|
"""
|
||||||
Changes PhoneNumber objects to a string representation on the way in and
|
Changes PhoneNumber objects to a string representation on the way in and
|
||||||
@@ -18,7 +64,7 @@ class PhoneNumberType(types.TypeDecorator):
|
|||||||
as storing format, no country code is needed for parsing the database
|
as storing format, no country code is needed for parsing the database
|
||||||
value to PhoneNumber object.
|
value to PhoneNumber object.
|
||||||
"""
|
"""
|
||||||
STORE_FORMAT = phonenumbers.PhoneNumberFormat.E164
|
STORE_FORMAT = 'e164'
|
||||||
impl = types.Unicode(20)
|
impl = types.Unicode(20)
|
||||||
|
|
||||||
def __init__(self, country_code='US', max_length=20, *args, **kwargs):
|
def __init__(self, country_code='US', max_length=20, *args, **kwargs):
|
||||||
@@ -27,18 +73,10 @@ class PhoneNumberType(types.TypeDecorator):
|
|||||||
self.impl = types.Unicode(max_length)
|
self.impl = types.Unicode(max_length)
|
||||||
|
|
||||||
def process_bind_param(self, value, dialect):
|
def process_bind_param(self, value, dialect):
|
||||||
return phonenumbers.format_number(
|
return getattr(value, self.STORE_FORMAT)
|
||||||
value,
|
|
||||||
self.STORE_FORMAT
|
|
||||||
)
|
|
||||||
|
|
||||||
def process_result_value(self, value, dialect):
|
def process_result_value(self, value, dialect):
|
||||||
if self.STORE_FORMAT == phonenumbers.PhoneNumberFormat.E164:
|
return PhoneNumber(value, self.country_code)
|
||||||
return phonenumbers.parse(value)
|
|
||||||
return phonenumbers.parse(
|
|
||||||
value,
|
|
||||||
self.country_code
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class InstrumentedList(_InstrumentedList):
|
class InstrumentedList(_InstrumentedList):
|
||||||
|
42
tests.py
42
tests.py
@@ -1,4 +1,3 @@
|
|||||||
import phonenumbers
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
@@ -9,6 +8,7 @@ from sqlalchemy_utils import (
|
|||||||
escape_like,
|
escape_like,
|
||||||
sort_query,
|
sort_query,
|
||||||
InstrumentedList,
|
InstrumentedList,
|
||||||
|
PhoneNumber,
|
||||||
PhoneNumberType,
|
PhoneNumberType,
|
||||||
merge
|
merge
|
||||||
)
|
)
|
||||||
@@ -120,10 +120,48 @@ class TestSortQuery(TestCase):
|
|||||||
assert 'category.name ASC' in str(sorted_query)
|
assert 'category.name ASC' in str(sorted_query)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPhoneNumber(object):
|
||||||
|
def setup_method(self, method):
|
||||||
|
self.valid_phone_numbers = [
|
||||||
|
'040 1234567',
|
||||||
|
'+358 401234567',
|
||||||
|
'09 2501234',
|
||||||
|
'+358 92501234',
|
||||||
|
'0800 939393',
|
||||||
|
'09 4243 0456',
|
||||||
|
'0600 900 500'
|
||||||
|
]
|
||||||
|
self.invalid_phone_numbers = [
|
||||||
|
'abc',
|
||||||
|
'+040 1234567',
|
||||||
|
'0111234567',
|
||||||
|
'358'
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_valid_phone_numbers(self):
|
||||||
|
for raw_number in self.valid_phone_numbers:
|
||||||
|
phone_number = PhoneNumber(raw_number, 'FI')
|
||||||
|
assert phone_number.is_valid_number()
|
||||||
|
|
||||||
|
def test_invalid_phone_numbers(self):
|
||||||
|
for raw_number in self.invalid_phone_numbers:
|
||||||
|
try:
|
||||||
|
phone_number = PhoneNumber(raw_number, 'FI')
|
||||||
|
assert not phone_number.is_valid_number()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_phone_number_attributes(self):
|
||||||
|
phone_number = PhoneNumber('+358401234567')
|
||||||
|
assert phone_number.e164 == u'+358401234567'
|
||||||
|
assert phone_number.international == u'+358 40 1234567'
|
||||||
|
assert phone_number.national == u'040 1234567'
|
||||||
|
|
||||||
|
|
||||||
class TestPhoneNumberType(TestCase):
|
class TestPhoneNumberType(TestCase):
|
||||||
def setup_method(self, method):
|
def setup_method(self, method):
|
||||||
super(TestPhoneNumberType, self).setup_method(method)
|
super(TestPhoneNumberType, self).setup_method(method)
|
||||||
self.phone_number = phonenumbers.parse(
|
self.phone_number = PhoneNumber(
|
||||||
'040 1234567',
|
'040 1234567',
|
||||||
'FI'
|
'FI'
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user