Merge pull request #24218 from open-craft/symbolist/word-cloud-block
[BD-4] Convert WordCloud XModule into WordCloud XBlock. [SE-2722]
This commit is contained in:
@@ -24,7 +24,6 @@ XMODULES = [
|
||||
"videosequence = xmodule.seq_module:SequenceDescriptor",
|
||||
"custom_tag_template = xmodule.raw_module:RawDescriptor",
|
||||
"annotatable = xmodule.annotatable_module:AnnotatableDescriptor",
|
||||
"word_cloud = xmodule.word_cloud_module:WordCloudDescriptor",
|
||||
"hidden = xmodule.hidden_module:HiddenDescriptor",
|
||||
"raw = xmodule.raw_module:RawDescriptor",
|
||||
"lti = xmodule.lti_module:LTIDescriptor",
|
||||
@@ -40,6 +39,7 @@ XBLOCKS = [
|
||||
"vertical = xmodule.vertical_block:VerticalBlock",
|
||||
"video = xmodule.video_module:VideoBlock",
|
||||
"videoalpha = xmodule.video_module:VideoBlock",
|
||||
"word_cloud = xmodule.word_cloud_module:WordCloudBlock",
|
||||
"wrapper = xmodule.wrapper_module:WrapperBlock",
|
||||
]
|
||||
XBLOCKS_ASIDES = [
|
||||
|
||||
@@ -22,6 +22,7 @@ from path import Path as path
|
||||
|
||||
from xmodule.capa_module import ProblemBlock
|
||||
from xmodule.html_module import AboutBlock, CourseInfoBlock, HtmlBlock, StaticTabBlock
|
||||
from xmodule.word_cloud_module import WordCloudBlock
|
||||
from xmodule.x_module import XModuleDescriptor, HTMLSnippet
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@@ -69,6 +70,7 @@ XBLOCK_CLASSES = [
|
||||
ProblemBlock,
|
||||
StaticTabBlock,
|
||||
VideoBlock,
|
||||
WordCloudBlock,
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -1,35 +1,89 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Test for Word cloud Xmodule functional logic."""
|
||||
|
||||
import json
|
||||
|
||||
from django.test import TestCase
|
||||
from fs.memoryfs import MemoryFS
|
||||
from lxml import etree
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from webob.multidict import MultiDict
|
||||
from xblock.field_data import DictFieldData
|
||||
|
||||
from xmodule.word_cloud_module import WordCloudDescriptor
|
||||
|
||||
from . import LogicTest
|
||||
from xmodule.word_cloud_module import WordCloudBlock
|
||||
from . import get_test_descriptor_system, get_test_system
|
||||
|
||||
|
||||
class WordCloudModuleTest(LogicTest):
|
||||
"""Logic tests for Word Cloud Xmodule."""
|
||||
descriptor_class = WordCloudDescriptor
|
||||
class WordCloudBlockTest(TestCase):
|
||||
"""
|
||||
Logic tests for Word Cloud XBlock.
|
||||
"""
|
||||
|
||||
raw_field_data = {
|
||||
'all_words': {'cat': 10, 'dog': 5, 'mom': 1, 'dad': 2},
|
||||
'top_words': {'cat': 10, 'dog': 5, 'dad': 2},
|
||||
'submitted': False
|
||||
}
|
||||
|
||||
def test_xml_import_export_cycle(self):
|
||||
"""
|
||||
Test the import export cycle.
|
||||
"""
|
||||
|
||||
runtime = get_test_descriptor_system()
|
||||
runtime.export_fs = MemoryFS()
|
||||
|
||||
original_xml = (
|
||||
'<word_cloud display_name="Favorite Fruits" display_student_percents="false" '
|
||||
'instructions="What are your favorite fruits?" num_inputs="3" num_top_words="100"/>\n'
|
||||
)
|
||||
|
||||
olx_element = etree.fromstring(original_xml)
|
||||
id_generator = Mock()
|
||||
block = WordCloudBlock.parse_xml(olx_element, runtime, None, id_generator)
|
||||
block.location = BlockUsageLocator(
|
||||
CourseLocator('org', 'course', 'run', branch='revision'), 'word_cloud', 'block_id'
|
||||
)
|
||||
|
||||
self.assertEqual(block.display_name, 'Favorite Fruits')
|
||||
self.assertFalse(block.display_student_percents)
|
||||
self.assertEqual(block.instructions, 'What are your favorite fruits?')
|
||||
self.assertEqual(block.num_inputs, 3)
|
||||
self.assertEqual(block.num_top_words, 100)
|
||||
|
||||
node = etree.Element("unknown_root")
|
||||
# This will export the olx to a separate file.
|
||||
block.add_xml_to_node(node)
|
||||
with runtime.export_fs.open(u'word_cloud/block_id.xml') as f:
|
||||
exported_xml = f.read()
|
||||
|
||||
self.assertEqual(exported_xml, original_xml)
|
||||
|
||||
def test_bad_ajax_request(self):
|
||||
"Make sure that answer for incorrect request is error json"
|
||||
response = self.ajax_request('bad_dispatch', {})
|
||||
"""
|
||||
Make sure that answer for incorrect request is error json.
|
||||
"""
|
||||
|
||||
module_system = get_test_system()
|
||||
block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock())
|
||||
|
||||
response = json.loads(block.handle_ajax('bad_dispatch', {}))
|
||||
self.assertDictEqual(response, {
|
||||
'status': 'fail',
|
||||
'error': 'Unknown Command!'
|
||||
})
|
||||
|
||||
def test_good_ajax_request(self):
|
||||
"Make sure that ajax request works correctly"
|
||||
"""
|
||||
Make sure that ajax request works correctly.
|
||||
"""
|
||||
|
||||
module_system = get_test_system()
|
||||
block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock())
|
||||
|
||||
post_data = MultiDict(('student_words[]', word) for word in ['cat', 'cat', 'dog', 'sun'])
|
||||
response = self.ajax_request('submit', post_data)
|
||||
response = json.loads(block.handle_ajax('submit', post_data))
|
||||
self.assertEqual(response['status'], 'success')
|
||||
self.assertEqual(response['submitted'], True)
|
||||
self.assertEqual(response['total_count'], 22)
|
||||
|
||||
@@ -39,7 +39,7 @@ from xmodule.randomize_module import RandomizeDescriptor
|
||||
from xmodule.seq_module import SequenceDescriptor
|
||||
from xmodule.tests import get_test_descriptor_system, get_test_system
|
||||
from xmodule.vertical_block import VerticalBlock
|
||||
from xmodule.word_cloud_module import WordCloudDescriptor
|
||||
from xmodule.word_cloud_module import WordCloudBlock
|
||||
from xmodule.wrapper_module import WrapperBlock
|
||||
from xmodule.x_module import (
|
||||
PUBLIC_VIEW,
|
||||
@@ -58,7 +58,7 @@ LEAF_XMODULES = {
|
||||
AnnotatableDescriptor: [{}],
|
||||
HtmlBlock: [{}],
|
||||
PollDescriptor: [{'display_name': 'Poll Display Name'}],
|
||||
WordCloudDescriptor: [{}],
|
||||
WordCloudBlock: [{}],
|
||||
}
|
||||
|
||||
|
||||
@@ -295,7 +295,8 @@ class XBlockWrapperTestMixin(object):
|
||||
# pylint: disable=no-member
|
||||
descriptor.runtime.id_reader.get_definition_id = Mock(return_value='a')
|
||||
descriptor.runtime.modulestore = modulestore
|
||||
descriptor._xmodule.graded = 'False'
|
||||
if hasattr(descriptor, '_xmodule'):
|
||||
descriptor._xmodule.graded = 'False'
|
||||
self.check_property(descriptor)
|
||||
|
||||
# Test that when an xmodule is generated from descriptor_cls
|
||||
|
||||
@@ -16,10 +16,18 @@ import six
|
||||
from six.moves import map
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.fields import Boolean, Dict, Integer, List, Scope, String
|
||||
from xmodule.editing_module import MetadataOnlyEditingDescriptor
|
||||
from xmodule.raw_module import EmptyDataRawDescriptor
|
||||
from xmodule.x_module import XModule
|
||||
|
||||
from xmodule.editing_module import EditingMixin
|
||||
from xmodule.raw_module import EmptyDataRawMixin
|
||||
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__)
|
||||
|
||||
# Make '_' a no-op so we can scrape strings. Using lambda instead of
|
||||
@@ -37,8 +45,20 @@ def pretty_bool(value):
|
||||
return value in bool_dict
|
||||
|
||||
|
||||
class WordCloudFields(object):
|
||||
"""XFields for word cloud."""
|
||||
class WordCloudBlock( # pylint: disable=abstract-method
|
||||
EmptyDataRawMixin,
|
||||
XmlMixin,
|
||||
EditingMixin,
|
||||
XModuleDescriptorToXBlockMixin,
|
||||
XModuleToXBlockMixin,
|
||||
HTMLSnippet,
|
||||
ResourceTemplates,
|
||||
XModuleMixin,
|
||||
):
|
||||
"""
|
||||
Word Cloud XBlock.
|
||||
"""
|
||||
|
||||
display_name = String(
|
||||
display_name=_("Display Name"),
|
||||
help=_("The display name for this component."),
|
||||
@@ -91,12 +111,32 @@ class WordCloudFields(object):
|
||||
scope=Scope.user_state_summary
|
||||
)
|
||||
|
||||
resources_dir = 'assets/word_cloud'
|
||||
template_dir_name = 'word_cloud'
|
||||
|
||||
class WordCloudModule(WordCloudFields, XModule):
|
||||
"""WordCloud Xmodule"""
|
||||
js = {'js': [resource_string(__name__, 'assets/word_cloud/src/js/word_cloud.js')]}
|
||||
css = {'scss': [resource_string(__name__, 'css/word_cloud/display.scss')]}
|
||||
js_module_name = "WordCloud"
|
||||
preview_view_js = {
|
||||
'js': [
|
||||
resource_string(__name__, 'assets/word_cloud/src/js/word_cloud.js'),
|
||||
],
|
||||
'xmodule_js': resource_string(__name__, 'js/src/xmodule.js'),
|
||||
}
|
||||
preview_view_css = {
|
||||
'scss': [
|
||||
resource_string(__name__, 'css/word_cloud/display.scss'),
|
||||
],
|
||||
}
|
||||
|
||||
studio_view_js = {
|
||||
'js': [
|
||||
resource_string(__name__, 'js/src/raw/edit/metadata-only.js'),
|
||||
],
|
||||
'xmodule_js': resource_string(__name__, 'js/src/xmodule.js'),
|
||||
}
|
||||
studio_view_css = {
|
||||
'scss': [],
|
||||
}
|
||||
studio_js_module_name = "MetadataOnlyEditingDescriptor"
|
||||
mako_template = "widgets/metadata-only-edit.html"
|
||||
|
||||
def get_state(self):
|
||||
"""Return success json answer for client."""
|
||||
@@ -239,9 +279,8 @@ class WordCloudModule(WordCloudFields, XModule):
|
||||
Renders the output that a student will see.
|
||||
"""
|
||||
fragment = Fragment()
|
||||
|
||||
fragment.add_content(self.system.render_template('word_cloud.html', {
|
||||
'ajax_url': self.system.ajax_url,
|
||||
'ajax_url': self.ajax_url,
|
||||
'display_name': self.display_name,
|
||||
'instructions': self.instructions,
|
||||
'element_class': self.location.block_type,
|
||||
@@ -249,6 +288,8 @@ class WordCloudModule(WordCloudFields, XModule):
|
||||
'num_inputs': self.num_inputs,
|
||||
'submitted': self.submitted,
|
||||
}))
|
||||
add_webpack_to_fragment(fragment, 'WordCloudBlockPreview')
|
||||
shim_xmodule_js(fragment, 'WordCloud')
|
||||
|
||||
return fragment
|
||||
|
||||
@@ -258,9 +299,13 @@ class WordCloudModule(WordCloudFields, XModule):
|
||||
"""
|
||||
return self.student_view(context)
|
||||
|
||||
|
||||
class WordCloudDescriptor(WordCloudFields, MetadataOnlyEditingDescriptor, EmptyDataRawDescriptor):
|
||||
"""Descriptor for WordCloud Xmodule."""
|
||||
module_class = WordCloudModule
|
||||
resources_dir = 'assets/word_cloud'
|
||||
template_dir_name = 'word_cloud'
|
||||
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, 'WordCloudBlockStudio')
|
||||
shim_xmodule_js(fragment, self.studio_js_module_name)
|
||||
return fragment
|
||||
|
||||
@@ -255,7 +255,7 @@ class TestWordCloud(BaseTestXmodule):
|
||||
"""
|
||||
fragment = self.runtime.render(self.item_descriptor, STUDENT_VIEW)
|
||||
expected_context = {
|
||||
'ajax_url': self.item_descriptor.xmodule_runtime.ajax_url,
|
||||
'ajax_url': self.item_descriptor.ajax_url,
|
||||
'display_name': self.item_descriptor.display_name,
|
||||
'instructions': self.item_descriptor.instructions,
|
||||
'element_class': self.item_descriptor.location.block_type,
|
||||
|
||||
Reference in New Issue
Block a user