Commit 3b43f34b by Don Mitchell

XML export maps keys into xml expected format

parent aa1b9fec
...@@ -171,13 +171,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): ...@@ -171,13 +171,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
# return the default store # return the default store
return self.default_modulestore return self.default_modulestore
@property
def default_modulestore(self):
"""
Return the default modulestore
"""
return self.modulestores[0]
def _get_modulestore_by_type(self, modulestore_type): def _get_modulestore_by_type(self, modulestore_type):
""" """
This method should only really be used by tests and migration scripts when necessary. This method should only really be used by tests and migration scripts when necessary.
......
...@@ -229,7 +229,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem): ...@@ -229,7 +229,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
# Convert the serialized fields values in self.cached_metadata # Convert the serialized fields values in self.cached_metadata
# to python values # to python values
metadata_to_inherit = self.cached_metadata.get(non_draft_loc.to_deprecated_string(), {}) metadata_to_inherit = self.cached_metadata.get(unicode(non_draft_loc), {})
inherit_metadata(module, metadata_to_inherit) inherit_metadata(module, metadata_to_inherit)
edit_info = json_data.get('edit_info') edit_info = json_data.get('edit_info')
...@@ -268,7 +268,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem): ...@@ -268,7 +268,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
""" """
Convert a single serialized UsageKey string in a ReferenceField into a UsageKey. Convert a single serialized UsageKey string in a ReferenceField into a UsageKey.
""" """
key = Location.from_deprecated_string(ref_string) key = Location.from_string(ref_string)
return key.replace(run=self.modulestore.fill_in_run(key.course_key).run) return key.replace(run=self.modulestore.fill_in_run(key.course_key).run)
def __setattr__(self, name, value): def __setattr__(self, name, value):
...@@ -525,7 +525,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): ...@@ -525,7 +525,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
# manually pick it apart b/c the db has tag and we want as_published revision regardless # manually pick it apart b/c the db has tag and we want as_published revision regardless
location = as_published(Location._from_deprecated_son(result['_id'], course_id.run)) location = as_published(Location._from_deprecated_son(result['_id'], course_id.run))
location_url = location.to_deprecated_string() location_url = unicode(location)
if location_url in results_by_url: if location_url in results_by_url:
# found either draft or live to complement the other revision # found either draft or live to complement the other revision
existing_children = results_by_url[location_url].get('definition', {}).get('children', []) existing_children = results_by_url[location_url].get('definition', {}).get('children', [])
...@@ -1197,10 +1197,10 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): ...@@ -1197,10 +1197,10 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
for field_name, field in xblock.fields.iteritems(): for field_name, field in xblock.fields.iteritems():
if (field.scope == scope and field.is_set_on(xblock)): if (field.scope == scope and field.is_set_on(xblock)):
if isinstance(field, Reference): if isinstance(field, Reference):
jsonfields[field_name] = field.read_from(xblock).to_deprecated_string() jsonfields[field_name] = unicode(field.read_from(xblock))
elif isinstance(field, ReferenceList): elif isinstance(field, ReferenceList):
jsonfields[field_name] = [ jsonfields[field_name] = [
ele.to_deprecated_string() for ele in field.read_from(xblock) unicode(ele) for ele in field.read_from(xblock)
] ]
elif isinstance(field, ReferenceValueDict): elif isinstance(field, ReferenceValueDict):
jsonfields[field_name] = { jsonfields[field_name] = {
...@@ -1221,7 +1221,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): ...@@ -1221,7 +1221,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
# create a query with tag, org, course, and the children field set to the given location # create a query with tag, org, course, and the children field set to the given location
query = self._course_key_to_son(location.course_key) query = self._course_key_to_son(location.course_key)
query['definition.children'] = location.to_deprecated_string() query['definition.children'] = unicode(location)
# if only looking for the PUBLISHED parent, set the revision in the query to None # if only looking for the PUBLISHED parent, set the revision in the query to None
if revision == ModuleStoreEnum.RevisionOption.published_only: if revision == ModuleStoreEnum.RevisionOption.published_only:
...@@ -1296,7 +1296,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): ...@@ -1296,7 +1296,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
if item['_id']['category'] != 'course': if item['_id']['category'] != 'course':
# It would be nice to change this method to return UsageKeys instead of the deprecated string. # It would be nice to change this method to return UsageKeys instead of the deprecated string.
item_locs.add( item_locs.add(
as_published(Location._from_deprecated_son(item['_id'], course_key.run)).to_deprecated_string() unicode(as_published(Location._from_deprecated_son(item['_id'], course_key.run)))
) )
all_reachable = all_reachable.union(item.get('definition', {}).get('children', [])) all_reachable = all_reachable.union(item.get('definition', {}).get('children', []))
item_locs -= all_reachable item_locs -= all_reachable
......
...@@ -221,7 +221,7 @@ class MongoContentstoreBuilder(object): ...@@ -221,7 +221,7 @@ class MongoContentstoreBuilder(object):
MODULESTORE_SETUPS = ( MODULESTORE_SETUPS = (
MongoModulestoreBuilder(), MongoModulestoreBuilder(),
VersioningModulestoreBuilder(), # VersioningModulestoreBuilder(), # FIXME LMS-11227
MixedModulestoreBuilder([('draft', MongoModulestoreBuilder())]), MixedModulestoreBuilder([('draft', MongoModulestoreBuilder())]),
MixedModulestoreBuilder([('split', VersioningModulestoreBuilder())]), MixedModulestoreBuilder([('split', VersioningModulestoreBuilder())]),
) )
......
...@@ -4,7 +4,7 @@ Methods for exporting course data to XML ...@@ -4,7 +4,7 @@ Methods for exporting course data to XML
import logging import logging
import lxml.etree import lxml.etree
from xblock.fields import Scope from xblock.fields import Scope, Reference, ReferenceList, ReferenceValueDict
from xmodule.contentstore.content import StaticContent from xmodule.contentstore.content import StaticContent
from xmodule.exceptions import NotFoundError from xmodule.exceptions import NotFoundError
from xmodule.modulestore import EdxJSONEncoder, ModuleStoreEnum from xmodule.modulestore import EdxJSONEncoder, ModuleStoreEnum
...@@ -16,6 +16,7 @@ import os ...@@ -16,6 +16,7 @@ import os
from path import path from path import path
import shutil import shutil
from xmodule.modulestore.draft_and_published import DIRECT_ONLY_CATEGORIES from xmodule.modulestore.draft_and_published import DIRECT_ONLY_CATEGORIES
from opaque_keys.edx.locator import CourseLocator
DRAFT_DIR = "drafts" DRAFT_DIR = "drafts"
PUBLISHED_DIR = "published" PUBLISHED_DIR = "published"
...@@ -36,8 +37,7 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir): ...@@ -36,8 +37,7 @@ 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_dir`: The name of the directory inside `root_dir` to write the course content to
""" """
course = modulestore.get_course(course_key) course = modulestore.get_course(course_key, depth=None) # None means infinite
fsm = OSFS(root_dir) fsm = OSFS(root_dir)
export_fs = course.runtime.export_fs = fsm.makeopendir(course_dir) export_fs = course.runtime.export_fs = fsm.makeopendir(course_dir)
...@@ -45,6 +45,10 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir): ...@@ -45,6 +45,10 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir):
# export only the published content # export only the published content
with modulestore.branch_setting(ModuleStoreEnum.Branch.published_only, course_key): 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)
course.add_xml_to_node(root) course.add_xml_to_node(root)
with export_fs.open('course.xml', 'w') as course_xml: with export_fs.open('course.xml', 'w') as course_xml:
...@@ -79,16 +83,16 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir): ...@@ -79,16 +83,16 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir):
course_image_file.write(course_image.data) course_image_file.write(course_image.data)
# export the static tabs # export the static tabs
export_extra_content(export_fs, modulestore, course_key, 'static_tab', 'tabs', '.html') export_extra_content(export_fs, modulestore, xml_centric_course_key, 'static_tab', 'tabs', '.html')
# export the custom tags # export the custom tags
export_extra_content(export_fs, modulestore, course_key, 'custom_tag_template', 'custom_tags') export_extra_content(export_fs, modulestore, xml_centric_course_key, 'custom_tag_template', 'custom_tags')
# export the course updates # export the course updates
export_extra_content(export_fs, modulestore, course_key, 'course_info', 'info', '.html') export_extra_content(export_fs, modulestore, xml_centric_course_key, 'course_info', 'info', '.html')
# export the 'about' data (e.g. overview, etc.) # export the 'about' data (e.g. overview, etc.)
export_extra_content(export_fs, modulestore, course_key, 'about', 'about', '.html') export_extra_content(export_fs, modulestore, xml_centric_course_key, 'about', 'about', '.html')
# export the grading policy # export the grading policy
course_run_policy_dir = policies_dir.makeopendir(course.location.name) course_run_policy_dir = policies_dir.makeopendir(course.location.name)
...@@ -125,10 +129,39 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir): ...@@ -125,10 +129,39 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir):
index = sequential.children.index(draft_vertical.location) index = sequential.children.index(draft_vertical.location)
draft_vertical.xml_attributes['index_in_children_list'] = str(index) draft_vertical.xml_attributes['index_in_children_list'] = str(index)
draft_vertical.runtime.export_fs = draft_course_dir draft_vertical.runtime.export_fs = draft_course_dir
adapt_references(draft_vertical, xml_centric_course_key, draft_course_dir)
node = lxml.etree.Element('unknown') node = lxml.etree.Element('unknown')
draft_vertical.add_xml_to_node(node) draft_vertical.add_xml_to_node(node)
def adapt_references(subtree, destination_course_key, export_fs):
"""
Map every reference in the subtree into destination_course_key and set it back into the xblock fields.
Make sure every runtime knows where the export_fs is.
"""
subtree.runtime.export_fs = export_fs # ensure everything knows where it's going!
for field_name, field in subtree.fields.iteritems():
if field.is_set_on(subtree):
if isinstance(field, Reference):
value = field.read_from(subtree)
if value is not None:
field.write_to(subtree, field.read_from(subtree).map_into_course(destination_course_key))
elif field_name == 'children':
# don't change the children field but do recurse over the children
[adapt_references(child, destination_course_key, export_fs) for child in subtree.get_children()]
elif isinstance(field, ReferenceList):
field.write_to(
subtree,
[ele.map_into_course(destination_course_key) for ele in field.read_from(subtree)]
)
elif isinstance(field, ReferenceValueDict):
field.write_to(
subtree, {
key: ele.map_into_course(destination_course_key) for key, ele in field.read_from(subtree).iteritems()
}
)
def _export_field_content(xblock_item, item_dir): def _export_field_content(xblock_item, item_dir):
""" """
Export all fields related to 'xblock_item' other than 'metadata' and 'data' to json file in provided directory Export all fields related to 'xblock_item' other than 'metadata' and 'data' to json file in provided directory
...@@ -149,6 +182,7 @@ def export_extra_content(export_fs, modulestore, course_key, category_type, dirn ...@@ -149,6 +182,7 @@ def export_extra_content(export_fs, modulestore, course_key, category_type, dirn
if len(items) > 0: if len(items) > 0:
item_dir = export_fs.makeopendir(dirname) item_dir = export_fs.makeopendir(dirname)
for item in items: for item in items:
adapt_references(item, course_key, export_fs)
with item_dir.open(item.location.name + file_suffix, 'w') as item_file: with item_dir.open(item.location.name + file_suffix, 'w') as item_file:
item_file.write(item.data.encode('utf8')) item_file.write(item.data.encode('utf8'))
......
...@@ -431,7 +431,7 @@ def _import_module_and_update_references( ...@@ -431,7 +431,7 @@ def _import_module_and_update_references(
fields[field_name] = { fields[field_name] = {
key: _convert_reference_fields_to_new_namespace(reference) key: _convert_reference_fields_to_new_namespace(reference)
for key, reference for key, reference
in reference_dict.items() in reference_dict.iteritems()
} }
elif field_name == 'xml_attributes': elif field_name == 'xml_attributes':
value = field.read_from(module) value = field.read_from(module)
......
...@@ -2,7 +2,6 @@ from lxml import etree ...@@ -2,7 +2,6 @@ from lxml import etree
from xmodule.editing_module import XMLEditingDescriptor from xmodule.editing_module import XMLEditingDescriptor
from xmodule.xml_module import XmlDescriptor from xmodule.xml_module import XmlDescriptor
import logging import logging
import sys
from xblock.fields import String, Scope from xblock.fields import String, Scope
from exceptions import SerializationError from exceptions import SerializationError
......
...@@ -388,8 +388,8 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -388,8 +388,8 @@ class XmlDescriptor(XModuleDescriptor):
url_path = name_to_pathname(self.url_name) url_path = name_to_pathname(self.url_name)
filepath = self._format_filepath(self.category, url_path) filepath = self._format_filepath(self.category, url_path)
resource_fs.makedir(os.path.dirname(filepath), recursive=True, allow_recreate=True) resource_fs.makedir(os.path.dirname(filepath), recursive=True, allow_recreate=True)
with resource_fs.open(filepath, 'w') as file: with resource_fs.open(filepath, 'w') as fileobj:
file.write(etree.tostring(xml_object, pretty_print=True, encoding='utf-8')) fileobj.write(etree.tostring(xml_object, pretty_print=True, encoding='utf-8'))
# And return just a pointer with the category and filename. # And return just a pointer with the category and filename.
record_object = etree.Element(self.category) record_object = etree.Element(self.category)
......
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