diff --git a/openassessment/xblock/openassessmentblock.py b/openassessment/xblock/openassessmentblock.py index 0d4d33b730..2f2f3d4977 100644 --- a/openassessment/xblock/openassessmentblock.py +++ b/openassessment/xblock/openassessmentblock.py @@ -18,7 +18,7 @@ from webob import Response from xblock.core import XBlock from xblock.exceptions import NoSuchServiceError -from xblock.fields import Boolean, Integer, List, Scope, String +from xblock.fields import Boolean, Dict, Integer, List, Scope, String from openassessment.staffgrader.staff_grader_mixin import StaffGraderMixin from openassessment.workflow.errors import AssessmentWorkflowError @@ -283,6 +283,9 @@ class OpenAssessmentBlock( help="A title to display to a student (plain text)." ) + xml_attributes = Dict(help="Map of unhandled xml attributes, used only for storage between import and export", + default={}, scope=Scope.settings) + white_listed_file_types = List( default=[], scope=Scope.content, @@ -935,6 +938,12 @@ def parse_xml(cls, node, runtime, keys, id_generator): block.text_response_editor = config['text_response_editor'] block.title = config['title'] block.white_listed_file_types_string = config['white_listed_file_types'] + + # Deserialize and add tag data info to block if any + if hasattr(block, 'read_tags_from_node') and callable(block.read_tags_from_node): # pylint: disable=no-member + # This comes from TaggedBlockMixin + block.read_tags_from_node(node) # pylint: disable=no-member + return block @property diff --git a/openassessment/xblock/test/data/content_tags.xml b/openassessment/xblock/test/data/content_tags.xml new file mode 100644 index 0000000000..3a1d9aaf44 --- /dev/null +++ b/openassessment/xblock/test/data/content_tags.xml @@ -0,0 +1,93 @@ + + Open Assessment Test + + + Given the state of the world today, what do you think should be done to combat poverty? Please answer in a short essay of 200-300 words. + + + Given the state of the world today, what do you think should be done to combat pollution? + + + + + Concise + How concise is it? + + + + + + + + Clear-headed + How clear is the thinking? + + + + + + + + Form + Lastly, how is its form? Punctuation, grammar, and spelling all count. + + + + + + + + + + + + + diff --git a/openassessment/xblock/test/test_openassessment.py b/openassessment/xblock/test/test_openassessment.py index 847cf2cdaa..75853fc164 100644 --- a/openassessment/xblock/test/test_openassessment.py +++ b/openassessment/xblock/test/test_openassessment.py @@ -8,6 +8,7 @@ from unittest import mock from unittest.mock import MagicMock, Mock, PropertyMock, patch from django.test.utils import override_settings +from workbench.runtime import WorkbenchRuntime import ddt import pytz @@ -24,6 +25,29 @@ from .base import XBlockHandlerTestCase, scenario +original_construct_xblock_from_class = WorkbenchRuntime.construct_xblock_from_class + + +def _read_tags_from_node(self, node): + """ + This method is originally defined in the XmlMixin in edx-platform. + """ + assert 'tags-v1' in node.attrib + self.xml_attributes['tags-v1'] = str(node.attrib['tags-v1']) + + +def _construct_xblock_from_class(*args, **kwargs): + """ + Mock the original construct_xblock_from_class method to add the read_tags_from_node method and xml_attributes + property to the xblock. + + In edx-platform, these members are part of the XmlMixin. + """ + xblock = original_construct_xblock_from_class(*args, **kwargs) + xblock.read_tags_from_node = lambda node: _read_tags_from_node(xblock, node) + return xblock + + def assert_is_closed( xblock, now, @@ -758,6 +782,12 @@ def test_mfe_views_supported__rearranged_steps(self, xblock): # Given this ORA has rearranged our assessment steps self.assertTrue(xblock.mfe_views_supported) + @patch.object(WorkbenchRuntime, 'construct_xblock_from_class', new=_construct_xblock_from_class) + @scenario('data/content_tags.xml') + def test_content_tags(self, xblock): + # Check if content tags are set properly + self.assertEqual(xblock.xml_attributes["tags-v1"], "test content tags") + class TestDates(XBlockHandlerTestCase): """ Test Assessment Dates. """ @@ -1185,7 +1215,8 @@ def defined_manual_dates(self, xblock, step): dt.datetime.fromisoformat(assessment.get('start')), dt.datetime.fromisoformat(assessment.get('due')) ) - return None + + assert False, f"Assessment {step} not found" # pragma: no cover def setup_dates(self, xblock, course_dates=None, subsection_dates=None): """