Commit 7e0fad0a by cahrens

If existing course is found on import make sure dest_course_id is correct.

TNL-1362
parent 7e26107e
...@@ -12,7 +12,7 @@ from django.core.management import call_command ...@@ -12,7 +12,7 @@ from django.core.management import call_command
from django_comment_common.utils import are_permissions_roles_seeded from django_comment_common.utils import are_permissions_roles_seeded
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from opaque_keys.edx.locations import SlashSeparatedCourseKey from xmodule.modulestore import ModuleStoreEnum
class TestImport(ModuleStoreTestCase): class TestImport(ModuleStoreTestCase):
...@@ -20,10 +20,6 @@ class TestImport(ModuleStoreTestCase): ...@@ -20,10 +20,6 @@ class TestImport(ModuleStoreTestCase):
Unit tests for importing a course from command line Unit tests for importing a course from command line
""" """
BASE_COURSE_KEY = SlashSeparatedCourseKey(u'edX', u'test_import_course', u'2013_Spring')
DIFF_KEY = SlashSeparatedCourseKey(u'edX', u'test_import_course', u'2014_Spring')
TRUNCATED_KEY = SlashSeparatedCourseKey(u'edX', u'test_import', u'2014_Spring')
def create_course_xml(self, content_dir, course_id): def create_course_xml(self, content_dir, course_id):
directory = tempfile.mkdtemp(dir=content_dir) directory = tempfile.mkdtemp(dir=content_dir)
os.makedirs(os.path.join(directory, "course")) os.makedirs(os.path.join(directory, "course"))
...@@ -32,7 +28,7 @@ class TestImport(ModuleStoreTestCase): ...@@ -32,7 +28,7 @@ class TestImport(ModuleStoreTestCase):
'course="{0.course}"/>'.format(course_id)) 'course="{0.course}"/>'.format(course_id))
with open(os.path.join(directory, "course", "{0.run}.xml".format(course_id)), "w+") as f: with open(os.path.join(directory, "course", "{0.run}.xml".format(course_id)), "w+") as f:
f.write('<course></course>') f.write('<course><chapter name="Test Chapter"></chapter></course>')
return directory return directory
...@@ -44,38 +40,23 @@ class TestImport(ModuleStoreTestCase): ...@@ -44,38 +40,23 @@ class TestImport(ModuleStoreTestCase):
self.content_dir = path(tempfile.mkdtemp()) self.content_dir = path(tempfile.mkdtemp())
self.addCleanup(shutil.rmtree, self.content_dir) self.addCleanup(shutil.rmtree, self.content_dir)
# Create good course xml self.base_course_key = self.store.make_course_key(u'edX', u'test_import_course', u'2013_Spring')
self.good_dir = self.create_course_xml(self.content_dir, self.BASE_COURSE_KEY) self.truncated_key = self.store.make_course_key(u'edX', u'test_import', u'2014_Spring')
# Create run changed course xml # Create good course xml
self.dupe_dir = self.create_course_xml(self.content_dir, self.DIFF_KEY) self.good_dir = self.create_course_xml(self.content_dir, self.base_course_key)
# Create course XML where TRUNCATED_COURSE.org == BASE_COURSE_ID.org # Create course XML where TRUNCATED_COURSE.org == BASE_COURSE_ID.org
# and BASE_COURSE_ID.startswith(TRUNCATED_COURSE.course) # and BASE_COURSE_ID.startswith(TRUNCATED_COURSE.course)
self.course_dir = self.create_course_xml(self.content_dir, self.TRUNCATED_KEY) self.course_dir = self.create_course_xml(self.content_dir, self.truncated_key)
def test_forum_seed(self): def test_forum_seed(self):
""" """
Tests that forum roles were created with import. Tests that forum roles were created with import.
""" """
self.assertFalse(are_permissions_roles_seeded(self.BASE_COURSE_KEY)) self.assertFalse(are_permissions_roles_seeded(self.base_course_key))
call_command('import', self.content_dir, self.good_dir) call_command('import', self.content_dir, self.good_dir)
self.assertTrue(are_permissions_roles_seeded(self.BASE_COURSE_KEY)) self.assertTrue(are_permissions_roles_seeded(self.base_course_key))
def test_duplicate_with_url(self):
"""
Check to make sure an import doesn't import courses that have the
same org and course, but they have different runs in order to
prevent modulestore "findone" exceptions on deletion
"""
# Load up base course and verify it is available
call_command('import', self.content_dir, self.good_dir)
store = modulestore()
self.assertIsNotNone(store.get_course(self.BASE_COURSE_KEY))
# Now load up duped course and verify it doesn't load
call_command('import', self.content_dir, self.dupe_dir)
self.assertIsNone(store.get_course(self.DIFF_KEY))
def test_truncated_course_with_url(self): def test_truncated_course_with_url(self):
""" """
...@@ -87,8 +68,28 @@ class TestImport(ModuleStoreTestCase): ...@@ -87,8 +68,28 @@ class TestImport(ModuleStoreTestCase):
# Load up base course and verify it is available # Load up base course and verify it is available
call_command('import', self.content_dir, self.good_dir) call_command('import', self.content_dir, self.good_dir)
store = modulestore() store = modulestore()
self.assertIsNotNone(store.get_course(self.BASE_COURSE_KEY)) self.assertIsNotNone(store.get_course(self.base_course_key))
# Now load up the course with a similar course_id and verify it loads # Now load up the course with a similar course_id and verify it loads
call_command('import', self.content_dir, self.course_dir) call_command('import', self.content_dir, self.course_dir)
self.assertIsNotNone(store.get_course(self.TRUNCATED_KEY)) self.assertIsNotNone(store.get_course(self.truncated_key))
def test_existing_course_with_different_modulestore(self):
"""
Checks that a course that originally existed in old mongo can be re-imported when
split is the default modulestore.
"""
with modulestore().default_store(ModuleStoreEnum.Type.mongo):
call_command('import', self.content_dir, self.good_dir)
# Clear out the modulestore mappings, else when the next import command goes to create a destination
# course_key, it will find the existing course and return the mongo course_key. To reproduce TNL-1362,
# the destination course_key needs to be the one for split modulestore.
modulestore().mappings = {}
with modulestore().default_store(ModuleStoreEnum.Type.split):
call_command('import', self.content_dir, self.good_dir)
course = modulestore().get_course(self.base_course_key)
# With the bug, this fails because the chapter's course_key is the split mongo form,
# while the course's course_key is the old mongo form.
self.assertEqual(unicode(course.location.course_key), unicode(course.children[0].course_key))
...@@ -83,21 +83,24 @@ class ContentStoreImportTest(ModuleStoreTestCase): ...@@ -83,21 +83,24 @@ class ContentStoreImportTest(ModuleStoreTestCase):
""" """
# Test that importing course with unicode 'id' and 'display name' doesn't give UnicodeEncodeError # Test that importing course with unicode 'id' and 'display name' doesn't give UnicodeEncodeError
""" """
module_store = modulestore() # Test with the split modulestore because store.has_course fails in old mongo with unicode characters.
course_id = SlashSeparatedCourseKey(u'Юникода', u'unicode_course', u'échantillon') with modulestore().default_store(ModuleStoreEnum.Type.split):
import_from_xml( module_store = modulestore()
module_store, course_id = module_store.make_course_key(u'Юникода', u'unicode_course', u'échantillon')
self.user.id, import_from_xml(
TEST_DATA_DIR, module_store,
['2014_Uni'], self.user.id,
target_course_id=course_id TEST_DATA_DIR,
) ['2014_Uni'],
target_course_id=course_id,
create_course_if_not_present=True
)
course = module_store.get_course(course_id) course = module_store.get_course(course_id)
self.assertIsNotNone(course) self.assertIsNotNone(course)
# test that course 'display_name' same as imported course 'display_name' # test that course 'display_name' same as imported course 'display_name'
self.assertEqual(course.display_name, u"Φυσικά το όνομα Unicode") self.assertEqual(course.display_name, u"Φυσικά το όνομα Unicode")
def test_static_import(self): def test_static_import(self):
''' '''
......
...@@ -195,11 +195,18 @@ def import_from_xml( ...@@ -195,11 +195,18 @@ def import_from_xml(
if target_course_id is not None: if target_course_id is not None:
dest_course_id = target_course_id dest_course_id = target_course_id
else: else:
# Note that dest_course_id will be in the format for the default modulestore.
dest_course_id = store.make_course_key(course_key.org, course_key.course, course_key.run) dest_course_id = store.make_course_key(course_key.org, course_key.course, course_key.run)
existing_course_id = store.has_course(dest_course_id, ignore_case=True)
# store.has_course will return the course_key in the format for the modulestore in which it was found.
# This may be different from dest_course_id, so correct to the format found.
if existing_course_id:
dest_course_id = existing_course_id
runtime = None runtime = None
# Creates a new course if it doesn't already exist # Creates a new course if it doesn't already exist
if create_course_if_not_present and not store.has_course(dest_course_id, ignore_case=True): if create_course_if_not_present and not existing_course_id:
try: try:
new_course = store.create_course(dest_course_id.org, dest_course_id.course, dest_course_id.run, user_id) new_course = store.create_course(dest_course_id.org, dest_course_id.course, dest_course_id.run, user_id)
runtime = new_course.runtime runtime = new_course.runtime
......
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