Commit ffbaf1fe by M. Rehan Committed by GitHub

Merge pull request #12813 from edx/aj/TNL4826-lib-problems-not-update-with-import

Refersh library children for the block during import.
parents 290b9029 aca82be3
......@@ -64,7 +64,7 @@ class LibraryTestCase(ModuleStoreTestCase):
self.assertIsInstance(lib_key, LibraryLocator)
return lib_key
def _add_library_content_block(self, course, library_key, other_settings=None):
def _add_library_content_block(self, course, library_key, publish_item=False, other_settings=None):
"""
Helper method to add a LibraryContent block to a course.
The block will be configured to select content from the library
......@@ -75,7 +75,7 @@ class LibraryTestCase(ModuleStoreTestCase):
category='library_content',
parent_location=course.location,
user_id=self.user.id,
publish_item=False,
publish_item=publish_item,
source_library_id=unicode(library_key),
**(other_settings or {})
)
......@@ -159,7 +159,7 @@ class TestLibraries(LibraryTestCase):
with modulestore().default_store(ModuleStoreEnum.Type.split):
course = CourseFactory.create()
lc_block = self._add_library_content_block(course, self.lib_key, {'max_count': num_to_select})
lc_block = self._add_library_content_block(course, self.lib_key, other_settings={'max_count': num_to_select})
self.assertEqual(len(lc_block.children), 0)
lc_block = self._refresh_children(lc_block)
......
......@@ -15,15 +15,17 @@ from uuid import uuid4
from django.test.utils import override_settings
from django.conf import settings
from contentstore.tests.test_libraries import LibraryTestCase
from xmodule.contentstore.django import contentstore
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.xml_exporter import export_library_to_xml
from xmodule.modulestore.xml_importer import import_library_from_xml
from xmodule.modulestore.xml_exporter import export_library_to_xml, export_course_to_xml
from xmodule.modulestore.xml_importer import import_library_from_xml, import_course_from_xml
from xmodule.modulestore import LIBRARY_ROOT, ModuleStoreEnum
from contentstore.utils import reverse_course_url
from contentstore.tests.utils import CourseTestCase
from xmodule.modulestore.tests.factories import ItemFactory, LibraryFactory
from xmodule.modulestore.tests.factories import ItemFactory, LibraryFactory, CourseFactory
from xmodule.modulestore.tests.utils import (
MongoContentstoreBuilder, SPLIT_MODULESTORE_SETUP, TEST_DATA_DIR
)
......@@ -697,3 +699,111 @@ class TestLibraryImportExport(CourseTestCase):
# Compare the two content libraries for equality.
self.assertCoursesEqual(source_library1_key, source_library2_key)
@ddt.ddt
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
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()
# Create a problem in library
ItemFactory.create(
category="problem",
parent_location=self.library.location,
user_id=self.user.id, # pylint: disable=no-member
publish_item=False,
display_name='Test Problem',
data="<problem><multiplechoiceresponse></multiplechoiceresponse></problem>",
)
# Create a source course.
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_library_content(self, publish=False):
"""
Sets up course with library content.
"""
chapter = ItemFactory.create(
parent_location=self.source_course.location,
category='chapter',
display_name='Test Section'
)
sequential = ItemFactory.create(
parent_location=chapter.location,
category='sequential',
display_name='Test Sequential'
)
vertical = ItemFactory.create(
category='vertical',
parent_location=sequential.location,
display_name='Test Unit'
)
lc_block = self._add_library_content_block(vertical, self.lib_key, publish_item=publish)
self._refresh_children(lc_block)
def get_lib_content_block_children(self, block_location):
"""
Search for library content block to return its immediate children
"""
if block_location.block_type == 'library_content':
return self.store.get_item(block_location).children
return self.get_lib_content_block_children(self.store.get_item(block_location).children[0])
def assert_problem_display_names(self, source_course_location, dest_course_location):
"""
Asserts that problems' display names in both source and destination courses are same.
"""
source_course_lib_children = self.get_lib_content_block_children(source_course_location)
dest_course_lib_children = self.get_lib_content_block_children(dest_course_location)
self.assertEquals(len(source_course_lib_children), len(dest_course_lib_children))
for source_child_location, dest_child_location in zip(source_course_lib_children, dest_course_lib_children):
source_child = self.store.get_item(source_child_location)
dest_child = self.store.get_item(dest_child_location)
self.assertEquals(source_child.display_name, dest_child.display_name)
@ddt.data(True, False)
def test_library_content_on_course_export_import(self, publish_item):
"""
Verify that library contents in destination and source courses are same after importing
the source course into destination course.
"""
self._setup_source_course_with_library_content(publish=publish_item)
# Create a course to import source course.
dest_course = CourseFactory.create(default_store=ModuleStoreEnum.Type.split)
# Export the source course.
export_course_to_xml(
self.store,
contentstore(),
self.source_course.location.course_key,
self.export_dir,
'exported_source_course',
)
# Now, import it back to dest_course.
import_course_from_xml(
self.store,
self.user.id, # pylint: disable=no-member
self.export_dir,
['exported_source_course'],
static_content_store=contentstore(),
target_id=dest_course.location.course_key,
load_error_modules=False,
raise_on_failure=True,
create_if_not_present=True,
)
self.assert_problem_display_names(
self.source_course.location,
dest_course.location
)
......@@ -980,8 +980,13 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
to the given branch_setting. If course_id is None, the default store is used.
"""
store = self._verify_modulestore_support(course_id, 'branch_setting')
with store.branch_setting(branch_setting, course_id):
yield
previous_thread_branch_setting = getattr(self.thread_cache, 'branch_setting', None)
try:
self.thread_cache.branch_setting = branch_setting
with store.branch_setting(branch_setting, course_id):
yield
finally:
self.thread_cache.branch_setting = previous_thread_branch_setting
@contextmanager
def bulk_operations(self, course_id, emit_signals=True, ignore_case=False):
......
......@@ -2003,6 +2003,14 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# there should be no published problems with the old name
assertNumProblems(problem_original_name, 0)
# verify branch setting is published-only in manager
with self.store.branch_setting(ModuleStoreEnum.Branch.published_only):
self.assertEquals(self.store.get_branch_setting(), ModuleStoreEnum.Branch.published_only)
# verify branch setting is draft-preferred in manager
with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred):
self.assertEquals(self.store.get_branch_setting(), ModuleStoreEnum.Branch.draft_preferred)
def verify_default_store(self, store_type):
"""
Verifies the default_store property
......
......@@ -30,6 +30,7 @@ import json
import re
from lxml import etree
from xmodule.library_tools import LibraryToolsService
from xmodule.modulestore.xml import XMLModuleStore, LibraryXMLModuleStore, ImportSystem
from xblock.runtime import KvsFieldData, DictKeyValueStore
from xmodule.x_module import XModuleDescriptor, XModuleMixin
......@@ -739,11 +740,27 @@ def _update_and_import_module(
fields = _update_module_references(module, source_course_id, dest_course_id)
asides = module.get_asides() if isinstance(module, XModuleMixin) else None
return store.import_xblock(
block = store.import_xblock(
user_id, dest_course_id, module.location.category,
module.location.block_id, fields, runtime, asides=asides
)
# TODO: Move this code once the following condition is met.
# Get to the point where XML import is happening inside the
# modulestore that is eventually going to store the data.
# Ticket: https://openedx.atlassian.net/browse/PLAT-1046
if block.location.category == 'library_content':
# if library exists, update source_library_version and children
# according to this existing library and library content block.
if store.get_library(block.source_library_key):
LibraryToolsService(store).update_children(
block,
user_id,
version=block.source_library_version
)
return block
def _import_course_draft(
xml_module_store,
......
Markdown is supported
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