passerelle/passerelle/contrib/iws/models.py

228 lines
8.5 KiB
Python

# passerelle.contrib.iws
# Copyright (C) 2016 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime
import json
from django.db import models
from django.utils import dateformat
from django.utils.translation import ugettext_lazy as _
from jsonschema import validate, ValidationError
import lxml.etree
import pkg_resources
from zeep import Client
from zeep.transports import Transport
from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint
from passerelle.utils.jsonresponse import APIError
CODE_EQUIPE = {"DECHET": "DMT", "ENCOMBRANT": "VPVIGIE"}
NS = '{http://isilog.fr}'
BOOKDATE_SCHEMA = {
"$schema": "http://json-schema.org/draft-03/schema#",
"title": "IWS",
"description": "",
"type": "object",
"properties": {
"firstname": {
"description": "Firstname",
"type": "string",
"required": True
},
"lastname": {
"description": "Lastname",
"type": "string",
"required": True
},
"email": {
"description": "Email",
"type": "string",
"required": True
},
"description": {
"description": "Description of the request",
"type": "string",
},
"tel_number": {
"description": "Telephone number",
"type": "string",
},
"date": {
"description": "Booking date",
"type": "string",
"required": True
},
"token": {
"description": "Booking token",
"type": "string",
"required": True
}
}
}
class IWSConnector(BaseResource):
wsdl_url = models.URLField(
max_length=400, verbose_name=_('SOAP wsdl endpoint'),
help_text=_('URL of the SOAP wsdl endpoint'))
operation_endpoint = models.URLField(
max_length=400, verbose_name=_('SOAP operation endpoint'),
help_text=_('URL of SOAP operation endpoint'))
username = models.CharField(max_length=128, verbose_name=_('Service username'))
password = models.CharField(
max_length=128, verbose_name=_('Service password'), null=True, blank=True)
database = models.CharField(max_length=128, verbose_name=_('Service database'))
category = _('Business Process Connectors')
class Meta:
verbose_name = _('IWS connector')
def _soap_call(self, iws_data, method):
client = self.soap_client()
header = client.get_element('%sIsiWsAuthHeader' % NS)
header_value = header(
IsiLogin=self.username, IsiPassword=self.password, IsiDataBaseID=self.database)
client.set_default_soapheaders([header_value])
service = client.create_service('%sIsiHelpDeskServiceSoap' % NS, self.operation_endpoint)
self.logger.debug("calling %s method of iws", method, extra={'data': iws_data})
IsiWsEntity = client.get_type('%sIsiWsEntity' % NS)
ArrayOfIsiWsDataField = client.get_type('%sArrayOfIsiWsDataField' % NS)
IsiWsDataField = client.get_type('%sIsiWsDataField' % NS)
ll = []
for field, value in iws_data.items():
ll.append(IsiWsDataField(IsiField=field, IsiValue=value))
soap_list = ArrayOfIsiWsDataField(ll)
ws_entity = IsiWsEntity(IsiFields=soap_list)
iws_res = getattr(service, method)(ws_entity)
schema_root = lxml.etree.parse(pkg_resources.resource_stream(
'passerelle.contrib.iws', 'xsd/ReponseWS.xsd'))
schema = lxml.etree.XMLSchema(schema_root)
parser = lxml.etree.XMLParser(schema=schema, encoding='utf-8')
try:
tree = lxml.etree.fromstring(iws_res.encode('utf-8'), parser).getroottree()
except lxml.etree.XMLSyntaxError:
raise APIError("IWS response is not valid")
result = {
"status": tree.find('//Statut').text,
"trace": tree.find('//Trace').text
}
fields = {}
for data_field in tree.xpath('//IsiWsDataField'):
fields[data_field.find('IsiField').text] = data_field.find('IsiValue').text
result['fields'] = fields
self.logger.debug("recieved data from %s method of iws", method, extra={'data': result})
return result
def _check_status(self, iws_res):
if iws_res['status'] != 'responseOk':
raise APIError('iws error, status: "%(status)s", trace: "%(trace)s"' % iws_res)
@endpoint(
methods=['get'], perm='can_access', example_pattern='{sti_code}/{request_type}/{volume}/',
pattern='^(?P<sti_code>[0-9]{16})/(?P<request_type>\w+)/(?P<volume>[0-9]+)/$',
parameters={
'sti_code': {
'description': _('Adrress STI code'), 'example_value': '3155570464130003'
},
'request_type': {
'description': _('DECHET or ENCOMBRANT'),
'example_value': 'DECHET'
},
'volume': {
'description': _('Volume of waste'),
'example_value': '1'
},
'city': {
'description': _('City'),
'example_value': 'TOULOUSE'
},
}
)
def checkdate(self, request, sti_code, request_type, volume, city):
if request_type not in ('DECHET', 'ENCOMBRANT'):
raise APIError("request_type should be 'DECHET' or 'ENCOMBRANT'")
iws_data = {
'C_ORIGINE': 'TELESERVICE',
'I_APP_TYPEDEM': request_type,
'I_AP_QTEAGENDA': '1' if request_type == 'DECHET' else volume,
'C_STAPPEL': 'E',
'C_NATURE': 'INC',
'DE_SYMPAPPEL': 'booking description',
'I_AP_COMMUNE': city,
'I_AP_COMMUNEINTER': sti_code,
'J_PRJACTPREV': '5',
'C_EQUIPE': CODE_EQUIPE[request_type],
'I_APP_DEMANDEUR': 'booking, demandeur',
'I_AP_ADRESSEMAIL': 'booking@localhost'
}
iws_res = self._soap_call(iws_data, 'IsiAddAndGetCall')
self._check_status(iws_res)
iws_fields = iws_res['fields']
token = iws_fields['NO_APPEL']
if not token:
raise APIError('iws error, missing token')
dates = []
result = {'data': dates}
iws_dates = iws_fields['I_APP_DATESPOSSIBLES']
if iws_dates == 'Aucune dates disponibles':
return result
for raw_date in iws_dates.split(';'):
if raw_date:
raw_date = raw_date.strip()
date_obj = datetime.strptime(raw_date, '%d/%m/%Y').date()
date_text = dateformat.format(date_obj, 'l d F Y')
dates.append({"id": raw_date, "text": date_text, "token": token})
return result
@endpoint(methods=['post'], perm='can_access')
def bookdate(self, request):
try:
data = json.loads(request.body)
except ValueError as e:
raise APIError("could not decode body to json: %s" % e, http_status=400)
try:
validate(data, BOOKDATE_SCHEMA)
except ValidationError as e:
raise APIError(e.message, http_status=400)
iws_data = {
'NO_APPEL': data['token'],
'I_APP_DEMANDEUR': '%s, %s' % (data['lastname'], data['firstname']),
'I_AP_TEL_DEMANDEU': data['tel_number'] or '',
'I_AP_ADRESSEMAIL': data['email'],
'I_AP_DATRDVAGENDA': data['date'],
'C_STAPPEL': 'E',
'I_AP_SERVICE': 'TELESERVICE',
'I_AP_SOURCE': 'TELESERVICE',
'C_ORIGINE': 'TELESERVICE',
'C_BLOCAGE': 2,
'I_AP_EMAIL': 'OUI',
'I_AP_CNIL': 1,
'I_AP_SMS': 'NON',
'DE_SYMPAPPEL': data['description'] or '',
'C_QUALIFICATIF': 'INTERVENTION',
}
iws_res = self._soap_call(iws_data, 'IsiUpdateAndGetCall')
self._check_status(iws_res)
return {'data': iws_res['fields']}