Beispiel #1
0
    def get_base_details(self, enterprise_uuid, course_id):
        """
        Retrieve fundamental details used by both POST and GET versions of this view.

        Specifically, take an EnterpriseCustomer UUID and a course ID, and transform those
        into an actual EnterpriseCustomer, a set of details about the course, and a list
        of the available course modes for that course.
        """
        try:
            client = CourseApiClient()
            course_details = client.get_course_details(course_id)
        except HttpClientError:
            logger.error('Failed to get course details for course ID: %s',
                         course_id)
            raise Http404

        if course_details is None:
            logger.error('Unable to find course details for course ID: %s',
                         course_id)
            raise Http404

        enterprise_customer = get_enterprise_customer_or_404(enterprise_uuid)

        try:
            enrollment_client = EnrollmentApiClient()
            modes = enrollment_client.get_course_modes(course_id)
        except HttpClientError:
            logger.error(
                'Failed to determine available course modes for course ID: %s',
                course_id)
            raise Http404

        course_modes = []

        audit_modes = getattr(settings,
                              'ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES',
                              ['audit', 'honor'])

        for mode in modes:
            if mode['min_price']:
                price_text = '${}'.format(mode['min_price'])
            else:
                price_text = self.context_data['free_price_text']
            if mode['slug'] in audit_modes:
                description = self.context_data['audit_text']
            else:
                description = self.context_data['verified_text']
            course_modes.append({
                'mode': mode['slug'],
                'min_price': mode['min_price'],
                'sku': mode['sku'],
                'title': mode['name'],
                'original_price': price_text,
                'final_price': price_text,
                'description': description,
                'premium': mode['slug'] not in audit_modes
            })

        return enterprise_customer, course_details, course_modes
Beispiel #2
0
 def clean_course(self):
     """
     Verify course ID and retrieve course details.
     """
     course_id = self.cleaned_data[self.Fields.COURSE].strip()
     if not course_id:
         return None
     try:
         client = EnrollmentApiClient()
         return client.get_course_details(course_id)
     except (HttpClientError, HttpServerError):
         raise ValidationError(ValidationMessages.INVALID_COURSE_ID.format(course_id=course_id))
Beispiel #3
0
    def get(self, request, enterprise_uuid, course_id):
        """
        Show course track selection page for the enterprise.

        Based on `enterprise_uuid` in URL, the view will decide which
        enterprise customer's course enrollment page is to use.

        Unauthenticated learners will be redirected to enterprise-linked SSO.

        A 404 will be raised if any of the following conditions are met:
            * No enterprise customer uuid kwarg `enterprise_uuid` in request.
            * No enterprise customer found against the enterprise customer
                uuid `enterprise_uuid` in the request kwargs.
            * No course is found in database against the provided `course_id`.
        """
        # Verify that all necessary resources are present
        verify_edx_resources()

        enterprise_customer, course, modes = self.get_base_details(
            enterprise_uuid, course_id)

        # Create a link between the user and the enterprise customer if it
        # does not already exist.
        EnterpriseCustomerUser.objects.get_or_create(
            enterprise_customer=enterprise_customer, user_id=request.user.id)

        enrollment_client = EnrollmentApiClient()
        enrolled_course = enrollment_client.get_course_enrollment(
            request.user.username, course_id)
        if (enrolled_course is not None
                and EnterpriseCourseEnrollment.objects.filter(
                    enterprise_customer_user__enterprise_customer=
                    enterprise_customer,
                    enterprise_customer_user__user_id=request.user.id,
                    course_id=course_id).exists()):
            # The user is already enrolled in the course through the Enterprise Customer, so redirect to the course
            # info page.
            return redirect(LMS_COURSE_URL.format(course_id=course_id))

        return self.get_enterprise_course_enrollment_page(
            request, enterprise_customer, course, modes)
Beispiel #4
0
    def get_enrolled_course_string(self, enterprise_customer_user):
        """
        Get an HTML string representing the courses the user is enrolled in.
        """
        enrollment_client = EnrollmentApiClient()
        enrolled_courses = enrollment_client.get_enrolled_courses(self.username(enterprise_customer_user))
        course_details = []
        courses_client = CourseApiClient()
        for course in enrolled_courses:
            course_id = course['course_details']['course_id']
            name = courses_client.get_course_details(course_id)['name']
            course_details.append({'course_id': course_id, 'course_name': name})

        template = '<a href="{url}">{course_name}</a>'
        joiner = '<br/>'
        return joiner.join(
            template.format(
                url=reverse('about_course', args=[course['course_id']]),
                course_name=course['course_name'],
            )
            for course in course_details
        )
Beispiel #5
0
    def enroll_user(cls, enterprise_customer, user, course_mode, *course_ids):
        """
        Enroll a single user in any number of courses using a particular course mode.

        Args:
            enterprise_customer: The EnterpriseCustomer which is sponsoring the enrollment
            user: The user who needs to be enrolled in the course
            course_mode: The mode with which the enrollment should be created
            *course_ids: An iterable containing any number of course IDs to eventually enroll the user in.

        Returns:
            Boolean: Whether or not enrollment succeeded for all courses specified
        """
        enterprise_customer_user, __ = EnterpriseCustomerUser.objects.get_or_create(
            enterprise_customer=enterprise_customer,
            user_id=user.id
        )
        enrollment_client = EnrollmentApiClient()
        succeeded = True
        for course_id in course_ids:
            try:
                enrollment_client.enroll_user_in_course(user.username, course_id, course_mode)
            except HttpClientError as exc:
                succeeded = False
                default_message = 'No error message provided'
                try:
                    error_message = json.loads(exc.content.decode()).get('message', default_message)
                except ValueError:
                    error_message = default_message
                logging.error(
                    'Error while enrolling user %(user)s: %(message)s',
                    dict(user=user.username, message=error_message)
                )
            else:
                EnterpriseCourseEnrollment.objects.get_or_create(
                    enterprise_customer_user=enterprise_customer_user,
                    course_id=course_id
                )
        return succeeded
Beispiel #6
0
    def post(self, request, enterprise_uuid, course_id):
        """
        Process a submitted track selection form for the enterprise.
        """
        enterprise_customer, course, course_modes = self.get_base_details(
            enterprise_uuid, course_id)

        # Create a link between the user and the enterprise customer if it
        # does not already exist.
        enterprise_customer_user, __ = EnterpriseCustomerUser.objects.get_or_create(
            enterprise_customer=enterprise_customer, user_id=request.user.id)

        selected_course_mode_name = request.POST.get('course_mode')
        selected_course_mode = None
        for course_mode in course_modes:
            if course_mode['mode'] == selected_course_mode_name:
                selected_course_mode = course_mode
                break

        if not selected_course_mode:
            return self.get_enterprise_course_enrollment_page(
                request, enterprise_customer, course, course_modes)

        user_consent_needed = is_consent_required_for_user(
            enterprise_customer_user, course_id)
        if not selected_course_mode.get('premium') and not user_consent_needed:
            # For the audit course modes (audit, honor), where DSC is not
            # required, enroll the learner directly through enrollment API
            # client and redirect the learner to LMS courseware page.
            with transaction.atomic():
                # Create the Enterprise backend database records for this course
                # enrollment.
                EnterpriseCourseEnrollment.objects.get_or_create(
                    enterprise_customer_user=enterprise_customer_user,
                    course_id=course_id,
                )
                client = EnrollmentApiClient()
                client.enroll_user_in_course(request.user.username, course_id,
                                             selected_course_mode_name)

            return redirect(LMS_COURSEWARE_URL.format(course_id=course_id))
        elif user_consent_needed:
            # For the audit course modes (audit, honor) or for the premium
            # course modes (Verified, Prof Ed) where DSC is required, redirect
            # the learner to course specific DSC with enterprise UUID from
            # there the learner will be directed to the ecommerce flow after
            # providing DSC.
            next_url = '{handle_consent_enrollment_url}?{query_string}'.format(
                handle_consent_enrollment_url=reverse(
                    'enterprise_handle_consent_enrollment',
                    args=[enterprise_customer.uuid, course_id]),
                query_string=urlencode(
                    {'course_mode': selected_course_mode_name}))
            return redirect('{grant_data_sharing_url}?{params}'.format(
                grant_data_sharing_url=reverse(
                    'grant_data_sharing_permissions'),
                params=urlencode({
                    'next': next_url,
                    'enterprise_id': enterprise_customer.uuid,
                    'course_id': course_id,
                    'enrollment_deferred': True,
                })))
        else:
            # For the premium course modes (Verified, Prof Ed) where DSC is
            # not required, redirect the enterprise learner to the ecommerce
            # flow in LMS.
            # Note: LMS start flow automatically detects the paid mode
            return redirect(
                LMS_START_PREMIUM_COURSE_FLOW_URL.format(course_id=course_id))
Beispiel #7
0
    def get(self, request, enterprise_uuid, course_id):
        """
        Handle the enrollment of enterprise learner in the provided course.

        Based on `enterprise_uuid` in URL, the view will decide which
        enterprise customer's course enrollment record should be created.

        Depending on the value of query parameter `course_mode` then learner
        will be either redirected to LMS dashboard for audit modes or
        redirected to ecommerce basket flow for payment of premium modes.
        """
        # Verify that all necessary resources are present
        verify_edx_resources()
        enrollment_course_mode = request.GET.get('course_mode')

        # Redirect the learner to LMS dashboard in case no course mode is
        # provided as query parameter `course_mode`
        if not enrollment_course_mode:
            return redirect(LMS_DASHBOARD_URL)

        try:
            enrollment_client = EnrollmentApiClient()
            course_modes = enrollment_client.get_course_modes(course_id)
        except HttpClientError:
            logger.error(
                'Failed to determine available course modes for course ID: %s',
                course_id)
            raise Http404

        # Verify that the request user belongs to the enterprise against the
        # provided `enterprise_uuid`.
        enterprise_customer = get_enterprise_customer_or_404(enterprise_uuid)
        enterprise_customer_user = get_enterprise_customer_user(
            request.user.id, enterprise_customer.uuid)
        if not enterprise_customer_user:
            raise Http404

        selected_course_mode = None
        for course_mode in course_modes:
            if course_mode['slug'] == enrollment_course_mode:
                selected_course_mode = course_mode
                break

        if not selected_course_mode:
            return redirect(LMS_DASHBOARD_URL)

        # Create the Enterprise backend database records for this course
        # enrollment
        EnterpriseCourseEnrollment.objects.update_or_create(
            enterprise_customer_user=enterprise_customer_user,
            course_id=course_id,
            defaults={
                'consent_granted': True,
            })

        audit_modes = getattr(settings,
                              'ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES',
                              ['audit', 'honor'])
        if selected_course_mode['slug'] in audit_modes:
            # In case of Audit course modes enroll the learner directly through
            # enrollment API client and redirect the learner to dashboard.
            enrollment_api_client = EnrollmentApiClient()
            enrollment_api_client.enroll_user_in_course(
                request.user.username, course_id, selected_course_mode['slug'])

            return redirect(LMS_COURSEWARE_URL.format(course_id=course_id))

        # redirect the enterprise learner to the ecommerce flow in LMS
        # Note: LMS start flow automatically detects the paid mode
        return redirect(
            LMS_START_PREMIUM_COURSE_FLOW_URL.format(course_id=course_id))