Commit dd338c17 by Calen Pennington

Merge pull request #477 from edx/cale/xmodule-student-vieww

Add an XBlock student_view to XModule
parents 8e125906 3774ea15
......@@ -19,10 +19,45 @@ from xblock.core import String, Scope
log = logging.getLogger(__name__)
DEFAULT_RENDER="""
<h2>Graphic slider tool: Dynamic range and implicit functions.</h2>
<p>You can make the range of the x axis (but not ticks of x axis) of
functions depend on a parameter value. This can be useful when the
function domain needs to be variable.</p>
<p>Implicit functions like a circle can be plotted as 2 separate
functions of the same color.</p>
<div style="height:50px;">
<slider var='r' style="width:400px;float:left;"/>
<textbox var='r' style="float:left;width:60px;margin-left:15px;"/>
</div>
<plot style="margin-top:15px;margin-bottom:15px;"/>
"""
DEFAULT_CONFIGURATION="""
<parameters>
<param var="r" min="5" max="25" step="0.5" initial="12.5" />
</parameters>
<functions>
<function color="red">Math.sqrt(r * r - x * x)</function>
<function color="red">-Math.sqrt(r * r - x * x)</function>
</functions>
<plot>
<xrange>
<!-- dynamic range -->
<min>-r</min>
<max>r</max>
</xrange>
<num_points>1000</num_points>
<xticks>-30, 6, 30</xticks>
<yticks>-30, 6, 30</yticks>
</plot>
"""
class GraphicalSliderToolFields(object):
render = String(scope=Scope.content)
configuration = String(scope=Scope.content)
render = String(scope=Scope.content, default=DEFAULT_RENDER)
configuration = String(scope=Scope.content, default=DEFAULT_CONFIGURATION)
class GraphicalSliderToolModule(GraphicalSliderToolFields, XModule):
......
......@@ -27,7 +27,7 @@ class HtmlFields(object):
# use display_name_with_default for those
default="Blank HTML Page"
)
data = String(help="Html contents to display for this module", default="", scope=Scope.content)
data = String(help="Html contents to display for this module", default=u"", scope=Scope.content)
source_code = String(help="Source code for LaTeX documents. This feature is not well-supported.", scope=Scope.settings)
......
from .x_module import XModule, XModuleDescriptor
class ModuleDescriptor(XModuleDescriptor):
pass
class Module(XModule):
def get_html(self):
return '<input type="hidden" class="schematic" name="{item_id}" height="480" width="640">'.format(item_id=self.item_id)
"""
Tests for the wrapping layer that provides the XBlock API using XModule/Descriptor
functionality
"""
from nose.tools import assert_equal
from unittest.case import SkipTest
from mock import Mock
from xmodule.annotatable_module import AnnotatableDescriptor
from xmodule.capa_module import CapaDescriptor
from xmodule.course_module import CourseDescriptor
from xmodule.combined_open_ended_module import CombinedOpenEndedDescriptor
from xmodule.discussion_module import DiscussionDescriptor
from xmodule.gst_module import GraphicalSliderToolDescriptor
from xmodule.html_module import HtmlDescriptor
from xmodule.peer_grading_module import PeerGradingDescriptor
from xmodule.poll_module import PollDescriptor
from xmodule.video_module import VideoDescriptor
from xmodule.word_cloud_module import WordCloudDescriptor
from xmodule.crowdsource_hinter import CrowdsourceHinterDescriptor
from xmodule.videoalpha_module import VideoAlphaDescriptor
from xmodule.seq_module import SequenceDescriptor
from xmodule.conditional_module import ConditionalDescriptor
from xmodule.randomize_module import RandomizeDescriptor
from xmodule.vertical_module import VerticalDescriptor
from xmodule.wrapper_module import WrapperDescriptor
LEAF_XMODULES = (
AnnotatableDescriptor,
CapaDescriptor,
CombinedOpenEndedDescriptor,
DiscussionDescriptor,
GraphicalSliderToolDescriptor,
HtmlDescriptor,
PeerGradingDescriptor,
PollDescriptor,
VideoDescriptor,
# This is being excluded because it has dependencies on django
#VideoAlphaDescriptor,
WordCloudDescriptor,
)
CONTAINER_XMODULES = (
CrowdsourceHinterDescriptor,
CourseDescriptor,
SequenceDescriptor,
ConditionalDescriptor,
RandomizeDescriptor,
VerticalDescriptor,
WrapperDescriptor,
CourseDescriptor,
)
class TestXBlockWrapper(object):
@property
def leaf_module_runtime(self):
runtime = Mock()
runtime.render_template = lambda *args, **kwargs: unicode((args, kwargs))
runtime.anonymous_student_id = 'anonymous_student_id'
runtime.open_ended_grading_interface = {}
runtime.seed = 5
runtime.get = lambda x: getattr(runtime, x)
runtime.position = 2
runtime.ajax_url = 'ajax_url'
runtime.xblock_model_data = lambda d: d._model_data
return runtime
@property
def leaf_descriptor_runtime(self):
runtime = Mock()
runtime.render_template = lambda *args, **kwargs: unicode((args, kwargs))
return runtime
def leaf_descriptor(self, descriptor_cls):
return descriptor_cls(
self.leaf_descriptor_runtime,
{'location': 'i4x://org/course/catagory/name'}
)
def leaf_module(self, descriptor_cls):
return self.leaf_descriptor(descriptor_cls).xmodule(self.leaf_module_runtime)
def container_module_runtime(self, depth):
runtime = self.leaf_module_runtime
if depth == 0:
runtime.get_module.side_effect = lambda x: self.leaf_module(HtmlDescriptor)
else:
runtime.get_module.side_effect = lambda x: self.container_module(VerticalDescriptor, depth-1)
return runtime
@property
def container_descriptor_runtime(self):
runtime = Mock()
runtime.render_template = lambda *args, **kwargs: unicode((args, kwargs))
return runtime
def container_descriptor(self, descriptor_cls):
return descriptor_cls(
self.container_descriptor_runtime,
{
'location': 'i4x://org/course/catagory/name',
'children': range(3)
}
)
def container_module(self, descriptor_cls, depth):
return self.container_descriptor(descriptor_cls).xmodule(self.container_module_runtime(depth))
class TestStudentView(TestXBlockWrapper):
# Test that for all of the leaf XModule Descriptors,
# the student_view wrapper returns the same thing in its content
# as get_html returns
def test_student_view_leaf_node(self):
for descriptor_cls in LEAF_XMODULES:
yield self.check_student_view_leaf_node, descriptor_cls
# Check that when an xmodule is instantiated from descriptor_cls
# it generates the same thing from student_view that it does from get_html
def check_student_view_leaf_node(self, descriptor_cls):
xmodule = self.leaf_module(descriptor_cls)
assert_equal(xmodule.get_html(), xmodule.student_view(None).content)
# Test that for all container XModule Descriptors,
# their corresponding XModule renders the same thing using student_view
# as it does using get_html, under the following conditions:
# a) All of its descendents are xmodules
# b) Some of its descendents are xmodules and some are xblocks
# c) All of its descendents are xblocks
def test_student_view_container_node(self):
for descriptor_cls in CONTAINER_XMODULES:
yield self.check_student_view_container_node_xmodules_only, descriptor_cls
yield self.check_student_view_container_node_mixed, descriptor_cls
yield self.check_student_view_container_node_xblocks_only, descriptor_cls
# Check that when an xmodule is generated from descriptor_cls
# with only xmodule children, it generates the same html from student_view
# as it does using get_html
def check_student_view_container_node_xmodules_only(self, descriptor_cls):
xmodule = self.container_module(descriptor_cls, 2)
assert_equal(xmodule.get_html(), xmodule.student_view(None).content)
# Check that when an xmodule is generated from descriptor_cls
# with mixed xmodule and xblock children, it generates the same html from student_view
# as it does using get_html
def check_student_view_container_node_mixed(self, descriptor_cls):
raise SkipTest("XBlock support in XDescriptor not yet fully implemented")
# Check that when an xmodule is generated from descriptor_cls
# with only xblock children, it generates the same html from student_view
# as it does using get_html
def check_student_view_container_node_xblocks_only(self, descriptor_cls):
raise SkipTest("XBlock support in XModules not yet fully implemented")
......@@ -11,6 +11,7 @@ from xmodule.modulestore import inheritance, Location
from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecificationError, InvalidLocationError
from xblock.core import XBlock, Scope, String, Integer, Float, ModelType
from xblock.fragment import Fragment
from xmodule.modulestore.locator import BlockUsageLocator
log = logging.getLogger(__name__)
......@@ -314,6 +315,19 @@ class XModule(XModuleFields, HTMLSnippet, XBlock):
return ""
# ~~~~~~~~~~~~~~~ XBlock API Wrappers ~~~~~~~~~~~~~~~~
def student_view(self, context):
"""
Return a fragment with the html from this XModule
Doesn't yet add any of the javascript to the fragment, nor the css.
Also doesn't expect any javascript binding, yet.
Makes no use of the context parameter
"""
return Fragment(self.get_html())
def policy_key(location):
"""
Get the key for a location in a policy file. (Since the policy file is
......@@ -906,8 +920,8 @@ class ModuleSystem(object):
publish(event) - A function that allows XModules to publish events (such as grade changes)
xblock_model_data - A dict-like object containing the all data available to this
xblock
xblock_model_data - A function that constructs a model_data for an xblock from its
corresponding descriptor
cache - A cache object with two methods:
.get(key) returns an object from the cache or None.
......
......@@ -109,13 +109,6 @@ Progress
:members:
:show-inheritance:
Schematic
=========
.. automodule:: xmodule.schematic_module
:members:
:show-inheritance:
Sequence
========
......
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