Commit a164e2fa by Bill DeRusha

Merge pull request #10934 from edx/release

Hotfix 2015-12-09a and Hotfix 2015-12-09b
parents 259de407 75bbed72
......@@ -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.
......
"""
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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment