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:
		
							parent
							
								
									638ec26126
								
							
						
					
					
						commit
						c24b9ee213
					
				
					 6 changed files with 65 additions and 33 deletions
				
			
		| 
						 | 
					@ -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,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										25
									
								
								registrasion/migrations/0024_auto_20160411_2230.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								registrasion/migrations/0024_auto_20160411_2230.py
									
										
									
									
									
										Normal 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'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
| 
						 | 
					@ -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 '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue