示例#1
0
    def test_update_enrollment_attributes(self):
        # Add fake course enrollment information to the fake data API
        fake_data_api.add_course(
            self.COURSE_ID,
            course_modes=['honor', 'verified', 'audit', 'credit'])
        # Enroll in the course and verify the URL we get sent to
        result = api.add_enrollment(self.USERNAME,
                                    self.COURSE_ID,
                                    mode='audit')
        get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
        self.assertEqual(result, get_result)

        enrollment_attributes = [{
            "namespace": "credit",
            "name": "provider_id",
            "value": "hogwarts",
        }]

        result = api.update_enrollment(
            self.USERNAME,
            self.COURSE_ID,
            mode='credit',
            enrollment_attributes=enrollment_attributes)
        self.assertEqual('credit', result['mode'])
        attributes = api.get_enrollment_attributes(self.USERNAME,
                                                   self.COURSE_ID)
        self.assertEqual(enrollment_attributes[0], attributes[0])
示例#2
0
def get_enrollment(*args, **kwargs):
    """
    Return enrollment of given user in the course provided.

    Example:
        >>>get_enrollment(
            {
            "username": "******",
            "course_id": "course-v1-edX-DemoX-1T2015"
            }
        )
    """
    errors = []
    course_id = kwargs.pop('course_id', None)
    username = kwargs.get('username', None)

    try:
        LOG.info('Getting enrollment information of student: %s  course: %s',
                 username, course_id)
        enrollment = api.get_enrollment(username, course_id)
        if not enrollment:
            errors.append('No enrollment found for user:`{}`'.format(username))
            return None, errors
    except InvalidKeyError:
        errors.append('No course found for course_id `{}`'.format(course_id))
        return None, errors
    enrollment['enrollment_attributes'] = api.get_enrollment_attributes(
        username, course_id)
    enrollment['course_id'] = course_id
    return enrollment, errors
示例#3
0
 def assert_add_modes_with_enrollment(self, enrollment_mode):
     """ Dry method for adding fake course enrollment information to fake
     data API and enroll the student in the course. """
     fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit'])
     result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode=enrollment_mode)
     get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
     self.assertEqual(result, get_result)
     # set the course verify mode as expire.
     fake_data_api.set_expired_mode(self.COURSE_ID)
示例#4
0
 def test_existing_inactive_enrollment(self):
     """
     If the user has an inactive enrollment for the course, the view should behave as if the
     user has no enrollment.
     """
     # Create an inactive enrollment
     CourseEnrollment.enroll(self.user, self.course.id)
     CourseEnrollment.unenroll(self.user, self.course.id, True)
     assert not CourseEnrollment.is_enrolled(self.user, self.course.id)
     assert get_enrollment(self.user.username, str(self.course.id)) is not None
示例#5
0
 def test_existing_inactive_enrollment(self):
     """
     If the user has an inactive enrollment for the course, the view should behave as if the
     user has no enrollment.
     """
     # Create an inactive enrollment
     CourseEnrollment.enroll(self.user, self.course.id)
     CourseEnrollment.unenroll(self.user, self.course.id, True)
     self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id))
     self.assertIsNotNone(get_enrollment(self.user.username, unicode(self.course.id)))
示例#6
0
 def test_existing_inactive_enrollment(self):
     """
     If the user has an inactive enrollment for the course, the view should behave as if the
     user has no enrollment.
     """
     # Create an inactive enrollment
     CourseEnrollment.enroll(self.user, self.course.id)
     CourseEnrollment.unenroll(self.user, self.course.id, True)
     self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id))
     self.assertIsNotNone(get_enrollment(self.user.username, six.text_type(self.course.id)))
示例#7
0
    def test_enroll(self, course_modes, mode):
        # Add a fake course enrollment information to the fake data API
        fake_data_api.add_course(self.COURSE_ID, course_modes=course_modes)
        # Enroll in the course and verify the URL we get sent to
        result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode=mode)
        self.assertIsNotNone(result)
        self.assertEqual(result['student'], self.USERNAME)
        self.assertEqual(result['course']['course_id'], self.COURSE_ID)
        self.assertEqual(result['mode'], mode)

        get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
        self.assertEqual(result, get_result)
示例#8
0
    def test_enroll(self, course_modes, mode):
        # Add a fake course enrollment information to the fake data API
        fake_data_api.add_course(self.COURSE_ID, course_modes=course_modes)
        # Enroll in the course and verify the URL we get sent to
        result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode=mode)
        assert result is not None
        assert result['student'] == self.USERNAME
        assert result['course']['course_id'] == self.COURSE_ID
        assert result['mode'] == mode

        get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
        assert result == get_result
    def test_enroll_user(self):

        command_args = [
            '--course',
            self.course_id,
            '--email',
            self.user_email,
        ]

        call_command('enroll_user_in_course', *command_args)

        user_enroll = get_enrollment(self.username, self.course_id)
        self.assertTrue(user_enroll['is_active'])
示例#10
0
    def test_update_enrollment(self):
        # Add fake course enrollment information to the fake data API
        fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit'])
        # Enroll in the course and verify the URL we get sent to
        result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode='audit')
        get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
        self.assertEqual(result, get_result)

        result = api.update_enrollment(self.USERNAME, self.COURSE_ID, mode='honor')
        self.assertEqual('honor', result['mode'])

        result = api.update_enrollment(self.USERNAME, self.COURSE_ID, mode='verified')
        self.assertEqual('verified', result['mode'])
示例#11
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)

        if not course_home_mfe_dates_tab_is_active(course_key):
            return Response(status=status.HTTP_404_NOT_FOUND)

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_metric('course_id', course_key_string)
        monitoring_utils.set_custom_metric('user_id', request.user.id)
        monitoring_utils.set_custom_metric('is_staff', request.user.is_staff)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)
        blocks = get_course_date_blocks(course,
                                        request.user,
                                        request,
                                        include_access=True,
                                        include_past_dates=True)
        display_reset_dates_text, _ = dates_banner_should_display(
            course_key, request)

        learner_is_verified = False
        enrollment = get_enrollment(request.user.username, course_key_string)
        if enrollment:
            learner_is_verified = enrollment.get('mode') == 'verified'

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        data = {
            'course_date_blocks':
            [block for block in blocks if not isinstance(block, TodaysDate)],
            'display_reset_dates_text':
            display_reset_dates_text,
            'learner_is_verified':
            learner_is_verified,
            'user_timezone':
            user_timezone,
            'verified_upgrade_link':
            verified_upgrade_deadline_link(request.user, course=course),
        }
        context = self.get_serializer_context()
        context['learner_is_verified'] = learner_is_verified
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
    def test_enroll_user(self):

        command_args = [
            '--course', self.course_id,
            '--email', self.user_email,
        ]

        call_command(
            'enroll_user_in_course',
            *command_args
        )

        user_enroll = get_enrollment(self.username, self.course_id)
        self.assertTrue(user_enroll['is_active'])
    def test_enroll_user_twice(self):
        """
        Ensures the command is idempotent.
        """

        command_args = [
            '--course',
            self.course_id,
            '--email',
            self.user_email,
        ]

        for _ in range(2):
            call_command('enroll_user_in_course', *command_args)

        # Second run does not impact the first run (i.e., the
        # user is still enrolled, no exception was raised, etc)
        user_enroll = get_enrollment(self.username, self.course_id)
        self.assertTrue(user_enroll['is_active'])
    def test_enroll_user_twice(self):
        """
        Ensures the command is idempotent.
        """

        command_args = [
            '--course', self.course_id,
            '--email', self.user_email,
        ]

        for _ in range(2):
            call_command(
                'enroll_user_in_course',
                *command_args
            )

        # Second run does not impact the first run (i.e., the
        # user is still enrolled, no exception was raised, etc)
        user_enroll = get_enrollment(self.username, self.course_id)
        self.assertTrue(user_enroll['is_active'])
示例#15
0
文件: views.py 项目: edx/edx-platform
    def get(self, request, course_id=None, username=None):
        """Create, read, or update enrollment information for a user.

        HTTP Endpoint for all CRUD operations for a user course enrollment. Allows creation, reading, and
        updates of the current enrollment for a particular course.

        Args:
            request (Request): To get current course enrollment information, a GET request will return
                information for the current user and the specified course.
            course_id (str): URI element specifying the course location. Enrollment information will be
                returned, created, or updated for this particular course.
            username (str): The username associated with this enrollment request.

        Return:
            A JSON serialized representation of the course enrollment.

        """
        username = username or request.user.username

        # TODO Implement proper permissions
        if request.user.username != username and not self.has_api_key_permissions(request) \
                and not request.user.is_staff:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        try:
            return Response(api.get_enrollment(username, course_id))
        except CourseEnrollmentError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while retrieving enrollments for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=username, course_id=course_id)
                }
            )
示例#16
0
    def get(self, request, course_id=None, username=None):
        """Create, read, or update enrollment information for a user.

        HTTP Endpoint for all CRUD operations for a user course enrollment. Allows creation, reading, and
        updates of the current enrollment for a particular course.

        Args:
            request (Request): To get current course enrollment information, a GET request will return
                information for the current user and the specified course.
            course_id (str): URI element specifying the course location. Enrollment information will be
                returned, created, or updated for this particular course.
            username (str): The username associated with this enrollment request.

        Return:
            A JSON serialized representation of the course enrollment.

        """
        username = username or request.user.username

        # TODO Implement proper permissions
        if request.user.username != username and not self.has_api_key_permissions(request) \
                and not request.user.is_staff:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        try:
            return Response(api.get_enrollment(username, course_id))
        except CourseEnrollmentError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        "An error occurred while retrieving enrollments for user "
                        "'{username}' in course '{course_id}'"
                    ).format(username=username, course_id=course_id)
                }
            )
示例#17
0
    def post(self, request):
        # pylint: disable=too-many-statements
        """Enrolls the currently logged-in user in a course.

        Server-to-server calls may deactivate or modify the mode of existing enrollments. All other requests
        go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
        """
        # Get the User, Course ID, and Mode from the request.

        username = request.data.get('user', request.user.username)
        course_id = request.data.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"Course ID must be specified to create a new enrollment."
                })

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"No course '{course_id}' found for enrollment".format(
                        course_id=course_id)
                })

        mode = request.data.get('mode')

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not username:
            username = request.user.username
        if username != request.user.username and not has_api_key_permissions:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        if mode not in (CourseMode.AUDIT, CourseMode.HONOR,
                        None) and not has_api_key_permissions:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message":
                    u"User does not have permission to create enrollment with mode [{mode}]."
                    .format(mode=mode)
                })

        try:
            # Lookup the user, instead of using request.user, since request.user may not match the username POSTed.
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            return Response(status=status.HTTP_406_NOT_ACCEPTABLE,
                            data={
                                'message':
                                u'The user {} does not exist.'.format(username)
                            })

        embargo_response = embargo_api.get_embargo_response(
            request, course_id, user)

        if embargo_response:
            return embargo_response

        try:
            is_active = request.data.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={
                        'message':
                        (u"'{value}' is an invalid enrollment activation status."
                         ).format(value=is_active)
                    })

            explicit_linked_enterprise = request.data.get(
                'linked_enterprise_customer')
            if explicit_linked_enterprise and has_api_key_permissions and enterprise_enabled(
            ):
                enterprise_api_client = EnterpriseApiServiceClient()
                consent_client = ConsentApiServiceClient()
                try:
                    enterprise_api_client.post_enterprise_course_enrollment(
                        username, text_type(course_id), None)
                except EnterpriseApiException as error:
                    log.exception(
                        u"An unexpected error occurred while creating the new EnterpriseCourseEnrollment "
                        u"for user [%s] in course run [%s]", username,
                        course_id)
                    raise CourseEnrollmentError(text_type(error))
                kwargs = {
                    'username': username,
                    'course_id': text_type(course_id),
                    'enterprise_customer_uuid': explicit_linked_enterprise,
                }
                consent_client.provide_consent(**kwargs)

            enrollment_attributes = request.data.get('enrollment_attributes')
            enrollment = api.get_enrollment(username, text_type(course_id))
            mode_changed = enrollment and mode is not None and enrollment[
                'mode'] != mode
            active_changed = enrollment and is_active is not None and enrollment[
                'is_active'] != is_active
            missing_attrs = []
            if enrollment_attributes:
                actual_attrs = [
                    u"{namespace}:{name}".format(**attr)
                    for attr in enrollment_attributes
                ]
                missing_attrs = set(REQUIRED_ATTRIBUTES.get(
                    mode, [])) - set(actual_attrs)
            if has_api_key_permissions and (mode_changed or active_changed):
                if mode_changed and active_changed and not is_active:
                    # if the requester wanted to deactivate but specified the wrong mode, fail
                    # the request (on the assumption that the requester had outdated information
                    # about the currently active enrollment).
                    msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
                        enrollment["mode"], mode)
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST,
                                    data={"message": msg})

                if missing_attrs:
                    msg = u"Missing enrollment attributes: requested mode={} required attributes={}".format(
                        mode, REQUIRED_ATTRIBUTES.get(mode))
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST,
                                    data={"message": msg})

                response = api.update_enrollment(
                    username,
                    text_type(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes,
                    # If we are updating enrollment by authorized api caller, we should allow expired modes
                    include_expired=has_api_key_permissions)
            else:
                # Will reactivate inactive enrollments.
                response = api.add_enrollment(
                    username,
                    text_type(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes)

            cohort_name = request.data.get('cohort')
            if cohort_name is not None:
                cohort = get_cohort_by_name(course_id, cohort_name)
                try:
                    add_user_to_cohort(cohort, user)
                except ValueError:
                    # user already in cohort, probably because they were un-enrolled and re-enrolled
                    log.exception('Cohort re-addition')
            email_opt_in = request.data.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)

            log.info(
                u'The user [%s] has already been enrolled in course run [%s].',
                username, course_id)
            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    (u"The [{mode}] course mode is expired or otherwise unavailable for course run [{course_id}]."
                     ).format(mode=mode, course_id=course_id),
                    "course_details":
                    error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"No course '{course_id}' found for enrollment".format(
                        course_id=course_id)
                })
        except CourseEnrollmentExistsError as error:
            log.warning(
                u'An enrollment already exists for user [%s] in course run [%s].',
                username, course_id)
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            log.exception(
                u"An error occurred while creating the new course enrollment for user "
                u"[%s] in course run [%s]", username, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    (u"An error occurred while creating the new course enrollment for user "
                     u"'{username}' in course '{course_id}'").format(
                         username=username, course_id=course_id)
                })
        except CourseUserGroup.DoesNotExist:
            log.exception(u'Missing cohort [%s] in course run [%s]',
                          cohort_name, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"An error occured while adding to cohort [%s]" %
                    cohort_name
                })
        finally:
            # Assumes that the ecommerce service uses an API key to authenticate.
            if has_api_key_permissions:
                current_enrollment = api.get_enrollment(
                    username, text_type(course_id))
                audit_log('enrollment_change_requested',
                          course_id=text_type(course_id),
                          requested_mode=mode,
                          actual_mode=current_enrollment['mode']
                          if current_enrollment else None,
                          requested_activation=is_active,
                          actual_activation=current_enrollment['is_active']
                          if current_enrollment else None,
                          user_id=user.id)
示例#18
0
文件: views.py 项目: edx/edx-platform
    def post(self, request):
        # pylint: disable=too-many-statements
        """Enrolls the currently logged-in user in a course.

        Server-to-server calls may deactivate or modify the mode of existing enrollments. All other requests
        go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
        """
        # Get the User, Course ID, and Mode from the request.

        username = request.data.get('user', request.user.username)
        course_id = request.data.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )

        mode = request.data.get('mode')

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not username:
            username = request.user.username
        if username != request.user.username and not has_api_key_permissions:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        if mode not in (CourseMode.AUDIT, CourseMode.HONOR, None) and not has_api_key_permissions:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message": u"User does not have permission to create enrollment with mode [{mode}].".format(
                        mode=mode
                    )
                }
            )

        try:
            # Lookup the user, instead of using request.user, since request.user may not match the username POSTed.
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            return Response(
                status=status.HTTP_406_NOT_ACCEPTABLE,
                data={
                    'message': u'The user {} does not exist.'.format(username)
                }
            )

        embargo_response = embargo_api.get_embargo_response(request, course_id, user)

        if embargo_response:
            return embargo_response

        try:
            is_active = request.data.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={
                        'message': (u"'{value}' is an invalid enrollment activation status.").format(value=is_active)
                    }
                )

            explicit_linked_enterprise = request.data.get('linked_enterprise_customer')
            if explicit_linked_enterprise and has_api_key_permissions and enterprise_enabled():
                enterprise_api_client = EnterpriseApiServiceClient()
                consent_client = ConsentApiServiceClient()
                try:
                    enterprise_api_client.post_enterprise_course_enrollment(username, text_type(course_id), None)
                except EnterpriseApiException as error:
                    log.exception(u"An unexpected error occurred while creating the new EnterpriseCourseEnrollment "
                                  u"for user [%s] in course run [%s]", username, course_id)
                    raise CourseEnrollmentError(text_type(error))
                kwargs = {
                    'username': username,
                    'course_id': text_type(course_id),
                    'enterprise_customer_uuid': explicit_linked_enterprise,
                }
                consent_client.provide_consent(**kwargs)

            enrollment_attributes = request.data.get('enrollment_attributes')
            enrollment = api.get_enrollment(username, text_type(course_id))
            mode_changed = enrollment and mode is not None and enrollment['mode'] != mode
            active_changed = enrollment and is_active is not None and enrollment['is_active'] != is_active
            missing_attrs = []
            if enrollment_attributes:
                actual_attrs = [
                    u"{namespace}:{name}".format(**attr)
                    for attr in enrollment_attributes
                ]
                missing_attrs = set(REQUIRED_ATTRIBUTES.get(mode, [])) - set(actual_attrs)
            if has_api_key_permissions and (mode_changed or active_changed):
                if mode_changed and active_changed and not is_active:
                    # if the requester wanted to deactivate but specified the wrong mode, fail
                    # the request (on the assumption that the requester had outdated information
                    # about the currently active enrollment).
                    msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
                        enrollment["mode"], mode
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                if missing_attrs:
                    msg = u"Missing enrollment attributes: requested mode={} required attributes={}".format(
                        mode, REQUIRED_ATTRIBUTES.get(mode)
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                response = api.update_enrollment(
                    username,
                    text_type(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes,
                    # If we are updating enrollment by authorized api caller, we should allow expired modes
                    include_expired=has_api_key_permissions
                )
            else:
                # Will reactivate inactive enrollments.
                response = api.add_enrollment(
                    username,
                    text_type(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes
                )

            cohort_name = request.data.get('cohort')
            if cohort_name is not None:
                cohort = get_cohort_by_name(course_id, cohort_name)
                try:
                    add_user_to_cohort(cohort, user)
                except ValueError:
                    # user already in cohort, probably because they were un-enrolled and re-enrolled
                    log.exception('Cohort re-addition')
            email_opt_in = request.data.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)

            log.info(u'The user [%s] has already been enrolled in course run [%s].', username, course_id)
            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The [{mode}] course mode is expired or otherwise unavailable for course run [{course_id}]."
                    ).format(mode=mode, course_id=course_id),
                    "course_details": error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        except CourseEnrollmentExistsError as error:
            log.warning(u'An enrollment already exists for user [%s] in course run [%s].', username, course_id)
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            log.exception(u"An error occurred while creating the new course enrollment for user "
                          u"[%s] in course run [%s]", username, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=username, course_id=course_id)
                }
            )
        except CourseUserGroup.DoesNotExist:
            log.exception(u'Missing cohort [%s] in course run [%s]', cohort_name, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"An error occured while adding to cohort [%s]" % cohort_name
                })
        finally:
            # Assumes that the ecommerce service uses an API key to authenticate.
            if has_api_key_permissions:
                current_enrollment = api.get_enrollment(username, text_type(course_id))
                audit_log(
                    'enrollment_change_requested',
                    course_id=text_type(course_id),
                    requested_mode=mode,
                    actual_mode=current_enrollment['mode'] if current_enrollment else None,
                    requested_activation=is_active,
                    actual_activation=current_enrollment['is_active'] if current_enrollment else None,
                    user_id=user.id
                )
示例#19
0
def lms_enroll_user_in_course(
    username,
    course_id,
    mode,
    enterprise_uuid,
    is_active=True,
):
    """
    Enrollment function meant to be called by edx-enterprise to replace the
    current uses of the EnrollmentApiClient
    The REST enrollment endpoint may also eventually also want to reuse this function
    since it's a subset of what the endpoint handles

    Unlike the REST endpoint, this function does not check for enterprise enabled, or user api key
    permissions etc. Those concerns are still going to be used by REST endpoint but this function
    is meant for use from within edx-enterprise hence already presume such privileges.

    Arguments:
     - username (str): User name
     - course_id (obj) : Course key obtained using CourseKey.from_string(course_id_input)
     - mode (CourseMode): course mode
     - enterprise_uuid (str): id to identify the enterprise to enroll under
     - is_active (bool): Optional. A Boolean value that indicates whether the
        enrollment is to be set to inactive (if False). Usually we want a True if enrolling anew.

    Returns: A serializable dictionary of the new course enrollment. If it hits
     `CourseEnrollmentExistsError` then it logs the error and returns None.
    """
    user = _validate_enrollment_inputs(username, course_id)

    with transaction.atomic():
        try:
            response = enrollment_api.add_enrollment(
                username,
                str(course_id),
                mode=mode,
                is_active=is_active,
                enrollment_attributes=None,
                enterprise_uuid=enterprise_uuid,
            )
            log.info('The user [%s] has been enrolled in course run [%s].',
                     username, course_id)
            return response
        except CourseEnrollmentExistsError as error:  # pylint: disable=unused-variable
            log.warning(
                'An enrollment already exists for user [%s] in course run [%s].',
                username, course_id)
            return None
        except CourseEnrollmentError as error:
            log.exception(
                "An error occurred while creating the new course enrollment for user "
                "[%s] in course run [%s]", username, course_id)
            raise error
        finally:
            # Assumes that the ecommerce service uses an API key to authenticate.
            current_enrollment = enrollment_api.get_enrollment(
                username, str(course_id))
            audit_log('enrollment_change_requested',
                      course_id=str(course_id),
                      requested_mode=mode,
                      actual_mode=current_enrollment['mode']
                      if current_enrollment else None,
                      requested_activation=is_active,
                      actual_activation=current_enrollment['is_active']
                      if current_enrollment else None,
                      user_id=user.id)