sms: improve SMSGatewayMixin.clean_numbers() (fixes #6867)

- normalize numbers to the 00... international format
- a default_trunk_prefix setting to all sms backends
- set default_trunk_prefix and default_country_code to French ones
- modify test
This commit is contained in:
Benjamin Dauvergne 2015-03-31 15:53:13 +02:00
parent 9388339ead
commit 14abe6efcd
10 changed files with 149 additions and 46 deletions

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('choosit', '0004_auto_20160407_0456'),
]
operations = [
migrations.AddField(
model_name='choositsmsgateway',
name='default_trunk_prefix',
field=models.CharField(default='0', max_length=2),
preserve_default=True,
),
]

View File

@ -14,8 +14,11 @@ from .choosit import ChoositRegisterWS
class ChoositSMSGateway(BaseResource, SMSGatewayMixin):
key = models.CharField(max_length=64)
default_country_code = models.CharField(max_length=3, default=u'33')
key = models.CharField(verbose_name=_('Key'), max_length=64)
default_country_code = models.CharField(verbose_name=_('Default country code'), max_length=3,
default=u'33')
default_trunk_prefix = models.CharField(verbose_name=_('Default trunk prefix'), max_length=2,
default=u'0')
# FIXME: add regexp field, to check destination and from format
TEST_DEFAULTS = {
@ -29,9 +32,9 @@ class ChoositSMSGateway(BaseResource, SMSGatewayMixin):
'err': 1,
'err_desc': 'Choosit error: some destinations failed',
'data': [
[u'33688888888', u'Choosit error: bad JSON response No JSON object '
[u'0033688888888', u'Choosit error: bad JSON response No JSON object '
'could be decoded'],
[u'33677777777', u'Choosit error: bad JSON response No JSON object '
[u'0033677777777', u'Choosit error: bad JSON response No JSON object '
'could be decoded'],
]
}
@ -44,8 +47,8 @@ class ChoositSMSGateway(BaseResource, SMSGatewayMixin):
'err': 1,
'err_desc': 'Choosit error: some destinations failed',
'data': [
[u'33688888888', u'Choosit error: not ok'],
[u'33677777777', u'Choosit error: not ok'],
[u'0033688888888', u'Choosit error: not ok'],
[u'0033677777777', u'Choosit error: not ok'],
],
}
},
@ -57,8 +60,8 @@ class ChoositSMSGateway(BaseResource, SMSGatewayMixin):
'result': {
'err': 0,
'data': [
[u'33688888888', {'result': u'Envoi terminé', 'sms_id': 1234}],
[u'33677777777', {'result': u'Envoi terminé', 'sms_id': 1234}],
[u'0033688888888', {'result': u'Envoi terminé', 'sms_id': 1234}],
[u'0033677777777', {'result': u'Envoi terminé', 'sms_id': 1234}],
],
}
}
@ -83,7 +86,9 @@ class ChoositSMSGateway(BaseResource, SMSGatewayMixin):
"""Send a SMS using the Choosit provider"""
# from http://sms.choosit.com/documentation_technique.html
# unfortunately it lacks a batch API...
destinations = self.clean_numbers(destinations, self.default_country_code, prefix='')
destinations = self.clean_numbers(destinations,
self.default_country_code,
self.default_trunk_prefix)
results = []
for dest in destinations:
params = {

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('mobyt', '0004_auto_20160407_0456'),
]
operations = [
migrations.AddField(
model_name='mobytsmsgateway',
name='default_trunk_prefix',
field=models.CharField(default='0', max_length=2),
preserve_default=True,
),
]

View File

@ -15,11 +15,14 @@ class MobytSMSGateway(BaseResource, SMSGatewayMixin):
('ll', _('sms low-cost')),
('n', _('sms top')),
)
username = models.CharField(max_length=64)
password = models.CharField(max_length=64)
username = models.CharField(verbose_name=_('Username'), max_length=64)
password = models.CharField(verbose_name=_('Password'), max_length=64)
quality = models.CharField(max_length=4, choices=MESSAGES_QUALITIES, default='l',
verbose_name=_('message quality'))
default_country_code = models.CharField(max_length=3, default=u'33')
verbose_name=_('Message quality'))
default_country_code = models.CharField(verbose_name=_('Default contry code'), max_length=3,
default=u'33')
default_trunk_prefix = models.CharField(verbose_name=_('Default trunk prefix'), max_length=2,
default=u'0')
# FIXME: add regexp field, to check destination and from format
manager_view_template_name = 'passerelle/manage/messages_service_view.html'
@ -53,7 +56,9 @@ class MobytSMSGateway(BaseResource, SMSGatewayMixin):
def send_msg(self, text, sender, destinations, **kwargs):
"""Send a SMS using the Mobyt provider"""
# unfortunately it lacks a batch API...
destinations = self.clean_numbers(destinations, self.default_country_code)
destinations = self.clean_numbers(destinations,
self.default_country_code,
self.default_trunk_prefix)
rcpt = ','.join(destinations)
params = {
'user': self.username,

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('ovh', '0004_auto_20160407_0456'),
]
operations = [
migrations.AddField(
model_name='ovhsmsgateway',
name='default_trunk_prefix',
field=models.CharField(default='0', max_length=2),
preserve_default=True,
),
]

View File

@ -21,14 +21,18 @@ class OVHSMSGateway(BaseResource, SMSGatewayMixin):
(3, _('Messages are stored in external storage like a PDA or '
'a PC.')),
)
account = models.CharField(max_length=64)
username = models.CharField(max_length=64)
password = models.CharField(max_length=64)
account = models.CharField(verbose_name=_('Account'), max_length=64)
username = models.CharField(verbose_name=_('Username'), max_length=64)
password = models.CharField(verbose_name=_('Password'), max_length=64)
msg_class = models.IntegerField(choices=MESSAGES_CLASSES, default=1,
verbose_name=_('message class'))
credit_threshold_alert = models.PositiveIntegerField(default=100)
default_country_code = models.CharField(max_length=3, default=u'33')
credit_left = models.PositiveIntegerField(default=0)
verbose_name=_('Message class'))
credit_threshold_alert = models.PositiveIntegerField(verbose_name=_('Credit alert threshold'),
default=100)
default_country_code = models.CharField(verbose_name=_('Default country code'), max_length=3,
default=u'33')
default_trunk_prefix = models.CharField(verbose_name=_('Default trunk prefix'), max_length=2,
default=u'0')
credit_left = models.PositiveIntegerField(verbose_name=_('Dredit left'), default=0)
# FIXME: add regexp field, to check destination and from format
manager_view_template_name = 'passerelle/manage/messages_service_view.html'
@ -83,8 +87,9 @@ class OVHSMSGateway(BaseResource, SMSGatewayMixin):
def send_msg(self, text, sender, destinations, **kwargs):
"""Send a SMS using the OVH provider"""
# unfortunately it lacks a batch API...
destinations = self.clean_numbers(destinations, self.default_country_code)
destinations = self.clean_numbers(destinations,
self.default_country_code,
self.default_trunk_prefix)
text = unicode(text).encode('utf-8')
to = ','.join(destinations)

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('oxyd', '0004_auto_20160407_0456'),
]
operations = [
migrations.AddField(
model_name='oxydsmsgateway',
name='default_trunk_prefix',
field=models.CharField(default='0', max_length=2),
preserve_default=True,
),
]

View File

@ -5,12 +5,16 @@ from django.db import models
from passerelle.utils.jsonresponse import APIError
from passerelle.base.models import BaseResource
from passerelle.sms import SMSGatewayMixin
from django.utils.translation import ugettext_lazy as _
class OxydSMSGateway(BaseResource, SMSGatewayMixin):
username = models.CharField(max_length=64)
password = models.CharField(max_length=64)
default_country_code = models.CharField(max_length=3, default=u'33')
username = models.CharField(verbose_name=_('Username'), max_length=64)
password = models.CharField(verbose_name=_('Password'), max_length=64)
default_country_code = models.CharField(verbose_name=_('Default country code'), max_length=3,
default=u'33')
default_trunk_prefix = models.CharField(verbose_name=_('Default trunk prefix'), max_length=2,
default=u'0')
# FIXME: add regexp field, to check destination and from format
manager_view_template_name = 'passerelle/manage/messages_service_view.html'
@ -27,8 +31,8 @@ class OxydSMSGateway(BaseResource, SMSGatewayMixin):
'err': 1,
'err_desc': 'OXYD error: some destinations failed',
'data': [
['33688888888', "OXYD error: received content '' instead of 200"],
['33677777777', "OXYD error: received content '' instead of 200"],
['0033688888888', "OXYD error: received content '' instead of 200"],
['0033677777777', "OXYD error: received content '' instead of 200"],
],
}
},
@ -54,7 +58,9 @@ class OxydSMSGateway(BaseResource, SMSGatewayMixin):
def send_msg(self, text, sender, destinations, **kwargs):
"""Send a SMS using the Oxyd provider"""
# unfortunately it lacks a batch API...
destinations = self.clean_numbers(destinations, self.default_country_code, prefix='')
destinations = self.clean_numbers(destinations,
self.default_country_code,
self.default_trunk_prefix)
results = []
for dest in destinations:
params = {

View File

@ -11,21 +11,26 @@ class SMSGatewayMixin(object):
category = _('SMS Providers')
@classmethod
def clean_numbers(cls, destinations, default_country_code, prefix='+'):
def clean_numbers(cls, destinations, default_country_code='33',
default_trunk_prefix='0'): # Yeah France first !
numbers = []
for dest in destinations:
# most gateways needs the number prefixed by the country code, this is
# really unfortunate.
dest = dest.strip()
number = ''.join(re.findall('[0-9]', dest))
if dest.startswith('+'):
pass # it already is fully qualified
number = '00' + number
elif number.startswith('00'):
# assumes 00 is international access code, remove it
number = number[2:]
elif number.startswith('0'):
# local prefix, remove 0 and add default country code
number = default_country_code + number[1:]
numbers.append(prefix + number)
pass
elif number.startswith(default_trunk_prefix):
number = '00' + default_country_code + number[len(default_trunk_prefix):]
else:
raise NotImplementedError('phone number %r is unsupported (no '
'international prefix, no local '
'trunk prefix)' % number)
numbers.append(number)
return numbers
@endpoint('json-api', perm='can_send_messages', methods=['post'])

View File

@ -15,15 +15,12 @@ klasses = SMSGatewayMixin.__subclasses__()
def test_clean_numbers():
assert SMSGatewayMixin.clean_numbers(['+ 33 12'], '33') == ['+3312']
assert SMSGatewayMixin.clean_numbers(['0 0 33 12'], '33') == ['+3312']
assert SMSGatewayMixin.clean_numbers(['0 12'], '33') == ['+3312']
def test_clean_numbers_no_prefix():
assert SMSGatewayMixin.clean_numbers(['+ 33 12'], '33', prefix='') == ['3312']
assert SMSGatewayMixin.clean_numbers(['0 0 33 12'], '33', prefix='') == ['3312']
assert SMSGatewayMixin.clean_numbers(['0 12'], '33', prefix='') == ['3312']
assert SMSGatewayMixin.clean_numbers(['+ 33 12']) == ['003312']
assert SMSGatewayMixin.clean_numbers(['0 0 33 12']) == ['003312']
assert SMSGatewayMixin.clean_numbers(['0 12']) == ['003312']
assert SMSGatewayMixin.clean_numbers(['+ 33 12'], '32', '1') == ['003312']
assert SMSGatewayMixin.clean_numbers(['0 0 33 12'], '32', '1') == ['003312']
assert SMSGatewayMixin.clean_numbers(['1 12'], '32', '1') == ['003212']
@pytest.fixture(params=klasses)
@ -60,11 +57,11 @@ def test_connectors(app, connector):
for test_vector in getattr(connector, 'TEST_DEFAULTS', {}).get('test_vectors', []):
with utils.mock_url(connector.URL, test_vector['response']):
result = app.post_json(path, params=payload)
print result.json
for key, value in test_vector['result'].iteritems():
assert key in result.json
assert result.json[key] == value
def test_manage_views(admin_user, app, connector):
url = '/%s/%s/' % (connector.get_connector_slug(), connector.slug)
resp = app.get(url)