from abc import ABC, abstractmethod import json import requests from django.conf import settings from rest_framework import status import booking.models.models as models from rest_framework import serializers class AbstractBookingService(ABC): """ Abstract class for Guestonline && Lastable booking services""" def __init__(self, service): self.service = None self.response = None if service not in [models.Booking.LASTABLE, models.Booking.GUESTONLINE]: raise Exception('Service %s is not implemented yet' % service) self.service = service if service == models.Booking.GUESTONLINE: self.token = settings.GUESTONLINE_TOKEN self.url = settings.GUESTONLINE_SERVICE elif service == models.Booking.LASTABLE: self.token = settings.LASTABLE_TOKEN self.url = settings.LASTABLE_SERVICE @staticmethod def get_certain_keys(d: dict, keys_to_preserve: set) -> dict: """ Helper """ return {key: d[key] for key in d.keys() & keys_to_preserve} @abstractmethod def check_whether_booking_available(self, restaurant_id, date): """ checks whether booking is available """ if date is None: raise serializers.ValidationError(detail='date query param is required') @abstractmethod def cancel_booking(self, payload): """ cancels booking and returns the result """ pass @abstractmethod def create_pending_booking(self, payload): """ returns pending booking id if created. otherwise False """ pass @abstractmethod def update_pending_booking(self, payload): """ updates pending booking with contacts """ pass @abstractmethod def get_common_headers(self): pass @abstractmethod def get_booking_details(self, payload): """ fetches booking details from external service """ pass class GuestonlineService(AbstractBookingService): def __init__(self): super().__init__(models.Booking.GUESTONLINE) def get_common_headers(self): return {'X-Token': self.token, 'Content-type': 'application/json', 'Accept': 'application/json'} def check_whether_booking_available(self, restaurant_id, date: str): super().check_whether_booking_available(restaurant_id, date) url = f'{self.url}v1/runtime_services' params = {'restaurant_id': restaurant_id, 'date': date, 'expands[]': 'table_availabilities'} r = requests.get(url, headers=self.get_common_headers(), params=params) if not status.is_success(r.status_code): return False response = json.loads(r.content)['runtime_services'] keys_to_preserve = {'left_seats', 'table_availabilities', 'closed', 'start_time', 'end_time', 'last_booking'} response = map(lambda x: self.get_certain_keys(x, keys_to_preserve), response) self.response = response return True def commit_booking(self, payload): url = f'{self.url}v1/pending_bookings/{payload}/commit' r = requests.put(url, headers=self.get_common_headers()) self.response = json.loads(r.content) if status.is_success(r.status_code) and self.response is None: raise serializers.ValidationError(detail='Booking already committed.') return status.is_success(r.status_code) def update_pending_booking(self, payload): booking_id = payload.pop('pending_booking_id') url = f'{self.url}v1/pending_bookings/{booking_id}' payload['lastname'] = payload.pop('last_name') payload['firstname'] = payload.pop('first_name') payload['mobile_phone'] = payload.pop('phone') headers = self.get_common_headers() r = requests.put(url, headers=headers, data=json.dumps({'contact_info': payload})) return status.is_success(r.status_code) def create_pending_booking(self, payload): url = self.url + 'v1/pending_bookings' payload['hour'] = payload.pop('booking_time') payload['persons'] = payload.pop('booked_persons_number') payload['date'] = payload.pop('booking_date') r = requests.post(url, headers=self.get_common_headers(), data=json.dumps(payload)) return json.loads(r.content)['id'] if status.is_success(r.status_code) else False def cancel_booking(self, payload): url = f'{self.url}v1/pending_bookings/{payload}' r = requests.delete(url, headers=self.get_common_headers()) return status.is_success(r.status_code) def get_booking_details(self, payload): url = f'{self.url}v1/bookings/{payload}' r = requests.get(url, headers=self.get_common_headers()) return json.loads(r.content) class LastableService(AbstractBookingService): def __init__(self): super().__init__(models.Booking.LASTABLE) self.proxies = { 'http': settings.LASTABLE_PROXY, 'https': settings.LASTABLE_PROXY, } def create_pending_booking(self, payload): return False # url = f'{self.url}v1/restaurant/5d951f0e2dc6a50017588065/offers' # r = requests.post(url, headers=self.get_common_headers(), proxies=self.proxies) # return json.loads(r.content)['id'] if status.is_success(r.status_code) else False def get_common_headers(self): return {'Authorization': f'Bearer {self.token}', 'Content-type': 'application/json', 'Accept': 'application/json'} def check_whether_booking_available(self, restaurant_id, date): super().check_whether_booking_available(restaurant_id, date) url = f'{self.url}v1/restaurant/{restaurant_id}/offers' r = requests.get(url, headers=self.get_common_headers(), proxies=self.proxies) if not status.is_success(r.status_code): return False response = json.loads(r.content)['data'] self.response = response return True def commit_booking(self, payload): return False def update_pending_booking(self, payload): return False def cancel_booking(self, payload): return False def get_booking_details(self, payload): return {}