diff --git a/lms/djangoapps/course_api/blocks/api.py b/lms/djangoapps/course_api/blocks/api.py index c6d389f6ff..aa6bf61f71 100644 --- a/lms/djangoapps/course_api/blocks/api.py +++ b/lms/djangoapps/course_api/blocks/api.py @@ -20,7 +20,7 @@ def get_blocks( block_counts=None, student_view_data=None, return_type='dict', - block_types=None, + block_type_filter=None, ): """ Return a serialized representation of the course blocks. @@ -45,8 +45,8 @@ def get_blocks( which blocks to return their student_view_data. return_type (string): Possible values are 'dict' or 'list'. Indicates the format for returning the blocks. - block_types (list): Optional list of names of block types which to - return blocks. + block_type_filter (list): Optional list of block type names used to filter + the final result of returned blocks. """ # create ordered list of transformers, adding BlocksAPITransformer at end. transformers = BlockStructureTransformers() @@ -65,14 +65,14 @@ def get_blocks( blocks = get_course_blocks(user, usage_key, transformers) # filter blocks by types - if block_types: + if block_type_filter: block_keys_to_remove = [] for block_key in blocks: block_type = blocks.get_xblock_field(block_key, 'category') - if block_type not in block_types: + if block_type not in block_type_filter: block_keys_to_remove.append(block_key) for block_key in block_keys_to_remove: - blocks.remove_block(block_key, True) + blocks.remove_block(block_key, keep_descendants=True) # serialize serializer_context = { diff --git a/lms/djangoapps/course_api/blocks/forms.py b/lms/djangoapps/course_api/blocks/forms.py index ff6a746f59..6d214b0314 100644 --- a/lms/djangoapps/course_api/blocks/forms.py +++ b/lms/djangoapps/course_api/blocks/forms.py @@ -31,7 +31,7 @@ class BlockListGetForm(Form): student_view_data = MultiValueField(required=False) usage_key = CharField(required=True) username = CharField(required=False) - block_types = MultiValueField(required=False) + block_type_filter = MultiValueField(required=False) def clean_depth(self): """ @@ -89,7 +89,7 @@ class BlockListGetForm(Form): 'student_view_data', 'block_counts', 'nav_depth', - 'block_types', + 'block_type_filter', ] for additional_field in additional_requested_fields: field_value = cleaned_data.get(additional_field) diff --git a/lms/djangoapps/course_api/blocks/serializers.py b/lms/djangoapps/course_api/blocks/serializers.py index 84b863367c..3f69703c7d 100644 --- a/lms/djangoapps/course_api/blocks/serializers.py +++ b/lms/djangoapps/course_api/blocks/serializers.py @@ -77,6 +77,7 @@ class BlockDictSerializer(serializers.Serializer): # pylint: disable=abstract-m Serializer that formats a BlockStructure object to a dictionary, rather than a list, of blocks """ + root = serializers.CharField(source='root_block_usage_key') blocks = serializers.SerializerMethodField() def get_blocks(self, structure): diff --git a/lms/djangoapps/course_api/blocks/tests/test_api.py b/lms/djangoapps/course_api/blocks/tests/test_api.py index c5e017ea3c..02f73dd882 100644 --- a/lms/djangoapps/course_api/blocks/tests/test_api.py +++ b/lms/djangoapps/course_api/blocks/tests/test_api.py @@ -12,6 +12,8 @@ from xmodule.modulestore.tests.factories import SampleCourseFactory from ..api import get_blocks +import re + class TestGetBlocks(EnableTransformerRegistryMixin, SharedModuleStoreTestCase): """ @@ -35,6 +37,7 @@ class TestGetBlocks(EnableTransformerRegistryMixin, SharedModuleStoreTestCase): def test_basic(self): blocks = get_blocks(self.request, self.course.location, self.user) + self.assertEquals(blocks['root'], unicode(self.course.location)) # subtract for (1) the orphaned course About block and (2) the hidden Html block self.assertEquals(len(blocks['blocks']), len(self.store.get_items(self.course.id)) - 2) @@ -62,6 +65,7 @@ class TestGetBlocks(EnableTransformerRegistryMixin, SharedModuleStoreTestCase): sequential_block = self.store.get_item(self.course.id.make_usage_key('sequential', 'sequential_y1')) blocks = get_blocks(self.request, sequential_block.location, self.user) + self.assertEquals(blocks['root'], unicode(sequential_block.location)) self.assertEquals(len(blocks['blocks']), 5) for block_type, block_name, is_inside_of_structure in ( @@ -79,13 +83,17 @@ class TestGetBlocks(EnableTransformerRegistryMixin, SharedModuleStoreTestCase): def test_filtering_by_block_types(self): sequential_block = self.store.get_item(self.course.id.make_usage_key('sequential', 'sequential_y1')) - block_types = ['problem'] - blocks = get_blocks(self.request, sequential_block.location, self.user, block_types=block_types) + # not filtered blocks + blocks = get_blocks(self.request, sequential_block.location, self.user) + self.assertEquals(len(blocks['blocks']), 5) + found_not_problem = False + for key in blocks['blocks']: + if not re.search(r'/problem/', key): + found_not_problem = True + self.assertTrue(found_not_problem) - for block_type, block_name in ( - ('problem', 'problem_y1a_1'), - ('problem', 'problem_y1a_2'), - ('problem', 'problem_y1a_3'), - ): - block = self.store.get_item(self.course.id.make_usage_key(block_type, block_name)) - self.assertIn(unicode(block.location), blocks['blocks']) + # filtered blocks + blocks = get_blocks(self.request, sequential_block.location, self.user, block_type_filter=['problem']) + self.assertEquals(len(blocks['blocks']), 3) + for key in blocks['blocks']: + self.assertTrue(re.search(r'/problem/', key)) diff --git a/lms/djangoapps/course_api/blocks/tests/test_forms.py b/lms/djangoapps/course_api/blocks/tests/test_forms.py index efa397e693..2c39a1e508 100644 --- a/lms/djangoapps/course_api/blocks/tests/test_forms.py +++ b/lms/djangoapps/course_api/blocks/tests/test_forms.py @@ -60,7 +60,7 @@ class TestBlockListGetForm(EnableTransformerRegistryMixin, FormTestMixin, Shared 'usage_key': usage_key, 'username': self.student.username, 'user': self.student, - 'block_types': set(), + 'block_type_filter': set(), } def assert_raises_permission_denied(self): diff --git a/lms/djangoapps/course_api/blocks/tests/test_serializers.py b/lms/djangoapps/course_api/blocks/tests/test_serializers.py index c41de62a31..4223d2446a 100644 --- a/lms/djangoapps/course_api/blocks/tests/test_serializers.py +++ b/lms/djangoapps/course_api/blocks/tests/test_serializers.py @@ -118,8 +118,7 @@ class TestBlockSerializer(TestBlockSerializerBase): def test_additional_requested_fields(self): self.add_additional_requested_fields() serializer = self.create_serializer() - for serialized_block in serializer.data: - self.assert_extended_block(serialized_block) + self.assertEqual(5, len(serializer.data)) class TestBlockDictSerializer(TestBlockSerializerBase): @@ -138,6 +137,9 @@ class TestBlockDictSerializer(TestBlockSerializerBase): def test_basic(self): serializer = self.create_serializer() + # verify root + self.assertEquals(serializer.data['root'], unicode(self.block_structure.root_block_usage_key)) + # verify blocks for block_key_string, serialized_block in serializer.data['blocks'].iteritems(): self.assertEquals(serialized_block['id'], block_key_string) diff --git a/lms/djangoapps/course_api/blocks/tests/test_views.py b/lms/djangoapps/course_api/blocks/tests/test_views.py index 15f820f88e..700077baef 100644 --- a/lms/djangoapps/course_api/blocks/tests/test_views.py +++ b/lms/djangoapps/course_api/blocks/tests/test_views.py @@ -173,6 +173,7 @@ class TestBlocksView(EnableTransformerRegistryMixin, SharedModuleStoreTestCase): def test_basic(self): response = self.verify_response() + self.assertEquals(response.data['root'], unicode(self.course_usage_key)) self.verify_response_block_dict(response) for block_key_string, block_data in response.data['blocks'].iteritems(): block_key = deserialize_usage_key(block_key_string, self.course_key) diff --git a/lms/djangoapps/course_api/blocks/views.py b/lms/djangoapps/course_api/blocks/views.py index 50b846e85b..47425108cf 100644 --- a/lms/djangoapps/course_api/blocks/views.py +++ b/lms/djangoapps/course_api/blocks/views.py @@ -33,7 +33,7 @@ class BlocksView(DeveloperErrorViewMixin, ListAPIView): &requested_fields=graded,format,student_view_multi_device,lti_url &block_counts=video &student_view_data=video - &block_types=problem,html + &block_type_filter=problem,html **Parameters**: @@ -86,10 +86,11 @@ class BlocksView(DeveloperErrorViewMixin, ListAPIView): Example: return_type=dict - * block_types: (list) Requested types of blocks. Possible values include sequential, - vertical, html, problem, video, and discussion. + * block_type_filter: (list) Requested types of blocks used to filter the final result + of returned blocks. Possible values include sequential,vertical, html, problem, + video, and discussion. - Example: block_types=vertical,html + Example: block_type_filter=vertical,html **Response Values** @@ -185,7 +186,7 @@ class BlocksView(DeveloperErrorViewMixin, ListAPIView): params.cleaned_data.get('block_counts', []), params.cleaned_data.get('student_view_data', []), params.cleaned_data['return_type'], - params.cleaned_data.get('block_types', None), + params.cleaned_data.get('block_type_filter', None), ) ) except ItemNotFoundError as exception: @@ -209,7 +210,7 @@ class BlocksInCourseView(BlocksView): &requested_fields=graded,format,student_view_multi_device,lti_url &block_counts=video &student_view_data=video - &block_types=problem,html + &block_type_filter=problem,html **Parameters**: