Merge pull request #8580 from edx/ahsan/ECOM-1719-LMS-Modification-enrollment-API-update
LMS: Modification in enrollment API
This commit is contained in:
@@ -5,8 +5,8 @@ course level, such as available course modes.
|
||||
"""
|
||||
from django.utils import importlib
|
||||
import logging
|
||||
from django.core.cache import cache
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from enrollment import errors
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -181,7 +181,7 @@ def add_enrollment(user_id, course_id, mode='honor', 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):
|
||||
def update_enrollment(user_id, course_id, mode=None, is_active=None, enrollment_attributes=None):
|
||||
"""Updates the course mode for the enrolled user.
|
||||
|
||||
Update a course enrollment for the given user and course.
|
||||
@@ -232,6 +232,10 @@ def update_enrollment(user_id, course_id, mode=None, is_active=None):
|
||||
msg = u"Course Enrollment not found for user {user} in course {course}".format(user=user_id, course=course_id)
|
||||
log.warn(msg)
|
||||
raise errors.EnrollmentNotFoundError(msg)
|
||||
else:
|
||||
if enrollment_attributes is not None:
|
||||
set_enrollment_attributes(user_id, course_id, enrollment_attributes)
|
||||
|
||||
return enrollment
|
||||
|
||||
|
||||
@@ -302,6 +306,53 @@ def get_course_enrollment_details(course_id, include_expired=False):
|
||||
return course_enrollment_details
|
||||
|
||||
|
||||
def set_enrollment_attributes(user_id, course_id, attributes):
|
||||
"""Set enrollment attributes for the enrollment of given user in the
|
||||
course provided.
|
||||
|
||||
Args:
|
||||
course_id (str): The Course to set enrollment attributes for.
|
||||
user_id (str): The User to set enrollment attributes for.
|
||||
attributes (list): Attributes to be set.
|
||||
|
||||
Example:
|
||||
>>>set_enrollment_attributes(
|
||||
"Bob",
|
||||
"course-v1-edX-DemoX-1T2015",
|
||||
[
|
||||
{
|
||||
"namespace": "credit",
|
||||
"name": "provider_id",
|
||||
"value": "hogwarts",
|
||||
},
|
||||
]
|
||||
)
|
||||
"""
|
||||
_data_api().add_or_update_enrollment_attr(user_id, course_id, attributes)
|
||||
|
||||
|
||||
def get_enrollment_attributes(user_id, course_id):
|
||||
"""Retrieve enrollment attributes for given user for provided course.
|
||||
|
||||
Args:
|
||||
user_id: The User to get enrollment attributes for
|
||||
course_id (str): The Course to get enrollment attributes for.
|
||||
|
||||
Example:
|
||||
>>>get_enrollment_attributes("Bob", "course-v1-edX-DemoX-1T2015")
|
||||
[
|
||||
{
|
||||
"namespace": "credit",
|
||||
"name": "provider_id",
|
||||
"value": "hogwarts",
|
||||
},
|
||||
]
|
||||
|
||||
Returns: list
|
||||
"""
|
||||
return _data_api().get_enrollment_attributes(user_id, course_id)
|
||||
|
||||
|
||||
def _validate_course_mode(course_id, mode):
|
||||
"""Checks to see if the specified course mode is valid for the course.
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ from opaque_keys.edx.keys import CourseKey
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from enrollment.errors import (
|
||||
CourseNotFoundError, CourseEnrollmentClosedError, CourseEnrollmentFullError,
|
||||
CourseEnrollmentExistsError, UserNotFoundError,
|
||||
CourseEnrollmentExistsError, UserNotFoundError, InvalidEnrollmentAttribute
|
||||
)
|
||||
from enrollment.serializers import CourseEnrollmentSerializer, CourseField
|
||||
from student.models import (
|
||||
CourseEnrollment, NonExistentCourseError, EnrollmentClosedError,
|
||||
CourseFullError, AlreadyEnrolledError,
|
||||
CourseFullError, AlreadyEnrolledError, CourseEnrollmentAttribute
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -136,12 +136,112 @@ def update_course_enrollment(username, course_id, mode=None, is_active=None):
|
||||
return None
|
||||
|
||||
|
||||
def add_or_update_enrollment_attr(user_id, course_id, attributes):
|
||||
"""Set enrollment attributes for the enrollment of given user in the
|
||||
course provided.
|
||||
|
||||
Args:
|
||||
course_id (str): The Course to set enrollment attributes for.
|
||||
user_id (str): The User to set enrollment attributes for.
|
||||
attributes (list): Attributes to be set.
|
||||
|
||||
Example:
|
||||
>>>add_or_update_enrollment_attr(
|
||||
"Bob",
|
||||
"course-v1-edX-DemoX-1T2015",
|
||||
[
|
||||
{
|
||||
"namespace": "credit",
|
||||
"name": "provider_id",
|
||||
"value": "hogwarts",
|
||||
},
|
||||
]
|
||||
)
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
user = _get_user(user_id)
|
||||
enrollment = CourseEnrollment.get_enrollment(user, course_key)
|
||||
if not _invalid_attribute(attributes) and enrollment is not None:
|
||||
CourseEnrollmentAttribute.add_enrollment_attr(enrollment, attributes)
|
||||
|
||||
|
||||
def get_enrollment_attributes(user_id, course_id):
|
||||
"""Retrieve enrollment attributes for given user for provided course.
|
||||
|
||||
Args:
|
||||
user_id: The User to get enrollment attributes for
|
||||
course_id (str): The Course to get enrollment attributes for.
|
||||
|
||||
Example:
|
||||
>>>get_enrollment_attributes("Bob", "course-v1-edX-DemoX-1T2015")
|
||||
[
|
||||
{
|
||||
"namespace": "credit",
|
||||
"name": "provider_id",
|
||||
"value": "hogwarts",
|
||||
},
|
||||
]
|
||||
|
||||
Returns: list
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
user = _get_user(user_id)
|
||||
enrollment = CourseEnrollment.get_enrollment(user, course_key)
|
||||
return CourseEnrollmentAttribute.get_enrollment_attributes(enrollment)
|
||||
|
||||
|
||||
def _get_user(user_id):
|
||||
"""Retrieve user with provided user_id
|
||||
|
||||
Args:
|
||||
user_id(str): username of the user for which object is to retrieve
|
||||
|
||||
Returns: obj
|
||||
"""
|
||||
try:
|
||||
return User.objects.get(username=user_id)
|
||||
except User.DoesNotExist:
|
||||
msg = u"Not user with username '{username}' found.".format(username=user_id)
|
||||
log.warn(msg)
|
||||
raise UserNotFoundError(msg)
|
||||
|
||||
|
||||
def _update_enrollment(enrollment, is_active=None, mode=None):
|
||||
enrollment.update_enrollment(is_active=is_active, mode=mode)
|
||||
enrollment.save()
|
||||
return CourseEnrollmentSerializer(enrollment).data # pylint: disable=no-member
|
||||
|
||||
|
||||
def _invalid_attribute(attributes):
|
||||
"""Validate enrollment attribute
|
||||
|
||||
Args:
|
||||
attributes(dict): dict of attribute
|
||||
|
||||
Return:
|
||||
list of invalid attributes
|
||||
"""
|
||||
invalid_attributes = []
|
||||
for attribute in attributes:
|
||||
if "namespace" not in attribute:
|
||||
msg = u"'namespace' not in enrollment attribute"
|
||||
log.warn(msg)
|
||||
invalid_attributes.append("namespace")
|
||||
raise InvalidEnrollmentAttribute(msg)
|
||||
if "name" not in attribute:
|
||||
msg = u"'name' not in enrollment attribute"
|
||||
log.warn(msg)
|
||||
invalid_attributes.append("name")
|
||||
raise InvalidEnrollmentAttribute(msg)
|
||||
if "value" not in attribute:
|
||||
msg = u"'value' not in enrollment attribute"
|
||||
log.warn(msg)
|
||||
invalid_attributes.append("value")
|
||||
raise InvalidEnrollmentAttribute(msg)
|
||||
|
||||
return invalid_attributes
|
||||
|
||||
|
||||
def get_course_enrollment_info(course_id, include_expired=False):
|
||||
"""Returns all course enrollment information for the given course.
|
||||
|
||||
|
||||
@@ -50,3 +50,8 @@ class EnrollmentNotFoundError(CourseEnrollmentError):
|
||||
class EnrollmentApiLoadError(CourseEnrollmentError):
|
||||
"""The data API could not be loaded."""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidEnrollmentAttribute(CourseEnrollmentError):
|
||||
"""Enrollment Attributes could not be validated"""
|
||||
pass
|
||||
|
||||
@@ -19,6 +19,8 @@ _ENROLLMENTS = []
|
||||
|
||||
_COURSES = []
|
||||
|
||||
_ENROLLMENT_ATTRIBUTES = []
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get_course_enrollments(student_id):
|
||||
@@ -78,6 +80,23 @@ def add_enrollment(student_id, course_id, is_active=True, mode='honor'):
|
||||
return enrollment
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def add_or_update_enrollment_attr(user_id, course_id, attributes):
|
||||
"""Add or update enrollment attribute array"""
|
||||
for attribute in attributes:
|
||||
_ENROLLMENT_ATTRIBUTES.append({
|
||||
'namespace': attribute['namespace'],
|
||||
'name': attribute['name'],
|
||||
'value': attribute['value']
|
||||
})
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get_enrollment_attributes(user_id, course_id):
|
||||
"""Retrieve enrollment attribute array"""
|
||||
return _ENROLLMENT_ATTRIBUTES
|
||||
|
||||
|
||||
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 = {
|
||||
|
||||
@@ -143,6 +143,29 @@ class EnrollmentTest(TestCase):
|
||||
result = api.update_enrollment(self.USERNAME, self.COURSE_ID, mode='verified')
|
||||
self.assertEquals('verified', result['mode'])
|
||||
|
||||
def test_update_enrollment_attributes(self):
|
||||
# Add fake course enrollment information to the fake data API
|
||||
fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit', 'credit'])
|
||||
# Enroll in the course and verify the URL we get sent to
|
||||
result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode='audit')
|
||||
get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
|
||||
self.assertEquals(result, get_result)
|
||||
|
||||
enrollment_attributes = [
|
||||
{
|
||||
"namespace": "credit",
|
||||
"name": "provider_id",
|
||||
"value": "hogwarts",
|
||||
}
|
||||
]
|
||||
|
||||
result = api.update_enrollment(
|
||||
self.USERNAME, self.COURSE_ID, mode='credit', enrollment_attributes=enrollment_attributes
|
||||
)
|
||||
self.assertEquals('credit', result['mode'])
|
||||
attributes = api.get_enrollment_attributes(self.USERNAME, self.COURSE_ID)
|
||||
self.assertEquals(enrollment_attributes[0], attributes[0])
|
||||
|
||||
def test_get_course_details(self):
|
||||
# Add a fake course enrollment information to the fake data API
|
||||
fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit'])
|
||||
|
||||
@@ -170,6 +170,45 @@ class EnrollmentDataTest(ModuleStoreTestCase):
|
||||
self.assertEqual(self.user.username, result['user'])
|
||||
self.assertEqual(enrollment, result)
|
||||
|
||||
@ddt.data(
|
||||
# Default (no course modes in the database)
|
||||
# Expect that users are automatically enrolled as "honor".
|
||||
([], 'credit'),
|
||||
|
||||
# Audit / Verified / Honor
|
||||
# We should always go to the "choose your course" page.
|
||||
# We should also be enrolled as "honor" by default.
|
||||
(['honor', 'verified', 'audit', 'credit'], 'credit'),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_add_or_update_enrollment_attr(self, course_modes, enrollment_mode):
|
||||
# Create the course modes (if any) required for this test case
|
||||
self._create_course_modes(course_modes)
|
||||
data.create_course_enrollment(self.user.username, unicode(self.course.id), enrollment_mode, True)
|
||||
enrollment_attributes = [
|
||||
{
|
||||
"namespace": "credit",
|
||||
"name": "provider_id",
|
||||
"value": "hogwarts",
|
||||
}
|
||||
]
|
||||
|
||||
data.add_or_update_enrollment_attr(self.user.username, unicode(self.course.id), enrollment_attributes)
|
||||
enrollment_attr = data.get_enrollment_attributes(self.user.username, unicode(self.course.id))
|
||||
self.assertEqual(enrollment_attr[0], enrollment_attributes[0])
|
||||
|
||||
enrollment_attributes = [
|
||||
{
|
||||
"namespace": "credit",
|
||||
"name": "provider_id",
|
||||
"value": "ASU",
|
||||
}
|
||||
]
|
||||
|
||||
data.add_or_update_enrollment_attr(self.user.username, unicode(self.course.id), enrollment_attributes)
|
||||
enrollment_attr = data.get_enrollment_attributes(self.user.username, unicode(self.course.id))
|
||||
self.assertEqual(enrollment_attr[0], enrollment_attributes[0])
|
||||
|
||||
@raises(CourseNotFoundError)
|
||||
def test_non_existent_course(self):
|
||||
data.get_course_enrollment_info("this/is/bananas")
|
||||
|
||||
@@ -46,6 +46,7 @@ class EnrollmentTestMixin(object):
|
||||
as_server=False,
|
||||
mode=CourseMode.HONOR,
|
||||
is_active=None,
|
||||
enrollment_attributes=None,
|
||||
):
|
||||
"""
|
||||
Enroll in the course and verify the response's status code. If the expected status is 200, also validates
|
||||
@@ -62,7 +63,8 @@ class EnrollmentTestMixin(object):
|
||||
'course_details': {
|
||||
'course_id': course_id
|
||||
},
|
||||
'user': username
|
||||
'user': username,
|
||||
'enrollment_attributes': enrollment_attributes
|
||||
}
|
||||
|
||||
if is_active is not None:
|
||||
@@ -547,6 +549,78 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(course_mode, CourseMode.VERIFIED)
|
||||
|
||||
def test_enrollment_with_credit_mode(self):
|
||||
"""With the right API key, update an existing enrollment with credit
|
||||
mode and set enrollment attributes.
|
||||
"""
|
||||
for mode in [CourseMode.HONOR, CourseMode.CREDIT_MODE]:
|
||||
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 honor.
|
||||
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.HONOR)
|
||||
|
||||
# Check that the enrollment upgraded to credit.
|
||||
enrollment_attributes = [{
|
||||
"namespace": "credit",
|
||||
"name": "provider_id",
|
||||
"value": "hogwarts",
|
||||
}]
|
||||
self.assert_enrollment_status(
|
||||
as_server=True,
|
||||
mode=CourseMode.CREDIT_MODE,
|
||||
expected_status=status.HTTP_200_OK,
|
||||
enrollment_attributes=enrollment_attributes
|
||||
)
|
||||
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(course_mode, CourseMode.CREDIT_MODE)
|
||||
|
||||
def test_enrollment_with_invalid_attr(self):
|
||||
"""Check response status is bad request when invalid enrollment
|
||||
attributes are passed
|
||||
"""
|
||||
for mode in [CourseMode.HONOR, CourseMode.CREDIT_MODE]:
|
||||
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 honor.
|
||||
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.HONOR)
|
||||
|
||||
# Check that the enrollment upgraded to credit.
|
||||
enrollment_attributes = [{
|
||||
"namespace": "credit",
|
||||
"name": "invalid",
|
||||
"value": "hogwarts",
|
||||
}]
|
||||
self.assert_enrollment_status(
|
||||
as_server=True,
|
||||
mode=CourseMode.CREDIT_MODE,
|
||||
expected_status=status.HTTP_400_BAD_REQUEST,
|
||||
enrollment_attributes=enrollment_attributes
|
||||
)
|
||||
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(course_mode, CourseMode.HONOR)
|
||||
|
||||
def test_downgrade_enrollment_with_mode(self):
|
||||
"""With the right API key, downgrade an existing enrollment with a new mode. """
|
||||
# Create an honor and verified mode for a course. This allows an update.
|
||||
|
||||
@@ -5,7 +5,6 @@ consist primarily of authentication, request validation, and serialization.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from ipware.ip import get_ip
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.decorators import method_decorator
|
||||
from opaque_keys import InvalidKeyError
|
||||
@@ -33,7 +32,11 @@ from enrollment.errors import (
|
||||
)
|
||||
from student.models import User
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
REQUIRED_ATTRIBUTES = {
|
||||
"credit": ["credit:provider_id"],
|
||||
}
|
||||
|
||||
|
||||
class EnrollmentCrossDomainSessionAuth(SessionAuthenticationAllowInactiveUser, SessionAuthenticationCrossDomainCsrf):
|
||||
@@ -264,9 +267,13 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
|
||||
If honor mode is not supported for the course, the request fails and returns the available modes.
|
||||
|
||||
A server-to-server call can be used by this command to enroll a user in other modes, such as "verified"
|
||||
or "professional". If the mode is not supported for the course, the request will fail and return the
|
||||
available modes.
|
||||
A server-to-server call can be used by this command to enroll a user in other modes, such as "verified",
|
||||
"professional" or "credit". If the mode is not supported for the course, the request will fail and
|
||||
return the available modes.
|
||||
|
||||
You can include other parameters as enrollment attributes for specific course mode as needed. For
|
||||
example, for credit mode, you can include parameters namespace:'credit', name:'provider_id',
|
||||
value:'UniversityX' to specify credit provider attribute.
|
||||
|
||||
**Example Requests**:
|
||||
|
||||
@@ -274,6 +281,12 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
|
||||
POST /api/enrollment/v1/enrollment{"mode": "honor", "course_details":{"course_id": "edX/DemoX/Demo_Course"}}
|
||||
|
||||
POST /api/enrollment/v1/enrollment{
|
||||
"mode": "credit",
|
||||
"course_details":{"course_id": "edX/DemoX/Demo_Course"},
|
||||
"enrollment_attributes":[{"namespace": "credit","name": "provider_id","value": "hogwarts",},]
|
||||
}
|
||||
|
||||
**Post Parameters**
|
||||
|
||||
* user: The username of the currently logged in user. Optional.
|
||||
@@ -292,6 +305,12 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
* email_opt_in: A Boolean indicating whether the user
|
||||
wishes to opt into email from the organization running this course. Optional.
|
||||
|
||||
* enrollment_attributes: A list of dictionary that contains:
|
||||
|
||||
* namespace: Namespace of the attribute
|
||||
* name: Name of the attribute
|
||||
* value: Value of the attribute
|
||||
|
||||
**Response Values**
|
||||
|
||||
A collection of course enrollments for the user, or for the newly created enrollment.
|
||||
@@ -335,7 +354,6 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
|
||||
* user: The username of the user.
|
||||
"""
|
||||
|
||||
authentication_classes = OAuth2AuthenticationAllowInactiveUser, EnrollmentCrossDomainSessionAuth
|
||||
permission_classes = ApiKeyHeaderPermissionIsAuthenticated,
|
||||
throttle_classes = EnrollmentUserThrottle,
|
||||
@@ -370,6 +388,7 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
|
||||
"""
|
||||
# Get the User, Course ID, and Mode from the request.
|
||||
|
||||
username = request.DATA.get('user', request.user.username)
|
||||
course_id = request.DATA.get('course_details', {}).get('course_id')
|
||||
|
||||
@@ -438,9 +457,17 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
}
|
||||
)
|
||||
|
||||
enrollment_attributes = request.DATA.get('enrollment_attributes')
|
||||
enrollment = api.get_enrollment(username, unicode(course_id))
|
||||
mode_changed = enrollment and mode is not None and enrollment['mode'] != mode
|
||||
active_changed = enrollment and is_active is not None and enrollment['is_active'] != is_active
|
||||
missing_attrs = []
|
||||
if enrollment_attributes:
|
||||
actual_attrs = [
|
||||
u"{namespace}:{name}".format(**attr)
|
||||
for attr in enrollment_attributes
|
||||
]
|
||||
missing_attrs = set(REQUIRED_ATTRIBUTES.get(mode, [])) - set(actual_attrs)
|
||||
if has_api_key_permissions and (mode_changed or active_changed):
|
||||
if mode_changed and active_changed and not is_active:
|
||||
# if the requester wanted to deactivate but specified the wrong mode, fail
|
||||
@@ -451,7 +478,21 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
)
|
||||
log.warning(msg)
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})
|
||||
response = api.update_enrollment(username, unicode(course_id), mode=mode, is_active=is_active)
|
||||
|
||||
if len(missing_attrs) > 0:
|
||||
msg = u"Missing enrollment attributes: requested mode={} required attributes={}".format(
|
||||
mode, REQUIRED_ATTRIBUTES.get(mode)
|
||||
)
|
||||
log.warning(msg)
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})
|
||||
|
||||
response = api.update_enrollment(
|
||||
username,
|
||||
unicode(course_id),
|
||||
mode=mode,
|
||||
is_active=is_active,
|
||||
enrollment_attributes=enrollment_attributes
|
||||
)
|
||||
else:
|
||||
# Will reactivate inactive enrollments.
|
||||
response = api.add_enrollment(username, unicode(course_id), mode=mode, is_active=is_active)
|
||||
|
||||
@@ -1854,3 +1854,47 @@ class CourseEnrollmentAttribute(models.Model):
|
||||
name=self.name,
|
||||
value=self.value,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def add_enrollment_attr(cls, enrollment, data_list):
|
||||
"""Delete all the enrollment attributes for the given enrollment and
|
||||
add new attributes.
|
||||
|
||||
Args:
|
||||
enrollment(CourseEnrollment): 'CourseEnrollment' for which attribute is to be added
|
||||
data(list): list of dictionaries containing data to save
|
||||
"""
|
||||
cls.objects.filter(enrollment=enrollment).delete()
|
||||
attributes = [
|
||||
cls(enrollment=enrollment, namespace=data['namespace'], name=data['name'], value=data['value'])
|
||||
for data in data_list
|
||||
]
|
||||
cls.objects.bulk_create(attributes)
|
||||
|
||||
@classmethod
|
||||
def get_enrollment_attributes(cls, enrollment):
|
||||
"""Retrieve list of all enrollment attributes.
|
||||
|
||||
Args:
|
||||
enrollment(CourseEnrollment): 'CourseEnrollment' for which list is to retrieve
|
||||
|
||||
Returns: list
|
||||
|
||||
Example:
|
||||
>>> CourseEnrollmentAttribute.get_enrollment_attributes(CourseEnrollment)
|
||||
[
|
||||
{
|
||||
"namespace": "credit",
|
||||
"name": "provider_id",
|
||||
"value": "hogwarts",
|
||||
},
|
||||
]
|
||||
"""
|
||||
return [
|
||||
{
|
||||
"namespace": attribute.namespace,
|
||||
"name": attribute.name,
|
||||
"value": attribute.value,
|
||||
}
|
||||
for attribute in cls.objects.filter(enrollment=enrollment)
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user