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}
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)
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)
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, }
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
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.'), }
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}
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.'), }
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}
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