Last active
May 2, 2020 16:57
-
-
Save pmburu/3ce796b239f91b6f0f3f66c70e8464d5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MultipleObjectsReturned at /api/v1/bookings/booking/ | |
get() returned more than one Driver -- it returned 3! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.urls import reverse | |
from django.db import models | |
from django.utils import timezone | |
from django.core.exceptions import ValidationError | |
# import datetime as dt | |
# from django.db.models.signals import pre_save, post_save, post_delete | |
import datetime | |
from wash_authentication.models import Client, Driver | |
# SERVICES | |
# _________________________________________________________________________# | |
class VehicleType(models.Model): | |
""" Represents vehicle categories """ | |
name = models.CharField(max_length=30, help_text='E.g. SUV') | |
parent = models.ForeignKey('VehicleType', on_delete=models.CASCADE, | |
null=True, blank=True) | |
created = models.DateTimeField(default=timezone.now) | |
updated = models.DateTimeField(blank=True, null=True) | |
def __str__(self): | |
return self.name | |
class PackageDetails(models.Model): | |
""" Represents vehcile details """ | |
vehicle_category = models.ForeignKey( | |
VehicleType, on_delete=models.CASCADE, related_name='wash_package', | |
) | |
package_name = models.CharField(max_length=30, help_text='E.g. Deluxe') | |
package_description = models.TextField(null=True, blank=True) | |
package_cost = models.DecimalField(max_digits=10, decimal_places=2) | |
created = models.DateTimeField(default=timezone.now) | |
updated = models.DateTimeField(blank=True, null=True) | |
def __str__(self): | |
return self.package_name | |
def get_absolute_url(self): | |
return reverse("product_detail", kwargs={"pk": self.pk}) | |
def get_price(self): | |
return self.package_cost | |
class ExtraService(models.Model): | |
""" Represents extra services details """ | |
service_name = models.CharField(max_length=30, | |
help_text='E.g. Engine Wash') | |
service_cost = models.DecimalField(max_digits=10, decimal_places=2) | |
# BOOKINGS | |
# _________________________________________________________________________# | |
class AllocatedBooking(models.Model): | |
allocated_booking_id = models.PositiveIntegerField() | |
allocated_booking_date = models.DateField() | |
allocated_booking_time = models.TimeField() | |
allocated_driver_id = models.PositiveIntegerField() | |
def __str__(self): | |
return str(self.id) | |
class BookingState(models.TextChoices): | |
PENDING = 'PENDING', 'Pending' | |
CONFIRMED = 'CONFIRMED', 'Confirmed' | |
ON_THE_WAY = 'ON_THE_WAY', 'On The Way' | |
IN_PROGRESS = 'IN_PROGRESS', 'In Progress' | |
COMPLETE = 'COMPLETE', 'Complete' | |
class Booking(models.Model): | |
driver = models.ForeignKey(Driver, on_delete=models.CASCADE, | |
null=True, blank=True) | |
client = models.ForeignKey(Client, on_delete=models.CASCADE, | |
related_name='customer') | |
booking_location = models.CharField(max_length=250, blank=True, null=True) | |
booking_address = models.CharField(max_length=100, blank=True, null=True) | |
booking_date = models.DateField() | |
booking_time = models.TimeField() | |
service = models.ForeignKey(PackageDetails, | |
on_delete=models.CASCADE, | |
related_name='packages') | |
extra_service = models.ForeignKey(ExtraService, on_delete=models.CASCADE, | |
related_name='extra_services', | |
null=True, blank=True) | |
booking_status = models.CharField( | |
max_length=15, choices=BookingState.choices, | |
default=BookingState.PENDING | |
) | |
booked_at = models.DateTimeField(default=timezone.now) | |
finishing_at = models.TimeField() | |
booking_total = models.DecimalField(decimal_places=2, max_digits=10) | |
def __str__(self): | |
return str(self.id) | |
def calc_finishing_at(self): | |
duration = datetime.timedelta(hours=1) | |
booking_time = self.booking_time | |
self.finishing_at = (datetime.datetime.combine( | |
datetime.date(1, 1, 1), booking_time) + duration).time() | |
return self.finishing_at | |
def calc_booking_total(self): | |
if self.extra_service and self.service: | |
self.booking_total = self.service.package_cost + \ | |
self.extra_service.service_cost | |
elif self.service: | |
self.booking_total = self.service.package_cost | |
else: | |
self.booking_total = 0.00 | |
def check_time_overlap(self, fixed_start, fixed_end, new_start, new_end): | |
time_overlap = False | |
if new_start == fixed_end or new_end == fixed_start: # edge case | |
time_overlap = False | |
elif (new_start >= fixed_start and new_start <= fixed_end) or \ | |
(new_end >= fixed_start and new_end <= fixed_end): \ | |
# innner limits | |
time_overlap = True | |
elif new_start <= fixed_start and new_end >= fixed_end: \ | |
# outter limits | |
time_overlap = True | |
return time_overlap | |
def assign_driver_to_booking(self): | |
if self.finishing_at <= self.booking_time: | |
raise ValidationError( | |
'Finishing times must be after booking times' | |
) | |
bookings = Booking.objects.filter( | |
booking_date=self.booking_date, driver_id=self.driver | |
) | |
if bookings.exists(): | |
for booking in bookings: | |
""" Check whether date and time overlaps """ | |
if self.check_time_overlap( | |
booking.booking_time, booking.finishing_at, | |
self.booking_time, self.finishing_at | |
): | |
""" If all drivers are allocated, raise an error \ | |
message. """ | |
raise ValidationError( | |
'All our drivers are booked at: ' + | |
str(booking.booking_date) + ', ' + str( | |
booking.booking_time) + '-' + | |
str(booking.finishing_at)) | |
def save(self, *args, **kwargs): | |
self.calc_booking_total() | |
self.calc_finishing_at() | |
# self.assign_driver_to_booking() | |
super(Booking, self).save(*args, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"id": 143, | |
"package": "Premium", | |
"vehicle_type": "SUV", | |
"client_name": "Test Client", | |
"client_phone": "0722345678", | |
"booking_location": "-538978, 56251817", | |
"booking_address": "ABC Place, Hse 41", | |
"booking_date": "2020-05-28", | |
"booking_time": "13:00:00", | |
"booking_status": "PENDING", | |
"booked_at": "2020-05-01T11:49:40.219171", | |
"finishing_at": "14:00:00", | |
"booking_total": "3700.00", | |
"driver": 4, | |
"client": 3, | |
"service": 8, | |
"extra_service": 3 | |
} | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db.models import F | |
from django.db.models import Q | |
from django.db import connection | |
from rest_framework import serializers | |
from .models import ( | |
Booking, PackageDetails, VehicleType, ExtraService, BookingState, | |
AllocatedBooking | |
) | |
from wash_authentication.models import ( | |
Client, Driver | |
) | |
# from pushy.utils import send_push_notification | |
# from pushy.models import Device | |
class PackageSerializer(serializers.ModelSerializer): | |
package_id = serializers.IntegerField(source='id') | |
class Meta: | |
model = PackageDetails | |
fields = ( | |
'package_id', | |
'package_name', | |
'package_description', | |
'package_cost', | |
) | |
class VehicleTypeSerializer(serializers.ModelSerializer): | |
wash_package = PackageSerializer(many=True, read_only=True) | |
class Meta: | |
model = VehicleType | |
fields = ( | |
'id', | |
'name', | |
'wash_package' | |
) | |
class ExtraServiceSerializer(serializers.ModelSerializer): | |
extra_service_id = serializers.IntegerField(source='id') | |
class Meta: | |
model = ExtraService | |
fields = ( | |
'extra_service_id', | |
'service_name', | |
'service_cost', | |
) | |
# BOOKING SERIALIZER | |
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––# | |
class BookingClientSerializer(serializers.ModelSerializer): | |
class Meta: | |
model = Client | |
fields = ("id", "name", "phone") | |
class BookingDriverSerializer(serializers.ModelSerializer): | |
class Meta: | |
model = Driver | |
fields = ("id", "name", "phone") | |
class BookingSerializer(serializers.ModelSerializer): | |
package = serializers.SerializerMethodField() | |
vehicle_type = serializers.SerializerMethodField() | |
client_name = serializers.SerializerMethodField() | |
client_phone = serializers.SerializerMethodField() | |
class Meta: | |
model = Booking | |
fields = '__all__' | |
read_only_fields = ( | |
'booking_total', | |
'booked_at', 'finishing_at', 'client', 'driver' | |
) | |
write_only_fields = ('booking_status',) | |
def get_fields(self, *args, **kwargs): | |
fields = super(BookingSerializer, self).get_fields(*args, **kwargs) | |
request = self.context.get('request', None) | |
if request and getattr(request, 'method', None) == "POST": | |
fields['booking_status'].required = False | |
return fields | |
def get_package(self, obj): | |
return obj.service.package_name | |
def get_vehicle_type(self, obj): | |
return obj.service.vehicle_category.name | |
def get_client_name(self, obj): | |
return obj.client.name | |
def get_client_phone(self, obj): | |
return str(obj.client.phone) | |
# THINGS I WAS TRYING BEFORE!! UNTIL I WENT TO MANIPULATE THEM ON THE VIEWSETS | |
# def check_driver_in_booking(): | |
# drivers = Booking.objects.select_related('driver').annotate( | |
# drive_id=F('driver__user_ptr') | |
# ).all() | |
# # | |
# # print(drivers) | |
# # check_driver_in_booking() | |
# booking_arr = [] | |
# for single_driver in drivers: | |
# booking_arr.append({ | |
# 'allocated_booking_id': single_driver.id, | |
# 'allocated_booking_date': single_driver.booking_date, | |
# 'allocated_driver_id': single_driver.drive_id, | |
# 'allocated_booking_time': single_driver.booking_time, | |
# # 'client': single_driver.client.user_ptr_id | |
# }) | |
# | |
# for allocated in booking_arr: | |
# my_list = [] | |
# | |
# my_list.append(allocated) | |
# | |
# for my_dict in my_list: | |
# cursor = connection.cursor() | |
# placeholders = ', '.join(['%s'] * len(my_dict)) | |
# columns = ', '.join("`" + str(x).replace('/', '_') + | |
# "`" for x in my_dict.keys()) | |
# values = ', '.join("'" + str(x).replace('/', '_') + | |
# "'" for x in my_dict.values()) | |
# sql = "INSERT INTO %s ( %s ) VALUES ( %s )" % ( | |
# 'wash_bookings_allocatedbooking', columns, values, placeholders) | |
# | |
# # sql = f"INSERT INTO wash_bookings_allocatedbooking \ | |
# # ({', '.join(my_dict.keys())})" | |
# # sql += f"VALUES ({', '.join(my_dict.keys())})" | |
# cursor.execute(sql, my_dict) | |
# | |
# rows = cursor.fetchall() | |
# # return rows | |
# print(rows) | |
# columns = ', '.join("`" + str(x).replace('/', '_') + | |
# "`" for x in allocated.keys()) | |
# values = ', '.join("'" + str(x).replace('/', '_') + | |
# "'" for x in allocated.values()) | |
# sql = "INSERT INTO %s ( %s ) VALUES ( %s );" %[ | |
# 'wash_bookings_allocatedbooking', columns, values] | |
# cursor.execute(sql, allocated.values()) | |
# check_driver_in_booking() | |
# MyModel.objects.bulk_create(objects) | |
# | |
# for single_booking in booking_arr: | |
# for b_key, b_val in single_booking.items(): | |
# if b_key == 'drive_id': | |
# return b_val | |
# # return single_booking | |
# | |
# def check_driver_in_drivers(): | |
# driver_arr = [] | |
# all_drivers = Driver.objects.filter(active=True) | |
# | |
# for a_driver in all_drivers: | |
# driver_arr.append({ | |
# 'drive_id': a_driver.user_ptr_id, | |
# }) | |
# for single_driver in driver_arr: | |
# single_driver_arr = [] | |
# for d_key, d_val in single_driver.items(): | |
# if d_key == 'drive_id': | |
# # continue | |
# single_driver_arr.append(d_val) | |
# print(single_driver_arr) | |
# check_driver_in_booking() | |
# def comparison(self): | |
# self.check_driver_in_booking(): | |
# continue | |
# def get_driver(self, booking): | |
# qs = Driver.objects.filter(booking__isnull=True, booking=booking) | |
# serializer = BookingDriverSerializer(instance=qs, many=True) | |
# return serializer.data | |
# validated_data['driver'] = Driver.objects\ | |
# .filter( | |
# Q(booking__booking_date__isnull=True) & | |
# Q(booking__booking_time__isnull=True) | |
# ) | |
# def create(self, validated_data): | |
# validated_data['client'] = self.context['request'].user | |
# | |
# # validated_data['driver'] = Driver.objects.filter(active=True).last() | |
# bookings = Booking.objects.all() | |
# | |
# for booking in bookings: | |
# if booking.booking_date.exists() and booking.booking_time.exists(): | |
# <QuerySet [<Booking: 143>, <Booking: 146>]> | |
# bookings = Booking.objects.create(**validated_data) | |
# driver_a = Driver.objects.filter(active=True).first() | |
# driver_b = Driver.objects.filter(active=True).last() | |
# bookings = Booking.objects.all() | |
# for booking in bookings: | |
# if driver_a.user_ptr == booking.driver_id: | |
# continue | |
# validated_data['driver'] = driver_b | |
# booking = Booking.objects.create(**validated_data) | |
# print(bookings) | |
# return booking | |
# validated_data['driver'] = driver_a and driver_b | |
# validated_data['driver'] = Driver.objects.filter(active=True).last() | |
# bookings = Booking.objects.create(**validated_data) | |
# return bookings | |
# validated_data['driver'] = Driver.objects.get(active=True) | |
# validated_data['driver'] = Driver.objects.filter(active=True) -->>>> | |
# occupied_drivers = Booking.objects.select_related('driver').filter( | |
# driver__isnull=False, | |
# ).all() | |
# if occupied_drivers.exists(): | |
# for occupied_driver in occupied_drivers: | |
# drivers = Driver.objects.get(active=True) | |
# if drivers in occupied_driver. | |
# not_occupied_drivers = Booking.objects.filter(driver__in=F('driver')) | |
# print(occupied_drivers) | |
# bookings = Booking.objects.filter( | |
# booking_date=self.booking_date, | |
# driver_id=self.driver_id, | |
# booking_time=self.booking_time | |
# ) | |
# | |
# if bookings.exists(): | |
# for booking in bookings: | |
# driver = Driver.objects.get(active=True) | |
# if driver == booking.drive_id: | |
# continue | |
# print(driver) | |
# if self.check_time_overlap( | |
# booking.booking_time, booking.finishing_at, | |
# self.booking_time, self.finishing_at | |
# ): | |
# """ If all drivers are allocated, raise an error \ | |
# message. """ | |
# raise ValidationError( | |
# 'All our drivers are booked at: ' + | |
# str(booking.booking_date) + ', ' + str( | |
# booking.booking_time) + '-' + | |
# str(booking.finishing_at)) | |
# .first() or Driver.objects.exclude(active=False).last() | |
# validated_data['driver'] = Driver.objects.exclude(active=False)\ | |
# .last() | |
# validated_data['driver'] = Driver.objects.filter(active=True)[0:1]\ | |
# .get() | |
# validated_data['driver'] = Driver.objects.select_related('booking')\ | |
# .exclude( | |
# user_ptr='booking__driver' | |
# ).get(active=True) | |
# if single_driver in validated_data['driver']: | |
# break | |
# bookings = Booking.objects.create(**validated_data) | |
# print(bookings) | |
# for dr_bk in validated_data['driver']: | |
# if dr_bk.booking__id == validated_data.get['id'] and \ | |
# dr_bk: | |
# continue | |
# bookings = Booking.objects.create(**validated_data) | |
# return bookings | |
# if bookings: | |
# device = Device.objects.get() | |
# send_push_notification( | |
# 'New Booking!', | |
# payload={'body': 'Please check the new booking'}, | |
# device=device, store=True | |
# ) | |
# return bookings | |
# {'id': 143, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': 4, 'booking_time': datetime.time(13, 0), 'client': 3} | |
# {'id': 149, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': 4, 'booking_time': datetime.time(15, 0), 'client': 3} | |
# {'id': 146, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': 6, 'booking_time': datetime.time(13, 0), 'client': 3} | |
# {'id': 150, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': 6, 'booking_time': datetime.time(15, 0), 'client': 3} | |
# {'id': 151, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': 6, 'booking_time': datetime.time(18, 0), 'client': 3} | |
# {'id': 152, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': None, 'booking_time': datetime.time(18, 0), 'client': 3} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# import json | |
from django.db.models import Q | |
# from django.utils import timezone | |
from django.http import Http404 | |
from rest_framework.views import APIView | |
from rest_framework.response import Response | |
from rest_framework import status | |
from django.shortcuts import get_object_or_404 | |
from django.http import JsonResponse | |
from django.views.decorators.csrf import csrf_exempt | |
from rest_framework.decorators import api_view | |
from rest_framework import viewsets | |
from rest_framework import generics | |
from rest_framework.permissions import IsAuthenticated | |
# from rest_framework.decorators import permission_classes | |
# from .permissions import IsDriver | |
from wash_authentication.models import ( | |
Client, Driver | |
) | |
from .serializers import ( | |
PackageSerializer, VehicleTypeSerializer, ExtraServiceSerializer, | |
BookingSerializer | |
) | |
from .models import ( | |
PackageDetails, VehicleType, ExtraService, Booking, BookingState | |
) | |
# PACKAGE & SERVICE VIEWS | |
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––# | |
class PackageViewSet(viewsets.ModelViewSet): | |
serializer_class = PackageSerializer | |
permission_classes = (IsAuthenticated,) | |
def get_queryset(self): | |
return PackageDetails.objects.filter( | |
pk=self.kwargs['pk']) | |
class VehicleTypeViewSet(viewsets.ModelViewSet): | |
queryset = VehicleType.objects.all() | |
serializer_class = VehicleTypeSerializer | |
permission_classes = (IsAuthenticated,) | |
class ExtraServiceViewSet(viewsets.ModelViewSet): | |
queryset = ExtraService.objects.all() | |
serializer_class = ExtraServiceSerializer | |
permission_classes = (IsAuthenticated,) | |
# BOOKING & CART VIEWS | |
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––# | |
""" This class facilitates client booking and retrieval views """ | |
class BookingViewSet(viewsets.ModelViewSet): | |
serializer_class = BookingSerializer | |
permission_classes = (IsAuthenticated,) | |
queryset = Booking.objects.all() | |
def get_queryset(self): | |
request = self.request | |
qs = Booking.objects.select_related('client').prefetch_related( | |
'service').filter(client=request.user.client)\ | |
.order_by('-booking_date', '-booking_time') | |
return qs | |
def perform_create(self, serializer): | |
client = get_object_or_404(Client, | |
id=self.request.user.user_ptr_id) | |
drivers = Driver.objects.get(active=True) | |
# bookings = Booking.objects.get().order_by('-booking_date', | |
# 'booking_time', | |
# 'driver') | |
if drivers.exists(): | |
for drv in drivers: | |
if drv.user_ptr_id in Booking.objects\ | |
.get( | |
booking_date=self.request.data['booking_date'], | |
booking_time=self.request.data['booking_time'] | |
): | |
continue | |
try: | |
instance, created = Booking.objects\ | |
.get_or_create(driver=drv) | |
serializer.save(client=client, driver=drv) | |
except Driver.DoesNotExist: | |
# serializer.save(client=client, driver=drv) | |
return Response('All our drivers are currently booked') | |
# def perform_create(self, serializer): | |
# queryset = SignupRequest.objects.filter(user=self.request.user) | |
# if queryset.exists(): | |
# raise ValidationError('You have already signed up') | |
# serializer.save(user=self.request.user) | |
# def dispatch(self, *args, **kwargs): | |
# response = super().dispatch(*args, **kwargs) | |
# | |
# return response | |
class DriverBookingList(generics.ListAPIView): | |
""" | |
List all driver bookings, or create a new snippet. | |
""" | |
permission_classes = [IsAuthenticated] | |
queryset = Booking.objects.all() | |
serializer_class = BookingSerializer | |
def get_queryset(self): | |
request = self.request | |
qs = Booking.objects.filter( | |
Q(booking_status=BookingState.PENDING) | | |
Q(booking_status=BookingState.IN_PROGRESS), | |
driver=request.user.driver | |
).order_by("-id") | |
return qs | |
class DriverBookingDetails(generics.RetrieveUpdateAPIView): | |
""" | |
Retrieve all driver bookings, and partial update on status. | |
""" | |
permission_classes = [IsAuthenticated] | |
queryset = Booking.objects.all() | |
serializer_class = BookingSerializer | |
def get_queryset(self): | |
request = self.request | |
qs = Booking.objects.filter( | |
Q(booking_status=BookingState.PENDING) | | |
Q(booking_status=BookingState.IN_PROGRESS), | |
driver=request.user.driver | |
).order_by("-id") | |
return qs | |
def get_serializer(self, *args, **kwargs): | |
kwargs['partial'] = True | |
# kwargs['get'] = True | |
return super(DriverBookingDetails, self)\ | |
.get_serializer(*args, **kwargs) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment