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])
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
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)
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
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)))
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)))
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)
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'])
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'])
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'])
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) } )
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) } )
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)
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 )
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)