Skip to content

Instantly share code, notes, and snippets.

@pmburu
Last active April 29, 2020 17:13
Show Gist options
  • Select an option

  • Save pmburu/05fa49c1fa55596e8a807674d50b9576 to your computer and use it in GitHub Desktop.

Select an option

Save pmburu/05fa49c1fa55596e8a807674d50b9576 to your computer and use it in GitHub Desktop.

Revisions

  1. pmburu revised this gist Apr 29, 2020. 1 changed file with 48 additions and 19 deletions.
    67 changes: 48 additions & 19 deletions models.py
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    from django.urls import reverse
    from django.db import models
    from django.utils import timezone
    # from datetime import datetime, timedelta
    from django.core.exceptions import ValidationError
    # import datetime as dt
    # from django.db.models.signals import pre_save, post_save, post_delete
    import datetime
    @@ -111,27 +111,56 @@ def calc_booking_total(self):
    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):
    """ Update booking status to \
    add driver automatically to booking """
    if not isinstance(
    (self.booking_time, self.finishing_at), self.booking_date
    ):
    # if not (self.booking_time, self.finishing_at) in self.booking_date:
    """ Checking whether the driver is working \
    or has a booking on that day and time"""
    if not self.driver:

    Booking.objects\
    .select_related(self.driver.id)\
    .filter(id=self.id)\
    .add(driver=self.driver)
    # return assign_booking
    return self.driver
    # return assign_booking
    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
    )
    # drivers = Booking.objects.filter(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 booking.driver is None:
    # """ If time overlaps, check whether there is \
    # a driver assigned to that booking. If there is none, \
    # assign one."""
    # Booking.objects\
    # .filter(id=booking.id)\
    # .update(driver=booking.driver)
    # return booking.driver
    # else:
    """ 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()
    self.assign_driver_to_booking()
    super(Booking, self).save(*args, **kwargs)
  2. pmburu revised this gist Apr 29, 2020. 1 changed file with 19 additions and 0 deletions.
    19 changes: 19 additions & 0 deletions result.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    {
    "id": 27,
    "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": "12:00:00",
    "booking_status": "PENDING",
    "booked_at": "2020-04-29T15:17:23.660080",
    "finishing_at": "13:00:00",
    "booking_total": "3700.00",
    "driver": null,
    "client": 3,
    "service": 8,
    "extra_service": 3
    }
  3. pmburu revised this gist Apr 29, 2020. 1 changed file with 13 additions and 10 deletions.
    23 changes: 13 additions & 10 deletions models.py
    Original file line number Diff line number Diff line change
    @@ -95,13 +95,22 @@ def __str__(self):
    return f'{self.booking_status} order containing: \
    {len(self.service.all())} service'

    def get_finishing_at(self):
    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 assign_driver_to_booking(self):
    """ Update booking status to \
    add driver automatically to booking """
    @@ -122,13 +131,7 @@ def assign_driver_to_booking(self):
    # return assign_booking

    def save(self, *args, **kwargs):
    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
    self.get_finishing_at()
    self.assign_driver_to_booking()
    self.calc_booking_total()
    self.calc_finishing_at()
    # self.assign_driver_to_booking()
    super(Booking, self).save(*args, **kwargs)
  4. pmburu created this gist Apr 29, 2020.
    134 changes: 134 additions & 0 deletions models.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,134 @@
    from django.urls import reverse
    from django.db import models
    from django.utils import timezone
    # from datetime import datetime, timedelta
    # 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 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.OneToOneField(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 f'{self.booking_status} order containing: \
    {len(self.service.all())} service'

    def get_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 assign_driver_to_booking(self):
    """ Update booking status to \
    add driver automatically to booking """
    if not isinstance(
    (self.booking_time, self.finishing_at), self.booking_date
    ):
    # if not (self.booking_time, self.finishing_at) in self.booking_date:
    """ Checking whether the driver is working \
    or has a booking on that day and time"""
    if not self.driver:

    Booking.objects\
    .select_related(self.driver.id)\
    .filter(id=self.id)\
    .add(driver=self.driver)
    # return assign_booking
    return self.driver
    # return assign_booking

    def save(self, *args, **kwargs):
    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
    self.get_finishing_at()
    self.assign_driver_to_booking()
    super(Booking, self).save(*args, **kwargs)
    97 changes: 97 additions & 0 deletions serializers.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,97 @@

    from rest_framework import serializers
    from .models import (
    Booking, PackageDetails, VehicleType, ExtraService
    )

    from wash_authentication.models import (
    Client, Driver
    )


    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__'
    # exclude = ('client',)
    read_only_fields = (
    'booking_total', 'booking_status',
    'booked_at', 'finishing_at', 'client'
    )

    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)

    def create(self, validated_data):
    validated_data['client'] = self.context['request'].user
    booking = Booking.objects.create(**validated_data)

    return booking
    144 changes: 144 additions & 0 deletions views.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,144 @@
    import json

    from django.utils import timezone
    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.permissions import IsAuthenticated
    from rest_framework.decorators import permission_classes
    # from django_filters.rest_framework import DjangoFilterBackend


    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
    # ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––#


    class BookingViewSet(viewsets.ModelViewSet):
    serializer_class = BookingSerializer
    # permission_classes = (IsAuthenticated,)
    queryset = (
    Booking.objects
    .select_related('client')
    .prefetch_related('service')
    )
    # filter_backends = (DjangoFilterBackend,)
    # filterset_fields = ('booking_status', 'client__name',)

    def dispatch(self, *args, **kwargs):
    response = super().dispatch(*args, **kwargs)

    return response


    @api_view(['GET'])
    @permission_classes((IsAuthenticated, ))
    def driver_get_ready_bookings(request):
    bookings = BookingSerializer(
    Booking.objects.filter(booking_status=BookingState.PENDING,
    driver=None).order_by("-id"),
    many=True
    ).data

    return JsonResponse({"driver_bookings": bookings})


    @csrf_exempt
    # POST
    # params: access_token, order_id
    def driver_pick_booking(request):

    if request.method == "POST":

    # Get driver
    driver = request.user.driver

    # Check that driver only has one order
    if Booking.objects.filter(driver=driver).exclude(
    booking_status=BookingState.ON_THE_WAY
    ):
    return JsonResponse({
    "status": "failed", "error":
    "You can only cater for one booking at a time."
    })

    try:
    booking = Booking.objects.get(
    id=request.POST["booking_id"],
    driver=None,
    booking_status=BookingState.IN_PROGRESS
    )
    booking.driver = driver
    booking.booking_status = BookingState.ON_THE_WAY
    booking.pickedup_at = timezone.now()
    booking.save()
    return JsonResponse({"status": "success"})
    except Booking.DoesNotExist:
    return JsonResponse({
    "status": "failed", "error":
    "This order has been picked up by another driver."
    })

    return JsonResponse({})

    # GET params: access_token


    @api_view(['GET'])
    @permission_classes((IsAuthenticated, ))
    def driver_get_latest_booking(request):
    # Get driver
    driver = request.user.driver

    booking = BookingSerializer(
    Booking.objects.filter(driver=driver).order_by("booked_at").last()
    ).data

    return JsonResponse({"next_jobs": booking})

    # POST params: access_token, order_id
    @csrf_exempt
    def driver_complete_booking(request):
    # Get driver

    driver = request.user.driver

    booking = Booking.objects.get(id=request.POST["booking_id"], driver=driver)
    booking.status = BookingState.COMPLETE
    booking.save()

    return JsonResponse({"status": "success"})