def create_fixtures(self): self.user_email = self.user_email_template.format(random_string(8)) self.user = User(self.user_email, 'test_user') self.db.session.add(self.user) self.group_name = self.group_template.format(random_string(8)) self.group = ProductGroup(type='test', name=self.group_name, capacity_max=10) self.product1 = Product(name=self.product1_name, parent=self.group, capacity_max=3) self.tier1_1 = PriceTier(name=self.tier1_1_name, parent=self.product1) self.price1_1 = Price(price_tier=self.tier1_1, currency='GBP', price_int=10) self.db.session.add(self.tier1_1) self.tier1_2 = PriceTier(name=self.tier1_2_name, parent=self.product1) self.price1_2 = Price(price_tier=self.tier1_2, currency='GBP', price_int=20) self.db.session.add(self.tier1_2) self.product2 = Product(name=self.product2_name, parent=self.group) self.tier3 = PriceTier(name=self.tier3_name, parent=self.product2) self.price3 = Price(price_tier=self.tier3, currency='GBP', price_int=30) self.db.session.add(self.tier3) self.db.session.commit()
def product_group_copy(group_id): group = ProductGroup.query.get_or_404(group_id) form = CopyProductGroupForm() if group.children: # No recursive copying abort(404) if request.method != "POST": form.name.data = group.name + " (copy)" if group.capacity_max is None: del form.capacity_max_required else: del form.capacity_max if form.validate_on_submit(): capacity_max = None if group.capacity_max is not None: capacity_max = form.capacity_max_required.data group.capacity_max -= capacity_max new_group = ProductGroup( type=group.type, name=form.name.data, capacity_max=capacity_max, expires=form.expires.data, ) for product in group.products: new_product = Product( name=product.name, display_name=product.display_name, description=product.description, ) new_group.products.append(new_product) for pt in product.price_tiers: if not pt.active and not form.include_inactive.data: continue new_pt = PriceTier(name=pt.name, personal_limit=pt.personal_limit, active=pt.active) new_product.price_tiers.append(new_pt) for price in pt.prices: new_price = Price(price.currency, price.value) new_pt.prices.append(new_price) new_group.parent = group.parent db.session.add(new_group) db.session.commit() flash("ProductGroup copied") return redirect( url_for(".product_group_details", group_id=new_group.id)) return render_template("admin/products/product-group-copy.html", group=group, form=form)
def create_fixtures(self): self.item_name = self.item_template.format(random_string(8)) self.item = ProductGroup(type='tent', name=self.item_name, capacity_max=1, expires=datetime(2012, 8, 31)) self.db.session.add(self.item) self.db.session.commit()
def calc_sales_state(date): site_capacity = ProductGroup.get_by_name('admissions') if site_capacity is None: return "unavailable" if site_capacity.get_total_remaining_capacity() < 1: # We've hit capacity - no more tickets will be sold return "sold-out" elif date > config_date('EVENT_END'): return "sales-ended" # Active price tier for the full ticket product in the main flow. view = ProductView.query.filter_by(name='main') product = view.join(ProductViewProduct, Product).filter_by(name='full') try: tier = product.join(Product.price_tiers) \ .filter_by(active=True) \ .with_entities(PriceTier) \ .one_or_none() except MultipleResultsFound: log.error("Multiple active PriceTiers found. Forcing sales state to unavailable.") return "unavailable" if tier is None or tier.has_expired() or tier.get_total_remaining_capacity() <= 0: # Tickets not currently available, probably just for this round, but we haven't hit site capacity return "unavailable" return "available"
def parent_group(db): parent_group_template = "parent_group{}" group_name = parent_group_template.format(random_string(8)) group = ProductGroup(type="admissions", name=group_name, capacity_max=10) db.session.add(group) db.session.commit() yield group
def create_fixtures(self): self.user1_email = self.user1_email_template.format(random_string(8)) self.user2_email = self.user2_email_template.format(random_string(8)) self.user1 = User(self.user1_email, 'test_user1') self.user2 = User(self.user2_email, 'test_user2') self.db.session.add(self.user1) self.db.session.add(self.user2) self.group_name = self.group_template.format(random_string(8)) self.group = ProductGroup(type='admissions', name=self.group_name) self.product = Product(name=self.product_name, parent=self.group) self.tier = PriceTier(name=self.tier_name, parent=self.product) self.price = Price(price_tier=self.tier, currency='GBP', price_int=666) self.db.session.add(self.price) self.db.session.commit() # PriceTier needs to have been committed before this basket = Basket(self.user1, 'GBP') basket[self.tier] = 1 basket.create_purchases() basket.ensure_purchase_capacity() payment = basket.create_payment(BankPayment) assert len(payment.purchases) == 1 self.db.session.commit()
def calc_sales_state(date): site_capacity = ProductGroup.get_by_name('admissions') if site_capacity is None: return "unavailable" if site_capacity.get_total_remaining_capacity() < 1: # We've hit capacity - no more tickets will be sold return "sold-out" elif date > config_date('EVENT_END'): return "sales-ended" # Active price tier for the full ticket product in the main flow. view = ProductView.query.filter_by(name='main') product = view.join(ProductViewProduct, Product).filter_by(name='full') try: tier = product.join(Product.price_tiers) \ .filter_by(active=True) \ .with_entities(PriceTier) \ .one_or_none() except MultipleResultsFound: log.error( "Multiple active PriceTiers found. Forcing sales state to unavailable." ) return "unavailable" if tier is None or tier.has_expired( ) or tier.get_total_remaining_capacity() <= 0: # Tickets not currently available, probably just for this round, but we haven't hit site capacity return "unavailable" return "available"
def product_group_new(): if request.args.get('parent'): parent = ProductGroup.query.get_or_404(request.args.get('parent')) else: parent = None form = NewProductGroupForm() if form.validate_on_submit(): pg = ProductGroup(form.type.data, parent, parent.id if parent else None, name=form.name.data, capacity_max=form.capacity_max.data, expires=form.expires.data) app.logger.info('%s adding new ProductGroup %s', current_user.name, pg) db.session.add(pg) db.session.commit() flash("ProductGroup created") return redirect(url_for('.product_group_details', group_id=pg.id)) return render_template('admin/products/product-group-edit.html', method='new', parent=parent, form=form)
def create_fixtures(self): self.user_email = self.user_email_template.format(random_string(8)) self.user = User(self.user_email, 'test_user') self.db.session.add(self.user) self.group_name = self.group_template.format(random_string(8)) self.group = ProductGroup(type='admissions', name=self.group_name) self.product = Product(name=self.product_name, capacity_max=3, parent=self.group) self.tier = PriceTier(name=self.tier_name, parent=self.product) self.price = Price(price_tier=self.tier, currency='GBP', price_int=666) # These have `cascade=all` so just add the bottom of the hierarchy self.db.session.add(self.price) self.db.session.commit()
def create_fixtures(self): self.parent_group_name = self.parent_group_template.format( random_string(8)) self.parent_group = ProductGroup(type='test', name=self.parent_group_name, capacity_max=10) self.db.session.add(self.parent_group) self.db.session.commit()
def test_validate_capacity_max(db, parent_group): group_template = 'group{}' group_name = group_template.format(random_string(8)) # Create a product group without a parent so validate_capacity_max returns early group = ProductGroup(type='test') assert group.name is None assert group.id is None # Now add a parent group.parent = parent_group # This should call validate_capacity_max, which may flush the session, which we don't want group.capacity_max = 5 # If that was OK, we can continue group.name = group_name db.session.flush() assert group.id is not None
def test_validate_capacity_max(db, parent_group): group_template = "group{}" group_name = group_template.format(random_string(8)) # Create a product group without a parent so validate_capacity_max returns early group = ProductGroup(type="test") assert group.name is None assert group.id is None # Now add a parent group.parent = parent_group # This should call validate_capacity_max, which may flush the session, which we don't want group.capacity_max = 5 # If that was OK, we can continue group.name = group_name db.session.flush() assert group.id is not None
def tent(db): item_template = "killer_tent{}" item_name = item_template.format(random_string(8)) item = ProductGroup(type="tent", name=item_name, capacity_max=1, expires=datetime(2012, 8, 31)) db.session.add(item) db.session.commit() yield item db.session.delete(item) db.session.commit()
def test_validate_capacity_max(self): with self.app.app_context(): self.create_fixtures() self.group_name = self.group_template.format(random_string(8)) # Create a product group without a parent so validate_capacity_max returns early self.group = ProductGroup(type='test') assert self.group.name is None assert self.group.id is None # Now add a parent self.group.parent = self.parent_group # This should call validate_capacity_max, which may flush the session, which we don't want self.group.capacity_max = 5 # If that was OK, we can continue self.group.name = self.group_name self.db.session.flush() assert self.group.id is not None
def create_product_groups(): top_level_groups = [ # name, capacity, expires ('admissions', None, 2500), ('parking', None, None), ('campervan', None, None), ('merchandise', None, None), ] for name, expires, capacity in top_level_groups: if ProductGroup.get_by_name(name): continue pg = ProductGroup(name=name, type=name, capacity_max=capacity, expires=expires) db.session.add(pg) db.session.flush() allocations = [ # name, capacity ('vendors', 100), ('sponsors', 200), ('speakers', 100), ('general', 1800), ] admissions = ProductGroup.get_by_name('admissions') for name, capacity in allocations: if ProductGroup.get_by_name(name): continue ProductGroup(name=name, capacity_max=capacity, parent=admissions) view = ProductView.get_by_name('main') if not view: view = ProductView(name='main', type='tickets') db.session.add(view) db.session.flush() general = ProductGroup.get_by_name('general') products = [ # name, display name, transferable, badge, capacity, description, (std cap, gbp eur), (early cap, gbp, eur), (late cap, gbp, eur) ('full', 'Full Camp Ticket', True, True, None, 'Full ticket', ((1500, 115, 135), (250, 105, 125), (None, 125, 145)) ), ('full-s', 'Full Camp Ticket (Supporter)', True, True, None, 'Support this non-profit event by paying a bit more. All money will go towards making EMF more awesome.', ((None, 150, 180),) ), ('full-sg', 'Full Camp Ticket (Gold Supporter)', True, True, None, 'Support this non-profit event by paying a bit more. All money will go towards making EMF more awesome.', ((None, 200, 240),) ), ('u18', 'Under-18', True, False, 150, 'For visitors born after August 30th, 2000. All under-18s must be accompanied by an adult.', ((None, 55, 63),) ), ('u12', 'Under-12', True, False, 50, 'For children born after August 30th, 2006. All children must be accompanied by an adult.', ((None, 0, 0),) ), ] order = 0 for name, display_name, has_xfer, has_badge, capacity, description, prices in products: if Product.get_by_name('general', name): continue product = Product(name=name, display_name=display_name, capacity_max=capacity, description=description, parent=general, attributes={'is_transferable': has_xfer, 'has_badge': has_badge}) for index, (price_cap, gbp, eur) in enumerate(prices): if len(prices) == 1 or index == 0: tier_name = name + '-std' active = True elif index == 1: tier_name = name + '-early-bird' active = False elif index == 2: tier_name = name + '-late' active = False if PriceTier.get_by_name('general', 'name', tier_name): continue pt = PriceTier(name=tier_name, capacity_max=price_cap, personal_limit=10, parent=product, active=active) Price(currency='GBP', price_int=gbp * 100, price_tier=pt) Price(currency='EUR', price_int=eur * 100, price_tier=pt) ProductViewProduct(view, product, order) order += 1 db.session.flush() misc = [ # name, display_name, cap, personal_limit, gbp, eur, description ('parking', 'Parking Ticket', 1700, 4, 15, 21, "We're trying to keep cars to a minimum. Please take public transport or car-share if you can."), ('campervan', 'Caravan/\u200cCampervan Ticket', 60, 2, 30, 42, "If you bring a caravan, you won't need a separate parking ticket for the towing car."), ] for name, display_name, cap, personal_limit, gbp, eur, description in misc: if Product.get_by_name(name, name): continue group = ProductGroup.get_by_name(name) product = Product(name=name, display_name=display_name, description=description, parent=group) pt = PriceTier(name=name, personal_limit=personal_limit, parent=product) db.session.add(pt) db.session.add(Price(currency='GBP', price_int=gbp * 100, price_tier=pt)) db.session.add(Price(currency='EUR', price_int=eur * 100, price_tier=pt)) ProductViewProduct(view, product, order) order += 1 db.session.commit()
class PurchaseTest(unittest.TestCase): user_email_template = '{}@test.invalid' group_template = 'pg{}' product_name = 'product' tier_name = 'tier' def setUp(self): self.client, self.app, self.db = get_app() self.app.testing = True def create_fixtures(self): self.user_email = self.user_email_template.format(random_string(8)) self.user = User(self.user_email, 'test_user') self.db.session.add(self.user) self.group_name = self.group_template.format(random_string(8)) self.group = ProductGroup(type='admissions', name=self.group_name) self.product = Product(name=self.product_name, capacity_max=3, parent=self.group) self.tier = PriceTier(name=self.tier_name, parent=self.product) self.price = Price(price_tier=self.tier, currency='GBP', price_int=666) # These have `cascade=all` so just add the bottom of the hierarchy self.db.session.add(self.price) self.db.session.commit() def create_purchases(self, tier, count): basket = Basket(self.user, 'GBP') basket[tier] = count basket.create_purchases() basket.ensure_purchase_capacity() payment = basket.create_payment(BankPayment) assert len(payment.purchases) == count self.db.session.commit() return payment.purchases def test_create_purchases(self): with self.app.app_context(): self.create_fixtures() assert self.tier.capacity_used == 0 purchases = self.create_purchases(self.tier, 1) purchase = purchases[0] assert self.tier.capacity_used == 1 assert self.product.capacity_used == 1 # NB: Decimal('6.66') != Decimal(6.66) == Decimal(float(6.66)) ~= 6.6600000000000001 assert purchase.price.value == Decimal('6.66') # Test issuing multiple instances works new_purchases = self.create_purchases(self.tier, 2) assert len(new_purchases) == 2 assert self.product.capacity_used == 3 # Test issuing beyond capacity errors with pytest.raises(CapacityException): self.create_purchases(self.tier, 1) def test_purchase_state_machine(self): states_dict = PURCHASE_STATES # 'reserved' is the start state, all other states must # exist as the next_state of some state. # e.g. "payment-pending" and "paid" are next states for # "reserved" and "payment-pending" respectively. self.assertIn('reserved', states_dict) seen_in_next_states = list(states_dict.keys()) seen_in_next_states.remove('reserved') for state in states_dict: next_states = states_dict[state] for allowed_state in next_states: self.assertIn(allowed_state, states_dict) if allowed_state in seen_in_next_states: seen_in_next_states.remove(allowed_state) self.assertEqual(0, len(seen_in_next_states), 'Found unreachable states: %s' % seen_in_next_states) def test_set_state(self): with self.app.app_context(): self.create_fixtures() purchases = self.create_purchases(self.tier, 1) purchase = purchases[0] with self.assertRaises(PurchaseStateException): purchase.set_state('disallowed-state') with self.assertRaises(PurchaseStateException): purchase.set_state('receipt-emailed') purchase.set_state('payment-pending') self.assertEqual('payment-pending', purchase.state) def test_product_group_get_counts_by_state(self): with self.app.app_context(): self.create_fixtures() # Test it works at the PriceTier level purchases = self.create_purchases(self.tier, 1) purchase1 = purchases[0] expected = { 'reserved': 1, } assert self.tier.purchase_count_by_state == expected assert self.product.purchase_count_by_state == expected assert self.group.purchase_count_by_state == expected # Test that other states show up purchase1.set_state('payment-pending') self.db.session.commit() expected = { 'payment-pending': 1, } assert self.tier.purchase_count_by_state == expected assert self.product.purchase_count_by_state == expected assert self.group.purchase_count_by_state == expected # Add another purchase in another tier self.tier2 = PriceTier(name='2', parent=self.product) self.price = Price(price_tier=self.tier2, currency='GBP', price_int=666) self.db.session.commit() self.create_purchases(self.tier2, 1) assert self.tier.purchase_count_by_state == expected expected = { 'payment-pending': 1, 'reserved': 1, } assert self.product.purchase_count_by_state == expected assert self.group.purchase_count_by_state == expected @unittest.skip('This appears to be a test for an otherwise unused function' ) def test_get_purchase_count(self): with self.app.app_context(): self.create_fixtures() # Test it works at the PriceTier level purchases = self.create_purchases(self.tier, 1) purchase = purchases[0] purchase.state = 'paid' self.db.session.commit() assert self.tier.get_purchase_count() == 1 assert self.group.get_purchase_count() == 1 def test_check_in(self): with self.app.app_context(): self.create_fixtures() purchases = self.create_purchases(self.tier, 1) purchase = purchases[0] purchase.state = 'receipt-emailed' assert purchase.checked_in is False purchase.check_in() assert purchase.checked_in is True
def create_product_groups(): top_level_groups = [ # name, capacity, expires ('admissions', datetime(2018, 9, 3), app.config.get('MAXIMUM_ADMISSIONS')), ('parking', datetime(2018, 9, 3), None), ('campervan', datetime(2018, 9, 3), None), ('merchandise', datetime(2018, 8, 12), None), ] for name, expires, capacity in top_level_groups: if ProductGroup.get_by_name(name): continue pg = ProductGroup(name=name, type=name, capacity_max=capacity, expires=expires) db.session.add(pg) db.session.flush() allocations = [ # name, capacity ('vendors', 100), ('sponsors', 200), ('speakers', 100), ('general', 800), ] admissions = ProductGroup.get_by_name('admissions') for name, capacity in allocations: if ProductGroup.get_by_name(name): continue ProductGroup(name=name, capacity_max=capacity, parent=admissions) view = ProductView.get_by_name('main') if not view: view = ProductView(name='main', type='tickets') db.session.add(view) db.session.flush() general = ProductGroup.get_by_name('general') products = [ # name, display name, transferable, badge, capacity, description, (std cap, gbp eur), (early cap, gbp, eur), (late cap, gbp, eur) ('full', 'Full Camp Ticket', True, True, None, 'Full ticket', ((1500, 115, 135), (250, 105, 125), (None, 125, 145))), ('full-s', 'Full Camp Ticket (Supporter)', True, True, None, 'Support this non-profit event by paying a bit more. All money will go towards making EMF more awesome.', ((None, 150, 180), )), ('full-sg', 'Full Camp Ticket (Gold Supporter)', True, True, None, 'Support this non-profit event by paying a bit more. All money will go towards making EMF more awesome.', ((None, 200, 240), )), ('u18', 'Under-18', True, False, 150, 'For visitors born after August 30th, 2000. All under-18s must be accompanied by an adult.', ((None, 55, 63), )), ('u12', 'Under-12', True, False, 50, 'For children born after August 30th, 2006. All children must be accompanied by an adult.', ((None, 0, 0), )), ] order = 0 for name, display_name, has_xfer, has_badge, capacity, description, prices in products: if Product.get_by_name('general', name): continue product = Product(name=name, display_name=display_name, capacity_max=capacity, description=description, parent=general, attributes={ 'is_transferable': has_xfer, 'has_badge': has_badge }) for index, (price_cap, gbp, eur) in enumerate(prices): if len(prices) == 1 or index == 0: tier_name = name + '-std' active = True elif index == 1: tier_name = name + '-early-bird' active = False elif index == 2: tier_name = name + '-late' active = False if PriceTier.get_by_name('general', 'name', tier_name): continue pt = PriceTier(name=tier_name, capacity_max=price_cap, personal_limit=10, parent=product, active=active) Price(currency='GBP', price_int=gbp * 100, price_tier=pt) Price(currency='EUR', price_int=eur * 100, price_tier=pt) ProductViewProduct(view, product, order) order += 1 db.session.flush() misc = [ # name, display_name, cap, personal_limit, gbp, eur, description ('parking', 'Parking Ticket', 1700, 4, 15, 21, "We're trying to keep cars to a minimum. Please take public transport or car-share if you can." ), ('campervan', 'Caravan/\u200cCampervan Ticket', 60, 2, 30, 42, "If you bring a caravan, you won't need a separate parking ticket for the towing car." ), ] for name, display_name, cap, personal_limit, gbp, eur, description in misc: if Product.get_by_name(name, name): continue group = ProductGroup.get_by_name(name) product = Product(name=name, display_name=display_name, description=description, parent=group) pt = PriceTier(name=name, personal_limit=personal_limit, parent=product) db.session.add(pt) db.session.add( Price(currency='GBP', price_int=gbp * 100, price_tier=pt)) db.session.add( Price(currency='EUR', price_int=eur * 100, price_tier=pt)) ProductViewProduct(view, product, order) order += 1 db.session.commit()
class SingleProductGroupTest(unittest.TestCase): item_template = 'killer_tent{}' def setUp(self): self.client, self.app, self.db = get_app() self.app.testing = True def create_fixtures(self): self.item_name = self.item_template.format(random_string(8)) self.item = ProductGroup(type='tent', name=self.item_name, capacity_max=1, expires=datetime(2012, 8, 31)) self.db.session.add(self.item) self.db.session.commit() def create_purchases(self, tier, count): basket = Basket(self.user, 'GBP') basket[tier] = count basket.create_purchases() basket.ensure_purchase_capacity() payment = basket.create_payment(BankPayment) assert len(payment.purchases) == count self.db.session.commit() return payment.purchases def test_has_capacity(self): with self.app.app_context(): self.create_fixtures() # With no parent this is a trivial test self.assertTrue(self.item.has_capacity()) with self.assertRaises(ValueError): self.item.has_capacity(-1) @unittest.skip('not how it works any more') def test_has_expired(self): with self.app.app_context(): self.create_fixtures() with patch('models.mixins.datetime') as mock_good_datetime: mock_good_datetime.utcnow = Mock( return_value=datetime(2012, 8, 2)) self.assertFalse(self.item.has_expired()) with patch('models.mixins.datetime') as mock_expired_datetime: mock_expired_datetime.utcnow = Mock( return_value=datetime(2012, 9, 2)) self.assertTrue(self.item.has_expired()) @unittest.skip('not how it works any more') def test_reserve_tickets(self): with self.app.app_context(): self.create_fixtures() # Will raise an error if we try to issue once expired with patch('models.mixins.datetime') as mock_expired_datetime: mock_expired_datetime.utcnow = Mock( return_value=datetime(2012, 9, 2)) with self.assertRaises(CapacityException): self.create_purchases(item, 1) # Now test with a good value for now() with patch('models.mixins.datetime') as mock_good_datetime: mock_good_datetime.utcnow = Mock( return_value=datetime(2012, 8, 2)) self.create_purchases(item, 1) self.db.session.commit() self.assertFalse(self.item.has_capacity()) with self.assertRaises(CapacityException): self.create_purchases(item, 1) def test_capacity_remaining(self): with self.app.app_context(): self.create_fixtures() self.assertEqual(self.item.capacity_max, self.item.get_total_remaining_capacity()) self.item.capacity_used = self.item.capacity_max self.db.session.commit() self.assertEqual(0, self.item.get_total_remaining_capacity())
class MultipleProductGroupTest(unittest.TestCase): user_email_template = '{}@test.invalid' group_template = 'group{}' product1_name = 'product' product2_name = 'product2' tier1_1_name = 'tier1' tier1_2_name = 'tier2' tier3_name = 'tier3' def setUp(self): self.client, self.app, self.db = get_app() self.app.testing = True def create_fixtures(self): self.user_email = self.user_email_template.format(random_string(8)) self.user = User(self.user_email, 'test_user') self.db.session.add(self.user) self.group_name = self.group_template.format(random_string(8)) self.group = ProductGroup(type='test', name=self.group_name, capacity_max=10) self.product1 = Product(name=self.product1_name, parent=self.group, capacity_max=3) self.tier1_1 = PriceTier(name=self.tier1_1_name, parent=self.product1) self.price1_1 = Price(price_tier=self.tier1_1, currency='GBP', price_int=10) self.db.session.add(self.tier1_1) self.tier1_2 = PriceTier(name=self.tier1_2_name, parent=self.product1) self.price1_2 = Price(price_tier=self.tier1_2, currency='GBP', price_int=20) self.db.session.add(self.tier1_2) self.product2 = Product(name=self.product2_name, parent=self.group) self.tier3 = PriceTier(name=self.tier3_name, parent=self.product2) self.price3 = Price(price_tier=self.tier3, currency='GBP', price_int=30) self.db.session.add(self.tier3) self.db.session.commit() def create_purchases(self, tier, count): basket = Basket(self.user, 'GBP') basket[tier] = count basket.create_purchases() basket.ensure_purchase_capacity() payment = basket.create_payment(BankPayment) assert len(payment.purchases) == count self.db.session.commit() return payment.purchases def test_capacity_propagation(self): with self.app.app_context(): self.create_fixtures() # Check all our items have the correct initial capacity assert self.group.get_total_remaining_capacity() == 10 assert self.product1.get_total_remaining_capacity() == 3 assert self.tier1_1.get_total_remaining_capacity() == 3 assert self.tier1_2.get_total_remaining_capacity() == 3 assert self.product2.get_total_remaining_capacity() == 10 assert self.tier3.get_total_remaining_capacity() == 10 # Issue three instances to exhaust product1 self.create_purchases(self.tier1_1, 3) self.db.session.commit() # Now we shouldn't be able to issue any more tickets from this product with pytest.raises(CapacityException): self.create_purchases(self.tier1_1, 1) with pytest.raises(CapacityException): self.create_purchases(self.tier1_2, 1) # All the capacity went from product1 assert self.tier1_1.get_total_remaining_capacity() == 0 assert self.tier1_2.get_total_remaining_capacity() == 0 assert self.product1.get_total_remaining_capacity() == 0 # product2 still has capacity but is limited by the parent assert self.group.get_total_remaining_capacity() == 7 assert self.product2.get_total_remaining_capacity() == 7 assert self.tier3.get_total_remaining_capacity() == 7 def test_get_cheapest(self): with self.app.app_context(): self.create_fixtures() price1 = Price(price_tier=self.tier1_1, currency='GBP', price_int=5) price2 = Price(price_tier=self.tier1_2, currency='GBP', price_int=500) self.db.session.add(price1) self.db.session.add(price2) self.db.session.commit() assert price1 == self.product1.get_cheapest_price('GBP')
def create_product_groups(): top_level_groups = [ # name, capacity, expires ("admissions", None, 2500), ("parking", None, None), ("campervan", None, None), ("merchandise", None, None), ] for name, expires, capacity in top_level_groups: if ProductGroup.get_by_name(name): continue pg = ProductGroup(name=name, type=name, capacity_max=capacity, expires=expires) db.session.add(pg) db.session.flush() allocations = [ # name, capacity ("vendors", 100), ("sponsors", 200), ("speakers", 100), ("general", 1800), ] admissions = ProductGroup.get_by_name("admissions") for name, capacity in allocations: if ProductGroup.get_by_name(name): continue ProductGroup(name=name, capacity_max=capacity, parent=admissions) view = ProductView.get_by_name("main") if not view: view = ProductView(name="main", type="tickets") db.session.add(view) db.session.flush() general = ProductGroup.get_by_name("general") products = [ # name, display name, transferable, badge, capacity, description, (std cap, gbp eur), (early cap, gbp, eur), (late cap, gbp, eur) ( "full", "Full Camp Ticket", True, True, None, "Full ticket", ((1500, 115, 135), (250, 105, 125), (None, 125, 145)), ), ( "full-s", "Full Camp Ticket (Supporter)", True, True, None, "Support this non-profit event by paying a bit more. All money will go towards making EMF more awesome.", ((None, 150, 180), ), ), ( "full-sg", "Full Camp Ticket (Gold Supporter)", True, True, None, "Support this non-profit event by paying a bit more. All money will go towards making EMF more awesome.", ((None, 200, 240), ), ), ( "u18", "Under-18", True, False, 150, "For visitors born after August 30th, 2000. All under-18s must be accompanied by an adult.", ((None, 55, 63), ), ), ( "u12", "Under-12", True, False, 50, "For children born after August 30th, 2006. All children must be accompanied by an adult.", ((None, 0, 0), ), ), ] order = 0 for ( name, display_name, has_xfer, has_badge, capacity, description, prices, ) in products: if Product.get_by_name("general", name): continue product = Product( name=name, display_name=display_name, capacity_max=capacity, description=description, parent=general, attributes={ "is_transferable": has_xfer, "has_badge": has_badge }, ) for index, (price_cap, gbp, eur) in enumerate(prices): if len(prices) == 1 or index == 0: tier_name = name + "-std" active = True elif index == 1: tier_name = name + "-early-bird" active = False elif index == 2: tier_name = name + "-late" active = False if PriceTier.get_by_name("general", "name", tier_name): continue pt = PriceTier( name=tier_name, capacity_max=price_cap, personal_limit=10, parent=product, active=active, ) Price(currency="GBP", price_int=gbp * 100, price_tier=pt) Price(currency="EUR", price_int=eur * 100, price_tier=pt) ProductViewProduct(view, product, order) order += 1 db.session.flush() misc = [ # name, display_name, cap, personal_limit, gbp, eur, description ( "parking", "Parking Ticket", 1700, 4, 15, 21, "We're trying to keep cars to a minimum. Please take public transport or car-share if you can.", ), ( "campervan", "Caravan/\u200cCampervan Ticket", 60, 2, 30, 42, "If you bring a caravan, you won't need a separate parking ticket for the towing car.", ), ] for name, display_name, cap, personal_limit, gbp, eur, description in misc: if Product.get_by_name(name, name): continue group = ProductGroup.get_by_name(name) product = Product(name=name, display_name=display_name, description=description, parent=group) pt = PriceTier(name=name, personal_limit=personal_limit, parent=product) db.session.add(pt) db.session.add( Price(currency="GBP", price_int=gbp * 100, price_tier=pt)) db.session.add( Price(currency="EUR", price_int=eur * 100, price_tier=pt)) ProductViewProduct(view, product, order) order += 1 db.session.commit()