Merge pull request #23996 from edx/ahtisham/PROD-1540

Fixed Import error due to pre tag
This commit is contained in:
Ahtisham Shahid
2020-05-20 21:56:34 +05:00
committed by GitHub
2 changed files with 56 additions and 28 deletions

View File

@@ -2,7 +2,6 @@
Unit tests for course import and export
"""
import copy
import json
import logging
@@ -55,6 +54,7 @@ class ImportEntranceExamTestCase(CourseTestCase, MilestonesTestCaseMixin):
"""
Unit tests for importing a course with entrance exam
"""
def setUp(self):
super(ImportEntranceExamTestCase, self).setUp()
self.url = reverse_course_url('import_handler', self.course.id)
@@ -533,6 +533,7 @@ class ExportTestCase(CourseTestCase):
"""
Tests for export_handler.
"""
def setUp(self):
"""
Sets up the test course.
@@ -788,9 +789,9 @@ class ExportTestCase(CourseTestCase):
@patch('contentstore.views.import_export._latest_task_status')
@patch('user_tasks.models.UserTaskArtifact.objects.get')
def test_export_status_handler_other(
self,
mock_get_user_task_artifact,
mock_latest_task_status,
self,
mock_get_user_task_artifact,
mock_latest_task_status,
):
"""
Verify that the export status handler generates the correct export path
@@ -808,9 +809,9 @@ class ExportTestCase(CourseTestCase):
@patch('contentstore.views.import_export._latest_task_status')
@patch('user_tasks.models.UserTaskArtifact.objects.get')
def test_export_status_handler_s3(
self,
mock_get_user_task_artifact,
mock_latest_task_status,
self,
mock_get_user_task_artifact,
mock_latest_task_status,
):
"""
Verify that the export status handler generates the correct export path
@@ -828,9 +829,9 @@ class ExportTestCase(CourseTestCase):
@patch('contentstore.views.import_export._latest_task_status')
@patch('user_tasks.models.UserTaskArtifact.objects.get')
def test_export_status_handler_filesystem(
self,
mock_get_user_task_artifact,
mock_latest_task_status,
self,
mock_get_user_task_artifact,
mock_latest_task_status,
):
"""
Verify that the export status handler generates the correct export path
@@ -849,6 +850,7 @@ class TestLibraryImportExport(CourseTestCase):
"""
Tests for importing content libraries from XML and exporting them to XML.
"""
def setUp(self):
super(TestLibraryImportExport, self).setUp()
self.export_dir = tempfile.mkdtemp()
@@ -906,6 +908,7 @@ class TestCourseExportImport(LibraryTestCase):
"""
Tests for importing after exporting the course containing content libraries from XML.
"""
def setUp(self):
super(TestCourseExportImport, self).setUp()
self.export_dir = tempfile.mkdtemp()
@@ -1022,18 +1025,20 @@ class TestCourseExportImport(LibraryTestCase):
)
@ddt.ddt
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class TestCourseExportImportProblem(CourseTestCase):
"""
Tests for importing after exporting the course containing problem with pre tags from XML.
"""
def setUp(self):
super(TestCourseExportImportProblem, self).setUp()
self.export_dir = tempfile.mkdtemp()
self.source_course = CourseFactory.create(default_store=ModuleStoreEnum.Type.split)
self.addCleanup(shutil.rmtree, self.export_dir, ignore_errors=True)
def _setup_source_course_with_problem_content(self, publish_item=False):
def _setup_source_course_with_problem_content(self, data, publish_item=False):
"""
Sets up course with problem content.
"""
@@ -1058,8 +1063,7 @@ class TestCourseExportImportProblem(CourseTestCase):
category='problem',
display_name='Test Problem',
publish_item=publish_item,
data='<problem><pre><code>x=10 print("hello \n")</code></pre>'
'<multiplechoiceresponse></multiplechoiceresponse></problem>',
data=data,
)
def get_problem_content(self, block_location):
@@ -1071,21 +1075,38 @@ class TestCourseExportImportProblem(CourseTestCase):
return self.get_problem_content(self.store.get_item(block_location).children[0])
def assert_problem_definition(self, course_location):
def assert_problem_definition(self, course_location, expected_problem_content):
"""
Asserts that problems' data is as expected with pre-tag content maintained.
"""
expected_problem_content = '<problem>\n <pre>\n <code>x=10 print("hello \n")</code>\n </pre>\n ' \
'<multiplechoiceresponse/>\n</problem>\n'
problem_content = self.get_problem_content(course_location)
self.assertEqual(expected_problem_content, problem_content)
def test_problem_content_on_course_export_import(self):
@ddt.data(
[
'<problem><pre><code>x=10 print("hello \n")</code></pre>'
'<pre><div><pre><code>x=10 print("hello \n")</code></pre></div></pre>'
'<multiplechoiceresponse></multiplechoiceresponse></problem>',
'<problem>\n <pre>\n <code>x=10 print("hello \n")</code>\n </pre>\n <pre>\n <div>\n <pre>\n '
' <code>x=10 print("hello \n")</code>\n </pre>\n </div>\n </pre>\n '
'<multiplechoiceresponse/>\n</problem>\n'
],
[
'<problem><pre><code>x=10 print("hello \n")</code></pre>'
'<multiplechoiceresponse></multiplechoiceresponse></problem>',
'<problem>\n <pre>\n <code>x=10 print("hello \n")</code>\n </pre>\n '
'<multiplechoiceresponse/>\n</problem>\n'
]
)
@ddt.unpack
def test_problem_content_on_course_export_import(self, problem_data, expected_problem_content):
"""
Verify that problem content in destination matches expected problem content,
specifically concerned with pre tag data with problem.
"""
self._setup_source_course_with_problem_content()
self._setup_source_course_with_problem_content(problem_data)
dest_course = CourseFactory.create(default_store=ModuleStoreEnum.Type.split)
@@ -1109,4 +1130,4 @@ class TestCourseExportImportProblem(CourseTestCase):
create_if_not_present=True,
)
self.assert_problem_definition(dest_course.location)
self.assert_problem_definition(dest_course.location, expected_problem_content)

View File

@@ -1,5 +1,3 @@
import logging
import re
@@ -12,7 +10,7 @@ from .exceptions import SerializationError
log = logging.getLogger(__name__)
PRE_TAG_REGEX = re.compile(r'<pre>[\s\S]*?</pre>')
PRE_TAG_REGEX = re.compile(r'<pre\b[^>]*>(?:(?=([^<]+))\1|<(?!pre\b[^>]*>))*?</pre>')
class RawMixin(object):
@@ -25,12 +23,21 @@ class RawMixin(object):
@classmethod
def definition_from_xml(cls, xml_object, system):
pre_tag_data = [etree.tostring(pre_tag_info) for pre_tag_info in xml_object.findall('.//pre')]
data = etree.tostring(xml_object, pretty_print=True, encoding='unicode')
if pre_tag_data:
for index, pre_tag in enumerate(re.findall(PRE_TAG_REGEX, data)):
data = re.sub(pre_tag, pre_tag_data[index].decode(), data)
return {'data': data}, []
try:
data = etree.tostring(xml_object, pretty_print=True, encoding='unicode')
pre_tag_data = []
for pre_tag_info in xml_object.findall('.//pre'):
if len(pre_tag_info.findall('.//pre')) == 0:
pre_tag_data.append(etree.tostring(pre_tag_info))
if pre_tag_data:
matches = re.finditer(PRE_TAG_REGEX, data)
for match_num, match in enumerate(matches):
data = re.sub(match.group(), pre_tag_data[match_num].decode(), data)
etree.XML(data) # it just checks if generated string is valid xml
return {'data': data}, []
except etree.XMLSyntaxError:
return {'data': etree.tostring(xml_object, pretty_print=True, encoding='unicode')}, []
def definition_to_xml(self, resource_fs):
"""