diff --git a/products/views.py b/products/views.py index 41fb76a..adb10b2 100644 --- a/products/views.py +++ b/products/views.py @@ -7,6 +7,7 @@ from django.db.models import Q from django.core import serializers from django.core.validators import validate_email from django.contrib.auth import get_user_model +from django.contrib.gis.geos import Point from django.template.loader import render_to_string from django.core.mail import EmailMessage from django.db.models import Max, Min @@ -176,109 +177,122 @@ def product_search(request): price_min = request.GET.get('price_min', None) price_max = request.GET.get('price_max', None) order = request.GET.get('order', '') + latitude = request.GET.get('latitude', None) + longitude = request.GET.get('longitude', None) - try: - # we collect our results here - result_set = set() - # values for response - prices = { - 'min': None, - 'max': None, - } + # try: + # we collect our results here + result_set = set() + # values for response + prices = { + 'min': None, + 'max': None, + } - if not q: - # filter entire queryset - products_qs = Product.objects.filter(active=True) - # filter by category - if categories is not None: - descendants = [] - for entry in categories: - cat = CategoryTag.objects.filter(label__iexact=entry).first() - # append category tag, and children - descendants.append(cat) - descendants.extend(cat.get_descendants()) + if not q: + # filter entire queryset + products_qs = Product.objects.filter(active=True) + # filter by category + if categories is not None: + descendants = [] + for entry in categories: + cat = CategoryTag.objects.filter(label__iexact=entry).first() + # append category tag, and children + descendants.append(cat) + descendants.extend(cat.get_descendants()) - products_qs = products_qs.filter(category__in=descendants) - # filter by tags - if tags is not None: - products_qs = products_qs.filter(tags__name__icontains=tags) + products_qs = products_qs.filter(category__in=descendants) + # filter by tags + if tags is not None: + products_qs = products_qs.filter(tags__name__icontains=tags) - # filter by shipping cost - if shipping_cost is True: - # only instances with shipping costs - products_qs = products_qs.filter( - Q(shipping_cost__isnull=False)& - Q(shipping_cost__gte=1) - ) - elif shipping_cost is False: - # only intances without shpping costs - products_qs = products_qs.filter(Q(shipping_cost=None)|Q(shipping_cost=0.00)) + # filter by shipping cost + if shipping_cost is True: + # only instances with shipping costs + products_qs = products_qs.filter( + Q(shipping_cost__isnull=False)& + Q(shipping_cost__gte=1) + ) + elif shipping_cost is False: + # only intances without shpping costs + products_qs = products_qs.filter(Q(shipping_cost=None)|Q(shipping_cost=0.00)) - # filter by discount - if discount is True: - # only instances with shipping costs - products_qs = products_qs.filter( - Q(discount__isnull=False)& - Q(discount__gte=1) - ) - elif discount is False: - # only intances without shpping costs - products_qs = products_qs.filter(Q(discount=None)|Q(discount=0.00)) + # filter by discount + if discount is True: + # only instances with shipping costs + products_qs = products_qs.filter( + Q(discount__isnull=False)& + Q(discount__gte=1) + ) + elif discount is False: + # only intances without shpping costs + products_qs = products_qs.filter(Q(discount=None)|Q(discount=0.00)) - # filter by price - if price_min is not None: - products_qs = products_qs.filter(price__gte=price_min) - if price_max is not None: - products_qs = products_qs.filter(price__lte=price_max) + if latitude is not None and longitude is not None: + coordinates = (float(longitude), float(latitude)) + # (n km / 40,000 km * 360 degrees) = radius length degrees + # Radiuses: 10km, 50km, 200km + radiuses = [0.09, 0.45, 1.8] + for radius in reversed(radiuses): + products_geo_filtered = products_qs.filter(company__geo__dwithin=(Point(coordinates), radius)) + if len(products_geo_filtered) >= 10: + products_qs = products_geo_filtered + - # get min_price and max_price - prices['min'] = products_qs.aggregate(Min('price'))['price__min'] - prices['max'] = products_qs.aggregate(Max('price'))['price__max'] - # 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 = ranked_product_search(chunk, shipping_cost, discount, categories, 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] + # filter by price + if price_min is not None: + products_qs = products_qs.filter(price__gte=price_min) + if price_max is not None: + products_qs = products_qs.filter(price__lte=price_max) - # 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) + # get min_price and max_price + prices['min'] = products_qs.aggregate(Min('price'))['price__min'] + prices['max'] = products_qs.aggregate(Max('price'))['price__max'] + # 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 = ranked_product_search(chunk, shipping_cost, discount, categories, 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] - 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) + # 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) + + 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) class CategoryTagAutocomplete(autocomplete.Select2QuerySetView):