Merge pull request #21149 from edx/PROD-305/add-credit-mode-enrollment

Add credit mode in support enrollment tool.
This commit is contained in:
Uzair Rasheed
2019-07-21 00:45:52 +05:00
committed by GitHub
3 changed files with 45 additions and 30 deletions

View File

@@ -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])

View File

@@ -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."""

View File

@@ -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