import itertools import logging from django.db.models import Case from django.db.models import F, Q from django.db.models import Sum from django.db.models import When from django.db.models import Value from registrasion.models import commerce from registrasion.models import inventory from .batch import BatchController from .category import CategoryController from .flag import FlagController class ProductController(object): def __init__(self, product): self.product = product @classmethod def available_products(cls, user, category=None, products=None): ''' Returns a list of all of the products that are available per flag conditions from the given categories. ''' logging.debug("Checking available products for user %(user)s: " "category %(category)s or products %(product)s", {'user': user, 'category': category, 'products': products}) if category is None and products is None: raise ValueError("You must provide products or a category") if category is not None: all_products = inventory.Product.objects.filter(category=category) all_products = all_products.select_related("category") else: all_products = [] if products is not None: all_products = set(itertools.chain(all_products, products)) category_remainders = CategoryController.user_remainders(user) product_remainders = ProductController.user_remainders(user) passed_limits = set( product for product in all_products if category_remainders[product.category.id] > 0 if product_remainders[product.id] > 0 ) logging.debug("Passed limits: %s", passed_limits) failed_and_messages = FlagController.test_flags( user, products=passed_limits ) failed_conditions = set(i[0] for i in failed_and_messages) logging.debug("Failed conditions: %s", failed_conditions) out = list(passed_limits - failed_conditions) out.sort(key=lambda product: product.order) logging.debug("Returning: %s", out) return out @classmethod @BatchController.memoise def user_remainders(cls, user): ''' Return: Mapping[int->int]: A dictionary that maps the product ID to the user's remainder for that product. ''' products = inventory.Product.objects.all() cart_filter = ( Q(productitem__cart__user=user) & Q(productitem__cart__status=commerce.Cart.STATUS_PAID) ) quantity = When( cart_filter, then='productitem__quantity' ) quantity_or_zero = Case( quantity, default=Value(0), ) remainder = Case( When(limit_per_user=None, then=Value(99999999)), default=F('limit_per_user') - Sum(quantity_or_zero), ) products = products.annotate(remainder=remainder) return dict((product.id, product.remainder) for product in products)