Merge pull request #497 from edx/alex/graphical_slider_tool_for_studio
"Graphical Slider Tool" integration into studio
This commit is contained in:
@@ -46,7 +46,12 @@ COMPONENT_TYPES = ['discussion', 'html', 'problem', 'video']
|
||||
|
||||
OPEN_ENDED_COMPONENT_TYPES = ["combinedopenended", "peergrading"]
|
||||
NOTE_COMPONENT_TYPES = ['notes']
|
||||
ADVANCED_COMPONENT_TYPES = ['annotatable', 'word_cloud', 'videoalpha'] + OPEN_ENDED_COMPONENT_TYPES + NOTE_COMPONENT_TYPES
|
||||
ADVANCED_COMPONENT_TYPES = [
|
||||
'annotatable',
|
||||
'word_cloud',
|
||||
'videoalpha',
|
||||
'graphical_slider_tool'
|
||||
] + OPEN_ENDED_COMPONENT_TYPES + NOTE_COMPONENT_TYPES
|
||||
ADVANCED_COMPONENT_CATEGORY = 'advanced'
|
||||
ADVANCED_COMPONENT_POLICY_KEY = 'advanced_modules'
|
||||
|
||||
|
||||
44
common/lib/xmodule/xmodule/css/gst/display.scss
Normal file
44
common/lib/xmodule/xmodule/css/gst/display.scss
Normal file
@@ -0,0 +1,44 @@
|
||||
// In the LMS sliders use built-in styles from jquery-ui-1.8.22.custom.css.
|
||||
// CMS uses its own sliders styles.
|
||||
// These styles we use only to sure, that slider in GST module
|
||||
// will be render correctly (just like a duplication some from jquery-ui-1.8.22.custom.css).
|
||||
// Cause, for example, CMS overwrites many jquery-ui-1.8.22.custom.css styles,
|
||||
// and we must overwrite them again.
|
||||
|
||||
.ui-widget-content {
|
||||
border: 1px solid #dddddd;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.ui-widget {
|
||||
font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl {
|
||||
-moz-border-radius-topleft: 4px;
|
||||
-webkit-border-top-left-radius: 4px;
|
||||
-khtml-border-top-left-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
}
|
||||
|
||||
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr {
|
||||
-moz-border-radius-topright: 4px;
|
||||
-webkit-border-top-right-radius: 4px;
|
||||
-khtml-border-top-right-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl {
|
||||
-moz-border-radius-bottomleft: 4px;
|
||||
-webkit-border-bottom-left-radius: 4px;
|
||||
-khtml-border-bottom-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br {
|
||||
-moz-border-radius-bottomright: 4px;
|
||||
-webkit-border-bottom-right-radius: 4px;
|
||||
-khtml-border-bottom-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
@@ -9,17 +9,16 @@ from lxml import etree
|
||||
from lxml import html
|
||||
import xmltodict
|
||||
|
||||
from xmodule.mako_module import MakoModuleDescriptor
|
||||
from xmodule.editing_module import XMLEditingDescriptor
|
||||
from xmodule.xml_module import XmlDescriptor
|
||||
from xmodule.x_module import XModule
|
||||
from xmodule.stringify import stringify_children
|
||||
from pkg_resources import resource_string
|
||||
from xblock.core import String, Scope
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_RENDER="""
|
||||
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
|
||||
@@ -33,13 +32,19 @@ DEFAULT_RENDER="""
|
||||
</div>
|
||||
<plot style="margin-top:15px;margin-bottom:15px;"/>
|
||||
"""
|
||||
DEFAULT_CONFIGURATION="""
|
||||
|
||||
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>
|
||||
<function color="red">Math.sqrt(r * r / 20 - Math.pow(x-r/2.5, 2)) + r/8</function>
|
||||
<function color="red">-Math.sqrt(r * r / 20 - Math.pow(x-r/2.5, 2)) + r/5.5</function>
|
||||
<function color="red">Math.sqrt(r * r / 20 - Math.pow(x+r/2.5, 2)) + r/8</function>
|
||||
<function color="red">-Math.sqrt(r * r / 20 - Math.pow(x+r/2.5, 2)) + r/5.5</function>
|
||||
<function color="red">-Math.sqrt(r * r / 5 - x * x) - r/5.5</function>
|
||||
</functions>
|
||||
<plot>
|
||||
<xrange>
|
||||
@@ -54,10 +59,13 @@ DEFAULT_CONFIGURATION="""
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class GraphicalSliderToolFields(object):
|
||||
render = String(scope=Scope.content, default=DEFAULT_RENDER)
|
||||
configuration = String(scope=Scope.content, default=DEFAULT_CONFIGURATION)
|
||||
data = String(
|
||||
help="Html contents to display for this module",
|
||||
default='<render>{}</render><configuration>{}</configuration>'.format(
|
||||
DEFAULT_RENDER, DEFAULT_CONFIGURATION),
|
||||
scope=Scope.content
|
||||
)
|
||||
|
||||
|
||||
class GraphicalSliderToolModule(GraphicalSliderToolFields, XModule):
|
||||
@@ -65,40 +73,54 @@ class GraphicalSliderToolModule(GraphicalSliderToolFields, XModule):
|
||||
'''
|
||||
|
||||
js = {
|
||||
'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee')],
|
||||
'js': [
|
||||
# 3rd party libraries used by graphic slider tool.
|
||||
# TODO - where to store them - outside xmodule?
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/gst_main.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/state.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/logme.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/general_methods.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/sliders.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/inputs.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/graph.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/el_output.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/g_label_el_output.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/gst.js')
|
||||
|
||||
]
|
||||
'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee')],
|
||||
'js': [
|
||||
# 3rd party libraries used by graphic slider tool.
|
||||
# TODO - where to store them - outside xmodule?
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/gst_main.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/state.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/logme.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/general_methods.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/sliders.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/inputs.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/graph.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/el_output.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/g_label_el_output.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/gst.js')
|
||||
]
|
||||
}
|
||||
css = {'scss': [resource_string(__name__, 'css/gst/display.scss')]}
|
||||
js_module_name = "GraphicalSliderTool"
|
||||
|
||||
@property
|
||||
def configuration(self):
|
||||
return stringify_children(
|
||||
html.fromstring(self.data).xpath('configuration')[0]
|
||||
)
|
||||
|
||||
@property
|
||||
def render(self):
|
||||
return stringify_children(
|
||||
html.fromstring(self.data).xpath('render')[0]
|
||||
)
|
||||
|
||||
def get_html(self):
|
||||
""" Renders parameters to template. """
|
||||
|
||||
# these 3 will be used in class methods
|
||||
self.html_id = self.location.html_id()
|
||||
self.html_class = self.location.category
|
||||
|
||||
self.configuration_json = self.build_configuration_json()
|
||||
params = {
|
||||
'gst_html': self.substitute_controls(self.render),
|
||||
'element_id': self.html_id,
|
||||
'element_class': self.html_class,
|
||||
'configuration_json': self.configuration_json
|
||||
}
|
||||
'gst_html': self.substitute_controls(self.render),
|
||||
'element_id': self.html_id,
|
||||
'element_class': self.html_class,
|
||||
'configuration_json': self.configuration_json
|
||||
}
|
||||
content = self.system.render_template(
|
||||
'graphical_slider_tool.html', params)
|
||||
'graphical_slider_tool.html', params
|
||||
)
|
||||
return content
|
||||
|
||||
def substitute_controls(self, html_string):
|
||||
@@ -126,9 +148,10 @@ class GraphicalSliderToolModule(GraphicalSliderToolFields, XModule):
|
||||
if plot_el:
|
||||
plot_el = plot_el[0]
|
||||
plot_el.getparent().replace(plot_el, html.fromstring(
|
||||
plot_div.format(element_class=self.html_class,
|
||||
element_id=self.html_id,
|
||||
style=plot_el.get('style', ""))))
|
||||
plot_div.format(
|
||||
element_class=self.html_class,
|
||||
element_id=self.html_id,
|
||||
style=plot_el.get('style', ""))))
|
||||
|
||||
# substitute sliders
|
||||
slider_div = '<div class="{element_class}_slider" \
|
||||
@@ -139,10 +162,11 @@ class GraphicalSliderToolModule(GraphicalSliderToolFields, XModule):
|
||||
slider_els = xml.xpath('//slider')
|
||||
for slider_el in slider_els:
|
||||
slider_el.getparent().replace(slider_el, html.fromstring(
|
||||
slider_div.format(element_class=self.html_class,
|
||||
element_id=self.html_id,
|
||||
var=slider_el.get('var', ""),
|
||||
style=slider_el.get('style', ""))))
|
||||
slider_div.format(
|
||||
element_class=self.html_class,
|
||||
element_id=self.html_id,
|
||||
var=slider_el.get('var', ""),
|
||||
style=slider_el.get('style', ""))))
|
||||
|
||||
# substitute inputs aka textboxes
|
||||
input_div = '<input class="{element_class}_input" \
|
||||
@@ -151,11 +175,12 @@ class GraphicalSliderToolModule(GraphicalSliderToolFields, XModule):
|
||||
input_els = xml.xpath('//textbox')
|
||||
for input_index, input_el in enumerate(input_els):
|
||||
input_el.getparent().replace(input_el, html.fromstring(
|
||||
input_div.format(element_class=self.html_class,
|
||||
element_id=self.html_id,
|
||||
var=input_el.get('var', ""),
|
||||
style=input_el.get('style', ""),
|
||||
input_index=input_index)))
|
||||
input_div.format(
|
||||
element_class=self.html_class,
|
||||
element_id=self.html_id,
|
||||
var=input_el.get('var', ""),
|
||||
style=input_el.get('style', ""),
|
||||
input_index=input_index)))
|
||||
|
||||
return html.tostring(xml)
|
||||
|
||||
@@ -170,11 +195,13 @@ class GraphicalSliderToolModule(GraphicalSliderToolFields, XModule):
|
||||
"""
|
||||
# <root> added for interface compatibility with xmltodict.parse
|
||||
# class added for javascript's part purposes
|
||||
return json.dumps(xmltodict.parse('<root class="' + self.html_class +
|
||||
'">' + self.configuration + '</root>'))
|
||||
root = '<root class="{}">{}</root>'.format(
|
||||
self.html_class,
|
||||
self.configuration)
|
||||
return json.dumps(xmltodict.parse(root))
|
||||
|
||||
|
||||
class GraphicalSliderToolDescriptor(GraphicalSliderToolFields, MakoModuleDescriptor, XmlDescriptor):
|
||||
class GraphicalSliderToolDescriptor(GraphicalSliderToolFields, XMLEditingDescriptor, XmlDescriptor):
|
||||
module_class = GraphicalSliderToolModule
|
||||
|
||||
@classmethod
|
||||
@@ -202,24 +229,14 @@ class GraphicalSliderToolDescriptor(GraphicalSliderToolFields, MakoModuleDescrip
|
||||
exactly one '{0}' tag".format(child))
|
||||
# finished
|
||||
|
||||
def parse(k):
|
||||
"""Assumes that xml_object has child k"""
|
||||
return stringify_children(xml_object.xpath(k)[0])
|
||||
return {
|
||||
'render': parse('render'),
|
||||
'configuration': parse('configuration')
|
||||
}, []
|
||||
'data': stringify_children(xml_object)
|
||||
}, []
|
||||
|
||||
def definition_to_xml(self, resource_fs):
|
||||
'''Return an xml element representing this definition.'''
|
||||
xml_object = etree.Element('graphical_slider_tool')
|
||||
|
||||
def add_child(k):
|
||||
child_str = '<{tag}>{body}</{tag}>'.format(tag=k, body=getattr(self, k))
|
||||
child_node = etree.fromstring(child_str)
|
||||
xml_object.append(child_node)
|
||||
|
||||
for child in ['render', 'configuration']:
|
||||
add_child(child)
|
||||
|
||||
data = '<{tag}>{body}</{tag}>'.format(
|
||||
tag='graphical_slider_tool',
|
||||
body=self.data)
|
||||
xml_object = etree.fromstring(data)
|
||||
return xml_object
|
||||
|
||||
@@ -445,7 +445,7 @@ class ImportTestCase(BaseCourseTestCase):
|
||||
render_string_from_sample_gst_xml = """
|
||||
<slider var="a" style="width:400px;float:left;"/>\
|
||||
<plot style="margin-top:15px;margin-bottom:15px;"/>""".strip()
|
||||
self.assertEqual(gst_sample.render, render_string_from_sample_gst_xml)
|
||||
self.assertIn(render_string_from_sample_gst_xml, gst_sample.data)
|
||||
|
||||
def test_word_cloud_import(self):
|
||||
modulestore = XMLModuleStore(DATA_DIR, course_dirs=['word_cloud'])
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<script type="text/javascript" src="${static.url('js/vendor/jquery.cookie.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/jquery.qtip.min.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/jquery.scrollTo-1.4.2-min.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.js')}"></script>
|
||||
|
||||
## codemirror
|
||||
<link rel="stylesheet" href="${static.url('js/vendor/CodeMirror/codemirror.css')}" type="text/css" media="all" />
|
||||
|
||||
Reference in New Issue
Block a user