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)
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 ""
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
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
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
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
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
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
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
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
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)
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)
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
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
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 {}
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)
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
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
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
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)
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
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
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
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])
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
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]
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
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"
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
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
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
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 ""
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
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
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>'))
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
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
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
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)
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()
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)
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
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
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
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()