diff --git a/cms/djangoapps/contentstore/tests/test_course_index.py b/cms/djangoapps/contentstore/tests/test_course_index.py index 4c4c736fb719..496b9b70569f 100644 --- a/cms/djangoapps/contentstore/tests/test_course_index.py +++ b/cms/djangoapps/contentstore/tests/test_course_index.py @@ -6,7 +6,7 @@ from contentstore.tests.utils import CourseTestCase from xmodule.modulestore.django import loc_mapper -from xmodule.modulestore.tests.factories import CourseFactory +from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore import parsers class TestCourseIndex(CourseTestCase): @@ -83,3 +83,49 @@ def test_course_staff_access(self): # test access self.check_index_and_outline(course_staff_client) + + def test_json_responses(self): + outline_url = self.course_locator.url_reverse('course/') + chapter = ItemFactory.create(parent_location=self.course.location, category='chapter', display_name="Week 1") + lesson = ItemFactory.create(parent_location=chapter.location, category='sequential', display_name="Lesson 1") + subsection = ItemFactory.create(parent_location=lesson.location, category='vertical', display_name='Subsection 1') + ItemFactory.create(parent_location=subsection.location, category="video", display_name="My Video") + + resp = self.client.get(outline_url, HTTP_ACCEPT='application/json') + json_response = json.loads(resp.content) + + # First spot check some values in the root response + self.assertEqual(json_response['category'], 'course') + self.assertEqual(json_response['id'], 'MITx.999.Robot_Super_Course/branch/draft/block/Robot_Super_Course') + self.assertEqual(json_response['display_name'], 'Robot Super Course') + self.assertTrue(json_response['is_container']) + self.assertFalse(json_response['is_draft']) + + # Now verify that the first child + children = json_response['children'] + self.assertTrue(len(children) > 0) + first_child_response = children[0] + self.assertEqual(first_child_response['category'], 'chapter') + self.assertEqual(first_child_response['id'], 'MITx.999.Robot_Super_Course/branch/draft/block/Week_1') + self.assertEqual(first_child_response['display_name'], 'Week 1') + self.assertTrue(first_child_response['is_container']) + self.assertFalse(first_child_response['is_draft']) + self.assertTrue(len(first_child_response['children']) > 0) + + # Finally, validate the entire response for consistency + self.assert_correct_json_response(json_response) + + def assert_correct_json_response(self, json_response): + """ + Asserts that the JSON response is syntactically consistent + """ + self.assertIsNotNone(json_response['display_name']) + self.assertIsNotNone(json_response['id']) + self.assertIsNotNone(json_response['category']) + self.assertIsNotNone(json_response['is_draft']) + self.assertIsNotNone(json_response['is_container']) + if json_response['is_container']: + for child_response in json_response['children']: + self.assert_correct_json_response(child_response) + else: + self.assertFalse('children' in json_response) diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 5b3415f071b1..89c5a33db779 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -100,9 +100,10 @@ def course_handler(request, tag=None, package_id=None, branch=None, version_guid DELETE json: delete this branch from this course (leaving off /branch/draft would imply delete the course) """ - if 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json'): + response_format = request.REQUEST.get('format', 'html') + if response_format == 'json' or 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json'): if request.method == 'GET': - raise NotImplementedError('coming soon') + return JsonResponse(_course_json(request, package_id, branch, version_guid, block)) elif request.method == 'POST': # not sure if this is only post. If one will have ids, it goes after access return create_new_course(request) elif not has_course_access( @@ -125,6 +126,37 @@ def course_handler(request, tag=None, package_id=None, branch=None, version_guid return HttpResponseNotFound() +@login_required +def _course_json(request, package_id, branch, version_guid, block): + """ + Returns a JSON overview of a course + """ + __, course = _get_locator_and_course( + package_id, branch, version_guid, block, request.user, depth=None + ) + return _xmodule_json(course, course.location.course_id) + + +def _xmodule_json(xmodule, course_id): + """ + Returns a JSON overview of an XModule + """ + locator = loc_mapper().translate_location( + course_id, xmodule.location, published=False, add_entry_if_missing=True + ) + is_container = xmodule.has_children + result = { + 'display_name': xmodule.display_name, + 'id': unicode(locator), + 'category': xmodule.category, + 'is_draft': getattr(xmodule, 'is_draft', False), + 'is_container': is_container, + } + if is_container: + result['children'] = [_xmodule_json(child, course_id) for child in xmodule.get_children()] + return result + + @login_required @ensure_csrf_cookie def course_listing(request):