diff --git a/lms/djangoapps/course_api/blocks/api.py b/lms/djangoapps/course_api/blocks/api.py index 12636fef52..bbfd3d827d 100644 --- a/lms/djangoapps/course_api/blocks/api.py +++ b/lms/djangoapps/course_api/blocks/api.py @@ -20,6 +20,7 @@ def get_blocks( block_counts=None, student_view_data=None, return_type='dict', + block_types=None ): """ Return a serialized representation of the course blocks. @@ -61,6 +62,18 @@ def get_blocks( # transform blocks = get_course_blocks(user, usage_key, transformers) + # filter blocks by types + if block_types and len(block_types) == 0: + block_types = None + if block_types: + block_keys_to_remove = [] + for block_key in blocks: + block_type = blocks.get_xblock_field(block_key, 'category') + if not block_type in block_types: + block_keys_to_remove.append(block_key) + for block_key in block_keys_to_remove: + blocks.remove_block(block_key, True) + # serialize serializer_context = { 'request': request, diff --git a/lms/djangoapps/course_api/blocks/forms.py b/lms/djangoapps/course_api/blocks/forms.py index d77a148f81..ff6a746f59 100644 --- a/lms/djangoapps/course_api/blocks/forms.py +++ b/lms/djangoapps/course_api/blocks/forms.py @@ -31,6 +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) def clean_depth(self): """ @@ -88,6 +89,7 @@ class BlockListGetForm(Form): 'student_view_data', 'block_counts', 'nav_depth', + 'block_types', ] 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 f0682f3041..84b863367c 100644 --- a/lms/djangoapps/course_api/blocks/serializers.py +++ b/lms/djangoapps/course_api/blocks/serializers.py @@ -44,6 +44,13 @@ class BlockSerializer(serializers.Serializer): # pylint: disable=abstract-metho ), } + if 'lti_url' in self.context['requested_fields']: + data['lti_url'] = reverse( + 'lti_provider_launch', + kwargs={'course_id': unicode(block_key.course_key), 'usage_id': unicode(block_key)}, + request=self.context['request'], + ) + # add additional requested fields that are supported by the various transformers for supported_field in SUPPORTED_FIELDS: if supported_field.requested_field_name in self.context['requested_fields']: @@ -70,7 +77,6 @@ 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 b4ab1112a5..c5e017ea3c 100644 --- a/lms/djangoapps/course_api/blocks/tests/test_api.py +++ b/lms/djangoapps/course_api/blocks/tests/test_api.py @@ -35,7 +35,6 @@ 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) @@ -63,7 +62,6 @@ 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 ( @@ -77,3 +75,17 @@ class TestGetBlocks(EnableTransformerRegistryMixin, SharedModuleStoreTestCase): self.assertIn(unicode(block.location), blocks['blocks']) else: self.assertNotIn(unicode(block.location), blocks['blocks']) + + 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) + + 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']) diff --git a/lms/djangoapps/course_api/blocks/tests/test_forms.py b/lms/djangoapps/course_api/blocks/tests/test_forms.py index 78f94e4b71..efa397e693 100644 --- a/lms/djangoapps/course_api/blocks/tests/test_forms.py +++ b/lms/djangoapps/course_api/blocks/tests/test_forms.py @@ -60,6 +60,7 @@ class TestBlockListGetForm(EnableTransformerRegistryMixin, FormTestMixin, Shared 'usage_key': usage_key, 'username': self.student.username, 'user': self.student, + 'block_types': 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 f3a8691c28..c41de62a31 100644 --- a/lms/djangoapps/course_api/blocks/tests/test_serializers.py +++ b/lms/djangoapps/course_api/blocks/tests/test_serializers.py @@ -70,6 +70,7 @@ class TestBlockSerializerBase(EnableTransformerRegistryMixin, SharedModuleStoreT 'block_counts', 'student_view_data', 'student_view_multi_device', + 'lti_url', ]) def assert_extended_block(self, serialized_block): @@ -81,6 +82,7 @@ class TestBlockSerializerBase(EnableTransformerRegistryMixin, SharedModuleStoreT 'id', 'type', 'lms_web_url', 'student_view_url', 'display_name', 'graded', 'block_counts', 'student_view_multi_device', + 'lti_url', }, set(serialized_block.iterkeys()), ) @@ -136,9 +138,6 @@ 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/views.py b/lms/djangoapps/course_api/blocks/views.py index 8cbc4dcee4..50b846e85b 100644 --- a/lms/djangoapps/course_api/blocks/views.py +++ b/lms/djangoapps/course_api/blocks/views.py @@ -30,9 +30,10 @@ class BlocksView(DeveloperErrorViewMixin, ListAPIView): GET /api/courses/v1/blocks//? username=anjali &depth=all - &requested_fields=graded,format,student_view_multi_device + &requested_fields=graded,format,student_view_multi_device,lti_url &block_counts=video &student_view_data=video + &block_types=problem,html **Parameters**: @@ -85,6 +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. + + Example: block_types=vertical,html + **Response Values** The following fields are returned with a successful response. @@ -147,6 +153,7 @@ class BlocksView(DeveloperErrorViewMixin, ListAPIView): if the student_view_url and the student_view_data fields are not supported. + * lti_url: The block URL for an LTI consumer. """ def list(self, request, usage_key_string): # pylint: disable=arguments-differ @@ -177,7 +184,8 @@ class BlocksView(DeveloperErrorViewMixin, ListAPIView): params.cleaned_data['requested_fields'], params.cleaned_data.get('block_counts', []), params.cleaned_data.get('student_view_data', []), - params.cleaned_data['return_type'] + params.cleaned_data['return_type'], + params.cleaned_data.get('block_types', None), ) ) except ItemNotFoundError as exception: @@ -198,9 +206,10 @@ class BlocksInCourseView(BlocksView): GET /api/courses/v1/blocks/?course_id= &username=anjali &depth=all - &requested_fields=graded,format,student_view_multi_device + &requested_fields=graded,format,student_view_multi_device,lti_url &block_counts=video &student_view_data=video + &block_types=problem,html **Parameters**: diff --git a/openedx/core/lib/block_structure/block_structure.py b/openedx/core/lib/block_structure/block_structure.py index a526612d78..cd2422285f 100644 --- a/openedx/core/lib/block_structure/block_structure.py +++ b/openedx/core/lib/block_structure/block_structure.py @@ -70,7 +70,7 @@ class BlockStructure(object): traversal since it's the more common case and we currently need to support DAGs. """ - return self.topological_traversal() + return self.get_block_keys() #--- Block structure relation methods ---#