def test_flush_with_product_is_not_tracked_for_temporary_basket_calculation(self): """ Verify the method does NOT fire 'Product Removed' Segment for temporary basket calculation """ basket = self._create_basket_with_product() DEFAULT_REQUEST_CACHE.set(TEMPORARY_BASKET_CACHE_KEY, True) with mock.patch.object(Client, 'track') as mock_track: basket.flush() mock_track.assert_not_called()
def test_add_product_not_tracked_for_temporary_basket_calculation(self): """ Verify the method does NOT fire Product Added analytic event when a product is added to the basket """ course = CourseFactory(partner=self.partner) basket = create_basket(empty=True) seat = course.create_or_update_seat('verified', True, 100) DEFAULT_REQUEST_CACHE.set(TEMPORARY_BASKET_CACHE_KEY, True) with mock.patch('ecommerce.extensions.basket.models.track_segment_event') as mock_track: basket.add_product(seat) properties = translate_basket_line_for_segment(basket.lines.first()) properties['cart_id'] = basket.id mock_track.assert_not_called()
def _cache_if_authenticated_user_found_in_middleware(self, request, value): """ Updates the cached process step in which the authenticated user was found, if it hasn't already been found. """ cached_response = DEFAULT_REQUEST_CACHE.get_cached_response( self.AUTHENTICATED_USER_FOUND_CACHE_KEY) if cached_response.is_found: # since we are tracking the earliest point the authenticated user was found, # and the value was already set in earlier middleware step, do not set again. return if hasattr(request, 'user') and request.user and request.user.is_authenticated: DEFAULT_REQUEST_CACHE.set(self.AUTHENTICATED_USER_FOUND_CACHE_KEY, value)
def _set_owner_metrics_for_request(self, request): """ Uses the request path to find the view_func and then sets code owner metrics based on the view. """ if not is_code_owner_mappings_configured(): return try: view_func, _, _ = resolve(request.path) view_func_module = view_func.__module__ DEFAULT_REQUEST_CACHE.set(self._VIEW_FUNC_MODULE_METRIC_CACHE_KEY, view_func_module) set_custom_metric('view_func_module', view_func_module) code_owner = get_code_owner_from_module(view_func_module) if code_owner: set_custom_metric('code_owner', code_owner) except Resolver404: set_custom_metric( 'code_owner_mapping_error', "Couldn't resolve view for request path {}".format( request.path)) except Exception as e: set_custom_metric('code_owner_mapping_error', e)
def get(self, request): """ Calculate basket totals given a list of sku's Create a temporary basket add the sku's and apply an optional voucher code. Then calculate the total price less discounts. If a voucher code is not provided apply a voucher in the Enterprise entitlements available to the user. Query Params: sku (string): A list of sku(s) to calculate code (string): Optional voucher code to apply to the basket. username (string): Optional username of a user for which to calculate the basket. Returns: JSON: { 'total_incl_tax_excl_discounts': basket.total_incl_tax_excl_discounts, 'total_incl_tax': basket.total_incl_tax, 'currency': basket.currency } """ DEFAULT_REQUEST_CACHE.set(TEMPORARY_BASKET_CACHE_KEY, True) partner = get_partner_for_site(request) skus = request.GET.getlist('sku') if not skus: return HttpResponseBadRequest(_('No SKUs provided.')) skus.sort() code = request.GET.get('code', None) try: voucher = Voucher.objects.get(code=code) if code else None except Voucher.DoesNotExist: voucher = None products = Product.objects.filter(stockrecords__partner=partner, stockrecords__partner_sku__in=skus) if not products: return HttpResponseBadRequest( _('Products with SKU(s) [{skus}] do not exist.').format( skus=', '.join(skus))) # If there is only one product apply an Enterprise entitlement voucher if not voucher and len(products) == 1: voucher = get_entitlement_voucher(request, products[0]) basket_owner = request.user requested_username = request.GET.get('username', default='') is_anonymous = request.GET.get('is_anonymous', 'false').lower() == 'true' use_default_basket = is_anonymous # validate query parameters if requested_username and is_anonymous: return HttpResponseBadRequest( _('Provide username or is_anonymous query param, but not both') ) elif not requested_username and not is_anonymous: logger.warning( "Request to Basket Calculate must supply either username or is_anonymous query" " param. Requesting user=%s. Future versions of this API will treat this " "WARNING as an ERROR and raise an exception.", basket_owner.username) requested_username = request.user.username # If a username is passed in, validate that the user has staff access or is the same user. if requested_username: if basket_owner.username.lower() == requested_username.lower(): pass elif basket_owner.is_staff: try: basket_owner = User.objects.get( username=requested_username) except User.DoesNotExist: # This case represents a user who is logged in to marketing, but # doesn't yet have an account in ecommerce. These users have # never purchased before. use_default_basket = True else: return HttpResponseForbidden('Unauthorized user credentials') if basket_owner.username == self.MARKETING_USER and not use_default_basket: # For legacy requests that predate is_anonymous parameter, we will calculate # an anonymous basket if the calculated user is the marketing user. # TODO: LEARNER-5057: Remove this special case for the marketing user # once logs show no more requests with no parameters (see above). use_default_basket = True if use_default_basket: basket_owner = None cache_key = None if use_default_basket: # For an anonymous user we can directly get the cached price, because # there can't be any enrollments or entitlements. cache_key = get_cache_key(site_comain=request.site, resource_name='calculate', skus=skus) cached_response = TieredCache.get_cached_response(cache_key) if cached_response.is_found: return Response(cached_response.value) response = self._calculate_temporary_basket_atomic( basket_owner, request, products, voucher, skus, code) if response and use_default_basket: TieredCache.set_all_tiers( cache_key, response, settings.ANONYMOUS_BASKET_CALCULATE_CACHE_TIMEOUT) return Response(response)
def get(self, request): # pylint: disable=too-many-statements """ Calculate basket totals given a list of sku's Create a temporary basket add the sku's and apply an optional voucher code. Then calculate the total price less discounts. If a voucher code is not provided apply a voucher in the Enterprise entitlements available to the user. Query Params: sku (string): A list of sku(s) to calculate code (string): Optional voucher code to apply to the basket. username (string): Optional username of a user for which to calculate the basket. Returns: JSON: { 'total_incl_tax_excl_discounts': basket.total_incl_tax_excl_discounts, 'total_incl_tax': basket.total_incl_tax, 'currency': basket.currency } Side effects: If the basket owner does not have an LMS user id, tries to find it. If found, adds the id to the user and saves the user. If the id cannot be found, writes custom metrics to record this fact. """ DEFAULT_REQUEST_CACHE.set(TEMPORARY_BASKET_CACHE_KEY, True) partner = get_partner_for_site(request) skus = request.GET.getlist('sku') if not skus: return HttpResponseBadRequest(_('No SKUs provided.')) skus.sort() code = request.GET.get('code', None) try: voucher = Voucher.objects.get(code=code) if code else None except Voucher.DoesNotExist: voucher = None products = Product.objects.filter(stockrecords__partner=partner, stockrecords__partner_sku__in=skus) if not products: return HttpResponseBadRequest( _('Products with SKU(s) [{skus}] do not exist.').format( skus=', '.join(skus))) basket_owner = request.user requested_username = request.GET.get('username', default='') is_anonymous = request.GET.get('is_anonymous', 'false').lower() == 'true' use_default_basket = is_anonymous use_default_basket_case = 0 # validate query parameters if requested_username and is_anonymous: return HttpResponseBadRequest( _('Provide username or is_anonymous query param, but not both') ) if not requested_username and not is_anonymous: logger.warning( "Request to Basket Calculate must supply either username or is_anonymous query" " param. Requesting user=%s. Future versions of this API will treat this " "WARNING as an ERROR and raise an exception.", basket_owner.username) requested_username = request.user.username # If a username is passed in, validate that the user has staff access or is the same user. if requested_username: if basket_owner.username.lower() == requested_username.lower(): pass elif basket_owner.is_staff: try: basket_owner = User.objects.get( username=requested_username) except User.DoesNotExist: # This case represents a user who is logged in to marketing, but # doesn't yet have an account in ecommerce. These users have # never purchased before. use_default_basket = True use_default_basket_case = 1 else: return HttpResponseForbidden('Unauthorized user credentials') if basket_owner.username == self.MARKETING_USER and not use_default_basket: # For legacy requests that predate is_anonymous parameter, we will calculate # an anonymous basket if the calculated user is the marketing user. # TODO: LEARNER-5057: Remove this special case for the marketing user # once logs show no more requests with no parameters (see above). use_default_basket = True use_default_basket_case = 2 if use_default_basket: basket_owner = None # If we have a basket owner, ensure they have an LMS user id try: if basket_owner: called_from = u'calculation of basket total' basket_owner.add_lms_user_id( 'ecommerce_missing_lms_user_id_calculate_basket_total', called_from) except MissingLmsUserIdException: return self._report_bad_request( api_exceptions.LMS_USER_ID_NOT_FOUND_DEVELOPER_MESSAGE.format( user_id=basket_owner.id), api_exceptions.LMS_USER_ID_NOT_FOUND_USER_MESSAGE) cache_key = None if use_default_basket: # For an anonymous user we can directly get the cached price, because # there can't be any enrollments or entitlements. cache_key = get_cache_key(site_comain=request.site, resource_name='calculate', skus=skus) cached_response = TieredCache.get_cached_response(cache_key) logger.info( 'bundle debugging 2: request [%s] referrer [%s] url [%s] Cache key [%s] response [%s]' 'skus [%s] case [%s] basket_owner [%s]', str(request), str(request.META.get('HTTP_REFERER')), str(request._request), str(cache_key), # pylint: disable=protected-access str(cached_response), str(skus), str(use_default_basket_case), str(basket_owner)) if cached_response.is_found: return Response(cached_response.value) response = self._calculate_temporary_basket_atomic( basket_owner, request, products, voucher, skus, code) if response and use_default_basket: logger.info( 'bundle debugging 3: request [%s] referrer [%s] url [%s] Cache key [%s] response [%s]' 'skus [%s] case [%s] basket_owner [%s]', str(request), str(request.META.get('HTTP_REFERER')), str(request._request), str(cache_key), # pylint: disable=protected-access str(response), str(skus), str(use_default_basket_case), str(basket_owner)) TieredCache.set_all_tiers( cache_key, response, settings.ANONYMOUS_BASKET_CALCULATE_CACHE_TIMEOUT) return Response(response)