Browse Source

FDN eligibility test implementation

Damien Nicolas 9 years ago
parent
commit
5aa551fbf2

+ 1 - 1
coin/urls.py

@@ -26,6 +26,7 @@ urlpatterns = patterns(
     url(r'^billing/', include('coin.billing.urls', namespace='billing')),
     url(r'^subscription/', include('coin.offers.urls', namespace='subscription')),
     url(r'^vpn/', include('coin.vpn.urls', namespace='vpn')),
+    url(r'^fdn-white-label/', include('fdnwhitelabel.urls', namespace='fdn')),
 
     url(r'^admin/', include(admin.site.urls)),
 
@@ -39,4 +40,3 @@ urlpatterns = patterns(
 
 urlpatterns += staticfiles_urlpatterns()
 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
-

+ 41 - 0
fdnwhitelabel/cacert.crt

@@ -0,0 +1,41 @@
+-----BEGIN CERTIFICATE-----
+MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
+IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
+IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
+Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
+BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
+MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
+ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
+8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
+zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
+fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
+w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
+G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
+epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
+laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
+QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
+fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
+YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
+ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
+gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
+MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
+IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
+dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
+czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
+dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
+aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
+AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
+b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
+ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
+nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
+18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
+gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
+Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
+sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
+SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
+CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
+GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
+zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
+omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
+-----END CERTIFICATE-----

+ 23 - 0
fdnwhitelabel/forms.py

@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+from django import forms
+from django.core.validators import RegexValidator
+
+
+class EligibilityForm(forms.Form):
+    phone_number = forms.CharField(max_length=20,
+                                   label='Numéro de téléphone')
+    postal_code = forms.CharField(max_length=5,
+                                  label='Code postal',
+                                  validators=[
+                                      RegexValidator(regex=r'^\d{5}$',
+                                                     message='Code postal '
+                                                             'non valide.')
+                                      ],
+                                  )
+
+
+class FDNWhiteLabelForm(forms.Form):
+    offer_type = forms.ChoiceField(choices=tuple())
+    address = forms.CharField(label='adresse postale')
+    city = forms.CharField(max_length=200, label='commune')

+ 34 - 0
fdnwhitelabel/migrations/0002_auto_20150616_1459.py

@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('fdnwhitelabel', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='FDNUnbundledWhiteLabel',
+            fields=[
+                ('fdnwhitelabel_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='fdnwhitelabel.FDNWhiteLabel')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('fdnwhitelabel.fdnwhitelabel',),
+        ),
+        migrations.AlterModelOptions(
+            name='fdnwhitelabel',
+            options={},
+        ),
+        migrations.AddField(
+            model_name='fdnwhitelabel',
+            name='fdn_option',
+            field=models.CharField(max_length=10, null=True, verbose_name='Option chez  FDN', blank=True),
+            preserve_default=True,
+        ),
+    ]

+ 18 - 0
fdnwhitelabel/migrations/0003_auto_20150616_1515.py

@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('fdnwhitelabel', '0002_auto_20150616_1459'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='fdnwhitelabel',
+            options={'verbose_name': 'marque blanche FDN'},
+        ),
+    ]

+ 57 - 0
fdnwhitelabel/models.py

@@ -1,11 +1,28 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
+import os
 from django.db import models
 from django.core.validators import RegexValidator
+import requests
 
 from coin.configuration.models import Configuration
 
+FDN_ELIGIBILITY_URL = 'https://vador.fdn.fr/souscription/eligibilite.cgi'
+
+
+def convert_bandwidth(value):
+    """Convert bandwidth expression to Int
+    """
+    units = {'k': 1000, 'M': 1000000}
+    if value[-1] not in units:
+        raise ValueError('Incorrect bandwidth value: {}'.format(value))
+    return int(value[:-1]) * units[value[-1]]
+
+
+class FDNWhiteLabelError(Exception):
+    pass
+
 
 class FDNWhiteLabel(Configuration):
     class Meta:
@@ -36,6 +53,42 @@ class FDNWhiteLabel(Configuration):
                                  verbose_name='identifiant PPP')
     ppp_password = models.CharField(max_length=200, blank=True, null=True,
                                     verbose_name='mot de passe PPP')
+    fdn_option = models.CharField(max_length=10, verbose_name='Option chez'
+                                  '  FDN', blank=True, null=True)
+    _fdn_bundled_line = True
+
+    def is_compatible_option(self, option):
+        if self._fdn_bundled_line:
+            return 'opt1' in option['code_offre']
+        else:
+            return 'opt1' not in option['code_offre']
+
+    def check_eligibility(self):
+        """sends a request to FDN in order to get DSL eligibility status for
+        that number and postal code, and returns the best option, as a tuple of
+        offer code, and bandwidth, or raises an exception if no offer is
+        available.
+        TODO: add caching
+        """
+        if not self.phone_number or not self.postal_code:
+            raise AttributeError('Phone number and postal code must be '
+                                 'defined')
+        cert_file = os.path.join(os.getcwd(), 'fdnwhitelabel',
+                                 'cacert.crt')
+        request_params = {'tel': self.phone_number,
+                          'cp': self.postal_code,
+                          'etape': 'eligibilite',
+                          'tarif': 'blanche'}
+        eligibility = requests\
+            .get(FDN_ELIGIBILITY_URL, params=request_params,
+                 verify=cert_file).json()
+        if 'erreur' in eligibility and eligibility['erreur']:
+            raise FDNWhiteLabelError(eligibility['message'])
+        offers = [o for o in eligibility['offres']
+                  if self.is_compatible_option(o)]
+        best_offer = sorted(offers,
+                            key=lambda o: convert_bandwidth(o['debit']))[-1]
+        return (best_offer['code_offre'], best_offer['debit'])
 
     def __unicode__(self):
         return self.phone_number
@@ -43,3 +96,7 @@ class FDNWhiteLabel(Configuration):
     def subnet_event(self):
         # Do something with self.ip_subnet.all() here.
         pass
+
+
+class FDNUnbundledWhiteLabel(FDNWhiteLabel):
+    _fdn_bundled_line = True

+ 10 - 0
fdnwhitelabel/templates/fdnwhitelabel/subscribe.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+	<head>
+        <meta charset="utf-8" />
+		<title>subscribe</title>
+	</head>
+	<body>
+        <h1>Subscribe form for FDNWhiteLabel</h1>
+	</body>
+</html>

+ 28 - 0
fdnwhitelabel/templates/formtools/wizard/wizard_form.html

@@ -0,0 +1,28 @@
+{% extends "base.html" %}
+{% load i18n %}
+
+{% block head %}
+{{ wizard.form.media }}
+{% endblock %}
+
+{% block content %}
+<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
+<form action="" method="post">{% csrf_token %}
+<table>
+{{ wizard.management_form }}
+{% if wizard.form.forms %}
+    {{ wizard.form.management_form }}
+    {% for form in wizard.form.forms %}
+        {{ form }}
+    {% endfor %}
+{% else %}
+    {{ wizard.form }}
+{% endif %}
+</table>
+{% if wizard.steps.prev %}
+<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{% trans "first step" %}</button>
+<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% trans "prev step" %}</button>
+{% endif %}
+<input type="submit" value="{% trans "submit" %}"/>
+</form>
+{% endblock %}

+ 13 - 0
fdnwhitelabel/urls.py

@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.conf.urls import patterns, url
+from .views import ConfigCreationFormView
+
+
+urlpatterns = patterns(
+    '',
+    # Redirect to the appropriate configuration backend.
+    url(r'^subscribe$', ConfigCreationFormView.as_view(),
+        name='subscription'),
+)

+ 23 - 1
fdnwhitelabel/views.py

@@ -1,3 +1,25 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
 from django.shortcuts import render
+from formtools.wizard.views import SessionWizardView
 
-# Create your views here.
+from .forms import EligibilityForm, FDNWhiteLabelForm
+from .models import FDNWhiteLabel
+
+
+class ConfigCreationFormView(SessionWizardView):
+
+    form_list = [EligibilityForm, FDNWhiteLabelForm]
+
+    def get_form_initial(self, step):
+        if step == '1':
+            form_data = self.get_cleaned_data_for_step('0')
+            white_label = FDNWhiteLabel()
+            white_label.phone_number = form_data['phone_number']
+            white_label.postal_code = form_data['postal_code']
+            eligibility = white_label.check_eligibility()
+
+    def done(self, form_list, **kwargs):
+        return render(self.request, 'fdnwhitelabel/done.html', {
+            'form_data': [form.cleaned_data for form in form_list],
+        })