Merge pull request #12574 from edx/awais786/ECOM-4214-support-tool-modifications
Remove support tool limitation based on verified enrollment deadline
This commit is contained in:
@@ -192,7 +192,7 @@ def add_enrollment(user_id, course_id, mode=None, is_active=True):
|
||||
return _data_api().create_course_enrollment(user_id, course_id, mode, is_active)
|
||||
|
||||
|
||||
def update_enrollment(user_id, course_id, mode=None, is_active=None, enrollment_attributes=None):
|
||||
def update_enrollment(user_id, course_id, mode=None, is_active=None, enrollment_attributes=None, include_expired=False):
|
||||
"""Updates the course mode for the enrolled user.
|
||||
|
||||
Update a course enrollment for the given user and course.
|
||||
@@ -205,6 +205,7 @@ def update_enrollment(user_id, course_id, mode=None, is_active=None, enrollment_
|
||||
mode (str): The new course mode for this enrollment.
|
||||
is_active (bool): Sets whether the enrollment is active or not.
|
||||
enrollment_attributes (list): Attributes to be set the enrollment.
|
||||
include_expired (bool): Boolean denoting whether expired course modes should be included.
|
||||
|
||||
Returns:
|
||||
A serializable dictionary representing the updated enrollment.
|
||||
@@ -241,7 +242,7 @@ def update_enrollment(user_id, course_id, mode=None, is_active=None, enrollment_
|
||||
|
||||
"""
|
||||
if mode is not None:
|
||||
_validate_course_mode(course_id, mode, is_active=is_active)
|
||||
_validate_course_mode(course_id, mode, is_active=is_active, include_expired=include_expired)
|
||||
enrollment = _data_api().update_course_enrollment(user_id, course_id, mode=mode, is_active=is_active)
|
||||
if enrollment is None:
|
||||
msg = u"Course Enrollment not found for user {user} in course {course}".format(user=user_id, course=course_id)
|
||||
@@ -393,7 +394,7 @@ def _default_course_mode(course_id):
|
||||
return CourseMode.DEFAULT_MODE_SLUG
|
||||
|
||||
|
||||
def _validate_course_mode(course_id, mode, is_active=None):
|
||||
def _validate_course_mode(course_id, mode, is_active=None, include_expired=False):
|
||||
"""Checks to see if the specified course mode is valid for the course.
|
||||
|
||||
If the requested course mode is not available for the course, raise an error with corresponding
|
||||
@@ -405,6 +406,7 @@ def _validate_course_mode(course_id, mode, is_active=None):
|
||||
|
||||
Keyword Arguments:
|
||||
is_active (bool): Whether the enrollment is to be activated or deactivated.
|
||||
include_expired (bool): Boolean denoting whether expired course modes should be included.
|
||||
|
||||
Returns:
|
||||
None
|
||||
@@ -414,7 +416,9 @@ def _validate_course_mode(course_id, mode, is_active=None):
|
||||
"""
|
||||
# If the client has requested an enrollment deactivation, we want to include expired modes
|
||||
# in the set of available modes. This allows us to unenroll users from expired modes.
|
||||
include_expired = not is_active if is_active is not None else False
|
||||
# If include_expired is set as True we should not redetermine its value.
|
||||
if not include_expired:
|
||||
include_expired = not is_active if is_active is not None else False
|
||||
|
||||
course_enrollment_info = _data_api().get_course_enrollment_info(course_id, include_expired=include_expired)
|
||||
course_modes = course_enrollment_info["course_modes"]
|
||||
|
||||
@@ -21,6 +21,8 @@ _COURSES = []
|
||||
|
||||
_ENROLLMENT_ATTRIBUTES = []
|
||||
|
||||
_VERIFIED_MODE_EXPIRED = []
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get_course_enrollments(student_id):
|
||||
@@ -50,7 +52,7 @@ def update_course_enrollment(student_id, course_id, mode=None, is_active=None):
|
||||
|
||||
def get_course_enrollment_info(course_id, include_expired=False):
|
||||
"""Stubbed out Enrollment data request."""
|
||||
return _get_fake_course_info(course_id)
|
||||
return _get_fake_course_info(course_id, include_expired)
|
||||
|
||||
|
||||
def _get_fake_enrollment(student_id, course_id):
|
||||
@@ -60,10 +62,14 @@ def _get_fake_enrollment(student_id, course_id):
|
||||
return enrollment
|
||||
|
||||
|
||||
def _get_fake_course_info(course_id):
|
||||
def _get_fake_course_info(course_id, include_expired=False):
|
||||
"""Get a course from the courses array."""
|
||||
# if verified mode is expired and include expired is false
|
||||
# then remove the verified mode from the course.
|
||||
for course in _COURSES:
|
||||
if course_id == course['course_id']:
|
||||
if course_id in _VERIFIED_MODE_EXPIRED and not include_expired:
|
||||
course['course_modes'] = [mode for mode in course['course_modes'] if mode['slug'] != 'verified']
|
||||
return course
|
||||
|
||||
|
||||
@@ -97,6 +103,11 @@ def get_enrollment_attributes(user_id, course_id):
|
||||
return _ENROLLMENT_ATTRIBUTES
|
||||
|
||||
|
||||
def set_expired_mode(course_id):
|
||||
"""Set course verified mode as expired."""
|
||||
_VERIFIED_MODE_EXPIRED.append(course_id)
|
||||
|
||||
|
||||
def add_course(course_id, enrollment_start=None, enrollment_end=None, invite_only=False, course_modes=None):
|
||||
"""Append course to the courses array."""
|
||||
course_info = {
|
||||
@@ -122,3 +133,5 @@ def reset():
|
||||
_COURSES = []
|
||||
global _ENROLLMENTS # pylint: disable=global-statement
|
||||
_ENROLLMENTS = []
|
||||
global _VERIFIED_MODE_EXPIRED # pylint: disable=global-statement
|
||||
_VERIFIED_MODE_EXPIRED = []
|
||||
|
||||
@@ -230,3 +230,52 @@ class EnrollmentTest(CacheIsolationTestCase):
|
||||
# The data matches
|
||||
self.assertEqual(len(details['course_modes']), 3)
|
||||
self.assertEqual(details, cached_details)
|
||||
|
||||
def test_update_enrollment_expired_mode_with_error(self):
|
||||
""" Verify that if verified mode is expired and include expire flag is
|
||||
false then enrollment cannot be updated. """
|
||||
self.assert_add_modes_with_enrollment('audit')
|
||||
# On updating enrollment mode to verified it should the raise the error.
|
||||
with self.assertRaises(CourseModeNotFoundError):
|
||||
self.assert_update_enrollment(mode='verified', include_expired=False)
|
||||
|
||||
def test_update_enrollment_with_expired_mode(self):
|
||||
""" Verify that if verified mode is expired then enrollment can be
|
||||
updated if include_expired flag is true."""
|
||||
self.assert_add_modes_with_enrollment('audit')
|
||||
# enrollment in verified mode will work fine with include_expired=True
|
||||
self.assert_update_enrollment(mode='verified', include_expired=True)
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_unenroll_with_expired_mode(self, include_expired):
|
||||
""" Verify that un-enroll will work fine for expired courses whether include_expired
|
||||
is true or false."""
|
||||
self.assert_add_modes_with_enrollment('verified')
|
||||
self.assert_update_enrollment(mode='verified', is_active=False, include_expired=include_expired)
|
||||
|
||||
def assert_add_modes_with_enrollment(self, enrollment_mode):
|
||||
""" Dry method for adding fake course enrollment information to fake
|
||||
data API and enroll the student in the course. """
|
||||
fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit'])
|
||||
result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode=enrollment_mode)
|
||||
get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
|
||||
self.assertEquals(result, get_result)
|
||||
# set the course verify mode as expire.
|
||||
fake_data_api.set_expired_mode(self.COURSE_ID)
|
||||
|
||||
def assert_update_enrollment(self, mode, is_active=True, include_expired=False):
|
||||
""" Dry method for updating enrollment."""
|
||||
|
||||
result = api.update_enrollment(
|
||||
self.USERNAME, self.COURSE_ID, mode=mode, is_active=is_active, include_expired=include_expired
|
||||
)
|
||||
self.assertEquals(mode, result['mode'])
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEquals(result['student'], self.USERNAME)
|
||||
self.assertEquals(result['course']['course_id'], self.COURSE_ID)
|
||||
self.assertEquals(result['mode'], mode)
|
||||
|
||||
if is_active:
|
||||
self.assertTrue(result['is_active'])
|
||||
else:
|
||||
self.assertFalse(result['is_active'])
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
Test the Data Aggregation Layer for Course Enrollments.
|
||||
|
||||
"""
|
||||
import datetime
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
from mock import patch
|
||||
from nose.tools import raises
|
||||
import unittest
|
||||
|
||||
from pytz import UTC
|
||||
from django.conf import settings
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from enrollment import data
|
||||
from enrollment.errors import (
|
||||
UserNotFoundError, CourseEnrollmentClosedError,
|
||||
CourseEnrollmentFullError, CourseEnrollmentExistsError,
|
||||
@@ -17,7 +20,8 @@ from enrollment.errors import (
|
||||
from openedx.core.lib.exceptions import CourseNotFoundError
|
||||
from student.tests.factories import UserFactory, CourseModeFactory
|
||||
from student.models import CourseEnrollment, EnrollmentClosedError, CourseFullError, AlreadyEnrolledError
|
||||
from enrollment import data
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -257,3 +261,34 @@ class EnrollmentDataTest(ModuleStoreTestCase):
|
||||
def test_update_for_non_existent_course(self):
|
||||
enrollment = data.update_course_enrollment(self.user.username, "some/fake/course", is_active=False)
|
||||
self.assertIsNone(enrollment)
|
||||
|
||||
def test_get_course_with_expired_mode_included(self):
|
||||
"""Verify that method returns expired modes if include_expired
|
||||
is true."""
|
||||
modes = ['honor', 'verified', 'audit']
|
||||
self._create_course_modes(modes, course=self.course)
|
||||
self._update_verified_mode_as_expired(self.course.id)
|
||||
self.assert_enrollment_modes(modes, True)
|
||||
|
||||
def test_get_course_without_expired_mode_included(self):
|
||||
"""Verify that method does not returns expired modes if include_expired
|
||||
is false."""
|
||||
self._create_course_modes(['honor', 'verified', 'audit'], course=self.course)
|
||||
self._update_verified_mode_as_expired(self.course.id)
|
||||
self.assert_enrollment_modes(['audit', 'honor'], False)
|
||||
|
||||
def _update_verified_mode_as_expired(self, course_id):
|
||||
"""Dry method to change verified mode expiration."""
|
||||
mode = CourseMode.objects.get(course_id=course_id, mode_slug=CourseMode.VERIFIED)
|
||||
mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=UTC)
|
||||
mode.save()
|
||||
|
||||
def assert_enrollment_modes(self, expected_modes, include_expired):
|
||||
"""Get enrollment data and assert response with expected modes."""
|
||||
result_course = data.get_course_enrollment_info(unicode(self.course.id), include_expired=include_expired)
|
||||
result_slugs = [mode['slug'] for mode in result_course['course_modes']]
|
||||
for course_mode in expected_modes:
|
||||
self.assertIn(course_mode, result_slugs)
|
||||
|
||||
if not include_expired:
|
||||
self.assertNotIn('verified', result_slugs)
|
||||
|
||||
@@ -885,6 +885,37 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
self.assert_enrollment_status(username='fake-user', expected_status=status.HTTP_406_NOT_ACCEPTABLE,
|
||||
as_server=True)
|
||||
|
||||
def test_update_enrollment_with_expired_mode_throws_error(self):
|
||||
"""Verify that if verified mode is expired than it's enrollment cannot be updated. """
|
||||
for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]:
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=mode,
|
||||
mode_display_name=mode,
|
||||
)
|
||||
|
||||
# Create an enrollment
|
||||
self.assert_enrollment_status(as_server=True)
|
||||
|
||||
# Check that the enrollment is the default.
|
||||
self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
|
||||
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
# Change verified mode expiration.
|
||||
mode = CourseMode.objects.get(course_id=self.course.id, mode_slug=CourseMode.VERIFIED)
|
||||
mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=pytz.utc)
|
||||
mode.save()
|
||||
self.assert_enrollment_status(
|
||||
as_server=True,
|
||||
mode=CourseMode.VERIFIED,
|
||||
expected_status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
class EnrollmentEmbargoTest(EnrollmentTestMixin, UrlResetMixin, ModuleStoreTestCase):
|
||||
|
||||
@@ -161,7 +161,10 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
|
||||
self.course = CourseFactory(display_name=u'teꜱᴛ')
|
||||
self.student = UserFactory.create(username='student', email='test@example.com', password='test')
|
||||
|
||||
for mode in (CourseMode.AUDIT, CourseMode.VERIFIED):
|
||||
for mode in (
|
||||
CourseMode.AUDIT, CourseMode.PROFESSIONAL, CourseMode.CREDIT_MODE,
|
||||
CourseMode.NO_ID_PROFESSIONAL_MODE, CourseMode.VERIFIED, CourseMode.HONOR
|
||||
):
|
||||
CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) # pylint: disable=no-member
|
||||
|
||||
self.verification_deadline = VerificationDeadline(
|
||||
@@ -200,7 +203,8 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
|
||||
'verified_upgrade_deadline': None,
|
||||
}, data[0])
|
||||
self.assertEqual(
|
||||
{CourseMode.VERIFIED, CourseMode.AUDIT},
|
||||
{CourseMode.VERIFIED, CourseMode.AUDIT, CourseMode.HONOR,
|
||||
CourseMode.NO_ID_PROFESSIONAL_MODE, CourseMode.PROFESSIONAL},
|
||||
{mode['slug'] for mode in data[0]['course_modes']}
|
||||
)
|
||||
|
||||
@@ -252,11 +256,11 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
|
||||
'reason': ''
|
||||
}, r'User \w+ is not enrolled with mode ' + CourseMode.HONOR),
|
||||
({
|
||||
'course_id': None,
|
||||
'course_id': 'course-v1:TestX+T101+2015',
|
||||
'old_mode': CourseMode.AUDIT,
|
||||
'new_mode': CourseMode.CREDIT_MODE,
|
||||
'reason': ''
|
||||
}, "Specified course mode '{}' unavailable".format(CourseMode.CREDIT_MODE))
|
||||
'reason': 'Enrollment cannot be changed to credit mode'
|
||||
}, '')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_change_enrollment_bad_data(self, data, error_message):
|
||||
@@ -269,3 +273,104 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
|
||||
self.assertIsNotNone(re.match(error_message, response.content))
|
||||
self.assert_enrollment(CourseMode.AUDIT)
|
||||
self.assertIsNone(ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email))
|
||||
|
||||
@ddt.data('honor', 'audit', 'verified', 'professional', 'no-id-professional')
|
||||
def test_update_enrollment_for_all_modes(self, new_mode):
|
||||
""" Verify support can changed the enrollment to all available modes
|
||||
except credit. """
|
||||
self.assert_update_enrollment('username', new_mode)
|
||||
|
||||
@ddt.data('honor', 'audit', 'verified', 'professional', 'no-id-professional')
|
||||
def test_update_enrollment_for_ended_course(self, new_mode):
|
||||
""" Verify support can changed the enrollment of archived course. """
|
||||
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. """
|
||||
self.set_course_end_date_and_expiry()
|
||||
url = reverse(
|
||||
'support:enrollment_list',
|
||||
kwargs={'username_or_email': getattr(self.student, search_string_type)}
|
||||
)
|
||||
response = self.client.get(url)
|
||||
self._assert_generated_modes(response)
|
||||
|
||||
@ddt.data('username', 'email')
|
||||
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 _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) # pylint: disable=no-member
|
||||
modes_data = []
|
||||
for mode in modes:
|
||||
expiry = mode.expiration_datetime.strftime('%Y-%m-%dT%H:%M:%SZ') if mode.expiration_datetime else None
|
||||
modes_data.append({
|
||||
'sku': mode.sku,
|
||||
'expiration_datetime': expiry,
|
||||
'name': mode.name,
|
||||
'currency': mode.currency,
|
||||
'bulk_sku': mode.bulk_sku,
|
||||
'min_price': mode.min_price,
|
||||
'suggested_prices': mode.suggested_prices,
|
||||
'slug': mode.slug,
|
||||
'description': mode.description
|
||||
})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = json.loads(response.content)
|
||||
self.assertEqual(len(data), 1)
|
||||
|
||||
self.assertEqual(
|
||||
modes_data,
|
||||
data[0]['course_modes']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
{CourseMode.VERIFIED, CourseMode.AUDIT, CourseMode.NO_ID_PROFESSIONAL_MODE,
|
||||
CourseMode.PROFESSIONAL, CourseMode.HONOR},
|
||||
{mode['slug'] for mode in data[0]['course_modes']}
|
||||
)
|
||||
|
||||
def assert_update_enrollment(self, search_string_type, new_mode):
|
||||
""" Dry method to update the enrollment and assert response."""
|
||||
self.assertIsNone(ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email))
|
||||
url = reverse(
|
||||
'support:enrollment_list',
|
||||
kwargs={'username_or_email': getattr(self.student, search_string_type)}
|
||||
)
|
||||
response = self.client.post(url, data={
|
||||
'course_id': unicode(self.course.id), # pylint: disable=no-member
|
||||
'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)
|
||||
|
||||
def set_course_end_date_and_expiry(self):
|
||||
""" Set the course-end date and expire its verified mode."""
|
||||
self.course.start = datetime(year=1970, month=1, day=1, tzinfo=UTC)
|
||||
self.course.end = datetime(year=1970, month=1, day=10, tzinfo=UTC)
|
||||
|
||||
# change verified mode expiry.
|
||||
verified_mode = CourseMode.objects.get(
|
||||
course_id=self.course.id, # pylint: disable=no-member
|
||||
mode_slug=CourseMode.VERIFIED
|
||||
)
|
||||
verified_mode.expiration_datetime = datetime(year=1970, month=1, day=9, tzinfo=UTC)
|
||||
verified_mode.save()
|
||||
|
||||
@@ -16,6 +16,7 @@ from course_modes.models import CourseMode
|
||||
from edxmako.shortcuts import render_to_response
|
||||
from enrollment.api import get_enrollments, update_enrollment
|
||||
from enrollment.errors import CourseModeNotFoundError
|
||||
from enrollment.serializers import ModeSerializer
|
||||
from lms.djangoapps.support.decorators import require_support_permission
|
||||
from lms.djangoapps.support.serializers import ManualEnrollmentSerializer
|
||||
from lms.djangoapps.verify_student.models import VerificationDeadline
|
||||
@@ -61,6 +62,8 @@ class EnrollmentSupportListView(GenericAPIView):
|
||||
# Folds the course_details field up into the main JSON object.
|
||||
enrollment.update(**enrollment.pop('course_details'))
|
||||
course_key = CourseKey.from_string(enrollment['course_id'])
|
||||
# get the all courses modes and replace with existing modes.
|
||||
enrollment['course_modes'] = self.get_course_modes(course_key)
|
||||
# Add the price of the course's verified mode.
|
||||
self.include_verified_mode_info(enrollment, course_key)
|
||||
# Add manual enrollment history, if it exists
|
||||
@@ -83,6 +86,8 @@ 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(err.message))
|
||||
except InvalidKeyError:
|
||||
@@ -98,7 +103,7 @@ class EnrollmentSupportListView(GenericAPIView):
|
||||
# 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)
|
||||
update_enrollment(user.username, course_id, mode=new_mode, include_expired=True)
|
||||
manual_enrollment = ManualEnrollmentAudit.create_manual_enrollment_audit(
|
||||
request.user,
|
||||
enrollment.user.email,
|
||||
@@ -150,3 +155,24 @@ class EnrollmentSupportListView(GenericAPIView):
|
||||
if manual_enrollment_audit is None:
|
||||
return {}
|
||||
return ManualEnrollmentSerializer(instance=manual_enrollment_audit).data
|
||||
|
||||
@staticmethod
|
||||
def get_course_modes(course_key):
|
||||
"""
|
||||
Returns a list of all modes including expired modes for a given course id
|
||||
|
||||
Arguments:
|
||||
course_id (CourseKey): Search for course modes for this course.
|
||||
|
||||
Returns:
|
||||
list of `Mode`
|
||||
|
||||
"""
|
||||
course_modes = CourseMode.modes_for_course(
|
||||
course_key,
|
||||
include_expired=True
|
||||
)
|
||||
return [
|
||||
ModeSerializer(mode).data
|
||||
for mode in course_modes
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user