diff --git a/cms/djangoapps/contentstore/views/certificates.py b/cms/djangoapps/contentstore/views/certificates.py index 2b409ddb29..b254f18400 100644 --- a/cms/djangoapps/contentstore/views/certificates.py +++ b/cms/djangoapps/contentstore/views/certificates.py @@ -360,7 +360,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 diff --git a/common/djangoapps/course_modes/models.py b/common/djangoapps/course_modes/models.py index 55d0148edb..1eb6a529ec 100644 --- a/common/djangoapps/course_modes/models.py +++ b/common/djangoapps/course_modes/models.py @@ -94,8 +94,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] @@ -107,7 +107,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') @@ -507,7 +507,7 @@ class CourseMode(models.Model): return False # Check that the default mode is available. - return cls.HONOR in modes_dict + return cls.DEFAULT_MODE_SLUG in modes_dict @classmethod def is_white_label(cls, course_id, modes_dict=None): diff --git a/common/djangoapps/course_modes/tests/test_views.py b/common/djangoapps/course_modes/tests/test_views.py index 66197bf194..369b16d8bc 100644 --- a/common/djangoapps/course_modes/tests/test_views.py +++ b/common/djangoapps/course_modes/tests/test_views.py @@ -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): diff --git a/common/djangoapps/enrollment/api.py b/common/djangoapps/enrollment/api.py index f7774b53a1..1a0043eb0e 100644 --- a/common/djangoapps/enrollment/api.py +++ b/common/djangoapps/enrollment/api.py @@ -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", diff --git a/common/djangoapps/enrollment/tests/test_views.py b/common/djangoapps/enrollment/tests/test_views.py index 20052bff34..914075ee1f 100644 --- a/common/djangoapps/enrollment/tests/test_views.py +++ b/common/djangoapps/enrollment/tests/test_views.py @@ -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), diff --git a/common/djangoapps/enrollment/views.py b/common/djangoapps/enrollment/views.py index d3aa42eab2..110df7e906 100644 --- a/common/djangoapps/enrollment/views.py +++ b/common/djangoapps/enrollment/views.py @@ -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={ diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index daec221983..de736b7f9f 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -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 diff --git a/common/djangoapps/student/tests/factories.py b/common/djangoapps/student/tests/factories.py index f59750eb40..e9ed3395d5 100644 --- a/common/djangoapps/student/tests/factories.py +++ b/common/djangoapps/student/tests/factories.py @@ -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' diff --git a/common/djangoapps/student/tests/test_enrollment.py b/common/djangoapps/student/tests/test_enrollment.py index 2334c99b95..e8c26cdb62 100644 --- a/common/djangoapps/student/tests/test_enrollment.py +++ b/common/djangoapps/student/tests/test_enrollment.py @@ -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,13 @@ 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 / 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 default mode. + (['honor', 'verified', 'audit'], 'course_modes_choose', CourseMode.DEFAULT_MODE_SLUG), # Professional ed # Expect that we're sent to the "choose your track" page diff --git a/common/djangoapps/student/tests/test_recent_enrollments.py b/common/djangoapps/student/tests/test_recent_enrollments.py index 6372030367..ec94f4868b 100644 --- a/common/djangoapps/student/tests/test_recent_enrollments.py +++ b/common/djangoapps/student/tests/test_recent_enrollments.py @@ -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), diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py index 42f19757f2..a4bcacd915 100644 --- a/common/djangoapps/student/tests/tests.py +++ b/common/djangoapps/student/tests/tests.py @@ -18,6 +18,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 @@ -321,7 +322,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 +345,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 +571,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 +583,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 +759,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 +794,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 +802,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 +815,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 +831,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 +852,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): diff --git a/lms/djangoapps/certificates/tests/test_api.py b/lms/djangoapps/certificates/tests/test_api.py index d264a67a93..b5af0cd2f2 100644 --- a/lms/djangoapps/certificates/tests/test_api.py +++ b/lms/djangoapps/certificates/tests/test_api.py @@ -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 @@ -305,6 +306,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) diff --git a/lms/djangoapps/commerce/api/v0/tests/test_views.py b/lms/djangoapps/commerce/api/v0/tests/test_views.py index 93c4e7a114..3be720322c 100644 --- a/lms/djangoapps/commerce/api/v0/tests/test_views.py +++ b/lms/djangoapps/commerce/api/v0/tests/test_views.py @@ -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): diff --git a/lms/djangoapps/commerce/api/v0/views.py b/lms/djangoapps/commerce/api/v0/views.py index ba548ddf0d..7a050b1ad0 100644 --- a/lms/djangoapps/commerce/api/v0/views.py +++ b/lms/djangoapps/commerce/api/v0/views.py @@ -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 ) diff --git a/lms/djangoapps/commerce/api/v1/tests/test_views.py b/lms/djangoapps/commerce/api/v1/tests/test_views.py index f3db3da0cf..62245bef2a 100644 --- a/lms/djangoapps/commerce/api/v1/tests/test_views.py +++ b/lms/djangoapps/commerce/api/v1/tests/test_views.py @@ -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' ) diff --git a/lms/djangoapps/commerce/constants.py b/lms/djangoapps/commerce/constants.py index 729830a2e8..447847fb9b 100644 --- a/lms/djangoapps/commerce/constants.py +++ b/lms/djangoapps/commerce/constants.py @@ -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}.' diff --git a/lms/djangoapps/course_blocks/transformers/tests/test_helpers.py b/lms/djangoapps/course_blocks/transformers/tests/test_helpers.py index 6cc5072b60..1d049c8d25 100644 --- a/lms/djangoapps/course_blocks/transformers/tests/test_helpers.py +++ b/lms/djangoapps/course_blocks/transformers/tests/test_helpers.py @@ -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, diff --git a/lms/djangoapps/courseware/tests/test_about.py b/lms/djangoapps/courseware/tests/test_about.py index 0aeda4fdba..fb457e079d 100644 --- a/lms/djangoapps/courseware/tests/test_about.py +++ b/lms/djangoapps/courseware/tests/test_about.py @@ -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() diff --git a/lms/djangoapps/courseware/tests/test_microsites.py b/lms/djangoapps/courseware/tests/test_microsites.py index 9772375e7e..943b68ea87 100644 --- a/lms/djangoapps/courseware/tests/test_microsites.py +++ b/lms/djangoapps/courseware/tests/test_microsites.py @@ -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() diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index ab271768de..cd2075f92f 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -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', diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index dbfc8a1faf..d83bcab859 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -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) diff --git a/lms/djangoapps/instructor/enrollment.py b/lms/djangoapps/instructor/enrollment.py index 305ee8b636..d963721508 100644 --- a/lms/djangoapps/instructor/enrollment.py +++ b/lms/djangoapps/instructor/enrollment.py @@ -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 diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index b7cdd755de..ef4bfa9d7c 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -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) diff --git a/lms/djangoapps/instructor/tests/test_registration_codes.py b/lms/djangoapps/instructor/tests/test_registration_codes.py index 30cc2fbb7a..e6f250d633 100644 --- a/lms/djangoapps/instructor/tests/test_registration_codes.py +++ b/lms/djangoapps/instructor/tests/test_registration_codes.py @@ -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] diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py index 37ea9c4e7a..8a40121fb7 100644 --- a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py +++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py @@ -56,10 +56,12 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT display_name='' ) - 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() diff --git a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py index 5e851a5019..9a51b00835 100644 --- a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py +++ b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py @@ -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() diff --git a/lms/djangoapps/mobile_api/social_facebook/test_utils.py b/lms/djangoapps/mobile_api/social_facebook/test_utils.py index 5bb3130ff4..c9c7304e10 100644 --- a/lms/djangoapps/mobile_api/social_facebook/test_utils.py +++ b/lms/djangoapps/mobile_api/social_facebook/test_utils.py @@ -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) diff --git a/lms/djangoapps/shoppingcart/management/tests/test_retire_order.py b/lms/djangoapps/shoppingcart/management/tests/test_retire_order.py index 044c7115c1..140cbd309a 100644 --- a/lms/djangoapps/shoppingcart/management/tests/test_retire_order.py +++ b/lms/djangoapps/shoppingcart/management/tests/test_retire_order.py @@ -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() diff --git a/lms/djangoapps/shoppingcart/tests/test_models.py b/lms/djangoapps/shoppingcart/tests/test_models.py index a22b91a791..98db8769b7 100644 --- a/lms/djangoapps/shoppingcart/tests/test_models.py +++ b/lms/djangoapps/shoppingcart/tests/test_models.py @@ -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) diff --git a/lms/djangoapps/shoppingcart/tests/test_reports.py b/lms/djangoapps/shoppingcart/tests/test_reports.py index a2304cceff..dccb1aa9e2 100644 --- a/lms/djangoapps/shoppingcart/tests/test_reports.py +++ b/lms/djangoapps/shoppingcart/tests/test_reports.py @@ -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) diff --git a/lms/djangoapps/shoppingcart/tests/test_views.py b/lms/djangoapps/shoppingcart/tests/test_views.py index 459c763ecc..381277ed52 100644 --- a/lms/djangoapps/shoppingcart/tests/test_views.py +++ b/lms/djangoapps/shoppingcart/tests/test_views.py @@ -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) diff --git a/lms/djangoapps/shoppingcart/views.py b/lms/djangoapps/shoppingcart/views.py index 06ded6ebe6..7789f9247a 100644 --- a/lms/djangoapps/shoppingcart/views.py +++ b/lms/djangoapps/shoppingcart/views.py @@ -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 diff --git a/lms/djangoapps/student_account/test/test_views.py b/lms/djangoapps/student_account/test/test_views.py index f67b71b3ab..8718bdcaaa 100644 --- a/lms/djangoapps/student_account/test/test_views.py +++ b/lms/djangoapps/student_account/test/test_views.py @@ -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'), ] diff --git a/lms/djangoapps/verify_student/tests/test_models.py b/lms/djangoapps/verify_student/tests/test_models.py index 7517e553d5..07fa37548f 100644 --- a/lms/djangoapps/verify_student/tests/test_models.py +++ b/lms/djangoapps/verify_student/tests/test_models.py @@ -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'}, ) diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 21e08adf0b..f98153e180 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -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( diff --git a/lms/static/js/spec/student_account/finish_auth_spec.js b/lms/static/js/spec/student_account/finish_auth_spec.js index f29291747b..2086526631 100644 --- a/lms/static/js/spec/student_account/finish_auth_spec.js +++ b/lms/static/js/spec/student_account/finish_auth_spec.js @@ -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); diff --git a/lms/static/js/verify_student/views/make_payment_step_view.js b/lms/static/js/verify_student/views/make_payment_step_view.js index 42c0176e58..f1313d5d87 100644 --- a/lms/static/js/verify_student/views/make_payment_step_view.js +++ b/lms/static/js/verify_student/views/make_payment_step_view.js @@ -26,7 +26,7 @@ var edx = edx || {}; hasVisibleReqs: false, platformName: '', alreadyVerified: false, - courseModeSlug: 'honor', + courseModeSlug: 'audit', verificationGoodUntil: '' }; }, diff --git a/lms/templates/verify_student/missed_deadline.html b/lms/templates/verify_student/missed_deadline.html index 05be906911..f342ec0372 100644 --- a/lms/templates/verify_student/missed_deadline.html +++ b/lms/templates/verify_student/missed_deadline.html @@ -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

diff --git a/openedx/core/djangoapps/credit/tests/test_services.py b/openedx/core/djangoapps/credit/tests/test_services.py index c250ce0074..00080406c2 100644 --- a/openedx/core/djangoapps/credit/tests/test_services.py +++ b/openedx/core/djangoapps/credit/tests/test_services.py @@ -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(