commit
						27d0e1c6be
					
				
					 5 changed files with 188 additions and 13 deletions
				
			
		|  | @ -97,6 +97,19 @@ class SpeakerDiscountAdmin(admin.ModelAdmin, EffectsDisplayMixin): | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @admin.register(conditions.GroupMemberDiscount) | ||||||
|  | class GroupMemberDiscountAdmin(admin.ModelAdmin, EffectsDisplayMixin): | ||||||
|  | 
 | ||||||
|  |     fields = ("description", "group") | ||||||
|  | 
 | ||||||
|  |     list_display = ("description", "effects") | ||||||
|  | 
 | ||||||
|  |     inlines = [ | ||||||
|  |         DiscountForProductInline, | ||||||
|  |         DiscountForCategoryInline, | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # Vouchers | # Vouchers | ||||||
| 
 | 
 | ||||||
| class VoucherDiscountInline(nested_admin.NestedStackedInline): | class VoucherDiscountInline(nested_admin.NestedStackedInline): | ||||||
|  | @ -194,3 +207,11 @@ class SpeakerFlagAdmin(nested_admin.NestedAdmin, EffectsDisplayMixin): | ||||||
|     list_display = ("description", "is_presenter", "is_copresenter", "effects") |     list_display = ("description", "is_presenter", "is_copresenter", "effects") | ||||||
| 
 | 
 | ||||||
|     ordering = ("-is_presenter", "-is_copresenter") |     ordering = ("-is_presenter", "-is_copresenter") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @admin.register(conditions.GroupMemberFlag) | ||||||
|  | class GroupMemberFlagAdmin(admin.ModelAdmin, EffectsDisplayMixin): | ||||||
|  | 
 | ||||||
|  |     fields = ("description", "group") | ||||||
|  | 
 | ||||||
|  |     list_display = ("description", "effects") | ||||||
|  |  | ||||||
|  | @ -23,6 +23,8 @@ class ConditionController(object): | ||||||
|     def _controllers(): |     def _controllers(): | ||||||
|         return { |         return { | ||||||
|             conditions.CategoryFlag: CategoryConditionController, |             conditions.CategoryFlag: CategoryConditionController, | ||||||
|  |             conditions.GroupMemberDiscount: GroupMemberConditionController, | ||||||
|  |             conditions.GroupMemberFlag: GroupMemberConditionController, | ||||||
|             conditions.IncludedProductDiscount: ProductConditionController, |             conditions.IncludedProductDiscount: ProductConditionController, | ||||||
|             conditions.ProductFlag: ProductConditionController, |             conditions.ProductFlag: ProductConditionController, | ||||||
|             conditions.SpeakerFlag: SpeakerConditionController, |             conditions.SpeakerFlag: SpeakerConditionController, | ||||||
|  | @ -319,7 +321,19 @@ class SpeakerConditionController(IsMetByFilter, ConditionController): | ||||||
|         # User is a copresenter |         # User is a copresenter | ||||||
|         user_is_copresenter = Q( |         user_is_copresenter = Q( | ||||||
|             is_copresenter=True, |             is_copresenter=True, | ||||||
|             proposal_kind__proposalbase__presentation__additional_speakers__user=u, |             proposal_kind__proposalbase__presentation__additional_speakers__user=u,  # NOQA | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         return queryset.filter(user_is_presenter | user_is_copresenter) |         return queryset.filter(user_is_presenter | user_is_copresenter) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupMemberConditionController(IsMetByFilter, ConditionController): | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def pre_filter(self, conditions, user): | ||||||
|  |         ''' Returns all of the items from conditions which are enabled by a | ||||||
|  |         user being member of a Django Auth Group. ''' | ||||||
|  | 
 | ||||||
|  |         return conditions.filter( | ||||||
|  |             group=user.groups.all(), | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,41 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Generated by Django 1.9.2 on 2016-09-04 23:59 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('auth', '0007_alter_validators_add_error_messages'), | ||||||
|  |         ('registrasion', '0003_auto_20160904_0235'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='GroupMemberDiscount', | ||||||
|  |             fields=[ | ||||||
|  |                 ('discountbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registrasion.DiscountBase')), | ||||||
|  |                 ('group', models.ManyToManyField(help_text='The groups a user needs to be a member of for thiscondition to be met.', to='auth.Group')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': 'discount (group member)', | ||||||
|  |                 'verbose_name_plural': 'discounts (group member)', | ||||||
|  |             }, | ||||||
|  |             bases=('registrasion.discountbase', models.Model), | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='GroupMemberFlag', | ||||||
|  |             fields=[ | ||||||
|  |                 ('flagbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registrasion.FlagBase')), | ||||||
|  |                 ('group', models.ManyToManyField(help_text='The groups a user needs to be a member of for thiscondition to be met.', to='auth.Group')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': 'flag (group member)', | ||||||
|  |                 'verbose_name_plural': 'flags (group member)', | ||||||
|  |             }, | ||||||
|  |             bases=('registrasion.flagbase', models.Model), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  | @ -2,6 +2,7 @@ import itertools | ||||||
| 
 | 
 | ||||||
| from . import inventory | from . import inventory | ||||||
| 
 | 
 | ||||||
|  | from django.contrib.auth.models import Group | ||||||
| from django.core.exceptions import ValidationError | from django.core.exceptions import ValidationError | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.utils.encoding import python_2_unicode_compatible | from django.utils.encoding import python_2_unicode_compatible | ||||||
|  | @ -81,7 +82,7 @@ class IncludedProductCondition(models.Model): | ||||||
| 
 | 
 | ||||||
| class SpeakerCondition(models.Model): | class SpeakerCondition(models.Model): | ||||||
|     ''' Conditions that are met if a user is a presenter, or copresenter, |     ''' Conditions that are met if a user is a presenter, or copresenter, | ||||||
|     of a specific of presentation. ''' |     of a specific kind of presentation. ''' | ||||||
| 
 | 
 | ||||||
|     class Meta: |     class Meta: | ||||||
|         abstract = True |         abstract = True | ||||||
|  | @ -103,6 +104,20 @@ class SpeakerCondition(models.Model): | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class GroupMemberCondition(models.Model): | ||||||
|  |     ''' Conditions that are met if a user is a member (not declined or | ||||||
|  |     rejected) of a specific django auth group. ''' | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         abstract = True | ||||||
|  | 
 | ||||||
|  |     group = models.ManyToManyField( | ||||||
|  |         Group, | ||||||
|  |         help_text=_("The groups a user needs to be a member of for this" | ||||||
|  |                     "condition to be met."), | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # Discounts | # Discounts | ||||||
| 
 | 
 | ||||||
| @python_2_unicode_compatible | @python_2_unicode_compatible | ||||||
|  | @ -325,12 +340,23 @@ class SpeakerDiscount(SpeakerCondition, DiscountBase): | ||||||
|         verbose_name_plural = _("discounts (speaker)") |         verbose_name_plural = _("discounts (speaker)") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class RoleDiscount(object): | class GroupMemberDiscount(GroupMemberCondition, DiscountBase): | ||||||
|     ''' Discounts that are enabled because the active user has a specific |     ''' Discounts that are enabled because the user is a member of a specific | ||||||
|     role. This is for e.g. volunteers who can get a discount ticket. ''' |     django auth Group. | ||||||
|     # TODO: implement RoleDiscount |  | ||||||
|     pass |  | ||||||
| 
 | 
 | ||||||
|  |     Attributes: | ||||||
|  |         group ([Group, ...]): The condition should be met if the user is a | ||||||
|  |             member of one of these groups. | ||||||
|  | 
 | ||||||
|  |     ''' | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         app_label = "registrasion" | ||||||
|  |         verbose_name = _("discount (group member)") | ||||||
|  |         verbose_name_plural = _("discounts (group member)") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Flags | ||||||
| 
 | 
 | ||||||
| @python_2_unicode_compatible | @python_2_unicode_compatible | ||||||
| class FlagBase(models.Model): | class FlagBase(models.Model): | ||||||
|  | @ -517,9 +543,17 @@ class SpeakerFlag(SpeakerCondition, FlagBase): | ||||||
|         verbose_name_plural = _("flags (speaker)") |         verbose_name_plural = _("flags (speaker)") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # @python_2_unicode_compatible | class GroupMemberFlag(GroupMemberCondition, FlagBase): | ||||||
| class RoleFlag(object): |     ''' Flag whose conditions are metbecause the user is a member of a specific | ||||||
|     ''' The condition is met because the active user has a particular Role. |     django auth Group. | ||||||
|     This is for e.g. enabling Team tickets. ''' | 
 | ||||||
|     # TODO: implement RoleFlag |     Attributes: | ||||||
|     pass |         group ([Group, ...]): The condition should be met if the user is a | ||||||
|  |             member of one of these groups. | ||||||
|  | 
 | ||||||
|  |     ''' | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         app_label = "registrasion" | ||||||
|  |         verbose_name = _("flag (group member)") | ||||||
|  |         verbose_name_plural = _("flags (group member)") | ||||||
|  |  | ||||||
							
								
								
									
										65
									
								
								registrasion/tests/test_group_member.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								registrasion/tests/test_group_member.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | ||||||
|  | import pytz | ||||||
|  | 
 | ||||||
|  | from django.contrib.auth.models import Group | ||||||
|  | from django.core.exceptions import ValidationError | ||||||
|  | 
 | ||||||
|  | from registrasion.models import commerce | ||||||
|  | from registrasion.models import conditions | ||||||
|  | from registrasion.controllers.category import CategoryController | ||||||
|  | from controller_helpers import TestingCartController | ||||||
|  | from controller_helpers import TestingInvoiceController | ||||||
|  | from registrasion.controllers.product import ProductController | ||||||
|  | 
 | ||||||
|  | from test_cart import RegistrationCartTestCase | ||||||
|  | 
 | ||||||
|  | UTC = pytz.timezone('UTC') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupMemberTestCase(RegistrationCartTestCase): | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def _create_group_and_flag(cls): | ||||||
|  |         ''' Creates cls.GROUP, and restricts cls.PROD_1 only to users who are | ||||||
|  |         members of the group. ''' | ||||||
|  | 
 | ||||||
|  |         group = Group.objects.create( | ||||||
|  |             name="TEST GROUP", | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         flag = conditions.GroupMemberFlag.objects.create( | ||||||
|  |             description="Group member flag", | ||||||
|  |             condition=conditions.FlagBase.ENABLE_IF_TRUE, | ||||||
|  |         ) | ||||||
|  |         flag.group.add(group) | ||||||
|  |         flag.products.add(cls.PROD_1) | ||||||
|  | 
 | ||||||
|  |         cls.GROUP = group | ||||||
|  | 
 | ||||||
|  |     def test_product_not_enabled_until_user_joins_group(self): | ||||||
|  |         ''' Tests that GroupMemberFlag disables a product for a user until | ||||||
|  |         they are a member of a specific group. ''' | ||||||
|  | 
 | ||||||
|  |         self._create_group_and_flag() | ||||||
|  | 
 | ||||||
|  |         # USER_1 cannot see PROD_1 until they're in GROUP. | ||||||
|  |         available = ProductController.available_products( | ||||||
|  |             self.USER_1, | ||||||
|  |             products=[self.PROD_1], | ||||||
|  |         ) | ||||||
|  |         self.assertNotIn(self.PROD_1, available) | ||||||
|  | 
 | ||||||
|  |         self.USER_1.groups.add(self.GROUP) | ||||||
|  | 
 | ||||||
|  |         # USER_1 cannot see PROD_1 until they're in GROUP. | ||||||
|  |         available = ProductController.available_products( | ||||||
|  |             self.USER_1, | ||||||
|  |             products=[self.PROD_1], | ||||||
|  |         ) | ||||||
|  |         self.assertIn(self.PROD_1, available) | ||||||
|  | 
 | ||||||
|  |         # USER_2 is still locked out | ||||||
|  |         available = ProductController.available_products( | ||||||
|  |             self.USER_2, | ||||||
|  |             products=[self.PROD_1], | ||||||
|  |         ) | ||||||
|  |         self.assertNotIn(self.PROD_1, available) | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Christopher Neugebauer
						Christopher Neugebauer