Merge pull request #10934 from edx/release
Hotfix 2015-12-09a and Hotfix 2015-12-09b
This commit is contained in:
@@ -7,6 +7,8 @@ import importlib
|
||||
import logging
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from enrollment import errors
|
||||
|
||||
@@ -133,7 +135,7 @@ def get_enrollment(user_id, course_id):
|
||||
return _data_api().get_course_enrollment(user_id, course_id)
|
||||
|
||||
|
||||
def add_enrollment(user_id, course_id, mode=CourseMode.DEFAULT_MODE_SLUG, is_active=True):
|
||||
def add_enrollment(user_id, course_id, mode=None, is_active=True):
|
||||
"""Enrolls a user in a course.
|
||||
|
||||
Enrolls a user in a course. If the mode is not specified, this will default to `CourseMode.DEFAULT_MODE_SLUG`.
|
||||
@@ -180,6 +182,8 @@ def add_enrollment(user_id, course_id, mode=CourseMode.DEFAULT_MODE_SLUG, is_act
|
||||
}
|
||||
}
|
||||
"""
|
||||
if mode is None:
|
||||
mode = _default_course_mode(course_id)
|
||||
_validate_course_mode(course_id, mode, is_active=is_active)
|
||||
return _data_api().create_course_enrollment(user_id, course_id, mode, is_active)
|
||||
|
||||
@@ -359,16 +363,36 @@ def get_enrollment_attributes(user_id, course_id):
|
||||
return _data_api().get_enrollment_attributes(user_id, course_id)
|
||||
|
||||
|
||||
def _default_course_mode(course_id):
|
||||
"""Return the default enrollment for a course.
|
||||
|
||||
Special case the default enrollment to return if nothing else is found.
|
||||
|
||||
Arguments:
|
||||
course_id (str): The course to check against for available course modes.
|
||||
|
||||
Returns:
|
||||
str
|
||||
"""
|
||||
course_modes = CourseMode.modes_for_course(CourseKey.from_string(course_id))
|
||||
available_modes = [m.slug for m in course_modes]
|
||||
|
||||
if CourseMode.DEFAULT_MODE_SLUG in available_modes:
|
||||
return CourseMode.DEFAULT_MODE_SLUG
|
||||
elif 'audit' in available_modes:
|
||||
return 'audit'
|
||||
elif 'honor' in available_modes:
|
||||
return 'honor'
|
||||
|
||||
return CourseMode.DEFAULT_MODE_SLUG
|
||||
|
||||
|
||||
def _validate_course_mode(course_id, mode, is_active=None):
|
||||
"""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
|
||||
course enrollment information.
|
||||
|
||||
'honor' is special cased. If there are no course modes configured, and the specified mode is
|
||||
'honor', return true, allowing the enrollment to be 'honor' even if the mode is not explicitly
|
||||
set for the course.
|
||||
|
||||
Arguments:
|
||||
course_id (str): The course to check against for available course modes.
|
||||
mode (str): The slug for the course mode specified in the enrollment.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""
|
||||
Tests for student enrollment.
|
||||
"""
|
||||
from mock import patch, Mock
|
||||
|
||||
import ddt
|
||||
from django.core.cache import cache
|
||||
from nose.tools import raises
|
||||
@@ -8,6 +10,8 @@ import unittest
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.conf import settings
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from enrollment import api
|
||||
from enrollment.errors import EnrollmentApiLoadError, EnrollmentNotFoundError, CourseModeNotFoundError
|
||||
from enrollment.tests import fake_data_api
|
||||
@@ -56,6 +60,37 @@ class EnrollmentTest(TestCase):
|
||||
get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
|
||||
self.assertEquals(result, get_result)
|
||||
|
||||
@ddt.data(
|
||||
([CourseMode.DEFAULT_MODE_SLUG, 'verified', 'credit'], CourseMode.DEFAULT_MODE_SLUG),
|
||||
(['audit', 'verified', 'credit'], 'audit'),
|
||||
(['honor', 'verified', 'credit'], 'honor'),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_enroll_no_mode_success(self, course_modes, expected_mode):
|
||||
# Add a fake course enrollment information to the fake data API
|
||||
fake_data_api.add_course(self.COURSE_ID, course_modes=course_modes)
|
||||
with patch('enrollment.api.CourseMode.modes_for_course') as mock_modes_for_course:
|
||||
mock_course_modes = [Mock(slug=mode) for mode in course_modes]
|
||||
mock_modes_for_course.return_value = mock_course_modes
|
||||
# Enroll in the course and verify the URL we get sent to
|
||||
result = api.add_enrollment(self.USERNAME, self.COURSE_ID)
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEquals(result['student'], self.USERNAME)
|
||||
self.assertEquals(result['course']['course_id'], self.COURSE_ID)
|
||||
self.assertEquals(result['mode'], expected_mode)
|
||||
|
||||
@ddt.data(
|
||||
['professional'],
|
||||
['verified'],
|
||||
['verified', 'professional'],
|
||||
)
|
||||
@raises(CourseModeNotFoundError)
|
||||
def test_enroll_no_mode_error(self, course_modes):
|
||||
# Add a fake course enrollment information to the fake data API
|
||||
fake_data_api.add_course(self.COURSE_ID, course_modes=course_modes)
|
||||
# Enroll in the course and verify that we raise CourseModeNotFoundError
|
||||
api.add_enrollment(self.USERNAME, self.COURSE_ID)
|
||||
|
||||
@raises(CourseModeNotFoundError)
|
||||
def test_prof_ed_enroll(self):
|
||||
# Add a fake course enrollment information to the fake data API
|
||||
|
||||
@@ -520,7 +520,7 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
}
|
||||
)
|
||||
|
||||
mode = request.data.get('mode', CourseMode.DEFAULT_MODE_SLUG)
|
||||
mode = request.data.get('mode')
|
||||
|
||||
has_api_key_permissions = self.has_api_key_permissions(request)
|
||||
|
||||
@@ -532,7 +532,7 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
# other users, do not let them deduce the existence of an enrollment.
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
if mode != CourseMode.DEFAULT_MODE_SLUG and not has_api_key_permissions:
|
||||
if mode not in (CourseMode.AUDIT, CourseMode.HONOR, None) and not has_api_key_permissions:
|
||||
return Response(
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
data={
|
||||
|
||||
@@ -47,6 +47,7 @@ from xmodule_django.models import CourseKeyField, NoneToEmptyManager
|
||||
|
||||
from certificates.models import GeneratedCertificate
|
||||
from course_modes.models import CourseMode
|
||||
from enrollment.api import _default_course_mode
|
||||
import lms.lib.comment_client as cc
|
||||
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client, ECOMMERCE_DATE_FORMAT
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
@@ -1090,7 +1091,7 @@ class CourseEnrollment(models.Model):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def enroll(cls, user, course_key, mode=CourseMode.DEFAULT_MODE_SLUG, check_access=False):
|
||||
def enroll(cls, user, course_key, mode=None, check_access=False):
|
||||
"""
|
||||
Enroll a user in a course. This saves immediately.
|
||||
|
||||
@@ -1124,6 +1125,8 @@ class CourseEnrollment(models.Model):
|
||||
|
||||
Also emits relevant events for analytics purposes.
|
||||
"""
|
||||
if mode is None:
|
||||
mode = _default_course_mode(unicode(course_key))
|
||||
# All the server-side checks for whether a user is allowed to enroll.
|
||||
try:
|
||||
course = CourseOverview.get_from_id(course_key)
|
||||
@@ -1165,7 +1168,7 @@ class CourseEnrollment(models.Model):
|
||||
return enrollment
|
||||
|
||||
@classmethod
|
||||
def enroll_by_email(cls, email, course_id, mode=CourseMode.DEFAULT_MODE_SLUG, ignore_errors=True):
|
||||
def enroll_by_email(cls, email, course_id, mode=None, ignore_errors=True):
|
||||
"""
|
||||
Enroll a user in a course given their email. This saves immediately.
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ def enroll_email(course_id, student_email, auto_enroll=False, email_students=Fal
|
||||
if CourseMode.is_white_label(course_id):
|
||||
course_mode = CourseMode.DEFAULT_SHOPPINGCART_MODE_SLUG
|
||||
else:
|
||||
course_mode = CourseMode.DEFAULT_MODE_SLUG
|
||||
course_mode = None
|
||||
|
||||
if previous_state.enrollment:
|
||||
course_mode = previous_state.mode
|
||||
|
||||
Reference in New Issue
Block a user