Merge branch 'chrisjrn/team_conditions'

Fixes #70.
This commit is contained in:
Christopher Neugebauer 2016-09-05 10:49:02 +10:00
commit 27d0e1c6be
5 changed files with 188 additions and 13 deletions

View file

@ -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
class VoucherDiscountInline(nested_admin.NestedStackedInline):
@ -194,3 +207,11 @@ class SpeakerFlagAdmin(nested_admin.NestedAdmin, EffectsDisplayMixin):
list_display = ("description", "is_presenter", "is_copresenter", "effects")
ordering = ("-is_presenter", "-is_copresenter")
@admin.register(conditions.GroupMemberFlag)
class GroupMemberFlagAdmin(admin.ModelAdmin, EffectsDisplayMixin):
fields = ("description", "group")
list_display = ("description", "effects")

View file

@ -23,6 +23,8 @@ class ConditionController(object):
def _controllers():
return {
conditions.CategoryFlag: CategoryConditionController,
conditions.GroupMemberDiscount: GroupMemberConditionController,
conditions.GroupMemberFlag: GroupMemberConditionController,
conditions.IncludedProductDiscount: ProductConditionController,
conditions.ProductFlag: ProductConditionController,
conditions.SpeakerFlag: SpeakerConditionController,
@ -319,7 +321,19 @@ class SpeakerConditionController(IsMetByFilter, ConditionController):
# User is a copresenter
user_is_copresenter = Q(
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)
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(),
)

View file

@ -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),
),
]

View file

@ -2,6 +2,7 @@ import itertools
from . import inventory
from django.contrib.auth.models import Group
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@ -81,7 +82,7 @@ class IncludedProductCondition(models.Model):
class SpeakerCondition(models.Model):
''' 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:
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
@python_2_unicode_compatible
@ -325,12 +340,23 @@ class SpeakerDiscount(SpeakerCondition, DiscountBase):
verbose_name_plural = _("discounts (speaker)")
class RoleDiscount(object):
''' Discounts that are enabled because the active user has a specific
role. This is for e.g. volunteers who can get a discount ticket. '''
# TODO: implement RoleDiscount
pass
class GroupMemberDiscount(GroupMemberCondition, DiscountBase):
''' Discounts that are enabled because the user is a member of a specific
django auth Group.
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
class FlagBase(models.Model):
@ -517,9 +543,17 @@ class SpeakerFlag(SpeakerCondition, FlagBase):
verbose_name_plural = _("flags (speaker)")
# @python_2_unicode_compatible
class RoleFlag(object):
''' The condition is met because the active user has a particular Role.
This is for e.g. enabling Team tickets. '''
# TODO: implement RoleFlag
pass
class GroupMemberFlag(GroupMemberCondition, FlagBase):
''' Flag whose conditions are metbecause the user is a member of a specific
django auth Group.
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 = _("flag (group member)")
verbose_name_plural = _("flags (group member)")

View 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)