From 07821927e225bfa005cdbc6249f56fedc8054bf7 Mon Sep 17 00:00:00 2001 From: uzairr Date: Wed, 17 Jul 2019 15:39:03 +0500 Subject: [PATCH] Add credit modein support enrollment tool. Currently, enrollment support tool is only allowing support members to change course enrollment only one of the two modes i.e. audit and verified.To move a learner other than these modes,they need to ping devops.To broaden the scope of enrollment support tool,changes have been done so that enrollment would be changed to other modes as well. PROD-305 --- common/djangoapps/course_modes/models.py | 6 ++- lms/djangoapps/support/tests/test_views.py | 51 +++++++++++---------- lms/djangoapps/support/views/enrollments.py | 18 ++++++-- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/common/djangoapps/course_modes/models.py b/common/djangoapps/course_modes/models.py index c03b164e37..85c4db1a43 100644 --- a/common/djangoapps/course_modes/models.py +++ b/common/djangoapps/course_modes/models.py @@ -333,7 +333,9 @@ class CourseMode(models.Model): @classmethod @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 @@ -384,7 +386,7 @@ class CourseMode(models.Model): if only_selectable: if course is not None and hasattr(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) modes = ([mode.to_tuple() for mode in found_course_modes]) diff --git a/lms/djangoapps/support/tests/test_views.py b/lms/djangoapps/support/tests/test_views.py index 1ec44bd791..092b0db596 100644 --- a/lms/djangoapps/support/tests/test_views.py +++ b/lms/djangoapps/support/tests/test_views.py @@ -15,13 +15,14 @@ import six from django.contrib.auth.models import User from django.db.models import signals from django.urls import reverse +from mock import patch from pytz import UTC from common.test.utils import disable_signal from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory 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.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase @@ -257,7 +258,8 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase }, data[0]) self.assertEqual( {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']} ) @@ -329,10 +331,9 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase self.assertIsNone(ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email)) @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): - """ Verify support can changed the enrollment to all available modes - except credit. """ + """ Verify support can changed the enrollment to all available modes""" self.assert_update_enrollment('username', new_mode) @disable_signal(signals, 'post_save') @@ -342,10 +343,6 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase self.set_course_end_date_and_expiry() 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') def test_get_enrollments_with_expired_mode(self, search_string_type): """ 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): """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 = [] for mode in modes: 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( {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']} ) @@ -405,19 +402,25 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase 'support:enrollment_list', kwargs={'username_or_email': getattr(self.student, search_string_type)} ) - response = self.client.post(url, data={ - 'course_id': six.text_type(self.course.id), - 'old_mode': CourseMode.AUDIT, - 'new_mode': new_mode, - 'reason': 'Financial Assistance' - }) - # Enrollment cannot be changed to credit mode. - if new_mode == CourseMode.CREDIT_MODE: - self.assertEqual(response.status_code, 400) - else: - self.assertEqual(response.status_code, 200) - self.assertIsNotNone(ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email)) - self.assert_enrollment(new_mode) + + with patch('support.views.enrollments.get_credit_provider_attribute_values') as mock_method: + credit_provider = ( + [u'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': six.text_type(self.course.id), + 'old_mode': CourseMode.AUDIT, + 'new_mode': new_mode, + 'reason': 'Financial Assistance' + }) + + 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): """ Set the course-end date and expire its verified mode.""" diff --git a/lms/djangoapps/support/views/enrollments.py b/lms/djangoapps/support/views/enrollments.py index 54cec1e0f2..3c7d7aa092 100644 --- a/lms/djangoapps/support/views/enrollments.py +++ b/lms/djangoapps/support/views/enrollments.py @@ -21,10 +21,11 @@ from edxmako.shortcuts import render_to_response from lms.djangoapps.support.decorators import require_support_permission from lms.djangoapps.support.serializers import ManualEnrollmentSerializer 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.errors import CourseModeNotFoundError 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 @@ -94,8 +95,6 @@ class EnrollmentSupportListView(GenericAPIView): username=user.username, old_mode=old_mode )) - if new_mode == CourseMode.CREDIT_MODE: - return HttpResponseBadRequest(u'Enrollment cannot be changed to credit mode.') except KeyError as err: return HttpResponseBadRequest(u'The field {} is required.'.format(text_type(err))) except InvalidKeyError: @@ -119,6 +118,16 @@ class EnrollmentSupportListView(GenericAPIView): 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] + ) return JsonResponse(ManualEnrollmentSerializer(instance=manual_enrollment).data) except CourseModeNotFoundError as err: return HttpResponseBadRequest(text_type(err)) @@ -178,7 +187,8 @@ class EnrollmentSupportListView(GenericAPIView): """ course_modes = CourseMode.modes_for_course( course_key, - include_expired=True + include_expired=True, + exclude_credit=False ) return [ ModeSerializer(mode).data