Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
5a44f391
Commit
5a44f391
authored
Jul 09, 2015
by
Nimisha Asthagiri
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MA-977 Use "supports" decorator on XBlocks for multi-device support
parent
56da2852
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
69 additions
and
36 deletions
+69
-36
common/lib/capa/capa/capa_problem.py
+5
-3
common/lib/capa/capa/responsetypes.py
+7
-7
common/lib/xmodule/xmodule/capa_module.py
+11
-7
common/lib/xmodule/xmodule/html_module.py
+3
-2
lms/djangoapps/course_structure_api/v0/tests.py
+27
-4
lms/djangoapps/course_structure_api/v0/views.py
+15
-12
requirements/edx/github.txt
+1
-1
No files found.
common/lib/capa/capa/capa_problem.py
View file @
5a44f391
...
@@ -575,11 +575,13 @@ class LoncapaProblem(object):
...
@@ -575,11 +575,13 @@ class LoncapaProblem(object):
return
{}
return
{}
@property
@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 ========
# ======= Private Methods Below ========
...
...
common/lib/capa/capa/responsetypes.py
View file @
5a44f391
...
@@ -145,9 +145,9 @@ class LoncapaResponse(object):
...
@@ -145,9 +145,9 @@ class LoncapaResponse(object):
required_attributes
=
[]
required_attributes
=
[]
# Overridable field that specifies whether this capa response type has support for
# 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.
# 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
):
def
__init__
(
self
,
xml
,
inputfields
,
context
,
system
,
capa_module
):
"""
"""
...
@@ -808,7 +808,7 @@ class ChoiceResponse(LoncapaResponse):
...
@@ -808,7 +808,7 @@ class ChoiceResponse(LoncapaResponse):
max_inputfields
=
1
max_inputfields
=
1
allowed_inputfields
=
[
'checkboxgroup'
,
'radiogroup'
]
allowed_inputfields
=
[
'checkboxgroup'
,
'radiogroup'
]
correct_choices
=
None
correct_choices
=
None
has_responsive_ui
=
True
multi_device_support
=
True
def
setup_response
(
self
):
def
setup_response
(
self
):
self
.
assign_choice_names
()
self
.
assign_choice_names
()
...
@@ -993,7 +993,7 @@ class MultipleChoiceResponse(LoncapaResponse):
...
@@ -993,7 +993,7 @@ class MultipleChoiceResponse(LoncapaResponse):
max_inputfields
=
1
max_inputfields
=
1
allowed_inputfields
=
[
'choicegroup'
]
allowed_inputfields
=
[
'choicegroup'
]
correct_choices
=
None
correct_choices
=
None
has_responsive_ui
=
True
multi_device_support
=
True
def
setup_response
(
self
):
def
setup_response
(
self
):
# call secondary setup for MultipleChoice questions, to set name
# call secondary setup for MultipleChoice questions, to set name
...
@@ -1346,7 +1346,7 @@ class OptionResponse(LoncapaResponse):
...
@@ -1346,7 +1346,7 @@ class OptionResponse(LoncapaResponse):
hint_tag
=
'optionhint'
hint_tag
=
'optionhint'
allowed_inputfields
=
[
'optioninput'
]
allowed_inputfields
=
[
'optioninput'
]
answer_fields
=
None
answer_fields
=
None
has_responsive_ui
=
True
multi_device_support
=
True
def
setup_response
(
self
):
def
setup_response
(
self
):
self
.
answer_fields
=
self
.
inputfields
self
.
answer_fields
=
self
.
inputfields
...
@@ -1421,7 +1421,7 @@ class NumericalResponse(LoncapaResponse):
...
@@ -1421,7 +1421,7 @@ class NumericalResponse(LoncapaResponse):
allowed_inputfields
=
[
'textline'
,
'formulaequationinput'
]
allowed_inputfields
=
[
'textline'
,
'formulaequationinput'
]
required_attributes
=
[
'answer'
]
required_attributes
=
[
'answer'
]
max_inputfields
=
1
max_inputfields
=
1
has_responsive_ui
=
True
multi_device_support
=
True
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
correct_answer
=
''
self
.
correct_answer
=
''
...
@@ -1645,7 +1645,7 @@ class StringResponse(LoncapaResponse):
...
@@ -1645,7 +1645,7 @@ class StringResponse(LoncapaResponse):
required_attributes
=
[
'answer'
]
required_attributes
=
[
'answer'
]
max_inputfields
=
1
max_inputfields
=
1
correct_answer
=
[]
correct_answer
=
[]
has_responsive_ui
=
True
multi_device_support
=
True
def
setup_response_backward
(
self
):
def
setup_response_backward
(
self
):
self
.
correct_answer
=
[
self
.
correct_answer
=
[
...
...
common/lib/xmodule/xmodule/capa_module.py
View file @
5a44f391
...
@@ -189,13 +189,6 @@ class CapaDescriptor(CapaFields, RawDescriptor):
...
@@ -189,13 +189,6 @@ class CapaDescriptor(CapaFields, RawDescriptor):
registered_tags
=
responsetypes
.
registry
.
registered_tags
()
registered_tags
=
responsetypes
.
registry
.
registered_tags
()
return
set
([
node
.
tag
for
node
in
tree
.
iter
()
if
node
.
tag
in
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
):
def
index_dictionary
(
self
):
"""
"""
Return dictionary prepared with module content and type for indexing.
Return dictionary prepared with module content and type for indexing.
...
@@ -211,6 +204,17 @@ class CapaDescriptor(CapaFields, RawDescriptor):
...
@@ -211,6 +204,17 @@ class CapaDescriptor(CapaFields, RawDescriptor):
result
.
update
(
index
)
result
.
update
(
index
)
return
result
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
# Proxy to CapaModule for access to any of its attributes
answer_available
=
module_attr
(
'answer_available'
)
answer_available
=
module_attr
(
'answer_available'
)
check_button_name
=
module_attr
(
'check_button_name'
)
check_button_name
=
module_attr
(
'check_button_name'
)
...
...
common/lib/xmodule/xmodule/html_module.py
View file @
5a44f391
...
@@ -84,7 +84,9 @@ class HtmlModule(HtmlModuleMixin):
...
@@ -84,7 +84,9 @@ class HtmlModule(HtmlModuleMixin):
"""
"""
Module for putting raw html in a course
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
class
HtmlDescriptor
(
HtmlFields
,
XmlDescriptor
,
EditingDescriptor
):
# pylint: disable=abstract-method
...
@@ -95,7 +97,6 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor): # pylint: d
...
@@ -95,7 +97,6 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor): # pylint: d
module_class
=
HtmlModule
module_class
=
HtmlModule
filename_extension
=
"xml"
filename_extension
=
"xml"
template_dir_name
=
"html"
template_dir_name
=
"html"
has_responsive_ui
=
True
show_in_read_only_mode
=
True
show_in_read_only_mode
=
True
js
=
{
'coffee'
:
[
resource_string
(
__name__
,
'js/src/html/edit.coffee'
)]}
js
=
{
'coffee'
:
[
resource_string
(
__name__
,
'js/src/html/edit.coffee'
)]}
...
...
lms/djangoapps/course_structure_api/v0/tests.py
View file @
5a44f391
...
@@ -80,7 +80,7 @@ class CourseViewTestsMixin(object):
...
@@ -80,7 +80,7 @@ class CourseViewTestsMixin(object):
factory
=
MultipleChoiceResponseXMLFactory
()
factory
=
MultipleChoiceResponseXMLFactory
()
args
=
{
'choices'
:
[
False
,
True
,
False
]}
args
=
{
'choices'
:
[
False
,
True
,
False
]}
problem_xml
=
factory
.
build_xml
(
**
args
)
problem_xml
=
factory
.
build_xml
(
**
args
)
ItemFactory
.
create
(
self
.
problem
=
ItemFactory
.
create
(
category
=
"problem"
,
category
=
"problem"
,
parent_location
=
self
.
sequential
.
location
,
parent_location
=
self
.
sequential
.
location
,
display_name
=
"Problem 1"
,
display_name
=
"Problem 1"
,
...
@@ -94,6 +94,12 @@ class CourseViewTestsMixin(object):
...
@@ -94,6 +94,12 @@ class CourseViewTestsMixin(object):
display_name
=
"Video 1"
,
display_name
=
"Video 1"
,
)
)
self
.
html
=
ItemFactory
.
create
(
category
=
"html"
,
parent_location
=
self
.
sequential
.
location
,
display_name
=
"HTML 1"
,
)
self
.
empty_course
=
CourseFactory
.
create
(
self
.
empty_course
=
CourseFactory
.
create
(
start
=
datetime
(
2014
,
6
,
16
,
14
,
30
),
start
=
datetime
(
2014
,
6
,
16
,
14
,
30
),
end
=
datetime
(
2015
,
1
,
16
),
end
=
datetime
(
2015
,
1
,
16
),
...
@@ -457,7 +463,7 @@ class CourseBlocksOrNavigationTestMixin(CourseDetailTestMixin, CourseViewTestsMi
...
@@ -457,7 +463,7 @@ class CourseBlocksOrNavigationTestMixin(CourseDetailTestMixin, CourseViewTestsMi
blocks
=
response
.
data
[
self
.
block_navigation_view_type
]
blocks
=
response
.
data
[
self
.
block_navigation_view_type
]
# verify number of blocks
# verify number of blocks
self
.
assertEquals
(
len
(
blocks
),
4
)
self
.
assertEquals
(
len
(
blocks
),
5
)
# verify fields in blocks
# verify fields in blocks
for
field
,
block
in
product
(
self
.
block_fields
,
blocks
.
values
()):
for
field
,
block
in
product
(
self
.
block_fields
,
blocks
.
values
()):
...
@@ -519,6 +525,23 @@ class CourseBlocksTestMixin(object):
...
@@ -519,6 +525,23 @@ class CourseBlocksTestMixin(object):
self
.
assertIn
(
'problem'
,
root_block
[
'block_count'
])
self
.
assertIn
(
'problem'
,
root_block
[
'block_count'
])
self
.
assertEquals
(
root_block
[
'block_count'
][
'problem'
],
1
)
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
):
class
CourseNavigationTestMixin
(
object
):
"""
"""
...
@@ -535,7 +558,7 @@ class CourseNavigationTestMixin(object):
...
@@ -535,7 +558,7 @@ class CourseNavigationTestMixin(object):
)
)
root_block
=
response
.
data
[
self
.
block_navigation_view_type
][
unicode
(
self
.
course
.
location
)]
root_block
=
response
.
data
[
self
.
block_navigation_view_type
][
unicode
(
self
.
course
.
location
)]
self
.
assertIn
(
'descendants'
,
root_block
)
self
.
assertIn
(
'descendants'
,
root_block
)
self
.
assertEquals
(
len
(
root_block
[
'descendants'
]),
3
)
self
.
assertEquals
(
len
(
root_block
[
'descendants'
]),
4
)
def
test_depth
(
self
):
def
test_depth
(
self
):
"""
"""
...
@@ -545,7 +568,7 @@ class CourseNavigationTestMixin(object):
...
@@ -545,7 +568,7 @@ class CourseNavigationTestMixin(object):
container_descendants
=
(
container_descendants
=
(
(
self
.
course
.
location
,
1
),
(
self
.
course
.
location
,
1
),
(
self
.
sequential
.
location
,
2
),
(
self
.
sequential
.
location
,
3
),
)
)
for
container_location
,
expected_num_descendants
in
container_descendants
:
for
container_location
,
expected_num_descendants
in
container_descendants
:
block
=
response
.
data
[
self
.
block_navigation_view_type
][
unicode
(
container_location
)]
block
=
response
.
data
[
self
.
block_navigation_view_type
][
unicode
(
container_location
)]
...
...
lms/djangoapps/course_structure_api/v0/views.py
View file @
5a44f391
...
@@ -321,7 +321,7 @@ class CourseBlocksAndNavigation(ListAPIView):
...
@@ -321,7 +321,7 @@ class CourseBlocksAndNavigation(ListAPIView):
GET api/course_structure/v0/courses/{course_id}/blocks+navigation/
GET api/course_structure/v0/courses/{course_id}/blocks+navigation/
&block_count=video
&block_count=video
&block_json={"video":{"profiles":["mobile_low"]}}
&block_json={"video":{"profiles":["mobile_low"]}}
&fields=graded,format,
responsive_ui
&fields=graded,format,
multi_device
**Parameters**:
**Parameters**:
...
@@ -335,9 +335,9 @@ class CourseBlocksAndNavigation(ListAPIView):
...
@@ -335,9 +335,9 @@ class CourseBlocksAndNavigation(ListAPIView):
Example: block_count="video,problem"
Example: block_count="video,problem"
* fields: (list) Indicates which additional fields to return for each block.
* 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
* navigation_depth (integer) Indicates how far deep to traverse into the course hierarchy before bundling
all the descendants.
all the descendants.
...
@@ -387,8 +387,9 @@ class CourseBlocksAndNavigation(ListAPIView):
...
@@ -387,8 +387,9 @@ class CourseBlocksAndNavigation(ListAPIView):
Possible values can be "Homework", "Lab", "Midterm Exam", and "Final Exam".
Possible values can be "Homework", "Lab", "Midterm Exam", and "Final Exam".
Returned only if "format" is included in the "fields" parameter.
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.
* multi_device: (boolean) Whether or not the block's rendering obtained via block_url has support
Returned only if "responsive_ui" is included in the "fields" parameter.
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.
* 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.
Each block contains the following fields. Returned only if using the "navigation" endpoint.
...
@@ -405,7 +406,7 @@ class CourseBlocksAndNavigation(ListAPIView):
...
@@ -405,7 +406,7 @@ class CourseBlocksAndNavigation(ListAPIView):
"""
"""
A class for encapsulating the request information, including what optional fields are requested.
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
):
def
__init__
(
self
,
request
,
course
):
self
.
request
=
request
self
.
request
=
request
...
@@ -417,10 +418,6 @@ class CourseBlocksAndNavigation(ListAPIView):
...
@@ -417,10 +418,6 @@ class CourseBlocksAndNavigation(ListAPIView):
# fields
# fields
self
.
fields
=
set
(
request
.
GET
.
get
(
'fields'
,
self
.
DEFAULT_FIELDS
)
.
split
(
","
))
self
.
fields
=
set
(
request
.
GET
.
get
(
'fields'
,
self
.
DEFAULT_FIELDS
)
.
split
(
","
))
# children
self
.
children
=
'children'
in
self
.
fields
self
.
fields
.
discard
(
'children'
)
# block_count
# block_count
self
.
block_count
=
request
.
GET
.
get
(
'block_count'
,
""
)
self
.
block_count
=
request
.
GET
.
get
(
'block_count'
,
""
)
self
.
block_count
=
(
self
.
block_count
=
(
...
@@ -489,7 +486,7 @@ class CourseBlocksAndNavigation(ListAPIView):
...
@@ -489,7 +486,7 @@ class CourseBlocksAndNavigation(ListAPIView):
self
.
descendants_of_parent
=
parent_block_info
.
descendants_of_self
self
.
descendants_of_parent
=
parent_block_info
.
descendants_of_self
# add ourselves to the parent's children, if requested.
# 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
))
parent_block_info
.
value
.
setdefault
(
"children"
,
[])
.
append
(
unicode
(
block
.
location
))
# the block's data to include in the response
# the block's data to include in the response
...
@@ -592,6 +589,13 @@ class CourseBlocksAndNavigation(ListAPIView):
...
@@ -592,6 +589,13 @@ class CourseBlocksAndNavigation(ListAPIView):
# block JSON data
# block JSON data
self
.
add_block_json
(
request_info
,
block_info
)
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
# additional fields
self
.
add_additional_fields
(
request_info
,
block_info
)
self
.
add_additional_fields
(
request_info
,
block_info
)
...
@@ -667,7 +671,6 @@ class CourseBlocksAndNavigation(ListAPIView):
...
@@ -667,7 +671,6 @@ class CourseBlocksAndNavigation(ListAPIView):
FIELD_MAP
=
{
FIELD_MAP
=
{
'graded'
:
BlockApiField
(
block_field_name
=
'graded'
,
api_field_default
=
False
),
'graded'
:
BlockApiField
(
block_field_name
=
'graded'
,
api_field_default
=
False
),
'format'
:
BlockApiField
(
block_field_name
=
'format'
,
api_field_default
=
None
),
'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
):
def
add_additional_fields
(
self
,
request_info
,
block_info
):
...
...
requirements/edx/github.txt
View file @
5a44f391
...
@@ -34,7 +34,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c
...
@@ -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
git+https://github.com/edx/rfc6266.git@v0.0.5-edx#egg=rfc6266==0.0.5-edx
# Our libraries:
# 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/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/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
-e git+https://github.com/edx/event-tracking.git@0.2.0#egg=event-tracking
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment