Projet

Général

Profil

0001-sms-improve-SMSGatewayMixin.clean_numbers-fixes-6867.patch

Benjamin Dauvergne, 27 mars 2017 12:04

Télécharger (18,3 ko)

Voir les différences:

Subject: [PATCH] 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
 .../0005_choositsmsgateway_default_trunk_prefix.py | 20 +++++++++++++++++++
 passerelle/apps/choosit/models.py                  | 23 +++++++++++++---------
 .../0005_mobytsmsgateway_default_trunk_prefix.py   | 20 +++++++++++++++++++
 passerelle/apps/mobyt/models.py                    | 13 ++++++++----
 .../0005_ovhsmsgateway_default_trunk_prefix.py     | 20 +++++++++++++++++++
 passerelle/apps/ovh/models.py                      | 21 ++++++++++++--------
 .../0005_oxydsmsgateway_default_trunk_prefix.py    | 20 +++++++++++++++++++
 passerelle/apps/oxyd/models.py                     | 18 +++++++++++------
 passerelle/sms/__init__.py                         | 19 +++++++++++-------
 tests/test_sms.py                                  | 17 +++++++---------
 10 files changed, 147 insertions(+), 44 deletions(-)
 create mode 100644 passerelle/apps/choosit/migrations/0005_choositsmsgateway_default_trunk_prefix.py
 create mode 100644 passerelle/apps/mobyt/migrations/0005_mobytsmsgateway_default_trunk_prefix.py
 create mode 100644 passerelle/apps/ovh/migrations/0005_ovhsmsgateway_default_trunk_prefix.py
 create mode 100644 passerelle/apps/oxyd/migrations/0005_oxydsmsgateway_default_trunk_prefix.py
passerelle/apps/choosit/migrations/0005_choositsmsgateway_default_trunk_prefix.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models, migrations
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('choosit', '0004_auto_20160407_0456'),
11
    ]
12

  
13
    operations = [
14
        migrations.AddField(
15
            model_name='choositsmsgateway',
16
            name='default_trunk_prefix',
17
            field=models.CharField(default='0', max_length=2),
18
            preserve_default=True,
19
        ),
20
    ]
passerelle/apps/choosit/models.py
14 14

  
15 15

  
16 16
class ChoositSMSGateway(BaseResource, SMSGatewayMixin):
17
    key = models.CharField(max_length=64)
18
    default_country_code = models.CharField(max_length=3, default=u'33')
17
    key = models.CharField(verbose_name=_('key'), max_length=64)
18
    default_country_code = models.CharField(verbose_name=_('default country code'), max_length=3,
19
                                            default=u'33')
20
    default_trunk_prefix = models.CharField(verbose_name=_('default trunk prefix'), max_length=2,
21
                                            default=u'0')
19 22
    # FIXME: add regexp field, to check destination and from format
20 23

  
21 24
    TEST_DEFAULTS = {
......
29 32
                    'err': 1,
30 33
                    'err_desc': 'Choosit error: some destinations failed',
31 34
                    'data': [
32
                        [u'33688888888', u'Choosit error: bad JSON response No JSON object '
35
                        [u'0033688888888', u'Choosit error: bad JSON response No JSON object '
33 36
                         'could be decoded'],
34
                        [u'33677777777', u'Choosit error: bad JSON response No JSON object '
37
                        [u'0033677777777', u'Choosit error: bad JSON response No JSON object '
35 38
                         'could be decoded'],
36 39
                    ]
37 40
                }
......
44 47
                    'err': 1,
45 48
                    'err_desc': 'Choosit error: some destinations failed',
46 49
                    'data': [
47
                        [u'33688888888', u'Choosit error: not ok'],
48
                        [u'33677777777', u'Choosit error: not ok'],
50
                        [u'0033688888888', u'Choosit error: not ok'],
51
                        [u'0033677777777', u'Choosit error: not ok'],
49 52
                    ],
50 53
                }
51 54
            },
......
57 60
                'result': {
58 61
                    'err': 0,
59 62
                    'data': [
60
                        [u'33688888888', {'result': u'Envoi terminé', 'sms_id': 1234}],
61
                        [u'33677777777', {'result': u'Envoi terminé', 'sms_id': 1234}],
63
                        [u'0033688888888', {'result': u'Envoi terminé', 'sms_id': 1234}],
64
                        [u'0033677777777', {'result': u'Envoi terminé', 'sms_id': 1234}],
62 65
                    ],
63 66
                }
64 67
            }
......
83 86
        """Send a SMS using the Choosit provider"""
84 87
        # from http://sms.choosit.com/documentation_technique.html
85 88
        # unfortunately it lacks a batch API...
86
        destinations = self.clean_numbers(destinations, self.default_country_code, prefix='')
89
        destinations = self.clean_numbers(destinations,
90
                                          self.default_country_code,
91
                                          self.default_trunk_prefix)
87 92
        results = []
88 93
        for dest in destinations:
89 94
            params = {
passerelle/apps/mobyt/migrations/0005_mobytsmsgateway_default_trunk_prefix.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models, migrations
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('mobyt', '0004_auto_20160407_0456'),
11
    ]
12

  
13
    operations = [
14
        migrations.AddField(
15
            model_name='mobytsmsgateway',
16
            name='default_trunk_prefix',
17
            field=models.CharField(default='0', max_length=2),
18
            preserve_default=True,
19
        ),
20
    ]
passerelle/apps/mobyt/models.py
15 15
        ('ll', _('sms low-cost')),
16 16
        ('n', _('sms top')),
17 17
    )
18
    username = models.CharField(max_length=64)
19
    password = models.CharField(max_length=64)
18
    username = models.CharField(verbose_name=_('username'), max_length=64)
19
    password = models.CharField(verbose_name=_('password'), max_length=64)
20 20
    quality = models.CharField(max_length=4, choices=MESSAGES_QUALITIES, default='l',
21 21
                               verbose_name=_('message quality'))
22
    default_country_code = models.CharField(max_length=3, default=u'33')
22
    default_country_code = models.CharField(verbose_name=_('default contry code'), max_length=3,
23
                                            default=u'33')
24
    default_trunk_prefix = models.CharField(verbose_name=_('default trunk prefix'), max_length=2,
25
                                            default=u'0')
23 26
    # FIXME: add regexp field, to check destination and from format
24 27

  
25 28
    manager_view_template_name = 'passerelle/manage/messages_service_view.html'
......
53 56
    def send_msg(self, text, sender, destinations, **kwargs):
54 57
        """Send a SMS using the Mobyt provider"""
55 58
        # unfortunately it lacks a batch API...
56
        destinations = self.clean_numbers(destinations, self.default_country_code)
59
        destinations = self.clean_numbers(destinations,
60
                                          self.default_country_code,
61
                                          self.default_trunk_prefix)
57 62
        rcpt = ','.join(destinations)
58 63
        params = {
59 64
            'user': self.username,
passerelle/apps/ovh/migrations/0005_ovhsmsgateway_default_trunk_prefix.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models, migrations
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('ovh', '0004_auto_20160407_0456'),
11
    ]
12

  
13
    operations = [
14
        migrations.AddField(
15
            model_name='ovhsmsgateway',
16
            name='default_trunk_prefix',
17
            field=models.CharField(default='0', max_length=2),
18
            preserve_default=True,
19
        ),
20
    ]
passerelle/apps/ovh/models.py
21 21
        (3, _('Messages are stored in external storage like a PDA or '
22 22
              'a PC.')),
23 23
    )
24
    account = models.CharField(max_length=64)
25
    username = models.CharField(max_length=64)
26
    password = models.CharField(max_length=64)
24
    account = models.CharField(verbose_name=_('account'), max_length=64)
25
    username = models.CharField(verbose_name=_('username'), max_length=64)
26
    password = models.CharField(verbose_name=_('password'), max_length=64)
27 27
    msg_class = models.IntegerField(choices=MESSAGES_CLASSES, default=1,
28 28
                                    verbose_name=_('message class'))
29
    credit_threshold_alert = models.PositiveIntegerField(default=100)
30
    default_country_code = models.CharField(max_length=3, default=u'33')
31
    credit_left = models.PositiveIntegerField(default=0)
29
    credit_threshold_alert = models.PositiveIntegerField(verbose_name=_('credit alert threshold'),
30
                                                         default=100)
31
    default_country_code = models.CharField(verbose_name=_('default country code'), max_length=3,
32
                                            default=u'33')
33
    default_trunk_prefix = models.CharField(verbose_name=_('default trunk prefix'), max_length=2,
34
                                            default=u'0')
35
    credit_left = models.PositiveIntegerField(verbose_name=_('credit left'), default=0)
32 36
    # FIXME: add regexp field, to check destination and from format
33 37

  
34 38
    manager_view_template_name = 'passerelle/manage/messages_service_view.html'
......
83 87

  
84 88
    def send_msg(self, text, sender, destinations, **kwargs):
85 89
        """Send a SMS using the OVH provider"""
86
        # unfortunately it lacks a batch API...
87
        destinations = self.clean_numbers(destinations, self.default_country_code)
90
        destinations = self.clean_numbers(destinations,
91
                                          self.default_country_code,
92
                                          self.default_trunk_prefix)
88 93

  
89 94
        text = unicode(text).encode('utf-8')
90 95
        to = ','.join(destinations)
passerelle/apps/oxyd/migrations/0005_oxydsmsgateway_default_trunk_prefix.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models, migrations
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('oxyd', '0004_auto_20160407_0456'),
11
    ]
12

  
13
    operations = [
14
        migrations.AddField(
15
            model_name='oxydsmsgateway',
16
            name='default_trunk_prefix',
17
            field=models.CharField(default='0', max_length=2),
18
            preserve_default=True,
19
        ),
20
    ]
passerelle/apps/oxyd/models.py
5 5
from passerelle.utils.jsonresponse import APIError
6 6
from passerelle.base.models import BaseResource
7 7
from passerelle.sms import SMSGatewayMixin
8
from django.utils.translation import ugettext_lazy as _
8 9

  
9 10

  
10 11
class OxydSMSGateway(BaseResource, SMSGatewayMixin):
11
    username = models.CharField(max_length=64)
12
    password = models.CharField(max_length=64)
13
    default_country_code = models.CharField(max_length=3, default=u'33')
12
    username = models.CharField(verbose_name=_('username'), max_length=64)
13
    password = models.CharField(verbose_name=_('password'), max_length=64)
14
    default_country_code = models.CharField(verbose_name=_('default country code'), max_length=3,
15
                                            default=u'33')
16
    default_trunk_prefix = models.CharField(verbose_name=_('default trunk prefix'), max_length=2,
17
                                            default=u'0')
14 18
    # FIXME: add regexp field, to check destination and from format
15 19

  
16 20
    manager_view_template_name = 'passerelle/manage/messages_service_view.html'
......
27 31
                    'err': 1,
28 32
                    'err_desc': 'OXYD error: some destinations failed',
29 33
                    'data': [
30
                        ['33688888888', "OXYD error: received content '' instead of 200"],
31
                        ['33677777777', "OXYD error: received content '' instead of 200"],
34
                        ['0033688888888', "OXYD error: received content '' instead of 200"],
35
                        ['0033677777777', "OXYD error: received content '' instead of 200"],
32 36
                    ],
33 37
                }
34 38
            },
......
54 58
    def send_msg(self, text, sender, destinations, **kwargs):
55 59
        """Send a SMS using the Oxyd provider"""
56 60
        # unfortunately it lacks a batch API...
57
        destinations = self.clean_numbers(destinations, self.default_country_code, prefix='')
61
        destinations = self.clean_numbers(destinations,
62
                                          self.default_country_code,
63
                                          self.default_trunk_prefix)
58 64
        results = []
59 65
        for dest in destinations:
60 66
            params = {
passerelle/sms/__init__.py
11 11
    category = _('SMS Providers')
12 12

  
13 13
    @classmethod
14
    def clean_numbers(cls, destinations, default_country_code, prefix='+'):
14
    def clean_numbers(cls, destinations, default_country_code='33',
15
                      default_trunk_prefix='0'):  # Yeah France first !
15 16
        numbers = []
16 17
        for dest in destinations:
17 18
            # most gateways needs the number prefixed by the country code, this is
18 19
            # really unfortunate.
20
            dest = dest.strip()
19 21
            number = ''.join(re.findall('[0-9]', dest))
20 22
            if dest.startswith('+'):
21
                pass  # it already is fully qualified
23
                number = '00' + number
22 24
            elif number.startswith('00'):
23 25
                # assumes 00 is international access code, remove it
24
                number = number[2:]
25
            elif number.startswith('0'):
26
                # local prefix, remove 0 and add default country code
27
                number = default_country_code + number[1:]
28
            numbers.append(prefix + number)
26
                pass
27
            elif number.startswith(default_trunk_prefix):
28
                number = '00' + default_country_code + number[len(default_trunk_prefix):]
29
            else:
30
                raise NotImplementedError('phone number %r is unsupported (no '
31
                                          'international prefix, no local '
32
                                          'trunk prefix)' % number)
33
            numbers.append(number)
29 34
        return numbers
30 35

  
31 36
    @endpoint('json-api', perm='can_send_messages', methods=['post'])
tests/test_sms.py
15 15

  
16 16

  
17 17
def test_clean_numbers():
18
    assert SMSGatewayMixin.clean_numbers(['+ 33 12'], '33') == ['+3312']
19
    assert SMSGatewayMixin.clean_numbers(['0 0 33 12'], '33') == ['+3312']
20
    assert SMSGatewayMixin.clean_numbers(['0 12'], '33') == ['+3312']
21

  
22

  
23
def test_clean_numbers_no_prefix():
24
    assert SMSGatewayMixin.clean_numbers(['+ 33 12'], '33', prefix='') == ['3312']
25
    assert SMSGatewayMixin.clean_numbers(['0 0 33 12'], '33', prefix='') == ['3312']
26
    assert SMSGatewayMixin.clean_numbers(['0 12'], '33', prefix='') == ['3312']
18
    assert SMSGatewayMixin.clean_numbers(['+ 33 12']) == ['003312']
19
    assert SMSGatewayMixin.clean_numbers(['0 0 33 12']) == ['003312']
20
    assert SMSGatewayMixin.clean_numbers(['0 12']) == ['003312']
21
    assert SMSGatewayMixin.clean_numbers(['+ 33 12'], '32', '1') == ['003312']
22
    assert SMSGatewayMixin.clean_numbers(['0 0 33 12'], '32', '1') == ['003312']
23
    assert SMSGatewayMixin.clean_numbers(['1 12'], '32', '1') == ['003212']
27 24

  
28 25

  
29 26
@pytest.fixture(params=klasses)
......
60 57
    for test_vector in getattr(connector, 'TEST_DEFAULTS', {}).get('test_vectors', []):
61 58
        with utils.mock_url(connector.URL, test_vector['response']):
62 59
            result = app.post_json(path, params=payload)
63
            print result.json
64 60
            for key, value in test_vector['result'].iteritems():
65 61
                assert key in result.json
66 62
                assert result.json[key] == value
67 63

  
64

  
68 65
def test_manage_views(admin_user, app, connector):
69 66
    url = '/%s/%s/' % (connector.get_connector_slug(), connector.slug)
70 67
    resp = app.get(url)
71
-