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())
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)
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)
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')
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)
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 })
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)
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