293 lines
10 KiB
Python
293 lines
10 KiB
Python
# Copyright (C) 2019 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/>.
|
|
|
|
import hashlib
|
|
from urllib import parse as urlparse
|
|
|
|
from django.core.cache import cache
|
|
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from passerelle.base.models import BaseResource
|
|
from passerelle.utils.api import endpoint
|
|
from passerelle.utils.jsonresponse import APIError
|
|
|
|
EVENTBOOK_SCHEMA = {
|
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
"title": "Vivaticket",
|
|
"description": "",
|
|
"type": "object",
|
|
"required": [
|
|
"id",
|
|
"email",
|
|
"start_datetime",
|
|
"end_datetime",
|
|
"event",
|
|
"theme",
|
|
"room",
|
|
"quantity",
|
|
"form_url",
|
|
],
|
|
"properties": {
|
|
"id": {
|
|
"description": "formdata id",
|
|
"type": "string",
|
|
},
|
|
"title": {
|
|
"description": "user title",
|
|
"type": "string",
|
|
},
|
|
"last_name": {
|
|
"description": "user last name",
|
|
"type": "string",
|
|
},
|
|
"first_name": {
|
|
"description": "user first name",
|
|
"type": "string",
|
|
},
|
|
"social_reason": {
|
|
"description": "user social reason",
|
|
"type": "string",
|
|
},
|
|
"address": {
|
|
"description": "user address",
|
|
"type": "string",
|
|
},
|
|
"zipcode": {
|
|
"description": "user zipcode",
|
|
"type": "string",
|
|
},
|
|
"city": {
|
|
"description": "user city",
|
|
"type": "string",
|
|
},
|
|
"country": {
|
|
"description": "user country",
|
|
"type": "string",
|
|
},
|
|
"phone": {
|
|
"description": "user phone",
|
|
"type": "string",
|
|
},
|
|
"mobile": {
|
|
"description": "user mobile",
|
|
"type": "string",
|
|
},
|
|
"email": {
|
|
"description": "user email",
|
|
"type": "string",
|
|
},
|
|
"start_datetime": {
|
|
"description": "event start datetime",
|
|
"type": "string",
|
|
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}$",
|
|
},
|
|
"end_datetime": {
|
|
"description": "event end datetime",
|
|
"type": "string",
|
|
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}$",
|
|
},
|
|
"event": {
|
|
"description": "event id",
|
|
"type": "string",
|
|
},
|
|
"theme": {
|
|
"description": "theme id",
|
|
"type": "string",
|
|
},
|
|
"room": {
|
|
"description": "room id",
|
|
"type": "string",
|
|
},
|
|
"quantity": {"description": "quantity", "type": "string", "pattern": "^[0-9]+$"},
|
|
"booking_comment": {
|
|
"description": "booking comment",
|
|
"type": "string",
|
|
},
|
|
"room_comment": {
|
|
"description": "room comment",
|
|
"type": "string",
|
|
},
|
|
"form_url": {
|
|
"description": "form url",
|
|
"type": "string",
|
|
},
|
|
"school_level": {
|
|
"description": "School Level code",
|
|
"type": "string",
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
class VivaTicket(BaseResource):
|
|
url = models.URLField(_('API URL'))
|
|
login = models.CharField(_('API Login'), max_length=256)
|
|
password = models.CharField(_('API Password'), max_length=256)
|
|
|
|
category = _('Business Process Connectors')
|
|
|
|
log_requests_errors = False
|
|
|
|
class Meta:
|
|
verbose_name = 'VivaTicket'
|
|
|
|
@classmethod
|
|
def get_verbose_name(cls):
|
|
return cls._meta.verbose_name
|
|
|
|
def check_status(self):
|
|
response = self.requests.get(urlparse.urljoin(self.url, 'Settings/GetVersion'))
|
|
response.raise_for_status()
|
|
|
|
def get_apikey(self, renew=False):
|
|
cache_key_name = 'vivaticket-%s-key' % self.id
|
|
if not renew and cache.get(cache_key_name):
|
|
return cache.get(cache_key_name)
|
|
url = urlparse.urljoin(self.url, 'Connect/PostConnect')
|
|
payload = {'Login': self.login, 'Password': self.password}
|
|
response = self.requests.post(url, json=payload)
|
|
if not response.ok:
|
|
raise APIError(response.content)
|
|
api_key = response.json()['Key']
|
|
# api key is available for 30 minutes
|
|
cache.set(cache_key_name, api_key, 60 * 30)
|
|
return api_key
|
|
|
|
def get(self, endpoint, **kwargs):
|
|
url = urlparse.urljoin(self.url, endpoint)
|
|
params = {'key': self.get_apikey()}
|
|
params.update(kwargs)
|
|
response = self.requests.get(url, params=params)
|
|
# api key is expired
|
|
if response.status_code == 401:
|
|
params['key'] = self.get_apikey(True)
|
|
else:
|
|
return response
|
|
return self.requests.get(url, params=params)
|
|
|
|
def post(self, endpoint, payload, headers=None):
|
|
url = urlparse.urljoin(self.url, endpoint)
|
|
payload.update({'Key': self.get_apikey()})
|
|
response = self.requests.post(url, json=payload, headers=headers)
|
|
# api key is expired
|
|
if response.status_code == 401:
|
|
payload['key'] = self.get_apikey(True)
|
|
return self.requests.post(url, json=payload, headers=headers)
|
|
return response
|
|
|
|
def get_list_of_settings(self, endpoint, **kwargs):
|
|
response = self.get(endpoint, **kwargs)
|
|
json = response.json()
|
|
data = []
|
|
for setting in json.get("ListOfSettings", []):
|
|
data.append({'id': setting['Code'], 'text': setting['Label']})
|
|
return {'data': data}
|
|
|
|
@endpoint(perm='can_access', methods=['get'], description=_('Get event categories'))
|
|
def events(self, request):
|
|
return self.get_list_of_settings('Settings/GetEventCategory')
|
|
|
|
@endpoint(perm='can_access', methods=['get'], description=_('Get rooms'))
|
|
def rooms(self, request, event=None):
|
|
query = {}
|
|
if event is not None:
|
|
query['eventCategory'] = event
|
|
return self.get_list_of_settings('Settings/GetRooms', **query)
|
|
|
|
@endpoint(perm='can_access', methods=['get'], description=_('Get themes'))
|
|
def themes(self, request, room=None):
|
|
query = {}
|
|
if room is not None:
|
|
query['room'] = room
|
|
return self.get_list_of_settings('Settings/GetThemes', **query)
|
|
|
|
@endpoint(name='school-levels', perm='can_access', methods=['get'], description=_('Get school levels'))
|
|
def school_levels(self, request):
|
|
return self.get_list_of_settings('Settings/GetSchoolLevel')
|
|
|
|
def get_or_create_contact(self, data, name_id=None):
|
|
contact_payload = {
|
|
'Civility': data.get('title', ''),
|
|
'LastName': data.get('last_name', ''),
|
|
'FirstName': data.get('first_name', ''),
|
|
'SocialReason': data.get('social_reason', ''),
|
|
'Address1': data.get('address', ''),
|
|
'ZipCode': data.get('zipcode', ''),
|
|
'City': data.get('city', ''),
|
|
'Country': data.get('country', ''),
|
|
'Email': data['email'],
|
|
'Phone': data.get('phone', ''),
|
|
'Mobile': data.get('mobile', ''),
|
|
}
|
|
if name_id is not None:
|
|
unhashed_external_code = name_id
|
|
else:
|
|
unhashed_external_code = data['email']
|
|
external_code = hashlib.md5(unhashed_external_code.encode('utf-8')).hexdigest()[:20]
|
|
contact_payload['ExternalCode'] = external_code
|
|
response = self.get('Contact/Get', externalCode=external_code, email=data['email'])
|
|
self.logger.debug('Got contact response: %r', response.text)
|
|
if not response.ok:
|
|
response = self.post('Contact/Post', {'Contact': contact_payload})
|
|
self.logger.debug('Contact creation response: %r', response.text)
|
|
response.raise_for_status()
|
|
internal_code = response.json()['InternalCode']
|
|
else:
|
|
internal_code = response.json()['InternalCode']
|
|
# update contact data
|
|
url = urlparse.urljoin(self.url, 'Contact/Put')
|
|
response = self.requests.put(
|
|
url,
|
|
params={'id': response.json()['InternalCode']},
|
|
json={'Key': self.get_apikey(), 'Contact': contact_payload},
|
|
)
|
|
return {'InternalCode': internal_code}
|
|
|
|
@endpoint(
|
|
perm='can_access',
|
|
description=_('Book an event'),
|
|
post={
|
|
'description': _('Creates a booking for an event'),
|
|
'request_body': {'schema': {'application/json': EVENTBOOK_SCHEMA}},
|
|
},
|
|
)
|
|
def book(self, request, post_data, nameid=None):
|
|
booking = {
|
|
'externalCode': post_data['id'],
|
|
'startDateTime': post_data['start_datetime'],
|
|
'endDateTime': post_data['end_datetime'],
|
|
'comment': post_data.get('booking_comment', ''),
|
|
'contact': self.get_or_create_contact(post_data, nameid),
|
|
'roomList': [
|
|
{
|
|
'eventCategoryCode': post_data['event'],
|
|
'roomCode': post_data['room'],
|
|
'themeCode': post_data['theme'],
|
|
'quantity': int(post_data['quantity']),
|
|
'startDateTime': post_data['start_datetime'],
|
|
'endDateTime': post_data['end_datetime'],
|
|
'comment': post_data.get('room_comment', ''),
|
|
'schoolLevelCode': post_data.get('school_level', ''),
|
|
}
|
|
],
|
|
}
|
|
headers = {'X-Vivaticket-Form-URL': post_data['form_url']}
|
|
r = self.post('Booking/Post', {'Booking': booking}, headers=headers)
|
|
self.logger.debug('Book response: %r' % r.text)
|
|
if not r.ok:
|
|
raise APIError(r.text)
|
|
return {'data': r.json()}
|