def test_create_new_enrollment(self, search_string_type): """ Assert that a new enrollment is created through post request endpoint. """ test_user = UserFactory.create(username='******', email='*****@*****.**', password='******') assert ManualEnrollmentAudit.get_manual_enrollment_by_email( test_user.email) is None url = reverse('support:enrollment_list', kwargs={ 'username_or_email': getattr(test_user, search_string_type) }) response = self.client.post(url, data={ 'course_id': str(self.course.id), 'mode': CourseMode.AUDIT, 'reason': 'Financial Assistance' }) assert response.status_code == 200 manual_enrollment = ManualEnrollmentAudit.get_manual_enrollment_by_email( test_user.email) assert manual_enrollment is not None assert manual_enrollment.reason == response.json()['reason'] assert manual_enrollment.enrolled_email == '*****@*****.**' assert manual_enrollment.state_transition == UNENROLLED_TO_ENROLLED
def assert_update_enrollment(self, search_string_type, new_mode): """ Dry method to update the enrollment and assert response.""" assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is None url = reverse( 'support:enrollment_list', kwargs={'username_or_email': getattr(self.student, search_string_type)} ) with patch('lms.djangoapps.support.views.enrollments.get_credit_provider_attribute_values') as mock_method: credit_provider = ( ['Arizona State University'], 'You are now eligible for credit from Arizona State University' ) mock_method.return_value = credit_provider response = self.client.post(url, data={ 'course_id': str(self.course.id), 'old_mode': CourseMode.AUDIT, 'new_mode': new_mode, 'reason': 'Financial Assistance' }) assert response.status_code == 200 assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is not None self.assert_enrollment(new_mode) if new_mode == 'credit': enrollment_attr = CourseEnrollmentAttribute.objects.first() assert enrollment_attr.value == str(credit_provider[0])
def test_change_enrollment_mode_fullfills_entitlement(self, search_string_type, mock_get_course_uuid): """ Assert that changing student's enrollment fulfills it's respective entitlement if it exists. """ assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is None enrollment = CourseEnrollment.get_enrollment(self.student, self.course.id) entitlement = CourseEntitlementFactory.create( user=self.user, mode=CourseMode.VERIFIED, enrollment_course_run=enrollment ) mock_get_course_uuid.return_value = entitlement.course_uuid url = reverse( 'support:enrollment_list', kwargs={'username_or_email': getattr(self.student, search_string_type)} ) response = self.client.post(url, data={ 'course_id': str(self.course.id), 'old_mode': CourseMode.AUDIT, 'new_mode': CourseMode.VERIFIED, 'reason': 'Financial Assistance' }) entitlement.refresh_from_db() assert response.status_code == 200 assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is not None assert entitlement.enrollment_course_run is not None assert entitlement.is_entitlement_redeemable() is False self.assert_enrollment(CourseMode.VERIFIED)
def test_retirement(self): """ Tests that calling the retirement method for a specific enrollment retires the enrolled_email and reason columns of each row associated with that enrollment. """ enrollment = CourseEnrollment.enroll(self.user, self.course.id) # lint-amnesty, pylint: disable=no-member other_enrollment = CourseEnrollment.enroll(self.user, self.other_course.id) # lint-amnesty, pylint: disable=no-member ManualEnrollmentAudit.create_manual_enrollment_audit( self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED, 'manually enrolling unenrolled user', enrollment) ManualEnrollmentAudit.create_manual_enrollment_audit( self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED, 'manually enrolling unenrolled user again', enrollment) ManualEnrollmentAudit.create_manual_enrollment_audit( self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED, 'manually enrolling unenrolled user', other_enrollment) ManualEnrollmentAudit.create_manual_enrollment_audit( self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED, 'manually enrolling unenrolled user again', other_enrollment) assert ManualEnrollmentAudit.objects.filter( enrollment=enrollment).exists() # retire the ManualEnrollmentAudit objects associated with the above enrollments ManualEnrollmentAudit.retire_manual_enrollments(user=self.user, retired_email="xxx") assert ManualEnrollmentAudit.objects.filter( enrollment=enrollment).exists() assert not ManualEnrollmentAudit.objects.filter( enrollment=enrollment).exclude(enrolled_email='xxx') assert not ManualEnrollmentAudit.objects.filter( enrollment=enrollment).exclude(reason='')
def test_retirement(self): """ Tests that calling the retirement method for a specific enrollment retires the enrolled_email and reason columns of each row associated with that enrollment. """ enrollment = CourseEnrollment.enroll(self.user, self.course.id) other_enrollment = CourseEnrollment.enroll(self.user, self.other_course.id) ManualEnrollmentAudit.create_manual_enrollment_audit( self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED, 'manually enrolling unenrolled user', enrollment ) ManualEnrollmentAudit.create_manual_enrollment_audit( self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED, 'manually enrolling unenrolled user again', enrollment ) ManualEnrollmentAudit.create_manual_enrollment_audit( self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED, 'manually enrolling unenrolled user', other_enrollment ) ManualEnrollmentAudit.create_manual_enrollment_audit( self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED, 'manually enrolling unenrolled user again', other_enrollment ) self.assertTrue(ManualEnrollmentAudit.objects.filter(enrollment=enrollment).exists()) # retire the ManualEnrollmentAudit objects associated with the above enrollments ManualEnrollmentAudit.retire_manual_enrollments(user=self.user, retired_email="xxx") self.assertTrue(ManualEnrollmentAudit.objects.filter(enrollment=enrollment).exists()) self.assertFalse(ManualEnrollmentAudit.objects.filter(enrollment=enrollment).exclude( enrolled_email="xxx" )) self.assertFalse(ManualEnrollmentAudit.objects.filter(enrollment=enrollment).exclude( reason="" ))
def create_manual_enrollment_audit( # lint-amnesty, pylint: disable=missing-function-docstring enrolled_by, user_email, state_transition, reason, course_enrollment): _ManualEnrollmentAudit.create_manual_enrollment_audit( user=enrolled_by, email=user_email, state_transition=state_transition, reason=reason, enrollment=course_enrollment, )
def create_manual_enrollment_audit(enrolled_by, user_email, state_transition, reason, course_enrollment, role): _ManualEnrollmentAudit.create_manual_enrollment_audit( user=enrolled_by, email=user_email, state_transition=state_transition, reason=reason, enrollment=course_enrollment, role=role, )
def test_get_manual_enrollment_history(self): ManualEnrollmentAudit.create_manual_enrollment_audit( self.user, self.student.email, ENROLLED_TO_ENROLLED, 'Financial Assistance', CourseEnrollment.objects.get(course_id=self.course.id, user=self.student) ) response = self.client.get(self.url) assert response.status_code == 200 self.assertDictContainsSubset({ 'enrolled_by': self.user.email, 'reason': 'Financial Assistance', }, json.loads(response.content.decode('utf-8'))[0]['manual_enrollment'])
def test_change_enrollment(self, search_string_type): assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is None url = reverse( 'support:enrollment_list', kwargs={'username_or_email': getattr(self.student, search_string_type)} ) response = self.client.post(url, data={ 'course_id': str(self.course.id), 'old_mode': CourseMode.AUDIT, 'new_mode': CourseMode.VERIFIED, 'reason': 'Financial Assistance' }) assert response.status_code == 200 assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is not None self.assert_enrollment(CourseMode.VERIFIED)
def test_update_enrollments_with_expired_mode(self, search_string_type): """ Verify that enrollment can be updated to verified mode. """ self.set_course_end_date_and_expiry() self.assertIsNone( ManualEnrollmentAudit.get_manual_enrollment_by_email( self.student.email)) self.assert_update_enrollment(search_string_type, CourseMode.VERIFIED)
def post(self, request, username_or_email): """Allows support staff to alter a user's enrollment.""" try: user = User.objects.get( Q(username=username_or_email) | Q(email=username_or_email)) course_id = request.data['course_id'] course_key = CourseKey.from_string(course_id) old_mode = request.data['old_mode'] new_mode = request.data['new_mode'] reason = request.data['reason'] enrollment = CourseEnrollment.objects.get(user=user, course_id=course_key) if enrollment.mode != old_mode: return HttpResponseBadRequest( 'User {username} is not enrolled with mode {old_mode}.'. format(username=user.username, old_mode=old_mode)) except KeyError as err: return HttpResponseBadRequest('The field {} is required.'.format( str(err))) except InvalidKeyError: return HttpResponseBadRequest('Could not parse course key.') except (CourseEnrollment.DoesNotExist, User.DoesNotExist): return HttpResponseBadRequest( 'Could not find enrollment for user {username} in course {course}.' .format(username=username_or_email, course=str(course_key))) try: # Wrapped in a transaction so that we can be sure the # ManualEnrollmentAudit record is always created correctly. with transaction.atomic(): update_enrollment(user.username, course_id, mode=new_mode, include_expired=True) manual_enrollment = ManualEnrollmentAudit.create_manual_enrollment_audit( request.user, enrollment.user.email, ENROLLED_TO_ENROLLED, reason=reason, enrollment=enrollment) if new_mode == CourseMode.CREDIT_MODE: provider_ids = get_credit_provider_attribute_values( course_key, 'id') credit_provider_attr = { 'namespace': 'credit', 'name': 'provider_id', 'value': provider_ids[0], } CourseEnrollmentAttribute.add_enrollment_attr( enrollment=enrollment, data_list=[credit_provider_attr]) entitlement = CourseEntitlement.get_fulfillable_entitlement_for_user_course_run( user=user, course_run_key=course_id) if entitlement is not None and entitlement.mode == new_mode: entitlement.set_enrollment( CourseEnrollment.get_enrollment(user, course_id)) return JsonResponse( ManualEnrollmentSerializer( instance=manual_enrollment).data) except CourseModeNotFoundError as err: return HttpResponseBadRequest(str(err))
def test_change_enrollment_bad_data(self, data, error_message): # `self` isn't available from within the DDT declaration, so # assign the course ID here if 'course_id' in data and data['course_id'] is None: data['course_id'] = str(self.course.id) response = self.client.post(self.url, data) assert response.status_code == 400 assert re.match(error_message, response.content.decode('utf-8').replace("'", '').replace('"', '')) is not None self.assert_enrollment(CourseMode.AUDIT) assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is None
def test_create_existing_enrollment(self, search_string_type): """ Assert that a new enrollment is not created when an enrollment already exist for that course. """ assert ManualEnrollmentAudit.get_manual_enrollment_by_email( self.student.email) is None url = reverse('support:enrollment_list', kwargs={ 'username_or_email': getattr(self.student, search_string_type) }) response = self.client.post(url, data={ 'course_id': str(self.course.id), 'mode': CourseMode.AUDIT, 'reason': 'Financial Assistance' }) assert response.status_code == 400 assert ManualEnrollmentAudit.get_manual_enrollment_by_email( self.student.email) is None
def post(self, request, username_or_email): """ Allows support staff to create a user's enrollment. """ try: course_id = request.data['course_id'] course_key = CourseKey.from_string(course_id) mode = request.data['mode'] reason = request.data['reason'] user = User.objects.get( Q(username=username_or_email) | Q(email=username_or_email)) except KeyError as err: return HttpResponseBadRequest(f'The field {str(err)} is required.') except InvalidKeyError: return HttpResponseBadRequest('Could not parse course key.') except User.DoesNotExist: return HttpResponseBadRequest( 'Could not find user {username}.'.format( username=username_or_email)) enrollment = CourseEnrollment.get_enrollment(user=user, course_key=course_key) if enrollment is not None: return HttpResponseBadRequest( f'The user {str(username_or_email)} is already enrolled in {str(course_id)}.' ) enrollment_modes = [ enrollment_mode['slug'] for enrollment_mode in self.get_course_modes(course_key) ] if mode not in enrollment_modes: return HttpResponseBadRequest( f'{str(mode)} is not a valid mode for {str(course_id)}. ' f'Possible valid modes are {str(enrollment_modes)}') enrollment = CourseEnrollment.enroll(user=user, course_key=course_key, mode=mode) # Wrapped in a transaction so that we can be sure the # ManualEnrollmentAudit record is always created correctly. with transaction.atomic(): manual_enrollment = ManualEnrollmentAudit.create_manual_enrollment_audit( request.user, enrollment.user.email, UNENROLLED_TO_ENROLLED, reason=reason, enrollment=enrollment) return JsonResponse( ManualEnrollmentSerializer(instance=manual_enrollment).data)
def manual_enrollment_data(enrollment_data, course_key): """ Returns serialized information about the manual enrollment belonging to this enrollment, if it exists. Args: enrollment_data (dict): Representation of a single course enrollment. course_key (CourseKey): The course for this enrollment. Returns: None: If no manual enrollment change has been made. dict: Serialization of the latest manual enrollment change. """ user = User.objects.get(username=enrollment_data['user']) enrollment = CourseEnrollment.get_enrollment(user, course_key) manual_enrollment_audit = ManualEnrollmentAudit.get_manual_enrollment(enrollment) if manual_enrollment_audit is None: return {} return ManualEnrollmentSerializer(instance=manual_enrollment_audit).data