Beispiel #1
0
    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
Beispiel #2
0
    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
Beispiel #3
0
 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
Beispiel #4
0
    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
Beispiel #5
0
    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
Beispiel #6
0
    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)
Beispiel #7
0
    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)