def is_satisfied(self, offer, basket): # pylint: disable=unused-argument """ Determines if a user is eligible for a journal bundle offer based on the products in their basket. Args: basket: contains information on line items for order, associated siteconfiguration and associated user Returns: bool: True if condition is met """ try: # TODO: WL-1680: All calls from ecommerce to other services should be async self.journal_bundle = fetch_journal_bundle( site=basket.site, journal_bundle_uuid=self.journal_bundle_uuid) except (HttpNotFoundError, SlumberBaseException, Timeout): return False if not self.journal_bundle: return False # self._basket_contains_all_required_courses and self._basket_contains_all_journals require self.basket_skus to # be set before they are called. self.basket_skus = set( [line.stockrecord.partner_sku for line in basket.all_lines()]) if not self._basket_contains_all_required_courses(): return False if not self._basket_contains_all_journals(): return False return True
def get_applicable_skus(self, site): """ Returns set of SKUs to which this condition applies. """ journal_bundle_skus = set() # TODO: WL-1680: All calls from ecommerce to other services should be async self.journal_bundle = fetch_journal_bundle( site=site, journal_bundle_uuid=self.journal_bundle_uuid) journal_bundle_skus.update( self.get_applicable_course_skus(return_set=True)) journal_bundle_skus.update(self.get_applicable_journal_skus()) return journal_bundle_skus
def get_context_data(self, **kwargs): context = super(JournalBundleOfferUpdateView, self).get_context_data(**kwargs) context.update({ 'editing': True, 'journal_bundle': fetch_journal_bundle( site=self.request.site, journal_bundle_uuid=self.object.condition.journal_bundle_uuid) }) return context
def get_context_data(self, **kwargs): context = super(JournalBundleOfferListView, self).get_context_data(**kwargs) offers = [] for offer in context['object_list']: offer.journal_bundle = fetch_journal_bundle( site=self.request.site, journal_bundle_uuid=offer.condition.journal_bundle_uuid) offers.append(offer) context['offers'] = offers return context
def get_context_data(self, **kwargs): context = super(JournalBundleOfferListView, self).get_context_data(**kwargs) offers = [] # context['object_list'] returns all conditional offers (including enterprise and program) # we only want to pass journal bundle offers to the context, so ignore all offers that do not # have a journal bundle uuid for offer in context['object_list']: if offer.condition.journal_bundle_uuid: offer.journal_bundle = fetch_journal_bundle( site=self.request.site, journal_bundle_uuid=offer.condition.journal_bundle_uuid) offers.append(offer) context['offers'] = offers return context
def save(self, commit=True): journal_bundle_uuid = self.cleaned_data['journal_bundle_uuid'] site = self.request.site journal_bundle = fetch_journal_bundle( site=site, journal_bundle_uuid=journal_bundle_uuid ) offer_name = 'Journal Bundle Offer: {title}'.format( title=journal_bundle['title'] ) # Truncate offer_names down to 128 characters, as Oscar's AbstractConditionalOffer name is max_length 128 offer_name = (offer_name[:125] + '...') if len(offer_name) > 128 else offer_name # pylint: disable=unsubscriptable-object self.instance.name = offer_name self.instance.status = ConditionalOffer.OPEN self.instance.offer_type = ConditionalOffer.SITE self.instance.max_basket_applications = 1 self.instance.site = site if commit: benefit = getattr(self.instance, 'benefit', Benefit()) benefit.proxy_class = class_path(BENEFIT_MAP[self.cleaned_data['benefit_type']]) benefit.value = self.cleaned_data['benefit_value'] benefit.save() self.instance.benefit = benefit if hasattr(self.instance, 'condition'): self.instance.condition.journal_bundle_uuid = journal_bundle_uuid self.instance.condition.save() else: self.instance.condition = create_condition( JournalBundleCondition, journal_bundle_uuid=journal_bundle_uuid ) return super(JournalBundleOfferForm, self).save(commit)
def test_fetch_journal_bundle(self): """ Test 'fetch_journal_bundle' properly calls API and uses cache """ # The first time it is called the journal discovery api should get hit # and store the journal bundle in the cache # The second time the api should not be called, the bundle should be retrieved from the cache self.mock_access_token_response() test_bundle = { "uuid": "4786e7be-2390-4332-a20e-e24895c38109", "title": "Transfiguration Bundle", "partner": "edX", "journals": [{ "uuid": "a3db3f6e-f290-4eae-beea-873034c5a967", "partner": "edx", "organization": "edX", "title": "Intermediate Transfiguration", "price": "120.00", "currency": "USD", "sku": "88482D8", "card_image_url": "http://localhost:18606/media/original_images/transfiguration.jpg", "short_description": "Turning things into different things!", "full_description": "", "access_length": 365, "status": "active", "slug": "intermediate-transfiguration-about-page" }], "courses": [{ "key": "HogwartsX+TR301", "uuid": "6d7c2805-ec9c-4961-8b0d-c8d608cc948e", "title": "Transfiguration 301", "course_runs": [{ "key": "course-v1:HogwartsX+TR301+TR301_2014", "uuid": "ddaa84ce-e99c-4e3d-a3ca-7d5b4978b43b", "title": "Transfiguration 301", "image": 'fake_image_url', "short_description": 'fake_description', "marketing_url": 'fake_marketing_url', "seats": [], "start": "2030-01-01T00:00:00Z", "end": "2040-01-01T00:00:00Z", "enrollment_start": "2020-01-01T00:00:00Z", "enrollment_end": "2040-01-01T00:00:00Z", "pacing_type": "instructor_paced", "type": "fake_course_type", "status": "published" }], "entitlements": [], "owners": [{ "uuid": "becfbab0-c78d-42f1-b44e-c92abb99011a", "key": "HogwartsX", "name": "" }], "image": "fake_image_url", "short_description": "fake_description" }], "applicable_seat_types": ["verified"] } journal_bundle_uuid = test_bundle['uuid'] test_url = urljoin(self.journal_discovery_url, 'journal_bundles/{}/'.format(journal_bundle_uuid)) responses.add(responses.GET, test_url, json=test_bundle, status=200) # First call, should hit journal discovery api and store in cache journal_bundle_response = fetch_journal_bundle( site=self.site, journal_bundle_uuid=journal_bundle_uuid) # The first call (response.calls[0]) is to get post the access token # The second call (response.calls[1]) is the 'fetch_journal_bundle' call self.assertEqual(len(responses.calls), 2, "Incorrect number of API calls") self.assertEqual(journal_bundle_response, test_bundle) # check that the journal bundle was stored in the cache cache_key = get_cache_key(site_domain=self.site.domain, resource='journal_bundle', journal_bundle_uuid=journal_bundle_uuid) journal_bundle_cached_response = TieredCache.get_cached_response( cache_key) self.assertTrue(journal_bundle_cached_response is not None) self.assertEqual(journal_bundle_cached_response.value, test_bundle) # Call 'fetch_journal_bundle' again, the api should not get hit again and response should be the same journal_bundle_response = fetch_journal_bundle( site=self.site, journal_bundle_uuid=journal_bundle_uuid) self.assertEqual(len(responses.calls), 2, "Should have hit cache, not called API") self.assertEqual(journal_bundle_response, test_bundle)