feat: add a create enrollment endpoint for support tools

This commit is contained in:
Ali-D-Akbar
2021-06-03 01:01:08 +05:00
parent 20d631ff70
commit 0d1d9190e5
5 changed files with 110 additions and 10 deletions

View File

@@ -5,7 +5,7 @@
updateEnrollment: function(new_mode, reason) {
return $.ajax({
url: this.url(),
type: 'POST',
type: 'PATCH',
contentType: 'application/json',
data: JSON.stringify({
course_id: this.get('course_id'),

View File

@@ -22,7 +22,7 @@ define([
reason: 'Financial Assistance'
};
enrollment.updateEnrollment('verified', 'Financial Assistance');
AjaxHelpers.expectJsonRequest(requests, 'POST', '/support/enrollment/test-user', {
AjaxHelpers.expectJsonRequest(requests, 'PATCH', '/support/enrollment/test-user', {
course_id: EnrollmentHelpers.TEST_COURSE,
new_mode: 'verified',
old_mode: 'audit',

View File

@@ -73,7 +73,7 @@ define([
$('.enrollment-new-mode').val('verified');
$('.enrollment-reason').val('Financial Assistance');
$('.enrollment-change-submit').click();
AjaxHelpers.expectJsonRequest(requests, 'POST', '/support/enrollment/test-user', {
AjaxHelpers.expectJsonRequest(requests, 'PATCH', '/support/enrollment/test-user', {
course_id: EnrollmentHelpers.TEST_COURSE,
new_mode: 'verified',
old_mode: 'audit',

View File

@@ -24,6 +24,7 @@ from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.entitlements.tests.factories import CourseEntitlementFactory
from common.djangoapps.student.models import (
ENROLLED_TO_ENROLLED,
UNENROLLED_TO_ENROLLED,
CourseEnrollment,
CourseEnrollmentAttribute,
ManualEnrollmentAudit
@@ -329,18 +330,64 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
@disable_signal(signals, 'post_save')
@ddt.data('username', 'email')
def test_change_enrollment(self, search_string_type):
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='newStudent', email='test2@example.com', password='test')
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 == 'test2@example.com'
assert manual_enrollment.state_transition == UNENROLLED_TO_ENROLLED
@disable_signal(signals, 'post_save')
@ddt.data('username', 'email')
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
@disable_signal(signals, 'post_save')
@ddt.data('username', 'email')
def test_change_enrollment(self, search_string_type):
"""
Assert changing mode for an enrollment.
"""
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.patch(url, data={
'course_id': str(self.course.id),
'old_mode': CourseMode.AUDIT,
'new_mode': CourseMode.VERIFIED,
'reason': 'Financial Assistance'
})
}, content_type='application/json')
assert response.status_code == 200
assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is not None
self.assert_enrollment(CourseMode.VERIFIED)
@@ -365,12 +412,12 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
'support:enrollment_list',
kwargs={'username_or_email': getattr(self.student, search_string_type)}
)
response = self.client.post(url, data={
response = self.client.patch(url, data={
'course_id': str(self.course.id),
'old_mode': CourseMode.AUDIT,
'new_mode': CourseMode.VERIFIED,
'reason': 'Financial Assistance'
})
}, content_type='application/json')
entitlement.refresh_from_db()
assert response.status_code == 200
assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is not None
@@ -406,7 +453,7 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
# 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)
response = self.client.patch(self.url, data, content_type='application/json')
assert response.status_code == 400
assert re.match(error_message, response.content.decode('utf-8').replace("'", '').replace('"', '')) is not None
@@ -485,12 +532,12 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
['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={
response = self.client.patch(url, data={
'course_id': str(self.course.id),
'old_mode': CourseMode.AUDIT,
'new_mode': new_mode,
'reason': 'Financial Assistance'
})
}, content_type='application/json')
assert response.status_code == 200
assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is not None

View File

@@ -17,6 +17,7 @@ from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.edxmako.shortcuts import render_to_response
from common.djangoapps.student.models import (
ENROLLED_TO_ENROLLED,
UNENROLLED_TO_ENROLLED,
CourseEnrollment,
CourseEnrollmentAttribute,
ManualEnrollmentAudit
@@ -84,6 +85,58 @@ class EnrollmentSupportListView(GenericAPIView):
@method_decorator(require_support_permission)
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)
@method_decorator(require_support_permission)
def patch(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))