diff --git a/common/lib/xmodule/setup.py b/common/lib/xmodule/setup.py index 095fa5a3cc..2b22814c1c 100644 --- a/common/lib/xmodule/setup.py +++ b/common/lib/xmodule/setup.py @@ -7,7 +7,6 @@ XMODULES = [ "customtag = xmodule.template_module:CustomTagDescriptor", "discuss = xmodule.backcompat_module:TranslateCustomTagDescriptor", "image = xmodule.backcompat_module:TranslateCustomTagDescriptor", - "poll_question = xmodule.poll_module:PollDescriptor", "section = xmodule.backcompat_module:SemanticSectionDescriptor", "slides = xmodule.backcompat_module:TranslateCustomTagDescriptor", "videodev = xmodule.backcompat_module:TranslateCustomTagDescriptor", @@ -29,6 +28,7 @@ XBLOCKS = [ "library_sourced = xmodule.library_sourced_block:LibrarySourcedBlock", "lti = xmodule.lti_module:LTIBlock", "nonstaff_error = xmodule.error_module:NonStaffErrorBlock", + "poll_question = xmodule.poll_module:PollBlock", "problem = xmodule.capa_module:ProblemBlock", "problemset = xmodule.seq_module:SequenceBlock", "randomize = xmodule.randomize_module:RandomizeBlock", diff --git a/common/lib/xmodule/xmodule/poll_module.py b/common/lib/xmodule/xmodule/poll_module.py index 519ee5280e..6c739f4a91 100644 --- a/common/lib/xmodule/xmodule/poll_module.py +++ b/common/lib/xmodule/xmodule/poll_module.py @@ -14,19 +14,38 @@ from collections import OrderedDict from copy import deepcopy from pkg_resources import resource_string +from web_fragments.fragment import Fragment + from lxml import etree from openedx.core.djangolib.markup import Text, HTML from xblock.fields import Boolean, Dict, List, Scope, String # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.mako_module import MakoModuleDescriptor +from xmodule.mako_module import MakoTemplateBlockBase from xmodule.stringify import stringify_children -from xmodule.x_module import XModule -from xmodule.xml_module import XmlDescriptor +from xmodule.util.xmodule_django import add_webpack_to_fragment +from xmodule.x_module import ( + HTMLSnippet, + ResourceTemplates, + shim_xmodule_js, + XModuleMixin, + XModuleDescriptorToXBlockMixin, + XModuleToXBlockMixin, +) +from xmodule.xml_module import XmlMixin log = logging.getLogger(__name__) _ = lambda text: text -class PollFields(object): # lint-amnesty, pylint: disable=missing-class-docstring +class PollBlock( + MakoTemplateBlockBase, + XmlMixin, + XModuleDescriptorToXBlockMixin, + XModuleToXBlockMixin, + HTMLSnippet, + ResourceTemplates, + XModuleMixin, +): # pylint: disable=abstract-method + """Poll Module""" # Name of poll to use in links to this poll display_name = String( help=_("The display name for this component."), @@ -61,18 +80,33 @@ class PollFields(object): # lint-amnesty, pylint: disable=missing-class-docstri default='' ) + resources_dir = None + uses_xmodule_styles_setup = True -class PollModule(PollFields, XModule): - """Poll Module""" - js = { + preview_view_js = { 'js': [ resource_string(__name__, 'js/src/javascript_loader.js'), resource_string(__name__, 'js/src/poll/poll.js'), resource_string(__name__, 'js/src/poll/poll_main.js') - ] + ], + 'xmodule_js': resource_string(__name__, 'js/src/xmodule.js'), + } + preview_view_css = { + 'scss': [ + resource_string(__name__, 'css/poll/display.scss') + ], + } + + # There is no studio_view() for this XBlock but this is needed to make the + # the static_content command happy. + studio_view_js = { + 'js': [], + 'xmodule_js': resource_string(__name__, 'js/src/xmodule.js') + } + + studio_view_css = { + 'scss': [] } - css = {'scss': [resource_string(__name__, 'css/poll/display.scss')]} - js_module_name = "Poll" def handle_ajax(self, dispatch, data): # lint-amnesty, pylint: disable=unused-argument """Ajax handler. @@ -103,7 +137,7 @@ class PollModule(PollFields, XModule): 'total': sum(self.poll_answers.values()) }) elif dispatch == 'reset_poll' and self.voted and \ - self.descriptor.xml_attributes.get('reset', 'True').lower() != 'false': + self.xml_attributes.get('reset', 'True').lower() != 'false': self.voted = False # FIXME: fix this, when xblock will support mutable types. @@ -117,16 +151,21 @@ class PollModule(PollFields, XModule): else: # return error message return json.dumps({'error': 'Unknown Command!'}) - def get_html(self): - """Renders parameters to template.""" + def student_view(self, _context): + """ + Renders the student view. + """ + fragment = Fragment() params = { 'element_id': self.location.html_id(), 'element_class': self.location.block_type, - 'ajax_url': self.system.ajax_url, + 'ajax_url': self.ajax_url, 'configuration_json': self.dump_poll(), } - self.content = self.system.render_template('poll.html', params) # lint-amnesty, pylint: disable=attribute-defined-outside-init - return self.content + fragment.add_content(self.system.render_template('poll.html', params)) + add_webpack_to_fragment(fragment, 'PollBlockPreview') + shim_xmodule_js(fragment, 'Poll') + return fragment def dump_poll(self): """Dump poll information. @@ -160,17 +199,12 @@ class PollModule(PollFields, XModule): 'poll_answer': self.poll_answer, 'poll_answers': self.poll_answers if self.voted else {}, 'total': sum(self.poll_answers.values()) if self.voted else 0, - 'reset': str(self.descriptor.xml_attributes.get('reset', 'true')).lower() + 'reset': str(self.xml_attributes.get('reset', 'true')).lower() }) - -class PollDescriptor(PollFields, MakoModuleDescriptor, XmlDescriptor): # lint-amnesty, pylint: disable=missing-class-docstring _tag_name = 'poll_question' _child_tag_name = 'answer' - module_class = PollModule - resources_dir = None - @classmethod def definition_from_xml(cls, xml_object, system): """Pull out the data into dictionary. diff --git a/common/lib/xmodule/xmodule/static_content.py b/common/lib/xmodule/xmodule/static_content.py index eac98a2f75..6f5e37b758 100755 --- a/common/lib/xmodule/xmodule/static_content.py +++ b/common/lib/xmodule/xmodule/static_content.py @@ -26,6 +26,7 @@ from xmodule.conditional_module import ConditionalBlock from xmodule.html_module import AboutBlock, CourseInfoBlock, HtmlBlock, StaticTabBlock from xmodule.library_content_module import LibraryContentBlock from xmodule.lti_module import LTIBlock +from xmodule.poll_module import PollBlock from xmodule.seq_module import SequenceBlock from xmodule.split_test_module import SplitTestBlock from xmodule.word_cloud_module import WordCloudBlock @@ -77,6 +78,7 @@ XBLOCK_CLASSES = [ HtmlBlock, LibraryContentBlock, LTIBlock, + PollBlock, ProblemBlock, SequenceBlock, SplitTestBlock, diff --git a/common/lib/xmodule/xmodule/tests/__init__.py b/common/lib/xmodule/xmodule/tests/__init__.py index d8c76e34bb..f8b531aa52 100644 --- a/common/lib/xmodule/xmodule/tests/__init__.py +++ b/common/lib/xmodule/xmodule/tests/__init__.py @@ -191,29 +191,6 @@ class ModelsTest(unittest.TestCase): # lint-amnesty, pylint: disable=missing-cl assert str(vc) == vc_str -class LogicTest(unittest.TestCase): - """Base class for testing xmodule logic.""" - descriptor_class = None - raw_field_data = {} - - def setUp(self): - super(LogicTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments - self.system = get_test_system() - self.descriptor = Mock(name="descriptor", url_name='', category='test') - - self.xmodule_class = self.descriptor_class.module_class - usage_key = self.system.course_id.make_usage_key(self.descriptor.category, 'test_loc') - # ScopeIds has 4 fields: user_id, block_type, def_id, usage_id - scope_ids = ScopeIds(1, self.descriptor.category, usage_key, usage_key) - self.xmodule = self.xmodule_class( - self.descriptor, self.system, DictFieldData(self.raw_field_data), scope_ids - ) - - def ajax_request(self, dispatch, data): - """Call Xmodule.handle_ajax.""" - return json.loads(self.xmodule.handle_ajax(dispatch, data)) - - def map_references(value, field, actual_course_key): """ Map the references in value to actual_course_key and return value diff --git a/common/lib/xmodule/xmodule/tests/test_poll.py b/common/lib/xmodule/xmodule/tests/test_poll.py index f2e46aa184..631e8335e4 100644 --- a/common/lib/xmodule/xmodule/tests/test_poll.py +++ b/common/lib/xmodule/xmodule/tests/test_poll.py @@ -1,24 +1,42 @@ # -*- coding: utf-8 -*- """Test for Poll Xmodule functional logic.""" +import json +import unittest from mock import Mock -from xmodule.poll_module import PollDescriptor +from xblock.field_data import DictFieldData +from xblock.fields import ScopeIds +from xmodule.poll_module import PollBlock -from . import LogicTest +from . import get_test_system from .test_import import DummySystem -class PollModuleTest(LogicTest): +class PollBlockTest(unittest.TestCase): """Logic tests for Poll Xmodule.""" - descriptor_class = PollDescriptor + raw_field_data = { 'poll_answers': {'Yes': 1, 'Dont_know': 0, 'No': 0}, 'voted': False, 'poll_answer': '' } + def setUp(self): + super().setUp() + self.system = get_test_system() + usage_key = self.system.course_id.make_usage_key(PollBlock.category, 'test_loc') + # ScopeIds has 4 fields: user_id, block_type, def_id, usage_id + scope_ids = ScopeIds(1, PollBlock.category, usage_key, usage_key) + self.xmodule = PollBlock( + self.system, DictFieldData(self.raw_field_data), scope_ids + ) + + def ajax_request(self, dispatch, data): + """Call Xmodule.handle_ajax.""" + return json.loads(self.xmodule.handle_ajax(dispatch, data)) + def test_bad_ajax_request(self): # Make sure that answer for incorrect request is error json. response = self.ajax_request('bad_answer', {}) @@ -52,7 +70,7 @@ class PollModuleTest(LogicTest): ''' - output = PollDescriptor.from_xml(sample_poll_xml, module_system, id_generator) + output = PollBlock.from_xml(sample_poll_xml, module_system, id_generator) # Update the answer with invalid character. invalid_characters_poll_answer = output.answers[0] # Invalid less-than character. diff --git a/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py b/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py index 685fe0becc..c742448ffa 100644 --- a/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py +++ b/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py @@ -34,7 +34,7 @@ from xmodule.annotatable_module import AnnotatableBlock from xmodule.conditional_module import ConditionalBlock from xmodule.course_module import CourseBlock from xmodule.html_module import HtmlBlock -from xmodule.poll_module import PollDescriptor +from xmodule.poll_module import PollBlock from xmodule.randomize_module import RandomizeBlock from xmodule.seq_module import SequenceBlock from xmodule.tests import get_test_descriptor_system, get_test_system @@ -57,7 +57,7 @@ from xmodule.x_module import ( LEAF_XMODULES = { AnnotatableBlock: [{}], HtmlBlock: [{}], - PollDescriptor: [{'display_name': 'Poll Display Name'}], + PollBlock: [{'display_name': 'Poll Display Name'}], WordCloudBlock: [{}], } @@ -76,7 +76,7 @@ CONTAINER_XMODULES = { # These modules are not editable in studio yet NOT_STUDIO_EDITABLE = ( - PollDescriptor, + PollBlock, )