Makes EnablingConditionBase a minimal reification of an abstract base model FlagBase, replaces enablingconditionbase with flagbase where possible, and fixes method names and documentation

This commit is contained in:
Christopher Neugebauer 2016-04-12 08:30:33 +10:00
parent 638ec26126
commit c24b9ee213
6 changed files with 65 additions and 33 deletions

View file

@ -118,7 +118,7 @@ class CartController(object):
def _test_limits(self, product_quantities): def _test_limits(self, product_quantities):
''' Tests that the quantity changes we intend to make do not violate ''' Tests that the quantity changes we intend to make do not violate
the limits and enabling conditions imposed on the products. ''' the limits and flag conditions imposed on the products. '''
errors = [] errors = []
@ -159,8 +159,8 @@ class CartController(object):
) )
)) ))
# Test the enabling conditions # Test the flag conditions
errs = ConditionController.test_enabling_conditions( errs = ConditionController.test_flags(
self.cart.user, self.cart.user,
product_quantities=product_quantities, product_quantities=product_quantities,
) )

View file

@ -65,16 +65,16 @@ class ConditionController(object):
} }
@classmethod @classmethod
def test_enabling_conditions( def test_flags(
cls, user, products=None, product_quantities=None): cls, user, products=None, product_quantities=None):
''' Evaluates all of the enabling conditions on the given products. ''' Evaluates all of the flag conditions on the given products.
If `product_quantities` is supplied, the condition is only met if it If `product_quantities` is supplied, the condition is only met if it
will permit the sum of the product quantities for all of the products will permit the sum of the product quantities for all of the products
it covers. Otherwise, it will be met if at least one item can be it covers. Otherwise, it will be met if at least one item can be
accepted. accepted.
If all enabling conditions pass, an empty list is returned, otherwise If all flag conditions pass, an empty list is returned, otherwise
a list is returned containing all of the products that are *not a list is returned containing all of the products that are *not
enabled*. ''' enabled*. '''
@ -90,14 +90,13 @@ class ConditionController(object):
quantities = {} quantities = {}
# Get the conditions covered by the products themselves # Get the conditions covered by the products themselves
prods = ( prods = (
product.enablingconditionbase_set.select_subclasses() product.flagbase_set.select_subclasses()
for product in products for product in products
) )
# Get the conditions covered by their categories # Get the conditions covered by their categories
cats = ( cats = (
category.enablingconditionbase_set.select_subclasses() category.flagbase_set.select_subclasses()
for category in set(product.category for product in products) for category in set(product.category for product in products)
) )
@ -172,7 +171,7 @@ class ConditionController(object):
return error_fields return error_fields
def user_quantity_remaining(self, user): def user_quantity_remaining(self, user):
''' Returns the number of items covered by this enabling condition the ''' Returns the number of items covered by this flag condition the
user can add to the current cart. This default implementation returns user can add to the current cart. This default implementation returns
a big number if is_met() is true, otherwise 0. a big number if is_met() is true, otherwise 0.
@ -182,7 +181,7 @@ class ConditionController(object):
return 99999999 if self.is_met(user) else 0 return 99999999 if self.is_met(user) else 0
def is_met(self, user): def is_met(self, user):
''' Returns True if this enabling condition is met, otherwise returns ''' Returns True if this flag condition is met, otherwise returns
False. False.
Either this method, or user_quantity_remaining() must be overridden Either this method, or user_quantity_remaining() must be overridden

View file

@ -15,9 +15,9 @@ class ProductController(object):
@classmethod @classmethod
def available_products(cls, user, category=None, products=None): def available_products(cls, user, category=None, products=None):
''' Returns a list of all of the products that are available per ''' Returns a list of all of the products that are available per
enabling conditions from the given categories. flag conditions from the given categories.
TODO: refactor so that all conditions are tested here and TODO: refactor so that all conditions are tested here and
can_add_with_enabling_conditions calls this method. ''' can_add_with_flags calls this method. '''
if category is None and products is None: if category is None and products is None:
raise ValueError("You must provide products or a category") raise ValueError("You must provide products or a category")
@ -45,7 +45,7 @@ class ProductController(object):
if cls(product).user_quantity_remaining(user) > 0 if cls(product).user_quantity_remaining(user) > 0
) )
failed_and_messages = ConditionController.test_enabling_conditions( failed_and_messages = ConditionController.test_flags(
user, products=passed_limits user, products=passed_limits
) )
failed_conditions = set(i[0] for i in failed_and_messages) failed_conditions = set(i[0] for i in failed_and_messages)

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.2 on 2016-04-11 22:30
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('registrasion', '0023_auto_20160411_1001_squashed_0024_auto_20160411_1002'),
]
operations = [
migrations.AlterField(
model_name='enablingconditionbase',
name='categories',
field=models.ManyToManyField(blank=True, help_text="Categories whose products are affected by this flag's condition.", related_name='flagbase_set', to='registrasion.Category'),
),
migrations.AlterField(
model_name='enablingconditionbase',
name='products',
field=models.ManyToManyField(blank=True, help_text="Products affected by this flag's condition.", related_name='flagbase_set', to='registrasion.Product'),
),
]

View file

@ -366,15 +366,8 @@ class RoleDiscount(object):
pass pass
class FlagBase(object):
''' This will replace EnablingConditionBase once it's ready. '''
DISABLE_IF_FALSE = 1
ENABLE_IF_TRUE = 2
@python_2_unicode_compatible @python_2_unicode_compatible
class EnablingConditionBase(models.Model): class FlagBase(models.Model):
''' This defines a condition which allows products or categories to ''' This defines a condition which allows products or categories to
be made visible, or be prevented from being visible. be made visible, or be prevented from being visible.
@ -387,10 +380,14 @@ class EnablingConditionBase(models.Model):
If both types of conditions exist on a product, both of these rules apply. If both types of conditions exist on a product, both of these rules apply.
''' '''
# TODO: rename to FlagBase once
# https://code.djangoproject.com/ticket/26488 is solved.
objects = InheritanceManager() class Meta:
# TODO: make concrete once https://code.djangoproject.com/ticket/26488
# is solved.
abstract = True
DISABLE_IF_FALSE = 1
ENABLE_IF_TRUE = 2
def __str__(self): def __str__(self):
return self.description return self.description
@ -409,10 +406,10 @@ class EnablingConditionBase(models.Model):
description = models.CharField(max_length=255) description = models.CharField(max_length=255)
condition = models.IntegerField( condition = models.IntegerField(
default=FlagBase.ENABLE_IF_TRUE, default=ENABLE_IF_TRUE,
choices=( choices=(
(FlagBase.DISABLE_IF_FALSE, _("Disable if false")), (DISABLE_IF_FALSE, _("Disable if false")),
(FlagBase.ENABLE_IF_TRUE, _("Enable if true")), (ENABLE_IF_TRUE, _("Enable if true")),
), ),
help_text=_("If there is at least one 'disable if false' flag " help_text=_("If there is at least one 'disable if false' flag "
"defined on a product or category, all such flag " "defined on a product or category, all such flag "
@ -426,6 +423,7 @@ class EnablingConditionBase(models.Model):
Product, Product,
blank=True, blank=True,
help_text=_("Products affected by this flag's condition."), help_text=_("Products affected by this flag's condition."),
related_name="flagbase_set",
) )
categories = models.ManyToManyField( categories = models.ManyToManyField(
Category, Category,
@ -433,9 +431,19 @@ class EnablingConditionBase(models.Model):
help_text=_("Categories whose products are affected by this flag's " help_text=_("Categories whose products are affected by this flag's "
"condition." "condition."
), ),
related_name="flagbase_set",
) )
class EnablingConditionBase(FlagBase):
''' Reifies the abstract FlagBase. This is necessary because django
prevents renaming base classes in migrations. '''
# TODO: remove this, and make subclasses subclass FlagBase once
# https://code.djangoproject.com/ticket/26488 is solved.
objects = InheritanceManager()
class TimeOrStockLimitFlag(EnablingConditionBase): class TimeOrStockLimitFlag(EnablingConditionBase):
''' Registration product ceilings ''' ''' Registration product ceilings '''

View file

@ -16,7 +16,7 @@ class FlagTestCases(RegistrationCartTestCase):
@classmethod @classmethod
def add_product_flag(cls, condition=rego.FlagBase.ENABLE_IF_TRUE): def add_product_flag(cls, condition=rego.FlagBase.ENABLE_IF_TRUE):
''' Adds a product enabling condition: adding PROD_1 to a cart is ''' Adds a product flag condition: adding PROD_1 to a cart is
predicated on adding PROD_2 beforehand. ''' predicated on adding PROD_2 beforehand. '''
flag = rego.ProductFlag.objects.create( flag = rego.ProductFlag.objects.create(
description="Product condition", description="Product condition",
@ -29,7 +29,7 @@ class FlagTestCases(RegistrationCartTestCase):
@classmethod @classmethod
def add_product_flag_on_category(cls, condition=rego.FlagBase.ENABLE_IF_TRUE): def add_product_flag_on_category(cls, condition=rego.FlagBase.ENABLE_IF_TRUE):
''' Adds a product enabling condition that operates on a category: ''' Adds a product flag condition that operates on a category:
adding an item from CAT_1 is predicated on adding PROD_3 beforehand ''' adding an item from CAT_1 is predicated on adding PROD_3 beforehand '''
flag = rego.ProductFlag.objects.create( flag = rego.ProductFlag.objects.create(
description="Product condition", description="Product condition",
@ -41,7 +41,7 @@ class FlagTestCases(RegistrationCartTestCase):
flag.save() flag.save()
def add_category_flag(cls, condition=rego.FlagBase.ENABLE_IF_TRUE): def add_category_flag(cls, condition=rego.FlagBase.ENABLE_IF_TRUE):
''' Adds a category enabling condition: adding PROD_1 to a cart is ''' Adds a category flag condition: adding PROD_1 to a cart is
predicated on adding an item from CAT_2 beforehand.''' predicated on adding an item from CAT_2 beforehand.'''
flag = rego.CategoryFlag.objects.create( flag = rego.CategoryFlag.objects.create(
description="Category condition", description="Category condition",
@ -114,7 +114,7 @@ class FlagTestCases(RegistrationCartTestCase):
self.add_product_flag() self.add_product_flag()
self.add_category_flag() self.add_category_flag()
# User 1 is testing the product enabling condition # User 1 is testing the product flag condition
cart_1 = TestingCartController.for_user(self.USER_1) cart_1 = TestingCartController.for_user(self.USER_1)
# Cannot add PROD_1 until a condition is met # Cannot add PROD_1 until a condition is met
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
@ -122,7 +122,7 @@ class FlagTestCases(RegistrationCartTestCase):
cart_1.add_to_cart(self.PROD_2, 1) cart_1.add_to_cart(self.PROD_2, 1)
cart_1.add_to_cart(self.PROD_1, 1) cart_1.add_to_cart(self.PROD_1, 1)
# User 2 is testing the category enabling condition # User 2 is testing the category flag condition
cart_2 = TestingCartController.for_user(self.USER_2) cart_2 = TestingCartController.for_user(self.USER_2)
# Cannot add PROD_1 until a condition is met # Cannot add PROD_1 until a condition is met
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):