Merge pull request #10802 from edx/feature/remove-honor-code-certs
Remove default honor code.
This commit is contained in:
@@ -362,7 +362,7 @@ def certificates_list_handler(request, course_key_string):
|
||||
certificate_web_view_url = get_lms_link_for_certificate_web_view(
|
||||
user_id=request.user.id,
|
||||
course_key=course_key,
|
||||
mode=course_modes[0] # CourseMode.modes_for_course returns default mode 'honor' if doesn't find anyone.
|
||||
mode=course_modes[0] # CourseMode.modes_for_course returns default mode if doesn't find anyone.
|
||||
)
|
||||
certificates = None
|
||||
is_active = False
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
""" Helper methods for CourseModes. """
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from student.helpers import (
|
||||
VERIFY_STATUS_NEED_TO_VERIFY,
|
||||
VERIFY_STATUS_SUBMITTED,
|
||||
VERIFY_STATUS_APPROVED
|
||||
)
|
||||
|
||||
DISPLAY_VERIFIED = "verified"
|
||||
DISPLAY_HONOR = "honor"
|
||||
DISPLAY_AUDIT = "audit"
|
||||
DISPLAY_PROFESSIONAL = "professional"
|
||||
|
||||
|
||||
def enrollment_mode_display(mode, verification_status, course_id):
|
||||
""" Select appropriate display strings and CSS classes.
|
||||
|
||||
Uses mode and verification status to select appropriate display strings and CSS classes
|
||||
for certificate display.
|
||||
|
||||
Args:
|
||||
mode (str): enrollment mode.
|
||||
verification_status (str) : verification status of student
|
||||
|
||||
Returns:
|
||||
dictionary:
|
||||
"""
|
||||
show_image = False
|
||||
image_alt = ''
|
||||
enrollment_title = ''
|
||||
enrollment_value = ''
|
||||
display_mode = _enrollment_mode_display(mode, verification_status, course_id)
|
||||
|
||||
if display_mode == DISPLAY_VERIFIED:
|
||||
if verification_status in [VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED]:
|
||||
enrollment_title = _("Your verification is pending")
|
||||
enrollment_value = _("Verified: Pending Verification")
|
||||
show_image = True
|
||||
image_alt = _("ID verification pending")
|
||||
elif verification_status == VERIFY_STATUS_APPROVED:
|
||||
enrollment_title = _("You're enrolled as a verified student")
|
||||
enrollment_value = _("Verified")
|
||||
show_image = True
|
||||
image_alt = _("ID Verified Ribbon/Badge")
|
||||
elif display_mode == DISPLAY_HONOR:
|
||||
enrollment_title = _("You're enrolled as an honor code student")
|
||||
enrollment_value = _("Honor Code")
|
||||
elif display_mode == DISPLAY_PROFESSIONAL:
|
||||
enrollment_title = _("You're enrolled as a professional education student")
|
||||
enrollment_value = _("Professional Ed")
|
||||
|
||||
return {
|
||||
'enrollment_title': unicode(enrollment_title),
|
||||
'enrollment_value': unicode(enrollment_value),
|
||||
'show_image': show_image,
|
||||
'image_alt': unicode(image_alt),
|
||||
'display_mode': _enrollment_mode_display(mode, verification_status, course_id)
|
||||
}
|
||||
|
||||
|
||||
def _enrollment_mode_display(enrollment_mode, verification_status, course_id):
|
||||
"""Checking enrollment mode and status and returns the display mode
|
||||
Args:
|
||||
enrollment_mode (str): enrollment mode.
|
||||
verification_status (str) : verification status of student
|
||||
|
||||
Returns:
|
||||
display_mode (str) : display mode for certs
|
||||
"""
|
||||
course_mode_slugs = [mode.slug for mode in CourseMode.modes_for_course(course_id)]
|
||||
|
||||
if enrollment_mode == CourseMode.VERIFIED:
|
||||
if verification_status in [VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED, VERIFY_STATUS_APPROVED]:
|
||||
display_mode = DISPLAY_VERIFIED
|
||||
elif DISPLAY_HONOR in course_mode_slugs:
|
||||
display_mode = DISPLAY_HONOR
|
||||
else:
|
||||
display_mode = DISPLAY_AUDIT
|
||||
elif enrollment_mode in [CourseMode.PROFESSIONAL, CourseMode.NO_ID_PROFESSIONAL_MODE]:
|
||||
display_mode = DISPLAY_PROFESSIONAL
|
||||
else:
|
||||
display_mode = enrollment_mode
|
||||
|
||||
return display_mode
|
||||
|
||||
@@ -99,8 +99,8 @@ class CourseMode(models.Model):
|
||||
NO_ID_PROFESSIONAL_MODE = "no-id-professional"
|
||||
CREDIT_MODE = "credit"
|
||||
|
||||
DEFAULT_MODE = Mode(HONOR, _('Honor Code Certificate'), 0, '', 'usd', None, None, None)
|
||||
DEFAULT_MODE_SLUG = HONOR
|
||||
DEFAULT_MODE = Mode(AUDIT, _('Audit'), 0, '', 'usd', None, None, None)
|
||||
DEFAULT_MODE_SLUG = AUDIT
|
||||
|
||||
# Modes that allow a student to pursue a verified certificate
|
||||
VERIFIED_MODES = [VERIFIED, PROFESSIONAL]
|
||||
@@ -112,7 +112,7 @@ class CourseMode(models.Model):
|
||||
CREDIT_MODES = [CREDIT_MODE]
|
||||
|
||||
# Modes that are allowed to upsell
|
||||
UPSELL_TO_VERIFIED_MODES = [HONOR]
|
||||
UPSELL_TO_VERIFIED_MODES = [HONOR, AUDIT]
|
||||
|
||||
class Meta(object):
|
||||
unique_together = ('course_id', 'mode_slug', 'currency')
|
||||
@@ -511,8 +511,27 @@ class CourseMode(models.Model):
|
||||
if cls.is_white_label(course_id, modes_dict=modes_dict):
|
||||
return False
|
||||
|
||||
# Check that the default mode is available.
|
||||
return cls.HONOR in modes_dict
|
||||
# Check that a free mode is available.
|
||||
return cls.AUDIT in modes_dict or cls.HONOR in modes_dict
|
||||
|
||||
@classmethod
|
||||
def auto_enroll_mode(cls, course_id, modes_dict=None):
|
||||
"""
|
||||
return the auto-enrollable mode from given dict
|
||||
|
||||
Args:
|
||||
modes_dict (dict): course modes.
|
||||
|
||||
Returns:
|
||||
String: Mode name
|
||||
"""
|
||||
if modes_dict is None:
|
||||
modes_dict = cls.modes_for_course_dict(course_id)
|
||||
|
||||
if cls.HONOR in modes_dict:
|
||||
return cls.HONOR
|
||||
elif cls.AUDIT in modes_dict:
|
||||
return cls.AUDIT
|
||||
|
||||
@classmethod
|
||||
def is_white_label(cls, course_id, modes_dict=None):
|
||||
@@ -552,96 +571,6 @@ class CourseMode(models.Model):
|
||||
modes = cls.modes_for_course(course_id)
|
||||
return min(mode.min_price for mode in modes if mode.currency.lower() == currency.lower())
|
||||
|
||||
@classmethod
|
||||
def enrollment_mode_display(cls, mode, verification_status):
|
||||
""" Select appropriate display strings and CSS classes.
|
||||
|
||||
Uses mode and verification status to select appropriate display strings and CSS classes
|
||||
for certificate display.
|
||||
|
||||
Args:
|
||||
mode (str): enrollment mode.
|
||||
verification_status (str) : verification status of student
|
||||
|
||||
Returns:
|
||||
dictionary:
|
||||
"""
|
||||
|
||||
# import inside the function to avoid the circular import
|
||||
from student.helpers import (
|
||||
VERIFY_STATUS_NEED_TO_VERIFY,
|
||||
VERIFY_STATUS_SUBMITTED,
|
||||
VERIFY_STATUS_APPROVED
|
||||
)
|
||||
|
||||
show_image = False
|
||||
image_alt = ''
|
||||
|
||||
if mode == cls.VERIFIED:
|
||||
if verification_status in [VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED]:
|
||||
enrollment_title = _("Your verification is pending")
|
||||
enrollment_value = _("Verified: Pending Verification")
|
||||
show_image = True
|
||||
image_alt = _("ID verification pending")
|
||||
elif verification_status == VERIFY_STATUS_APPROVED:
|
||||
enrollment_title = _("You're enrolled as a verified student")
|
||||
enrollment_value = _("Verified")
|
||||
show_image = True
|
||||
image_alt = _("ID Verified Ribbon/Badge")
|
||||
else:
|
||||
enrollment_title = _("You're enrolled as an honor code student")
|
||||
enrollment_value = _("Honor Code")
|
||||
elif mode == cls.HONOR:
|
||||
enrollment_title = _("You're enrolled as an honor code student")
|
||||
enrollment_value = _("Honor Code")
|
||||
elif mode == cls.AUDIT:
|
||||
enrollment_title = _("You're auditing this course")
|
||||
enrollment_value = _("Auditing")
|
||||
elif mode in [cls.PROFESSIONAL, cls.NO_ID_PROFESSIONAL_MODE]:
|
||||
enrollment_title = _("You're enrolled as a professional education student")
|
||||
enrollment_value = _("Professional Ed")
|
||||
else:
|
||||
enrollment_title = ''
|
||||
enrollment_value = ''
|
||||
|
||||
return {
|
||||
'enrollment_title': unicode(enrollment_title),
|
||||
'enrollment_value': unicode(enrollment_value),
|
||||
'show_image': show_image,
|
||||
'image_alt': unicode(image_alt),
|
||||
'display_mode': cls._enrollment_mode_display(mode, verification_status)
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _enrollment_mode_display(enrollment_mode, verification_status):
|
||||
"""Checking enrollment mode and status and returns the display mode
|
||||
Args:
|
||||
enrollment_mode (str): enrollment mode.
|
||||
verification_status (str) : verification status of student
|
||||
|
||||
Returns:
|
||||
display_mode (str) : display mode for certs
|
||||
"""
|
||||
|
||||
# import inside the function to avoid the circular import
|
||||
from student.helpers import (
|
||||
VERIFY_STATUS_NEED_TO_VERIFY,
|
||||
VERIFY_STATUS_SUBMITTED,
|
||||
VERIFY_STATUS_APPROVED
|
||||
)
|
||||
|
||||
if enrollment_mode == CourseMode.VERIFIED:
|
||||
if verification_status in [VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED, VERIFY_STATUS_APPROVED]:
|
||||
display_mode = "verified"
|
||||
else:
|
||||
display_mode = "honor"
|
||||
elif enrollment_mode in [CourseMode.PROFESSIONAL, CourseMode.NO_ID_PROFESSIONAL_MODE]:
|
||||
display_mode = "professional"
|
||||
else:
|
||||
display_mode = enrollment_mode
|
||||
|
||||
return display_mode
|
||||
|
||||
def to_tuple(self):
|
||||
"""
|
||||
Takes a mode model and turns it into a model named tuple.
|
||||
|
||||
@@ -15,6 +15,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
import pytz
|
||||
|
||||
from course_modes.helpers import enrollment_mode_display
|
||||
from course_modes.models import CourseMode, Mode
|
||||
|
||||
|
||||
@@ -193,6 +194,21 @@ class CourseModeModelTest(TestCase):
|
||||
# Verify that we can or cannot auto enroll
|
||||
self.assertEqual(CourseMode.can_auto_enroll(self.course_key), can_auto_enroll)
|
||||
|
||||
@ddt.data(
|
||||
([], None),
|
||||
(["honor", "audit", "verified"], "honor"),
|
||||
(["honor", "audit"], "honor"),
|
||||
(["audit", "verified"], "audit"),
|
||||
(["professional"], None),
|
||||
(["no-id-professional"], None),
|
||||
(["credit", "audit", "verified"], "audit"),
|
||||
(["credit"], None),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_auto_enroll_mode(self, modes, result):
|
||||
# Verify that the proper auto enroll mode is returned
|
||||
self.assertEqual(CourseMode.auto_enroll_mode(self.course_key, modes), result)
|
||||
|
||||
def test_all_modes_for_courses(self):
|
||||
now = datetime.now(pytz.UTC)
|
||||
future = now + timedelta(days=1)
|
||||
@@ -316,30 +332,30 @@ class CourseModeModelTest(TestCase):
|
||||
def test_enrollment_mode_display(self, mode, verification_status):
|
||||
if mode == "verified":
|
||||
self.assertEqual(
|
||||
CourseMode.enrollment_mode_display(mode, verification_status),
|
||||
enrollment_mode_display(mode, verification_status, self.course_key),
|
||||
self._enrollment_display_modes_dicts(verification_status)
|
||||
)
|
||||
self.assertEqual(
|
||||
CourseMode.enrollment_mode_display(mode, verification_status),
|
||||
enrollment_mode_display(mode, verification_status, self.course_key),
|
||||
self._enrollment_display_modes_dicts(verification_status)
|
||||
)
|
||||
self.assertEqual(
|
||||
CourseMode.enrollment_mode_display(mode, verification_status),
|
||||
enrollment_mode_display(mode, verification_status, self.course_key),
|
||||
self._enrollment_display_modes_dicts(verification_status)
|
||||
)
|
||||
elif mode == "honor":
|
||||
self.assertEqual(
|
||||
CourseMode.enrollment_mode_display(mode, verification_status),
|
||||
enrollment_mode_display(mode, verification_status, self.course_key),
|
||||
self._enrollment_display_modes_dicts(mode)
|
||||
)
|
||||
elif mode == "audit":
|
||||
self.assertEqual(
|
||||
CourseMode.enrollment_mode_display(mode, verification_status),
|
||||
enrollment_mode_display(mode, verification_status, self.course_key),
|
||||
self._enrollment_display_modes_dicts(mode)
|
||||
)
|
||||
elif mode == "professional":
|
||||
self.assertEqual(
|
||||
CourseMode.enrollment_mode_display(mode, verification_status),
|
||||
enrollment_mode_display(mode, verification_status, self.course_key),
|
||||
self._enrollment_display_modes_dicts(mode)
|
||||
)
|
||||
|
||||
@@ -375,9 +391,9 @@ class CourseModeModelTest(TestCase):
|
||||
'ID verification pending', 'verified'],
|
||||
"verify_approved": ["You're enrolled as a verified student", "Verified", True, 'ID Verified Ribbon/Badge',
|
||||
'verified'],
|
||||
"verify_none": ["You're enrolled as an honor code student", "Honor Code", False, '', 'honor'],
|
||||
"verify_none": ["", "", False, '', 'audit'],
|
||||
"honor": ["You're enrolled as an honor code student", "Honor Code", False, '', 'honor'],
|
||||
"audit": ["You're auditing this course", "Auditing", False, '', 'audit'],
|
||||
"audit": ["", "", False, '', 'audit'],
|
||||
"professional": ["You're enrolled as a professional education student", "Professional Ed", False, '',
|
||||
'professional']
|
||||
}
|
||||
|
||||
@@ -177,6 +177,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
|
||||
# Mapping of course modes to the POST parameters sent
|
||||
# when the user chooses that mode.
|
||||
POST_PARAMS_FOR_COURSE_MODE = {
|
||||
'audit': {},
|
||||
'honor': {'honor_mode': True},
|
||||
'verified': {'verified_mode': True, 'contribution': '1.23'},
|
||||
'unsupported': {'unsupported_mode': True},
|
||||
@@ -227,9 +228,9 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
|
||||
expected_amount = decimal.Decimal(self.POST_PARAMS_FOR_COURSE_MODE['verified']['contribution'])
|
||||
self.assertEqual(actual_amount, expected_amount)
|
||||
|
||||
def test_successful_honor_enrollment(self):
|
||||
def test_successful_default_enrollment(self):
|
||||
# Create the course modes
|
||||
for mode in ('honor', 'verified'):
|
||||
for mode in (CourseMode.DEFAULT_MODE_SLUG, 'verified'):
|
||||
CourseModeFactory(mode_slug=mode, course_id=self.course.id)
|
||||
|
||||
# Enroll the user in the default mode (honor) to emulate
|
||||
@@ -242,11 +243,11 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
|
||||
|
||||
# Explicitly select the honor mode (POST request)
|
||||
choose_track_url = reverse('course_modes_choose', args=[unicode(self.course.id)])
|
||||
self.client.post(choose_track_url, self.POST_PARAMS_FOR_COURSE_MODE['honor'])
|
||||
self.client.post(choose_track_url, self.POST_PARAMS_FOR_COURSE_MODE[CourseMode.DEFAULT_MODE_SLUG])
|
||||
|
||||
# Verify that the user's enrollment remains unchanged
|
||||
mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
self.assertEqual(mode, 'honor')
|
||||
self.assertEqual(mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
self.assertEqual(is_active, True)
|
||||
|
||||
def test_unsupported_enrollment_mode_failure(self):
|
||||
|
||||
@@ -174,12 +174,16 @@ class ChooseModeView(View):
|
||||
if requested_mode not in allowed_modes:
|
||||
return HttpResponseBadRequest(_("Enrollment mode not supported"))
|
||||
|
||||
if requested_mode == 'honor':
|
||||
# The user will have already been enrolled in the honor mode at this
|
||||
if requested_mode == 'audit':
|
||||
# The user will have already been enrolled in the audit mode at this
|
||||
# point, so we just redirect them to the dashboard, thereby avoiding
|
||||
# hitting the database a second time attempting to enroll them.
|
||||
return redirect(reverse('dashboard'))
|
||||
|
||||
if requested_mode == 'honor':
|
||||
CourseEnrollment.enroll(user, course_key, mode=requested_mode)
|
||||
return redirect(reverse('dashboard'))
|
||||
|
||||
mode_info = allowed_modes[requested_mode]
|
||||
|
||||
if requested_mode == 'verified':
|
||||
@@ -224,6 +228,8 @@ class ChooseModeView(View):
|
||||
return 'verified'
|
||||
if 'honor_mode' in request_dict:
|
||||
return 'honor'
|
||||
if 'audit_mode' in request_dict:
|
||||
return 'audit'
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import importlib
|
||||
import logging
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from course_modes.models import CourseMode
|
||||
from enrollment import errors
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -132,10 +133,10 @@ 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='honor', is_active=True):
|
||||
def add_enrollment(user_id, course_id, mode=CourseMode.DEFAULT_MODE_SLUG, 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 'honor'.
|
||||
Enrolls a user in a course. If the mode is not specified, this will default to `CourseMode.DEFAULT_MODE_SLUG`.
|
||||
|
||||
Arguments:
|
||||
user_id (str): The user to enroll.
|
||||
@@ -143,7 +144,7 @@ def add_enrollment(user_id, course_id, mode='honor', is_active=True):
|
||||
|
||||
Keyword Arguments:
|
||||
mode (str): Optional argument for the type of enrollment to create. Ex. 'audit', 'honor', 'verified',
|
||||
'professional'. If not specified, this defaults to 'honor'.
|
||||
'professional'. If not specified, this defaults to the default course mode.
|
||||
is_active (boolean): Optional argument for making the new enrollment inactive. If not specified, is_active
|
||||
defaults to True.
|
||||
|
||||
@@ -154,7 +155,7 @@ def add_enrollment(user_id, course_id, mode='honor', is_active=True):
|
||||
>>> add_enrollment("Bob", "edX/DemoX/2014T2", mode="audit")
|
||||
{
|
||||
"created": "2014-10-20T20:18:00Z",
|
||||
"mode": "honor",
|
||||
"mode": "audit",
|
||||
"is_active": True,
|
||||
"user": "Bob",
|
||||
"course": {
|
||||
@@ -165,8 +166,8 @@ def add_enrollment(user_id, course_id, mode='honor', is_active=True):
|
||||
"course_end": "2015-05-06T00:00:00Z",
|
||||
"course_modes": [
|
||||
{
|
||||
"slug": "honor",
|
||||
"name": "Honor Code Certificate",
|
||||
"slug": "audit",
|
||||
"name": "Audit",
|
||||
"min_price": 0,
|
||||
"suggested_prices": "",
|
||||
"currency": "usd",
|
||||
|
||||
@@ -47,7 +47,7 @@ class EnrollmentTestMixin(object):
|
||||
expected_status=status.HTTP_200_OK,
|
||||
email_opt_in=None,
|
||||
as_server=False,
|
||||
mode=CourseMode.HONOR,
|
||||
mode=CourseMode.DEFAULT_MODE_SLUG,
|
||||
is_active=None,
|
||||
enrollment_attributes=None,
|
||||
min_mongo_calls=0,
|
||||
@@ -169,13 +169,13 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
|
||||
@ddt.data(
|
||||
# Default (no course modes in the database)
|
||||
# Expect that users are automatically enrolled as "honor".
|
||||
([], CourseMode.HONOR),
|
||||
# Expect that users are automatically enrolled as the default
|
||||
([], CourseMode.DEFAULT_MODE_SLUG),
|
||||
|
||||
# Audit / Verified / Honor
|
||||
# Audit / Verified
|
||||
# We should always go to the "choose your course" page.
|
||||
# We should also be enrolled as "honor" by default.
|
||||
([CourseMode.HONOR, CourseMode.VERIFIED, CourseMode.AUDIT], CourseMode.HONOR),
|
||||
# We should also be enrolled as the default.
|
||||
([CourseMode.VERIFIED, CourseMode.AUDIT], CourseMode.DEFAULT_MODE_SLUG),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_enroll(self, course_modes, enrollment_mode):
|
||||
@@ -198,8 +198,8 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
def test_check_enrollment(self):
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name=CourseMode.HONOR,
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
|
||||
)
|
||||
# Create an enrollment
|
||||
self.assert_enrollment_status()
|
||||
@@ -209,7 +209,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
self.assertEqual(resp.status_code, status.HTTP_200_OK)
|
||||
data = json.loads(resp.content)
|
||||
self.assertEqual(unicode(self.course.id), data['course_details']['course_id'])
|
||||
self.assertEqual(CourseMode.HONOR, data['mode'])
|
||||
self.assertEqual(CourseMode.DEFAULT_MODE_SLUG, data['mode'])
|
||||
self.assertTrue(data['is_active'])
|
||||
|
||||
@ddt.data(
|
||||
@@ -258,8 +258,8 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
def test_user_not_specified(self):
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name=CourseMode.HONOR,
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
|
||||
)
|
||||
# Create an enrollment
|
||||
self.assert_enrollment_status()
|
||||
@@ -269,7 +269,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
self.assertEqual(resp.status_code, status.HTTP_200_OK)
|
||||
data = json.loads(resp.content)
|
||||
self.assertEqual(unicode(self.course.id), data['course_details']['course_id'])
|
||||
self.assertEqual(CourseMode.HONOR, data['mode'])
|
||||
self.assertEqual(CourseMode.DEFAULT_MODE_SLUG, data['mode'])
|
||||
self.assertTrue(data['is_active'])
|
||||
|
||||
def test_user_not_authenticated(self):
|
||||
@@ -306,8 +306,8 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
# Try to enroll a user that is not the authenticated user.
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name=CourseMode.HONOR,
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
|
||||
)
|
||||
self.assert_enrollment_status(username=self.other_user.username, expected_status=status.HTTP_404_NOT_FOUND)
|
||||
# Verify that the server still has access to this endpoint.
|
||||
@@ -340,8 +340,8 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
for course in self.course, other_course:
|
||||
CourseModeFactory.create(
|
||||
course_id=unicode(course.id),
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name=CourseMode.HONOR,
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
|
||||
)
|
||||
self.assert_enrollment_status(
|
||||
course_id=unicode(course.id),
|
||||
@@ -520,8 +520,8 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
self.rate_limit_config.save()
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name=CourseMode.HONOR,
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
|
||||
)
|
||||
|
||||
for attempt in xrange(self.rate_limit + 10):
|
||||
@@ -534,8 +534,8 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
self.rate_limit_config.save()
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name=CourseMode.HONOR,
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
|
||||
)
|
||||
|
||||
for attempt in xrange(self.rate_limit + 10):
|
||||
@@ -595,7 +595,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
def test_update_enrollment_with_mode(self):
|
||||
"""With the right API key, update an existing enrollment with a new mode. """
|
||||
# Create an honor and verified mode for a course. This allows an update.
|
||||
for mode in [CourseMode.HONOR, CourseMode.VERIFIED]:
|
||||
for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]:
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=mode,
|
||||
@@ -605,11 +605,11 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
# Create an enrollment
|
||||
self.assert_enrollment_status(as_server=True)
|
||||
|
||||
# Check that the enrollment is honor.
|
||||
# Check that the enrollment is 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.HONOR)
|
||||
self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
# Check that the enrollment upgraded to verified.
|
||||
self.assert_enrollment_status(as_server=True, mode=CourseMode.VERIFIED, expected_status=status.HTTP_200_OK)
|
||||
@@ -621,7 +621,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
"""With the right API key, update an existing enrollment with credit
|
||||
mode and set enrollment attributes.
|
||||
"""
|
||||
for mode in [CourseMode.HONOR, CourseMode.CREDIT_MODE]:
|
||||
for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.CREDIT_MODE]:
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=mode,
|
||||
@@ -631,11 +631,11 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
# Create an enrollment
|
||||
self.assert_enrollment_status(as_server=True)
|
||||
|
||||
# Check that the enrollment is honor.
|
||||
# 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.HONOR)
|
||||
self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
# Check that the enrollment upgraded to credit.
|
||||
enrollment_attributes = [{
|
||||
@@ -657,7 +657,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
"""Check response status is bad request when invalid enrollment
|
||||
attributes are passed
|
||||
"""
|
||||
for mode in [CourseMode.HONOR, CourseMode.CREDIT_MODE]:
|
||||
for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.CREDIT_MODE]:
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=mode,
|
||||
@@ -667,11 +667,11 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
# Create an enrollment
|
||||
self.assert_enrollment_status(as_server=True)
|
||||
|
||||
# Check that the enrollment is honor.
|
||||
# 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.HONOR)
|
||||
self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
# Check that the enrollment upgraded to credit.
|
||||
enrollment_attributes = [{
|
||||
@@ -687,12 +687,12 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
)
|
||||
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(course_mode, CourseMode.HONOR)
|
||||
self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
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.
|
||||
for mode in [CourseMode.HONOR, CourseMode.VERIFIED]:
|
||||
for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]:
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=mode,
|
||||
@@ -708,16 +708,20 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(course_mode, CourseMode.VERIFIED)
|
||||
|
||||
# Check that the enrollment downgraded to honor.
|
||||
self.assert_enrollment_status(as_server=True, mode=CourseMode.HONOR, expected_status=status.HTTP_200_OK)
|
||||
# Check that the enrollment was downgraded to the default mode.
|
||||
self.assert_enrollment_status(
|
||||
as_server=True,
|
||||
mode=CourseMode.DEFAULT_MODE_SLUG,
|
||||
expected_status=status.HTTP_200_OK
|
||||
)
|
||||
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(course_mode, CourseMode.HONOR)
|
||||
self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
@ddt.data(
|
||||
((CourseMode.HONOR, ), CourseMode.HONOR),
|
||||
((CourseMode.HONOR, CourseMode.VERIFIED), CourseMode.HONOR),
|
||||
((CourseMode.HONOR, CourseMode.VERIFIED), CourseMode.VERIFIED),
|
||||
((CourseMode.DEFAULT_MODE_SLUG, ), CourseMode.DEFAULT_MODE_SLUG),
|
||||
((CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED), CourseMode.DEFAULT_MODE_SLUG),
|
||||
((CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED), CourseMode.VERIFIED),
|
||||
((CourseMode.PROFESSIONAL, ), CourseMode.PROFESSIONAL),
|
||||
((CourseMode.NO_ID_PROFESSIONAL_MODE, ), CourseMode.NO_ID_PROFESSIONAL_MODE),
|
||||
((CourseMode.VERIFIED, CourseMode.CREDIT_MODE), CourseMode.VERIFIED),
|
||||
@@ -758,8 +762,12 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
self.assert_enrollment_activation(False, selected_mode)
|
||||
|
||||
# Verify that omitting the mode returns 400 for course configurations
|
||||
# in which the default (honor) mode doesn't exist.
|
||||
expected_status = status.HTTP_200_OK if CourseMode.HONOR in configured_modes else status.HTTP_400_BAD_REQUEST
|
||||
# in which the default mode doesn't exist.
|
||||
expected_status = (
|
||||
status.HTTP_200_OK
|
||||
if CourseMode.DEFAULT_MODE_SLUG in configured_modes
|
||||
else status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
self.assert_enrollment_status(
|
||||
as_server=True,
|
||||
is_active=False,
|
||||
@@ -788,8 +796,8 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
|
||||
def test_change_mode_from_user(self):
|
||||
"""Users should not be able to alter the enrollment mode on an enrollment. """
|
||||
# Create an honor and verified mode for a course. This allows an update.
|
||||
for mode in [CourseMode.HONOR, CourseMode.VERIFIED]:
|
||||
# Create a default and a verified mode for a course. This allows an update.
|
||||
for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]:
|
||||
CourseModeFactory.create(
|
||||
course_id=self.course.id,
|
||||
mode_slug=mode,
|
||||
@@ -803,13 +811,13 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
|
||||
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)
|
||||
self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
# Get a 403 response when trying to upgrade yourself.
|
||||
self.assert_enrollment_status(mode=CourseMode.VERIFIED, expected_status=status.HTTP_403_FORBIDDEN)
|
||||
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(course_mode, CourseMode.HONOR)
|
||||
self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
@ddt.data(*itertools.product(
|
||||
(CourseMode.HONOR, CourseMode.VERIFIED),
|
||||
|
||||
@@ -287,9 +287,10 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
|
||||
* Enroll the currently signed in user in a course.
|
||||
|
||||
Currently a user can use this command only to enroll the user in
|
||||
honor mode. If honor mode is not supported for the course, the
|
||||
request fails and returns the available modes.
|
||||
Currently a user can use this command only to enroll the
|
||||
user in the default course mode. If this is not
|
||||
supported for the course, the request fails and returns
|
||||
the available modes.
|
||||
|
||||
This command can use a server-to-server call to enroll a user in
|
||||
other modes, such as "verified", "professional", or "credit". If
|
||||
@@ -325,7 +326,7 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
You cannot use the command to enroll a different user.
|
||||
|
||||
* mode: Optional. The course mode for the enrollment. Individual
|
||||
users cannot upgrade their enrollment mode from 'honor'. Only
|
||||
users cannot upgrade their enrollment mode from the default. Only
|
||||
server-to-server requests can enroll with other modes.
|
||||
|
||||
* is_active: Optional. A Boolean value indicating whether the
|
||||
@@ -353,7 +354,7 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
deactivate an enrollment.
|
||||
|
||||
* mode: Optional. The course mode for the enrollment. Individual
|
||||
users cannot upgrade their enrollment mode from "honor". Only
|
||||
users cannot upgrade their enrollment mode from the default. Only
|
||||
server-to-server requests can enroll with other modes.
|
||||
|
||||
* user: Optional. The user ID of the currently logged in user. You
|
||||
@@ -519,7 +520,7 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
|
||||
}
|
||||
)
|
||||
|
||||
mode = request.data.get('mode', CourseMode.HONOR)
|
||||
mode = request.data.get('mode', CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
has_api_key_permissions = self.has_api_key_permissions(request)
|
||||
|
||||
@@ -531,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.HONOR and not has_api_key_permissions:
|
||||
if mode != CourseMode.DEFAULT_MODE_SLUG and not has_api_key_permissions:
|
||||
return Response(
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
data={
|
||||
|
||||
@@ -895,7 +895,7 @@ class CourseEnrollment(models.Model):
|
||||
|
||||
# Represents the modes that are possible. We'll update this later with a
|
||||
# list of possible values.
|
||||
mode = models.CharField(default="honor", max_length=100)
|
||||
mode = models.CharField(default=CourseMode.DEFAULT_MODE_SLUG, max_length=100)
|
||||
|
||||
objects = CourseEnrollmentManager()
|
||||
|
||||
@@ -957,7 +957,7 @@ class CourseEnrollment(models.Model):
|
||||
|
||||
# If we *did* just create a new enrollment, set some defaults
|
||||
if created:
|
||||
enrollment.mode = "honor"
|
||||
enrollment.mode = CourseMode.DEFAULT_MODE_SLUG
|
||||
enrollment.is_active = False
|
||||
enrollment.save()
|
||||
|
||||
@@ -1043,8 +1043,8 @@ class CourseEnrollment(models.Model):
|
||||
u"mode:{}".format(self.mode)]
|
||||
)
|
||||
if mode_changed:
|
||||
# the user's default mode is "honor" and disabled for a course
|
||||
# mode change events will only be emitted when the user's mode changes from this
|
||||
# Only emit mode change events when the user's enrollment
|
||||
# mode has changed from its previous setting
|
||||
self.emit_event(EVENT_NAME_ENROLLMENT_MODE_CHANGED)
|
||||
|
||||
def emit_event(self, event_name):
|
||||
@@ -1090,7 +1090,7 @@ class CourseEnrollment(models.Model):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def enroll(cls, user, course_key, mode="honor", check_access=False):
|
||||
def enroll(cls, user, course_key, mode=CourseMode.DEFAULT_MODE_SLUG, check_access=False):
|
||||
"""
|
||||
Enroll a user in a course. This saves immediately.
|
||||
|
||||
@@ -1103,8 +1103,8 @@ class CourseEnrollment(models.Model):
|
||||
`course_key` is our usual course_id string (e.g. "edX/Test101/2013_Fall)
|
||||
|
||||
`mode` is a string specifying what kind of enrollment this is. The
|
||||
default is 'honor', meaning honor certificate. Other options
|
||||
include 'professional', 'verified', 'audit',
|
||||
default is the default course mode, 'audit'. Other options
|
||||
include 'professional', 'verified', 'honor',
|
||||
'no-id-professional' and 'credit'.
|
||||
See CourseMode in common/djangoapps/course_modes/models.py.
|
||||
|
||||
@@ -1165,7 +1165,7 @@ class CourseEnrollment(models.Model):
|
||||
return enrollment
|
||||
|
||||
@classmethod
|
||||
def enroll_by_email(cls, email, course_id, mode="honor", ignore_errors=True):
|
||||
def enroll_by_email(cls, email, course_id, mode=CourseMode.DEFAULT_MODE_SLUG, ignore_errors=True):
|
||||
"""
|
||||
Enroll a user in a course given their email. This saves immediately.
|
||||
|
||||
@@ -1181,9 +1181,10 @@ class CourseEnrollment(models.Model):
|
||||
`course_id` is our usual course_id string (e.g. "edX/Test101/2013_Fall)
|
||||
|
||||
`mode` is a string specifying what kind of enrollment this is. The
|
||||
default is "honor", meaning honor certificate. Future options
|
||||
may include "audit", "verified_id", etc. Please don't use it
|
||||
until we have these mapped out.
|
||||
default is the default course mode, 'audit'. Other options
|
||||
include 'professional', 'verified', 'honor',
|
||||
'no-id-professional' and 'credit'.
|
||||
See CourseMode in common/djangoapps/course_modes/models.py.
|
||||
|
||||
`ignore_errors` is a boolean indicating whether we should suppress
|
||||
`User.DoesNotExist` errors (returning None) or let it
|
||||
|
||||
@@ -52,8 +52,8 @@ class CourseModeFactory(DjangoModelFactory):
|
||||
model = CourseMode
|
||||
|
||||
course_id = None
|
||||
mode_display_name = u'Honor Code',
|
||||
mode_slug = 'honor'
|
||||
mode_display_name = CourseMode.DEFAULT_MODE.name
|
||||
mode_slug = CourseMode.DEFAULT_MODE_SLUG
|
||||
min_price = 0
|
||||
suggested_prices = ''
|
||||
currency = 'usd'
|
||||
|
||||
@@ -7,6 +7,7 @@ from mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from course_modes.models import CourseMode
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from util.testing import UrlResetMixin
|
||||
@@ -41,13 +42,20 @@ class EnrollmentTest(UrlResetMixin, ModuleStoreTestCase):
|
||||
@ddt.data(
|
||||
# Default (no course modes in the database)
|
||||
# Expect that we're redirected to the dashboard
|
||||
# and automatically enrolled as "honor"
|
||||
([], '', 'honor'),
|
||||
# and automatically enrolled
|
||||
([], '', CourseMode.DEFAULT_MODE_SLUG),
|
||||
|
||||
# Audit / Verified
|
||||
# We should always go to the "choose your course" page.
|
||||
# We should also be enrolled as the default mode.
|
||||
(['verified', 'audit'], 'course_modes_choose', CourseMode.DEFAULT_MODE_SLUG),
|
||||
|
||||
# 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'], 'course_modes_choose', 'honor'),
|
||||
# We should also be enrolled as the honor mode.
|
||||
# Since honor and audit are currently offered together this precedence must
|
||||
# be maintained.
|
||||
(['honor', 'verified', 'audit'], 'course_modes_choose', CourseMode.HONOR),
|
||||
|
||||
# Professional ed
|
||||
# Expect that we're sent to the "choose your track" page
|
||||
|
||||
@@ -127,22 +127,21 @@ class TestRecentEnrollments(ModuleStoreTestCase):
|
||||
self.assertContains(response, "Thank you for enrolling in")
|
||||
|
||||
@ddt.data(
|
||||
#Register as an honor in any course modes with no payment option
|
||||
# Register as honor in any course modes with no payment option
|
||||
([('audit', 0), ('honor', 0)], 'honor', True),
|
||||
([('honor', 0)], 'honor', True),
|
||||
([], 'honor', True),
|
||||
#Register as an honor in any course modes which has payment option
|
||||
# Register as honor in any course modes which has payment option
|
||||
([('honor', 10)], 'honor', False), # This is a paid course
|
||||
([('audit', 0), ('honor', 0), ('professional', 20)], 'honor', True),
|
||||
([('audit', 0), ('honor', 0), ('verified', 20)], 'honor', True),
|
||||
([('audit', 0), ('honor', 0), ('verified', 20), ('professional', 20)], 'honor', True),
|
||||
([], 'honor', True),
|
||||
#Register as an audit in any course modes with no payment option
|
||||
# Register as audit in any course modes with no payment option
|
||||
([('audit', 0), ('honor', 0)], 'audit', True),
|
||||
([('audit', 0)], 'audit', True),
|
||||
#Register as an audit in any course modes which has no payment option
|
||||
([], 'audit', True),
|
||||
# Register as audit in any course modes which has no payment option
|
||||
([('audit', 0), ('honor', 0), ('verified', 10)], 'audit', True),
|
||||
#Register as a verified in any course modes which has payment option
|
||||
# Register as verified in any course modes which has payment option
|
||||
([('professional', 20)], 'professional', False),
|
||||
([('verified', 20)], 'verified', False),
|
||||
([('professional', 20), ('verified', 20)], 'verified', False),
|
||||
|
||||
@@ -44,7 +44,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
|
||||
self.dashboard_url = reverse('dashboard')
|
||||
|
||||
def test_enrolled_as_non_verified(self):
|
||||
self._setup_mode_and_enrollment(None, "honor")
|
||||
self._setup_mode_and_enrollment(None, "audit")
|
||||
|
||||
# Expect that the course appears on the dashboard
|
||||
# without any verification messaging
|
||||
@@ -290,12 +290,9 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
|
||||
VerificationDeadline.set_deadline(self.course.id, deadline)
|
||||
|
||||
BANNER_ALT_MESSAGES = {
|
||||
None: "Honor",
|
||||
VERIFY_STATUS_NEED_TO_VERIFY: "ID verification pending",
|
||||
VERIFY_STATUS_SUBMITTED: "ID verification pending",
|
||||
VERIFY_STATUS_APPROVED: "ID Verified Ribbon/Badge",
|
||||
VERIFY_STATUS_MISSED_DEADLINE: "Honor",
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY: "Honor"
|
||||
}
|
||||
|
||||
NOTIFICATION_MESSAGES = {
|
||||
@@ -309,12 +306,12 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
|
||||
}
|
||||
|
||||
MODE_CLASSES = {
|
||||
None: "honor",
|
||||
None: "audit",
|
||||
VERIFY_STATUS_NEED_TO_VERIFY: "verified",
|
||||
VERIFY_STATUS_SUBMITTED: "verified",
|
||||
VERIFY_STATUS_APPROVED: "verified",
|
||||
VERIFY_STATUS_MISSED_DEADLINE: "honor",
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY: "honor"
|
||||
VERIFY_STATUS_MISSED_DEADLINE: "audit",
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY: "audit"
|
||||
}
|
||||
|
||||
def _assert_course_verification_status(self, status):
|
||||
@@ -334,7 +331,9 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
|
||||
self.assertContains(response, unicode(self.course.id))
|
||||
|
||||
# Verify that the correct banner is rendered on the dashboard
|
||||
self.assertContains(response, self.BANNER_ALT_MESSAGES[status])
|
||||
alt_text = self.BANNER_ALT_MESSAGES.get(status)
|
||||
if alt_text:
|
||||
self.assertContains(response, alt_text)
|
||||
|
||||
# Verify that the correct banner color is rendered
|
||||
self.assertContains(
|
||||
|
||||
@@ -11,6 +11,7 @@ from urlparse import urljoin
|
||||
import pytz
|
||||
from mock import Mock, patch
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from pyquery import PyQuery as pq
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User, AnonymousUser
|
||||
@@ -18,6 +19,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.client import Client
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from student.models import (
|
||||
anonymous_id_for_user, user_by_anonymous_id, CourseEnrollment,
|
||||
unique_id_for_user, LinkedInAddToProfileConfiguration
|
||||
@@ -248,7 +250,7 @@ class DashboardTest(ModuleStoreTestCase):
|
||||
self.client.login(username="jack", password="test")
|
||||
self._check_verification_status_on('verified', 'You\'re enrolled as a verified student')
|
||||
self._check_verification_status_on('honor', 'You\'re enrolled as an honor code student')
|
||||
self._check_verification_status_on('audit', 'You\'re auditing this course')
|
||||
self._check_verification_status_off('audit', '')
|
||||
self._check_verification_status_on('professional', 'You\'re enrolled as a professional education student')
|
||||
self._check_verification_status_on('no-id-professional', 'You\'re enrolled as a professional education student')
|
||||
|
||||
@@ -268,8 +270,13 @@ class DashboardTest(ModuleStoreTestCase):
|
||||
attempt.approve()
|
||||
|
||||
response = self.client.get(reverse('dashboard'))
|
||||
self.assertNotContains(response, "class=\"course {0}\"".format(mode))
|
||||
self.assertNotContains(response, value)
|
||||
|
||||
if mode == 'audit':
|
||||
# Audit mode does not have a banner. Assert no banner element.
|
||||
self.assertEqual(pq(response.content)(".sts-enrollment").length, 0)
|
||||
else:
|
||||
self.assertNotContains(response, "class=\"course {0}\"".format(mode))
|
||||
self.assertNotContains(response, value)
|
||||
|
||||
@patch.dict("django.conf.settings.FEATURES", {'ENABLE_VERIFIED_CERTIFICATES': False})
|
||||
def test_verification_status_invisible(self):
|
||||
@@ -280,7 +287,7 @@ class DashboardTest(ModuleStoreTestCase):
|
||||
self.client.login(username="jack", password="test")
|
||||
self._check_verification_status_off('verified', 'You\'re enrolled as a verified student')
|
||||
self._check_verification_status_off('honor', 'You\'re enrolled as an honor code student')
|
||||
self._check_verification_status_off('audit', 'You\'re auditing this course')
|
||||
self._check_verification_status_off('audit', '')
|
||||
|
||||
def test_course_mode_info(self):
|
||||
verified_mode = CourseModeFactory.create(
|
||||
@@ -321,7 +328,12 @@ class DashboardTest(ModuleStoreTestCase):
|
||||
course_id=self.course.id
|
||||
)
|
||||
course_reg_code = shoppingcart.models.CourseRegistrationCode(
|
||||
code="abcde", course_id=self.course.id, created_by=self.user, invoice=sale_invoice_1, invoice_item=invoice_item, mode_slug='honor'
|
||||
code="abcde",
|
||||
course_id=self.course.id,
|
||||
created_by=self.user,
|
||||
invoice=sale_invoice_1,
|
||||
invoice_item=invoice_item,
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG
|
||||
)
|
||||
course_reg_code.save()
|
||||
|
||||
@@ -339,7 +351,6 @@ class DashboardTest(ModuleStoreTestCase):
|
||||
#now activate the user by enrolling him/her to the course
|
||||
response = self.client.post(redeem_url)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
response = self.client.get(reverse('dashboard'))
|
||||
self.assertIn('You can no longer access this course because payment has not yet been received', response.content)
|
||||
optout_object = Optout.objects.filter(user=self.user, course_id=self.course.id)
|
||||
@@ -566,7 +577,7 @@ class EnrollmentEventTestMixin(EventTestMixin):
|
||||
{
|
||||
'course_id': course_key.to_deprecated_string(),
|
||||
'user_id': user.pk,
|
||||
'mode': 'honor'
|
||||
'mode': CourseMode.DEFAULT_MODE_SLUG
|
||||
}
|
||||
)
|
||||
self.mock_tracker.reset_mock()
|
||||
@@ -578,7 +589,7 @@ class EnrollmentEventTestMixin(EventTestMixin):
|
||||
{
|
||||
'course_id': course_key.to_deprecated_string(),
|
||||
'user_id': user.pk,
|
||||
'mode': 'honor'
|
||||
'mode': CourseMode.DEFAULT_MODE_SLUG
|
||||
}
|
||||
)
|
||||
self.mock_tracker.reset_mock()
|
||||
@@ -754,19 +765,19 @@ class EnrollInCourseTest(EnrollmentEventTestMixin, TestCase):
|
||||
user = User.objects.create(username="justin", email="jh@fake.edx.org")
|
||||
course_id = SlashSeparatedCourseKey("edX", "Test101", "2013")
|
||||
|
||||
CourseEnrollment.enroll(user, course_id)
|
||||
CourseEnrollment.enroll(user, course_id, "audit")
|
||||
self.assert_enrollment_event_was_emitted(user, course_id)
|
||||
|
||||
CourseEnrollment.enroll(user, course_id, "audit")
|
||||
self.assert_enrollment_mode_change_event_was_emitted(user, course_id, "audit")
|
||||
|
||||
# same enrollment mode does not emit an event
|
||||
CourseEnrollment.enroll(user, course_id, "audit")
|
||||
self.assert_no_events_were_emitted()
|
||||
|
||||
CourseEnrollment.enroll(user, course_id, "honor")
|
||||
self.assert_enrollment_mode_change_event_was_emitted(user, course_id, "honor")
|
||||
|
||||
# same enrollment mode does not emit an event
|
||||
CourseEnrollment.enroll(user, course_id, "honor")
|
||||
self.assert_no_events_were_emitted()
|
||||
|
||||
CourseEnrollment.enroll(user, course_id, "audit")
|
||||
self.assert_enrollment_mode_change_event_was_emitted(user, course_id, "audit")
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
class ChangeEnrollmentViewTest(ModuleStoreTestCase):
|
||||
@@ -789,7 +800,7 @@ class ChangeEnrollmentViewTest(ModuleStoreTestCase):
|
||||
)
|
||||
return response
|
||||
|
||||
def test_enroll_as_honor(self):
|
||||
def test_enroll_as_default(self):
|
||||
"""Tests that a student can successfully enroll through this view"""
|
||||
response = self._enroll_through_view(self.course)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -797,7 +808,7 @@ class ChangeEnrollmentViewTest(ModuleStoreTestCase):
|
||||
self.user, self.course.id
|
||||
)
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(enrollment_mode, u'honor')
|
||||
self.assertEqual(enrollment_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
def test_cannot_enroll_if_already_enrolled(self):
|
||||
"""
|
||||
@@ -810,14 +821,14 @@ class ChangeEnrollmentViewTest(ModuleStoreTestCase):
|
||||
response = self._enroll_through_view(self.course)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_change_to_honor_if_verified(self):
|
||||
def test_change_to_default_if_verified(self):
|
||||
"""
|
||||
Tests that a student that is a currently enrolled verified student cannot
|
||||
accidentally change their enrollment to verified
|
||||
accidentally change their enrollment mode
|
||||
"""
|
||||
CourseEnrollment.enroll(self.user, self.course.id, mode=u'verified')
|
||||
self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
|
||||
# now try to enroll the student in the honor mode:
|
||||
# now try to enroll the student in the default mode:
|
||||
response = self._enroll_through_view(self.course)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(
|
||||
@@ -826,7 +837,7 @@ class ChangeEnrollmentViewTest(ModuleStoreTestCase):
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(enrollment_mode, u'verified')
|
||||
|
||||
def test_change_to_honor_if_verified_not_active(self):
|
||||
def test_change_to_default_if_verified_not_active(self):
|
||||
"""
|
||||
Tests that one can renroll for a course if one has already unenrolled
|
||||
"""
|
||||
@@ -847,7 +858,7 @@ class ChangeEnrollmentViewTest(ModuleStoreTestCase):
|
||||
self.user, self.course.id
|
||||
)
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(enrollment_mode, u'honor')
|
||||
self.assertEqual(enrollment_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
|
||||
class AnonymousLookupTable(ModuleStoreTestCase):
|
||||
|
||||
@@ -1008,14 +1008,16 @@ def change_enrollment(request, check_access=True):
|
||||
# Check that auto enrollment is allowed for this course
|
||||
# (= the course is NOT behind a paywall)
|
||||
if CourseMode.can_auto_enroll(course_id):
|
||||
# Enroll the user using the default mode (honor)
|
||||
# Enroll the user using the default mode (audit)
|
||||
# We're assuming that users of the course enrollment table
|
||||
# will NOT try to look up the course enrollment model
|
||||
# by its slug. If they do, it's possible (based on the state of the database)
|
||||
# for no such model to exist, even though we've set the enrollment type
|
||||
# to "honor".
|
||||
# to "audit".
|
||||
try:
|
||||
CourseEnrollment.enroll(user, course_id, check_access=check_access)
|
||||
enroll_mode = CourseMode.auto_enroll_mode(course_id, available_modes)
|
||||
if enroll_mode:
|
||||
CourseEnrollment.enroll(user, course_id, check_access=check_access, mode=enroll_mode)
|
||||
except Exception:
|
||||
return HttpResponseBadRequest(_("Could not enroll"))
|
||||
|
||||
|
||||
@@ -143,15 +143,35 @@ from django.core.urlresolvers import reverse
|
||||
<div class="register-choice register-choice-audit">
|
||||
<div class="wrapper-copy">
|
||||
<span class="deco-ribbon"></span>
|
||||
<h4 class="title">${_("Audit This Course")}</h4>
|
||||
<h4 class="title">${_("Earn an Honor Certificate")}</h4>
|
||||
<div class="copy">
|
||||
<p>${_("Audit this course for free and have complete access to all the course material, activities, tests, and forums. If your work is satisfactory and you abide by the Honor Code, you'll receive a personalized Honor Code Certificate to showcase your achievement.")}</p>
|
||||
<p>${_("Take this course for free and have complete access to all the course material, activities, tests, and forums. Please note that learners who earn a passing grade will earn a certificate in this course.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="list-actions">
|
||||
<li class="action action-select">
|
||||
<input type="submit" name="honor_mode" value="${_('Audit This Course')}" />
|
||||
<input type="submit" name="honor_mode" value="${_('Pursue an Honor Certificate')}" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
% elif "audit" in modes:
|
||||
<span class="deco-divider">
|
||||
<span class="copy">${_("or")}</span>
|
||||
</span>
|
||||
|
||||
<div class="register-choice register-choice-audit">
|
||||
<div class="wrapper-copy">
|
||||
<span class="deco-ribbon"></span>
|
||||
<h4 class="title">${_("Audit This Course")}</h4>
|
||||
<div class="copy">
|
||||
<p>${_("Audit this course for free and have complete access to all the course material, activities, tests, and forums. Please note that this track does not offer a certificate for learners who earn a passing grade.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="list-actions">
|
||||
<li class="action action-select">
|
||||
<input type="submit" name="audit_mode" value="${_('Audit This Course')}" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from config_models.models import cache
|
||||
from util.testing import EventTestMixin
|
||||
@@ -333,6 +334,7 @@ class GenerateExampleCertificatesTest(TestCase):
|
||||
|
||||
def test_generate_example_certs(self):
|
||||
# Generate certificates for the course
|
||||
CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug=CourseMode.HONOR)
|
||||
with self._mock_xqueue() as mock_queue:
|
||||
certs_api.generate_example_certificates(self.COURSE_KEY)
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
|
||||
with mock_create_basket(response=return_value):
|
||||
self._test_successful_ecommerce_api_call(False)
|
||||
|
||||
def _test_course_without_sku(self):
|
||||
def _test_course_without_sku(self, enrollment_mode=CourseMode.DEFAULT_MODE_SLUG):
|
||||
"""
|
||||
Validates the view bypasses the E-Commerce API when the course has no CourseModes with SKUs.
|
||||
"""
|
||||
@@ -207,13 +207,16 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
|
||||
|
||||
# Validate the response content
|
||||
self.assertEqual(response.status_code, 200)
|
||||
msg = Messages.NO_SKU_ENROLLED.format(enrollment_mode='honor', course_id=self.course.id,
|
||||
username=self.user.username)
|
||||
msg = Messages.NO_SKU_ENROLLED.format(
|
||||
enrollment_mode=enrollment_mode,
|
||||
course_id=self.course.id,
|
||||
username=self.user.username
|
||||
)
|
||||
self.assertResponseMessage(response, msg)
|
||||
|
||||
def test_course_without_sku(self):
|
||||
def test_course_without_sku_default(self):
|
||||
"""
|
||||
If the course does NOT have a SKU, the user should be enrolled in the course (under the honor mode) and
|
||||
If the course does NOT have a SKU, the user should be enrolled in the course (under the default mode) and
|
||||
redirected to the user dashboard.
|
||||
"""
|
||||
# Remove SKU from all course modes
|
||||
@@ -223,6 +226,24 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
|
||||
|
||||
self._test_course_without_sku()
|
||||
|
||||
def test_course_without_sku_honor(self):
|
||||
"""
|
||||
If the course does not have an SKU and has an honor mode, the user
|
||||
should be enrolled as honor. This ensures backwards
|
||||
compatibility with courses existing before the removal of
|
||||
honor certificates.
|
||||
"""
|
||||
# Remove all existing course modes
|
||||
CourseMode.objects.filter(course_id=self.course.id).delete()
|
||||
# Ensure that honor mode exists
|
||||
CourseMode(
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name="Honor Cert",
|
||||
course_id=self.course.id
|
||||
).save()
|
||||
# We should be enrolled in honor mode
|
||||
self._test_course_without_sku(enrollment_mode=CourseMode.HONOR)
|
||||
|
||||
@override_settings(ECOMMERCE_API_URL=None, ECOMMERCE_API_SIGNING_KEY=None)
|
||||
def test_ecommerce_service_not_configured(self):
|
||||
"""
|
||||
@@ -240,7 +261,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
|
||||
self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
|
||||
|
||||
def assertProfessionalModeBypassed(self):
|
||||
""" Verifies that the view returns HTTP 406 when a course with no honor mode is encountered. """
|
||||
""" Verifies that the view returns HTTP 406 when a course with no honor or audit mode is encountered. """
|
||||
|
||||
CourseMode.objects.filter(course_id=self.course.id).delete()
|
||||
mode = CourseMode.NO_ID_PROFESSIONAL_MODE
|
||||
@@ -252,7 +273,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
|
||||
|
||||
# The view should return an error status code
|
||||
self.assertEqual(response.status_code, 406)
|
||||
msg = Messages.NO_HONOR_MODE.format(course_id=self.course.id)
|
||||
msg = Messages.NO_DEFAULT_ENROLLMENT_MODE.format(course_id=self.course.id)
|
||||
self.assertResponseMessage(response, msg)
|
||||
|
||||
def test_course_with_professional_mode_only(self):
|
||||
|
||||
@@ -59,9 +59,9 @@ class BasketsView(APIView):
|
||||
|
||||
return True, course_key, None
|
||||
|
||||
def _enroll(self, course_key, user):
|
||||
def _enroll(self, course_key, user, mode=CourseMode.DEFAULT_MODE_SLUG):
|
||||
""" Enroll the user in the course. """
|
||||
add_enrollment(user.username, unicode(course_key))
|
||||
add_enrollment(user.username, unicode(course_key), mode)
|
||||
|
||||
def _handle_marketing_opt_in(self, request, course_key, user):
|
||||
"""
|
||||
@@ -100,19 +100,28 @@ class BasketsView(APIView):
|
||||
msg = Messages.ENROLLMENT_EXISTS.format(course_id=course_id, username=user.username)
|
||||
return DetailResponse(msg, status=HTTP_409_CONFLICT)
|
||||
|
||||
# If there is no honor course mode, this most likely a Prof-Ed course. Return an error so that the JS
|
||||
# redirects to track selection.
|
||||
# If there is no audit or honor course mode, this most likely
|
||||
# a Prof-Ed course. Return an error so that the JS redirects
|
||||
# to track selection.
|
||||
honor_mode = CourseMode.mode_for_course(course_key, CourseMode.HONOR)
|
||||
audit_mode = CourseMode.mode_for_course(course_key, CourseMode.AUDIT)
|
||||
|
||||
if not honor_mode:
|
||||
msg = Messages.NO_HONOR_MODE.format(course_id=course_id)
|
||||
# Accept either honor or audit as an enrollment mode to
|
||||
# maintain backwards compatibility with existing courses
|
||||
default_enrollment_mode = audit_mode or honor_mode
|
||||
|
||||
if not default_enrollment_mode:
|
||||
msg = Messages.NO_DEFAULT_ENROLLMENT_MODE.format(course_id=course_id)
|
||||
return DetailResponse(msg, status=HTTP_406_NOT_ACCEPTABLE)
|
||||
elif not honor_mode.sku:
|
||||
elif default_enrollment_mode and not default_enrollment_mode.sku:
|
||||
# If there are no course modes with SKUs, enroll the user without contacting the external API.
|
||||
msg = Messages.NO_SKU_ENROLLED.format(enrollment_mode=CourseMode.HONOR, course_id=course_id,
|
||||
username=user.username)
|
||||
msg = Messages.NO_SKU_ENROLLED.format(
|
||||
enrollment_mode=default_enrollment_mode.slug,
|
||||
course_id=course_id,
|
||||
username=user.username
|
||||
)
|
||||
log.info(msg)
|
||||
self._enroll(course_key, user)
|
||||
self._enroll(course_key, user, default_enrollment_mode.slug)
|
||||
self._handle_marketing_opt_in(request, course_key, user)
|
||||
return DetailResponse(msg)
|
||||
|
||||
@@ -131,7 +140,7 @@ class BasketsView(APIView):
|
||||
# Make the API call
|
||||
try:
|
||||
response_data = api.baskets.post({
|
||||
'products': [{'sku': honor_mode.sku}],
|
||||
'products': [{'sku': default_enrollment_mode.sku}],
|
||||
'checkout': True,
|
||||
})
|
||||
|
||||
@@ -158,7 +167,7 @@ class BasketsView(APIView):
|
||||
audit_log(
|
||||
'checkout_requested',
|
||||
course_id=course_id,
|
||||
mode=honor_mode.slug,
|
||||
mode=default_enrollment_mode.slug,
|
||||
processor_name=None,
|
||||
user_id=user.id
|
||||
)
|
||||
|
||||
@@ -285,7 +285,7 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
|
||||
|
||||
expected_modes = [
|
||||
CourseMode(
|
||||
mode_slug=u'honor',
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
min_price=150, currency=u'USD',
|
||||
sku=u'ABC123'
|
||||
)
|
||||
|
||||
@@ -15,4 +15,5 @@ class Messages(object):
|
||||
ORDER_COMPLETED = u'Order {order_number} was completed.'
|
||||
ORDER_INCOMPLETE_ENROLLED = u'Order {order_number} was created, but is not yet complete. User was enrolled.'
|
||||
NO_HONOR_MODE = u'Course {course_id} does not have an honor mode.'
|
||||
NO_DEFAULT_ENROLLMENT_MODE = u'Course {course_id} does not have an honor or audit mode.'
|
||||
ENROLLMENT_EXISTS = u'User {username} is already enrolled in {course_id}.'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Test helpers for testing course block transformers.
|
||||
"""
|
||||
from course_modes.models import CourseMode
|
||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
@@ -221,7 +222,12 @@ class BlockParentsMapTestCase(ModuleStoreTestCase):
|
||||
self.password = 'test'
|
||||
self.student = UserFactory.create(is_staff=False, username='test_student', password=self.password)
|
||||
self.staff = UserFactory.create(is_staff=True, username='test_staff', password=self.password)
|
||||
CourseEnrollmentFactory.create(is_active=True, mode='honor', user=self.student, course_id=self.course.id)
|
||||
CourseEnrollmentFactory.create(
|
||||
is_active=True,
|
||||
mode=CourseMode.DEFAULT_MODE_SLUG,
|
||||
user=self.student,
|
||||
course_id=self.course.id
|
||||
)
|
||||
|
||||
def assert_transform_results(
|
||||
self,
|
||||
|
||||
@@ -58,10 +58,12 @@ class AboutTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, EventTrackingT
|
||||
)
|
||||
|
||||
self.purchase_course = CourseFactory.create(org='MITx', number='buyme', display_name='Course To Buy')
|
||||
self.course_mode = CourseMode(course_id=self.purchase_course.id,
|
||||
mode_slug="honor",
|
||||
mode_display_name="honor cert",
|
||||
min_price=10)
|
||||
self.course_mode = CourseMode(
|
||||
course_id=self.purchase_course.id,
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
|
||||
min_price=10
|
||||
)
|
||||
self.course_mode.save()
|
||||
|
||||
def test_anonymous_user(self):
|
||||
@@ -248,8 +250,7 @@ class AboutWithCappedEnrollmentsTestCase(LoginEnrollmentTestCase, ModuleStoreTes
|
||||
self.email = 'foo_second@test.com'
|
||||
self.password = 'bar'
|
||||
self.username = 'test_second'
|
||||
self.create_account(self.username,
|
||||
self.email, self.password)
|
||||
self.create_account(self.username, self.email, self.password)
|
||||
self.activate_user(self.email)
|
||||
self.login(self.email, self.password)
|
||||
|
||||
@@ -417,8 +418,8 @@ class AboutPurchaseCourseTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
|
||||
"""
|
||||
course_mode = CourseMode(
|
||||
course_id=course.id,
|
||||
mode_slug="honor",
|
||||
mode_display_name="honor cert",
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
|
||||
min_price=10,
|
||||
)
|
||||
course_mode.save()
|
||||
|
||||
@@ -201,8 +201,8 @@ class TestMicrosites(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
course_mode = CourseMode(
|
||||
course_id=self.course_with_visibility.id,
|
||||
mode_slug="honor",
|
||||
mode_display_name="honor cert",
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
|
||||
min_price=10,
|
||||
)
|
||||
course_mode.save()
|
||||
|
||||
@@ -27,6 +27,7 @@ from xblock.core import XBlock
|
||||
from xblock.fragment import Fragment
|
||||
|
||||
from capa.tests.response_xml_factory import OptionResponseXMLFactory
|
||||
from course_modes.models import CourseMode
|
||||
from courseware import module_render as render
|
||||
from courseware.courses import get_course_with_access, course_image_url, get_course_info_section
|
||||
from courseware.field_overrides import OverrideFieldData
|
||||
@@ -724,9 +725,9 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
@ddt.data(
|
||||
('honor', False, None, None),
|
||||
(CourseMode.DEFAULT_MODE_SLUG, False, None, None),
|
||||
(
|
||||
'honor',
|
||||
CourseMode.DEFAULT_MODE_SLUG,
|
||||
True,
|
||||
'eligible',
|
||||
{
|
||||
@@ -737,7 +738,7 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
}
|
||||
),
|
||||
(
|
||||
'honor',
|
||||
CourseMode.DEFAULT_MODE_SLUG,
|
||||
True,
|
||||
'submitted',
|
||||
{
|
||||
@@ -748,7 +749,7 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
}
|
||||
),
|
||||
(
|
||||
'honor',
|
||||
CourseMode.DEFAULT_MODE_SLUG,
|
||||
True,
|
||||
'error',
|
||||
{
|
||||
@@ -759,7 +760,7 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
}
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
None,
|
||||
{
|
||||
@@ -770,7 +771,7 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
}
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
'declined',
|
||||
{
|
||||
@@ -781,7 +782,7 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
}
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
'submitted',
|
||||
{
|
||||
@@ -792,7 +793,7 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
}
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
'verified',
|
||||
{
|
||||
@@ -803,7 +804,7 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
}
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
'rejected',
|
||||
{
|
||||
@@ -814,7 +815,7 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
}
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
'error',
|
||||
{
|
||||
@@ -851,56 +852,56 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
|
||||
@ddt.data(
|
||||
(
|
||||
'honor',
|
||||
CourseMode.DEFAULT_MODE_SLUG,
|
||||
True,
|
||||
None,
|
||||
'Try a proctored exam',
|
||||
True
|
||||
),
|
||||
(
|
||||
'honor',
|
||||
CourseMode.DEFAULT_MODE_SLUG,
|
||||
True,
|
||||
'submitted',
|
||||
'You have submitted this practice proctored exam',
|
||||
False
|
||||
),
|
||||
(
|
||||
'honor',
|
||||
CourseMode.DEFAULT_MODE_SLUG,
|
||||
True,
|
||||
'error',
|
||||
'There was a problem with your practice proctoring session',
|
||||
True
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
None,
|
||||
'This exam is proctored',
|
||||
False
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
'submitted',
|
||||
'You have submitted this proctored exam for review',
|
||||
True
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
'verified',
|
||||
'Your proctoring session was reviewed and passed all requirements',
|
||||
False
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
'rejected',
|
||||
'Your proctoring session was reviewed and did not pass requirements',
|
||||
True
|
||||
),
|
||||
(
|
||||
'verified',
|
||||
CourseMode.VERIFIED,
|
||||
False,
|
||||
'error',
|
||||
'There was a problem with your proctoring session',
|
||||
|
||||
@@ -708,7 +708,7 @@ class ProgressPageTests(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
# Enroll student into course
|
||||
CourseEnrollment.enroll(self.user, self.course.id, mode='honor')
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
resp = views.progress(self.request, course_id=self.course.id.to_deprecated_string(), student_id=self.user.id)
|
||||
# Assert that valid 'student_id' returns 200 status
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
@@ -12,6 +12,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.core.mail import send_mail
|
||||
from django.utils.translation import override as override_language
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from student.models import CourseEnrollment, CourseEnrollmentAllowed
|
||||
from courseware.models import StudentModule
|
||||
from edxmako.shortcuts import render_to_string
|
||||
@@ -110,7 +111,7 @@ def enroll_email(course_id, student_email, auto_enroll=False, email_students=Fal
|
||||
if previous_state.user:
|
||||
# if the student is currently unenrolled, don't enroll them in their
|
||||
# previous mode
|
||||
course_mode = u"honor"
|
||||
course_mode = CourseMode.DEFAULT_MODE_SLUG
|
||||
if previous_state.enrollment:
|
||||
course_mode = previous_state.mode
|
||||
|
||||
|
||||
@@ -1275,7 +1275,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest
|
||||
create paid course mode.
|
||||
"""
|
||||
paid_course = CourseFactory.create()
|
||||
CourseModeFactory.create(course_id=paid_course.id, min_price=50)
|
||||
CourseModeFactory.create(course_id=paid_course.id, min_price=50, mode_slug=CourseMode.HONOR)
|
||||
CourseInstructorRole(paid_course.id).add_users(self.instructor)
|
||||
return paid_course
|
||||
|
||||
@@ -1405,7 +1405,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest
|
||||
def test_unenroll_and_enroll_verified(self):
|
||||
"""
|
||||
Test that unenrolling and enrolling a student from a verified track
|
||||
results in that student being in an honor track
|
||||
results in that student being in the default track
|
||||
"""
|
||||
course_enrollment = CourseEnrollment.objects.get(
|
||||
user=self.enrolled_student, course_id=self.course.id
|
||||
@@ -1422,7 +1422,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest
|
||||
course_enrollment = CourseEnrollment.objects.get(
|
||||
user=self.enrolled_student, course_id=self.course.id
|
||||
)
|
||||
self.assertEqual(course_enrollment.mode, u'honor')
|
||||
self.assertEqual(course_enrollment.mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
def _change_student_enrollment(self, user, course, action):
|
||||
"""
|
||||
@@ -2138,7 +2138,11 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
|
||||
company_contact_email='test@123', recipient_name='R1',
|
||||
recipient_email='', customer_reference_number='PO#23')
|
||||
|
||||
paid_course_reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course.id)
|
||||
paid_course_reg_item = PaidCourseRegistration.add_to_order(
|
||||
self.cart,
|
||||
self.course.id,
|
||||
mode_slug=CourseMode.HONOR
|
||||
)
|
||||
# update the quantity of the cart item paid_course_reg_item
|
||||
resp = self.client.post(reverse('shoppingcart.views.update_user_cart'), {'ItemId': paid_course_reg_item.id, 'qty': '4'})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Test for the registration code status information.
|
||||
"""
|
||||
from course_modes.models import CourseMode
|
||||
from courseware.tests.factories import InstructorFactory
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from django.utils.translation import ugettext as _
|
||||
@@ -116,7 +117,7 @@ class TestCourseRegistrationCodeStatus(SharedModuleStoreTestCase):
|
||||
created_by=self.instructor,
|
||||
invoice=self.sale_invoice,
|
||||
invoice_item=self.invoice_item,
|
||||
mode_slug='honor'
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG
|
||||
)
|
||||
|
||||
reg_code = CourseRegistrationCode.objects.all()[0]
|
||||
@@ -247,7 +248,7 @@ class TestCourseRegistrationCodeStatus(SharedModuleStoreTestCase):
|
||||
created_by=self.instructor,
|
||||
invoice=self.sale_invoice,
|
||||
invoice_item=self.invoice_item,
|
||||
mode_slug='honor',
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
is_valid=False
|
||||
)
|
||||
|
||||
@@ -278,7 +279,7 @@ class TestCourseRegistrationCodeStatus(SharedModuleStoreTestCase):
|
||||
created_by=self.instructor,
|
||||
invoice=self.sale_invoice,
|
||||
invoice_item=self.invoice_item,
|
||||
mode_slug='honor'
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
)
|
||||
|
||||
reg_code = CourseRegistrationCode.objects.all()[0]
|
||||
|
||||
@@ -56,10 +56,12 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT
|
||||
display_name='<script>alert("XSS")</script>'
|
||||
)
|
||||
|
||||
self.course_mode = CourseMode(course_id=self.course.id,
|
||||
mode_slug="honor",
|
||||
mode_display_name="honor cert",
|
||||
min_price=40)
|
||||
self.course_mode = CourseMode(
|
||||
course_id=self.course.id,
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE.name,
|
||||
min_price=40
|
||||
)
|
||||
self.course_mode.save()
|
||||
# Create instructor account
|
||||
self.instructor = AdminFactory.create()
|
||||
|
||||
@@ -476,7 +476,7 @@ class TestInstructorDetailedEnrollmentReport(TestReportMixin, InstructorTaskCour
|
||||
created_by=self.instructor,
|
||||
invoice=self.sale_invoice_1,
|
||||
invoice_item=self.invoice_item,
|
||||
mode_slug='honor'
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG
|
||||
)
|
||||
course_registration_code.save()
|
||||
|
||||
@@ -517,7 +517,7 @@ class TestInstructorDetailedEnrollmentReport(TestReportMixin, InstructorTaskCour
|
||||
created_by=self.instructor,
|
||||
invoice=self.sale_invoice_1,
|
||||
invoice_item=self.invoice_item,
|
||||
mode_slug='honor'
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG
|
||||
)
|
||||
course_registration_code.save()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Test utils for Facebook functionality
|
||||
Test utils for Facebook functionality
|
||||
"""
|
||||
|
||||
import httpretty
|
||||
@@ -10,6 +10,7 @@ from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from social.apps.django_app.default.models import UserSocialAuth
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from student.models import CourseEnrollment
|
||||
from student.views import login_oauth_token
|
||||
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference, set_user_preference
|
||||
@@ -182,4 +183,4 @@ class SocialFacebookTestCase(ModuleStoreTestCase, APITestCase):
|
||||
self.assertTrue(CourseEnrollment.is_enrolled(user, course.id))
|
||||
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(user, course.id)
|
||||
self.assertTrue(is_active)
|
||||
self.assertEqual(course_mode, 'honor')
|
||||
self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from tempfile import NamedTemporaryFile
|
||||
from django.core.management import call_command
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from shoppingcart.models import Order, CertificateItem
|
||||
@@ -16,6 +17,11 @@ class TestRetireOrder(ModuleStoreTestCase):
|
||||
|
||||
course = CourseFactory.create()
|
||||
self.course_key = course.id
|
||||
CourseMode.objects.create(
|
||||
course_id=self.course_key,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name=CourseMode.HONOR
|
||||
)
|
||||
|
||||
# set up test carts
|
||||
self.cart, __ = self._create_cart()
|
||||
|
||||
@@ -56,7 +56,13 @@ class OrderTest(ModuleStoreTestCase):
|
||||
self.course_key = course.id
|
||||
self.other_course_keys = []
|
||||
for __ in xrange(1, 5):
|
||||
self.other_course_keys.append(CourseFactory.create().id)
|
||||
course_key = CourseFactory.create().id
|
||||
CourseMode.objects.create(
|
||||
course_id=course_key,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name="Honor"
|
||||
)
|
||||
self.other_course_keys.append(course_key)
|
||||
self.cost = 40
|
||||
|
||||
# Add mock tracker for event testing.
|
||||
@@ -64,6 +70,12 @@ class OrderTest(ModuleStoreTestCase):
|
||||
self.mock_tracker = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
CourseMode.objects.create(
|
||||
course_id=self.course_key,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name="Honor"
|
||||
)
|
||||
|
||||
def test_get_cart_for_user(self):
|
||||
# create a cart
|
||||
cart = Order.get_cart_for_user(user=self.user)
|
||||
@@ -479,10 +491,12 @@ class PaidCourseRegistrationTest(ModuleStoreTestCase):
|
||||
self.cost = 40
|
||||
self.course = CourseFactory.create()
|
||||
self.course_key = self.course.id
|
||||
self.course_mode = CourseMode(course_id=self.course_key,
|
||||
mode_slug="honor",
|
||||
mode_display_name="honor cert",
|
||||
min_price=self.cost)
|
||||
self.course_mode = CourseMode(
|
||||
course_id=self.course_key,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name="honor cert",
|
||||
min_price=self.cost
|
||||
)
|
||||
self.course_mode.save()
|
||||
self.percentage_discount = 20.0
|
||||
self.cart = Order.get_cart_for_user(self.user)
|
||||
@@ -492,7 +506,7 @@ class PaidCourseRegistrationTest(ModuleStoreTestCase):
|
||||
Test to check the total amount of the
|
||||
purchased items.
|
||||
"""
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.course_key, mode_slug=CourseMode.HONOR)
|
||||
self.cart.purchase()
|
||||
|
||||
total_amount = PaidCourseRegistration.get_total_amount_of_purchased_item(course_key=self.course_key)
|
||||
@@ -507,7 +521,7 @@ class PaidCourseRegistrationTest(ModuleStoreTestCase):
|
||||
self.assertEqual(total_amount, 0.00)
|
||||
|
||||
def test_add_to_order(self):
|
||||
reg1 = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
reg1 = PaidCourseRegistration.add_to_order(self.cart, self.course_key, mode_slug=CourseMode.HONOR)
|
||||
|
||||
self.assertEqual(reg1.unit_cost, self.cost)
|
||||
self.assertEqual(reg1.line_cost, self.cost)
|
||||
@@ -545,7 +559,7 @@ class PaidCourseRegistrationTest(ModuleStoreTestCase):
|
||||
|
||||
self.cart.order_type = 'business'
|
||||
self.cart.save()
|
||||
item = CourseRegCodeItem.add_to_order(self.cart, self.course_key, 2)
|
||||
item = CourseRegCodeItem.add_to_order(self.cart, self.course_key, 2, mode_slug=CourseMode.HONOR)
|
||||
self.cart.purchase()
|
||||
registration_codes = CourseRegistrationCode.order_generated_registration_codes(self.course_key)
|
||||
self.assertEqual(registration_codes.count(), item.qty)
|
||||
@@ -710,14 +724,15 @@ class PaidCourseRegistrationTest(ModuleStoreTestCase):
|
||||
|
||||
def test_add_with_default_mode(self):
|
||||
"""
|
||||
Tests add_to_cart where the mode specified in the argument is NOT in the database
|
||||
and NOT the default "honor". In this case it just adds the user in the CourseMode.DEFAULT_MODE, 0 price
|
||||
Tests add_to_cart where the mode specified in the argument is NOT
|
||||
in the database and NOT the default "audit". In this case it
|
||||
just adds the user in the CourseMode.DEFAULT_MODE for free.
|
||||
"""
|
||||
reg1 = PaidCourseRegistration.add_to_order(self.cart, self.course_key, mode_slug="DNE")
|
||||
|
||||
self.assertEqual(reg1.unit_cost, 0)
|
||||
self.assertEqual(reg1.line_cost, 0)
|
||||
self.assertEqual(reg1.mode, "honor")
|
||||
self.assertEqual(reg1.mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
self.assertEqual(reg1.user, self.user)
|
||||
self.assertEqual(reg1.status, "cart")
|
||||
self.assertEqual(self.cart.total_cost, 0)
|
||||
@@ -727,7 +742,7 @@ class PaidCourseRegistrationTest(ModuleStoreTestCase):
|
||||
|
||||
self.assertEqual(course_reg_code_item.unit_cost, 0)
|
||||
self.assertEqual(course_reg_code_item.line_cost, 0)
|
||||
self.assertEqual(course_reg_code_item.mode, "honor")
|
||||
self.assertEqual(course_reg_code_item.mode, CourseMode.DEFAULT_MODE_SLUG)
|
||||
self.assertEqual(course_reg_code_item.user, self.user)
|
||||
self.assertEqual(course_reg_code_item.status, "cart")
|
||||
self.assertEqual(self.cart.total_cost, 0)
|
||||
|
||||
@@ -184,7 +184,7 @@ class ItemizedPurchaseReportTest(ModuleStoreTestCase):
|
||||
self.course_reg_code_annotation = CourseRegCodeItemAnnotation(course_id=self.course_key, annotation=self.TEST_ANNOTATION)
|
||||
self.course_reg_code_annotation.save()
|
||||
self.cart = Order.get_cart_for_user(self.user)
|
||||
self.reg = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
self.reg = PaidCourseRegistration.add_to_order(self.cart, self.course_key, mode_slug=course_mode.mode_slug)
|
||||
self.cert_item = CertificateItem.add_to_order(self.cart, self.course_key, self.cost, 'verified')
|
||||
self.cart.purchase()
|
||||
self.now = datetime.datetime.now(pytz.UTC)
|
||||
|
||||
@@ -94,20 +94,41 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
self.coupon_code = 'abcde'
|
||||
self.reg_code = 'qwerty'
|
||||
self.percentage_discount = 10
|
||||
self.course_mode = CourseMode(course_id=self.course_key,
|
||||
mode_slug="honor",
|
||||
mode_display_name="honor cert",
|
||||
min_price=self.cost)
|
||||
self.course_mode = CourseMode(
|
||||
course_id=self.course_key,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name="honor cert",
|
||||
min_price=self.cost
|
||||
)
|
||||
self.course_mode.save()
|
||||
|
||||
# Saving another testing course mode
|
||||
self.testing_cost = 20
|
||||
self.testing_course_mode = CourseMode(course_id=self.testing_course.id,
|
||||
mode_slug="honor",
|
||||
mode_display_name="testing honor cert",
|
||||
min_price=self.testing_cost)
|
||||
self.testing_course_mode = CourseMode(
|
||||
course_id=self.testing_course.id,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name="testing honor cert",
|
||||
min_price=self.testing_cost
|
||||
)
|
||||
self.testing_course_mode.save()
|
||||
|
||||
# And for the XSS course
|
||||
CourseMode(
|
||||
course_id=self.xss_course_key,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name="honor cert",
|
||||
min_price=self.cost
|
||||
).save()
|
||||
|
||||
# And the verified course
|
||||
self.verified_course_mode = CourseMode(
|
||||
course_id=self.verified_course_key,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name="honor cert",
|
||||
min_price=self.cost
|
||||
)
|
||||
self.verified_course_mode.save()
|
||||
|
||||
self.cart = Order.get_cart_for_user(self.user)
|
||||
|
||||
self.addCleanup(patcher.stop)
|
||||
@@ -131,10 +152,12 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
percentage_discount=self.percentage_discount, created_by=self.user, is_active=is_active)
|
||||
coupon.save()
|
||||
|
||||
def add_reg_code(self, course_key, mode_slug='honor', is_valid=True):
|
||||
def add_reg_code(self, course_key, mode_slug=None, is_valid=True):
|
||||
"""
|
||||
add dummy registration code into models
|
||||
"""
|
||||
if mode_slug is None:
|
||||
mode_slug = self.course_mode.mode_slug
|
||||
course_reg_code = CourseRegistrationCode(
|
||||
code=self.reg_code, course_id=course_key,
|
||||
created_by=self.user, mode_slug=mode_slug,
|
||||
@@ -159,7 +182,7 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
adding course to user cart
|
||||
"""
|
||||
self.login_user()
|
||||
reg_item = PaidCourseRegistration.add_to_order(self.cart, course_key)
|
||||
reg_item = PaidCourseRegistration.add_to_order(self.cart, course_key, mode_slug=self.course_mode.mode_slug)
|
||||
return reg_item
|
||||
|
||||
def login_user(self):
|
||||
@@ -224,9 +247,18 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
test to check that that the same coupon code applied on multiple
|
||||
items in the cart.
|
||||
"""
|
||||
for course_key, cost in ((self.course_key, 40), (self.testing_course.id, 20)):
|
||||
CourseMode(
|
||||
course_id=course_key,
|
||||
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
|
||||
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
|
||||
min_price=cost
|
||||
).save()
|
||||
self.login_user()
|
||||
# add first course to user cart
|
||||
resp = self.client.post(reverse('shoppingcart.views.add_course_to_cart', args=[self.course_key.to_deprecated_string()]))
|
||||
resp = self.client.post(
|
||||
reverse('shoppingcart.views.add_course_to_cart', args=[self.course_key.to_deprecated_string()])
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# add and apply the coupon code to course in the cart
|
||||
self.add_coupon(self.course_key, True, self.coupon_code)
|
||||
@@ -237,7 +269,9 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
self.add_coupon(self.testing_course.id, True, self.coupon_code)
|
||||
#now add the second course to cart, the coupon code should be
|
||||
# applied when adding the second course to the cart
|
||||
resp = self.client.post(reverse('shoppingcart.views.add_course_to_cart', args=[self.testing_course.id.to_deprecated_string()]))
|
||||
resp = self.client.post(
|
||||
reverse('shoppingcart.views.add_course_to_cart', args=[self.testing_course.id.to_deprecated_string()])
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
#now check the user cart and see that the discount has been applied on both the courses
|
||||
resp = self.client.get(reverse('shoppingcart.views.show_cart', args=[]))
|
||||
@@ -586,7 +620,7 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
self.add_reg_code(course_key, mode_slug='verified')
|
||||
|
||||
# Enroll as honor in the course with the current user.
|
||||
CourseEnrollment.enroll(self.user, self.course_key)
|
||||
CourseEnrollment.enroll(self.user, self.course_key, mode=CourseMode.HONOR)
|
||||
self.login_user()
|
||||
current_enrollment, __ = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_key)
|
||||
self.assertEquals('honor', current_enrollment)
|
||||
@@ -753,8 +787,17 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
@patch('shoppingcart.views.render_to_response', render_mock)
|
||||
def test_show_cart(self):
|
||||
self.login_user()
|
||||
reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
|
||||
reg_item = PaidCourseRegistration.add_to_order(
|
||||
self.cart,
|
||||
self.course_key,
|
||||
mode_slug=self.course_mode.mode_slug
|
||||
)
|
||||
cert_item = CertificateItem.add_to_order(
|
||||
self.cart,
|
||||
self.verified_course_key,
|
||||
self.cost,
|
||||
self.course_mode.mode_slug
|
||||
)
|
||||
resp = self.client.get(reverse('shoppingcart.views.show_cart', args=[]))
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
@@ -860,7 +903,6 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
for __ in range(num_items):
|
||||
CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
|
||||
self.cart.purchase()
|
||||
|
||||
self.login_user()
|
||||
url = reverse('shoppingcart.views.show_receipt', args=[self.cart.id])
|
||||
resp = self.client.get(url, HTTP_ACCEPT="application/json")
|
||||
@@ -891,7 +933,7 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
'unit_cost': 40,
|
||||
'quantity': 1,
|
||||
'line_cost': 40,
|
||||
'line_desc': 'Honor Code Certificate for course Test Course',
|
||||
'line_desc': '{} for course Test Course'.format(self.verified_course_mode.mode_display_name),
|
||||
'course_key': unicode(self.verified_course_key)
|
||||
})
|
||||
|
||||
@@ -922,8 +964,17 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
|
||||
def test_show_receipt_json_multiple_items(self):
|
||||
# Two different item types
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
|
||||
PaidCourseRegistration.add_to_order(
|
||||
self.cart,
|
||||
self.course_key,
|
||||
mode_slug=self.course_mode.mode_slug
|
||||
)
|
||||
CertificateItem.add_to_order(
|
||||
self.cart,
|
||||
self.verified_course_key,
|
||||
self.cost,
|
||||
self.verified_course_mode.mode_slug
|
||||
)
|
||||
self.cart.purchase()
|
||||
|
||||
self.login_user()
|
||||
@@ -950,7 +1001,7 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
'unit_cost': 40,
|
||||
'quantity': 1,
|
||||
'line_cost': 40,
|
||||
'line_desc': 'Honor Code Certificate for course Test Course',
|
||||
'line_desc': '{} for course Test Course'.format(self.verified_course_mode.mode_display_name),
|
||||
'course_key': unicode(self.verified_course_key)
|
||||
})
|
||||
|
||||
@@ -1011,7 +1062,7 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
|
||||
self.client.login(username=self.instructor.username, password="test")
|
||||
cart = Order.get_cart_for_user(self.instructor)
|
||||
PaidCourseRegistration.add_to_order(cart, self.course_key)
|
||||
PaidCourseRegistration.add_to_order(cart, self.course_key, mode_slug=self.course_mode.mode_slug)
|
||||
cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
|
||||
|
||||
total_amount = PaidCourseRegistration.get_total_amount_of_purchased_item(self.course_key)
|
||||
@@ -1058,8 +1109,12 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
|
||||
# Two courses in user shopping cart
|
||||
self.login_user()
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
item2 = PaidCourseRegistration.add_to_order(self.cart, self.testing_course.id)
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.course_key, mode_slug=self.course_mode.mode_slug)
|
||||
item2 = PaidCourseRegistration.add_to_order(
|
||||
self.cart,
|
||||
self.testing_course.id,
|
||||
mode_slug=self.course_mode.mode_slug
|
||||
)
|
||||
self.assertEquals(self.cart.orderitem_set.count(), 2)
|
||||
|
||||
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
|
||||
@@ -1113,7 +1168,11 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
|
||||
@patch('shoppingcart.views.render_to_response', render_mock)
|
||||
def test_show_receipt_success(self):
|
||||
reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
reg_item = PaidCourseRegistration.add_to_order(
|
||||
self.cart,
|
||||
self.course_key,
|
||||
mode_slug=self.course_mode.mode_slug
|
||||
)
|
||||
cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
|
||||
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
|
||||
|
||||
@@ -1157,7 +1216,7 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
def test_courseregcode_item_total_price(self):
|
||||
self.cart.order_type = 'business'
|
||||
self.cart.save()
|
||||
CourseRegCodeItem.add_to_order(self.cart, self.course_key, 2)
|
||||
CourseRegCodeItem.add_to_order(self.cart, self.course_key, 2, mode_slug=self.course_mode.mode_slug)
|
||||
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
|
||||
self.assertEquals(CourseRegCodeItem.get_total_amount_of_purchased_item(self.course_key), 80)
|
||||
|
||||
@@ -1165,7 +1224,12 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
def test_show_receipt_success_with_order_type_business(self):
|
||||
self.cart.order_type = 'business'
|
||||
self.cart.save()
|
||||
reg_item = CourseRegCodeItem.add_to_order(self.cart, self.course_key, 2)
|
||||
reg_item = CourseRegCodeItem.add_to_order(
|
||||
self.cart,
|
||||
self.course_key,
|
||||
2,
|
||||
mode_slug=self.course_mode.mode_slug
|
||||
)
|
||||
self.cart.add_billing_details(company_name='T1Omega', company_contact_name='C1',
|
||||
company_contact_email='test@t1.com', recipient_email='test@t2.com')
|
||||
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
|
||||
@@ -1230,7 +1294,11 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
@patch('shoppingcart.views.render_to_response', render_mock)
|
||||
def test_show_receipt_success_with_upgrade(self):
|
||||
|
||||
reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
reg_item = PaidCourseRegistration.add_to_order(
|
||||
self.cart,
|
||||
self.course_key,
|
||||
mode_slug=self.course_mode.mode_slug
|
||||
)
|
||||
cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
|
||||
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
|
||||
|
||||
@@ -1255,7 +1323,11 @@ class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
|
||||
|
||||
@patch('shoppingcart.views.render_to_response', render_mock)
|
||||
def test_show_receipt_success_refund(self):
|
||||
reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
reg_item = PaidCourseRegistration.add_to_order(
|
||||
self.cart,
|
||||
self.course_key,
|
||||
mode_slug=self.course_mode.mode_slug
|
||||
)
|
||||
cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
|
||||
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
|
||||
cert_item.status = "refunded"
|
||||
@@ -1493,10 +1565,12 @@ class ShoppingcartViewsClosedEnrollment(ModuleStoreTestCase):
|
||||
|
||||
self.course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course')
|
||||
self.course_key = self.course.id
|
||||
self.course_mode = CourseMode(course_id=self.course_key,
|
||||
mode_slug="honor",
|
||||
mode_display_name="honor cert",
|
||||
min_price=self.cost)
|
||||
self.course_mode = CourseMode(
|
||||
course_id=self.course_key,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name="honor cert",
|
||||
min_price=self.cost
|
||||
)
|
||||
self.course_mode.save()
|
||||
self.testing_course = CourseFactory.create(
|
||||
org='Edx',
|
||||
@@ -1504,6 +1578,13 @@ class ShoppingcartViewsClosedEnrollment(ModuleStoreTestCase):
|
||||
display_name='Testing Super Course',
|
||||
metadata={"invitation_only": False}
|
||||
)
|
||||
self.testing_course_mode = CourseMode(
|
||||
course_id=self.testing_course.id,
|
||||
mode_slug=CourseMode.HONOR,
|
||||
mode_display_name="honor cert",
|
||||
min_price=self.cost
|
||||
)
|
||||
self.course_mode.save()
|
||||
self.percentage_discount = 20.0
|
||||
self.coupon_code = 'asdsad'
|
||||
self.course_mode = CourseMode(course_id=self.testing_course.id,
|
||||
@@ -1570,8 +1651,16 @@ class ShoppingcartViewsClosedEnrollment(ModuleStoreTestCase):
|
||||
|
||||
def test_to_check_that_cart_item_enrollment_is_closed_when_clicking_the_payment_button(self):
|
||||
self.login_user()
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.testing_course.id)
|
||||
PaidCourseRegistration.add_to_order(
|
||||
self.cart,
|
||||
self.course_key,
|
||||
mode_slug=self.course_mode.mode_slug
|
||||
)
|
||||
PaidCourseRegistration.add_to_order(
|
||||
self.cart,
|
||||
self.testing_course.id,
|
||||
mode_slug=self.testing_course_mode.mode_slug
|
||||
)
|
||||
|
||||
# update the testing_course enrollment dates
|
||||
self.testing_course.enrollment_start = self.tomorrow
|
||||
@@ -1593,8 +1682,8 @@ class ShoppingcartViewsClosedEnrollment(ModuleStoreTestCase):
|
||||
self.login_user()
|
||||
self.cart.order_type = 'business'
|
||||
self.cart.save()
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
CourseRegCodeItem.add_to_order(self.cart, self.testing_course.id, 2)
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.course_key, mode_slug=self.course_mode.mode_slug)
|
||||
CourseRegCodeItem.add_to_order(self.cart, self.testing_course.id, 2, mode_slug=self.course_mode.mode_slug)
|
||||
|
||||
# update the testing_course enrollment dates
|
||||
self.testing_course.enrollment_start = self.tomorrow
|
||||
@@ -2039,7 +2128,7 @@ class CSVReportViewsTest(SharedModuleStoreTestCase):
|
||||
report_type = 'itemized_purchase_report'
|
||||
start_date = '1970-01-01'
|
||||
end_date = '2100-01-01'
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.course_key)
|
||||
PaidCourseRegistration.add_to_order(self.cart, self.course_key, mode_slug=self.course_mode.mode_slug)
|
||||
self.cart.purchase()
|
||||
self.login_user()
|
||||
self.add_to_download_group(self.user)
|
||||
|
||||
@@ -339,8 +339,11 @@ def register_code_redemption(request, registration_code):
|
||||
}
|
||||
return render_to_response(template_to_render, context)
|
||||
elif request.method == "POST":
|
||||
reg_code_is_valid, reg_code_already_redeemed, course_registration = get_reg_code_validity(registration_code,
|
||||
request, limiter)
|
||||
reg_code_is_valid, reg_code_already_redeemed, course_registration = get_reg_code_validity(
|
||||
registration_code,
|
||||
request,
|
||||
limiter
|
||||
)
|
||||
course = get_course_by_id(course_registration.course_id, depth=0)
|
||||
|
||||
# Restrict the user from enrolling based on country access rules
|
||||
|
||||
@@ -17,6 +17,7 @@ from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.http import HttpRequest
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from openedx.core.djangoapps.user_api.accounts.api import activate_account, create_account
|
||||
from openedx.core.djangoapps.user_api.accounts import EMAIL_MAX_LENGTH
|
||||
from openedx.core.lib.js_utils import escape_json_dumps
|
||||
@@ -263,7 +264,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
|
||||
params = [
|
||||
('course_id', 'edX/DemoX/Demo_Course'),
|
||||
('enrollment_action', 'enroll'),
|
||||
('course_mode', 'honor'),
|
||||
('course_mode', CourseMode.DEFAULT_MODE_SLUG),
|
||||
('email_opt_in', 'true'),
|
||||
('next', '/custom/final/destination')
|
||||
]
|
||||
@@ -294,7 +295,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
|
||||
params = [
|
||||
('course_id', 'course-v1:Org+Course+Run'),
|
||||
('enrollment_action', 'enroll'),
|
||||
('course_mode', 'honor'),
|
||||
('course_mode', CourseMode.DEFAULT_MODE_SLUG),
|
||||
('email_opt_in', 'true'),
|
||||
('next', '/custom/final/destination'),
|
||||
]
|
||||
|
||||
@@ -472,6 +472,7 @@ class TestPhotoVerification(ModuleStoreTestCase):
|
||||
@ddt.unpack
|
||||
@ddt.data(
|
||||
{'enrollment_mode': 'honor', 'status': None, 'output': 'N/A'},
|
||||
{'enrollment_mode': 'audit', 'status': None, 'output': 'N/A'},
|
||||
{'enrollment_mode': 'verified', 'status': False, 'output': 'Not ID Verified'},
|
||||
{'enrollment_mode': 'verified', 'status': True, 'output': 'ID Verified'},
|
||||
)
|
||||
|
||||
@@ -105,7 +105,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
|
||||
@ddt.data("verified", "professional")
|
||||
def test_start_flow_not_verified(self, course_mode):
|
||||
course = self._create_course(course_mode)
|
||||
self._enroll(course.id, "honor")
|
||||
self._enroll(course.id)
|
||||
response = self._get_page('verify_student_start_flow', course.id)
|
||||
self._assert_displayed_mode(response, course_mode)
|
||||
self._assert_steps_displayed(
|
||||
@@ -123,8 +123,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
|
||||
@ddt.data("no-id-professional")
|
||||
def test_start_flow_with_no_id_professional(self, course_mode):
|
||||
course = self._create_course(course_mode)
|
||||
# by default enrollment is honor
|
||||
self._enroll(course.id, "honor")
|
||||
self._enroll(course.id)
|
||||
response = self._get_page('verify_student_start_flow', course.id)
|
||||
self._assert_displayed_mode(response, course_mode)
|
||||
self._assert_steps_displayed(
|
||||
@@ -164,7 +163,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
|
||||
@ddt.unpack
|
||||
def test_start_flow_already_verified(self, course_mode, verification_status):
|
||||
course = self._create_course(course_mode)
|
||||
self._enroll(course.id, "honor")
|
||||
self._enroll(course.id)
|
||||
self._set_verification_status(verification_status)
|
||||
response = self._get_page('verify_student_start_flow', course.id)
|
||||
self._assert_displayed_mode(response, course_mode)
|
||||
@@ -323,7 +322,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
|
||||
)
|
||||
def test_verify_now_not_paid(self, page_name):
|
||||
course = self._create_course("verified")
|
||||
self._enroll(course.id, "honor")
|
||||
self._enroll(course.id)
|
||||
response = self._get_page(page_name, course.id, expected_status_code=302)
|
||||
self._assert_redirects_to_upgrade(response, course.id)
|
||||
|
||||
@@ -440,7 +439,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
|
||||
@ddt.data("verified", "professional")
|
||||
def test_upgrade(self, course_mode):
|
||||
course = self._create_course(course_mode)
|
||||
self._enroll(course.id, "honor")
|
||||
self._enroll(course.id)
|
||||
|
||||
response = self._get_page('verify_student_upgrade_and_verify', course.id)
|
||||
self._assert_displayed_mode(response, course_mode)
|
||||
@@ -459,7 +458,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
|
||||
|
||||
def test_upgrade_already_verified(self):
|
||||
course = self._create_course("verified")
|
||||
self._enroll(course.id, "honor")
|
||||
self._enroll(course.id)
|
||||
self._set_verification_status("submitted")
|
||||
|
||||
response = self._get_page('verify_student_upgrade_and_verify', course.id)
|
||||
@@ -746,7 +745,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
|
||||
|
||||
return course
|
||||
|
||||
def _enroll(self, course_key, mode):
|
||||
def _enroll(self, course_key, mode=CourseMode.DEFAULT_MODE_SLUG):
|
||||
"""Enroll the user in a course. """
|
||||
CourseEnrollmentFactory.create(
|
||||
user=self.user,
|
||||
@@ -923,8 +922,8 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
|
||||
"""Check the course information on the page. """
|
||||
mode_display_name = u"Introduction à l'astrophysique"
|
||||
course = CourseFactory.create(display_name=mode_display_name)
|
||||
for course_mode in ["honor", "verified"]:
|
||||
min_price = (self.MIN_PRICE if course_mode != "honor" else 0)
|
||||
for course_mode in [CourseMode.DEFAULT_MODE_SLUG, "verified"]:
|
||||
min_price = (self.MIN_PRICE if course_mode != CourseMode.DEFAULT_MODE_SLUG else 0)
|
||||
CourseModeFactory(
|
||||
course_id=course.id,
|
||||
mode_slug=course_mode,
|
||||
@@ -932,7 +931,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
|
||||
min_price=min_price
|
||||
)
|
||||
|
||||
self._enroll(course.id, "honor")
|
||||
self._enroll(course.id)
|
||||
response_dict = self._get_page_data(self._get_page('verify_student_start_flow', course.id))
|
||||
|
||||
self.assertEqual(response_dict['course_name'], mode_display_name)
|
||||
@@ -948,7 +947,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
|
||||
# setting a nonempty sku on the course will a trigger calls to
|
||||
# the ecommerce api to get payment processors.
|
||||
course = self._create_course("verified", sku='nonempty-sku')
|
||||
self._enroll(course.id, "honor")
|
||||
self._enroll(course.id)
|
||||
|
||||
# mock out the payment processors endpoint
|
||||
httpretty.register_uri(
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
);
|
||||
});
|
||||
|
||||
it('sends the user to the payment flow when the course mode is not honor', function() {
|
||||
it('sends the user to the payment flow for a paid course mode', function() {
|
||||
// Simulate providing enrollment query string params
|
||||
// AND specifying a course mode.
|
||||
setFakeQueryParams({
|
||||
@@ -114,13 +114,13 @@
|
||||
);
|
||||
});
|
||||
|
||||
it('sends the user to the student dashboard when the course mode is honor', function() {
|
||||
it('sends the user to the student dashboard for an unpaid course mode', function() {
|
||||
// Simulate providing enrollment query string params
|
||||
// AND specifying a course mode.
|
||||
setFakeQueryParams({
|
||||
'?enrollment_action': 'enroll',
|
||||
'?course_id': COURSE_KEY,
|
||||
'?course_mode': 'honor'
|
||||
'?course_mode': 'audit'
|
||||
});
|
||||
|
||||
ajaxSpyAndInitialize(this);
|
||||
|
||||
@@ -26,7 +26,7 @@ var edx = edx || {};
|
||||
hasVisibleReqs: false,
|
||||
platformName: '',
|
||||
alreadyVerified: false,
|
||||
courseModeSlug: 'honor',
|
||||
courseModeSlug: 'audit',
|
||||
verificationGoodUntil: ''
|
||||
};
|
||||
},
|
||||
|
||||
@@ -33,9 +33,13 @@ else:
|
||||
% elif cert_status['status'] in ('generating', 'ready', 'notpassing', 'restricted'):
|
||||
<p class="message-copy">${_("Your final grade:")}
|
||||
<span class="grade-value">${"{0:.0f}%".format(float(cert_status['grade'])*100)}</span>.
|
||||
% if cert_status['status'] == 'notpassing' and enrollment.mode != 'audit':
|
||||
${_("Grade required for a {cert_name_short}:").format(cert_name_short=cert_name_short)} <span class="grade-value">
|
||||
${"{0:.0f}%".format(float(course_overview.lowest_passing_grade)*100)}</span>.
|
||||
% if cert_status['status'] == 'notpassing':
|
||||
% if enrollment.mode != 'audit':
|
||||
${_("Grade required for a {cert_name_short}:").format(cert_name_short=cert_name_short)} <span class="grade-value">
|
||||
% else:
|
||||
${_("Grade required to pass this course:")} <span class="grade-value">
|
||||
% endif
|
||||
${"{0:.0f}%".format(float(course_overview.lowest_passing_grade)*100)}</span>.
|
||||
% elif cert_status['status'] == 'restricted' and enrollment.mode == 'verified':
|
||||
<p class="message-copy">
|
||||
${_("Your verified {cert_name_long} is being held pending confirmation that the issuance of your {cert_name_short} is in compliance with strict U.S. embargoes on Iran, Cuba, Syria and Sudan. If you think our system has mistakenly identified you as being connected with one of those countries, please let us know by contacting {email}. If you would like a refund on your {cert_name_long}, please contact our billing address {billing_email}").format(email='<a class="contact-link" href="mailto:{email}">{email}</a>.'.format(email=settings.CONTACT_EMAIL), billing_email='<a class="contact-link" href="mailto:{email}">{email}</a>'.format(email=settings.PAYMENT_SUPPORT_EMAIL), cert_name_short=cert_name_short, cert_name_long=cert_name_long)}
|
||||
|
||||
@@ -9,6 +9,7 @@ from django.core.urlresolvers import reverse
|
||||
from markupsafe import escape
|
||||
from courseware.courses import get_course_university_about_section
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.helpers import enrollment_mode_display
|
||||
from student.helpers import (
|
||||
VERIFY_STATUS_NEED_TO_VERIFY,
|
||||
VERIFY_STATUS_SUBMITTED,
|
||||
@@ -34,7 +35,13 @@ from student.helpers import (
|
||||
|
||||
<li class="course-item">
|
||||
% if settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES'):
|
||||
<% course_verified_certs = CourseMode.enrollment_mode_display(enrollment.mode, verification_status.get('status')) %>
|
||||
<%
|
||||
course_verified_certs = enrollment_mode_display(
|
||||
enrollment.mode,
|
||||
verification_status.get('status'),
|
||||
course_overview.id
|
||||
)
|
||||
%>
|
||||
<%
|
||||
mode_class = course_verified_certs.get('display_mode', '')
|
||||
if mode_class != '':
|
||||
@@ -69,7 +76,7 @@ from student.helpers import (
|
||||
<img src="${course_overview.course_image_url}" class="course-image" alt="${_('{course_number} {course_name} Cover Image').format(course_number=course_overview.number, course_name=course_overview.display_name_with_default) | h}" />
|
||||
</a>
|
||||
% endif
|
||||
% if settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES'):
|
||||
% if settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES') and course_verified_certs.get('display_mode') != 'audit':
|
||||
<span class="sts-enrollment" title="${course_verified_certs.get('enrollment_title')}">
|
||||
<span class="label">${_("Enrolled as: ")}</span>
|
||||
% if course_verified_certs.get('show_image'):
|
||||
|
||||
@@ -22,7 +22,7 @@ from lms.djangoapps.verify_student.views import PayAndVerifyView
|
||||
${_(u"The verification deadline for {course_name} was {date}. Verification is no longer available.").format(
|
||||
course_name=course.display_name, date=deadline)}
|
||||
% elif deadline_name == PayAndVerifyView.UPGRADE_DEADLINE:
|
||||
${_(u"The deadline to upgrade to a verified certificate for this course has passed. You can still earn an honor code certificate.")}
|
||||
${_(u"The deadline to upgrade to a verified certificate for this course has passed.")}
|
||||
% endif
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -25,6 +25,15 @@ class CreditServiceTests(ModuleStoreTestCase):
|
||||
self.credit_course = CreditCourse.objects.create(course_key=self.course.id, enabled=True)
|
||||
self.profile = UserProfile.objects.create(user_id=self.user.id, name='Foo Bar')
|
||||
|
||||
def enroll(self, course_id=None):
|
||||
"""
|
||||
Enroll the test user in the given course's honor mode, or the test
|
||||
course if not provided.
|
||||
"""
|
||||
if course_id is None:
|
||||
course_id = self.course.id
|
||||
return CourseEnrollment.enroll(self.user, course_id, mode='honor')
|
||||
|
||||
def test_user_not_found(self):
|
||||
"""
|
||||
Makes sure that get_credit_state returns None if user_id cannot be found
|
||||
@@ -46,7 +55,7 @@ class CreditServiceTests(ModuleStoreTestCase):
|
||||
inactive
|
||||
"""
|
||||
|
||||
enrollment = CourseEnrollment.enroll(self.user, self.course.id)
|
||||
enrollment = self.enroll()
|
||||
enrollment.is_active = False
|
||||
enrollment.save()
|
||||
|
||||
@@ -58,7 +67,7 @@ class CreditServiceTests(ModuleStoreTestCase):
|
||||
Credit eligible
|
||||
"""
|
||||
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
self.enroll()
|
||||
|
||||
self.credit_course.enabled = False
|
||||
self.credit_course.save()
|
||||
@@ -86,7 +95,7 @@ class CreditServiceTests(ModuleStoreTestCase):
|
||||
|
||||
self.assertTrue(self.service.is_credit_course(self.course.id))
|
||||
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
self.enroll()
|
||||
|
||||
# set course requirements
|
||||
set_credit_requirements(
|
||||
@@ -127,7 +136,7 @@ class CreditServiceTests(ModuleStoreTestCase):
|
||||
"""
|
||||
self.assertTrue(self.service.is_credit_course(self.course.id))
|
||||
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
self.enroll()
|
||||
|
||||
# set course requirements
|
||||
set_credit_requirements(
|
||||
@@ -216,7 +225,7 @@ class CreditServiceTests(ModuleStoreTestCase):
|
||||
|
||||
self.assertFalse(self.service.is_credit_course(no_credit_course.id))
|
||||
|
||||
CourseEnrollment.enroll(self.user, no_credit_course.id)
|
||||
self.enroll(no_credit_course.id)
|
||||
|
||||
# this should be a no-op
|
||||
self.service.remove_credit_requirement_status(
|
||||
@@ -237,7 +246,7 @@ class CreditServiceTests(ModuleStoreTestCase):
|
||||
Make sure we can get back the optional course name
|
||||
"""
|
||||
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
self.enroll()
|
||||
|
||||
# make sure it is not returned by default
|
||||
credit_state = self.service.get_credit_state(self.user.id, self.course.id)
|
||||
@@ -258,7 +267,7 @@ class CreditServiceTests(ModuleStoreTestCase):
|
||||
|
||||
self.assertFalse(self.service.is_credit_course(no_credit_course.id))
|
||||
|
||||
CourseEnrollment.enroll(self.user, no_credit_course.id)
|
||||
self.enroll(no_credit_course.id)
|
||||
|
||||
# this should be a no-op
|
||||
self.service.set_credit_requirement_status(
|
||||
@@ -308,7 +317,7 @@ class CreditServiceTests(ModuleStoreTestCase):
|
||||
Make sure we can pass a course_id (string) and get back correct results as well
|
||||
"""
|
||||
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
self.enroll()
|
||||
|
||||
# set course requirements
|
||||
set_credit_requirements(
|
||||
|
||||
Reference in New Issue
Block a user