def test_cache_clears_at_batch_exit(self): with BatchController.batch(self.USER_1): cache_1 = BatchController.get_cache(self.USER_1) cache_2 = BatchController.get_cache(self.USER_1) self.assertIsNot(cache_1, cache_2)
def test_cart_discounts_only_calculated_at_end_of_batches(self): def count_discounts(cart): return cart.cart.discountitem_set.count() cart = TestingCartController.for_user(self.USER_1) self.make_discount_ceiling("FLOOZLE") count_0 = count_discounts(cart) with BatchController.batch(self.USER_1): # Memoise the cart same_cart = TestingCartController.for_user(self.USER_1) with BatchController.batch(self.USER_1): # Memoise the cart same_cart_2 = TestingCartController.for_user(self.USER_1) same_cart_2.add_to_cart(self.PROD_1, 1) count_1 = count_discounts(same_cart_2) count_2 = count_discounts(same_cart) count_3 = count_discounts(cart) self.assertEqual(0, count_0) self.assertEqual(0, count_1) self.assertEqual(0, count_2) self.assertEqual(1, count_3)
def test_caches_are_independent_for_different_users(self): with BatchController.batch(self.USER_1): cache_1 = BatchController.get_cache(self.USER_1) with BatchController.batch(self.USER_2): cache_2 = BatchController.get_cache(self.USER_2) self.assertIsNot(cache_1, cache_2)
def test_memoisation_clears_outside_batches(self): with BatchController.batch(self.USER_1): output_1 = self._memoiseme(self.USER_1) with BatchController.batch(self.USER_1): output_2 = self._memoiseme(self.USER_1) self.assertIsNot(output_1, output_2)
def test_memoisation_happens_in_batch_context(self): with BatchController.batch(self.USER_1): output_1 = self._memoiseme(self.USER_1) with BatchController.batch(self.USER_1): output_2 = self._memoiseme(self.USER_1) self.assertIs(output_1, output_2)
def test_memoisation_is_user_independent(self): with BatchController.batch(self.USER_1): output_1 = self._memoiseme(self.USER_1) with BatchController.batch(self.USER_2): output_2 = self._memoiseme(self.USER_2) output_3 = self._memoiseme(self.USER_1) self.assertIsNot(output_1, output_2) self.assertIs(output_1, output_3)
def test_caches_identical_within_nestings(self): with BatchController.batch(self.USER_1): cache_1 = BatchController.get_cache(self.USER_1) with BatchController.batch(self.USER_2): cache_2 = BatchController.get_cache(self.USER_1) cache_3 = BatchController.get_cache(self.USER_1) self.assertIs(cache_1, cache_2) self.assertIs(cache_2, cache_3)
def test_cart_revision_does_not_increment_if_not_modified(self): cart = TestingCartController.for_user(self.USER_1) rev_0 = cart.cart.revision with BatchController.batch(self.USER_1): # Memoise the cart same_cart = TestingCartController.for_user(self.USER_1) # Do nothing on exit rev_1 = self.reget(cart.cart).revision self.assertEqual(rev_0, rev_1)
def test_cart_revision_does_not_increment_if_not_modified(self): cart = TestingCartController.for_user(self.USER_1) rev_0 = cart.cart.revision with BatchController.batch(self.USER_1): # Memoise the cart TestingCartController.for_user(self.USER_1) # Do nothing on exit rev_1 = self.reget(cart.cart).revision self.assertEqual(rev_0, rev_1)
def test_batch_end_functionality_is_called(self): class Ender(object): end_count = 0 def end_batch(self): self.end_count += 1 @BatchController.memoise def get_ender(user): return Ender() # end_batch should get called once on exiting the batch with BatchController.batch(self.USER_1): ender = get_ender(self.USER_1) self.assertEquals(1, ender.end_count) # end_batch should get called once on exiting the batch # no matter how deep the object gets cached with BatchController.batch(self.USER_1): with BatchController.batch(self.USER_1): ender = get_ender(self.USER_1) self.assertEquals(1, ender.end_count)
def test_cart_revision_only_increments_at_end_of_batches(self): cart = TestingCartController.for_user(self.USER_1) rev_0 = cart.cart.revision with BatchController.batch(self.USER_1): # Memoise the cart same_cart = TestingCartController.for_user(self.USER_1) same_cart.add_to_cart(self.PROD_1, 1) rev_1 = self.reget(same_cart.cart).revision rev_2 = self.reget(cart.cart).revision self.assertEqual(rev_0, rev_1) self.assertNotEqual(rev_0, rev_2)
def test_new_caches_for_new_batches(self): with BatchController.batch(self.USER_1): cache_1 = BatchController.get_cache(self.USER_1) with BatchController.batch(self.USER_1): cache_2 = BatchController.get_cache(self.USER_1) with BatchController.batch(self.USER_1): cache_3 = BatchController.get_cache(self.USER_1) self.assertIs(cache_2, cache_3) self.assertIsNot(cache_1, cache_2)
def guided_registration(request): ''' Goes through the registration process in order, making sure user sees all valid categories. The user must be logged in to see this view. Returns: render: Renders ``registrasion/guided_registration.html``, with the following data:: { "current_step": int(), # The current step in the # registration "sections": sections, # A list of # GuidedRegistrationSections "title": str(), # The title of the page "total_steps": int(), # The total number of steps } ''' SESSION_KEY = "guided_registration_categories" ASK_FOR_PROFILE = 777 # Magic number. Meh. next_step = redirect("guided_registration") sections = [] attendee = people.Attendee.get_instance(request.user) if attendee.completed_registration: return redirect(review) # Step 1: Fill in a badge and collect a voucher code try: profile = attendee.attendeeprofilebase except ObjectDoesNotExist: profile = None # Figure out if we need to show the profile form and the voucher form show_profile_and_voucher = False if SESSION_KEY not in request.session: if not profile: show_profile_and_voucher = True else: if request.session[SESSION_KEY] == ASK_FOR_PROFILE: show_profile_and_voucher = True if show_profile_and_voucher: # Keep asking for the profile until everything passes. request.session[SESSION_KEY] = ASK_FOR_PROFILE voucher_form, voucher_handled = _handle_voucher(request, "voucher") profile_form, profile_handled = _handle_profile(request, "profile") voucher_section = GuidedRegistrationSection( title="Voucher Code", form=voucher_form, ) profile_section = GuidedRegistrationSection( title="Profile and Personal Information", form=profile_form, ) title = "Attendee information" current_step = 1 sections.append(voucher_section) sections.append(profile_section) else: # We're selling products starting = attendee.guided_categories_complete.count() == 0 # Get the next category cats = inventory.Category.objects if SESSION_KEY in request.session: _cats = request.session[SESSION_KEY] cats = cats.filter(id__in=_cats) else: cats = cats.exclude( id__in=attendee.guided_categories_complete.all(), ) cats = cats.order_by("order") request.session[SESSION_KEY] = [] if starting: # Only display the first Category title = "Select ticket type" current_step = 2 cats = [cats[0]] else: # Set title appropriately for remaining categories current_step = 3 title = "Additional items" all_products = inventory.Product.objects.filter( category__in=cats, ).select_related("category") with BatchController.batch(request.user): available_products = set(ProductController.available_products( request.user, products=all_products, )) if len(available_products) == 0: # We've filled in every category attendee.completed_registration = True attendee.save() return next_step for category in cats: products = [ i for i in available_products if i.category == category ] prefix = "category_" + str(category.id) p = _handle_products(request, category, products, prefix) products_form, discounts, products_handled = p section = GuidedRegistrationSection( title=category.name, description=category.description, discounts=discounts, form=products_form, ) if products: # This product category has items to show. sections.append(section) # Add this to the list of things to show if the form # errors. request.session[SESSION_KEY].append(category.id) if request.method == "POST" and not products_form.errors: # This is only saved if we pass each form with no # errors, and if the form actually has products. attendee.guided_categories_complete.add(category) if sections and request.method == "POST": for section in sections: if section.form.errors: break else: attendee.save() if SESSION_KEY in request.session: del request.session[SESSION_KEY] # We've successfully processed everything return next_step data = { "current_step": current_step, "sections": sections, "title": title, "total_steps": 3, } return render(request, "registrasion/guided_registration.html", data)
def test_no_caches_outside_of_batches(self): cache_1 = BatchController.get_cache(self.USER_1) cache_2 = BatchController.get_cache(self.USER_2) # Identity testing is important here self.assertIsNot(cache_1, cache_2)
def product_category(request, category_id): ''' Form for selecting products from an individual product category. Arguments: category_id (castable to int): The id of the category to display. Returns: redirect or render: If the form has been sucessfully submitted, redirect to ``dashboard``. Otherwise, render ``registrasion/product_category.html`` with data:: { "category": category, # An inventory.Category for # category_id "discounts": discounts, # A list of # DiscountAndQuantity "form": products_form, # A form for selecting # products "voucher_form": voucher_form, # A form for entering a # voucher code } ''' PRODUCTS_FORM_PREFIX = "products" VOUCHERS_FORM_PREFIX = "vouchers" # Handle the voucher form *before* listing products. # Products can change as vouchers are entered. v = _handle_voucher(request, VOUCHERS_FORM_PREFIX) voucher_form, voucher_handled = v category_id = int(category_id) # Routing is [0-9]+ category = inventory.Category.objects.get(pk=category_id) with BatchController.batch(request.user): products = ProductController.available_products( request.user, category=category, ) if not products: messages.warning( request, ( "There are no products available from category: " + category.name ), ) return redirect("dashboard") p = _handle_products(request, category, products, PRODUCTS_FORM_PREFIX) products_form, discounts, products_handled = p if request.POST and not voucher_handled and not products_form.errors: # Only return to the dashboard if we didn't add a voucher code # and if there's no errors in the products form if products_form.has_changed(): messages.success( request, "Your reservations have been updated.", ) return redirect(review) data = { "category": category, "discounts": discounts, "form": products_form, "voucher_form": voucher_form, } return render(request, "registrasion/product_category.html", data)
def product_category(request, category_id): ''' Form for selecting products from an individual product category. Arguments: category_id (castable to int): The id of the category to display. Returns: redirect or render: If the form has been sucessfully submitted, redirect to ``dashboard``. Otherwise, render ``registrasion/product_category.html`` with data:: { "category": category, # An inventory.Category for # category_id "discounts": discounts, # A list of # DiscountAndQuantity "form": products_form, # A form for selecting # products "voucher_form": voucher_form, # A form for entering a # voucher code } ''' PRODUCTS_FORM_PREFIX = "products" VOUCHERS_FORM_PREFIX = "vouchers" # Handle the voucher form *before* listing products. # Products can change as vouchers are entered. v = _handle_voucher(request, VOUCHERS_FORM_PREFIX) voucher_form, voucher_handled = v category_id = int(category_id) # Routing is [0-9]+ category = inventory.Category.objects.get(pk=category_id) with BatchController.batch(request.user): products = ProductController.available_products( request.user, category=category, ) if not products: messages.warning( request, "There are no products available from category: " + category.name, ) return redirect("dashboard") p = _handle_products(request, category, products, PRODUCTS_FORM_PREFIX) products_form, discounts, products_handled = p if request.POST and not voucher_handled and not products_form.errors: # Only return to the dashboard if we didn't add a voucher code # and if there's no errors in the products form messages.success( request, "Your reservations have been updated.", ) return redirect("dashboard") data = { "category": category, "discounts": discounts, "form": products_form, "voucher_form": voucher_form, } return render(request, "registrasion/product_category.html", data)
def guided_registration(request): ''' Goes through the registration process in order, making sure user sees all valid categories. The user must be logged in to see this view. Returns: render: Renders ``registrasion/guided_registration.html``, with the following data:: { "current_step": int(), # The current step in the # registration "sections": sections, # A list of # GuidedRegistrationSections "title": str(), # The title of the page "total_steps": int(), # The total number of steps } ''' SESSION_KEY = "guided_registration_categories" ASK_FOR_PROFILE = 777 # Magic number. Meh. next_step = redirect("guided_registration") sections = [] attendee = people.Attendee.get_instance(request.user) if attendee.completed_registration: return render( request, "registrasion/guided_registration_complete.html", {}, ) # Step 1: Fill in a badge and collect a voucher code try: profile = attendee.attendeeprofilebase except ObjectDoesNotExist: profile = None # Figure out if we need to show the profile form and the voucher form show_profile_and_voucher = False if SESSION_KEY not in request.session: if not profile: show_profile_and_voucher = True else: if request.session[SESSION_KEY] == ASK_FOR_PROFILE: show_profile_and_voucher = True if show_profile_and_voucher: # Keep asking for the profile until everything passes. request.session[SESSION_KEY] = ASK_FOR_PROFILE voucher_form, voucher_handled = _handle_voucher(request, "voucher") profile_form, profile_handled = _handle_profile(request, "profile") voucher_section = GuidedRegistrationSection( title="Voucher Code", form=voucher_form, ) profile_section = GuidedRegistrationSection( title="Profile and Personal Information", form=profile_form, ) title = "Attendee information" current_step = 1 sections.append(voucher_section) sections.append(profile_section) else: # We're selling products starting = attendee.guided_categories_complete.count() == 0 # Get the next category cats = inventory.Category.objects if SESSION_KEY in request.session: _cats = request.session[SESSION_KEY] cats = cats.filter(id__in=_cats) else: cats = cats.exclude( id__in=attendee.guided_categories_complete.all(), ) cats = cats.order_by("order") request.session[SESSION_KEY] = [] if starting: # Only display the first Category title = "Select ticket type" current_step = 2 cats = [cats[0]] else: # Set title appropriately for remaining categories current_step = 3 title = "Additional items" all_products = inventory.Product.objects.filter( category__in=cats, ).select_related("category") with BatchController.batch(request.user): available_products = set(ProductController.available_products( request.user, products=all_products, )) if len(available_products) == 0: # We've filled in every category attendee.completed_registration = True attendee.save() return next_step for category in cats: products = [ i for i in available_products if i.category == category ] prefix = "category_" + str(category.id) p = _handle_products(request, category, products, prefix) products_form, discounts, products_handled = p section = GuidedRegistrationSection( title=category.name, description=category.description, discounts=discounts, form=products_form, ) if products: # This product category has items to show. sections.append(section) # Add this to the list of things to show if the form # errors. request.session[SESSION_KEY].append(category.id) if request.method == "POST" and not products_form.errors: # This is only saved if we pass each form with no # errors, and if the form actually has products. attendee.guided_categories_complete.add(category) if sections and request.method == "POST": for section in sections: if section.form.errors: break else: attendee.save() if SESSION_KEY in request.session: del request.session[SESSION_KEY] # We've successfully processed everything return next_step data = { "current_step": current_step, "sections": sections, "title": title, "total_steps": 3, } return render(request, "registrasion/guided_registration.html", data)
def test_cart_controller_for_user_is_memoised(self): # - that for_user is memoised with BatchController.batch(self.USER_1): cart = TestingCartController.for_user(self.USER_1) cart_2 = TestingCartController.for_user(self.USER_1) self.assertIs(cart, cart_2)