Merge pull request #25618 from open-craft/symbolist/convert-annotatable-xmodule-to-xblock

[BD-4] Convert Annotatable XModule to XBlock. [SE-3640]
This commit is contained in:
David Ormsbee
2020-12-02 13:37:20 -05:00
committed by GitHub
6 changed files with 97 additions and 36 deletions

View File

@@ -20,13 +20,13 @@ XMODULES = [
"videodev = xmodule.backcompat_module:TranslateCustomTagDescriptor",
"videosequence = xmodule.seq_module:SequenceDescriptor",
"custom_tag_template = xmodule.raw_module:RawDescriptor",
"annotatable = xmodule.annotatable_module:AnnotatableDescriptor",
"hidden = xmodule.hidden_module:HiddenDescriptor",
"raw = xmodule.raw_module:RawDescriptor",
"lti = xmodule.lti_module:LTIDescriptor",
]
XBLOCKS = [
"about = xmodule.html_module:AboutBlock",
"annotatable = xmodule.annotatable_module:AnnotatableBlock",
"conditional = xmodule.conditional_module:ConditionalBlock",
"course_info = xmodule.html_module:CourseInfoBlock",
"html = xmodule.html_module:HtmlBlock",

View File

@@ -5,11 +5,22 @@ import textwrap
from lxml import etree
from pkg_resources import resource_string
from web_fragments.fragment import Fragment
from xblock.fields import Scope, String
from openedx.core.djangolib.markup import HTML, Text
from xmodule.raw_module import RawDescriptor
from xmodule.x_module import XModule
from xmodule.editing_module import EditingMixin
from xmodule.raw_module import RawMixin
from xmodule.util.xmodule_django import add_webpack_to_fragment
from xmodule.xml_module import XmlMixin
from xmodule.x_module import (
HTMLSnippet,
ResourceTemplates,
shim_xmodule_js,
XModuleMixin,
XModuleDescriptorToXBlockMixin,
XModuleToXBlockMixin,
)
log = logging.getLogger(__name__)
@@ -18,7 +29,20 @@ log = logging.getLogger(__name__)
_ = lambda text: text
class AnnotatableFields(object):
class AnnotatableBlock(
RawMixin,
XmlMixin,
EditingMixin,
XModuleDescriptorToXBlockMixin,
XModuleToXBlockMixin,
HTMLSnippet,
ResourceTemplates,
XModuleMixin,
):
"""
Annotatable XBlock.
"""
data = String(
help=_("XML data for the annotation"),
scope=Scope.content,
@@ -47,29 +71,41 @@ class AnnotatableFields(object):
default=_('Annotation'),
)
uses_xmodule_styles_setup = True
class AnnotatableModule(AnnotatableFields, XModule):
js = {
preview_view_js = {
'js': [
resource_string(__name__, 'js/src/html/display.js'),
resource_string(__name__, 'js/src/annotatable/display.js'),
resource_string(__name__, 'js/src/javascript_loader.js'),
resource_string(__name__, 'js/src/collapsible.js'),
]
],
'xmodule_js': resource_string(__name__, 'js/src/xmodule.js'),
}
js_module_name = "Annotatable"
css = {'scss': [resource_string(__name__, 'css/annotatable/display.scss')]}
preview_view_css = {
'scss': [
resource_string(__name__, 'css/annotatable/display.scss'),
],
}
studio_view_js = {
'js': [
resource_string(__name__, 'js/src/raw/edit/xml.js'),
],
'xmodule_js': resource_string(__name__, 'js/src/xmodule.js'),
}
studio_view_css = {
'scss': [
resource_string(__name__, 'css/codemirror/codemirror.scss'),
],
}
studio_js_module_name = "XMLEditingDescriptor"
mako_template = "widgets/raw-edit.html"
icon_class = 'annotatable'
resources_dir = None
def __init__(self, *args, **kwargs):
super(AnnotatableModule, self).__init__(*args, **kwargs)
xmltree = etree.fromstring(self.data)
self.instructions = self._extract_instructions(xmltree)
self.content = etree.tostring(xmltree, encoding='unicode')
self.element_id = self.location.html_id()
self.highlight_colors = ['yellow', 'orange', 'purple', 'blue', 'green']
HIGHLIGHT_COLORS = ['yellow', 'orange', 'purple', 'blue', 'green']
def _get_annotation_class_attr(self, index, el):
""" Returns a dict with the CSS class attribute to set on the annotation
@@ -82,7 +118,7 @@ class AnnotatableModule(AnnotatableFields, XModule):
color = el.get(highlight_key)
if color is not None:
if color in self.highlight_colors:
if color in self.HIGHLIGHT_COLORS:
cls.append('highlight-' + color)
attr['_delete'] = highlight_key
attr['value'] = ' '.join(cls)
@@ -127,7 +163,11 @@ class AnnotatableModule(AnnotatableFields, XModule):
def _render_content(self):
""" Renders annotatable content with annotation spans and returns HTML. """
xmltree = etree.fromstring(self.content)
xmltree = etree.fromstring(self.data)
content = etree.tostring(xmltree, encoding='unicode')
xmltree = etree.fromstring(content)
xmltree.tag = 'div'
if 'display_name' in xmltree.attrib:
del xmltree.attrib['display_name']
@@ -150,17 +190,37 @@ class AnnotatableModule(AnnotatableFields, XModule):
def get_html(self):
""" Renders parameters to template. """
xmltree = etree.fromstring(self.data)
instructions = self._extract_instructions(xmltree)
context = {
'display_name': self.display_name_with_default_escaped,
'element_id': self.element_id,
'instructions_html': self.instructions,
'display_name': self.display_name_with_default,
'element_id': self.location.html_id(),
'instructions_html': instructions,
'content_html': self._render_content()
}
return self.system.render_template('annotatable.html', context)
def student_view(self, context):
"""
Renders the output that a student will see.
"""
fragment = Fragment()
fragment.add_content(self.get_html())
add_webpack_to_fragment(fragment, 'AnnotatableBlockPreview')
shim_xmodule_js(fragment, 'Annotatable')
class AnnotatableDescriptor(AnnotatableFields, RawDescriptor):
module_class = AnnotatableModule
mako_template = "widgets/raw-edit.html"
resources_dir = None
return fragment
def studio_view(self, _context):
"""
Return the studio view.
"""
fragment = Fragment(
self.system.render_template(self.mako_template, self.get_context())
)
add_webpack_to_fragment(fragment, 'AnnotatableBlockStudio')
shim_xmodule_js(fragment, self.studio_js_module_name)
return fragment

View File

@@ -20,6 +20,7 @@ import six
from docopt import docopt
from path import Path as path
from xmodule.annotatable_module import AnnotatableBlock
from xmodule.capa_module import ProblemBlock
from xmodule.conditional_module import ConditionalBlock
from xmodule.html_module import AboutBlock, CourseInfoBlock, HtmlBlock, StaticTabBlock
@@ -67,6 +68,7 @@ class VideoBlock(HTMLSnippet):
# Should only be used for XModules being converted to XBlocks.
XBLOCK_CLASSES = [
AboutBlock,
AnnotatableBlock,
ConditionalBlock,
CourseInfoBlock,
HtmlBlock,

View File

@@ -9,12 +9,12 @@ from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds
from xmodule.annotatable_module import AnnotatableModule
from xmodule.annotatable_module import AnnotatableBlock
from . import get_test_system
class AnnotatableModuleTestCase(unittest.TestCase):
class AnnotatableBlockTestCase(unittest.TestCase):
sample_xml = '''
<annotatable display_name="Iliad">
<instructions>Read the text.</instructions>
@@ -33,9 +33,8 @@ class AnnotatableModuleTestCase(unittest.TestCase):
'''
def setUp(self):
super(AnnotatableModuleTestCase, self).setUp()
self.annotatable = AnnotatableModule(
Mock(),
super().setUp()
self.annotatable = AnnotatableBlock(
get_test_system(),
DictFieldData({'data': self.sample_xml}),
ScopeIds(None, None, None, BlockUsageLocator(CourseLocator('org', 'course', 'run'), 'category', 'name'))
@@ -68,7 +67,7 @@ class AnnotatableModuleTestCase(unittest.TestCase):
def test_annotation_class_attr_with_valid_highlight(self):
xml = '<annotation title="x" body="y" problem="0" highlight="{highlight}">test</annotation>'
for color in self.annotatable.highlight_colors:
for color in self.annotatable.HIGHLIGHT_COLORS:
el = etree.fromstring(xml.format(highlight=color))
value = 'annotatable-span highlight highlight-{highlight}'.format(highlight=color)

View File

@@ -30,7 +30,7 @@ from xblock.core import XBlock
from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds
from xmodule.annotatable_module import AnnotatableDescriptor
from xmodule.annotatable_module import AnnotatableBlock
from xmodule.conditional_module import ConditionalBlock
from xmodule.course_module import CourseDescriptor
from xmodule.html_module import HtmlBlock
@@ -55,7 +55,7 @@ from xmodule.x_module import (
# to a list of sample field values to test with.
# TODO: Add more types of sample data
LEAF_XMODULES = {
AnnotatableDescriptor: [{}],
AnnotatableBlock: [{}],
HtmlBlock: [{}],
PollDescriptor: [{'display_name': 'Poll Display Name'}],
WordCloudBlock: [{}],

View File

@@ -3,7 +3,7 @@
<div class="annotatable-wrapper">
<div class="annotatable-header">
% if display_name is not UNDEFINED and display_name is not None:
<h3 class="hd hd-3 annotatable-title">${display_name}</h3>
<h3 class="hd hd-3 annotatable-title">${display_name | h}</h3>
% endif
</div>