Merge pull request #21149 from edx/PROD-305/add-credit-mode-enrollment
Add credit mode in support enrollment tool.
This commit is contained in:
@@ -333,7 +333,9 @@ class CourseMode(models.Model):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@request_cached(CACHE_NAMESPACE)
|
@request_cached(CACHE_NAMESPACE)
|
||||||
def modes_for_course(cls, course_id=None, include_expired=False, only_selectable=True, course=None):
|
def modes_for_course(
|
||||||
|
cls, course_id=None, include_expired=False, only_selectable=True, course=None, exclude_credit=True
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Returns a list of the non-expired modes for a given course id
|
Returns a list of the non-expired modes for a given course id
|
||||||
|
|
||||||
@@ -384,7 +386,7 @@ class CourseMode(models.Model):
|
|||||||
if only_selectable:
|
if only_selectable:
|
||||||
if course is not None and hasattr(course, 'selectable_modes'):
|
if course is not None and hasattr(course, 'selectable_modes'):
|
||||||
found_course_modes = course.selectable_modes
|
found_course_modes = course.selectable_modes
|
||||||
else:
|
elif exclude_credit:
|
||||||
found_course_modes = found_course_modes.exclude(mode_slug__in=cls.CREDIT_MODES)
|
found_course_modes = found_course_modes.exclude(mode_slug__in=cls.CREDIT_MODES)
|
||||||
|
|
||||||
modes = ([mode.to_tuple() for mode in found_course_modes])
|
modes = ([mode.to_tuple() for mode in found_course_modes])
|
||||||
|
|||||||
@@ -15,13 +15,14 @@ import six
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from mock import patch
|
||||||
from pytz import UTC
|
from pytz import UTC
|
||||||
|
|
||||||
from common.test.utils import disable_signal
|
from common.test.utils import disable_signal
|
||||||
from course_modes.models import CourseMode
|
from course_modes.models import CourseMode
|
||||||
from course_modes.tests.factories import CourseModeFactory
|
from course_modes.tests.factories import CourseModeFactory
|
||||||
from lms.djangoapps.verify_student.models import VerificationDeadline
|
from lms.djangoapps.verify_student.models import VerificationDeadline
|
||||||
from student.models import ENROLLED_TO_ENROLLED, CourseEnrollment, ManualEnrollmentAudit
|
from student.models import ENROLLED_TO_ENROLLED, CourseEnrollment, CourseEnrollmentAttribute, ManualEnrollmentAudit
|
||||||
from student.roles import GlobalStaff, SupportStaffRole
|
from student.roles import GlobalStaff, SupportStaffRole
|
||||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory
|
from student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
|
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
|
||||||
@@ -257,7 +258,8 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
|
|||||||
}, data[0])
|
}, data[0])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{CourseMode.VERIFIED, CourseMode.AUDIT, CourseMode.HONOR,
|
{CourseMode.VERIFIED, CourseMode.AUDIT, CourseMode.HONOR,
|
||||||
CourseMode.NO_ID_PROFESSIONAL_MODE, CourseMode.PROFESSIONAL},
|
CourseMode.NO_ID_PROFESSIONAL_MODE, CourseMode.PROFESSIONAL,
|
||||||
|
CourseMode.CREDIT_MODE},
|
||||||
{mode['slug'] for mode in data[0]['course_modes']}
|
{mode['slug'] for mode in data[0]['course_modes']}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -329,10 +331,9 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
|
|||||||
self.assertIsNone(ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email))
|
self.assertIsNone(ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email))
|
||||||
|
|
||||||
@disable_signal(signals, 'post_save')
|
@disable_signal(signals, 'post_save')
|
||||||
@ddt.data('honor', 'audit', 'verified', 'professional', 'no-id-professional')
|
@ddt.data('honor', 'audit', 'verified', 'professional', 'no-id-professional', 'credit')
|
||||||
def test_update_enrollment_for_all_modes(self, new_mode):
|
def test_update_enrollment_for_all_modes(self, new_mode):
|
||||||
""" Verify support can changed the enrollment to all available modes
|
""" Verify support can changed the enrollment to all available modes"""
|
||||||
except credit. """
|
|
||||||
self.assert_update_enrollment('username', new_mode)
|
self.assert_update_enrollment('username', new_mode)
|
||||||
|
|
||||||
@disable_signal(signals, 'post_save')
|
@disable_signal(signals, 'post_save')
|
||||||
@@ -342,10 +343,6 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
|
|||||||
self.set_course_end_date_and_expiry()
|
self.set_course_end_date_and_expiry()
|
||||||
self.assert_update_enrollment('username', new_mode)
|
self.assert_update_enrollment('username', new_mode)
|
||||||
|
|
||||||
def test_update_enrollment_with_credit_mode_throws_error(self):
|
|
||||||
""" Verify that enrollment cannot be changed to credit mode. """
|
|
||||||
self.assert_update_enrollment('username', CourseMode.CREDIT_MODE)
|
|
||||||
|
|
||||||
@ddt.data('username', 'email')
|
@ddt.data('username', 'email')
|
||||||
def test_get_enrollments_with_expired_mode(self, search_string_type):
|
def test_get_enrollments_with_expired_mode(self, search_string_type):
|
||||||
""" Verify that page can get the all modes with archived course. """
|
""" Verify that page can get the all modes with archived course. """
|
||||||
@@ -367,7 +364,7 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
|
|||||||
|
|
||||||
def _assert_generated_modes(self, response):
|
def _assert_generated_modes(self, response):
|
||||||
"""Dry method to generate course modes dict and test with response data."""
|
"""Dry method to generate course modes dict and test with response data."""
|
||||||
modes = CourseMode.modes_for_course(self.course.id, include_expired=True)
|
modes = CourseMode.modes_for_course(self.course.id, include_expired=True, exclude_credit=False)
|
||||||
modes_data = []
|
modes_data = []
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
expiry = mode.expiration_datetime.strftime('%Y-%m-%dT%H:%M:%SZ') if mode.expiration_datetime else None
|
expiry = mode.expiration_datetime.strftime('%Y-%m-%dT%H:%M:%SZ') if mode.expiration_datetime else None
|
||||||
@@ -394,7 +391,7 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{CourseMode.VERIFIED, CourseMode.AUDIT, CourseMode.NO_ID_PROFESSIONAL_MODE,
|
{CourseMode.VERIFIED, CourseMode.AUDIT, CourseMode.NO_ID_PROFESSIONAL_MODE,
|
||||||
CourseMode.PROFESSIONAL, CourseMode.HONOR},
|
CourseMode.PROFESSIONAL, CourseMode.HONOR, CourseMode.CREDIT_MODE},
|
||||||
{mode['slug'] for mode in data[0]['course_modes']}
|
{mode['slug'] for mode in data[0]['course_modes']}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -405,19 +402,25 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
|
|||||||
'support:enrollment_list',
|
'support:enrollment_list',
|
||||||
kwargs={'username_or_email': getattr(self.student, search_string_type)}
|
kwargs={'username_or_email': getattr(self.student, search_string_type)}
|
||||||
)
|
)
|
||||||
response = self.client.post(url, data={
|
|
||||||
'course_id': six.text_type(self.course.id),
|
with patch('support.views.enrollments.get_credit_provider_attribute_values') as mock_method:
|
||||||
'old_mode': CourseMode.AUDIT,
|
credit_provider = (
|
||||||
'new_mode': new_mode,
|
[u'Arizona State University'], 'You are now eligible for credit from Arizona State University'
|
||||||
'reason': 'Financial Assistance'
|
)
|
||||||
})
|
mock_method.return_value = credit_provider
|
||||||
# Enrollment cannot be changed to credit mode.
|
response = self.client.post(url, data={
|
||||||
if new_mode == CourseMode.CREDIT_MODE:
|
'course_id': six.text_type(self.course.id),
|
||||||
self.assertEqual(response.status_code, 400)
|
'old_mode': CourseMode.AUDIT,
|
||||||
else:
|
'new_mode': new_mode,
|
||||||
self.assertEqual(response.status_code, 200)
|
'reason': 'Financial Assistance'
|
||||||
self.assertIsNotNone(ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email))
|
})
|
||||||
self.assert_enrollment(new_mode)
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertIsNotNone(ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email))
|
||||||
|
self.assert_enrollment(new_mode)
|
||||||
|
if new_mode == 'credit':
|
||||||
|
enrollment_attr = CourseEnrollmentAttribute.objects.first()
|
||||||
|
self.assertEqual(enrollment_attr.value, unicode(credit_provider[0]))
|
||||||
|
|
||||||
def set_course_end_date_and_expiry(self):
|
def set_course_end_date_and_expiry(self):
|
||||||
""" Set the course-end date and expire its verified mode."""
|
""" Set the course-end date and expire its verified mode."""
|
||||||
|
|||||||
@@ -21,10 +21,11 @@ from edxmako.shortcuts import render_to_response
|
|||||||
from lms.djangoapps.support.decorators import require_support_permission
|
from lms.djangoapps.support.decorators import require_support_permission
|
||||||
from lms.djangoapps.support.serializers import ManualEnrollmentSerializer
|
from lms.djangoapps.support.serializers import ManualEnrollmentSerializer
|
||||||
from lms.djangoapps.verify_student.models import VerificationDeadline
|
from lms.djangoapps.verify_student.models import VerificationDeadline
|
||||||
|
from openedx.core.djangoapps.credit.email_utils import get_credit_provider_attribute_values
|
||||||
from openedx.core.djangoapps.enrollments.api import get_enrollments, update_enrollment
|
from openedx.core.djangoapps.enrollments.api import get_enrollments, update_enrollment
|
||||||
from openedx.core.djangoapps.enrollments.errors import CourseModeNotFoundError
|
from openedx.core.djangoapps.enrollments.errors import CourseModeNotFoundError
|
||||||
from openedx.core.djangoapps.enrollments.serializers import ModeSerializer
|
from openedx.core.djangoapps.enrollments.serializers import ModeSerializer
|
||||||
from student.models import ENROLLED_TO_ENROLLED, CourseEnrollment, ManualEnrollmentAudit
|
from student.models import ENROLLED_TO_ENROLLED, CourseEnrollment, CourseEnrollmentAttribute, ManualEnrollmentAudit
|
||||||
from util.json_request import JsonResponse
|
from util.json_request import JsonResponse
|
||||||
|
|
||||||
|
|
||||||
@@ -94,8 +95,6 @@ class EnrollmentSupportListView(GenericAPIView):
|
|||||||
username=user.username,
|
username=user.username,
|
||||||
old_mode=old_mode
|
old_mode=old_mode
|
||||||
))
|
))
|
||||||
if new_mode == CourseMode.CREDIT_MODE:
|
|
||||||
return HttpResponseBadRequest(u'Enrollment cannot be changed to credit mode.')
|
|
||||||
except KeyError as err:
|
except KeyError as err:
|
||||||
return HttpResponseBadRequest(u'The field {} is required.'.format(text_type(err)))
|
return HttpResponseBadRequest(u'The field {} is required.'.format(text_type(err)))
|
||||||
except InvalidKeyError:
|
except InvalidKeyError:
|
||||||
@@ -119,6 +118,16 @@ class EnrollmentSupportListView(GenericAPIView):
|
|||||||
reason=reason,
|
reason=reason,
|
||||||
enrollment=enrollment
|
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]
|
||||||
|
)
|
||||||
return JsonResponse(ManualEnrollmentSerializer(instance=manual_enrollment).data)
|
return JsonResponse(ManualEnrollmentSerializer(instance=manual_enrollment).data)
|
||||||
except CourseModeNotFoundError as err:
|
except CourseModeNotFoundError as err:
|
||||||
return HttpResponseBadRequest(text_type(err))
|
return HttpResponseBadRequest(text_type(err))
|
||||||
@@ -178,7 +187,8 @@ class EnrollmentSupportListView(GenericAPIView):
|
|||||||
"""
|
"""
|
||||||
course_modes = CourseMode.modes_for_course(
|
course_modes = CourseMode.modes_for_course(
|
||||||
course_key,
|
course_key,
|
||||||
include_expired=True
|
include_expired=True,
|
||||||
|
exclude_credit=False
|
||||||
)
|
)
|
||||||
return [
|
return [
|
||||||
ModeSerializer(mode).data
|
ModeSerializer(mode).data
|
||||||
|
|||||||
Reference in New Issue
Block a user