Commit 79987666 by Calen Pennington

Lazily load module definition and metadata as needed, rather than immediately

parent 1d4e1d55
...@@ -19,11 +19,11 @@ class Command(BaseCommand): ...@@ -19,11 +19,11 @@ class Command(BaseCommand):
org, course, data_dir = args org, course, data_dir = args
module_store = XMLModuleStore(org, course, data_dir, 'xmodule.raw_module.RawDescriptor') module_store = XMLModuleStore(org, course, data_dir, 'xmodule.raw_module.RawDescriptor', eager=True)
for module in module_store.modules.itervalues(): for module in module_store.modules.itervalues():
keystore().create_item(module.location) keystore().create_item(module.location)
if 'data' in module.definition: if 'data' in module.definition:
keystore().update_item(module.location, module.definition['data']) keystore().update_item(module.location, module.definition['data'])
if 'children' in module.definition: if 'children' in module.definition:
keystore().update_children(module.location, module.definition['children']) keystore().update_children(module.location, module.definition['children'])
keystore().update_metadata(module.url, module.metadata) keystore().update_metadata(module.location, dict(module.metadata))
...@@ -18,7 +18,15 @@ class XMLModuleStore(ModuleStore): ...@@ -18,7 +18,15 @@ class XMLModuleStore(ModuleStore):
""" """
An XML backed ModuleStore An XML backed ModuleStore
""" """
def __init__(self, org, course, data_dir, default_class=None): def __init__(self, org, course, data_dir, default_class=None, eager=False):
"""
Initialize an XMLModuleStore from data_dir
org, course: Strings to be used in module keys
data_dir: path to data directory containing course.xml
default_class: dot-separated string defining the default descriptor class to use if non is specified in entry_points
eager: If true, load the modules children immediately to force the entire course tree to be parsed
"""
self.data_dir = path(data_dir) self.data_dir = path(data_dir)
self.modules = {} self.modules = {}
...@@ -57,6 +65,9 @@ class XMLModuleStore(ModuleStore): ...@@ -57,6 +65,9 @@ class XMLModuleStore(ModuleStore):
module = XModuleDescriptor.load_from_xml(etree.tostring(xml_data), self, org, course, modulestore.default_class) module = XModuleDescriptor.load_from_xml(etree.tostring(xml_data), self, org, course, modulestore.default_class)
modulestore.modules[module.location] = module modulestore.modules[module.location] = module
if eager:
module.get_children()
return module return module
XMLParsingSystem.__init__(self, modulestore.get_item, OSFS(data_dir), process_xml) XMLParsingSystem.__init__(self, modulestore.get_item, OSFS(data_dir), process_xml)
......
from collections import MutableMapping
from xmodule.x_module import XModuleDescriptor from xmodule.x_module import XModuleDescriptor
from lxml import etree from lxml import etree
class LazyLoadingDict(MutableMapping):
"""
A dictionary object that lazily loads it's contents from a provided
function on reads (of members that haven't already been set)
"""
def __init__(self, loader):
self._contents = {}
self._loaded = False
self._loader = loader
self._deleted = set()
def __getitem__(self, name):
if not (self._loaded or name in self._contents or name in self._deleted):
self.load()
return self._contents[name]
def __setitem__(self, name, value):
self._contents[name] = value
self._deleted.discard(name)
def __delitem__(self, name):
del self._contents[name]
self._deleted.add(name)
def __contains__(self, name):
self.load()
return name in self._contents
def __len__(self):
self.load()
return len(self._contents)
def __iter__(self):
self.load()
return iter(self._contents)
def load(self):
if self._loaded:
return
loaded_contents = self._loader()
loaded_contents.update(self._contents)
self._contents = loaded_contents
self._loaded = True
class XmlDescriptor(XModuleDescriptor): class XmlDescriptor(XModuleDescriptor):
""" """
Mixin class for standardized parsing of from xml Mixin class for standardized parsing of from xml
...@@ -29,27 +78,30 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -29,27 +78,30 @@ class XmlDescriptor(XModuleDescriptor):
""" """
xml_object = etree.fromstring(xml_data) xml_object = etree.fromstring(xml_data)
metadata = {} def metadata_loader():
for attr in ('format', 'graceperiod', 'showanswer', 'rerandomize', 'due'): metadata = {}
from_xml = xml_object.get(attr) for attr in ('format', 'graceperiod', 'showanswer', 'rerandomize', 'due'):
if from_xml is not None: from_xml = xml_object.get(attr)
metadata[attr] = from_xml if from_xml is not None:
metadata[attr] = from_xml
if xml_object.get('graded') is not None:
metadata['graded'] = xml_object.get('graded') == 'true'
if xml_object.get('graded') is not None: if xml_object.get('name') is not None:
metadata['graded'] = xml_object.get('graded') == 'true' metadata['display_name'] = xml_object.get('name')
if xml_object.get('name') is not None: return metadata
metadata['display_name'] = xml_object.get('name')
return cls( return cls(
system, system,
cls.definition_from_xml(xml_object, system), LazyLoadingDict(lambda: cls.definition_from_xml(xml_object, system)),
location=['i4x', location=['i4x',
org, org,
course, course,
xml_object.tag, xml_object.tag,
xml_object.get('slug')], xml_object.get('slug')],
metadata=metadata, metadata=LazyLoadingDict(metadata_loader),
) )
def export_to_xml(self, resource_fs): def export_to_xml(self, resource_fs):
......
...@@ -273,8 +273,11 @@ def add_histogram(module): ...@@ -273,8 +273,11 @@ def add_histogram(module):
module_id = module.id module_id = module.id
histogram = grade_histogram(module_id) histogram = grade_histogram(module_id)
render_histogram = len(histogram) > 0 render_histogram = len(histogram) > 0
staff_context = {'definition': json.dumps(module.definition, indent=4),
'metadata': json.dumps(module.metadata, indent=4), # Cast module.definition and module.metadata to dicts so that json can dump them
# even though they are lazily loaded
staff_context = {'definition': json.dumps(dict(module.definition), indent=4),
'metadata': json.dumps(dict(module.metadata), indent=4),
'element_id': module.location.html_id(), 'element_id': module.location.html_id(),
'histogram': json.dumps(histogram), 'histogram': json.dumps(histogram),
'render_histogram': render_histogram, 'render_histogram': render_histogram,
......
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