from django.shortcuts import get_object_or_404 from drf_yasg.utils import swagger_auto_schema from rest_framework import generics, permissions, status, serializers from rest_framework.response import Response from booking.models.models import Booking, GuestonlineService, LastableService from booking.serializers.web import (PendingBookingSerializer, UpdateBookingSerializer, GetBookingSerializer, CheckBookingSerializer, CommitBookingSerializer) from establishment.models import Establishment from notification.models import Subscriber from utils.methods import get_user_ip class CheckWhetherBookingAvailable(generics.GenericAPIView): """ Checks which service to use if establishmend is managed by any """ _VALID_GUESTONLINE_PERIODS = {'lunch', 'dinner', 'afternoon', 'breakfast'} _GUESTONLINE_PERIODS_TO_PRIOR = { 'breakfast': 1, 'lunch': 2, 'afternoon': 3, 'dinner': 4, } permission_classes = (permissions.AllowAny,) serializer_class = CheckBookingSerializer pagination_class = None def _fill_period_template(self, period_template, period_name): period_template_copy = period_template.copy() period_template_copy['period'] = period_name return period_template_copy def _preprocess_guestonline_response(self, response): periods = response['periods'] periods_by_name = {period['period']: period for period in periods if 'period' in period} if not periods_by_name: return response period_template = iter(periods_by_name.values()).__next__().copy() period_template.pop('total_left_seats') period_template['hours'] = [] period_template.pop('period') processed_periods = [ periods_by_name[period_name] if period_name in periods_by_name else self._fill_period_template(period_template, period_name) for period_name in CheckWhetherBookingAvailable._VALID_GUESTONLINE_PERIODS ] unnamed_periods = filter(lambda period: 'period' not in period, periods) for unnamed_period in unnamed_periods: processed_periods.append(unnamed_period) response['periods'] = sorted(processed_periods, key=lambda x: self._GUESTONLINE_PERIODS_TO_PRIOR[x.get('period', 'lunch')]) return response def get(self, request, *args, **kwargs): is_booking_available = False establishment = get_object_or_404(Establishment, pk=kwargs['establishment_id']) service = None date = request.query_params.get('date') g_service = GuestonlineService() l_service = LastableService() if establishment.lastable_id is not None and l_service \ .check_whether_booking_available(establishment.lastable_id, date): is_booking_available = True service = l_service service.service_id = establishment.lastable_id elif establishment.guestonline_id is not None and g_service \ .check_whether_booking_available(establishment.guestonline_id, **g_service.get_certain_keys(request.query_params, {'date', 'persons'})): is_booking_available = True service = g_service service.service_id = establishment.guestonline_id response = { 'available': is_booking_available, 'type': service.service if service else None, } service_response = self._preprocess_guestonline_response(service.response) \ if establishment.guestonline_id is not None \ else service.response if service else None response.update({'details': service_response}) if service_response is None: response['available'] = False return Response(data=response, status=200) class CreatePendingBooking(generics.CreateAPIView): """ Creates pending booking """ permission_classes = (permissions.AllowAny,) serializer_class = PendingBookingSerializer @swagger_auto_schema(operation_description="Request body params\n\n" "IN GUESTONLINE (type:G): {" "'restaurant_id', 'booking_time', " "'booking_date', 'booked_persons_number'}\n" "IN LASTABLE (type:L): {'booking_time', " "'booked_persons_number', 'offer_id' (Req), " "'email', 'phone', 'first_name', 'last_name'}") def post(self, request, *args, **kwargs): data = request.data.copy() if data.get('type') == Booking.LASTABLE and data.get("offer_id") is None: raise serializers.ValidationError(detail='Offer_id is required field for Lastable service') establishment = get_object_or_404(Establishment, pk=kwargs['establishment_id']) data['restaurant_id'] = Booking.get_booking_id_by_type(establishment, data.get('type')) service = Booking.get_service_by_type(request.data.get('type')) data['user'] = request.user.pk if request.user else None service_to_keys = { Booking.GUESTONLINE: {'restaurant_id', 'booking_time', 'booking_date', 'booked_persons_number', }, Booking.LASTABLE: {'booking_time', 'booked_persons_number', 'offer_id', 'email', 'phone', 'first_name', 'last_name', }, } data['pending_booking_id'] = service.create_booking( service.get_certain_keys(data.copy(), service_to_keys[data.get('type')])) if isinstance(data['pending_booking_id'], Response): return data['pending_booking_id'] elif not data['pending_booking_id']: return Response(status=status.HTTP_403_FORBIDDEN, data='Unable to create booking') data['booking_id'] = data['pending_booking_id'] if data.get('type') == Booking.LASTABLE else None serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) serializer.save() return Response(status=status.HTTP_201_CREATED, data=serializer.data) class CommitPendingBooking(generics.UpdateAPIView): """ Commit pending booking """ queryset = Booking.objects.all() permission_classes = (permissions.AllowAny,) serializer_class = CommitBookingSerializer class UpdatePendingBooking(generics.UpdateAPIView): """ Update pending booking with contacts """ queryset = Booking.objects.all() permission_classes = (permissions.AllowAny,) serializer_class = UpdateBookingSerializer @swagger_auto_schema(operation_description="Request body params\n\n" "Required: 'email', 'phone', 'last_name', " "'first_name', 'country_code', 'pending_booking_id'," "Not req: 'note'") def patch(self, request, *args, **kwargs): instance = self.get_object() data = request.data.copy() service = Booking.get_service_by_type(instance.type) data['pending_booking_id'] = instance.pending_booking_id r = service.update_booking(service.get_certain_keys(data, { 'email', 'phone', 'last_name', 'first_name', 'country_code', 'pending_booking_id', 'note', }, { 'email', 'phone', 'last_name', 'first_name', 'country_code', 'pending_booking_id', })) if isinstance(r, Response): return r if data.get('newsletter'): Subscriber.objects.make_subscriber(email=data['email'], country_code=data['country_code'], locale=request.locale, ip_address=get_user_ip(request), user=None if request.user.is_anonymous else request.user) if service.response: # если есть предоплата, возвращаем фронту страйп-ключ для совершения оплаты и цену amount = service.response.get('amount') stripe_key = service.response.get('stripe_key') data = { 'id': instance.pk, 'amount': amount, 'stripe_key': stripe_key, 'type': instance.type, } serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) serializer.update(instance, data) return Response(status=status.HTTP_200_OK, data=data) service.commit_booking(data['pending_booking_id']) data = { 'booking_id': service.response.get('id'), 'id': instance.pk, } serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) serializer.update(instance, data) return Response(status=status.HTTP_200_OK, data=serializer.data) class CancelBooking(generics.DestroyAPIView): """ Cancel existing booking """ queryset = Booking.objects.all() permission_classes = (permissions.AllowAny,) class LastBooking(generics.RetrieveAPIView): """ Get last booking by user credentials """ permission_classes = (permissions.IsAuthenticated,) serializer_class = GetBookingSerializer lookup_field = None def get_object(self): return Booking.objects.by_user(self.request.user).latest('modified') class GetBookingById(generics.RetrieveAPIView): """ Returns booking by its id""" permission_classes = (permissions.AllowAny,) serializer_class = GetBookingSerializer queryset = Booking.objects.all()