Exemplo n.º 1
0
def _is_eligible_for_REV1074_experiment(request, sku):
    """
    For https://openedx.atlassian.net/browse/REV-1074 we are testing a mostly hardcoded version of the checkout page.
    We are trying to improve performance and measure if there is an effect on revenue.
    In order to improve performance and simplify the engineering work, many use cases are not being handled.
    These use cases will all need to be omitted from the experiment and sent to the regular checkout page
    """
    basket = request.basket
    user_agent = request.META.get('HTTP_USER_AGENT')
    omit = (
        # We applied filters to our list of courses and only some courses will be included in the experiment
        sku not in SKUS_IN_EXPERIMENT or
        # The static page doesn't support offers/coupons so baskets with either are omitted from the experiment
        basket.applied_offers() or basket.voucher_discounts
        or basket.total_discount or
        # Optimizely is being used by the mobile app on the checkout page.
        # We are removing optimizely from the static version of the page,
        # so we are omitting mobile app traffic from this experiment
        (user_agent and re.search(r'edX/org.edx.mobile', user_agent)) or
        # Bundles would add substantial additional complexity to the experiment so we are omitting bundles
        basket.num_items > 1 or
        # The static page only supports seat products
        not basket.lines.first().product.is_seat_product or
        # The static page is not supporting enterprise use cases so enterprise learners need to be excluded
        # Excluding all offers and coupons above should handle most enterprise use cases
        # This check should handle enterprise users
        getattr(request.basket, 'ENTERPRISE_CATALOG_ATTRIBUTE_TYPE', None)
        or get_enterprise_id_for_user(basket.site, basket.owner) or
        # We do not want to include zero dollar purchases
        request.basket.total_incl_tax == 0
        or str(request.user.username).startswith('test_')
        and str(request.user.email).endswith('example.com'))
    return not omit
Exemplo n.º 2
0
 def test_get_enterprise_id_for_user_enterprise_in_jwt(
         self, mock_get_jwt_uuid):
     """
     Verify get_enterprise_id_for_user returns ent id if uuid in jwt context
     """
     mock_get_jwt_uuid.return_value = 'my-uuid'
     assert get_enterprise_id_for_user('some-site',
                                       self.learner) == 'my-uuid'
Exemplo n.º 3
0
    def test_get_enterprise_id_for_user_fetch_errors(self, mock_get_jwt_uuid, mock_fetch):
        """
        Verify if that learner data fetch errors, get_enterprise_id_for_user
        returns None
        """
        mock_get_jwt_uuid.return_value = None
        mock_fetch.side_effect = [KeyError]

        assert enterprise_api.get_enterprise_id_for_user('some-site', self.learner) is None
Exemplo n.º 4
0
 def test_get_enterprise_id_for_user_no_uuid_in_response(self, mock_get_jwt_uuid, mock_fetch):
     """
     Verify if learner data fetch is successful but does not include uuid field,
     None is returned
     """
     mock_get_jwt_uuid.return_value = None
     mock_fetch.return_value = {
         'results': []
     }
     assert enterprise_api.get_enterprise_id_for_user('some-site', self.learner) is None
Exemplo n.º 5
0
    def _get_enterprise_offers(self, site, user):
        """
        Return enterprise offers filtered by the user's enterprise, if it exists.
        """
        enterprise_id = get_enterprise_id_for_user(site, user)
        if enterprise_id:
            ConditionalOffer = get_model('offer', 'ConditionalOffer')
            offers = ConditionalOffer.active.filter(
                offer_type=ConditionalOffer.SITE,
                condition__enterprise_customer_uuid=enterprise_id)
            return offers.select_related('condition', 'benefit')

        return []
Exemplo n.º 6
0
 def test_get_enterprise_id_for_user_fetch_learner_data_has_uuid(self, mock_get_jwt_uuid, mock_fetch):
     """
     Verify get_enterprise_id_for_user returns enterprise id if jwt does not have
     enterprise uuid, but is able to fetch it via api call
     """
     mock_get_jwt_uuid.return_value = None
     mock_fetch.return_value = {
         'results': [
             {
                 'enterprise_customer': {
                     'uuid': 'my-uuid'
                 }
             }
         ]
     }
     assert enterprise_api.get_enterprise_id_for_user('some-site', self.learner) == 'my-uuid'
Exemplo n.º 7
0
    def is_satisfied(self, offer, basket):  # pylint: disable=unused-argument
        """
        Determines if a user is eligible for an enterprise customer offer
        based on their association with the enterprise customer.

        It also filter out the offer if the `enterprise_customer_catalog_uuid`
        value set on the offer condition does not match with the basket catalog
        value when explicitly provided by the enterprise learner.

        Note: Currently there is no mechanism to prioritize or apply multiple
        offers that may apply as opposed to disqualifying offers if the
        catalog doesn't explicitly match.

        Arguments:
            basket (Basket): Contains information about order line items, the current site,
                             and the user attempting to make the purchase.
        Returns:
            bool
        """
        if not basket.owner:
            # An anonymous user is never linked to any EnterpriseCustomer.
            return False

        enterprise_in_condition = str(self.enterprise_customer_uuid)
        enterprise_catalog = str(self.enterprise_customer_catalog_uuid) if self.enterprise_customer_catalog_uuid \
            else None
        enterprise_name_in_condition = str(self.enterprise_customer_name)
        username = basket.owner.username
        course_run_ids = []
        for line in basket.all_lines():
            course = line.product.course
            if not course:
                # Basket contains products not related to a course_run.
                # Only log for non-site offers to avoid noise.
                if offer.offer_type != ConditionalOffer.SITE:
                    logger.warning(
                        '[Code Redemption Failure] Unable to apply enterprise offer because '
                        'the Basket contains a product not related to a course_run. '
                        'User: %s, Offer: %s, Product: %s, Enterprise: %s, Catalog: %s',
                        username, offer.id, line.product.id,
                        enterprise_in_condition, enterprise_catalog)
                return False

            course_run_ids.append(course.id)

        courses_in_basket = ','.join(course_run_ids)
        user_enterprise = get_enterprise_id_for_user(basket.site, basket.owner)
        if user_enterprise and enterprise_in_condition != user_enterprise:
            # Learner is not linked to the EnterpriseCustomer associated with this condition.
            if offer.offer_type == ConditionalOffer.VOUCHER:
                logger.warning(
                    '[Code Redemption Failure] Unable to apply enterprise offer because Learner\'s '
                    'enterprise (%s) does not match this conditions\'s enterprise (%s). '
                    'User: %s, Offer: %s, Enterprise: %s, Catalog: %s, Courses: %s',
                    user_enterprise, enterprise_in_condition, username,
                    offer.id, enterprise_in_condition, enterprise_catalog,
                    courses_in_basket)

                logger.info(
                    '[Code Redemption Issue] Linking learner with the enterprise in Condition. '
                    'User [%s], Enterprise [%s]', username,
                    enterprise_in_condition)
                get_or_create_enterprise_customer_user(
                    basket.site, enterprise_in_condition, username, False)
                msg = _(
                    'This coupon has been made available through {new_enterprise}. '
                    'To redeem this coupon, you must first logout. When you log back in, '
                    'please select {new_enterprise} as your enterprise '
                    'and try again.').format(
                        new_enterprise=enterprise_name_in_condition)
                messages.warning(
                    crum.get_current_request(),
                    msg,
                )

            return False

        # Verify that the current conditional offer is related to the provided
        # enterprise catalog, this will also filter out offers which don't
        # have `enterprise_customer_catalog_uuid` value set on the condition.
        catalog = self._get_enterprise_catalog_uuid_from_basket(basket)
        if catalog:
            if offer.condition.enterprise_customer_catalog_uuid != catalog:
                logger.warning(
                    'Unable to apply enterprise offer %s because '
                    'Enterprise catalog id on the basket (%s) '
                    'does not match the catalog for this condition (%s).',
                    offer.id, catalog,
                    offer.condition.enterprise_customer_catalog_uuid)
                return False

        try:
            catalog_contains_course = catalog_contains_course_runs(
                basket.site,
                course_run_ids,
                enterprise_in_condition,
                enterprise_customer_catalog_uuid=enterprise_catalog)
        except (ReqConnectionError, KeyError, SlumberHttpBaseException,
                Timeout) as exc:
            logger.exception(
                '[Code Redemption Failure] Unable to apply enterprise offer because '
                'we failed to check if course_runs exist in the catalog. '
                'User: %s, Offer: %s, Message: %s, Enterprise: %s, Catalog: %s, Courses: %s',
                username, offer.id, exc, enterprise_in_condition,
                enterprise_catalog, courses_in_basket)
            return False

        if not catalog_contains_course:
            # Basket contains course runs that do not exist in the EnterpriseCustomerCatalogs
            # associated with the EnterpriseCustomer.
            logger.warning(
                '[Code Redemption Failure] Unable to apply enterprise offer because '
                'Enterprise catalog does not contain the course(s) in this basket. '
                'User: %s, Offer: %s, Enterprise: %s, Catalog: %s, Courses: %s',
                username, offer.id, enterprise_in_condition,
                enterprise_catalog, courses_in_basket)
            return False

        if not is_offer_max_discount_available(basket, offer):
            logger.warning(
                '[Enterprise Offer Failure] Unable to apply enterprise offer because bookings limit is consumed.'
                'User: %s, Offer: %s, Enterprise: %s, Catalog: %s, Courses: %s, BookingsLimit: %s, TotalDiscount: %s',
                username,
                offer.id,
                enterprise_in_condition,
                enterprise_catalog,
                courses_in_basket,
                offer.max_discount,
                offer.total_discount,
            )
            return False

        if not is_offer_max_user_discount_available(basket, offer):
            logger.warning(
                '[Enterprise Offer Failure] Unable to apply enterprise offer because user bookings limit is consumed.'
                'User: %s, Offer: %s, Enterprise: %s, Catalog: %s, Courses: %s, UserBookingsLimit: %s',
                username, offer.id, enterprise_in_condition,
                enterprise_catalog, courses_in_basket, offer.max_user_discount)
            return False

        return True