コード例 #1
0
    def partial_update(self, request, username=None):
        """Partially Updates the driver profile.

        Arguments:
            request: the request data sent by the user, it is used
                     to check the user's permissions and get the data
            username: the username of the driver profile that will be updated

        Returns:
            HTTP 404 Response if driver profile is not found,
            HTTP 400 Response if the data is
            not valid with the errors,
            HTTP 403 Response if the user is not
            authorized to update that profile,
            if not returns HTTP 200 Response with the update JSON data.
        """

        driver_profile = get_object_or_404(DriverProfileModel,
                                           account__username=username)
        self.check_object_permissions(request, driver_profile)
        serializer = DriverProfileSerializer(driver_profile,
                                             data=request.data,
                                             partial=True)
        if serializer.is_valid():
            serializer.save()
            update_session_auth_hash(request, driver_profile.account)
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
コード例 #2
0
    def create(self, request):
        """Creates A new driver profile and Logs it In.

        Checks if user is authenticated if true, return HTTP 401 Response,
        then it Validates the post data if not valid,
        return HTTP 400 Response, then creates the driver and logs him in,
        and returns 201 Response.

        Arguments:
            request: the request data sent by the user, it is used
                     to get the post data from it to get validated and created,
                     and to log the driver in.

        Returns:
            HTTP 400 Response if data is not valid,
            HTTP 401 Response if user is already logged in,
            HTTP 201 Response with the JSON data of the created profile.
        """

        if not request.user.is_authenticated:
            serializer = DriverProfileSerializer(data=request.data)
            if serializer.is_valid():
                driver_profile = serializer.save()
                login(request, driver_profile.account)
                return Response(serializer.data,
                                status=status.HTTP_201_CREATED)
            return Response(serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)
        return Response(status=status.HTTP_401_UNAUTHORIZED)
コード例 #3
0
    def test_driver_location(self):
        """test for driver location"""

        # true
        serializer = DriverProfileSerializer(data={'account': {'username': '******',
                                                               'password': '******'},
                                                   'profile_photo': img_file(), 'phone_number': 1234,
                                                   'vehicle_type': 'B', 'is_available': True,
                                                   'live_location_longitude': 30,
                                                   'live_location_latitude': 30})
        self.assertTrue(serializer.is_valid())

        # wrong longitude
        serializer = DriverProfileSerializer(data={'account': {'username': '******',
                                                               'password': '******'},
                                                   'profile_photo': img_file(), 'phone_number': 1234,
                                                   'vehicle_type': 'B', 'is_available': True,
                                                   'live_location_longitude': 1000,
                                                   'live_location_latitude': 30})
        self.assertFalse(serializer.is_valid())

        # wrong latitude
        serializer = DriverProfileSerializer(data={'account': {'username': '******',
                                                               'password': '******'},
                                                   'profile_photo': img_file(), 'phone_number': 1234,
                                                   'vehicle_type': 'B', 'is_available': True,
                                                   'live_location_longitude': 30,
                                                   'live_location_latitude': -200})
        self.assertFalse(serializer.is_valid())
コード例 #4
0
class OrderSerializer(serializers.ModelSerializer):
    """The list and create serializer for the orders model"""

    user = UserProfileSerializer()
    driver = DriverProfileSerializer()
    shops = ShopProfileSerializer(many=True, keep_only=('profile_photo', 'name'))

    class Meta:
        model = OrderModel
        fields = ('id', 'user', 'driver', 'shops', 'ordered_at', 'status', 'final_price')
コード例 #5
0
    def test_reviews_count(self):
        """test for driver number of reviews func"""

        account = User.objects.create_user(username='******', password='******')
        driver_profile = DriverProfileModel.objects.create(account=account, phone_number=12345,
                                                           vehicle_type='M', profile_photo='/drivers/tests/sample.jpg')
        account2 = User.objects.create_user(username='******', password='******')
        user_profile = UserProfileModel.objects.create(account=account2, phone_number=12345)

        DriverReviewModel.objects.create(user=user_profile, driver=driver_profile,
                                         text='text', stars=5)

        serializer = DriverProfileSerializer(driver_profile)
        self.assertEqual(serializer.data.get('reviews_count', 0), 1)

        DriverReviewModel.objects.create(user=user_profile, driver=driver_profile,
                                         text='text', stars=5)

        serializer = DriverProfileSerializer(driver_profile)
        self.assertEqual(serializer.data.get('reviews_count', 0), 2)
コード例 #6
0
    def list(self, request):
        """Lists all available driver profiles near a certain location

        Arguments:
            request: the request data sent by the user,
                     it is used to get the queries entered by user,
                     and for Pagination

        Returns:
            returns HTTP 200 Response with the drivers' JSON data.
            if there are no coordinates given will return 400 Response.
        """

        try:
            user_longitude = float(request.GET.get('longitude'))
            user_latitude = float(request.GET.get('latitude'))
        except Exception:
            return Response("invalid coordinates",
                            status=status.HTTP_400_BAD_REQUEST)

        min_active_time = timezone.now() - timezone.timedelta(seconds=10)
        queryset = DriverProfileModel.objects.annotate(distance=haversine(
            user_latitude, user_longitude, F('live_location_latitude'),
            F('live_location_longitude'))).filter(
                distance__lte=2.5,
                is_busy=False,
                last_time_online__gte=min_active_time,
                is_active=True,
                is_available=True).order_by('distance')

        paginator = LimitOffsetPagination()
        paginator.default_limit = 10
        paginator.max_limit = 100
        paginated_queryset = paginator.paginate_queryset(queryset, request)
        serializer = DriverProfileSerializer(paginated_queryset, many=True)

        return Response(
            data={
                'limit': paginator.limit,
                'offset': paginator.offset,
                'count': paginator.count,
                'drivers': serializer.data
            })
コード例 #7
0
    def retrieve(self, request, username=None):
        """Retrieves a driver profile by its username

        Checks if a driver profile with this username exist,
        if not, returns HTTP 404 Response.

        Arguments:
            request: the request data sent by the user,
                     it is not used here but required by django
            username: the username of the driver profile that the user wants info about.

        Returns:
            HTTP 404 Response if driver profile is not found,
            if not, returns HTTP 200 Response with the profile's JSON data.
        """

        driver_profile = get_object_or_404(DriverProfileModel,
                                           account__username=username)
        serializer = DriverProfileSerializer(driver_profile)
        return Response(serializer.data)
コード例 #8
0
class OrderDetailSerializer(serializers.ModelSerializer):
    """The Detailed serializer for the orders model"""

    user = UserProfileSerializer(read_only=True)
    driver = DriverProfileSerializer(read_only=True)
    item_groups = OrderItemsGroupSerializer(many=True, read_only=True)
    items = OrderItemSerializer(many=True, write_only=True)
    shipping_address = OrderAddressSerializer()

    class Meta:
        model = OrderModel
        fields = ('id', 'user', 'driver', 'items', 'item_groups', 'ordered_at', 'status',
                  'shipping_address', 'final_price', 'delivery_fee', 'vat')

        read_only_fields = ('id', 'user', 'driver', 'ordered_at', 'final_price',
                            'delivery_fee', 'vat')

    def validate(self, attrs):
        """checks if send order data is valid"""

        if not self.instance:
            # does this validation only if the user is creating a new order not updating

            user_longitude = float(attrs.get('shipping_address', '').get('location_longitude', ''))
            user_latitude = float(attrs.get('shipping_address', '').get('location_latitude', ''))

            # checks if there are any drivers available at user's location
            min_active_time = timezone.now() - timezone.timedelta(seconds=10)
            driver_available = DriverProfileModel.objects.annotate(distance=haversine(user_latitude, user_longitude,
                                                                                      F('live_location_latitude'),
                                                                                      F('live_location_longitude'))
                                                                   ).filter(distance__lte=2.5, is_busy=False,
                                                                            is_active=True, is_available=True,
                                                                            last_time_online__gte=min_active_time
                                                                            ).exists()

            if not driver_available:
                raise serializers.ValidationError("there are no drivers in your area")

            # checks if every item's shop is available and near the user's location
            for item in attrs['items']:
                product = item['product']
                shop = product.shop
                shops = []
                if shop not in shops:
                    shops.append(shop)

                    if not shop.is_active or not shop.is_open or shop.opens_at > timezone.now().time() or shop.closes_at < timezone.now().time():
                        raise serializers.ValidationError("this product's shop is not available right now")

                    shop_longitude = shop.address.location_longitude
                    shop_latitude = shop.address.location_latitude
                    distance = haversine(user_latitude, user_longitude, shop_latitude, shop_longitude)
                    if distance > 2.5:
                        raise serializers.ValidationError("these products are not available in you area")
        return attrs

    def validate_status(self, data):
        """validates the status attr in the order send data"""

        status_options = {'C': 1, 'P': 2, 'D': 3}
        # status is update-only and can't change to a previous one
        # for example a delivered order can't be reverted
        # back to a still-preparing order
        if self.instance and status_options[data] - status_options[self.instance.status] < 0:
            raise serializers.ValidationError("can't revert orders status")

        return data

    def create(self, validated_data):
        """Creates a new Order"""

        items_data = validated_data.pop('items')

        shops = set()
        item_groups = set()
        delivery_fee = 0
        vat = 0
        subtotal = 0

        for item in items_data:
            choices = item.pop('choices', [])
            add_ons_ids = item.pop('add_ons_sorts', [])
            order_item = OrderItemModel.objects.create(**item)
            # increase the product's number of sells
            order_item.product.num_sold = F('num_sold') + 1
            order_item.product.save()
            for choice in choices:
                # creates a new choice model with choosed option group and option
                option_group = order_item.product.option_groups.get(sort=choice['option_group_id'])
                choosed_option = option_group.options.get(sort=choice['choosed_option_id'])
                Choice.objects.create(order_item=order_item, option_group=option_group,
                                      choosed_option=choosed_option)
            for add_on_id in add_ons_ids:
                # adds all add-ons to that item
                add_on = order_item.product.add_ons.get(sort=add_on_id)
                order_item.add_ons.add(add_on)
            order_item.price = order_item.get_item_price()
            shop = order_item.product.shop
            if shop not in shops:
                # creates a new item group that for that shop
                shops.add(shop)
                delivery_fee += shop.delivery_fee  # adds the shop's delivery_fee to the orders total

                item_group = OrderItemsGroupModel.objects.create(shop=shop)
                item_groups.add(item_group)
            else:
                # sets the item group of that item if it already exists and in item_groups list
                item_group = [x for x in item_groups if x.shop == shop][0]  # only one shop in that list

            order_item.item_group = item_group
            order_item.save()

            # adds up that item's prices to the whole order
            vat += order_item.calculate_vat()
            subtotal += order_item.get_item_price()

        final_price = subtotal + vat + delivery_fee

        address_data = validated_data.pop('shipping_address')
        shipping_address = OrderAddressModel.objects.create(**address_data)

        user_longitude = float(shipping_address.location_longitude)
        user_latitude = float(shipping_address.location_latitude)
        min_active_time = timezone.now() - timezone.timedelta(seconds=10)
        # gets the nearest driver available
        driver = DriverProfileModel.objects.annotate(distance=haversine(user_latitude, user_longitude,
                                                                        F('live_location_latitude'),
                                                                        F('live_location_longitude'))
                                                     ).filter(distance__lte=2.5, is_busy=False,
                                                              is_active=True, is_available=True,
                                                              last_time_online__gte=min_active_time
                                                              ).order_by('distance')[0]
        # the driver now is busy and CAN'T deliver new orders
        driver.is_busy = True
        driver.save()

        # creates the final order
        order = OrderModel.objects.create(driver=driver, shipping_address=shipping_address,
                                          status='C', final_price=final_price,
                                          delivery_fee=delivery_fee, vat=vat,
                                          subtotal=subtotal, **validated_data)
        order.shops.set(shops)
        order.item_groups.set(item_groups)
        return order

    def update(self, instance, validated_data):
        """Updates the Order"""

        # only order's status can be changed
        status = validated_data.get('status', None)
        if status:
            instance.status = status
            if status == 'D':
                # means that the order is well done and delivered
                # the driver will be marked as not busy and
                # can deliver other orders
                instance.driver.is_busy = False
                instance.driver.save()
            instance.save()
        return instance