Commit 5a44f391 by Nimisha Asthagiri

MA-977 Use "supports" decorator on XBlocks for multi-device support

parent 56da2852
......@@ -575,11 +575,13 @@ class LoncapaProblem(object):
return {}
@property
def has_responsive_ui(self):
def has_multi_device_support(self):
"""
Returns whether this capa problem has support for responsive UI.
Returns whether this capa problem has multi-device support.
"""
return all(responder.has_responsive_ui for responder in self.responders.values())
return all(
responder.multi_device_support for responder in self.responders.values()
)
# ======= Private Methods Below ========
......
......@@ -145,9 +145,9 @@ class LoncapaResponse(object):
required_attributes = []
# Overridable field that specifies whether this capa response type has support for
# responsive UI, for rendering on devices of different sizes and shapes.
# for rendering on devices of different sizes and shapes.
# By default, we set this to False, allowing subclasses to override as appropriate.
has_responsive_ui = False
multi_device_support = False
def __init__(self, xml, inputfields, context, system, capa_module):
"""
......@@ -808,7 +808,7 @@ class ChoiceResponse(LoncapaResponse):
max_inputfields = 1
allowed_inputfields = ['checkboxgroup', 'radiogroup']
correct_choices = None
has_responsive_ui = True
multi_device_support = True
def setup_response(self):
self.assign_choice_names()
......@@ -993,7 +993,7 @@ class MultipleChoiceResponse(LoncapaResponse):
max_inputfields = 1
allowed_inputfields = ['choicegroup']
correct_choices = None
has_responsive_ui = True
multi_device_support = True
def setup_response(self):
# call secondary setup for MultipleChoice questions, to set name
......@@ -1346,7 +1346,7 @@ class OptionResponse(LoncapaResponse):
hint_tag = 'optionhint'
allowed_inputfields = ['optioninput']
answer_fields = None
has_responsive_ui = True
multi_device_support = True
def setup_response(self):
self.answer_fields = self.inputfields
......@@ -1421,7 +1421,7 @@ class NumericalResponse(LoncapaResponse):
allowed_inputfields = ['textline', 'formulaequationinput']
required_attributes = ['answer']
max_inputfields = 1
has_responsive_ui = True
multi_device_support = True
def __init__(self, *args, **kwargs):
self.correct_answer = ''
......@@ -1645,7 +1645,7 @@ class StringResponse(LoncapaResponse):
required_attributes = ['answer']
max_inputfields = 1
correct_answer = []
has_responsive_ui = True
multi_device_support = True
def setup_response_backward(self):
self.correct_answer = [
......
......@@ -189,13 +189,6 @@ class CapaDescriptor(CapaFields, RawDescriptor):
registered_tags = responsetypes.registry.registered_tags()
return set([node.tag for node in tree.iter() if node.tag in registered_tags])
@property
def has_responsive_ui(self):
"""
Returns whether this module has support for responsive UI.
"""
return self.lcp.has_responsive_ui
def index_dictionary(self):
"""
Return dictionary prepared with module content and type for indexing.
......@@ -211,6 +204,17 @@ class CapaDescriptor(CapaFields, RawDescriptor):
result.update(index)
return result
def has_support(self, view, functionality):
"""
Override the XBlock.has_support method to return appropriate
value for the multi-device functionality.
Returns whether the given view has support for the given functionality.
"""
if functionality == "multi_device":
return self.lcp.has_multi_device_support
else:
return False
# Proxy to CapaModule for access to any of its attributes
answer_available = module_attr('answer_available')
check_button_name = module_attr('check_button_name')
......
......@@ -84,7 +84,9 @@ class HtmlModule(HtmlModuleMixin):
"""
Module for putting raw html in a course
"""
pass
@XBlock.supports("multi_device")
def student_view(self, context):
return super(HtmlModule, self).student_view(context)
class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor): # pylint: disable=abstract-method
......@@ -95,7 +97,6 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor): # pylint: d
module_class = HtmlModule
filename_extension = "xml"
template_dir_name = "html"
has_responsive_ui = True
show_in_read_only_mode = True
js = {'coffee': [resource_string(__name__, 'js/src/html/edit.coffee')]}
......
......@@ -80,7 +80,7 @@ class CourseViewTestsMixin(object):
factory = MultipleChoiceResponseXMLFactory()
args = {'choices': [False, True, False]}
problem_xml = factory.build_xml(**args)
ItemFactory.create(
self.problem = ItemFactory.create(
category="problem",
parent_location=self.sequential.location,
display_name="Problem 1",
......@@ -94,6 +94,12 @@ class CourseViewTestsMixin(object):
display_name="Video 1",
)
self.html = ItemFactory.create(
category="html",
parent_location=self.sequential.location,
display_name="HTML 1",
)
self.empty_course = CourseFactory.create(
start=datetime(2014, 6, 16, 14, 30),
end=datetime(2015, 1, 16),
......@@ -457,7 +463,7 @@ class CourseBlocksOrNavigationTestMixin(CourseDetailTestMixin, CourseViewTestsMi
blocks = response.data[self.block_navigation_view_type]
# verify number of blocks
self.assertEquals(len(blocks), 4)
self.assertEquals(len(blocks), 5)
# verify fields in blocks
for field, block in product(self.block_fields, blocks.values()):
......@@ -519,6 +525,23 @@ class CourseBlocksTestMixin(object):
self.assertIn('problem', root_block['block_count'])
self.assertEquals(root_block['block_count']['problem'], 1)
def test_multi_device_support(self):
"""
Verifies the view's response when multi_device support is requested.
"""
response = self.http_get_for_course(
data={'fields': 'multi_device'}
)
self.assertEquals(response.status_code, 200)
for block, expected_multi_device_support in (
(self.problem, True),
(self.html, True),
(self.video, False)
):
block_response = response.data[self.block_navigation_view_type][unicode(block.location)]
self.assertEquals(block_response['multi_device'], expected_multi_device_support)
class CourseNavigationTestMixin(object):
"""
......@@ -535,7 +558,7 @@ class CourseNavigationTestMixin(object):
)
root_block = response.data[self.block_navigation_view_type][unicode(self.course.location)]
self.assertIn('descendants', root_block)
self.assertEquals(len(root_block['descendants']), 3)
self.assertEquals(len(root_block['descendants']), 4)
def test_depth(self):
"""
......@@ -545,7 +568,7 @@ class CourseNavigationTestMixin(object):
container_descendants = (
(self.course.location, 1),
(self.sequential.location, 2),
(self.sequential.location, 3),
)
for container_location, expected_num_descendants in container_descendants:
block = response.data[self.block_navigation_view_type][unicode(container_location)]
......
......@@ -321,7 +321,7 @@ class CourseBlocksAndNavigation(ListAPIView):
GET api/course_structure/v0/courses/{course_id}/blocks+navigation/
&block_count=video
&block_json={"video":{"profiles":["mobile_low"]}}
&fields=graded,format,responsive_ui
&fields=graded,format,multi_device
**Parameters**:
......@@ -335,9 +335,9 @@ class CourseBlocksAndNavigation(ListAPIView):
Example: block_count="video,problem"
* fields: (list) Indicates which additional fields to return for each block.
Default is "children,graded,format,responsive_ui"
Default is "children,graded,format,multi_device"
Example: fields=graded,format,responsive_ui
Example: fields=graded,format,multi_device
* navigation_depth (integer) Indicates how far deep to traverse into the course hierarchy before bundling
all the descendants.
......@@ -387,8 +387,9 @@ class CourseBlocksAndNavigation(ListAPIView):
Possible values can be "Homework", "Lab", "Midterm Exam", and "Final Exam".
Returned only if "format" is included in the "fields" parameter.
* responsive_ui: (boolean) Whether or not the block's rendering obtained via block_url is responsive.
Returned only if "responsive_ui" is included in the "fields" parameter.
* multi_device: (boolean) Whether or not the block's rendering obtained via block_url has support
for multiple devices.
Returned only if "multi_device" is included in the "fields" parameter.
* navigation: A dictionary that maps block IDs to a collection of navigation information about each block.
Each block contains the following fields. Returned only if using the "navigation" endpoint.
......@@ -405,7 +406,7 @@ class CourseBlocksAndNavigation(ListAPIView):
"""
A class for encapsulating the request information, including what optional fields are requested.
"""
DEFAULT_FIELDS = "children,graded,format,responsive_ui"
DEFAULT_FIELDS = "children,graded,format,multi_device"
def __init__(self, request, course):
self.request = request
......@@ -417,10 +418,6 @@ class CourseBlocksAndNavigation(ListAPIView):
# fields
self.fields = set(request.GET.get('fields', self.DEFAULT_FIELDS).split(","))
# children
self.children = 'children' in self.fields
self.fields.discard('children')
# block_count
self.block_count = request.GET.get('block_count', "")
self.block_count = (
......@@ -489,7 +486,7 @@ class CourseBlocksAndNavigation(ListAPIView):
self.descendants_of_parent = parent_block_info.descendants_of_self
# add ourselves to the parent's children, if requested.
if request_info.children:
if 'children' in request_info.fields:
parent_block_info.value.setdefault("children", []).append(unicode(block.location))
# the block's data to include in the response
......@@ -592,6 +589,13 @@ class CourseBlocksAndNavigation(ListAPIView):
# block JSON data
self.add_block_json(request_info, block_info)
# multi-device support
if 'multi_device' in request_info.fields:
block_info.value['multi_device'] = block_info.block.has_support(
getattr(block_info.block, 'student_view', None),
'multi_device'
)
# additional fields
self.add_additional_fields(request_info, block_info)
......@@ -667,7 +671,6 @@ class CourseBlocksAndNavigation(ListAPIView):
FIELD_MAP = {
'graded': BlockApiField(block_field_name='graded', api_field_default=False),
'format': BlockApiField(block_field_name='format', api_field_default=None),
'responsive_ui': BlockApiField(block_field_name='has_responsive_ui', api_field_default=False),
}
def add_additional_fields(self, request_info, block_info):
......
......@@ -34,7 +34,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c
git+https://github.com/edx/rfc6266.git@v0.0.5-edx#egg=rfc6266==0.0.5-edx
# Our libraries:
-e git+https://github.com/edx/XBlock.git@017b46b80e712b1318379912257cf1cc1b68eb0e#egg=XBlock
-e git+https://github.com/edx/XBlock.git@d1ff8cf31a9b94916ce06ba06d4176bd72e15768#egg=XBlock
-e git+https://github.com/edx/codejail.git@6b17c33a89bef0ac510926b1d7fea2748b73aadd#egg=codejail
-e git+https://github.com/edx/js-test-tool.git@v0.1.6#egg=js_test_tool
-e git+https://github.com/edx/event-tracking.git@0.2.0#egg=event-tracking
......
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