Commit a1a7504f by Carlos Andrés Rocha

Fix dump course structure when element metadata uses opaque keys.

Make sure that during the serialization, all opaque keys and their
references and serialized using the `unicode` function.

AN-3821
parent 0d36dd72
...@@ -26,7 +26,7 @@ from xmodule.modulestore.django import modulestore ...@@ -26,7 +26,7 @@ from xmodule.modulestore.django import modulestore
from xmodule.modulestore.inheritance import own_metadata, compute_inherited_metadata from xmodule.modulestore.inheritance import own_metadata, compute_inherited_metadata
from xblock.fields import Scope from xblock.fields import Scope
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.keys import CourseKey
FILTER_LIST = ['xml_attributes', 'checklists'] FILTER_LIST = ['xml_attributes', 'checklists']
INHERITED_FILTER_LIST = ['children', 'xml_attributes', 'checklists'] INHERITED_FILTER_LIST = ['children', 'xml_attributes', 'checklists']
...@@ -59,20 +59,22 @@ class Command(BaseCommand): ...@@ -59,20 +59,22 @@ class Command(BaseCommand):
raise CommandError("course_id not specified") raise CommandError("course_id not specified")
# Get the modulestore # Get the modulestore
store = modulestore() store = modulestore()
# Get the course data # Get the course data
try: try:
course_id = SlashSeparatedCourseKey.from_deprecated_string(args[0]) course_key = CourseKey.from_string(args[0])
except InvalidKeyError: except InvalidKeyError:
raise CommandError("Invalid course_id") raise CommandError("Invalid course_id")
course = store.get_course(course_id) course = store.get_course(course_key)
if course is None: if course is None:
raise CommandError("Invalid course_id") raise CommandError("Invalid course_id")
# precompute inherited metadata at the course level, if needed: # Precompute inherited metadata at the course level, if needed:
if options['inherited']: if options['inherited']:
compute_inherited_metadata(course) compute_inherited_metadata(course)
...@@ -80,7 +82,7 @@ class Command(BaseCommand): ...@@ -80,7 +82,7 @@ class Command(BaseCommand):
info = dump_module(course, inherited=options['inherited'], defaults=options['inherited_defaults']) info = dump_module(course, inherited=options['inherited'], defaults=options['inherited_defaults'])
return json.dumps(info, indent=2, sort_keys=True) return json.dumps(info, indent=2, sort_keys=True, default=unicode)
def dump_module(module, destination=None, inherited=False, defaults=False): def dump_module(module, destination=None, inherited=False, defaults=False):
...@@ -92,16 +94,17 @@ def dump_module(module, destination=None, inherited=False, defaults=False): ...@@ -92,16 +94,17 @@ def dump_module(module, destination=None, inherited=False, defaults=False):
destination = destination if destination else {} destination = destination if destination else {}
items = own_metadata(module) items = own_metadata(module)
filtered_metadata = {k: v for k, v in items.iteritems() if k not in FILTER_LIST} filtered_metadata = {k: v for k, v in items.iteritems() if k not in FILTER_LIST}
destination[module.location.to_deprecated_string()] = { destination[unicode(module.location)] = {
'category': module.location.category, 'category': module.location.category,
'children': [child.to_deprecated_string() for child in getattr(module, 'children', [])], 'children': [unicode(child) for child in getattr(module, 'children', [])],
'metadata': filtered_metadata, 'metadata': filtered_metadata,
} }
if inherited: if inherited:
# when calculating inherited metadata, don't include existing # When calculating inherited metadata, don't include existing
# locally-defined metadata # locally-defined metadata
inherited_metadata_filter_list = list(filtered_metadata.keys()) inherited_metadata_filter_list = list(filtered_metadata.keys())
inherited_metadata_filter_list.extend(INHERITED_FILTER_LIST) inherited_metadata_filter_list.extend(INHERITED_FILTER_LIST)
...@@ -117,7 +120,7 @@ def dump_module(module, destination=None, inherited=False, defaults=False): ...@@ -117,7 +120,7 @@ def dump_module(module, destination=None, inherited=False, defaults=False):
return field.values != field.default return field.values != field.default
inherited_metadata = {field.name: field.read_json(module) for field in module.fields.values() if is_inherited(field)} inherited_metadata = {field.name: field.read_json(module) for field in module.fields.values() if is_inherited(field)}
destination[module.location.to_deprecated_string()]['inherited_metadata'] = inherited_metadata destination[unicode(module.location)]['inherited_metadata'] = inherited_metadata
for child in module.get_children(): for child in module.get_children():
dump_module(child, destination, inherited, defaults) dump_module(child, destination, inherited, defaults)
......
...@@ -57,7 +57,7 @@ class CommandsTestBase(TestCase): ...@@ -57,7 +57,7 @@ class CommandsTestBase(TestCase):
courses = store.get_courses() courses = store.get_courses()
# NOTE: if xml store owns these, it won't import them into mongo # NOTE: if xml store owns these, it won't import them into mongo
if SlashSeparatedCourseKey.from_deprecated_string(TEST_COURSE_ID) not in [c.id for c in courses]: if SlashSeparatedCourseKey.from_deprecated_string(TEST_COURSE_ID) not in [c.id for c in courses]:
import_from_xml(store, ModuleStoreEnum.UserID.mgmt_command, DATA_DIR, ['toy', 'simple']) import_from_xml(store, ModuleStoreEnum.UserID.mgmt_command, DATA_DIR, ['toy', 'simple', 'open_ended'])
return [course.id for course in store.get_courses()] return [course.id for course in store.get_courses()]
...@@ -77,6 +77,19 @@ class CommandsTestBase(TestCase): ...@@ -77,6 +77,19 @@ class CommandsTestBase(TestCase):
dumped_ids = set(dumped_courses) dumped_ids = set(dumped_courses)
self.assertEqual(course_ids, dumped_ids) self.assertEqual(course_ids, dumped_ids)
def test_correct_course_structure_metadata(self):
course_id = 'edX/open_ended/2012_Fall'
args = [course_id]
kwargs = {'modulestore': 'default'}
try:
output = self.call_command('dump_course_structure', *args, **kwargs)
except TypeError, exception:
self.fail(exception)
dump = json.loads(output)
self.assertGreater(len(dump.values()), 0)
def test_dump_course_structure(self): def test_dump_course_structure(self):
args = [TEST_COURSE_ID] args = [TEST_COURSE_ID]
kwargs = {'modulestore': 'default'} kwargs = {'modulestore': 'default'}
......
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