Commit ae439310 by lenacom

New optional parameters for course blocks API: lti_url, block_types

parent 46533015
......@@ -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,
......
......@@ -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)
......
......@@ -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):
......
......@@ -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'])
......@@ -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):
......
......@@ -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)
......
......@@ -30,9 +30,10 @@ class BlocksView(DeveloperErrorViewMixin, ListAPIView):
GET /api/courses/v1/blocks/<usage_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**:
......@@ -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=<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**:
......
......@@ -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 ---#
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment