Skip to content
Snippets Groups Projects
Unverified Commit 4fc48190 authored by David Ormsbee's avatar David Ormsbee Committed by GitHub
Browse files

Merge pull request #24218 from open-craft/symbolist/word-cloud-block

[BD-4] Convert WordCloud XModule into WordCloud XBlock. [SE-2722]
parents ea31525d 4cc0f30d
Branches
Tags
No related merge requests found
......@@ -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,
]
......
# -*- 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 xmodule.word_cloud_module import WordCloudBlock
from . import get_test_descriptor_system, get_test_system
from . import LogicTest
class WordCloudBlockTest(TestCase):
"""
Logic tests for Word Cloud XBlock.
"""
class WordCloudModuleTest(LogicTest):
"""Logic tests for Word Cloud Xmodule."""
descriptor_class = WordCloudDescriptor
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,
......
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