diff --git a/common/lib/xmodule/xmodule/modulestore/xml_exporter.py b/common/lib/xmodule/xmodule/modulestore/xml_exporter.py index 2ff1f1da6d..180d840781 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml_exporter.py +++ b/common/lib/xmodule/xmodule/modulestore/xml_exporter.py @@ -37,106 +37,108 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir): `course_dir`: The name of the directory inside `root_dir` to write the course content to """ - course = modulestore.get_course(course_key, depth=None) # None means infinite - fsm = OSFS(root_dir) - export_fs = course.runtime.export_fs = fsm.makeopendir(course_dir) + with modulestore.bulk_operations(course_key): - root = lxml.etree.Element('unknown') + course = modulestore.get_course(course_key, depth=None) # None means infinite + fsm = OSFS(root_dir) + export_fs = course.runtime.export_fs = fsm.makeopendir(course_dir) - # export only the published content - with modulestore.branch_setting(ModuleStoreEnum.Branch.published_only, course_key): - # change all of the references inside the course to use the xml expected key type w/o version & branch - xml_centric_course_key = CourseLocator(course_key.org, course_key.course, course_key.run, deprecated=True) - adapt_references(course, xml_centric_course_key, export_fs) + root = lxml.etree.Element('unknown') - course.add_xml_to_node(root) + # export only the published content + with modulestore.branch_setting(ModuleStoreEnum.Branch.published_only, course_key): + # change all of the references inside the course to use the xml expected key type w/o version & branch + xml_centric_course_key = CourseLocator(course_key.org, course_key.course, course_key.run, deprecated=True) + adapt_references(course, xml_centric_course_key, export_fs) - with export_fs.open('course.xml', 'w') as course_xml: - lxml.etree.ElementTree(root).write(course_xml) + course.add_xml_to_node(root) - # export the static assets - policies_dir = export_fs.makeopendir('policies') - if contentstore: - contentstore.export_all_for_course( - course_key, - root_dir + '/' + course_dir + '/static/', - root_dir + '/' + course_dir + '/policies/assets.json', - ) + with export_fs.open('course.xml', 'w') as course_xml: + lxml.etree.ElementTree(root).write(course_xml) - # If we are using the default course image, export it to the - # legacy location to support backwards compatibility. - if course.course_image == course.fields['course_image'].default: - try: - course_image = contentstore.find( - StaticContent.compute_location( - course.id, - course.course_image - ), - ) - except NotFoundError: - pass - else: - output_dir = root_dir + '/' + course_dir + '/static/images/' - if not os.path.isdir(output_dir): - os.makedirs(output_dir) - with OSFS(output_dir).open('course_image.jpg', 'wb') as course_image_file: - course_image_file.write(course_image.data) - - # export the static tabs - export_extra_content(export_fs, modulestore, xml_centric_course_key, 'static_tab', 'tabs', '.html') - - # export the custom tags - export_extra_content(export_fs, modulestore, xml_centric_course_key, 'custom_tag_template', 'custom_tags') - - # export the course updates - export_extra_content(export_fs, modulestore, xml_centric_course_key, 'course_info', 'info', '.html') - - # export the 'about' data (e.g. overview, etc.) - export_extra_content(export_fs, modulestore, xml_centric_course_key, 'about', 'about', '.html') - - # export the grading policy - course_run_policy_dir = policies_dir.makeopendir(course.location.name) - with course_run_policy_dir.open('grading_policy.json', 'w') as grading_policy: - grading_policy.write(dumps(course.grading_policy, cls=EdxJSONEncoder, sort_keys=True, indent=4)) - - # export all of the course metadata in policy.json - with course_run_policy_dir.open('policy.json', 'w') as course_policy: - policy = {'course/' + course.location.name: own_metadata(course)} - course_policy.write(dumps(policy, cls=EdxJSONEncoder, sort_keys=True, indent=4)) - - #### DRAFTS #### - # xml backed courses don't support drafts! - if course.runtime.modulestore.get_modulestore_type() != ModuleStoreEnum.Type.xml: - # NOTE: this code assumes that verticals are the top most draftable container - # should we change the application, then this assumption will no longer be valid - # NOTE: we need to explicitly implement the logic for setting the vertical's parent - # and index here since the XML modulestore cannot load draft modules - with modulestore.branch_setting(ModuleStoreEnum.Branch.draft_preferred, course_key): - draft_verticals = modulestore.get_items( + # export the static assets + policies_dir = export_fs.makeopendir('policies') + if contentstore: + contentstore.export_all_for_course( course_key, - qualifiers={'category': 'vertical'}, - revision=ModuleStoreEnum.RevisionOption.draft_only + root_dir + '/' + course_dir + '/static/', + root_dir + '/' + course_dir + '/policies/assets.json', ) - if len(draft_verticals) > 0: - draft_course_dir = export_fs.makeopendir(DRAFT_DIR) - for draft_vertical in draft_verticals: - parent_loc = modulestore.get_parent_location( - draft_vertical.location, - revision=ModuleStoreEnum.RevisionOption.draft_preferred + # If we are using the default course image, export it to the + # legacy location to support backwards compatibility. + if course.course_image == course.fields['course_image'].default: + try: + course_image = contentstore.find( + StaticContent.compute_location( + course.id, + course.course_image + ), ) - # Don't try to export orphaned items. - if parent_loc is not None: - logging.debug('parent_loc = {0}'.format(parent_loc)) - if parent_loc.category in DIRECT_ONLY_CATEGORIES: - draft_vertical.xml_attributes['parent_sequential_url'] = parent_loc.to_deprecated_string() - sequential = modulestore.get_item(parent_loc) - index = sequential.children.index(draft_vertical.location) - draft_vertical.xml_attributes['index_in_children_list'] = str(index) - draft_vertical.runtime.export_fs = draft_course_dir - adapt_references(draft_vertical, xml_centric_course_key, draft_course_dir) - node = lxml.etree.Element('unknown') - draft_vertical.add_xml_to_node(node) + except NotFoundError: + pass + else: + output_dir = root_dir + '/' + course_dir + '/static/images/' + if not os.path.isdir(output_dir): + os.makedirs(output_dir) + with OSFS(output_dir).open('course_image.jpg', 'wb') as course_image_file: + course_image_file.write(course_image.data) + + # export the static tabs + export_extra_content(export_fs, modulestore, xml_centric_course_key, 'static_tab', 'tabs', '.html') + + # export the custom tags + export_extra_content(export_fs, modulestore, xml_centric_course_key, 'custom_tag_template', 'custom_tags') + + # export the course updates + export_extra_content(export_fs, modulestore, xml_centric_course_key, 'course_info', 'info', '.html') + + # export the 'about' data (e.g. overview, etc.) + export_extra_content(export_fs, modulestore, xml_centric_course_key, 'about', 'about', '.html') + + # export the grading policy + course_run_policy_dir = policies_dir.makeopendir(course.location.name) + with course_run_policy_dir.open('grading_policy.json', 'w') as grading_policy: + grading_policy.write(dumps(course.grading_policy, cls=EdxJSONEncoder, sort_keys=True, indent=4)) + + # export all of the course metadata in policy.json + with course_run_policy_dir.open('policy.json', 'w') as course_policy: + policy = {'course/' + course.location.name: own_metadata(course)} + course_policy.write(dumps(policy, cls=EdxJSONEncoder, sort_keys=True, indent=4)) + + #### DRAFTS #### + # xml backed courses don't support drafts! + if course.runtime.modulestore.get_modulestore_type() != ModuleStoreEnum.Type.xml: + # NOTE: this code assumes that verticals are the top most draftable container + # should we change the application, then this assumption will no longer be valid + # NOTE: we need to explicitly implement the logic for setting the vertical's parent + # and index here since the XML modulestore cannot load draft modules + with modulestore.branch_setting(ModuleStoreEnum.Branch.draft_preferred, course_key): + draft_verticals = modulestore.get_items( + course_key, + qualifiers={'category': 'vertical'}, + revision=ModuleStoreEnum.RevisionOption.draft_only + ) + + if len(draft_verticals) > 0: + draft_course_dir = export_fs.makeopendir(DRAFT_DIR) + for draft_vertical in draft_verticals: + parent_loc = modulestore.get_parent_location( + draft_vertical.location, + revision=ModuleStoreEnum.RevisionOption.draft_preferred + ) + # Don't try to export orphaned items. + if parent_loc is not None: + logging.debug('parent_loc = {0}'.format(parent_loc)) + if parent_loc.category in DIRECT_ONLY_CATEGORIES: + draft_vertical.xml_attributes['parent_sequential_url'] = parent_loc.to_deprecated_string() + sequential = modulestore.get_item(parent_loc) + index = sequential.children.index(draft_vertical.location) + draft_vertical.xml_attributes['index_in_children_list'] = str(index) + draft_vertical.runtime.export_fs = draft_course_dir + adapt_references(draft_vertical, xml_centric_course_key, draft_course_dir) + node = lxml.etree.Element('unknown') + draft_vertical.add_xml_to_node(node) def adapt_references(subtree, destination_course_key, export_fs):