summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerghei Mihai <smihai@entrouvert.com>2016-02-24 14:47:17 (GMT)
committerSerghei Mihai <smihai@entrouvert.com>2016-02-29 09:52:22 (GMT)
commit775a1f59b11cfa95a2afb0c9d6d9ca0b5087d7c2 (patch)
tree090110e5939ee3e43408c1279d150a7a49813b2d
parente150ca6d1214757259505aa3cdc9d6e2bc2a60c2 (diff)
downloadpasserelle-paris-poc-gru-775a1f59b11cfa95a2afb0c9d6d9ca0b5087d7c2.zip
passerelle-paris-poc-gru-775a1f59b11cfa95a2afb0c9d6d9ca0b5087d7c2.tar.gz
passerelle-paris-poc-gru-775a1f59b11cfa95a2afb0c9d6d9ca0b5087d7c2.tar.bz2
transform and push form data to wcs (#9296)
-rw-r--r--passerelle_paris_poc_gru/migrations/0003_auto_20160225_0746.py50
-rw-r--r--passerelle_paris_poc_gru/models.py9
-rw-r--r--passerelle_paris_poc_gru/views.py180
3 files changed, 233 insertions, 6 deletions
diff --git a/passerelle_paris_poc_gru/migrations/0003_auto_20160225_0746.py b/passerelle_paris_poc_gru/migrations/0003_auto_20160225_0746.py
new file mode 100644
index 0000000..cf399ea
--- /dev/null
+++ b/passerelle_paris_poc_gru/migrations/0003_auto_20160225_0746.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('passerelle_paris_poc_gru', '0002_auto_20160225_0743'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='parispocgru',
+ name='authentic_password',
+ field=models.CharField(default='', max_length=64, verbose_name='Authentic password'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='parispocgru',
+ name='authentic_username',
+ field=models.CharField(default='', max_length=64, verbose_name='Authentic username'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='parispocgru',
+ name='wcs_user_email',
+ field=models.EmailField(default='', help_text='to authenticate against wcs', max_length=75, verbose_name='WCS user email'),
+ preserve_default=False,
+ ),
+ migrations.AlterField(
+ model_name='parispocgru',
+ name='consumer_key',
+ field=models.CharField(default=None, max_length=128, verbose_name='Consumer Key'),
+ preserve_default=True,
+ ),
+ migrations.AlterField(
+ model_name='parispocgru',
+ name='consumer_secret',
+ field=models.CharField(default=None, max_length=128, verbose_name='Consumer Secret'),
+ preserve_default=True,
+ ),
+ migrations.AlterField(
+ model_name='parispocgru',
+ name='url',
+ field=models.CharField(default=None, max_length=128, verbose_name='WebService Base Url'),
+ preserve_default=True,
+ ),
+ ]
diff --git a/passerelle_paris_poc_gru/models.py b/passerelle_paris_poc_gru/models.py
index 15f1bdf..243a33e 100644
--- a/passerelle_paris_poc_gru/models.py
+++ b/passerelle_paris_poc_gru/models.py
@@ -19,6 +19,7 @@ import requests
from django.db import models
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
+from django.db import models
from passerelle.base.models import BaseResource
@@ -27,15 +28,16 @@ class ParisPocGru(BaseResource):
url = models.CharField(max_length=128, blank=False,
default="not provided", verbose_name=_('WebService Base Url')
)
-
consumer_key = models.CharField(max_length=128, blank=False,
default="not provided", verbose_name=_('Consumer Key')
)
-
consumer_secret = models.CharField(max_length=128, blank=False,
default="not provided", verbose_name=_('Consumer Secret')
)
-
+ authentic_username = models.CharField(_('Authentic username'), max_length=64)
+ authentic_password = models.CharField(_('Authentic password'), max_length=64)
+ wcs_user_email = models.EmailField(_('WCS user email'),
+ help_text=_('to authenticate against wcs'))
category = _('Business Process Connectors')
class Meta:
@@ -71,4 +73,3 @@ class ParisPocGru(BaseResource):
self.headers = {'Authorization': 'Bearer %s' %(self.access_token)}
return self.access_token
-
diff --git a/passerelle_paris_poc_gru/views.py b/passerelle_paris_poc_gru/views.py
index 942c052..449df84 100644
--- a/passerelle_paris_poc_gru/views.py
+++ b/passerelle_paris_poc_gru/views.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2015-2016 Entr'ouvert
#
@@ -16,20 +17,90 @@
import datetime
import json
+import unicodedata
+import re
+import random
+import requests
+import time
+import logging
-from django.core.files.storage import default_storage
+from django.conf import settings
from django.core.urlresolvers import reverse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.detail import SingleObjectMixin, DetailView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.base import View
+from django.core.files.storage import default_storage
+from django.utils.http import urlencode
+from django.http import HttpResponse
+from passerelle.base.signature import sign_url
from passerelle import utils
from .models import ParisPocGru
from .forms import ParisPocGruForm
+demand_types = {
+ 1: u"demande d'information",
+ 2: u"reclamation"
+}
+
+domains = {
+ 110: u"mairie",
+ 120: u"stationnement",
+ 100: u"autre",
+ 200: u"autre",
+ 210: u"Facil'familles"
+}
+
+categories = {
+ 2: u"reservation de salle",
+ 3: u"autre",
+ 4: u"horaire de stationnement",
+ 5: u"autre",
+ 6: u"autre",
+ 7: u"problème tarifaire périscolaire",
+ 8: u"problème tarifaire petite enfance",
+ 9: u"autres"
+}
+
+titles = {
+ '1': u'Monsieur',
+ '2': u'Madame'
+}
+
+contact_mode = {
+ 1: u"mail",
+ 2: u"phone",
+ 3: u"mobile"
+}
+
+fields_mapping = {
+ 'title': 'title',
+ 'last_name': 'last_name',
+ 'first_name': 'first_name',
+ 'email': 'email',
+ 'fixed_phone_number': 'fixed_phone_number',
+ 'mobile_phone_number': 'mobile_phone_number',
+ 'contact_mode': 'contact_mode',
+ 'comment': 'comment',
+ 'FFAccountNumber': 'family_number',
+ 'IncomeRevenue-Year-2': 'tax_bill',
+}
+
+def get_authentic_site_url():
+ return settings.KNOWN_SERVICES.get('authentic').items()[0][1]['url']
+
+def simplify(s, space='-'):
+ if s is None:
+ return ''
+ s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore')
+ s = re.sub(r'[^\w\s\'%s]' % space, '', s).strip().lower()
+ s = re.sub(r'[\s\'%s]+' % space, space, s)
+ return s
+
+
class ParisPocGruCreateView(CreateView):
model = ParisPocGru
form_class = ParisPocGruForm
@@ -55,7 +126,112 @@ class ParisPocGruDetailView(DetailView):
class EndpointView(View, SingleObjectMixin):
model = ParisPocGru
+ def get_user(self, data):
+ logger = logging.getLogger(__name__)
+ a2_url = get_authentic_site_url() + 'api/users/'
+ headers={'Content-type': 'application/json'}
+ auth = (self.object.authentic_username, self.object.authentic_password)
+ kwargs = {'headers': headers, 'auth': auth}
+ uid = data['guid'].replace('-', '')
+ r = requests.get(a2_url + uid, **kwargs)
+ result = r.json()
+ if 'email' in result:
+ logger.debug('user %r found', result['email'])
+ return result
+ data = {'password': str(random.SystemRandom().random()),
+ 'first_name': data['first_name'],
+ 'last_name': data['last_name'],
+ 'email': data['email'],
+ 'username': uid,
+ 'uuid': uid,
+ 'title': data['title'],
+ 'phone': data.get('fixed_phone_number'),
+ 'mobile': data.get('mobile_phone_number')
+ }
+ r = requests.post(a2_url, data=json.dumps(data),
+ **kwargs)
+ logger.debug('user %r created', result['email'])
+ return r.json()
+
+ def sign_wcs_url(self, endpoint):
+ wcs_site = settings.KNOWN_SERVICES.get('wcs').items()[0][1]
+ params = {'orig': wcs_site['orig'], 'email': self.object.wcs_user_email}
+ url = wcs_site['url'] + endpoint
+ url += '?' + urlencode(params)
+ return sign_url(url, wcs_site.get('secret'))
+
+
+ def create_demand(self, data):
+ logger = logging.getLogger(__name__)
+ self.object = self.get_object()
+ data = data['ticket']
+ user = self.get_user(data.pop('user'))
+ extra_fields = data.pop('extra_fields')
+ demand_type = data.get('type')
+ domain = data.get('domain')
+ category = data.get('category')
+
+ # compute form slug from input data
+ assert demand_type in demand_types
+ form_slug = demand_types[demand_type]
+ assert domain in domains
+ form_slug += ' ' + domains[domain]
+ assert category in categories
+ form_slug += ' ' + categories[category]
+ logger.debug('posting to form "%r"', simplify(form_slug))
+
+ extra_fields = dict([(item['field'], {'metadata': item.get('metadata'),
+ 'value': item['value']}) for item in extra_fields])
+ data.update(extra_fields)
+ user_uuid = user.get('uuid')
+ for i in xrange(50):
+ try:
+ r = requests.get(self.sign_wcs_url('api/users/%s/' % user_uuid))
+ user_data = r.json()
+ r.raise_for_status()
+ break
+ except requests.HTTPError as e:
+ if e.response.status_code != 404:
+ raise
+ time.sleep(0.1)
+ else:
+ # return gateway timeout if no user provisionned
+ return HttpResponse(status=504)
+
+ formdata = {}
+ for f, k in fields_mapping.iteritems():
+ if f == 'title':
+ d = titles[user.get(f)]
+ formdata['%s_raw' % k] = user.get(f)
+ elif f == 'contact_mode':
+ d = contact_mode[data.get(f)]
+ formdata['%s_raw' % k] = data.get(f)
+ elif f == 'IncomeRevenue-Year-2' and data.get(f):
+ metadata = dict([(item['name'], item['value']) for item in data.get(f, {}).get('metadata', [])])
+ if metadata:
+ d = {'filename': metadata['filename'],
+ 'content': data.get(f, {}).get('value'),
+ 'content_type': metadata['mimetype']}
+ else:
+ d = data.get(f, user.get(f))
+ if not d:
+ continue
+ if 'value' in d:
+ d = d['value']
+ formdata[str(k)] = d
+ data = {
+ 'data': formdata,
+ 'context': {'user_id': user_data['id']}
+ }
+ url = self.sign_wcs_url('api/formdefs/%s/submit' % simplify(form_slug))
+ response = requests.post(url, data=json.dumps(data),
+ headers={'Content-type': 'application/json'})
+ if response.json().get('err') != 0:
+ raise Exception('error %r' % response.content)
+ return response.json()['data']['id']
+
@method_decorator(csrf_exempt)
+ @utils.to_json('api')
def dispatch(self, request, *args, **kwargs):
filename = 'paris-poc-gru-dump-%s.txt' % self.get_object().slug
with default_storage.open(filename, mode='a') as fp:
@@ -65,4 +241,4 @@ class EndpointView(View, SingleObjectMixin):
'qs': request.environ.get('QUERY_STRING'),
'body': request.body,
})
- return utils.response_for_json(request, {})
+ return self.create_demand(json.loads(request.body))