コード例 #1
0
ファイル: utils.py プロジェクト: shashikiranraifox/ecommerce
def _get_discovery_response(site, cache_key, resource, resource_id):
    """
    Return the discovery endpoint result of given resource or cached response if its already been cached.

    Arguments:
        site (Site): Site object containing Site Configuration data
        cache_key (str): Cache key for given resource
        resource_id (int or str): Identifies a specific resource to be retrieved

    Returns:
        dict: resource's information for given resource_id received from Discovery API
    """
    course_cached_response = TieredCache.get_cached_response(cache_key)
    if course_cached_response.is_found:
        return course_cached_response.value

    params = {}
    api = site.siteconfiguration.discovery_api_client
    endpoint = getattr(api, resource)

    if resource == 'course_runs':
        params['partner'] = site.siteconfiguration.partner.short_code
    response = endpoint(resource_id).get(**params)

    if resource_id is None:
        response = deprecated_traverse_pagination(response, endpoint)

    TieredCache.set_all_tiers(cache_key, response,
                              settings.COURSES_API_CACHE_TIMEOUT)
    return response
コード例 #2
0
    def get_course_duration(self, obj):
        """
        Get course's duration as a timedelta.

        Arguments:
            obj (CourseOverview): CourseOverview object

        Returns:
            (timedelta): Duration of a course.
        """
        course_run = None
        duration = None
        site = self.context.get('site')
        if site:
            cache_key = get_cache_key(course_id=obj.id, site=site)
            cached_response = TieredCache.get_cached_response(cache_key)
            if cached_response.is_found:
                course_run = cached_response.value
            else:
                try:
                    _, course_run = CourseCatalogApiServiceClient(site).get_course_and_course_run(str(obj.id))
                    TieredCache.set_all_tiers(cache_key, course_run, CACHE_TIMEOUT)
                except ImproperlyConfigured:
                    LOGGER.warning('CourseCatalogApiServiceClient is improperly configured.')
        if course_run and course_run.get('max_effort') and course_run.get('weeks_to_complete'):
            duration = '{effort} hours per week for {weeks} weeks.'.format(
                effort=course_run['max_effort'],
                weeks=course_run['weeks_to_complete']
            )
        return duration or ''
コード例 #3
0
ファイル: views.py プロジェクト: eduNEXT/ecommerce
    def get_credit_providers(self):
        """
        Retrieve all credit providers from LMS.

        Results will be sorted alphabetically by display name.
        """
        key = 'credit_providers'
        credit_providers_cache_response = TieredCache.get_cached_response(key)
        if credit_providers_cache_response.is_found:
            return credit_providers_cache_response.value

        try:
            client = self.request.site.siteconfiguration.oauth_api_client
            credit_url = urljoin(
                f"{self.request.site.siteconfiguration.credit_api_url}/",
                "providers/")
            resp = client.get(credit_url)
            resp.raise_for_status()
            credit_providers = resp.json()
            credit_providers.sort(
                key=lambda provider: provider['display_name'])

            # Update the cache
            TieredCache.set_all_tiers(key, credit_providers,
                                      settings.CREDIT_PROVIDER_CACHE_TIMEOUT)
        except (RequestException, Timeout):
            logger.exception('Failed to retrieve credit providers!')
            credit_providers = []
        return credit_providers
コード例 #4
0
 def decorator(*args, **kwargs):  # pylint: disable=unused-argument,missing-docstring
     result = TieredCache.get_cached_response(cache_key)
     if result.is_found:
         return result.value
     result = func()
     TieredCache.set_all_tiers(cache_key, result, self.cache_time)
     return result
コード例 #5
0
ファイル: utils.py プロジェクト: nadheemabdulla/ecommerce
def get_cached_voucher(code):
    """
    Returns a voucher from cache if one is stored to cache, if not the voucher
    is retrieved from database and stored to cache.

    Arguments:
        code (str): The code of a coupon voucher.

    Returns:
        voucher (Voucher): The Voucher for the passed code.

    Raises:
        Voucher.DoesNotExist: When no vouchers with provided code exist.
    """
    voucher_code = 'voucher_{code}'.format(code=code)
    cache_key = hashlib.md5(voucher_code.encode('utf-8')).hexdigest()
    voucher_cached_response = TieredCache.get_cached_response(cache_key)
    if voucher_cached_response.is_found:
        return voucher_cached_response.value

    voucher = Voucher.objects.get(code=code)

    TieredCache.set_all_tiers(cache_key, voucher,
                              settings.VOUCHER_CACHE_TIMEOUT)
    return voucher
コード例 #6
0
ファイル: models.py プロジェクト: raccoongang/ecommerce
    def catalog_contains_product(self, product):
        """
        Retrieve the results from using the catalog contains endpoint for
        catalog service for the catalog id contained in field "course_catalog".
        """
        request = get_current_request()
        partner_code = request.site.siteconfiguration.partner.short_code
        cache_key = get_cache_key(site_domain=request.site.domain,
                                  partner_code=partner_code,
                                  resource='catalogs.contains',
                                  course_id=product.course_id,
                                  catalog_id=self.course_catalog)
        cached_response = TieredCache.get_cached_response(cache_key)
        if cached_response.is_found:
            return cached_response.value

        discovery_api_client = request.site.siteconfiguration.discovery_api_client
        try:
            # GET: /api/v1/catalogs/{catalog_id}/contains?course_run_id={course_run_ids}
            response = discovery_api_client.catalogs(
                self.course_catalog).contains.get(
                    course_run_id=product.course_id)

            TieredCache.set_all_tiers(cache_key, response,
                                      settings.COURSES_API_CACHE_TIMEOUT)
            return response
        except (ReqConnectionError, SlumberBaseException, Timeout) as exc:
            logger.exception(
                '[Code Redemption Failure] Unable to connect to the Discovery Service '
                'for catalog contains endpoint. '
                'Product: %s, Message: %s, Range: %s', product.id, exc,
                self.id)
            raise Exception(
                'Unable to connect to Discovery Service for catalog contains endpoint.'
            ) from exc
コード例 #7
0
    def get_enterprise_customer(self, user, enterprise_id):
        """
        Get the enterprises that this user has access to.
        """
        cache_key = get_cache_key(
            resource='enterprise-customer',
            user=user.username,
            enterprise_customer=enterprise_id,
        )
        cached_response = TieredCache.get_cached_response(cache_key)
        if cached_response.is_found:
            return cached_response.value

        try:
            endpoint = getattr(self, 'enterprise-customer')
            endpoint = endpoint(enterprise_id)
            response = endpoint.get()
        except (HttpClientError, HttpServerError) as exc:
            LOGGER.warning(
                "[Data Overview Failure] Unable to retrieve Enterprise Customer details. "
                "User: {user}, Enterprise: {enteprise_id}, Exception: {exc}".
                format(user=user.username, enteprise_id=enterprise_id,
                       exc=exc))
            raise exc

        TieredCache.set_all_tiers(cache_key, response,
                                  DEFAULT_REPORTING_CACHE_TIMEOUT)

        return response
コード例 #8
0
ファイル: api.py プロジェクト: nadheemabdulla/ecommerce
    def get_program(self, uuid):
        """
        Retrieve the details for a single program.

        Args:
            uuid (str|uuid): Program UUID.

        Returns:
            dict
        """
        program_uuid = str(uuid)
        cache_key = '{site_domain}-program-{uuid}'.format(site_domain=self.site_domain, uuid=program_uuid)

        program_cached_response = TieredCache.get_cached_response(cache_key)

        if program_cached_response.is_found:  # pragma: no cover
            logger.debug('Program [%s] was found in the cache.', program_uuid)
            return program_cached_response.value

        logging.info('Retrieving details of of program [%s]...', program_uuid)
        program = self.client.programs(program_uuid).get()

        TieredCache.set_all_tiers(cache_key, program, self.cache_ttl)
        logging.info('Program [%s] was successfully retrieved and cached.', program_uuid)
        return program
コード例 #9
0
ファイル: api.py プロジェクト: lDDii/ecommerce
def catalog_contains_course_runs(site,
                                 course_run_ids,
                                 enterprise_customer_uuid,
                                 enterprise_customer_catalog_uuid=None):
    """
    Determine if course runs are associated with the EnterpriseCustomer.
    """
    query_params = {'course_run_ids': course_run_ids}
    api_resource_name = 'enterprise-customer'
    api_resource_id = enterprise_customer_uuid
    if enterprise_customer_catalog_uuid:
        api_resource_name = 'enterprise_catalogs'
        api_resource_id = enterprise_customer_catalog_uuid

    cache_key = get_cache_key(
        site_domain=site.domain,
        resource='{resource}-{resource_id}-contains_content_items'.format(
            resource=api_resource_name,
            resource_id=api_resource_id,
        ),
        query_params=urlencode(query_params, True))

    contains_content_cached_response = TieredCache.get_cached_response(
        cache_key)
    if contains_content_cached_response.is_found:
        return contains_content_cached_response.value

    api = site.siteconfiguration.enterprise_api_client
    endpoint = getattr(api, api_resource_name)(api_resource_id)
    contains_content = endpoint.contains_content_items.get(
        **query_params)['contains_content_items']
    TieredCache.set_all_tiers(cache_key, contains_content,
                              settings.ENTERPRISE_API_CACHE_TIMEOUT)

    return contains_content
コード例 #10
0
def get_with_access_to(site, user, jwt, enterprise_id):
    """
    Get the enterprises that this user has access to for the data api permission django group.
    """
    api_resource_name = 'enterprise-customer'
    api = EdxRestApiClient(site.siteconfiguration.enterprise_api_url, jwt=jwt)
    endpoint = getattr(api, api_resource_name)

    cache_key = get_cache_key(
        resource='{api_resource_name}-with_access_to_enterprises'.format(api_resource_name=api_resource_name),
        user=user.username,
        enterprise_customer=enterprise_id,
    )
    cached_response = TieredCache.get_cached_response(cache_key)
    if cached_response.is_found:
        return cached_response.value
    try:
        query_params = {
            'permissions': [settings.ENTERPRISE_DATA_API_GROUP],
            'enterprise_id': enterprise_id,
        }
        response = endpoint.with_access_to.get(**query_params)
    except (ConnectionError, SlumberHttpBaseException, Timeout) as exc:
        logger.warning('Unable to retrieve Enterprise Customer with_access_to details for user: %s: %r',
                       user.username, exc)
        return None
    if response.get('results', None) is None or response['count'] == 0:
        logger.warning('Unable to process Enterprise Customer with_access_to details for user: %s, enterprise: %s'
                       ' No Results Found', user.username, enterprise_id)
        return None
    if response['count'] > 1:
        logger.warning('Multiple Enterprise Customers found for user: %s, enterprise: %s', user.username, enterprise_id)
        return None
    TieredCache.set_all_tiers(cache_key, response['results'][0], settings.ENTERPRISE_API_CACHE_TIMEOUT)
    return response['results'][0]
コード例 #11
0
    def is_verified(self, site):
        """
        Check if a user has verified his/her identity.
        Calls the LMS verification status API endpoint and returns the verification status information.
        The status information is stored in cache, if the user is verified, until the verification expires.

        Args:
            site (Site): The site object from which the LMS account API endpoint is created.

        Returns:
            True if the user is verified, false otherwise.
        """
        try:
            cache_key = 'verification_status_{username}'.format(username=self.username)
            cache_key = hashlib.md5(cache_key.encode('utf-8')).hexdigest()
            verification_cached_response = TieredCache.get_cached_response(cache_key)
            if verification_cached_response.is_found:
                return verification_cached_response.value

            api = site.siteconfiguration.user_api_client
            response = api.accounts(self.username).verification_status().get()

            verification = response.get('is_verified', False)
            if verification:
                cache_timeout = int((parse(response.get('expiration_datetime')) - now()).total_seconds())
                TieredCache.set_all_tiers(cache_key, verification, cache_timeout)
            return verification
        except HttpNotFoundError:
            log.debug('No verification data found for [%s]', self.username)
            return False
        except (ReqConnectionError, SlumberBaseException, Timeout):
            msg = 'Failed to retrieve verification status details for [{username}]'.format(username=self.username)
            log.warning(msg)
            return False
コード例 #12
0
    def test_when_record_user_activity_does_not_perform_updates(self):
        '''
        Ensure that record user activity is not called when:
            1. user or course are not defined
            2. we have already recorded user activity for this user/course on this date
            and have a record in the cache
        '''
        with patch.object(
                TieredCache, 'set_all_tiers',
                wraps=TieredCache.set_all_tiers) as activity_cache_set:
            UserActivity.record_user_activity(self.user, None)
            activity_cache_set.assert_not_called()

            UserActivity.record_user_activity(None, self.course.id)
            activity_cache_set.assert_not_called()

        cache_key = 'goals_user_activity_{}_{}_{}'.format(
            str(self.user.id), str(self.course.id), str(datetime.now().date()))
        TieredCache.set_all_tiers(cache_key, 'test', 3600)

        with patch.object(
                TieredCache, 'set_all_tiers',
                wraps=TieredCache.set_all_tiers) as activity_cache_set:
            UserActivity.record_user_activity(self.user, self.course.id)
            activity_cache_set.assert_not_called()

            # Test that the happy path works to ensure that the measurement in this test isn't broken
            user2 = UserFactory()
            UserActivity.record_user_activity(user2, self.course.id)
            activity_cache_set.assert_called_once()
コード例 #13
0
def fetch_journal_bundle(site, journal_bundle_uuid):
    """
    Retrieve journal bundle for given uuid.
    Retrieve it from cache if present, otherwise send GET request to journal bundle
        discovery api and store in cache.

    Args:
        site (Site): site for current request
        journal_bundle_uuid (str): uuid for desired journal bundle

    Returns:
        (dict): contains dict of journal_bundle attributes

    Raises:
        ConnectionError: raised if ecommerce is unable to connect to enterprise api server.
        SlumberBaseException: raised if API response contains http error status like 4xx, 5xx etc...
        Timeout: request is raised if API is taking too long to respond
    """

    api_resource = 'journal_bundle'
    cache_key = get_cache_key(site_domain=site.domain,
                              resource=api_resource,
                              journal_bundle_uuid=journal_bundle_uuid)

    journal_bundle_cached_response = TieredCache.get_cached_response(cache_key)
    if journal_bundle_cached_response.is_hit:
        return journal_bundle_cached_response.value

    client = site.siteconfiguration.journal_discovery_api_client
    journal_bundle = client.journal_bundles(journal_bundle_uuid).get()
    TieredCache.set_all_tiers(cache_key, journal_bundle,
                              JOURNAL_BUNDLE_CACHE_TIMEOUT)

    return journal_bundle
コード例 #14
0
ファイル: utils.py プロジェクト: pawan-prog/ecommerce
    def is_entitlement_expired(entitlement_uuid, site):
        """
        Checks to see if a given entitlement is expired.

        Args:
            entitlement_uuid: UUID
            site: (Site)

        Returns:
            bool: True if the entitlement is expired

        """
        entitlement_api_client = EdxRestApiClient(
            get_lms_entitlement_api_url(),
            jwt=site.siteconfiguration.access_token)
        partner_short_code = site.siteconfiguration.partner.short_code
        key = 'course_entitlement_detail_{}{}'.format(entitlement_uuid,
                                                      partner_short_code)
        entitlement_cached_response = TieredCache.get_cached_response(key)
        if entitlement_cached_response.is_found:
            entitlement = entitlement_cached_response.value
        else:
            logger.debug('Trying to get entitlement {%s}', entitlement_uuid)
            entitlement = entitlement_api_client.entitlements(
                entitlement_uuid).get()
            TieredCache.set_all_tiers(key, entitlement,
                                      settings.COURSES_API_CACHE_TIMEOUT)

        expired = entitlement.get('expired_at')
        logger.debug('Entitlement {%s} expired = {%s}', entitlement_uuid,
                     expired)

        return expired
コード例 #15
0
ファイル: api.py プロジェクト: scottwedge/edx-platform-custom
def consent_needed_for_course(request, user, course_id, enrollment_exists=False):
    """
    Wrap the enterprise app check to determine if the user needs to grant
    data sharing permissions before accessing a course.
    """
    consent_cache_key = get_data_consent_share_cache_key(user.id, course_id)
    data_sharing_consent_needed_cache = TieredCache.get_cached_response(consent_cache_key)
    if data_sharing_consent_needed_cache.is_found and data_sharing_consent_needed_cache.value == 0:
        return False

    enterprise_learner_details = get_enterprise_learner_data(user)
    if not enterprise_learner_details:
        consent_needed = False
    else:
        client = ConsentApiClient(user=request.user)
        consent_needed = any(
            Site.objects.get(domain=learner['enterprise_customer']['site']['domain']) == request.site
            and client.consent_required(
                username=user.username,
                course_id=course_id,
                enterprise_customer_uuid=learner['enterprise_customer']['uuid'],
                enrollment_exists=enrollment_exists,
            )
            for learner in enterprise_learner_details
        )
    if not consent_needed:
        # Set an ephemeral item in the cache to prevent us from needing
        # to make a Consent API request every time this function is called.
        TieredCache.set_all_tiers(consent_cache_key, 0, settings.DATA_CONSENT_SHARE_CACHE_TIMEOUT)

    return consent_needed
コード例 #16
0
    def get_credit_providers(self):
        """
        Retrieve all credit providers from LMS.

        Results will be sorted alphabetically by display name.
        """
        key = 'credit_providers'
        credit_providers_cache_response = TieredCache.get_cached_response(key)
        if credit_providers_cache_response.is_found:
            return credit_providers_cache_response.value

        try:
            credit_api = EdxRestApiClient(
                get_lms_url('/api/credit/v1/'),
                oauth_access_token=self.request.user.access_token)
            credit_providers = credit_api.providers.get()
            credit_providers.sort(
                key=lambda provider: provider['display_name'])

            # Update the cache
            TieredCache.set_all_tiers(key, credit_providers,
                                      settings.CREDIT_PROVIDER_CACHE_TIMEOUT)
        except (SlumberBaseException, Timeout):
            logger.exception('Failed to retrieve credit providers!')
            credit_providers = []
        return credit_providers
コード例 #17
0
ファイル: models.py プロジェクト: shashikiranraifox/ecommerce
    def access_token(self):
        """ Returns an access token for this site's service user.

        The access token is retrieved using the current site's OAuth credentials and the client credentials grant.
        The token is cached for the lifetime of the token, as specified by the OAuth provider's response. The token
        type is JWT.

        Returns:
            str: JWT access token
        """
        key = 'siteconfiguration_access_token_{}'.format(self.id)
        access_token_cached_response = TieredCache.get_cached_response(key)
        if access_token_cached_response.is_found:
            return access_token_cached_response.value

        url = '{root}/access_token'.format(root=self.oauth2_provider_url)
        access_token, expiration_datetime = EdxRestApiClient.get_oauth_access_token(
            url,
            self.oauth_settings['BACKEND_SERVICE_EDX_OAUTH2_KEY'],  # pylint: disable=unsubscriptable-object
            self.oauth_settings['BACKEND_SERVICE_EDX_OAUTH2_SECRET'],  # pylint: disable=unsubscriptable-object
            token_type='jwt')

        expires = (expiration_datetime - datetime.datetime.utcnow()).seconds
        TieredCache.set_all_tiers(key, access_token, expires)
        return access_token
コード例 #18
0
ファイル: api.py プロジェクト: mitodl/edx-platform
def consent_needed_for_course(request, user, course_id, enrollment_exists=False):
    """
    Wrap the enterprise app check to determine if the user needs to grant
    data sharing permissions before accessing a course.
    """
    consent_cache_key = get_data_consent_share_cache_key(user.id, course_id)
    data_sharing_consent_needed_cache = TieredCache.get_cached_response(consent_cache_key)
    if data_sharing_consent_needed_cache.is_found and data_sharing_consent_needed_cache.value is 0:
        return False

    enterprise_learner_details = get_enterprise_learner_data(user)
    if not enterprise_learner_details:
        consent_needed = False
    else:
        client = ConsentApiClient(user=request.user)
        consent_needed = any(
            client.consent_required(
                username=user.username,
                course_id=course_id,
                enterprise_customer_uuid=learner['enterprise_customer']['uuid'],
                enrollment_exists=enrollment_exists,
            )
            for learner in enterprise_learner_details
        )
    if not consent_needed:
        # Set an ephemeral item in the cache to prevent us from needing
        # to make a Consent API request every time this function is called.
        TieredCache.set_all_tiers(consent_cache_key, 0, settings.DATA_CONSENT_SHARE_CACHE_TIMEOUT)

    return consent_needed
コード例 #19
0
ファイル: client.py プロジェクト: edx/edx-rest-api-client
def get_and_cache_oauth_access_token(url,
                                     client_id,
                                     client_secret,
                                     token_type='jwt',
                                     grant_type='client_credentials',
                                     refresh_token=None,
                                     timeout=(REQUEST_CONNECT_TIMEOUT,
                                              REQUEST_READ_TIMEOUT)):
    """ Retrieves a possibly cached OAuth 2.0 access token using the given grant type.

    See ``get_oauth_access_token`` for usage details.

    First retrieves the access token from the cache and ensures it has not expired. If
    the access token either wasn't found in the cache, or was expired, retrieves a new
    access token and caches it for the lifetime of the token.

    Note: Consider tokens to be expired ACCESS_TOKEN_EXPIRED_THRESHOLD_SECONDS early
    to ensure the token won't expire while it is in use.

    Returns:
        tuple: Tuple containing (access token string, expiration datetime).

    """
    oauth_url = _get_oauth_url(url)
    cache_key = 'edx_rest_api_client.access_token.{}.{}.{}.{}'.format(
        token_type,
        grant_type,
        client_id,
        oauth_url,
    )
    cached_response = TieredCache.get_cached_response(cache_key)

    # Attempt to get an unexpired cached access token
    if cached_response.is_found:
        _, expiration = cached_response.value
        # Double-check the token hasn't already expired as a safety net.
        adjusted_expiration = expiration - datetime.timedelta(
            seconds=ACCESS_TOKEN_EXPIRED_THRESHOLD_SECONDS)
        if datetime.datetime.utcnow() < adjusted_expiration:
            return cached_response.value

    # Get a new access token if no unexpired access token was found in the cache.
    oauth_access_token_response = get_oauth_access_token(
        oauth_url,
        client_id,
        client_secret,
        grant_type=grant_type,
        refresh_token=refresh_token,
        timeout=timeout,
    )

    # Cache the new access token with an expiration matching the lifetime of the token.
    _, expiration = oauth_access_token_response
    expires_in = (expiration - datetime.datetime.utcnow()
                  ).seconds - ACCESS_TOKEN_EXPIRED_THRESHOLD_SECONDS
    TieredCache.set_all_tiers(cache_key, oauth_access_token_response,
                              expires_in)

    return oauth_access_token_response
コード例 #20
0
ファイル: models.py プロジェクト: mraarif/edx-platform
    def record_user_activity(cls, user, course_key, request=None, only_if_mobile_app=False):
        '''
        Update the user activity table with a record for this activity.

        Since we store one activity per date, we don't need to query the database
        for every activity on a given date.
        To avoid unnecessary queries, we store a record in a cache once we have an activity for the date,
        which times out at the end of that date (in the user's timezone).

        The request argument is only used to check if the request is coming from a mobile app.
        Once the only_if_mobile_app argument is removed the request argument can be removed as well.

        The return value is the id of the object that was created, or retrieved.
        A return value of None signifies that there was an issue with the parameters (or the user was masquerading).
        '''
        if not (user and user.id) or not course_key:
            return None

        if only_if_mobile_app and request and not is_request_from_mobile_app(request):
            return None

        if is_masquerading(user, course_key):
            return None

        user_preferences = get_user_preferences(user)
        timezone = pytz.timezone(user_preferences.get('time_zone', 'UTC'))
        now = datetime.now(timezone)
        date = now.date()

        cache_key = 'goals_user_activity_{}_{}_{}'.format(str(user.id), str(course_key), str(date))

        cached_value = TieredCache.get_cached_response(cache_key)
        if cached_value.is_found:
            # Temporary debugging log for testing mobile app connection
            if request:
                log.info(
                    'Retrieved cached value with request {} for user and course combination {} {}'.format(
                        str(request.build_absolute_uri()), str(user.id), str(course_key)
                    )
                )
            return cached_value.value, False

        activity_object, __ = cls.objects.get_or_create(user=user, course_key=course_key, date=date)

        # Cache result until the end of the day to avoid unnecessary database requests
        tomorrow = now + timedelta(days=1)
        midnight = datetime(year=tomorrow.year, month=tomorrow.month,
                            day=tomorrow.day, hour=0, minute=0, second=0, tzinfo=timezone)
        seconds_until_midnight = (midnight - now).seconds

        TieredCache.set_all_tiers(cache_key, activity_object.id, seconds_until_midnight)
        # Temporary debugging log for testing mobile app connection
        if request:
            log.info(
                'Set cached value with request {} for user and course combination {} {}'.format(
                    str(request.build_absolute_uri()), str(user.id), str(course_key)
                )
            )
        return activity_object.id
コード例 #21
0
ファイル: models.py プロジェクト: pawan-prog/ecommerce
    def get_applicable_lines(self, offer, basket, range=None):  # pylint: disable=redefined-builtin
        """
        Returns the basket lines for which the benefit is applicable.
        """
        applicable_range = range if range else self.range

        if applicable_range and applicable_range.catalog_query is not None:

            query = applicable_range.catalog_query
            applicable_lines = self._filter_for_paid_course_products(
                basket.all_lines(), applicable_range)

            site = basket.site
            partner_code = site.siteconfiguration.partner.short_code
            course_run_ids, course_uuids, applicable_lines = self._identify_uncached_product_identifiers(
                applicable_lines, site.domain, partner_code, query)

            if course_run_ids or course_uuids:
                # Hit Discovery Service to determine if remaining courses and runs are in the range.
                try:
                    response = site.siteconfiguration.discovery_api_client.catalog.query_contains.get(
                        course_run_ids=','.join(
                            [metadata['id'] for metadata in course_run_ids]),
                        course_uuids=','.join(
                            [metadata['id'] for metadata in course_uuids]),
                        query=query,
                        partner=partner_code)
                except Exception as err:  # pylint: disable=bare-except
                    logger.exception(
                        '[Code Redemption Failure] Unable to apply benefit because we failed to query the '
                        'Discovery Service for catalog data. '
                        'User: %s, Offer: %s, Basket: %s, Message: %s',
                        basket.owner.username, offer.id, basket.id, err)
                    raise Exception(
                        'Failed to contact Discovery Service to retrieve offer catalog_range data.'
                    )

                # Cache range-state individually for each course or run identifier and remove lines not in the range.
                for metadata in course_run_ids + course_uuids:
                    in_range = response[str(metadata['id'])]

                    # Convert to int, because this is what memcached will return, and the request cache should return
                    # the same value.
                    # Note: once the TieredCache is fixed to handle this case, we could remove this line.
                    in_range = int(in_range)
                    TieredCache.set_all_tiers(
                        metadata['cache_key'], in_range,
                        settings.COURSES_API_CACHE_TIMEOUT)

                    if not in_range:
                        applicable_lines.remove(metadata['line'])

            return [(line.product.stockrecords.first().price_excl_tax, line)
                    for line in applicable_lines]
        else:
            return super(Benefit, self).get_applicable_lines(offer,
                                                             basket,
                                                             range=range)  # pylint: disable=bad-super-call
コード例 #22
0
def fetch_course_catalog(site, catalog_id):
    """
    Fetch course catalog for the given catalog id.

    This method will fetch catalog for given catalog id, if there is no catalog with the given
    catalog id, method will return `None`.

    Arguments:
        site (Site): Instance of the current site.
        catalog_id (int): An integer specifying the primary key value of catalog to fetch.

    Example:
        >>> fetch_course_catalog(site, catalog_id=1)
        {
            "id": 1,
            "name": "All Courses",
            "query": "*:*",
            ...
        }
    Returns:
        (dict): A dictionary containing key/value pairs corresponding to catalog attribute/values.

    Raises:
        ConnectionError: requests exception "ConnectionError", raised if if ecommerce is unable to connect
            to enterprise api server.
        SlumberBaseException: base slumber exception "SlumberBaseException", raised if API response contains
            http error status like 4xx, 5xx etc.
        Timeout: requests exception "Timeout", raised if enterprise API is taking too long for returning
            a response. This exception is raised for both connection timeout and read timeout.

    """
    api_resource = 'catalogs'

    cache_key = get_cache_key(
        site_domain=site.domain,
        resource=api_resource,
        catalog_id=catalog_id,
    )

    cached_response = TieredCache.get_cached_response(cache_key)
    if cached_response.is_found:
        return cached_response.value

    api = site.siteconfiguration.discovery_api_client
    endpoint = getattr(api, api_resource)

    try:
        response = endpoint(catalog_id).get()
    except HttpNotFoundError:
        logger.exception("Catalog '%s' not found.", catalog_id)
        raise

    TieredCache.set_all_tiers(cache_key, response,
                              settings.COURSES_API_CACHE_TIMEOUT)
    return response
コード例 #23
0
ファイル: utils.py プロジェクト: eduNEXT/ecommerce
def get_enterprise_catalog(site, enterprise_catalog, limit, page, endpoint_request_url=None):
    """
    Get the EnterpriseCustomerCatalog for a given catalog uuid.

    Args:
        site (Site): The site which is handling the current request
        enterprise_catalog (str): The uuid of the Enterprise Catalog
        limit (int): The number of results to return per page.
        page (int): The page number to fetch.
        endpoint_request_url (str): This is used to replace the lms url with ecommerce url

    Returns:
        dict: The result set containing the content objects associated with the Enterprise Catalog.
        NoneType: Return None if no catalog with that uuid is found.
    """
    resource = 'enterprise_catalogs'
    partner_code = site.siteconfiguration.partner.short_code
    cache_key = u'{site_domain}_{partner_code}_{resource}_{catalog}_{limit}_{page}'.format(
        site_domain=site.domain,
        partner_code=partner_code,
        resource=resource,
        catalog=enterprise_catalog,
        limit=limit,
        page=page
    )
    cache_key = hashlib.md5(cache_key.encode('utf-8')).hexdigest()

    cached_response = TieredCache.get_cached_response(cache_key)
    if cached_response.is_found:
        return cached_response.value

    api_client = site.siteconfiguration.oauth_api_client
    enterprise_api_url = urljoin(
        f"{site.siteconfiguration.enterprise_api_url}/",
        f"{resource}/{str(enterprise_catalog)}/"
    )

    response = api_client.get(
        enterprise_api_url,
        params={
            "limit": limit,
            "page": page,
        }
    )
    response.raise_for_status()
    result = response.json()

    if endpoint_request_url:
        result = update_paginated_response(endpoint_request_url, result)

    TieredCache.set_all_tiers(cache_key, result, settings.CATALOG_RESULTS_CACHE_TIMEOUT)

    return result
コード例 #24
0
ファイル: api.py プロジェクト: lDDii/ecommerce
def fetch_enterprise_learner_entitlements(site, learner_id):
    """
    Fetch enterprise learner entitlements along-with data sharing consent requirement.

    Arguments:
        site (Site): site instance.
        learner_id (int): Primary key identifier for the enterprise learner.

    Example:
        >>> from django.contrib.sites.shortcuts import get_current_site
        >>> site  = get_current_site()
        >>> fetch_enterprise_learner_entitlements(site, 1)
        [
            {
                "requires_consent": False,
                "entitlement_id": 1
            },
        ]

    Returns:
         (list): Containing dicts of the following structure
            {
                "requires_consent": True,
                "entitlement_id": 1
            }

    Raises:
        ConnectionError: requests exception "ConnectionError", raised if if ecommerce is unable to connect
            to enterprise api server.
        SlumberBaseException: base slumber exception "SlumberBaseException", raised if API response contains
            http error status like 4xx, 5xx etc.
        Timeout: requests exception "Timeout", raised if enterprise API is taking too long for returning
            a response. This exception is raised for both connection timeout and read timeout.
    """
    resource_url = 'enterprise-learner/{learner_id}/entitlements'.format(
        learner_id=learner_id)
    cache_key = get_cache_key(
        site_domain=site.domain,
        partner_code=site.siteconfiguration.partner.short_code,
        resource=resource_url,
        learner_id=learner_id)

    entitlements_cached_response = TieredCache.get_cached_response(cache_key)
    if entitlements_cached_response.is_found:
        return entitlements_cached_response.value

    api = site.siteconfiguration.enterprise_api_client
    entitlements = getattr(api, resource_url).get()

    TieredCache.set_all_tiers(cache_key, entitlements,
                              settings.ENTERPRISE_API_CACHE_TIMEOUT)
    return entitlements
コード例 #25
0
ファイル: utils.py プロジェクト: eduNEXT/ecommerce
def fetch_course_catalog(site, catalog_id):
    """
    Fetch course catalog for the given catalog id.

    This method will fetch catalog for given catalog id, if there is no catalog with the given
    catalog id, method will return `None`.

    Arguments:
        site (Site): Instance of the current site.
        catalog_id (int): An integer specifying the primary key value of catalog to fetch.

    Example:
        >>> fetch_course_catalog(site, catalog_id=1)
        {
            "id": 1,
            "name": "All Courses",
            "query": "*:*",
            ...
        }
    Returns:
        (dict): A dictionary containing key/value pairs corresponding to catalog attribute/values.

    Raises:
        HTTPError: requests exception "HTTPError".
    """
    api_resource = 'catalogs'

    cache_key = get_cache_key(
        site_domain=site.domain,
        resource=api_resource,
        catalog_id=catalog_id,
    )

    cached_response = TieredCache.get_cached_response(cache_key)
    if cached_response.is_found:
        return cached_response.value

    api_client = site.siteconfiguration.oauth_api_client
    api_url = urljoin(f"{site.siteconfiguration.discovery_api_url}/",
                      f"{api_resource}/{catalog_id}/")

    response = api_client.get(api_url)
    if response.status_code == 404:
        logger.exception("Catalog '%s' not found.", catalog_id)

    response.raise_for_status()

    result = response.json()

    TieredCache.set_all_tiers(cache_key, result,
                              settings.COURSES_API_CACHE_TIMEOUT)
    return result
コード例 #26
0
ファイル: utils.py プロジェクト: ecommerce-misc/ecommerce
def get_enterprise_catalog(site,
                           enterprise_catalog,
                           limit,
                           page,
                           endpoint_request_url=None):
    """
    Get the EnterpriseCustomerCatalog for a given catalog uuid.

    Args:
        site (Site): The site which is handling the current request
        enterprise_catalog (str): The uuid of the Enterprise Catalog
        limit (int): The number of results to return per page.
        page (int): The page number to fetch.
        endpoint_request_url (str): This is used to replace the lms url with ecommerce url

    Returns:
        dict: The result set containing the content objects associated with the Enterprise Catalog.
        NoneType: Return None if no catalog with that uuid is found.
    """
    resource = 'enterprise_catalogs'
    partner_code = site.siteconfiguration.partner.short_code
    cache_key = '{site_domain}_{partner_code}_{resource}_{catalog}_{limit}_{page}'.format(
        site_domain=site.domain,
        partner_code=partner_code,
        resource=resource,
        catalog=enterprise_catalog,
        limit=limit,
        page=page)
    cache_key = hashlib.md5(cache_key).hexdigest()

    cached_response = TieredCache.get_cached_response(cache_key)
    if cached_response.is_found:
        return cached_response.value

    client = get_enterprise_api_client(site)
    path = [resource, str(enterprise_catalog)]
    client = reduce(getattr, path, client)

    response = client.get(
        limit=limit,
        page=page,
    )

    if endpoint_request_url:
        response = update_paginated_response(endpoint_request_url, response)

    TieredCache.set_all_tiers(cache_key, response,
                              settings.CATALOG_RESULTS_CACHE_TIMEOUT)

    return response
コード例 #27
0
 def set_last_seen_courseware_timezone(self, user):
     """
     The timezone in the user's account is frequently not set.
     This method sets a user's recent timezone that can be used as a fallback
     """
     cache_key = 'browser_timezone_{}'.format(str(user.id))
     browser_timezone = self.request.query_params.get('browser_timezone', None)
     cached_value = TieredCache.get_cached_response(cache_key)
     if not cached_value.is_found:
         if browser_timezone:
             TieredCache.set_all_tiers(cache_key, str(browser_timezone), 86400)  # Refresh the cache daily
             LastSeenCoursewareTimezone.objects.update_or_create(
                 user=user,
                 last_seen_courseware_timezone=browser_timezone,
             )
コード例 #28
0
ファイル: utils.py プロジェクト: eduNEXT/ecommerce
def get_enterprise_customer(site, uuid):
    """
    Return a single enterprise customer
    """
    resource = 'enterprise-customer'
    cache_key = u'{site_domain}_{partner_code}_{resource}_{enterprise_uuid}'.format(
        site_domain=site.domain,
        partner_code=site.siteconfiguration.partner.short_code,
        resource=resource,
        enterprise_uuid=uuid,
    )
    cache_key = hashlib.md5(cache_key.encode('utf-8')).hexdigest()
    cached_response = TieredCache.get_cached_response(cache_key)
    if cached_response.is_found:
        return cached_response.value

    api_client = site.siteconfiguration.oauth_api_client
    enterprise_api_url = urljoin(
        f"{site.siteconfiguration.enterprise_api_url}/",
        f"{resource}/{str(uuid)}/"
    )

    try:
        response = api_client.get(enterprise_api_url)
        response.raise_for_status()
        response = response.json()
    except (ReqConnectionError, HTTPError, Timeout):
        log.exception("Failed to fetch enterprise customer")
        return None

    enterprise_customer_response = {
        'name': response['name'],
        'id': response['uuid'],
        'enable_data_sharing_consent': response['enable_data_sharing_consent'],
        'enforce_data_sharing_consent': response['enforce_data_sharing_consent'],
        'contact_email': response.get('contact_email', ''),
        'slug': response.get('slug'),
        'sender_alias': response.get('sender_alias', ''),
        'reply_to': response.get('reply_to', ''),
    }

    TieredCache.set_all_tiers(
        cache_key,
        enterprise_customer_response,
        settings.ENTERPRISE_CUSTOMER_RESULTS_CACHE_TIMEOUT
    )

    return enterprise_customer_response
    def test_get_access_token_from_cache_valid(self):
        encrypted_access_token = self.handler._encrypt_token('12345')
        encrypted_refresh_token = self.handler._encrypt_token('67890')
        tokens = {
            'access_token':
            encrypted_access_token,
            'refresh_token':
            encrypted_refresh_token,
            'expires_at':
            datetime.datetime.utcnow() + datetime.timedelta(seconds=20)
        }
        TieredCache.set_all_tiers('badgr-test-cache-key', tokens, None)

        access_token = self.handler._get_access_token()
        assert access_token == self.handler._decrypt_token(
            tokens.get('access_token'))
コード例 #30
0
def catalog_contains_course_runs(site,
                                 course_run_ids,
                                 enterprise_customer_uuid,
                                 enterprise_customer_catalog_uuid=None):
    """
    Determine if course runs are associated with the EnterpriseCustomer.
    """
    query_params = {'course_run_ids': course_run_ids}
    api_resource_name = 'enterprise-customer'
    api_resource_id = enterprise_customer_uuid
    if enterprise_customer_catalog_uuid:
        api_resource_name = 'enterprise_catalogs'
        api_resource_id = enterprise_customer_catalog_uuid

    cache_key = get_cache_key(
        site_domain=site.domain,
        resource='{resource}-{resource_id}-contains_content_items'.format(
            resource=api_resource_name,
            resource_id=api_resource_id,
        ),
        query_params=urlencode(query_params, True))

    contains_content_cached_response = TieredCache.get_cached_response(
        cache_key)
    if contains_content_cached_response.is_found:
        return contains_content_cached_response.value

    api = site.siteconfiguration.enterprise_api_client
    endpoint = getattr(api, api_resource_name)(api_resource_id)
    try:
        contains_content = endpoint.contains_content_items.get(
            **query_params)['contains_content_items']

        TieredCache.set_all_tiers(cache_key, contains_content,
                                  settings.ENTERPRISE_API_CACHE_TIMEOUT)
    except (ConnectionError, KeyError, SlumberHttpBaseException, Timeout):
        logger.exception(
            'Failed to check if course_runs [%s] exist in '
            'EnterpriseCustomerCatalog [%s]'
            'for EnterpriseCustomer [%s].',
            course_run_ids,
            enterprise_customer_catalog_uuid,
            enterprise_customer_uuid,
        )
        contains_content = False
    return contains_content
コード例 #31
0
    def test_get_order_attribute_from_ecommerce(self, mock_ecommerce_api_client):
        """
        Assert that the get_order_attribute_from_ecommerce method returns order details if it's already cached,
        without calling ecommerce.
        """
        order_details = {"number": self.ORDER_NUMBER, "vouchers": [{"end_datetime": '2025-09-25T00:00:00Z'}]}
        cache_key = get_cache_key(user_id=self.user.id, order_number=self.ORDER_NUMBER)
        TieredCache.set_all_tiers(cache_key, order_details, 60)

        self.enrollment.attributes.create(
            enrollment=self.enrollment,
            namespace='order',
            name='order_number',
            value=self.ORDER_NUMBER
        )
        assert self.enrollment.get_order_attribute_from_ecommerce("vouchers") == order_details["vouchers"]
        mock_ecommerce_api_client.assert_not_called()
コード例 #32
0
ファイル: test_signals.py プロジェクト: edx/edx-platform
 def _create_dsc_cache(user_id, course_id):
     consent_cache_key = get_data_consent_share_cache_key(user_id, course_id)
     TieredCache.set_all_tiers(consent_cache_key, 0)