diff --git a/products/tests.py b/products/tests.py index a4ab91f..2b09c7c 100644 --- a/products/tests.py +++ b/products/tests.py @@ -535,6 +535,37 @@ class ProductSearchTest(TestCase): # check prices self.assertTrue(payload['prices']['min'] <= payload['prices']['max']) + def test_anon_user_can_search_no_querystring(self): + expected_instances = [ + self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes"), + self.factory(tags="colores/rojos, tono/brillante"), + self.factory(tags="lunares/azules", description="zapatos rojos"), + self.factory(tags="lunares/rojos", description="zapatos"), + self.factory(attributes='"zapatos de campo", tono/oscuro'), + ] + unexpected_instances = [ + self.factory(description="chanclas"), + self.factory(tags="azules"), + ] + + q = quote("zapatos rojos") + + url = f"{self.endpoint}?q=" + # send in request + response = self.client.get(url) + # check response + self.assertEqual(response.status_code, 200) + # load response data + payload = response.json() + # check for expected fields in payload + self.assertIsNotNone(payload.get('filters')) + self.assertIsNotNone(payload.get('count')) + self.assertIsNotNone(payload.get('products')) + self.assertIsNotNone(payload.get('prices')) + + # check for object creation + self.assertEquals(len(payload['products']), len(expected_instances) + len(unexpected_instances)) + def test_anon_user_can_paginate_search(self): expected_instances = [ self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes"), @@ -549,10 +580,10 @@ class ProductSearchTest(TestCase): ] q = quote("zapatos rojos") - limit = 2 + # test limit less than available + limit = 3 url = f"{self.endpoint}?q={q}&limit={limit}" - # send in request response = self.client.get(url) # check response @@ -562,6 +593,44 @@ class ProductSearchTest(TestCase): self.assertEquals(len(payload['products']), limit) self.assertEquals(payload['count'], len(expected_instances)) + # test limit less than available + limit = 10 + url = f"{self.endpoint}?q={q}&limit={limit}" + response = self.client.get(url) + + # check response + self.assertEqual(response.status_code, 200) + # load response data + payload = response.json() + self.assertEquals(len(payload['products']), len(expected_instances)) + self.assertEquals(payload['count'], len(expected_instances)) + + # test limit equal to available, offset zero + limit = len(expected_instances) + offset = 0 + url = f"{self.endpoint}?q={q}&limit={limit}&offset={offset}" + response = self.client.get(url) + + # check response + self.assertEqual(response.status_code, 200) + # load response data + payload = response.json() + self.assertEquals(len(payload['products']), len(expected_instances)) + self.assertEquals(payload['count'], len(expected_instances)) + + # test limit and offset equal to available + limit = len(expected_instances) + offset = limit + url = f"{self.endpoint}?q={q}&limit={limit}&offset={offset}" + response = self.client.get(url) + + # check response + self.assertEqual(response.status_code, 200) + # load response data + payload = response.json() + self.assertEquals(len(payload['products']), 0) + self.assertEquals(payload['count'], len(expected_instances)) + def test_anon_user_can_filter_shipping_cost_true(self): expected_instances = [ self.factory(tags="colores/rojos, tono/brillante", shipping_cost=100.00), @@ -685,7 +754,6 @@ class ProductSearchTest(TestCase): ] q = quote("zapatos rojos") - # discount=true url = f"{self.endpoint}?q={q}&category=ropa/nueva" # send in request response = self.client.get(url) @@ -815,7 +883,7 @@ class ProductSearchTest(TestCase): url = f"{self.endpoint}?q={q}&order=oldest" # send in request response = self.client.get(url) - + import ipdb; ipdb.set_trace() # check response self.assertEqual(response.status_code, 200) # load response data diff --git a/products/views.py b/products/views.py index 226c55d..3199d80 100644 --- a/products/views.py +++ b/products/views.py @@ -90,115 +90,116 @@ def load_coop_products(request): @api_view(['GET',]) # include allowed methods def product_search(request): - """ - Takes a string of data, return relevant products + """ + Takes a string of data, return relevant products - Params: - - q: used for search [MANDATORY] - - limit: max number of returned instances [OPTIONAL] - - offset: where to start counting results [OPTIONAL] - - shipping_cost: true/false - - discount: true/false - - category: string - - tags: string - - order: string (newest/oldest) - - price_min: int - - price_max: int + Params: + - q: used for search [MANDATORY] + - limit: max number of returned instances [OPTIONAL] + - offset: where to start counting results [OPTIONAL] + - shipping_cost: true/false + - discount: true/false + - category: string + - tags: string + - order: string (newest/oldest) + - price_min: int + - price_max: int - In the response: - - filters - - count - - products - - price_min - - price_max + In the response: + - filters + - count + - products + - price_min + - price_max - """ - # capture query params - q = request.GET.get('q', None) - limit = request.GET.get('limit', None) - offset = request.GET.get('offset', None) - shipping_cost = request.GET.get('shipping_cost', None) - if shipping_cost is not None: - if shipping_cost == 'true': - shipping_cost = True - elif shipping_cost == 'false': - shipping_cost = False - else: - shipping_cost = None - discount = request.GET.get('discount', None) - if discount is not None: - if discount == 'true': - discount = True - elif discount == 'false': - discount = False - else: - discount = None - category = request.GET.get('category', None) - tags = request.GET.get('tags', None) - price_min = request.GET.get('price_min', None) - price_max = request.GET.get('price_max', None) - order = request.GET.get('order', '') + """ + # capture query params + q = request.GET.get('q', None) + limit = request.GET.get('limit', None) + offset = request.GET.get('offset', None) + shipping_cost = request.GET.get('shipping_cost', None) + if shipping_cost is not None: + if shipping_cost == 'true': + shipping_cost = True + elif shipping_cost == 'false': + shipping_cost = False + else: + shipping_cost = None + discount = request.GET.get('discount', None) + if discount is not None: + if discount == 'true': + discount = True + elif discount == 'false': + discount = False + else: + discount = None + category = request.GET.get('category', None) + tags = request.GET.get('tags', None) + price_min = request.GET.get('price_min', None) + price_max = request.GET.get('price_max', None) + order = request.GET.get('order', '') - if q is None: - return Response({"errors": {"details": "No query string to parse"}}) - elif q is '': - # return everything - serializer = ProductSerializer(Product.objects.all(), many=True) - products = serializer.data - # filters = extract_search_filters(products) - return Response(data={"filters": [], "count": len(products), "products": products}) - try: - # we collect our results here - result_set = set() - # values for response - prices = { - 'min': None, - 'max': None, - } + if q is None: + return Response({"errors": {"details": "No query string to parse"}}) + try: + # we collect our results here + result_set = set() + # values for response + prices = { + 'min': None, + 'max': None, + } + + if q == '': + # filter entire queryset + products_qs = Product.objects.all() + if tags: + products_qs = Product.objects.filter(tags=tags) + if category: + products_qs = Product.objects.filter(category=category) + # serialize and list data + serializer = ProductSerializer(products_qs, many=True) + result_list = [dict(i) for i in serializer.data] + else: # split query string into single words chunks = q.split(' ') for chunk in chunks: - product_set, min_price, max_price = find_related_products_v6(chunk, shipping_cost, discount, category, tags, price_min, price_max) - # update price values - if product_set: - # import ipdb; ipdb.set_trace() - if prices['min'] is None or min_price['price__min'] < prices['min']: - prices['min'] = min_price['price__min'] - if prices['max'] is None or max_price['price__max'] > prices['max']: - prices['max'] = max_price['price__max'] - # add to result set - result_set.update(product_set) - # TODO: add search for entire phrase ??? + product_set, min_price, max_price = find_related_products_v6(chunk, shipping_cost, discount, category, tags, price_min, price_max) + # update price values + if product_set: + if prices['min'] is None or min_price['price__min'] < prices['min']: + prices['min'] = min_price['price__min'] + if prices['max'] is None or max_price['price__max'] > prices['max']: + prices['max'] = max_price['price__max'] + # add to result set + result_set.update(product_set) + # serialize and list data + serializer = SearchResultSerializer(product_set, many=True) + result_list = [dict(i) for i in serializer.data] - # extract filters from result_set - filters = extract_search_filters(result_set) + # extract filters from result_set + filters = extract_search_filters(result_set) + # order the results + if order == 'newest': + # order results by created + result_list = sorted(result_list, key= lambda x:x['created'], reverse=True) + elif order == 'oldest': + # order results by created + result_list = sorted(result_list, key= lambda x:x['created'], reverse=False) + elif q != '': + # order results by RANK + result_list = sorted(result_list, key= lambda x:x['rank'], reverse=True) - result_list = list(result_set) - if order == 'newest': - # order results by created - ordered_products = sorted(result_list, key= lambda x:x.created, reverse=True) - elif order == 'oldest': - # order results by created - ordered_products = sorted(result_list, key= lambda x:x.created, reverse=False) - else: - # order results by RANK - ordered_products = sorted(result_list, key= lambda rank:rank.rank, reverse=True) - - # extract max and min price values - serializer = SearchResultSerializer(ordered_products, many=True) - product_results = [dict(i) for i in serializer.data] - total_results = len(product_results) - - # RESULTS PAGINATION - if limit is not None and offset is not None: - limit = int(limit) - offset = int(offset) - product_results = product_results[offset:(limit+offset)] - elif limit is not None: - limit = int(limit) - product_results = product_results[:limit] - - return Response(data={"filters": filters, "count": total_results, "products": product_results, 'prices': prices}) - except Exception as e: - return Response({"errors": {"details": str(e)}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + total_results = len(result_list) + # RESULTS PAGINATION + if limit is not None and offset is not None: + limit = int(limit) + offset = int(offset) + result_list = result_list[offset:(limit+offset)] + elif limit is not None: + limit = int(limit) + result_list = result_list[:limit] + return Response(data={"filters": filters, "count": total_results, "products": result_list, 'prices': prices}) + except Exception as e: + return Response({"errors": {"details": str(e)}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)