Files
edx-platform/common/lib/xmodule/xmodule/gst_module.py
Alexander Kryklia e1588ff903 fix regexp
2013-01-15 12:55:18 +02:00

243 lines
10 KiB
Python

"""
Graphical slider tool module is ungraded xmodule used by students to
understand functional dependencies.
"""
import json
import logging
from lxml import etree
import xmltodict
import re
from xmodule.mako_module import MakoModuleDescriptor
from xmodule.xml_module import XmlDescriptor
from xmodule.x_module import XModule
from xmodule.stringify import stringify_children
from pkg_resources import resource_string
log = logging.getLogger("mitx.common.lib.gst_module")
class GraphicalSliderToolModule(XModule):
''' Graphical-Slider-Tool Module
'''
js = {
'js': [
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/gst.js')
]
}
js_module_name = "GraphicalSliderTool"
def __init__(self, system, location, definition, descriptor, instance_state=None,
shared_state=None, **kwargs):
"""
For XML file format please look at documentation.
"""
XModule.__init__(self, system, location, definition, descriptor,
instance_state, shared_state, **kwargs)
def get_html(self):
self.get_configuration()
self.html_id = self.location.html_id()
self.html_class = self.location.category
gst_html = self.substitute_controls(self.definition['render'].strip())
# import ipdb; ipdb.set_trace()
params = {
'gst_html': gst_html,
'element_id': self.html_id,
'element_class': self.html_class,
'configuration_json': self.configuration_json
}
self.content = (self.system.render_template(
'graphical_slider_tool.html', params))
# import ipdb; ipdb.set_trace()
return self.content
def substitute_controls(self, html_string):
""" Substitue control element via their divs.
Simple variant: slider and plot controls are not inside any tag.
"""
#substitute plot
plot_div = '<div class="{element_class}_plot" id="{element_id}_plot" \
style="{style}"></div>'
# extract css style from plot
plot_def = re.search(r'\<plot[^\$]*\>', html_string)
if plot_def:
plot_def = plot_def.group()
style = re.search(r'(?=[^\$]*style\=[\"\'](.*)[\"\'])', plot_def,
flags=re.UNICODE | re.DOTALL)
if style:
style = style.groups()[0]
else: # no style parameter
style = ''
replacement = plot_div.format(element_class=self.html_class,
element_id=self.html_id,
style=style)
html_string = re.sub(r'\$plot[^\$]*\$', replacement, html_string,
flags=re.UNICODE)
# get variables
if json.loads(self.configuration_json)['root'].get('parameters'):
variables = json.loads(self.configuration_json)['root']['parameters']['param']
if type(variables) == dict:
variables = [variables]
variables = [x['@var'] for x in variables]
else:
return html_string
# if variables[0] == 'v':
# import ipdb; ipdb.set_trace()
#substitute sliders
slider_div = '<div class="{element_class}_slider" \
id="{element_id}_slider_{var}" \
data-var="{var}" style="{style}">\
</div>'
for var in variables:
# find $slider var='var' ... $
instances = re.findall(r'\$slider\s+(?=[^\$]*var\=[\"\']' + var + '[\"\'])' \
+ r'[^\$]*\$', html_string, flags=re.UNICODE | re.DOTALL)
if instances: # if presented, only one slider per var
slider_def = instances[0] # get $slider var='var' ... $ string
# extract var for proper style extraction further
var_substring = re.search(r'(var\=[\"\']' + var + r'[\"\'])',
slider_def).group()
slider_def = slider_def.replace(var_substring, '')
# get style
style = re.search(r'(?=[^\$]*style\=[\"\'](.*)[\"\'])', slider_def,
flags=re.UNICODE | re.DOTALL)
if style:
style = style.groups()[0]
else: # no style parameter
style = ''
# substitute parameters to slider div
replacement = slider_div.format(element_class=self.html_class,
element_id=self.html_id,
var=var, style=style)
# subsitute $slider var='var' ... $ in html_srting to proper
# html div element
html_string = re.sub(r'\$slider\s+(?=[^\$]*var\=[\"\'](' + \
var + ')[\"\'])' + r'[^\$]*\$',
replacement, html_string, flags=re.UNICODE | re.DOTALL)
# substitute inputs if we have them
input_el = '<input class="{element_class}_input" \
id="{element_id}_input_{var}_{input_index}" \
data-var="{var}" style="{style}" \
data-el_readonly="{readonly}"/>'
for var in variables:
input_index = 0 # make multiple inputs for same variable have
# different id
instances = re.findall(r'\$input\s+(?=[^\$]*var\=[\"\']' + var + '[\"\'])' \
+ r'[^\$]*\$', html_string, flags=re.UNICODE | re.DOTALL)
# import ipdb; ipdb.set_trace()
for input_def in instances: # for multiple inputs per var
input_index += 1
# extract var and readonly before style!
# import ipdb; ipdb.set_trace()
var_substring = re.search(r'(var\=[\"\']' + var + r'[\"\'])',
input_def).group()
input_def = input_def.replace(var_substring, '')
readonly = re.search(r'(?=[^\$]*(readonly\=[\"\'](\w+)[\"\']))',
input_def, flags=re.UNICODE | re.DOTALL)
if readonly:
input_def = input_def.replace(readonly.groups()[0], '')
readonly = readonly.groups()[1]
else:
readonly = ''
style = re.search(r'(?=[^\$]*style\=[\"\'](.*)[\"\'])', input_def,
flags=re.UNICODE | re.DOTALL)
if style:
style = style.groups()[0]
else:
style = ''
# import ipdb; ipdb.set_trace()
replacement = input_el.format(element_class=self.html_class,
element_id=self.html_id,
var=var, readonly=readonly, style=style,
input_index=input_index)
# import ipdb; ipdb.set_trace()
html_string = re.sub(r'\$input\s+(?=[^\$]*var\=[\"\'](' + \
var + ')[\"\'])' + r'[^\$]*\$',
replacement, html_string, count=1, flags=re.UNICODE | re.DOTALL)
return html_string
def get_configuration(self):
"""Parse self.definition['configuration'] and transfer it to javascript
via json.
"""
# root added for interface compatibility with xmltodict.parse
# import ipdb; ipdb.set_trace()
self.configuration_json = json.dumps(
xmltodict.parse('<root class="' + self.location.category + '">' +
stringify_children(self.definition['configuration'])
+ '</root>'))
return self.configuration_json
class GraphicalSliderToolDescriptor(MakoModuleDescriptor, XmlDescriptor):
module_class = GraphicalSliderToolModule
template_dir_name = 'graphical_slider_tool'
@classmethod
def definition_from_xml(cls, xml_object, system):
"""
Pull out the data into dictionary.
Args:
xml_object: xml from file.
Returns:
dict
"""
# check for presense of required tags in xml
expected_children_level_0 = ['render', 'configuration']
for child in expected_children_level_0:
if len(xml_object.xpath(child)) != 1:
raise ValueError("Graphical Slider Tool definition must include \
exactly one '{0}' tag".format(child))
expected_children_level_1 = ['functions']
for child in expected_children_level_1:
if len(xml_object.xpath('configuration')[0].xpath(child)) != 1:
raise ValueError("Graphical Slider Tool definition must include \
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': xml_object.xpath('configuration')[0]
}
def definition_to_xml(self, resource_fs):
'''Return an xml element representing this definition.
Not implemented'''
# import ipdb; ipdb.set_trace()
xml_object = etree.Element('gst')
def add_child(k):
# child_str = '<{tag}>{body}</{tag}>'.format(tag=k, body=self.definition[k])
child_str = child.export_to_xml(resource_fs)
child_node = etree.fromstring(child_str)
xml_object.append(child_node)
for child in self.get_children():
add_child(child)
return xml_object