Beispiel #1
0
def inject_global_snippet(context, content):
    if not valid_view(context):
        return

    from shuup.xtheme import get_current_theme
    from shuup.xtheme.models import Snippet, SnippetType
    shop = get_shop(context["request"])

    cache_key = GLOBAL_SNIPPETS_CACHE_KEY.format(shop_id=shop.id)
    snippets = cache.get(cache_key)

    if snippets is None:
        snippets = Snippet.objects.filter(shop=shop)
        cache.set(cache_key, snippets)

    for snippet in snippets:
        if snippet.themes:
            current_theme = get_current_theme(shop)
            if current_theme and current_theme.identifier not in snippet.themes:
                continue

        content = snippet.snippet
        if snippet.snippet_type == SnippetType.InlineJS:
            content = InlineScriptResource(content)
        elif snippet.snippet_type == SnippetType.InlineCSS:
            content = InlineStyleResource(content)
        elif snippet.snippet_type == SnippetType.InlineHTMLMarkup:
            content = InlineMarkupResource(content)

        add_resource(context, snippet.location, content)
Beispiel #2
0
    def render(self, context, cache_key_prefix=None):
        """
        Return the plugin's rendered content.

        :param context: Jinja2 rendering context.
        :type context: jinja2.runtime.Context
        :return: string of content.
        :rtype: str
        """
        if not self.plugin_identifier:
            return ""  # Null!
        plugin_inst = self.instantiate_plugin()
        if plugin_inst is None:
            return mark_safe("<!-- %s? -->" % self.plugin_identifier)

        if plugin_inst.is_context_valid(context=context):
            # check whether the plugin can be cached
            cacheabled = getattr(plugin_inst, "cacheable", False)
            cache_key = plugin_inst.get_cache_key() if hasattr(
                plugin_inst, "get_cache_key") else plugin_inst.identifier
            full_cache_key = "shuup_xtheme_cell:{}".format(
                hash(f"{cache_key_prefix}-{cache_key}"))
            cached_content = cache.get(full_cache_key)
            if cached_content is not None:
                return cached_content

            content = plugin_inst.render(context=context)
            if cacheabled:
                cache.set(full_cache_key, content)
            return content
        else:
            return ""
Beispiel #3
0
def thumbnail(source, alias=None, generate=True, **kwargs):
    if not source:
        return None

    cache_key, cached_thumbnail_url = _get_cached_thumbnail_url(
        source, alias=alias, generate=generate, **kwargs)

    if cached_thumbnail_url is not None:
        return cached_thumbnail_url

    thumbnailer_instance = get_thumbnailer(source)

    if not thumbnailer_instance:
        return None

    if _is_svg(thumbnailer_instance):
        return source.url if hasattr(source, 'url') else None

    if alias:
        options = aliases.get(alias, target=thumbnailer_instance.alias_target)
        options.update(process_thumbnailer_options(kwargs))
    else:
        options = process_thumbnailer_options(kwargs)

    try:
        thumbnail_instance = thumbnailer_instance.get_thumbnail(
            options, generate=generate)
        thumbnail_url = thumbnail_instance.url
        if cache_key:
            cache.set(cache_key, thumbnail_url)
        return thumbnail_url
    except (IOError, InvalidImageFormatError, ValueError):
        return None
Beispiel #4
0
def inject_global_snippet(context, content):
    if not valid_view(context):
        return

    from shuup.xtheme import get_current_theme
    from shuup.xtheme.models import Snippet, SnippetType
    shop = get_shop(context["request"])

    cache_key = GLOBAL_SNIPPETS_CACHE_KEY.format(shop_id=shop.id)
    snippets = cache.get(cache_key)

    if snippets is None:
        snippets = Snippet.objects.filter(shop=shop)
        cache.set(cache_key, snippets)

    for snippet in snippets:
        if snippet.themes:
            current_theme = get_current_theme(shop)
            if current_theme and current_theme.identifier not in snippet.themes:
                continue

        content = snippet.snippet
        if snippet.snippet_type == SnippetType.InlineJS:
            content = InlineScriptResource(content)
        elif snippet.snippet_type == SnippetType.InlineCSS:
            content = InlineStyleResource(content)
        elif snippet.snippet_type == SnippetType.InlineHTMLMarkup:
            content = InlineMarkupResource(content)
        elif snippet.snippet_type == SnippetType.InlineJinjaHTMLMarkup:
            context = dict(context.items())
            # prevent recursive injection
            context["allow_resource_injection"] = False
            content = JinjaMarkupResource(content, context)

        add_resource(context, snippet.location, content)
    def get_fields(self, request, category=None):
        if not category:
            return

        cache_key = "productvariationfilter:%s" % category.pk
        cached_fields = cache.get(cache_key)
        if cached_fields:
            return cached_fields

        variation_values = defaultdict(set)
        for variation in ProductVariationVariable.objects.filter(product__shop_products__categories=category):
            for value in variation.values.all():
                variation_values[slugify(variation.name)].add(value.value)

        fields = []
        for variation_key, variation_values in six.iteritems(variation_values):
            choices = [(value.lower(), value) for value in variation_values]

            fields.append((
                "variation_%s" % variation_key,
                forms.MultipleChoiceField(
                    choices=choices, required=False, label=capfirst(variation_key), widget=FilterWidget())
            ))
        cache.set(cache_key, fields, 60 * 15)
        return fields
Beispiel #6
0
def get_current_theme(request=None):
    """
    Get the currently active theme object.

    :param request: Request, if available
    :type request: HttpRequest|None
    :return: Theme object or None
    :rtype: Theme
    """

    if _current_theme_class is not _not_set:
        if _current_theme_class:
            return _current_theme_class()
        return None  # No theme (usually for testing)

    value = cache.get(THEME_CACHE_KEY)
    if value:
        return value

    if request and hasattr(request, "_current_xtheme"):
        return request._current_xtheme

    theme = _get_current_theme()

    if request:
        request._current_xtheme = theme

    cache.set(THEME_CACHE_KEY, theme)
    return theme
Beispiel #7
0
def thumbnail(source, alias=None, generate=True, **kwargs):
    if not source:
        return None

    cache_key, cached_thumbnail_url = _get_cached_thumbnail_url(source, alias=alias, generate=generate, **kwargs)

    if cached_thumbnail_url is not None:
        return cached_thumbnail_url

    thumbnailer_instance = get_thumbnailer(source)

    if not thumbnailer_instance:
        return None

    if _is_svg(thumbnailer_instance):
        return source.url if hasattr(source, 'url') else None

    if alias:
        options = aliases.get(alias, target=thumbnailer_instance.alias_target)
        options.update(process_thumbnailer_options(kwargs))
    else:
        options = process_thumbnailer_options(kwargs)

    try:
        thumbnail_instance = thumbnailer_instance.get_thumbnail(options, generate=generate)
        thumbnail_url = thumbnail_instance.url
        if cache_key:
            cache.set(cache_key, thumbnail_url)
        return thumbnail_url
    except (IOError, InvalidImageFormatError, ValueError):
        return None
Beispiel #8
0
    def get_fields(self, request, category=None):
        if not category:
            return

        cache_key = "productvariationfilter:%s" % category.pk
        cached_fields = cache.get(cache_key)
        if cached_fields:
            return cached_fields

        variation_values = defaultdict(set)
        for variation in ProductVariationVariable.objects.filter(product__shop_products__categories=category):
            for value in variation.values.all():
                variation_values[slugify(variation.name)].add(value.value)

        fields = []
        for variation_key, variation_values in six.iteritems(variation_values):
            choices = [(value.lower(), value) for value in variation_values]

            fields.append((
                "variation_%s" % variation_key,
                forms.MultipleChoiceField(
                    choices=choices, required=False, label=capfirst(variation_key), widget=FilterWidget())
            ))
        cache.set(cache_key, fields, 60 * 15)
        return fields
Beispiel #9
0
def get_current_theme(request=None):
    """
    Get the currently active theme object.

    :param request: Request, if available
    :type request: HttpRequest|None
    :return: Theme object or None
    :rtype: Theme
    """

    if _current_theme_class is not _not_set:
        if _current_theme_class:
            return _current_theme_class()
        return None  # No theme (usually for testing)

    value = cache.get(THEME_CACHE_KEY)
    if value:
        return value

    if request and hasattr(request, "_current_xtheme"):
        return request._current_xtheme

    theme = _get_current_theme()

    if request:
        request._current_xtheme = theme

    cache.set(THEME_CACHE_KEY, theme)
    return theme
Beispiel #10
0
 def get_cached_children(self):
     from shuup.core import cache
     key = "category_cached_children:{}".format(self.pk)
     children = cache.get(key)
     if children is not None:
         return children
     children = self.get_children()
     cache.set(key, children)
     return children
Beispiel #11
0
def test_cache_api():
    key = "test_prefix:123"
    value = "456"
    cache.set(key, value)
    assert cache.get(key) == value
    cache.bump_version(key)
    assert cache.get(key, default="derp") == "derp"  # version was bumped, so no way this is there
    cache.set(key, value)
    assert cache.get(key) == value
Beispiel #12
0
 def get_cached_children(self):
     from shuup.core import cache
     key = "category_cached_children:{}".format(self.pk)
     children = cache.get(key)
     if children is not None:
         return children
     children = self.get_children()
     cache.set(key, children)
     return children
Beispiel #13
0
def get_display_unit(sales_unit):
    cache_key = "display_unit:sales_unit_{}_default_display_unit".format(sales_unit.pk)
    default_display_unit = cache.get(cache_key)
    if default_display_unit is None:
        default_display_unit = sales_unit.display_units.filter(default=True).first()
        # Set 0 to cache to prevent None values, which will not be a valid cache value
        # 0 will be invalid below, hence we prevent another query here
        cache.set(cache_key, default_display_unit or 0)
    return default_display_unit
Beispiel #14
0
def test_alert_limit_notification(rf, admin_user):
    with override_settings(
            CACHES={
                'default': {
                    'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
                    'LOCATION': 'test_configuration_cache',
                }
            }):
        cache.init_cache()

        supplier = get_simple_supplier()
        shop = get_default_shop()
        product = create_product("simple-test-product", shop, supplier)
        product.stock_behavior = StockBehavior.STOCKED
        product.save()

        sc = StockCount.objects.get(supplier=supplier, product=product)
        sc.alert_limit = 10
        sc.save()

        # nothing in cache
        cache_key = AlertLimitReached.cache_key_fmt % (supplier.pk, product.pk)
        assert cache.get(cache_key) is None

        # put 11 units in stock
        supplier.adjust_stock(product.pk, +11)

        # still nothing in cache
        cache_key = AlertLimitReached.cache_key_fmt % (supplier.pk, product.pk)
        assert cache.get(cache_key) is None

        event = AlertLimitReached(product=product, supplier=supplier)
        assert event.variable_values["dispatched_last_24hs"] is False

        # stock should be 6, lower then the alert limit
        supplier.adjust_stock(product.pk, -5)
        last_run = cache.get(cache_key)
        assert last_run is not None

        event = AlertLimitReached(product=product, supplier=supplier)
        assert event.variable_values["dispatched_last_24hs"] is True

        # stock should be 1, lower then the alert limit
        supplier.adjust_stock(product.pk, -5)

        # last run should be updated
        assert cache.get(cache_key) != last_run

        event = AlertLimitReached(product=product, supplier=supplier)
        assert event.variable_values["dispatched_last_24hs"] is True

        # fake we have a cache with more than 24hrs
        cache.set(cache_key, time() - (24 * 60 * 60 * 2))

        event = AlertLimitReached(product=product, supplier=supplier)
        assert event.variable_values["dispatched_last_24hs"] is False
Beispiel #15
0
    def run(self, shop):
        cache_key = self.cache_key_fmt % (self.variable_values["supplier"].pk, self.variable_values["product"].pk)

        # do not run this if the last dispatch was < 1 minute
        last_dispatch_time = cache.get(cache_key)
        if last_dispatch_time and time() - last_dispatch_time < 60:
            return

        cache.set(cache_key, time(), timeout=(60 * 60 * 24))
        super(AlertLimitReached, self).run(shop=shop)
Beispiel #16
0
    def run(self, shop):
        cache_key = self.cache_key_fmt % (self.variable_values["supplier"].pk, self.variable_values["product"].pk)

        # do not run this if the last dispatch was < 1 minute
        last_dispatch_time = cache.get(cache_key)
        if last_dispatch_time and time() - last_dispatch_time < 60:
            return

        cache.set(cache_key, time(), timeout=(60 * 60 * 24))
        super(AlertLimitReached, self).run(shop=shop)
Beispiel #17
0
def set_cached_value(key, value, timeout=None):
    """
    Set value to context cache

    :param key: Unique key formed to the context
    :param value: Value to cache
    :param timeout: Timeout as seconds
    :type timeout: int
    """
    cache.set(key, value, timeout=timeout)
Beispiel #18
0
def set_cached_value(key, value, timeout=None):
    """
    Set value to context cache

    :param key: Unique key formed to the context
    :param value: Value to cache
    :param timeout: Timeout as seconds
    :type timeout: int
    """
    cache.set(key, value, timeout=timeout)
Beispiel #19
0
def get_search_product_ids(request, query):
    query = query.strip().lower()
    cache_key = "simple_search:%s" % hashlib.sha1(force_bytes(query)).hexdigest()
    product_ids = cache.get(cache_key)
    if product_ids is None:
        entry_query = get_compiled_query(
            query, ['translations__name', 'translations__description', 'translations__keywords'])
        product_ids = Product.objects.filter(entry_query).distinct().values_list("pk", flat=True)
        cache.set(cache_key, product_ids, 60 * 5)
    return product_ids
Beispiel #20
0
def get_matching_catalog_filters(shop_product):
    namespace = CATALOG_FILTER_CACHE_NAMESPACE
    catalog_filters_cache_key = "%s:%s" % (namespace, shop_product.pk)
    matching_catalog_filters = cache.get(catalog_filters_cache_key, None)
    if matching_catalog_filters is None:
        matching_catalog_filters = set()
        for filter in CatalogFilter.objects.all():
            if filter.matches(shop_product):
                matching_catalog_filters.add(filter.pk)
        cache.set(catalog_filters_cache_key, matching_catalog_filters, timeout=None)
    return matching_catalog_filters
Beispiel #21
0
def get_matching_catalog_filters(shop_product):
    namespace = CATALOG_FILTER_CACHE_NAMESPACE
    catalog_filters_cache_key = "%s:%s" % (namespace, shop_product.pk)
    matching_catalog_filters = cache.get(catalog_filters_cache_key, None)
    if matching_catalog_filters is None:
        matching_catalog_filters = set()
        for filter in CatalogFilter.objects.all():
            if filter.matches(shop_product):
                matching_catalog_filters.add(filter.pk)
        cache.set(catalog_filters_cache_key, matching_catalog_filters, timeout=None)
    return matching_catalog_filters
Beispiel #22
0
 def _get_resource(self, request, resource_id):
     cache_key = "SHUUPCOM_API_%s_%s" % (request.LANGUAGE_CODE, resource_id)
     resource = cache.get(cache_key)
     if not resource:
         try:
             r = requests.get("https://www.shuup.com/%s/api/%s/" % (request.LANGUAGE_CODE, resource_id))
             resource = r.json()
             cache.set(cache_key, resource, timeout=SECONDS_IN_DAY)
         except Exception:
             pass
     return resource or {}
Beispiel #23
0
    def render(self):
        """
        Get this placeholder's rendered contents.

        :return: Rendered markup.
        :rtype: markupsafe.Markup
        """
        language = get_language()

        if not may_inject(self.context):
            return ""

        full_content = ""
        saved_view_config = self.view_config.saved_view_config
        for layout in self.layouts:
            cache_key = slugify(
                "shuup_xtheme_placeholders:placeholder_%s_%s_%s_%s_%s_%s" % (
                    (saved_view_config.pk if saved_view_config else ""),
                    (saved_view_config.status if saved_view_config else ""),
                    (
                        saved_view_config.modified_on.isoformat()
                        if saved_view_config and saved_view_config.modified_on
                        else ""
                    ),
                    language,
                    self.placeholder_name,
                    get_layout_data_key(self.placeholder_name, layout, self.context)
                )
            )
            layout_content = cache.get(cache_key)
            if (
                settings.SHUUP_XTHEME_USE_PLACEHOLDER_CACHE and
                saved_view_config and
                saved_view_config.status == SavedViewConfigStatus.PUBLIC and
                layout_content
            ):
                full_content += layout_content
            else:
                wrapper_start = "<div%s>" % get_html_attrs(self._get_wrapper_attrs(layout))
                buffer = []
                write = buffer.append
                self._render_layout(write, layout)
                content = "".join(buffer)
                layout_content = (
                    "%(wrapper_start)s%(content)s%(wrapper_end)s" % {
                        "wrapper_start": wrapper_start,
                        "content": content,
                        "wrapper_end": "</div>",
                    })
                cache.set(cache_key, layout_content)
                full_content += layout_content

        return Markup('<div class="placeholder-edit-wrap">%s</div>' % full_content)
Beispiel #24
0
def get_search_product_ids(request, query, limit=150):
    query = query.strip().lower()
    cache_key = "simple_search:%s" % hashlib.sha1(force_bytes(query)).hexdigest()
    product_ids = cache.get(cache_key)
    if product_ids is None:
        entry_query = get_compiled_query(
            query, ['sku', 'translations__name', 'translations__description', 'translations__keywords'])
        product_ids = list(
            Product.objects.searchable(shop=request.shop).filter(entry_query).distinct().values_list("pk", flat=True)
        )[:limit]
        cache.set(cache_key, product_ids, 60 * 5)
    return product_ids
Beispiel #25
0
def get_search_product_ids(request, query):
    query = query.strip().lower()
    cache_key = "simple_search:%s" % hashlib.sha1(force_bytes(query)).hexdigest()
    product_ids = cache.get(cache_key)
    if product_ids is None:
        product_ids = Product.objects.filter(
            Q(translations__name__icontains=query) |
            Q(translations__description__icontains=query) |
            Q(translations__keywords__icontains=query)
        ).distinct().values_list("pk", flat=True)
        cache.set(cache_key, product_ids, 60 * 5)
    return product_ids
Beispiel #26
0
def get_search_product_ids(request, query):
    query = query.strip().lower()
    cache_key = "simple_search:%s" % hashlib.sha1(
        force_bytes(query)).hexdigest()
    product_ids = cache.get(cache_key)
    if product_ids is None:
        product_ids = Product.objects.filter(
            Q(translations__name__icontains=query)
            | Q(translations__description__icontains=query)
            | Q(translations__keywords__icontains=query)).distinct(
            ).values_list("pk", flat=True)
        cache.set(cache_key, product_ids, 60 * 5)
    return product_ids
Beispiel #27
0
def inject_global_snippet(context, content):  # noqa: C901
    if not valid_view(context):
        return

    from shuup.xtheme import get_current_theme
    from shuup.xtheme.models import Snippet, SnippetType

    request = context["request"]
    shop = getattr(request, "shop", None) or get_shop(context["request"])

    cache_key = GLOBAL_SNIPPETS_CACHE_KEY.format(shop_id=shop.id)
    snippets = cache.get(cache_key)

    if snippets is None:
        snippets = Snippet.objects.filter(shop=shop)
        cache.set(cache_key, snippets)

    for snippet in snippets:
        if snippet.themes:
            current_theme = getattr(request, "theme",
                                    None) or get_current_theme(shop)
            if current_theme and current_theme.identifier not in snippet.themes:
                continue

        snippet_blockers = get_provide_objects(
            "xtheme_snippet_blocker")  # type: Iterable[SnippetBlocker]
        blocked = False

        for snippet_blocker in snippet_blockers:
            if snippet_blocker.should_block_global_snippet_injection(
                    snippet, context):
                blocked = True
                break

        if blocked:
            continue

        content = snippet.snippet
        if snippet.snippet_type == SnippetType.InlineJS:
            content = InlineScriptResource(content)
        elif snippet.snippet_type == SnippetType.InlineCSS:
            content = InlineStyleResource(content)
        elif snippet.snippet_type == SnippetType.InlineHTMLMarkup:
            content = InlineMarkupResource(content)
        elif snippet.snippet_type == SnippetType.InlineJinjaHTMLMarkup:
            context = dict(context.items())
            # prevent recursive injection
            context["allow_resource_injection"] = False
            content = JinjaMarkupResource(content, context)

        add_resource(context, snippet.location, content)
Beispiel #28
0
def get_best_selling_product_info(shop_ids, cutoff_days=30):
    shop_ids = sorted(map(int, shop_ids))
    cutoff_date = datetime.date.today() - datetime.timedelta(days=cutoff_days)
    cache_key = "best_sellers:%r_%s" % (shop_ids, cutoff_date)
    sales_data = cache.get(cache_key)
    if sales_data is None:
        sales_data = (OrderLine.objects.filter(
            order__shop_id__in=shop_ids,
            order__order_date__gte=to_aware(cutoff_date),
            type=OrderLineType.PRODUCT).values("product").annotate(
                n=Sum("quantity")).order_by("-n")[:100].values_list(
                    "product", "product__variation_parent_id", "n"))
        cache.set(cache_key, sales_data, 3 * 60 * 60)  # three hours
    return sales_data
Beispiel #29
0
def get_matching_context_conditions(context):
    namespace = CONTEXT_CONDITION_CACHE_NAMESPACE
    ctx_cache_elements = dict(
        customer=context.customer.pk or 0,
        shop=context.shop.pk)
    conditions_cache_key = "%s:%s" % (namespace, hash(frozenset(ctx_cache_elements.items())))
    matching_context_conditions = cache.get(conditions_cache_key, None)
    if matching_context_conditions is None:
        matching_context_conditions = set()
        for condition in ContextCondition.objects.all():
            if condition.matches(context):
                matching_context_conditions.add(condition.pk)
        cache.set(conditions_cache_key, matching_context_conditions, timeout=None)
    return matching_context_conditions
Beispiel #30
0
def get_matching_context_conditions(context):
    namespace = CONTEXT_CONDITION_CACHE_NAMESPACE
    ctx_cache_elements = dict(
        customer=context.customer.pk or 0,
        shop=context.shop.pk)
    conditions_cache_key = "%s:%s" % (namespace, hash(frozenset(ctx_cache_elements.items())))
    matching_context_conditions = cache.get(conditions_cache_key, None)
    if matching_context_conditions is None:
        matching_context_conditions = set()
        for condition in ContextCondition.objects.all():
            if condition.matches(context):
                matching_context_conditions.add(condition.pk)
        cache.set(conditions_cache_key, matching_context_conditions, timeout=None)
    return matching_context_conditions
Beispiel #31
0
    def get_matching(cls, context, shop_product):
        prod_ctx_cache_elements = dict(customer=context.customer.pk or 0,
                                       shop=context.shop.pk,
                                       product_id=shop_product.pk)
        namespace = CAMPAIGNS_CACHE_NAMESPACE
        sorted_items = dict(
            sorted(prod_ctx_cache_elements.items(), key=lambda item: item[0]))
        key = "%s:%s" % (namespace,
                         hashlib.sha1(
                             str(sorted_items).encode("utf-8")).hexdigest())
        cached_matching = cache.get(key, None)
        if cached_matching is not None:
            return cached_matching

        from shuup.campaigns.models.matching import get_matching_catalog_filters, get_matching_context_conditions

        matching_context_conditions = get_matching_context_conditions(context)
        matching_catalog_filters = get_matching_catalog_filters(shop_product)

        if not (matching_context_conditions or matching_catalog_filters):
            return []

        # Get all possible campaign id's for matching context_conditions
        campaigns_based_on_conditions = set(
            cls.objects.filter(
                active=True,
                shop=context.shop,
                conditions__id__in=matching_context_conditions).values_list(
                    "pk", flat=True))

        campaigns_based_on_catalog_filters = set()
        if hasattr(cls, "filters"):
            # Get all possible campaigns for matching catalog_filters
            campaigns_based_on_catalog_filters = set(
                cls.objects.filter(
                    active=True,
                    shop=context.shop,
                    filters__id__in=matching_catalog_filters).values_list(
                        "pk", flat=True))

        all_possible_campaigns_ids = campaigns_based_on_conditions | campaigns_based_on_catalog_filters
        matching = []
        for campaign in cls.objects.filter(id__in=all_possible_campaigns_ids):
            if campaign.rules_match(context, shop_product,
                                    matching_catalog_filters,
                                    matching_context_conditions):
                matching.append(campaign)
        cache.set(key, matching, timeout=None)
        return matching
Beispiel #32
0
def get_products_ordered_with(prod, count=20, request=None, language=None):
    cache_key = "ordered_with:%d" % prod.pk
    product_ids = cache.get(cache_key)
    if product_ids is None:
        # XXX: could this be optimized more? (and does it matter?)
        order_ids = (OrderLine.objects.filter(
            product=prod, type=OrderLineType.PRODUCT).values_list("order__id",
                                                                  flat=True))
        product_ids = (OrderLine.objects.filter(
            order_id__in=order_ids).exclude(
                product=prod).distinct().values_list("product", flat=True))
        cache.set(cache_key, set(product_ids), 4 * 60 * 60)
    return (Product.objects.all_visible(
        request,
        language=language).filter(id__in=product_ids).order_by("?")[:count])
Beispiel #33
0
def set_current_theme(identifier):
    """
    Activate a theme based on identifier.

    :param identifier: Theme identifier
    :type identifier: str
    :return: Activated theme
    :rtype: Theme
    """
    cache.bump_version(THEME_CACHE_KEY)
    theme = get_theme_by_identifier(identifier)
    if not theme:
        raise ValueError("Invalid theme identifier")
    theme.set_current()
    cache.set(THEME_CACHE_KEY, theme)
    return theme
Beispiel #34
0
def get_products_ordered_with(prod, count=20, request=None, language=None):
    cache_key = "ordered_with:%d" % prod.pk
    product_ids = cache.get(cache_key)
    if product_ids is None:
        # XXX: could this be optimized more? (and does it matter?)
        order_ids = OrderLine.objects.filter(product=prod, type=OrderLineType.PRODUCT).values_list(
            "order__id", flat=True
        )
        product_ids = (
            OrderLine.objects.filter(order_id__in=order_ids)
            .exclude(product=prod)
            .distinct()
            .values_list("product", flat=True)
        )
        cache.set(cache_key, set(product_ids), 4 * 60 * 60)
    return Product.objects.all_visible(request, language=language).filter(id__in=product_ids).order_by("?")[:count]
Beispiel #35
0
def get_best_selling_product_info(shop_ids, cutoff_days=30):
    shop_ids = sorted(map(int, shop_ids))
    cutoff_date = datetime.date.today() - datetime.timedelta(days=cutoff_days)
    cache_key = "best_sellers:%r_%s" % (shop_ids, cutoff_date)
    sales_data = cache.get(cache_key)
    if sales_data is None:
        sales_data = (
            OrderLine.objects.filter(order__shop_id__in=shop_ids, order__order_date__gte=cutoff_date)
            .exclude(product=None)
            .values("product")
            .annotate(n=Sum("quantity"))
            .order_by("-n")[:100]
            .values_list("product", "n")
        )
        cache.set(cache_key, sales_data, 3 * 60 * 60)  # three hours
    return sales_data
Beispiel #36
0
def set_current_theme(identifier):
    """
    Activate a theme based on identifier.

    :param identifier: Theme identifier
    :type identifier: str
    :return: Activated theme
    :rtype: Theme
    """
    cache.bump_version(THEME_CACHE_KEY)
    theme = get_theme_by_identifier(identifier)
    if not theme:
        raise ValueError("Invalid theme identifier")
    theme.set_current()
    cache.set(THEME_CACHE_KEY, theme)
    return theme
Beispiel #37
0
def test_alert_limit_notification(rf, admin_user):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    product = create_product("simple-test-product", shop, supplier)
    product.stock_behavior = StockBehavior.STOCKED
    product.save()

    sc = StockCount.objects.get(supplier=supplier, product=product)
    sc.alert_limit = 10
    sc.save()

    # nothing in cache
    cache_key = AlertLimitReached.cache_key_fmt % (supplier.pk, product.pk)
    assert cache.get(cache_key) is None

    # put 11 units in stock
    supplier.adjust_stock(product.pk, +11)

    # still nothing in cache
    cache_key = AlertLimitReached.cache_key_fmt % (supplier.pk, product.pk)
    assert cache.get(cache_key) is None

    event = AlertLimitReached(product=product, supplier=supplier)
    assert event.variable_values["dispatched_last_24hs"] == "False"

    # stock should be 6, lower then the alert limit
    supplier.adjust_stock(product.pk, -5)
    last_run = cache.get(cache_key)
    assert last_run is not None

    event = AlertLimitReached(product=product, supplier=supplier)
    assert event.variable_values["dispatched_last_24hs"] == "True"

    # stock should be 1, lower then the alert limit
    supplier.adjust_stock(product.pk, -5)

    # last run should be updated
    assert cache.get(cache_key) != last_run

    event = AlertLimitReached(product=product, supplier=supplier)
    assert event.variable_values["dispatched_last_24hs"] == "True"

    # fake we have a cache with more than 24hrs
    cache.set(cache_key, time() - (24 * 60 * 60 * 2))

    event = AlertLimitReached(product=product, supplier=supplier)
    assert event.variable_values["dispatched_last_24hs"] == "False"
Beispiel #38
0
def test_cache_api():
    with override_settings(CACHES={
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'test_configuration_cache',
        }
    }):
        cache.init_cache()

        key = "test_prefix:123"
        value = "456"
        cache.set(key, value)
        assert cache.get(key) == value
        cache.bump_version(key)
        assert cache.get(key, default="derp") == "derp"  # version was bumped, so no way this is there
        cache.set(key, value)
        assert cache.get(key) == value
Beispiel #39
0
def test_cache_api():
    with override_settings(CACHES={
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'test_configuration_cache',
        }
    }):
        cache.init_cache()

        key = "test_prefix:123"
        value = "456"
        cache.set(key, value)
        assert cache.get(key) == value
        cache.bump_version(key)
        assert cache.get(key, default="derp") == "derp"  # version was bumped, so no way this is there
        cache.set(key, value)
        assert cache.get(key) == value
Beispiel #40
0
    def get_matching(cls, context, shop_product):
        prod_ctx_cache_elements = dict(
            customer=context.customer.pk or 0,
            shop=context.shop.pk,
            product_id=shop_product.pk)
        namespace = CAMPAIGNS_CACHE_NAMESPACE
        key = "%s:%s" % (namespace, hash(frozenset(prod_ctx_cache_elements.items())))
        cached_matching = cache.get(key, None)
        if cached_matching is not None:
            return cached_matching

        from shuup.campaigns.models.matching import get_matching_context_conditions, get_matching_catalog_filters
        matching_context_conditions = get_matching_context_conditions(context)
        matching_catalog_filters = get_matching_catalog_filters(shop_product)

        if not (matching_context_conditions or matching_catalog_filters):
            return []

        # Get all possible campaign id's for matching context_conditions
        campaigns_based_on_conditions = set(
            cls.objects.filter(
                active=True,
                shop=context.shop,
                conditions__id__in=matching_context_conditions
            ).values_list("pk", flat=True)
        )

        campaigns_based_on_catalog_filters = set()
        if hasattr(cls, "filters"):
            # Get all possible campaigns for matching catalog_filters
            campaigns_based_on_catalog_filters = set(
                cls.objects.filter(
                    active=True,
                    shop=context.shop,
                    filters__id__in=matching_catalog_filters
                ).values_list("pk", flat=True)
            )

        all_possible_campaigns_ids = (campaigns_based_on_conditions | campaigns_based_on_catalog_filters)
        matching = []
        for campaign in cls.objects.filter(id__in=all_possible_campaigns_ids):
            if campaign.rules_match(context, shop_product, matching_catalog_filters, matching_context_conditions):
                matching.append(campaign)
        cache.set(key, matching, timeout=None)
        return matching
Beispiel #41
0
def get_matching_context_conditions(context):
    namespace = CONTEXT_CONDITION_CACHE_NAMESPACE
    ctx_cache_elements = dict(customer=context.customer.pk or 0,
                              shop=context.shop.pk)
    sorted_items = dict(
        sorted(ctx_cache_elements.items(), key=lambda item: item[0]))
    conditions_cache_key = "%s:%s" % (
        namespace, hashlib.sha1(str(sorted_items).encode("utf-8")).hexdigest())
    matching_context_conditions = cache.get(conditions_cache_key, None)
    if matching_context_conditions is None:
        matching_context_conditions = set()
        for condition in ContextCondition.objects.filter(active=True):
            if condition.matches(context):
                matching_context_conditions.add(condition.pk)
        cache.set(conditions_cache_key,
                  matching_context_conditions,
                  timeout=None)
    return matching_context_conditions
Beispiel #42
0
    def render_product_review_rating(self, product):
        if not has_installed("shuup_product_reviews"):
            return ""

        cache_key = "_product_review_rendered_rating_%d" % product.pk
        cached_rating = cache.get(cache_key)

        if cached_rating:
            return cached_rating

        from shuup_product_reviews.utils import render_product_review_ratings
        rendered = render_product_review_ratings(product)

        if rendered:
            cache.set(cache_key, rendered)
            return rendered

        return ""
Beispiel #43
0
def get_search_product_ids(request, query, limit=150):
    query = query.strip().lower()
    cache_key_elements = {
        "query": query,
        "shop": request.shop.pk,
        "customer": request.customer.pk
    }
    cache_key = "simple_search:%s" % hash(frozenset(cache_key_elements.items()))
    product_ids = cache.get(cache_key)
    if product_ids is None:
        entry_query = get_compiled_query(
            query, ['sku', 'translations__name', 'translations__description', 'translations__keywords'])
        product_ids = list(Product.objects.searchable(
            shop=request.shop,
            customer=request.customer
        ).filter(entry_query).distinct().values_list("pk", flat=True))[:limit]
        cache.set(cache_key, product_ids, 60 * 5)
    return product_ids
Beispiel #44
0
def _cache_shop_configuration(shop):
    """
    Cache global or shop specific configuration.

    Global configuration (`shop` is ``None``) is read first, then `shop`
    based configuration is updated over that.

    :param shop: Shop to cache configuration for, or None
    :type shop: shuup.core.models.Shop|None
    :return: Cached configuration
    :rtype: dict
    """
    configuration = {}
    configuration.update(_get_configuration_from_db(None))
    if shop:
        configuration.update(_get_configuration_from_db(shop))
    cache.set(_get_cache_key(shop), configuration)
    return configuration
Beispiel #45
0
def _cache_shop_configuration(shop):
    """
    Cache global or shop specific configuration.

    Global configuration (`shop` is ``None``) is read first, then `shop`
    based configuration is updated over that.

    :param shop: Shop to cache configuration for, or None
    :type shop: shuup.core.models.Shop|None
    :return: Cached configuration
    :rtype: dict
    """
    configuration = {}
    configuration.update(_get_configuration_from_db(None))
    if shop:
        configuration.update(_get_configuration_from_db(shop))
    cache.set(_get_cache_key(shop), configuration)
    return configuration
Beispiel #46
0
    def render(self, context, cache_key_prefix=None):
        """
        Return the plugin's rendered content.

        :param context: Jinja2 rendering context.
        :type context: jinja2.runtime.Context
        :return: string of content.
        :rtype: str
        """
        if not self.plugin_identifier:
            return ""  # Null!
        plugin_inst = self.instantiate_plugin()
        if plugin_inst is None:
            return mark_safe("<!-- %s? -->" % self.plugin_identifier)

        try:
            if plugin_inst.is_context_valid(context=context):
                # check whether the plugin can be cached
                cacheabled = getattr(plugin_inst, "cacheable", False)
                cache_key = (plugin_inst.get_cache_key(context) if hasattr(
                    plugin_inst, "get_cache_key") else plugin_inst.identifier)
                hash_key = hashlib.sha1(
                    f"{cache_key_prefix}-{cache_key}".encode(
                        "utf-8")).hexdigest()
                full_cache_key = f"shuup_xtheme_cell:{hash_key}"
                cached_content = cache.get(full_cache_key)
                if cached_content is not None:
                    return cached_content

                content = plugin_inst.render(context=context)
                if cacheabled:
                    cache.set(full_cache_key, content)
                return content
            else:
                return ""

        except Exception:
            # catch any error while trying to render the cell
            LOGGER.exception(
                f"Failed to render the plugin: {self.plugin_identifier}")
            error_msg = gettext("Failed to render the plugin")
            return mark_safe(
                mark_safe(
                    f'<small class="plugin-render-error">{error_msg}</small>'))
Beispiel #47
0
def set_current_theme(identifier, shop):
    """
    Activate a theme based on identifier.

    :param identifier: Theme identifier
    :type identifier: str
    :param shop: Shop to fetch the theme settings
    :type shop: shuup.core.models.Shop
    :return: Activated theme
    :rtype: Theme
    """
    cache.bump_version(get_theme_cache_key(shop))
    theme = get_theme_by_identifier(identifier, shop)
    if not theme:
        raise ValueError("Invalid theme identifier")
    theme.set_current()
    cache.set(get_theme_cache_key(shop), theme)
    set_middleware_current_theme(theme)
    return theme
Beispiel #48
0
def set_current_theme(identifier, shop):
    """
    Activate a theme based on identifier.

    :param identifier: Theme identifier
    :type identifier: str
    :param shop: Shop to fetch the theme settings
    :type shop: shuup.core.models.Shop
    :return: Activated theme
    :rtype: Theme
    """
    cache.bump_version(get_theme_cache_key(shop))
    theme = get_theme_by_identifier(identifier, shop)
    if not theme:
        raise ValueError("Invalid theme identifier")
    theme.set_current()
    cache.set(get_theme_cache_key(shop), theme)
    set_middleware_current_theme(theme)
    return theme
Beispiel #49
0
def get_search_product_ids(request, query, limit=150):
    query = query.strip().lower()
    cache_key_elements = {
        "query": query,
        "shop": request.shop.pk,
        "customer": request.customer.pk
    }
    cache_key = "simple_search:%s" % hash(frozenset(cache_key_elements.items()))
    product_ids = cache.get(cache_key)
    if product_ids is None:
        product_ids = get_product_ids_for_query_str(request, query, limit)
        for word in query.split(" ") or []:
            if word == query:
                break
            prod_count = len(product_ids)
            if prod_count >= limit:
                break
            product_ids += get_product_ids_for_query_str(request, word.strip(), limit, product_ids)
        cache.set(cache_key, product_ids[:limit], 60 * 5)
    return product_ids
Beispiel #50
0
def get_current_theme(shop):
    """
    Get the currently active theme object.

    :param shop: The shop to get the active theme
    :type shop: shuup.core.models.Shop
    :return: Theme object or None
    :rtype: Theme
    """
    value = cache.get(get_theme_cache_key(shop))
    if value:
        set_middleware_current_theme(value)
        return value

    theme = _get_current_theme(shop)
    cache.set(get_theme_cache_key(shop), theme)
    # set this theme as the current for this thread
    set_middleware_current_theme(theme)

    return theme
Beispiel #51
0
def set(shop, key, value):
    """
    Set configuration item value for a shop or globally.

    If given `shop` is ``None``, the value of given `key` is set
    globally for all shops.  Otherwise sets a shop specific value which
    overrides the global value in configuration of the specified shop.

    :param shop: Shop to set value for, or None to set a global value
    :type shop: shuup.core.models.Shop|None
    :param key: Name of the key to set
    :type key: str
    :param value: Value to set.  Note: Must be JSON serializable.
    :type value: Any
    """
    ConfigurationItem.objects.update_or_create(
        shop=shop, key=key, defaults={"value": value})
    if shop:
        cache.set(_get_cache_key(shop), None)
    else:
        cache.bump_version(_SHOP_CONF_NAMESPACE)
Beispiel #52
0
def override_current_theme_class(theme_class=_not_set, shop=None):
    """
    Context manager for overriding the currently active theme class for testing.

    An instance of this class is then returned by `get_current_theme`.

    A falsy value means `None` is returned from `get_current_theme`, which is also
    useful for testing.

    :param theme_class: A theme class object
    :type theme_class: class[Theme]
    """
    # Circular import avoidance:
    from shuup.xtheme.views.extra import clear_view_cache
    old_theme_class = cache.get(get_theme_cache_key(shop))

    if theme_class is _not_set or not theme_class:
        cache.set(get_theme_cache_key(shop), None)
    else:
        from shuup.xtheme.models import ThemeSettings
        theme_settings = ThemeSettings.objects.get_or_create(
            shop=shop,
            theme_identifier=theme_class.identifier
        )[0]
        theme = theme_class(theme_settings)
        set_middleware_current_theme(theme)
        cache.set(get_theme_cache_key(shop), theme)

    clear_view_cache()
    yield

    cache.set(get_theme_cache_key(shop), old_theme_class)
    clear_view_cache()
Beispiel #53
0
    def display_unit(self):
        """
        Default display unit of this sales unit.

        Get a `DisplayUnit` object, which has this sales unit as its
        internal unit and is marked as a default, or if there is no
        default display unit for this sales unit, then a proxy object.
        The proxy object has the same display unit interface and mirrors
        the properties of the sales unit, such as symbol and decimals.

        :rtype: DisplayUnit
        """
        cache_key = "display_unit:sales_unit_{}_default_display_unit".format(self.pk)
        default_display_unit = cache.get(cache_key)

        if default_display_unit is None:
            default_display_unit = self.display_units.filter(default=True).first()
            # Set 0 to cache to prevent None values, which will not be a valid cache value
            # 0 will be invalid below, hence we prevent another query here
            cache.set(cache_key, default_display_unit or 0)

        return default_display_unit or SalesUnitAsDisplayUnit(self)
Beispiel #54
0
def get_currency_precision(currency):
    """
    Get precision by currency code.

    Precision values will be populated from the ``decimal_places``
    fields of the `Currency` objects in the database.

    :type currency: str
    :param currency: Currency code as 3-letter string (ISO-4217)

    :rtype: decimal.Decimal|None
    :return: Precision value for given currency code or None for unknown
    """
    cache_key = 'currency_precision:' + currency
    precision = cache.get(cache_key)
    if precision is None:
        currency_obj = Currency.objects.filter(code=currency).first()
        precision = (
            decimal.Decimal('0.1') ** currency_obj.decimal_places
            if currency_obj else None)
        cache.set(cache_key, precision)
    return precision
Beispiel #55
0
def _get_cached_value_from_context(context, key, value):
    cached_value = None

    # 1) check whether the value is cached inside the context as an attribute
    try:
        cache_key = "_ctx_cache_{}".format(key)
        cached_value = getattr(context, cache_key)
    except AttributeError:
        pass

    # 2) Check whether the value is cached in general cache
    # we can only cache objects that has `pk` attribute
    if cached_value is None and hasattr(value, "pk"):
        cache_key = "_ctx_cache:{}_{}".format(key, value.pk)
        cached_value = cache.get(cache_key)

    # 3) Nothing is cached, then read the value itself
    if cached_value is None:
        if key == "customer" and value:
            cached_value = _get_val(value.groups.all())
        else:
            cached_value = _get_val(value)

        # Set the value as attribute of the context
        # somethings this will raise AttributeError because the
        # context is not a valid object, like a dictionary
        try:
            cache_key = "_ctx_cache_{}".format(key)
            setattr(context, cache_key, cached_value)
        except AttributeError:
            pass

        # cache the value in the general cache
        if hasattr(value, "pk"):
            cache_key = "_ctx_cache:{}_{}".format(key, value.pk)
            cache.set(cache_key, cached_value)

    return cached_value
Beispiel #56
0
def test_alert_limit_notification(rf, admin_user):
    with override_settings(CACHES={
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'test_configuration_cache',
        }
    }):
        cache.init_cache()

        supplier = get_simple_supplier()
        shop = get_default_shop()
        product = create_product("simple-test-product", shop, supplier)

        sc = StockCount.objects.get(supplier=supplier, product=product)
        sc.alert_limit = 10
        sc.save()

        # nothing in cache
        cache_key = AlertLimitReached.cache_key_fmt % (supplier.pk, product.pk)
        assert cache.get(cache_key) is None

        # put 11 units in stock
        supplier.adjust_stock(product.pk, +11)

        # still nothing in cache
        cache_key = AlertLimitReached.cache_key_fmt % (supplier.pk, product.pk)
        assert cache.get(cache_key) is None

        event = AlertLimitReached(product=product, supplier=supplier)
        assert event.variable_values["dispatched_last_24hs"] is False

        # stock should be 6, lower then the alert limit
        supplier.adjust_stock(product.pk, -5)
        last_run = cache.get(cache_key)
        assert last_run is not None

        event = AlertLimitReached(product=product, supplier=supplier)
        assert event.variable_values["dispatched_last_24hs"] is True

        # stock should be 1, lower then the alert limit
        supplier.adjust_stock(product.pk, -5)

        # test whether that runs inside a minute
        event = AlertLimitReached(product=product, supplier=supplier)
        event.run(shop)
        # not updated, not ran
        assert cache.get(cache_key) == last_run

        last_run -= 1000
        cache.set(cache_key, last_run)
        event = AlertLimitReached(product=product, supplier=supplier)
        event.run(shop)

        # last run should be updated
        assert cache.get(cache_key) != last_run

        event = AlertLimitReached(
            product=product,
            supplier=supplier,
            supplier_email="*****@*****.**",
            shop_email="*****@*****.**"
        )
        assert event.variable_values["dispatched_last_24hs"] is True

        # fake we have a cache with more than 24hrs
        cache.set(cache_key, time() - (24 * 60 * 60 * 2))

        event = AlertLimitReached(product=product, supplier=supplier)
        assert event.variable_values["dispatched_last_24hs"] is False
Beispiel #57
0
 def run(self):
     cache_key = self.cache_key_fmt % (self.variable_values["supplier"].pk, self.variable_values["product"].pk)
     cache.set(cache_key, time(), timeout=(60 * 60 * 24))
     super(AlertLimitReached, self).run()