From d6046d786d3d72ee298ce6e3a682aa7a946196af Mon Sep 17 00:00:00 2001 From: Clinton Blackburn Date: Mon, 16 Mar 2015 13:50:18 -0400 Subject: [PATCH] Fixed bug for courses without an honor mode --- lms/djangoapps/commerce/constants.py | 1 + lms/djangoapps/commerce/tests.py | 27 ++++++++++++++++++++- lms/djangoapps/commerce/views.py | 35 ++++++++++++++++------------ 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/lms/djangoapps/commerce/constants.py b/lms/djangoapps/commerce/constants.py index fbbeae0666..a59ff3fa95 100644 --- a/lms/djangoapps/commerce/constants.py +++ b/lms/djangoapps/commerce/constants.py @@ -19,3 +19,4 @@ class Messages(object): NO_SKU_ENROLLED = u'The {enrollment_mode} mode for {course_id} does not have a SKU. Enrolling {username} directly.' 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.' diff --git a/lms/djangoapps/commerce/tests.py b/lms/djangoapps/commerce/tests.py index b3f79fbd91..e53853abbe 100644 --- a/lms/djangoapps/commerce/tests.py +++ b/lms/djangoapps/commerce/tests.py @@ -237,7 +237,6 @@ class OrdersViewTests(ModuleStoreTestCase): response = self._post_to_view() # Validate the response - self._mock_ecommerce_api() self.assertEqual(response.status_code, 200) msg = Messages.NO_ECOM_API.format(username=self.user.username, course_id=self.course.id) self.assertResponseMessage(response, msg) @@ -245,3 +244,29 @@ class OrdersViewTests(ModuleStoreTestCase): # Ensure that the user is not enrolled and that no calls were made to the E-Commerce API self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) self.assertIsInstance(httpretty.last_request(), HTTPrettyRequestEmpty) + + def _test_professional_mode_only(self): + """ Verifies that the view behaves appropriately when the course only has a professional mode. """ + CourseMode.objects.filter(course_id=self.course.id).delete() + mode = 'no-id-professional' + CourseModeFactory.create(course_id=self.course.id, mode_slug=mode, mode_display_name=mode, + sku=uuid4().hex.decode('ascii')) + self._mock_ecommerce_api() + response = self._post_to_view() + self.assertEqual(response.status_code, 406) + msg = Messages.NO_HONOR_MODE.format(course_id=self.course.id) + self.assertResponseMessage(response, msg) + + @httpretty.activate + def test_course_with_professional_mode_only(self): + """ Verifies that the view behaves appropriately when the course only has a professional mode. """ + self._test_professional_mode_only() + + @httpretty.activate + @override_settings(ECOMMERCE_API_URL=None, ECOMMERCE_API_SIGNING_KEY=None) + def test_no_settings_and_professional_mode_only(self): + """ + Verifies that the view behaves appropriately when the course only has a professional mode and + the E-Commerce API is not configured. + """ + self._test_professional_mode_only() diff --git a/lms/djangoapps/commerce/views.py b/lms/djangoapps/commerce/views.py index e029998e9e..3d9249b46a 100644 --- a/lms/djangoapps/commerce/views.py +++ b/lms/djangoapps/commerce/views.py @@ -79,28 +79,33 @@ class OrdersView(APIView): if not valid: return DetailResponse(error, status=HTTP_406_NOT_ACCEPTABLE) + # Ensure that the course has an honor mode with SKU + honor_mode = CourseMode.mode_for_course(course_key, CourseMode.HONOR) + course_id = unicode(course_key) + + # 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 not honor_mode: + msg = Messages.NO_HONOR_MODE.format(course_id=course_id) + return DetailResponse(msg, status=HTTP_406_NOT_ACCEPTABLE) + elif not honor_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) + log.debug(msg) + self._enroll(course_key, user) + return DetailResponse(msg) + # Ensure that the E-Commerce API is setup properly ecommerce_api_url = getattr(settings, 'ECOMMERCE_API_URL', None) ecommerce_api_signing_key = getattr(settings, 'ECOMMERCE_API_SIGNING_KEY', None) if not (ecommerce_api_url and ecommerce_api_signing_key): self._enroll(course_key, user) - msg = Messages.NO_ECOM_API.format(username=user.username, course_id=unicode(course_key)) + msg = Messages.NO_ECOM_API.format(username=user.username, course_id=course_id) log.debug(msg) return DetailResponse(msg) - # Default to honor mode. In the future we may expand this view to support additional modes. - mode = CourseMode.DEFAULT_MODE_SLUG - course_modes = CourseMode.objects.filter(course_id=course_key, mode_slug=mode, sku__isnull=False) - - # If there are no course modes with SKUs, enroll the user without contacting the external API. - if not course_modes.exists(): - msg = Messages.NO_SKU_ENROLLED.format(enrollment_mode=mode, course_id=unicode(course_key), - username=user.username) - log.debug(msg) - self._enroll(course_key, user) - return DetailResponse(msg) - # Contact external API headers = { 'Content-Type': 'application/json', @@ -111,7 +116,7 @@ class OrdersView(APIView): try: timeout = getattr(settings, 'ECOMMERCE_API_TIMEOUT', 5) - response = requests.post(url, data=json.dumps({'sku': course_modes[0].sku}), headers=headers, + response = requests.post(url, data=json.dumps({'sku': honor_mode.sku}), headers=headers, timeout=timeout) except Exception as ex: # pylint: disable=broad-except log.exception('Call to E-Commerce API failed: %s.', ex.message) @@ -143,7 +148,7 @@ class OrdersView(APIView): 'status': order_status, 'complete_status': OrderStatus.COMPLETE, 'username': user.username, - 'course_id': unicode(course_key), + 'course_id': course_id, } log.error(msg, msg_kwargs)