Beispiel #1
0
 def send_email(self, order):
     """ Sends an email with enrollment code order information. """
     # Note (multi-courses): Change from a course_name to a list of course names.
     product = order.lines.first().product
     course = Course.objects.get(id=product.attr.course_key)
     send_notification(order.user,
                       'ORDER_WITH_CSV',
                       context={
                           'contact_url':
                           get_lms_url('/contact'),
                           'course_name':
                           course.name,
                           'download_csv_link':
                           get_ecommerce_url(
                               reverse('coupons:enrollment_code_csv',
                                       args=[order.number])),
                           'enrollment_code_title':
                           product.title,
                           'order_number':
                           order.number,
                           'partner_name':
                           order.site.siteconfiguration.partner.name,
                           'lms_url':
                           get_lms_url(),
                           'receipt_page_url':
                           get_lms_url('{}?orderNum={}'.format(
                               settings.RECEIPT_PAGE_PATH, order.number)),
                       },
                       site=order.site)
def get_lms_footer():
    """
    Retrieve LMS footer via branding API.

    Returns:
        str: HTML representation of the footer.
    """
    try:
        footer_api_url = get_lms_url('api/branding/v1/footer')
        response = requests.get(
            footer_api_url,
            data={'language': 'en'}
        )
        if response.status_code == 200:
            return response.text
        else:
            logger.error(
                'Unable to retrieve footer from %s. Branding API returned status code %d.',
                footer_api_url,
                response.status_code
            )
            return None
    except requests.exceptions.ConnectionError:
        logger.exception('Connection error occurred while retrieving footer from %s.', get_lms_url())
        return None
    except requests.Timeout:
        logger.exception('Connection timed out while retrieving footer from %s.', get_lms_url())
        return None
    def setUp(self):
        super(CheckoutPageTest, self).setUp()

        user = self.create_user(is_superuser=False)
        self.create_access_token(user)
        self.client.login(username=user.username, password=self.password)
        self.provider = 'ASU'
        self.price = 100
        self.credit_hours = 2
        self.eligibility_url = get_lms_url('/api/credit/v1/eligibility/')
        self.provider_url = get_lms_url('/api/credit/v1/providers/')
        self.course = CourseFactory(
            thumbnail_url='http://www.edx.org/course.jpg',
            partner=self.partner)

        self.provider_data = [{
            'enable_integration': False,
            'description': 'Arizona State University',
            'url': 'https://credit.example.com/',
            'status_url': 'https://credit.example.com/status',
            'thumbnail_url': 'http://edX/DemoX/asset/images_course_image.jpg',
            'fulfillment_instructions': 'Sample fulfilment requirement.',
            'display_name': 'Arizona State University',
            'id': 'ASU'
        }]

        self.eligibilities = [{
            'deadline': '2016-10-28T09:56:44Z',
            'course_key': 'edx/cs01/2015'
        }]
Beispiel #4
0
    def setUp(self):
        super(CheckoutPageTest, self).setUp()

        user = self.create_user(is_superuser=False)
        self.create_access_token(user)
        self.client.login(username=user.username, password=self.password)
        self.provider = 'ASU'
        self.price = 100
        self.credit_hours = 2
        self.eligibility_url = get_lms_url('/api/credit/v1/eligibility/')
        self.provider_url = get_lms_url('/api/credit/v1/providers/')
        self.course = CourseFactory(thumbnail_url='http://www.edx.org/course.jpg')

        self.provider_data = [
            {
                'enable_integration': False,
                'description': 'Arizona State University',
                'url': 'https://credit.example.com/',
                'status_url': 'https://credit.example.com/status',
                'thumbnail_url': 'http://edX/DemoX/asset/images_course_image.jpg',
                'fulfillment_instructions': 'Sample fulfilment requirement.',
                'display_name': 'Arizona State University',
                'id': 'ASU'
            }
        ]

        self.eligibilities = [
            {
                'deadline': '2016-10-28T09:56:44Z',
                'course_key': 'edx/cs01/2015'
            }
        ]
Beispiel #5
0
def send_course_purchase_email(sender, order=None, **kwargs):  # pylint: disable=unused-argument
    """Send course purchase notification email when a course is purchased."""
    if waffle.switch_is_active('ENABLE_NOTIFICATIONS'):
        # We do not currently support email sending for orders with more than one item.
        if len(order.lines.all()) == ORDER_LINE_COUNT:
            product = order.lines.first().product
            provider_id = getattr(product.attr, 'credit_provider', None)
            if not provider_id:
                stripped_title = product.title.replace("Seat in ", "", 1)
                stripped_title = stripped_title.replace(
                    "with professional certificate", "")
                stripped_title = stripped_title.replace(
                    "with verified certificate", "")
                send_notification(
                    order.user, 'CREDIT_RECEIPT', {
                        'course_title':
                        stripped_title,
                        'receipt_page_url':
                        get_lms_url('{}?orderNum={}'.format(
                            settings.RECEIPT_PAGE_PATH, order.number)),
                        'credit_hours':
                        str(order.total_excl_tax),
                        'credit_provider':
                        'Credit provider',
                    },
                    threadlocals.get_current_request().site)
                logger.error(
                    'Failed to send credit receipt notification. Credit seat product [%s] has no provider.',
                    product.id)
                return

            elif product.get_product_class().name == 'Seat':
                provider_data = get_provider_data(provider_id)
                if provider_data:
                    send_notification(
                        order.user, 'CREDIT_RECEIPT', {
                            'course_title':
                            product.title,
                            'receipt_page_url':
                            get_lms_url('{}?orderNum={}'.format(
                                settings.RECEIPT_PAGE_PATH, order.number)),
                            'credit_hours':
                            product.attr.credit_hours,
                            'credit_provider':
                            provider_data['display_name'],
                        },
                        threadlocals.get_current_request().site)

        else:
            logger.info(
                'Currently support receipt emails for order with one item.')
Beispiel #6
0
    def test_post_checkout_callback(self):
        """
        When the post_checkout signal is emitted, the receiver should attempt
        to fulfill the newly-placed order and send receipt email.
        """
        httpretty.register_uri(httpretty.GET,
                               get_lms_url('api/credit/v1/providers/ASU'),
                               body='{"display_name": "Hogwarts"}',
                               content_type="application/json")
        toggle_switch('ENABLE_NOTIFICATIONS', True)
        course = Course.objects.create(id='edX/DemoX/Demo_Course',
                                       name='Demo Course')
        seat = course.create_or_update_seat('credit', False, 50, self.partner,
                                            'ASU', None, 2)

        basket = BasketFactory()
        basket.add_product(seat, 1)
        order = factories.create_order(number=1, basket=basket, user=self.user)
        with mock.patch(
                'threadlocals.threadlocals.get_current_request') as mock_gcr:
            mock_gcr.return_value = self.request
            send_course_purchase_email(None, order=order)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].from_email,
                         self.site_configuration.from_email)
        self.assertEqual(mail.outbox[0].subject, 'Order Receipt')
        self.assertEqual(
            mail.outbox[0].body, '\nPayment confirmation for: {course_title}'
            '\n\nDear {full_name},'
            '\n\nThank you for purchasing {credit_hours} credit hours from {credit_provider} for {course_title}. '
            'A charge will appear on your credit or debit card statement with a company name of "{platform_name}".'
            '\n\nTo receive your course credit, you must also request credit at the {credit_provider} website. '
            'For a link to request credit from {credit_provider}, or to see the status of your credit request, '
            'go to your {platform_name} dashboard.'
            '\n\nTo explore other credit-eligible courses, visit the {platform_name} website. '
            'We add new courses frequently!'
            '\n\nTo view your payment information, visit the following website.'
            '\n{receipt_url}'
            '\n\nThank you. We hope you enjoyed your course!'
            '\nThe {platform_name} team'
            '\n\nYou received this message because you purchased credit hours for {course_title}, '
            'an {platform_name} course.\n'.format(
                course_title=order.lines.first().product.title,
                full_name=self.user.get_full_name(),
                credit_hours=2,
                credit_provider='Hogwarts',
                platform_name=get_current_request().site.name,
                receipt_url=get_lms_url('{}?orderNum={}'.format(
                    settings.RECEIPT_PAGE_PATH, order.number))))
Beispiel #7
0
    def test_post_checkout_callback(self):
        """
        When the post_checkout signal is emitted, the receiver should attempt
        to fulfill the newly-placed order and send receipt email.
        """
        httpretty.register_uri(
            httpretty.GET, get_lms_url('api/credit/v1/providers/ASU'),
            body='{"display_name": "Hogwarts"}',
            content_type="application/json"
        )
        toggle_switch('ENABLE_NOTIFICATIONS', True)
        course = Course.objects.create(id='edX/DemoX/Demo_Course', name='Demo Course')
        seat = course.create_or_update_seat('credit', False, 50, self.partner, 'ASU', None, 2)

        basket = BasketFactory()
        basket.add_product(seat, 1)
        order = factories.create_order(number=1, basket=basket, user=self.user)
        with mock.patch('threadlocals.threadlocals.get_current_request') as mock_gcr:
            mock_gcr.return_value = self.request
            send_course_purchase_email(None, order=order)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].from_email, self.site_configuration.from_email)
        self.assertEqual(mail.outbox[0].subject, 'Order Receipt')
        self.assertEqual(
            mail.outbox[0].body,
            '\nPayment confirmation for: {course_title}'
            '\n\nDear {full_name},'
            '\n\nThank you for purchasing {credit_hours} credit hours from {credit_provider} for {course_title}. '
            'A charge will appear on your credit or debit card statement with a company name of "{platform_name}".'
            '\n\nTo receive your course credit, you must also request credit at the {credit_provider} website. '
            'For a link to request credit from {credit_provider}, or to see the status of your credit request, '
            'go to your {platform_name} dashboard.'
            '\n\nTo explore other credit-eligible courses, visit the {platform_name} website. '
            'We add new courses frequently!'
            '\n\nTo view your payment information, visit the following website.'
            '\n{receipt_url}'
            '\n\nThank you. We hope you enjoyed your course!'
            '\nThe {platform_name} team'
            '\n\nYou received this message because you purchased credit hours for {course_title}, '
            'an {platform_name} course.\n'.format(
                course_title=order.lines.first().product.title,
                full_name=self.user.get_full_name(),
                credit_hours=2,
                credit_provider='Hogwarts',
                platform_name=get_current_request().site.name,
                receipt_url=get_lms_url('{}?orderNum={}'.format(settings.RECEIPT_PAGE_PATH, order.number))
            )
        )
 def test_basket_redirect_enrollment_code(self):
     """ Verify the view redirects to LMS when an enrollment code is provided. """
     self.create_and_test_coupon()
     httpretty.register_uri(httpretty.POST,
                            get_lms_enrollment_api_url(),
                            status=200)
     self.assert_redemption_page_redirects(get_lms_url())
Beispiel #9
0
 def mock_creditcourse_endpoint(self, course_id, status, body=None):
     url = get_lms_url('/api/credit/v1/courses/{}/'.format(course_id))
     responses.add(responses.PUT,
                   url,
                   status=status,
                   json=body,
                   content_type=JSON)
Beispiel #10
0
 def test_multiple_vouchers(self):
     """ Verify a redirect to LMS happens when a basket with already existing vouchers is used. """
     self.create_and_test_coupon()
     basket = Basket.get_basket(self.user, self.site)
     basket.vouchers.add(Voucher.objects.get(code=COUPON_CODE))
     httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200)
     self.assert_redemption_page_redirects(get_lms_url())
    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
Beispiel #12
0
    def credit_api_client(self):
        """ Returns an instance of the Credit API client. """

        return EdxRestApiClient(
            get_lms_url('api/credit/v1/'),
            oauth_access_token=self.request.user.access_token
        )
def core(request):
    return {
        'lms_base_url': get_lms_url(),
        'lms_dashboard_url': get_lms_dashboard_url(),
        'platform_name': request.site.name,
        'support_url': settings.SUPPORT_URL,
    }
Beispiel #14
0
def get_provider_data(provider_id):
    """Get the provider information for provider id provider.

    Args:
        provider_id(str): Identifier for the provider

    Returns: dict
    """
    provider_info_url = get_lms_url('api/credit/v1/providers/{}'.format(provider_id))
    timeout = settings.PROVIDER_DATA_PROCESSING_TIMEOUT
    headers = {
        'Content-Type': 'application/json',
        'X-Edx-Api-Key': settings.EDX_API_KEY
    }
    try:
        response = requests.get(provider_info_url, headers=headers, timeout=timeout)
        if response.status_code == 200:
            return response.json()
        else:
            logger.error(
                'Failed retrieve provider information for %s provider. Provider API returned status code %d. Error: %s',
                provider_id, response.status_code, response.text)
            return None
    except requests.exceptions.ConnectionError:
        logger.exception('Connection error occurred during getting data for %s provider', provider_id)
        return None
    except requests.Timeout:
        logger.exception('Failed to retrieve data for %s provider, connection timeout', provider_id)
        return None
Beispiel #15
0
 def mock_course_api_response(self, course=None):
     """ Helper function to register an API endpoint for the course information. """
     course_info = {
         'short_description': 'Test description',
         'media': {
             'course_image': {
                 'uri':
                 '/asset-v1:test+test+test+type@asset+block@images_course_image.jpg'
             },
             'image': {
                 'raw': 'path/to/the/course/image'
             }
         },
         'start': '2013-02-05T05:00:00Z',
         'name': course.name if course else 'Test course',
         'org': 'test'
     }
     course_info_json = json.dumps(course_info)
     course_id = course.id if course else 'course-v1:test+test+test'
     course_url = get_lms_url(
         'api/courses/v1/courses/{}/'.format(course_id))
     httpretty.register_uri(httpretty.GET,
                            course_url,
                            body=course_info_json,
                            content_type=CONTENT_TYPE)
Beispiel #16
0
    def get(self, request):
        """
        Looks up the passed code and adds the matching product to a basket,
        then applies the voucher and if the basket total is FREE places the order and
        enrolls the user in the course.
        """
        template_name = 'coupons/offer.html'
        code = request.GET.get('code', None)

        if not code:
            return render(request, template_name,
                          {'error': _('Code not provided')})
        try:
            voucher, product = get_voucher_from_code(code=code)
        except Voucher.DoesNotExist:
            msg = 'No voucher found with code {code}'.format(code=code)
            return render(request, template_name, {'error': _(msg)})
        except exceptions.ProductNotFoundError:
            return render(
                request, template_name, {
                    'error':
                    _('The voucher is not applicable to your current basket.')
                })

        valid_voucher, msg = voucher_is_valid(voucher, product, request)
        if not valid_voucher:
            return render(request, template_name, {'error': msg})

        basket = prepare_basket(request, product, voucher)
        if basket.total_excl_tax == AC.FREE:
            self.place_free_order(basket)
        else:
            return HttpResponseRedirect(reverse('basket:summary'))

        return HttpResponseRedirect(get_lms_url(''))
    def test_get_offers_for_single_course_voucher(self):
        """ Verify that the course offers data is returned for a single course voucher. """
        course, seat = self.create_course_and_seat()
        new_range = RangeFactory(products=[seat, ])
        voucher, __ = prepare_voucher(_range=new_range, benefit_value=10)
        voucher, products = get_voucher_and_products_from_code(voucher.code)
        benefit = voucher.offers.first().benefit
        request = self.prepare_offers_listing_request(voucher.code)
        self.mock_course_api_response(course=course)
        offers = VoucherViewSet().get_offers(products=products, request=request, voucher=voucher)
        first_offer = offers[0]

        self.assertEqual(len(offers), 1)
        self.assertDictEqual(first_offer, {
            'benefit': {
                'type': benefit.type,
                'value': benefit.value
            },
            'contains_verified': True,
            'course_start_date': '2013-02-05T05:00:00Z',
            'id': course.id,
            'image_url': get_lms_url('/asset-v1:test+test+test+type@asset+block@images_course_image.jpg'),
            'organization': CourseKey.from_string(course.id).org,
            'seat_type': course.type,
            'stockrecords': serializers.StockRecordSerializer(seat.stockrecords.first()).data,
            'title': course.name,
            'voucher_end_date': voucher.end_datetime
        })
Beispiel #18
0
    def _add_to_context_data(self, context):
        formset = context.get('formset', [])
        lines = context.get('line_list', [])
        site_configuration = self.request.site.siteconfiguration

        context_updates, lines_data = self.process_basket_lines(lines)
        context.update(context_updates)
        context.update(self.process_totals(context))

        context.update({
            'analytics_data': prepare_analytics_data(
                self.request.user,
                site_configuration.segment_key,
            ),
            'enable_client_side_checkout': False,
            'sdn_check': site_configuration.enable_sdn_check
        })

        payment_processors = site_configuration.get_payment_processors()
        if (
                site_configuration.client_side_payment_processor and
                waffle.flag_is_active(self.request, CLIENT_SIDE_CHECKOUT_FLAG_NAME)
        ):
            payment_processors_data = self._get_payment_processors_data(payment_processors)
            context.update(payment_processors_data)

        context.update({
            'formset_lines_data': list(zip(formset, lines_data)),
            'homepage_url': get_lms_url(''),
            'min_seat_quantity': 1,
            'max_seat_quantity': 100,
            'payment_processors': payment_processors,
            'lms_url_root': site_configuration.lms_url_root,
        })
        return context
Beispiel #19
0
 def _add_dynamic_discount_to_request(self, basket):
     # TODO: Remove as a part of REVMI-124 as this is a hacky solution
     # The problem is that orders are being created after payment processing, and the discount is not
     # saved in the database, so it needs to be calculated again in order to save the correct info to the
     # order. REVMI-124 will create the order before payment processing, when we have the discount context.
     if waffle.flag_is_active(self.request, DYNAMIC_DISCOUNT_FLAG) and basket.lines.count() == 1:  # pragma: no cover  pylint: disable=line-too-long
         discount_lms_url = get_lms_url('/api/discounts/')
         lms_discount_client = EdxRestApiClient(
             discount_lms_url,
             jwt=self.request.site.siteconfiguration.access_token)
         ck = basket.lines.first().product.course_id
         user_id = basket.owner.lms_user_id
         try:
             response = lms_discount_client.user(user_id).course(ck).get()
             self.request.POST = self.request.POST.copy()
             self.request.POST['discount_jwt'] = response.get('jwt')
             logger.info(
                 """Received discount jwt from LMS with
                 url: [%s],
                 user_id: [%s],
                 course_id: [%s],
                 and basket_id: [%s]
                 returned [%s]""", discount_lms_url, str(user_id), ck,
                 basket.id, response)
         except (SlumberHttpBaseException,
                 requests.exceptions.Timeout) as error:
             logger.warning(
                 """Failed to receive discount jwt from LMS with
                 url: [%s],
                 user_id: [%s],
                 course_id: [%s],
                 and basket_id: [%s]
                 returned [%s]""", discount_lms_url, str(user_id), ck,
                 basket.id,
                 vars(error.response) if hasattr(error, 'response') else '')
Beispiel #20
0
    def is_eligible_for_credit(self, course_key):
        """
        Check if a user is eligible for a credit course.
        Calls the LMS eligibility API endpoint and sends the username and course key
        query parameters and returns eligibility details for the user and course combination.

        Args:
            course_key (string): The course key for which the eligibility is checked for.

        Returns:
            A list that contains eligibility information, or empty if user is not eligible.

        Raises:
            ConnectionError, SlumberBaseException and Timeout for failures in establishing a
            connection with the LMS eligibility API endpoint.
        """
        query_strings = {
            'username': self.username,
            'course_key': course_key
        }
        try:
            api = EdxRestApiClient(
                get_lms_url('api/credit/v1/'),
                oauth_access_token=self.access_token
            )
            response = api.eligibility().get(**query_strings)
        except (ConnectionError, SlumberBaseException, Timeout):  # pragma: no cover
            log.exception(
                'Failed to retrieve eligibility details for [%s] in course [%s]',
                self.username,
                course_key
            )
            raise
        return response
def core(request):
    return {
        'lms_base_url': get_lms_url(),
        'lms_dashboard_url': get_lms_dashboard_url(),
        'platform_name': request.site.name,
        'support_url': request.site.siteconfiguration.payment_support_url,
    }
Beispiel #22
0
def send_course_purchase_email(sender, order=None, **kwargs):  # pylint: disable=unused-argument
    """Send course purchase notification email when a course is purchased."""
    if waffle.switch_is_active('ENABLE_NOTIFICATIONS'):
        # We do not currently support email sending for orders with more than one item.
        if len(order.lines.all()) == ORDER_LINE_COUNT:
            product = order.lines.first().product
            provider_id = getattr(product.attr, 'credit_provider', None)
            if not provider_id:
                logger.error(
                    'Failed to send credit receipt notification. Credit seat product [%s] has not provider.', product.id
                )
                return
            elif product.get_product_class().name == 'Seat':
                provider_data = get_provider_data(provider_id)
                if provider_data:
                    send_notification(
                        order.user,
                        'CREDIT_RECEIPT',
                        {
                            'course_title': product.title,
                            'receipt_page_url': get_lms_url(
                                '{}?orderNum={}'.format(settings.RECEIPT_PAGE_PATH, order.number)
                            ),
                            'credit_hours': product.attr.credit_hours,
                            'credit_provider': provider_data['display_name'],
                        },
                        threadlocals.get_current_request().site
                    )

        else:
            logger.info('Currently support receipt emails for order with one item.')
    def get(self, request):
        """
        Looks up the passed code and adds the matching product to a basket,
        then applies the voucher and if the basket total is FREE places the order and
        enrolls the user in the course.
        """
        template_name = 'coupons/offer.html'
        code = request.GET.get('code', None)

        if not code:
            return render(request, template_name, {'error': _('Code not provided')})
        try:
            voucher, product = get_voucher_from_code(code=code)
        except Voucher.DoesNotExist:
            msg = 'No voucher found with code {code}'.format(code=code)
            return render(request, template_name, {'error': _(msg)})
        except exceptions.ProductNotFoundError:
            return render(request, template_name, {'error': _('The voucher is not applicable to your current basket.')})

        valid_voucher, msg = voucher_is_valid(voucher, product, request)
        if not valid_voucher:
            return render(request, template_name, {'error': msg})

        basket = prepare_basket(request, product, voucher)
        if basket.total_excl_tax == AC.FREE:
            self.place_free_order(basket)
        else:
            return HttpResponseRedirect(reverse('basket:summary'))

        return HttpResponseRedirect(get_lms_url(''))
Beispiel #24
0
 def get_context_data(self, **kwargs):
     context = super(PaymentFailedView, self).get_context_data(**kwargs)
     context.update({
         'dashboard_url': get_lms_url(),
         'payment_support_email': self.request.site.siteconfiguration.payment_support_email
     })
     return context
Beispiel #25
0
def send_order_completion_email(sender, order=None, **kwargs):  # pylint: disable=unused-argument
    """Send course purchase notification email when a course is purchased."""
    if waffle.switch_is_active('ENABLE_NOTIFICATIONS'):
        # We do not currently support email sending for orders with more than one item.
        if len(order.lines.all()) == ORDER_LINE_COUNT:
            product = order.lines.first().product
            if product.get_product_class().name == 'Seat':
                send_notification(
                    order.user, 'COURSE_PURCHASED', {
                        'course_title': product.title,
                        'full_name': order.user.get_full_name(),
                        'lms_dashboard': get_lms_url('dashboard'),
                        'lms_courses': get_lms_url('courses')
                    }, order.site)
        else:
            logger.info(
                'Currently support receipt emails for order with one item.')
Beispiel #26
0
    def test_invalid_user(self):
        """ Verify an unauthorized request is redirected to the LMS dashboard. """
        order = OrderFactory()
        order.user = self.create_user()
        response = self.client.get(reverse(self.path, args=[order.number]))

        self.assertEqual(response.status_code, 302)
        self.assertEqual(response['location'], get_lms_url('dashboard'))
Beispiel #27
0
 def test_invalid_user(self):
     """ Verify an unauthorized request is redirected to the LMS dashboard. """
     order = OrderFactory()
     order.user = self.create_user()
     response = self.client.get(reverse(self.path, args=[order.number]))
     self.assertEqual(response.status_code, 302)
     redirect_location = get_lms_url('dashboard')
     self.assertEqual(response['location'], redirect_location)
 def mock_footer_api_response(self):
     """ Helper function to register an API endpoint for the footer information. """
     footer_url = get_lms_url('api/branding/v1/footer')
     footer_content = {
         'footer': 'edX Footer'
     }
     content_json = json.dumps(footer_content)
     httpretty.register_uri(httpretty.GET, footer_url, body=content_json, content_type='application/json')
Beispiel #29
0
    def mock_credit_api_error(self):
        """ Mock an error response when calling the Credit API providers endpoint. """

        def callback(request, uri, headers):  # pylint: disable=unused-argument
            return 500, headers, 'Failure!'

        url = get_lms_url('/api/credit/v1/providers/')
        httpretty.register_uri(httpretty.GET, url, body=callback, content_type='application/json')
Beispiel #30
0
    def mock_credit_api_error(self):
        """ Mock an error response when calling the Credit API providers endpoint. """

        url = get_lms_url('/api/credit/v1/providers/')
        responses.add(responses.GET,
                      url,
                      body='Failure!',
                      content_type='application/json',
                      status=500)
 def test_payment_error_context(self):
     response = self.client.get(reverse('payment_error'))
     self.assertDictContainsSubset(
         {
             'dashboard_url':
             get_lms_url(),
             'payment_support_email':
             self.site.siteconfiguration.payment_support_email
         }, response.context)
Beispiel #32
0
    def get_context_data(self, **kwargs):
        context = super(BasketSummaryView, self).get_context_data(**kwargs)
        formset = context.get('formset', [])
        lines = context.get('line_list', [])
        site_configuration = self.request.site.siteconfiguration

        failed_enterprise_consent_code = self.request.GET.get(CONSENT_FAILED_PARAM)
        if failed_enterprise_consent_code:
            messages.error(
                self.request,
                _("Could not apply the code '{code}'; it requires data sharing consent.").format(
                    code=failed_enterprise_consent_code
                )
            )

        context_updates, lines_data = self._process_basket_lines(lines)
        context.update(context_updates)

        user = self.request.user
        context.update({
            'analytics_data': prepare_analytics_data(
                user,
                site_configuration.segment_key,
            ),
            'enable_client_side_checkout': False,
            'sdn_check': site_configuration.enable_sdn_check
        })

        payment_processors = site_configuration.get_payment_processors()
        if site_configuration.client_side_payment_processor \
                and waffle.flag_is_active(self.request, CLIENT_SIDE_CHECKOUT_FLAG_NAME):
            payment_processors_data = self._get_payment_processors_data(payment_processors)
            context.update(payment_processors_data)

        # Total benefit displayed in price summary.
        # Currently only one voucher per basket is supported.
        try:
            applied_voucher = self.request.basket.vouchers.first()
            total_benefit = (
                format_benefit_value(applied_voucher.best_offer.benefit)
                if applied_voucher else None
            )
        except ValueError:
            total_benefit = None
        num_of_items = self.request.basket.num_items
        context.update({
            'formset_lines_data': zip(formset, lines_data),
            'free_basket': context['order_total'].incl_tax == 0,
            'homepage_url': get_lms_url(''),
            'min_seat_quantity': 1,
            'max_seat_quantity': 100,
            'payment_processors': payment_processors,
            'total_benefit': total_benefit,
            'enable_alipay_wechatpay': settings.ENABLE_ALIPAY_WECHATPAY,
            'line_price': (self.request.basket.total_incl_tax_excl_discounts / num_of_items) if num_of_items > 0 else 0
        })
        return context
Beispiel #33
0
 def test_get_provider_data_unavailable_request(self):
     """
     Check if None return on the bad request
     """
     httpretty.register_uri(httpretty.GET,
                            get_lms_url('api/credit/v1/providers/ABC'),
                            status=400)
     provider_data = get_provider_data('ABC')
     self.assertEqual(provider_data, None)
 def test_multiple_vouchers(self):
     """ Verify a redirect to LMS happens when a basket with already existing vouchers is used. """
     self.create_and_test_coupon()
     basket = Basket.get_basket(self.user, self.site)
     basket.vouchers.add(Voucher.objects.get(code=COUPON_CODE))
     httpretty.register_uri(httpretty.POST,
                            get_lms_enrollment_api_url(),
                            status=200)
     self.assert_redemption_page_redirects(get_lms_url())
 def test_core(self):
     request = get_current_request()
     self.assertDictEqual(
         core(request), {
             'lms_base_url': get_lms_url(),
             'lms_dashboard_url': get_lms_dashboard_url(),
             'platform_name': request.site.name,
             'support_url': SUPPORT_URL
         })
Beispiel #36
0
 def test_get_provider_data_unavailable_request(self):
     """
     Check if None return on the bad request
     """
     httpretty.register_uri(
         httpretty.GET, get_lms_url('api/credit/v1/providers/ABC'),
         status=400
     )
     provider_data = get_provider_data('ABC')
     self.assertEqual(provider_data, None)
Beispiel #37
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
Beispiel #38
0
    def setUp(self):
        super(CouponRedeemViewTests, self).setUp()
        self.user = self.create_user()
        self.client.login(username=self.user.username, password=self.password)
        self.course = CourseFactory()
        self.seat = self.course.create_or_update_seat('verified', True, 50, self.partner)

        self.catalog = Catalog.objects.create(partner=self.partner)
        self.catalog.stock_records.add(StockRecord.objects.get(product=self.seat))
        self.student_dashboard_url = get_lms_url(self.site.siteconfiguration.student_dashboard_url)
Beispiel #39
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
Beispiel #40
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)
Beispiel #41
0
    def mock_creditcourse_endpoint(self, course_id, status, body=None):
        self.assertTrue(httpretty.is_enabled(),
                        'httpretty must be enabled to mock Credit API calls.')

        url = get_lms_url('/api/credit/v1/courses/{}/'.format(course_id))
        httpretty.register_uri(httpretty.PUT,
                               url,
                               status=status,
                               body=json.dumps(body),
                               content_type=JSON)
Beispiel #42
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}
 def test_core(self):
     request = get_current_request()
     self.assertDictEqual(
         core(request),
         {
             'lms_base_url': get_lms_url(),
             'lms_dashboard_url': get_lms_dashboard_url(),
             'platform_name': request.site.name,
             'support_url': SUPPORT_URL
         }
     )
    def mock_creditcourse_endpoint(self, course_id, status, body=None):
        self.assertTrue(httpretty.is_enabled(), 'httpretty must be enabled to mock Credit API calls.')

        url = get_lms_url('/api/credit/v1/courses/{}/'.format(course_id))
        httpretty.register_uri(
            httpretty.PUT,
            url,
            status=status,
            body=json.dumps(body),
            content_type=JSON
        )
Beispiel #45
0
 def send_email(self, order):
     """ Sends an email with enrollment code order information. """
     # Note (multi-courses): Change from a course_name to a list of course names.
     product = order.lines.first().product
     course = Course.objects.get(id=product.attr.course_key)
     send_notification(
         order.user,
         'ORDER_WITH_CSV',
         context={
             'contact_url': get_lms_url('/contact'),
             'course_name': course.name,
             'download_csv_link': get_ecommerce_url(reverse('coupons:enrollment_code_csv', args=[order.number])),
             'enrollment_code_title': product.title,
             'order_number': order.number,
             'partner_name': order.site.siteconfiguration.partner.name,
             'lms_url': get_lms_url(),
             'receipt_page_url': get_lms_url('{}?orderNum={}'.format(settings.RECEIPT_PAGE_PATH, order.number)),
         },
         site=order.site
     )
Beispiel #46
0
 def test_get_provider_data(self):
     """
     Check if correct data returns on the full filled request.
     """
     httpretty.register_uri(
         httpretty.GET, get_lms_url('api/credit/v1/providers/ASU'),
         body='{"display_name": "Arizona State University"}',
         content_type="application/json"
     )
     provider_data = get_provider_data('ASU')
     self.assertDictEqual(provider_data, {"display_name": "Arizona State University"})
 def mock_courses_api(self, status, body=None):
     """ Mock Courses API with specific status and body. """
     body = body or {}
     url = get_lms_url('/api/courses/v1/courses?page_size=50&page=1')
     responses.add(
         responses.GET,
         url,
         status=status,
         json=body,
         content_type=JSON
     )
Beispiel #48
0
 def test_core(self):
     request = get_current_request()
     self.assertDictEqual(
         core(request),
         {
             'lms_base_url': get_lms_url(),
             'lms_dashboard_url': get_lms_dashboard_url(),
             'platform_name': request.site.name,
             'support_url': request.site.siteconfiguration.payment_support_url,
         }
     )
Beispiel #49
0
    def test_successful_redirect(self):
        """ Verify redirect to the receipt page. """
        self.prepare_basket(0)
        self.assertEqual(Order.objects.count(), 0)
        receipt_page = get_lms_url(settings.RECEIPT_PAGE_PATH)

        response = self.client.get(self.path)
        self.assertEqual(Order.objects.count(), 1)

        expected_url = '{}?orderNum={}'.format(receipt_page, Order.objects.first().number)
        self.assertRedirects(response, expected_url, fetch_redirect_response=False)
def core(request):
    site = request.site
    site_configuration = site.siteconfiguration

    return {
        'lms_base_url': get_lms_url(),
        'lms_dashboard_url': get_lms_dashboard_url(),
        'platform_name': site.name,
        'support_url': site_configuration.payment_support_url,
        'optimizely_snippet_src': site_configuration.optimizely_snippet_src,
    }
    def mock_courses_api(self, status, body=None):
        """ Mock Courses API with specific status and body. """
        self.assertTrue(httpretty.is_enabled(), 'httpretty must be enabled to mock Course API calls.')

        body = body or {}
        url = get_lms_url('/api/courses/v1/courses/?page_size=1')
        httpretty.register_uri(
            httpretty.GET,
            url,
            status=status,
            body=json.dumps(body),
            content_type=JSON
        )
Beispiel #52
0
    def test_get_receipt_for_existing_order(self):
        """ Order owner should be able to see the Receipt Page."""
        order = self._create_order_for_receipt(self.user)
        response = self._get_receipt_response(order.number)
        context_data = {
            'payment_method': None,
            'fire_tracking_events': False,
            'display_credit_messaging': False,
            'verification_url': get_lms_url(
                'verify_student/verify-now/{course_id}'.format(course_id=self.course.id)
            ),
        }

        self.assertEqual(response.status_code, 200)
        self.assertDictContainsSubset(context_data, response.context_data)
 def mock_course_api_response(self, course=None):
     """ Helper function to register an API endpoint for the course information. """
     course_info = {
         'media': {
             'course_image': {
                 'uri': '/asset-v1:test+test+test+type@asset+block@images_course_image.jpg'
             }
         },
         'name': course.name if course else 'Test course',
         'org': 'test'
     }
     course_info_json = json.dumps(course_info)
     course_id = course.id if course else 'course-v1:test+test+test'
     course_url = get_lms_url('api/courses/v1/courses/{}/'.format(course_id))
     httpretty.register_uri(httpretty.GET, course_url, body=course_info_json, content_type='application/json')
Beispiel #54
0
 def setUp(self):
     super(CouponRedeemViewTests, self).setUp()
     self.user = self.create_user(email='*****@*****.**')
     self.client.login(username=self.user.username, password=self.password)
     self.course_mode = 'verified'
     self.course, self.seat = self.create_course_and_seat(
         seat_type=self.course_mode,
         id_verification=True,
         price=50,
         partner=self.partner
     )
     self.stock_record = StockRecord.objects.get(product=self.seat)
     self.catalog = Catalog.objects.create(partner=self.partner)
     self.catalog.stock_records.add(StockRecord.objects.get(product=self.seat))
     self.student_dashboard_url = get_lms_url(self.site.siteconfiguration.student_dashboard_url)
Beispiel #55
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)
    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