Example #1
0
    def _publish_creditcourse(self, course_id, access_token):
        """Creates or updates a CreditCourse object on the LMS."""

        api = EdxRestApiClient(get_lms_url('api/credit/v1/'),
                               oauth_access_token=access_token,
                               timeout=self.timeout)

        data = {'course_key': course_id, 'enabled': True}

        api.courses(course_id).put(data)
Example #2
0
    def _publish_creditcourse(self, course_id, access_token):
        """Creates or updates a CreditCourse object on the LMS."""

        api = EdxRestApiClient(
            get_lms_url('api/credit/v1/'),
            oauth_access_token=access_token,
            timeout=self.timeout
        )

        data = {
            'course_key': course_id,
            'enabled': True
        }

        api.courses(course_id).put(data)
Example #3
0
    def refresh_all_course_api_data(cls, access_token):
        course_api_url = settings.COURSES_API_URL
        client = EdxRestApiClient(course_api_url, oauth_access_token=access_token)

        count = None
        page = 1

        logger.info('Refreshing course api data from %s....', course_api_url)

        while page:
            # TODO Update API to not require username?
            response = client.courses().get(page=page, page_size=50, username='******')
            count = response['pagination']['count']
            results = response['results']
            logger.info('Retrieved %d courses...', len(results))

            if response['pagination']['next']:
                page += 1
            else:
                page = None

            for body in results:
                Course(body['id']).update(body)

        logger.info('Retrieved %d courses from %s.', count, course_api_url)
Example #4
0
    def get_context_data(self, **kwargs):
        context = super(CouponOfferView, self).get_context_data(**kwargs)

        code = self.request.GET.get('code', None)
        if code is not None:
            voucher, product = get_voucher(code=code)
            valid_voucher, msg = voucher_is_valid(voucher, product, self.request)
            if valid_voucher:
                api = EdxRestApiClient(
                    get_lms_url('api/courses/v1/'),
                )
                try:
                    course = api.courses(product.course_id).get()
                except SlumberHttpBaseException as e:
                    logger.exception('Could not get course information. [%s]', e)
                    return {
                        'error': _('Could not get course information. [{error}]'.format(error=e))
                    }

                course['image_url'] = get_lms_url(course['media']['course_image']['uri'])
                stock_records = voucher.offers.first().benefit.range.catalog.stock_records.first()
                context.update({
                    'course': course,
                    'code': code,
                    'price': stock_records.price_excl_tax,
                    'verified': (product.attr.certificate_type is 'verified')
                })
                return context
            return {
                'error': msg
            }
        return {
            'error': _('This coupon code is invalid.')
        }
Example #5
0
    def refresh_all(cls, access_token):
        """
        Refresh all course data.

        Args:
            access_token (str): OAuth access token

        Returns:
            None
        """
        client = EdxRestApiClient(settings.ECOMMERCE_API_URL,
                                  oauth_access_token=access_token)

        logger.info('Refreshing course data from %s....',
                    settings.ECOMMERCE_API_URL)

        count = None
        page = 1
        while page:
            response = client.courses().get(include_products=True,
                                            page=page,
                                            page_size=50)
            count = response['count']
            results = response['results']
            logger.info('Retrieved %d courses...', len(results))

            if response['next']:
                page += 1
            else:
                page = None

            for body in results:
                Course(body['id'], body).save()

        logger.info('Retrieved %d courses.', count)
Example #6
0
    def refresh_all(cls, access_token):
        """
        Refresh all course data.

        Args:
            access_token (str): OAuth access token

        Returns:
            None
        """
        client = EdxRestApiClient(settings.ECOMMERCE_API_URL, oauth_access_token=access_token)

        logger.info('Refreshing course data from %s....', settings.ECOMMERCE_API_URL)

        count = None
        page = 1
        while page:
            response = client.courses().get(include_products=True, page=page, page_size=50)
            count = response['count']
            results = response['results']
            logger.info('Retrieved %d courses...', len(results))

            if response['next']:
                page += 1
            else:
                page = None

            for body in results:
                Course(body['id'], body).save()

        logger.info('Retrieved %d courses.', count)
Example #7
0
def build_content_table(content_api_url, lms_url, access_token):
    """
    Use Course Discovery API to build dict of course runs
    """
    logger.info("Retrieving course content, url=" + content_api_url)

    client = EdxRestApiClient(content_api_url, jwt=access_token)

    page = 1
    content_table = {}

    # read the courses and create a Sailthru content item for each course_run

    while page:
        # get a page of courses
        response = client.courses().get(limit=500, offset=(page-1)*500)
        results = response['results']

        if response['next']:
            page += 1
        else:
            page = None

        for course in results:
            for course_run in course['course_runs']:

                content_table[course_run['key']] = create_sailthru_content(course, course_run, lms_url)

    logger.info('Retrieved %d course runs.', len(content_table))
    return content_table
Example #8
0
class CatalogApiService(object):
    """The service to interface with edX catalog API"""
    def __init__(self, access_token, oauth_host, oauth_key, oauth_secret,
                 api_url_root):
        self.access_token = access_token
        if not access_token:
            logger.info(
                'No access token provided. Retrieving access token using client_credential flow...'
            )
            try:
                self.access_token, expires = EdxRestApiClient.get_oauth_access_token(
                    '{root}/access_token'.format(root=oauth_host),
                    oauth_key,
                    oauth_secret,
                    token_type='jwt')
            except Exception:
                logger.exception(
                    'No access token provided or acquired through client_credential flow.'
                )
                raise

            logger.info('Token retrieved: %s', access_token)

        self.api_client = EdxRestApiClient(api_url_root, jwt=self.access_token)
        self._programs_dictionary = {}

    def _get_resource_from_api(self, api_endpoint, page_size, **kwargs):
        page = 0
        results = []

        while page >= 0:
            response = api_endpoint.get(limit=page_size,
                                        offset=(page * page_size),
                                        **kwargs)
            if response.get('next'):
                page += 1
            else:
                page = -1
            results.extend(response.get('results'))

        return results

    def get_courses(self):
        logger.debug('Get Courses called')
        return self._get_resource_from_api(self.api_client.courses(),
                                           COURSES_PAGE_SIZE,
                                           marketable=1)

    def get_program_dictionary(self):
        if not self._programs_dictionary:
            program_array = self._get_resource_from_api(
                self.api_client.programs(),
                PROGRAMS_PAGE_SIZE,
                marketable=1,
                published_course_runs_only=1)
            for program in program_array:
                self._programs_dictionary[program['uuid']] = program
        return self._programs_dictionary
Example #9
0
def get_course_info_from_lms(course_key):
    """ Get course information from LMS via the course api and cache """
    api = EdxRestApiClient(get_lms_url('api/courses/v1/'))
    cache_key = 'courses_api_detail_{}'.format(course_key)
    cache_hash = hashlib.md5(cache_key).hexdigest()
    course = cache.get(cache_hash)
    if not course:  # pragma: no cover
        course = api.courses(course_key).get()
        cache.set(cache_hash, course, settings.COURSES_API_CACHE_TIMEOUT)
    return course
Example #10
0
def get_course_info_from_lms(course_key):
    """ Get course information from LMS via the course api and cache """
    api = EdxRestApiClient(get_lms_url('api/courses/v1/'))
    cache_key = 'courses_api_detail_{}'.format(course_key)
    cache_hash = hashlib.md5(cache_key.encode('utf-8')).hexdigest()
    course = cache.get(cache_hash)
    if not course:  # pragma: no cover
        course = api.courses(course_key).get()
        cache.set(cache_hash, course, settings.COURSES_API_CACHE_TIMEOUT)
    return course
Example #11
0
 def get_context_data(self, **kwargs):
     context = super(CouponOfferView, self).get_context_data(**kwargs)
     footer = get_lms_footer()
     code = self.request.GET.get('code', None)
     if code is not None:
         voucher, product = get_voucher(code=code)
         valid_voucher, msg = voucher_is_valid(voucher, product,
                                               self.request)
         if valid_voucher:
             api = EdxRestApiClient(get_lms_url('api/courses/v1/'), )
             try:
                 course = api.courses(product.course_id).get()
             except SlumberHttpBaseException as e:
                 logger.exception('Could not get course information. [%s]',
                                  e)
                 return {
                     'error':
                     _('Could not get course information. [{error}]'.format(
                         error=e)),
                     'footer':
                     footer
                 }
             course['image_url'] = get_lms_url(
                 course['media']['course_image']['uri'])
             stock_records = voucher.offers.first(
             ).benefit.range.catalog.stock_records.first()
             benefit_type = voucher.offers.first().benefit.type
             benefit_value = voucher.offers.first().benefit.value
             price = stock_records.price_excl_tax
             if benefit_type == 'Percentage':
                 new_price = price - (price * (benefit_value / 100))
             else:
                 new_price = price - benefit_value
                 if new_price < 0:
                     new_price = 0.00
             context.update({
                 'benefit_type':
                 benefit_type,
                 'benefit_value':
                 benefit_value,
                 'course':
                 course,
                 'code':
                 code,
                 'price':
                 price,
                 'new_price':
                 "%.2f" % new_price,
                 'verified': (product.attr.certificate_type == 'verified'),
                 'footer':
                 footer
             })
             return context
         return {'error': msg, 'footer': footer}
     return {'error': _('This coupon code is invalid.'), 'footer': footer}
Example #12
0
 def get_context_data(self, **kwargs):
     context = super(CouponOfferView, self).get_context_data(**kwargs)
     footer = get_lms_footer()
     code = self.request.GET.get('code', None)
     if code is not None:
         voucher, product = get_voucher_from_code(code=code)
         valid_voucher, msg = voucher_is_valid(voucher, product, self.request)
         if valid_voucher:
             api = EdxRestApiClient(
                 get_lms_url('api/courses/v1/'),
             )
             try:
                 course = api.courses(product.course_id).get()
             except SlumberHttpBaseException as e:
                 logger.exception('Could not get course information. [%s]', e)
                 return {
                     'error': _('Could not get course information. [{error}]'.format(error=e)),
                     'footer': footer
                 }
             course['image_url'] = get_lms_url(course['media']['course_image']['uri'])
             benefit = voucher.offers.first().benefit
             stock_record = benefit.range.catalog.stock_records.first()
             price = stock_record.price_excl_tax
             context.update(get_voucher_discount_info(benefit, price))
             if benefit.type == 'Percentage':
                 new_price = price - (price * (benefit.value / 100))
             else:
                 new_price = price - benefit.value
                 if new_price < 0:
                     new_price = Decimal(0)
             context.update({
                 'benefit': benefit,
                 'course': course,
                 'code': code,
                 'is_discount_value_percentage': benefit.type == 'Percentage',
                 'is_enrollment_code': benefit.type == Benefit.PERCENTAGE and benefit.value == 100.00,
                 'discount_value': "%.2f" % (price - new_price),
                 'price': price,
                 'new_price': "%.2f" % new_price,
                 'verified': (product.attr.certificate_type == 'verified'),
                 'verification_deadline': product.course.verification_deadline,
                 'footer': footer
             })
             return context
         return {
             'error': msg,
             'footer': footer
         }
     return {
         'error': _('This coupon code is invalid.'),
         'footer': footer
     }
class CatalogApiService(object):
    """The service to interface with edX catalog API"""

    def __init__(self, access_token, oauth_host, oauth_key, oauth_secret, api_url_root):
        self.access_token = access_token
        if not access_token:
            logger.info('No access token provided. Retrieving access token using client_credential flow...')
            try:
                self.access_token, expires = EdxRestApiClient.get_oauth_access_token(
                    '{root}/access_token'.format(root=oauth_host),
                    oauth_key,
                    oauth_secret, token_type='jwt'
                )
            except Exception:
                logger.exception('No access token provided or acquired through client_credential flow.')
                raise

            logger.info('Token retrieved: %s', access_token)

        self.api_client = EdxRestApiClient(api_url_root, jwt=self.access_token)
        self._programs_dictionary = {}

    def _get_resource_from_api(self, api_endpoint, page_size, **kwargs):
        page = 0
        results = []

        while page >= 0:
            response = api_endpoint.get(limit=page_size, offset=(page * page_size), **kwargs)
            if response.get('next'):
                page += 1
            else:
                page = -1
            results.extend(response.get('results'))

        return results

    def get_courses(self):
        logger.debug('Get Courses called')
        return self._get_resource_from_api(self.api_client.courses(), COURSES_PAGE_SIZE, marketable=1)

    def get_program_dictionary(self):
        if not self._programs_dictionary:
            program_array = self._get_resource_from_api(
                self.api_client.programs(),
                PROGRAMS_PAGE_SIZE,
                marketable=1,
                published_course_runs_only=1
            )
            for program in program_array:
                self._programs_dictionary[program['uuid']] = program
        return self._programs_dictionary
    def _get_courses_enrollment_info(self):
        """
        Retrieve the enrollment information for all the courses.

        Returns:
            Dictionary representing the key-value pair (course_key, enrollment_end) of course.
        """
        def _parse_response(api_response):
            response_data = api_response.get('results', [])

            # Map course_id with enrollment end date.
            courses_enrollment = dict(
                (course_info['course_id'], course_info['enrollment_end'])
                for course_info in response_data
            )
            return courses_enrollment, api_response['pagination'].get('next', None)

        querystring = {'page_size': 50}
        api = EdxRestApiClient(get_lms_url('api/courses/v1/'))
        course_enrollments = {}

        page = 0
        throttling_attempts = 0
        next_page = True
        while next_page:
            page += 1
            querystring['page'] = page
            try:
                response = api.courses().get(**querystring)
                throttling_attempts = 0
            except HttpClientError as exc:
                # this is a known limitation; If we get HTTP429, we need to pause execution for a few seconds
                # before re-requesting the data. raise any other errors
                if exc.response.status_code == 429 and throttling_attempts < self.max_tries:
                    logger.warning(
                        'API calls are being rate-limited. Waiting for [%d] seconds before retrying...',
                        self.pause_time
                    )
                    time.sleep(self.pause_time)
                    page -= 1
                    throttling_attempts += 1
                    logger.info('Retrying [%d]...', throttling_attempts)
                    continue
                else:
                    raise
            enrollment_info, next_page = _parse_response(response)
            course_enrollments.update(enrollment_info)
        return course_enrollments
    def _get_courses_enrollment_info(self):
        """
        Retrieve the enrollment information for all the courses.

        Returns:
            Dictionary representing the key-value pair (course_key, enrollment_end) of course.
        """
        def _parse_response(api_response):
            response_data = api_response.get('results', [])

            # Map course_id with enrollment end date.
            courses_enrollment = dict(
                (course_info['course_id'], course_info['enrollment_end'])
                for course_info in response_data
            )
            return courses_enrollment, api_response['pagination'].get('next', None)

        querystring = {'page_size': 50}
        api = EdxRestApiClient(get_lms_url('api/courses/v1/'))
        course_enrollments = {}

        page = 0
        throttling_attempts = 0
        next_page = True
        while next_page:
            page += 1
            querystring['page'] = page
            try:
                response = api.courses().get(**querystring)
                throttling_attempts = 0
            except HttpClientError as exc:
                # this is a known limitation; If we get HTTP429, we need to pause execution for a few seconds
                # before re-requesting the data. raise any other errors
                if exc.response.status_code == 429 and throttling_attempts < self.max_tries:
                    logger.warning(
                        'API calls are being rate-limited. Waiting for [%d] seconds before retrying...',
                        self.pause_time
                    )
                    time.sleep(self.pause_time)
                    page -= 1
                    throttling_attempts += 1
                    logger.info('Retrying [%d]...', throttling_attempts)
                    continue
                else:
                    raise
            enrollment_info, next_page = _parse_response(response)
            course_enrollments.update(enrollment_info)
        return course_enrollments
Example #16
0
    def refresh(cls, course_id, access_token):
        """
        Refresh the course data from the raw data sources.

        Args:
            course_id (str): Course ID
            access_token (str): OAuth access token

        Returns:
            Course
        """
        client = EdxRestApiClient(settings.ECOMMERCE_API_URL, oauth_access_token=access_token)
        body = client.courses(course_id).get(include_products=True)
        course = Course(course_id, body)
        course.save()
        return course
Example #17
0
    def refresh(cls, course_id, access_token):
        """
        Refresh the course data from the raw data sources.

        Args:
            course_id (str): Course ID
            access_token (str): OAuth access token

        Returns:
            Course
        """
        client = EdxRestApiClient(settings.ECOMMERCE_API_URL,
                                  oauth_access_token=access_token)
        body = client.courses(course_id).get(include_products=True)
        course = Course(course_id, body)
        course.save()
        return course
Example #18
0
    def _query_course_structure_api(self):
        """Get course name from the Course Structure API."""
        api_client = EdxRestApiClient(
            self.site_configuration.build_lms_url('/api/course_structure/v0/'),
            jwt=self.site_configuration.access_token)
        data = api_client.courses(self.course.id).get()
        logger.debug(data)

        course_name = data.get('name')
        if course_name is None:
            message = 'Aborting migration. No name is available for {}.'.format(
                self.course.id)
            logger.error(message)
            raise Exception(message)

        # A course without entries in the LMS CourseModes table must be an honor course, meaning
        # it has no verification deadline.
        course_verification_deadline = None

        return course_name.strip(), course_verification_deadline
Example #19
0
    def get_context_data(self, **kwargs):
        context = super(BasketSummaryView, self).get_context_data(**kwargs)
        lines = context.get('line_list', [])
        api = EdxRestApiClient(get_lms_url('api/courses/v1/'))
        for line in lines:
            course_id = line.product.course_id

            # Get each course type so we can display to the user at checkout.
            try:
                line.certificate_type = get_certificate_type_display_value(line.product.attr.certificate_type)
            except ValueError:
                line.certificate_type = None

            cache_key = 'courses_api_detail_{}'.format(course_id)
            cache_hash = hashlib.md5(cache_key).hexdigest()
            try:
                course = cache.get(cache_hash)
                if not course:
                    course = api.courses(course_id).get()
                    course['image_url'] = get_lms_url(course['media']['course_image']['uri'])
                    cache.set(cache_hash, course, settings.COURSES_API_CACHE_TIMEOUT)
                line.course = course
            except (ConnectionError, SlumberBaseException, Timeout):
                logger.exception('Failed to retrieve data from Course API for course [%s].', course_id)

            if line.has_discount:
                line.discount_percentage = line.discount_value / line.unit_price_incl_tax * Decimal(100)
            else:
                line.discount_percentage = 0

        context.update({
            'payment_processors': self.get_payment_processors(),
            'homepage_url': get_lms_url(''),
            'footer': get_lms_footer(),
            'lines': lines,
            'faq_url': get_lms_url('') + '/verified-certificate',
        })
        return context
Example #20
0
    def refresh_all_ecommerce_data(cls, access_token):
        ecommerce_api_url = settings.ECOMMERCE_API_URL
        client = EdxRestApiClient(ecommerce_api_url, oauth_access_token=access_token)
        count = None
        page = 1

        logger.info('Refreshing ecommerce data from %s....', ecommerce_api_url)

        while page:
            response = client.courses().get(include_products=True, page=page, page_size=50)
            count = response['count']
            results = response['results']
            logger.info('Retrieved %d courses...', len(results))

            if response['next']:
                page += 1
            else:
                page = None

            for body in results:
                Course(body['id']).update(body)

        logger.info('Retrieved %d courses from %s.', count, ecommerce_api_url)
Example #21
0
    def get_context_data(self, **kwargs):
        context = super(CouponOfferView, self).get_context_data(**kwargs)

        code = self.request.GET.get('code', None)
        if code is not None:
            voucher, product = get_voucher(code=code)
            valid_voucher, msg = voucher_is_valid(voucher, product,
                                                  self.request)
            if valid_voucher:
                api = EdxRestApiClient(get_lms_url('api/courses/v1/'), )
                try:
                    course = api.courses(product.course_id).get()
                except SlumberHttpBaseException as e:
                    logger.exception('Could not get course information. [%s]',
                                     e)
                    return {
                        'error':
                        _('Could not get course information. [{error}]'.format(
                            error=e))
                    }

                course['image_url'] = get_lms_url(
                    course['media']['course_image']['uri'])
                stock_records = voucher.offers.first(
                ).benefit.range.catalog.stock_records.first()
                context.update({
                    'course':
                    course,
                    'code':
                    code,
                    'price':
                    stock_records.price_excl_tax,
                    'verified': (product.attr.certificate_type is 'verified')
                })
                return context
            return {'error': msg}
        return {'error': _('This coupon code is invalid.')}
Example #22
0
    def get_context_data(self, **kwargs):
        context = super(BasketSummaryView, self).get_context_data(**kwargs)
        formset = context.get('formset', [])
        lines = context.get('line_list', [])
        lines_data = []
        api = EdxRestApiClient(get_lms_url('api/courses/v1/'))
        is_verification_required = False
        for line in lines:
            course_key = CourseKey.from_string(line.product.attr.course_key)
            cache_key = 'courses_api_detail_{}'.format(course_key)
            cache_hash = hashlib.md5(cache_key).hexdigest()
            course_name = None
            image_url = None
            short_description = None
            try:
                course = cache.get(cache_hash)
                if not course:
                    course = api.courses(course_key).get()
                    cache.set(cache_hash, course, settings.COURSES_API_CACHE_TIMEOUT)
                image_url = get_lms_url(course['media']['course_image']['uri'])
                short_description = course['short_description']
                course_name = course['name']
            except (ConnectionError, SlumberBaseException, Timeout):
                logger.exception('Failed to retrieve data from Course API for course [%s].', course_key)

            if line.has_discount:
                benefit = self.request.basket.applied_offers().values()[0].benefit
                benefit_value = format_benefit_value(benefit)
            else:
                benefit_value = None

            lines_data.append({
                'seat_type': self._determine_seat_type(line.product),
                'course_name': course_name,
                'course_key': course_key,
                'image_url': image_url,
                'course_short_description': short_description,
                'benefit_value': benefit_value,
                'enrollment_code': line.product.get_product_class().name == ENROLLMENT_CODE_PRODUCT_CLASS_NAME,
                'line': line,
            })

            context.update({
                'analytics_data': prepare_analytics_data(
                    self.request.user,
                    self.request.site.siteconfiguration.segment_key,
                    unicode(course_key)
                ),
            })

            # Check product attributes to determine if ID verification is required for this basket
            try:
                is_verification_required = is_verification_required or line.product.attr.id_verification_required
            except AttributeError:
                pass

        context.update({
            'free_basket': context['order_total'].incl_tax == 0,
            'payment_processors': self.request.site.siteconfiguration.get_payment_processors(),
            'homepage_url': get_lms_url(''),
            'formset_lines_data': zip(formset, lines_data),
            'is_verification_required': is_verification_required,
        })
        return context
def process_load(args, sc, lms_url, temp_file):
    """ Process load command
    """

    # Try to get edX access token if not supplied
    access_token = args.access_token
    if not access_token:
        logger.info(
            'No access token provided. Retrieving access token using client_credential flow...'
        )

        try:
            access_token, expires = EdxRestApiClient.get_oauth_access_token(
                '{root}/access_token'.format(root=args.oauth_host),
                args.oauth_key,
                args.oauth_secret,
                token_type='jwt')
        except Exception:
            logger.exception(
                'No access token provided or acquired through client_credential flow.'
            )
            raise

    logger.info('Token retrieved: %s', access_token)

    # use programs api to build table of course runs that are part of xseries
    series_table = load_series_table()

    # load any fixups
    fixups = load_fixups(args.fixups)
    logger.info(fixups)

    client = EdxRestApiClient(args.content_api_url, jwt=access_token)

    count = None
    page = 1
    course_runs = 0

    # read the courses and create a Sailthru content item for each course_run within the course

    while page:
        # get a page of courses
        response = client.courses().get(limit=500, offset=(page - 1) * 500)

        count = response['count']
        results = response['results']

        if response['next']:
            page += 1
        else:
            page = None

        for course in results:
            for course_run in course['course_runs']:

                sailthru_content = create_sailthru_content(
                    course, course_run, series_table, lms_url, fixups)

                if sailthru_content:
                    course_runs += 1

                    if sc:
                        try:
                            response = sc.api_post('content', sailthru_content)
                        except SailthruClientError as exc:
                            logger.exception(
                                "Exception attempting to update Sailthru", exc)
                            # wait 10 seconds and retry
                            time.sleep(10)
                            response = sc.api_post('content', sailthru_content)

                        if not response.is_ok():
                            logger.error(
                                "Error code %d connecting to Sailthru content api: %s",
                                response.json['error'],
                                response.json['errormsg'])
                            return

                        logger.info(
                            "Course: %s, Course_run: %s saved in Sailthru.",
                            course['key'], course_run['key'])

                    elif temp_file:
                        json.dump(sailthru_content, temp_file)
                        temp_file.write('\n')
                        logger.info(
                            "Course: %s, Course_run: %s being updated.",
                            course['key'], course_run['key'])

                    else:
                        logger.info(sailthru_content)

    logger.info('Retrieved %d courses.', count)
    logger.info('Saved %d course runs in Sailthru.', course_runs)
Example #24
0
def process_load(args, sc, lms_url, test):
    """ Process load command
    :param sc: Sailthru client
    :return:
    """

    # Try to get edX access token if not supplied
    access_token = args.access_token
    if not access_token:
        logger.info('No access token provided. Retrieving access token using client_credential flow...')

        try:
            access_token, __ = EdxRestApiClient.get_oauth_access_token(
                '{root}/access_token'.format(root=args.oauth_host),
                args.oauth_key,
                args.oauth_seret
            )
        except Exception:
            logger.exception('No access token provided or acquired through client_credential flow.')
            raise

    # logger.info('Token retrieved: %s', access_token)

    # use programs api to build table of course runs that are part of xseries
    series_table = load_series_table()

    client = EdxRestApiClient(args.content_api_url, oauth_access_token=access_token)

    count = None
    page = 1
    course_runs = 0

    # read the courses and create a Sailthru content item for each course_run within the course

    while page:
        # get a page of courses
        response = client.courses().get(limit=20, offset=(page-1)*20)

        count = response['count']
        results = response['results']

        if response['next']:
            page += 1
        else:
            page = None

        for course in results:
            for course_run in course['course_runs']:

                sailthru_content = create_sailthru_content(course, course_run, series_table, lms_url)

                if sailthru_content:
                    course_runs += 1

                    if not test:
                        response = sc.api_post('content', sailthru_content)
                        if not response.is_ok():
                            logger.error("Error code %d connecting to Sailthru content api: %s",
                                         response.json['error'],
                                         response.json['errormsg'])
                            return

                        logger.info("Course: %s, Course_run: %s saved in Sailthru.", course['key'], course_run['key'])

                    else:
                        logger.info(sailthru_content)

    logger.info('Retrieved %d courses.', count)
    logger.info('Saved %d course runs in Sailthru.', course_runs)
Example #25
0
    def get_context_data(self, **kwargs):
        context = super(CouponOfferView, self).get_context_data(**kwargs)
        code = self.request.GET.get('code', None)
        if code is not None:
            try:
                voucher, product = get_voucher_from_code(code=code)
            except Voucher.DoesNotExist:
                return {
                    'error': _('Coupon does not exist'),
                }
            except exceptions.ProductNotFoundError:
                return {
                    'error':
                    _('The voucher is not applicable to your current basket.'),
                }
            valid_voucher, msg = voucher_is_valid(voucher, product,
                                                  self.request)
            if valid_voucher:
                api = EdxRestApiClient(get_lms_url('api/courses/v1/'), )
                try:
                    course = api.courses(product.course_id).get()
                except SlumberHttpBaseException as e:
                    logger.exception('Could not get course information. [%s]',
                                     e)
                    return {
                        'error':
                        _('Could not get course information. [{error}]'.format(
                            error=e)),
                    }
                course['image_url'] = get_lms_url(
                    course['media']['course_image']['uri'])
                benefit = voucher.offers.first().benefit
                # Note (multi-courses): fix this to work for all stock records / courses.
                if benefit.range.catalog:
                    stock_record = benefit.range.catalog.stock_records.first()
                else:
                    stock_record = StockRecord.objects.get(
                        product=benefit.range.included_products.first())
                price = stock_record.price_excl_tax
                benefit_value = format_benefit_value(benefit)
                if benefit.type == 'Percentage':
                    new_price = price - (price * (benefit.value / 100))
                else:
                    new_price = price - benefit.value
                    if new_price < 0:
                        new_price = Decimal(0)

                context.update({
                    'analytics_data':
                    prepare_analytics_data(
                        self.request.user,
                        self.request.site.siteconfiguration.segment_key,
                        product.course_id),
                    'benefit_value':
                    benefit_value,
                    'course':
                    course,
                    'code':
                    code,
                    'is_discount_value_percentage':
                    benefit.type == 'Percentage',
                    'is_enrollment_code':
                    benefit.type == Benefit.PERCENTAGE
                    and benefit.value == 100.00,
                    'discount_value':
                    "%.2f" % (price - new_price),
                    'price':
                    price,
                    'new_price':
                    "%.2f" % new_price,
                    'verified': (product.attr.certificate_type == 'verified'),
                    'verification_deadline':
                    product.course.verification_deadline,
                })
                return context
            return {
                'error': msg,
            }
        return {
            'error': _('This coupon code is invalid.'),
        }
Example #26
0
    def get_context_data(self, **kwargs):
        context = super(BasketSummaryView, self).get_context_data(**kwargs)
        formset = context.get('formset', [])
        lines = context.get('line_list', [])
        lines_data = []
        api = EdxRestApiClient(get_lms_url('api/courses/v1/'))
        is_verification_required = False
        for line in lines:
            course_key = CourseKey.from_string(line.product.attr.course_key)
            cache_key = 'courses_api_detail_{}'.format(course_key)
            cache_hash = hashlib.md5(cache_key).hexdigest()
            course_name = None
            image_url = None
            short_description = None
            try:
                course = cache.get(cache_hash)
                if not course:
                    course = api.courses(course_key).get()
                    cache.set(cache_hash, course, settings.COURSES_API_CACHE_TIMEOUT)
                image_url = get_lms_url(course['media']['course_image']['uri'])
                short_description = course['short_description']
                course_name = course['name']
            except (ConnectionError, SlumberBaseException, Timeout):
                logger.exception('Failed to retrieve data from Course API for course [%s].', course_key)

            if line.has_discount:
                benefit = self.request.basket.applied_offers().values()[0].benefit
                benefit_value = format_benefit_value(benefit)
            else:
                benefit_value = None

            lines_data.append({
                'seat_type': self._determine_seat_type(line.product),
                'course_name': course_name,
                'course_key': course_key,
                'image_url': image_url,
                'course_short_description': short_description,
                'benefit_value': benefit_value,
                'enrollment_code': line.product.get_product_class().name == ENROLLMENT_CODE_PRODUCT_CLASS_NAME,
                'line': line,
            })

            context.update({
                'analytics_data': prepare_analytics_data(
                    self.request.user,
                    self.request.site.siteconfiguration.segment_key,
                    unicode(course_key)
                ),
            })

            # Check product attributes to determine if ID verification is required for this basket
            try:
                is_verification_required = is_verification_required or line.product.attr.id_verification_required
            except AttributeError:
                pass

        context.update({
            'free_basket': context['order_total'].incl_tax == 0,
            'payment_processors': self.request.site.siteconfiguration.get_payment_processors(),
            'homepage_url': get_lms_url(''),
            'formset_lines_data': zip(formset, lines_data),
            'is_verification_required': is_verification_required,
        })
        return context
Example #27
0
class CatalogApiService(object):
    """The service to interface with edX catalog API"""
    def __init__(self, access_token, oauth_host, oauth_key, oauth_secret,
                 api_url_root):
        self.access_token = access_token
        if not access_token:
            logger.info(
                'No access token provided. Retrieving access token using client_credential flow...'
            )
            try:
                access_token_url = '{}/access_token'.format(oauth_host)
                self.access_token, __ = EdxRestApiClient.get_oauth_access_token(
                    access_token_url,
                    oauth_key,
                    oauth_secret,
                    token_type='jwt')
            except Exception:
                logger.exception(
                    'No access token provided or acquired through client_credential flow.'
                )
                raise

            logger.info('Retrieved access token.')

        self.api_client = EdxRestApiClient(api_url_root, jwt=self.access_token)
        self._programs_dictionary = {}

    def _get_resource_from_api(self, api_endpoint, **kwargs):
        results = []

        while True:
            response = api_endpoint.get(**kwargs)
            next_url = response.get('next')
            results.extend(response.get('results'))
            if next_url:
                parsed_url = urlparse(next_url)
                query_string_dict = parse_qs(parsed_url.query)
                kwargs = query_string_dict
            else:
                break

        return results

    def get_courses(self):
        logger.debug('Get Courses called')
        return self._get_resource_from_api(self.api_client.courses(),
                                           page=1,
                                           page_size=COURSES_PAGE_SIZE,
                                           exclude_utm=1)

    def get_marketable_only_course_runs_keys(self):
        logger.debug('Get marketable only course_runs called')
        courses = self._get_resource_from_api(
            self.api_client.courses(),
            page=1,
            page_size=COURSES_PAGE_SIZE,
            exclude_utm=1,
            marketable_course_runs_only=1,
        )
        course_run_keys = []
        for course in courses:
            course_runs = course.get('course_runs', [])
            for course_run in course_runs:
                course_run_keys.append(course_run.get('key'))

        logging.debug('Retrieved {} marketable course runs'.format(
            len(course_run_keys)))
        return course_run_keys

    def get_program_dictionary(self):
        if not self._programs_dictionary:
            program_array = self._get_resource_from_api(
                self.api_client.programs(),
                page=1,
                page_size=PROGRAMS_PAGE_SIZE,
                marketable_course_runs_only=1,
                marketable=1)
            for program in program_array:
                self._programs_dictionary[program['uuid']] = program
        return self._programs_dictionary