Exemple #1
0
    def get_offers(self, request, voucher):
        """
        Get the course offers associated with the voucher.
        Arguments:
            request (HttpRequest): Request data.
            voucher (Voucher): Oscar Voucher for which the offers are returned.
        Returns:
            dict: Dictionary containing a link to the next page of Course Discovery results and
                  a List of course offers where each offer is represented as a dictionary.
        """
        benefit = voucher.offers.first().benefit
        catalog_query = benefit.range.catalog_query
        catalog_id = benefit.range.course_catalog
        next_page = None
        offers = []

        if catalog_id:
            catalog = fetch_course_catalog(request.site, catalog_id)
            catalog_query = catalog.get("query") if catalog else catalog_query

        if catalog_query:
            offers, next_page = self.get_offers_from_query(
                request, voucher, catalog_query)
        else:
            product_range = voucher.offers.first().benefit.range
            products = product_range.all_products()
            if products:
                product = products[0]
            else:
                raise Product.DoesNotExist
            course_id = product.course_id
            course = get_object_or_404(Course, id=course_id)
            stock_record = get_object_or_404(StockRecord,
                                             product__id=product.id)
            course_info = get_course_info_from_lms(course_id)

            if course_info:
                course_info['image'] = {
                    'src':
                    get_lms_url(course_info['media']['course_image']['uri'])
                }

                offers.append(
                    self.get_course_offer_data(
                        benefit=benefit,
                        course=course,
                        course_info=course_info,
                        credit_provider_price=None,
                        multiple_credit_providers=False,
                        is_verified=(course.type == 'verified'),
                        product=product,
                        stock_record=stock_record,
                        voucher=voucher))

        return {'next': next_page, 'results': offers}
Exemple #2
0
    def test_get_course_info_from_lms(self):
        """ Check to see if course info gets cached """
        course = CourseFactory()
        course_url = get_lms_url('api/courses/v1/courses/{}/'.format(course.id))
        httpretty.register_uri(httpretty.GET, course_url, body=course.name, status=200, content_type='application/json')

        cache_key = 'courses_api_detail_{}'.format(course.id)
        cache_hash = hashlib.md5(cache_key).hexdigest()
        cached_course = cache.get(cache_hash)
        self.assertIsNone(cached_course)

        response = get_course_info_from_lms(course.id)
        self.assertEqual(response, course.name)

        cached_course = cache.get(cache_hash)
        self.assertEqual(cached_course, response)
Exemple #3
0
    def test_get_course_info_from_lms(self):
        """ Check to see if course info gets cached """
        course = CourseFactory()
        course_url = get_lms_url('api/courses/v1/courses/{}/'.format(
            course.id))
        httpretty.register_uri(httpretty.GET,
                               course_url,
                               body=course.name,
                               status=200,
                               content_type='application/json')

        cache_key = 'courses_api_detail_{}'.format(course.id)
        cache_key = hashlib.md5(cache_key).hexdigest()
        cached_course = cache.get(cache_key)
        self.assertIsNone(cached_course)

        response = get_course_info_from_lms(course.id)
        self.assertEqual(response, course.name)

        cached_course = cache.get(cache_key)
        self.assertEqual(cached_course, response)
Exemple #4
0
    def _get_course_data(self, product):
        """
        Return course data.

        Args:
            product (Product): A product that has course_key as attribute (seat or bulk enrollment coupon)
        Returns:
            Dictionary containing course name, course key, course image URL and description.
        """
        course_key = CourseKey.from_string(product.attr.course_key)
        course_name = None
        image_url = None
        short_description = None
        course_start = None
        course_end = None

        try:
            course = get_course_info_from_lms(course_key)
            try:
                image_url = course['media']['image']['raw']
            except (KeyError, TypeError):
                image_url = ''
            short_description = course.get('short_description', '')
            course_name = course['name']
        except (ConnectionError, SlumberBaseException, Timeout):
            logger.exception(
                'Failed to retrieve data from Course API for course [%s].',
                course_key)

        return {
            'product_title': course_name,
            'course_key': course_key,
            'image_url': image_url,
            'product_description': short_description,
            'course_start': course_start,
            'course_end': course_end,
        }
Exemple #5
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 = []
        is_verification_required = is_bulk_purchase = False
        switch_link_text = partner_sku = ''

        for line in lines:
            course_key = CourseKey.from_string(line.product.attr.course_key)
            course_name = None
            image_url = None
            short_description = None
            try:
                course = get_course_info_from_lms(course_key)
                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)

            # Set to true if any course in basket has bulk purchase scenario
            if line.product.get_product_class().name == ENROLLMENT_CODE_PRODUCT_CLASS_NAME:
                is_bulk_purchase = True
                # Iterate on message storage so all messages are marked as read
                list(messages.get_messages(self.request))

            # Get variables for alternative basket view
            switch_link_text, partner_sku = get_basket_switch_data(line.product)

            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,
            'min_seat_quantity': 1,
            'is_bulk_purchase': is_bulk_purchase,
            'switch_link_text': switch_link_text,
            'partner_sku': partner_sku,
        })

        return context
Exemple #6
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:
                try:
                    course = get_course_info_from_lms(product.course_id)
                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.all_products()[0])
                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.'),
        }
Exemple #7
0
    def get_offers(self, products, request, voucher):
        """
        Get the course offers associated with the voucher.
        Arguments:
            products (List): List of Products associated with the voucher
            request (HttpRequest): Request data
            voucher (Voucher): Oscar Voucher for which the offers are returned
        Returns:
            dict: Dictionary containing a link to the next page of Course Discovery results and
                  a List of course offers where each offer is represented as a dictionary
        """
        benefit = voucher.offers.first().benefit
        catalog_query = benefit.range.catalog_query
        next_page = None
        offers = []
        if catalog_query:
            response = get_range_catalog_query_results(
                limit=request.GET.get('limit', DEFAULT_CATALOG_PAGE_SIZE),
                offset=request.GET.get('offset'),
                query=catalog_query,
                site=request.site)
            next_page = response['next']
            course_ids = [product.course_id for product in products]
            courses = Course.objects.filter(id__in=course_ids)
            stock_records = StockRecord.objects.filter(product__in=products)
            contains_verified_course = (
                benefit.range.course_seat_types == 'verified')

            for product in products:
                # Omit unavailable seats from the offer results so that one seat does not cause an
                # error message for every seat in the query result.
                if not request.strategy.fetch_for_product(
                        product).availability.is_available_to_buy:
                    logger.info(
                        '%s is unavailable to buy. Omitting it from the results.',
                        product)
                    continue
                course_id = product.course_id
                course_catalog_data = next(
                    (result for result in response['results']
                     if result['key'] == course_id), None)

                try:
                    stock_record = stock_records.get(product__id=product.id)
                except StockRecord.DoesNotExist:
                    stock_record = None
                    logger.error('Stock Record for product %s not found.',
                                 product.id)

                try:
                    course = courses.get(id=course_id)
                except Course.DoesNotExist:
                    course = None
                    logger.error('Course %s not found.', course_id)

                if course_catalog_data and course and stock_record:
                    offers.append(
                        self.get_course_offer_data(
                            benefit=benefit,
                            course=course,
                            course_info=course_catalog_data,
                            is_verified=contains_verified_course,
                            stock_record=stock_record,
                            voucher=voucher))
        else:
            product = products[0]
            course_id = product.course_id
            course = get_object_or_404(Course, id=course_id)
            stock_record = get_object_or_404(StockRecord,
                                             product__id=product.id)
            course_info = get_course_info_from_lms(course_id)

            if course_info:
                course_info['image'] = {
                    'src':
                    get_lms_url(course_info['media']['course_image']['uri'])
                }

                offers.append(
                    self.get_course_offer_data(
                        benefit=benefit,
                        course=course,
                        course_info=course_info,
                        is_verified=(course.type == 'verified'),
                        stock_record=stock_record,
                        voucher=voucher))
        return {'next': next_page, 'results': offers}
Exemple #8
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, products = get_voucher_and_products_from_code(
                    code=code)
                product = products[0]
            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:
                try:
                    course = get_course_info_from_lms(product.course_id)
                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.all_products()[0])
                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.'),
        }
Exemple #9
0
    def get_offers(self, products, request, voucher):
        """
        Get the course offers associated with the voucher.
        Arguments:
            products (List): List of Products associated with the voucher
            request (HttpRequest): Request data
            voucher (Voucher): Oscar Voucher for which the offers are returned
        Returns:
            dict: Dictionary containing a link to the next page of Course Discovery results and
                  a List of course offers where each offer is represented as a dictionary
        """
        benefit = voucher.offers.first().benefit
        catalog_query = benefit.range.catalog_query
        next_page = None
        offers = []
        if catalog_query:
            response = get_range_catalog_query_results(
                limit=request.GET.get('limit', DEFAULT_CATALOG_PAGE_SIZE),
                offset=request.GET.get('offset'),
                query=catalog_query,
                site=request.site
            )
            next_page = response['next']
            course_ids = [product.course_id for product in products]
            courses = Course.objects.filter(id__in=course_ids)
            stock_records = StockRecord.objects.filter(product__in=products)
            contains_verified_course = (benefit.range.course_seat_types == 'verified')

            for product in products:
                # Omit unavailable seats from the offer results so that one seat does not cause an
                # error message for every seat in the query result.
                if not request.strategy.fetch_for_product(product).availability.is_available_to_buy:
                    logger.info('%s is unavailable to buy. Omitting it from the results.', product)
                    continue
                course_id = product.course_id
                course_catalog_data = next(
                    (result for result in response['results'] if result['key'] == course_id),
                    None
                )

                try:
                    stock_record = stock_records.get(product__id=product.id)
                except StockRecord.DoesNotExist:
                    stock_record = None
                    logger.error('Stock Record for product %s not found.', product.id)

                try:
                    course = courses.get(id=course_id)
                except Course.DoesNotExist:
                    course = None
                    logger.error('Course %s not found.', course_id)

                if course_catalog_data and course and stock_record:
                    offers.append(self.get_course_offer_data(
                        benefit=benefit,
                        course=course,
                        course_info=course_catalog_data,
                        is_verified=contains_verified_course,
                        stock_record=stock_record,
                        voucher=voucher
                    ))
        else:
            product = products[0]
            course_id = product.course_id
            course = get_object_or_404(Course, id=course_id)
            stock_record = get_object_or_404(StockRecord, product__id=product.id)
            course_info = get_course_info_from_lms(course_id)

            if course_info:
                course_info['image'] = {'src': get_lms_url(course_info['media']['course_image']['uri'])}

                offers.append(self.get_course_offer_data(
                    benefit=benefit,
                    course=course,
                    course_info=course_info,
                    is_verified=(course.type == 'verified'),
                    stock_record=stock_record,
                    voucher=voucher
                ))
        return {'next': next_page, 'results': offers}
Exemple #10
0
    def get_offers(self, products, request, voucher):
        """
        Get the course offers associated with the voucher.
        Arguments:
            products (List): List of Products associated with the voucher
            request (HttpRequest): Request data
            voucher (Voucher): Oscar Voucher for which the offers are returned
        Returns:
            List: List of course offers where each offer is represented by a dictionary
        """
        benefit = voucher.offers.first().benefit
        catalog_query = benefit.range.catalog_query
        offers = []

        if catalog_query:
            query_results = request.site.siteconfiguration.course_catalog_api_client.course_runs.get(
                q=catalog_query,
                page_size=DEFAULT_CATALOG_PAGE_SIZE,
                limit=DEFAULT_CATALOG_PAGE_SIZE
            )['results']

            course_ids = [product.course_id for product in products]
            courses = Course.objects.filter(id__in=course_ids)
            stock_records = StockRecord.objects.filter(product__in=products)
            contains_verified_course = (benefit.range.course_seat_types == 'verified')

            for product in products:
                course_id = product.course_id
                course_catalog_data = next((result for result in query_results if result['key'] == course_id), None)

                try:
                    stock_record = stock_records.get(product__id=product.id)
                except StockRecord.DoesNotExist:
                    stock_record = None
                    logger.error('Stock Record for product %s not found.', product.id)

                try:
                    course = courses.get(id=course_id)
                except Course.DoesNotExist:
                    course = None
                    logger.error('Course %s not found.', course_id)

                if course_catalog_data and course and stock_record:
                    offers.append(self.get_course_offer_data(
                        benefit=benefit,
                        course=course,
                        course_info=course_catalog_data,
                        is_verified=contains_verified_course,
                        stock_record=stock_record,
                        voucher=voucher
                    ))
        else:
            product = products[0]
            course_id = product.course_id
            course = get_object_or_404(Course, id=course_id)
            stock_record = get_object_or_404(StockRecord, product__id=product.id)
            course_info = get_course_info_from_lms(course_id)

            if course_info:
                course_info['image'] = {'src': get_lms_url(course_info['media']['course_image']['uri'])}

                offers.append(self.get_course_offer_data(
                    benefit=benefit,
                    course=course,
                    course_info=course_info,
                    is_verified=(course.type == 'verified'),
                    stock_record=stock_record,
                    voucher=voucher
                ))
        return offers