def get_best_selling_products(context, n_products=12, cutoff_days=30, orderable_only=True): request = context["request"] data = get_best_selling_product_info(shop_ids=[request.shop.pk], cutoff_days=cutoff_days) product_ids = [d[0] for d in data][:n_products] products = [] if orderable_only: # get suppliers for later use suppliers = Supplier.objects.all() for product in Product.objects.filter(id__in=product_ids): shop_product = product.get_shop_instance(request.shop) if orderable_only: for supplier in suppliers: if shop_product.is_orderable( supplier, request.customer, shop_product.minimum_purchase_quantity): products.append(product) break elif shop_product.is_visible(request.customer): products.append(product) products = cache_product_things(request, products) products = sorted( products, key=lambda p: product_ids.index(p.id)) # pragma: no branch return products
def best_selling(self, request): """ Returns the top 20 (default) best selling products. To change the number of products, set the `limit` query param. """ limit = int(parse_decimal_string(request.query_params.get("limit", 20))) best_selling_products = get_best_selling_product_info( shop_ids=[request.shop.pk]) combined_variation_products = defaultdict(int) for product_id, parent_id, qty in best_selling_products: if parent_id: combined_variation_products[parent_id] += qty else: combined_variation_products[product_id] += qty # take here the top `limit` records, because the filter_queryset below can mess with our work product_ids = [ d[0] for d in sorted(six.iteritems(combined_variation_products), key=lambda i: i[1], reverse=True)[:limit] ] products_qs = Product.objects.filter(id__in=product_ids) products_qs = self.filter_queryset(products_qs).distinct() serializer = ProductSerializer(products_qs, many=True, context={"request": request}) return Response(serializer.data)
def _get_best_selling_products(cutoff_days, n_products, orderable_only, request): # noqa (C901) data = get_best_selling_product_info( shop_ids=[request.shop.pk], cutoff_days=cutoff_days ) combined_variation_products = defaultdict(int) for product_id, parent_id, qty in data: if parent_id: combined_variation_products[parent_id] += qty else: combined_variation_products[product_id] += qty product_ids = [ d[0] for d in sorted(six.iteritems(combined_variation_products), key=lambda i: i[1], reverse=True) ][:n_products] products = [] if orderable_only: # get suppliers for later use suppliers = Supplier.objects.filter(shops__in=[request.shop]) for product in Product.objects.filter(id__in=product_ids): try: shop_product = product.get_shop_instance(request.shop, allow_cache=True) except ShopProduct.DoesNotExist: continue if orderable_only: for supplier in suppliers: if shop_product.is_orderable(supplier, request.customer, shop_product.minimum_purchase_quantity): products.append(product) break elif shop_product.is_visible(request.customer): products.append(product) products = cache_product_things(request, products) products = sorted(products, key=lambda p: product_ids.index(p.id)) # pragma: no branch return products
def _get_best_selling_products(cutoff_days, n_products, orderable_only, request, supplier=None): data = get_best_selling_product_info( shop_ids=[request.shop.pk], cutoff_days=cutoff_days, supplier=supplier, orderable_only=orderable_only, quantity=n_products, ) sorted_product_ids = sorted(data, key=lambda item: item[1], reverse=True) product_ids = [item[0] for item in sorted_product_ids] catalog = ProductCatalog( ProductCatalogContext( shop=request.shop, user=getattr(request, "user", None), supplier=supplier, contact=getattr(request, "customer", None), purchasable_only=orderable_only, visibility=ShopProductVisibility.LISTED, ) ) valid_products_qs = ( catalog.get_products_queryset() .filter(id__in=product_ids, mode__in=ProductMode.get_parent_modes()) .distinct()[:n_products] ) products = cache_product_things(request, valid_products_qs) # order products by the best selling order products = sorted(products, key=lambda product: product_ids.index(product.pk)) return products
def get_best_selling_products(context, n_products=12, cutoff_days=30, orderable_only=True): request = context["request"] data = get_best_selling_product_info( shop_ids=[request.shop.pk], cutoff_days=cutoff_days ) combined_variation_products = defaultdict(int) for product_id, parent_id, qty in data: if parent_id: combined_variation_products[parent_id] += qty else: combined_variation_products[product_id] += qty product_ids = [ d[0] for d in sorted(six.iteritems(combined_variation_products), key=lambda i: i[1], reverse=True)][:n_products] products = [] if orderable_only: # get suppliers for later use suppliers = Supplier.objects.all() for product in Product.objects.filter(id__in=product_ids): shop_product = product.get_shop_instance(request.shop) if orderable_only: for supplier in suppliers: if shop_product.is_orderable(supplier, request.customer, shop_product.minimum_purchase_quantity): products.append(product) break elif shop_product.is_visible(request.customer): products.append(product) products = cache_product_things(request, products) products = sorted(products, key=lambda p: product_ids.index(p.id)) # pragma: no branch return products
def _get_best_selling_products(cutoff_days, n_products, orderable_only, request): # noqa (C901) data = get_best_selling_product_info(shop_ids=[request.shop.pk], cutoff_days=cutoff_days) combined_variation_products = defaultdict(int) for product_id, parent_id, qty in data: if parent_id: combined_variation_products[parent_id] += qty else: combined_variation_products[product_id] += qty # get all the product ids product_ids = [ d[0] for d in sorted(six.iteritems(combined_variation_products), key=lambda i: i[1], reverse=True) ] # group product ids in groups of n_products # to prevent querying ALL products at once products = [] for grouped_product_ids in _group_list_items(product_ids, n_products): valid_products_qs = Product.objects.listed( shop=request.shop, customer=request.customer).filter( id__in=grouped_product_ids, shop_products__shop=request.shop, shop_products__suppliers__enabled=True) for product in valid_products_qs.iterator(): products.append(product) if len(products) == n_products: break if len(products) == n_products: break if orderable_only: valid_products = [] suppliers = Supplier.objects.enabled().filter(shops=request.shop) for product in products: # this instance should always exist as the listed() queryset uses the current shop as a filter shop_product = product.get_shop_instance(request.shop, allow_cache=True) for supplier in suppliers: if shop_product.is_orderable( supplier, request.customer, shop_product.minimum_purchase_quantity): valid_products.append(product) break products = valid_products products = cache_product_things(request, products) products = sorted( products, key=lambda p: product_ids.index(p.id)) # pragma: no branch return products
def _get_best_selling_products(cutoff_days, n_products, orderable_only, request, sale_items_only): # noqa (C901) data = get_best_selling_product_info( shop_ids=[request.shop.pk], cutoff_days=cutoff_days ) combined_variation_products = defaultdict(int) for product_id, parent_id, qty in data: if parent_id: combined_variation_products[parent_id] += qty else: combined_variation_products[product_id] += qty # get all the product ids product_ids = [ d[0] for d in sorted(six.iteritems(combined_variation_products), key=lambda i: i[1], reverse=True) ] products = [] suppliers = [] if orderable_only: # get suppliers for later use suppliers = Supplier.objects.enabled().filter(shops__in=[request.shop]) if sale_items_only: from shuup.core.pricing import PricingContext pricing_context = PricingContext(shop=request.shop, customer=request.customer) # group product ids in groups of n_products # to prevent querying ALL products at once for grouped_product_ids in _group_list_items(product_ids, n_products): for product in Product.objects.filter(id__in=grouped_product_ids): if len(products) == n_products: break if sale_items_only and not _is_sale_item(product, pricing_context): continue try: shop_product = product.get_shop_instance(request.shop, allow_cache=True) except ShopProduct.DoesNotExist: continue if orderable_only: for supplier in suppliers: if shop_product.is_orderable(supplier, request.customer, shop_product.minimum_purchase_quantity): products.append(product) break elif shop_product.is_visible(request.customer): products.append(product) if len(products) == n_products: break products = cache_product_things(request, products) products = sorted(products, key=lambda p: product_ids.index(p.id)) # pragma: no branch return products
def _get_best_selling_products(cutoff_days, n_products, orderable_only, request): # noqa (C901) data = get_best_selling_product_info( shop_ids=[request.shop.pk], cutoff_days=cutoff_days ) combined_variation_products = defaultdict(int) for product_id, parent_id, qty in data: if parent_id: combined_variation_products[parent_id] += qty else: combined_variation_products[product_id] += qty # get all the product ids product_ids = [ d[0] for d in sorted(six.iteritems(combined_variation_products), key=lambda i: i[1], reverse=True) ] # group product ids in groups of n_products # to prevent querying ALL products at once products = [] for grouped_product_ids in _group_list_items(product_ids, n_products): valid_products_qs = Product.objects.listed( shop=request.shop, customer=request.customer ).filter( id__in=grouped_product_ids, shop_products__shop=request.shop, shop_products__suppliers__enabled=True ) for product in valid_products_qs.iterator(): products.append(product) if len(products) == n_products: break if len(products) == n_products: break if orderable_only: valid_products = [] suppliers = Supplier.objects.enabled().filter(shops=request.shop) for product in products: # this instance should always exist as the listed() queryset uses the current shop as a filter shop_product = product.get_shop_instance(request.shop, allow_cache=True) for supplier in suppliers: if shop_product.is_orderable(supplier, request.customer, shop_product.minimum_purchase_quantity): valid_products.append(product) break products = valid_products products = cache_product_things(request, products) products = sorted(products, key=lambda p: product_ids.index(p.id)) # pragma: no branch return products
def get_best_selling_products(context, n_products=12, cutoff_days=30, orderable_only=True): request = context["request"] data = get_best_selling_product_info( shop_ids=[request.shop.pk], cutoff_days=cutoff_days ) product_ids = [d[0] for d in data][:n_products] products = [] for product in Product.objects.filter(id__in=product_ids): shop_product = product.get_shop_instance(request.shop) if orderable_only: for supplier in Supplier.objects.all(): if shop_product.is_orderable(supplier, request.customer, shop_product.minimum_purchase_quantity): products.append(product) break elif shop_product.is_visible(request.customer): products.append(product) products = cache_product_things(request, products) products = sorted(products, key=lambda p: product_ids.index(p.id)) # pragma: no branch return products
def best_selling(self, request): """ Returns the top 20 (default) best selling products. To change the number of products, set the `limit` query param. """ limit = int(parse_decimal_string(request.query_params.get("limit", 20))) best_selling_products = get_best_selling_product_info(shop_ids=[request.shop.pk]) combined_variation_products = defaultdict(int) for product_id, parent_id, qty in best_selling_products: if parent_id: combined_variation_products[parent_id] += qty else: combined_variation_products[product_id] += qty # take here the top `limit` records, because the filter_queryset below can mess with our work product_ids = [ d[0] for d in sorted(six.iteritems(combined_variation_products), key=lambda i: i[1], reverse=True)[:limit] ] products_qs = Product.objects.filter(id__in=product_ids) products_qs = self.filter_queryset(products_qs).distinct() serializer = ProductSerializer(products_qs, many=True, context={"request": request}) return Response(serializer.data)