import six
from django.conf import settings
from django.core.paginator import Paginator
from django.middleware.csrf import get_token
from django.utils.translation import get_language
from shuup.compat import contextfunction
from shuup.core.catalog import ProductCatalog, ProductCatalogContext
from shuup.core.models import Category, Manufacturer, ProductMode, ShopProductVisibility
from shuup.front.utils.companies import allow_company_registration
from shuup.front.utils.product_statistics import get_best_selling_product_info
from shuup.front.utils.translation import get_language_choices
from shuup.front.utils.user import is_admin_user
from shuup.front.utils.views import cache_product_things
from shuup.utils import django_compat
from shuup.utils.django_compat import reverse
from shuup.utils.importing import cached_load
from shuup.utils.mptt import get_cached_trees
from shuup.utils.translation import cache_translations_for_tree
def _get_listed_products(
context,
n_products,
ordering=None,
filter_dict=None,
orderable_only=True,
extra_filters=None,
):
"""
Returns all products marked as listed that are determined to be
visible based on the current context.
:param context: Rendering context
:type context: jinja2.runtime.Context
:param n_products: Number of products to return
:type n_products: int
:param ordering: String specifying ordering
:type ordering: str
:param filter_dict: Dictionary of filter parameters
:type filter_dict: dict[str, object]
:param orderable_only: Boolean limiting results to orderable products
:type orderable_only: bool
:param extra_filters: Extra filters to be used in Product Queryset
:type extra_filters: django.db.models.Q
:rtype: list[shuup.core.models.Product]
"""
request = context["request"]
customer = getattr(request, "customer", None)
shop = request.shop
catalog = ProductCatalog(
ProductCatalogContext(
shop=shop,
user=getattr(request, "user", None),
contact=customer,
purchasable_only=orderable_only,
visibility=ShopProductVisibility.LISTED,
)
)
if not filter_dict:
filter_dict = {}
products_qs = (
catalog.get_products_queryset()
.language(get_language())
.filter(mode__in=ProductMode.get_parent_modes(), **filter_dict)
)
if extra_filters:
products_qs = products_qs.filter(extra_filters)
if ordering:
products_qs = products_qs.order_by(ordering)
return products_qs.distinct()[:n_products]
[docs]
@contextfunction
def get_listed_products(
context,
n_products,
ordering=None,
filter_dict=None,
orderable_only=True,
extra_filters=None,
):
"""
A cached version of _get_listed_products
"""
request = context["request"]
products = _get_listed_products(
context,
n_products,
ordering=ordering,
filter_dict=filter_dict,
orderable_only=orderable_only,
extra_filters=extra_filters,
)
products = cache_product_things(request, products)
return products
[docs]
@contextfunction
def get_best_selling_products(context, n_products=12, cutoff_days=30, orderable_only=True, supplier=None):
request = context["request"]
products = _get_best_selling_products(cutoff_days, n_products, orderable_only, request, supplier=supplier)
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
[docs]
@contextfunction
def get_newest_products(context, n_products=6, orderable_only=True):
request = context["request"]
products = _get_listed_products(
context,
n_products,
ordering="-pk",
filter_dict={"variation_parent": None},
orderable_only=orderable_only,
)
products = cache_product_things(request, products)
return products
[docs]
@contextfunction
def get_random_products(context, n_products=6, orderable_only=True):
request = context["request"]
products = _get_listed_products(
context,
n_products,
ordering="?",
filter_dict={"variation_parent": None},
orderable_only=orderable_only,
)
products = cache_product_things(request, products)
return products
[docs]
@contextfunction
def get_products_for_categories(context, categories, n_products=6, orderable_only=True):
request = context["request"]
products = _get_listed_products(
context,
n_products,
ordering="?",
filter_dict={
"variation_parent": None,
"shop_products__categories__in": categories,
},
orderable_only=orderable_only,
)
products = cache_product_things(request, products)
return products
[docs]
@contextfunction
def get_all_manufacturers(context, purchasable_only=False):
request = context["request"]
catalog = ProductCatalog(
ProductCatalogContext(
shop=request.shop,
user=getattr(request, "user", None),
contact=getattr(request, "customer", None),
purchasable_only=purchasable_only,
visibility=ShopProductVisibility.LISTED,
)
)
manufacturers = Manufacturer.objects.filter(
pk__in=catalog.get_products_queryset().values_list("manufacturer_id", flat=True).distinct()
)
return manufacturers
[docs]
@contextfunction
def get_root_categories(context):
request = context["request"]
language = get_language()
roots = get_cached_trees(
Category.objects.all_visible(customer=request.customer, shop=request.shop, language=language)
)
cache_translations_for_tree(roots, languages=[language])
return roots
def _get_page_range(current_page, num_pages, range_gap=5):
"""
Get page range around given page for a given number of pages.
>>> list(_get_page_range(1, 10))
[1, 2, 3, 4, 5]
>>> list(_get_page_range(3, 10))
[1, 2, 3, 4, 5]
>>> list(_get_page_range(4, 10))
[2, 3, 4, 5, 6]
>>> list(_get_page_range(7, 10))
[5, 6, 7, 8, 9]
>>> list(_get_page_range(10, 10))
[6, 7, 8, 9, 10]
>>> list(_get_page_range(1, 1))
[1]
>>> list(_get_page_range(1, 4))
[1, 2, 3, 4]
>>> list(_get_page_range(3, 4))
[1, 2, 3, 4]
>>> list(_get_page_range(4, 4))
[1, 2, 3, 4]
"""
assert isinstance(num_pages, int)
assert isinstance(current_page, int)
assert num_pages >= 1
assert current_page >= 1
assert current_page <= num_pages
max_start = max(num_pages - range_gap + 1, 1)
start = min(max(current_page - (range_gap // 2), 1), max_start)
end = min(start + range_gap - 1, num_pages)
return six.moves.range(start, end + 1)
[docs]
@contextfunction
def get_shop_language_choices(context):
request = context["request"]
return get_language_choices(request.shop)
[docs]
@contextfunction
def is_shop_admin(context):
return is_admin_user(context["request"])
[docs]
@contextfunction
def is_company_registration_allowed(context, request=None):
current_request = request or context["request"] # From macros it doesn't seem to always pass context correctly
return allow_company_registration(current_request.shop)
[docs]
@contextfunction
def can_toggle_all_seeing(context):
request = context["request"]
if request.customer.is_anonymous or request.is_company_member:
# Looks like the user is currently forcing anonymous or company
# mode which means that the visibility limit can't be used since
# 'is all seeing' is purely person contact feature.
return False
return getattr(request.user, "is_superuser", False)
[docs]
@contextfunction
def get_admin_edit_url(context, intance_or_model):
from shuup.admin.template_helpers.shuup_admin import model_url
url = model_url(context, intance_or_model)
if url:
return {
"url": url,
"name": intance_or_model._meta.verbose_name.title(),
}
[docs]
@contextfunction
def get_powered_by_content(context):
return settings.SHUUP_FRONT_POWERED_BY_CONTENT
[docs]
@contextfunction
def get_config(context):
request = context["request"]
is_authenticated = request.user.is_authenticated
return {
"uploadUrl": (reverse("shuup:media-upload") if is_authenticated else None),
"csrf": get_token(request),
}
[docs]
def is_authenticated(user):
return django_compat.is_authenticated(user)