diff --git a/common/lib/xmodule/xmodule/modulestore/xml.py b/common/lib/xmodule/xmodule/modulestore/xml.py index 349975ea77..677b2e938e 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml.py +++ b/common/lib/xmodule/xmodule/modulestore/xml.py @@ -422,7 +422,32 @@ class XMLModuleStore(ModuleStoreBase): # breaks metadata inheritance via get_children(). Instead # (actually, in addition to, for now), we do a final inheritance pass # after we have the course descriptor. - #XModuleDescriptor.compute_inherited_metadata(course_descriptor) + def compute_inherited_metadata(descriptor): + """Given a descriptor, traverse all of its descendants and do metadata + inheritance. Should be called on a CourseDescriptor after importing a + course. + + NOTE: This means that there is no such thing as lazy loading at the + moment--this accesses all the children.""" + for child in descriptor.get_children(): + inherit_metadata(child, descriptor.metadata) + compute_inherited_metadata(child) + + def inherit_metadata(descriptor, metadata): + """ + Updates this module with metadata inherited from a containing module. + Only metadata specified in self.inheritable_metadata will + be inherited + """ + # Set all inheritable metadata from kwargs that are + # in self.inheritable_metadata and aren't already set in metadata + for attr in self.inheritable_metadata: + if attr not in self.metadata and attr in metadata: + self._inherited_metadata.add(attr) + self.metadata[attr] = metadata[attr] + + + compute_inherited_metadata(course_descriptor) # now import all pieces of course_info which is expected to be stored # in /info or /info/ diff --git a/common/lib/xmodule/xmodule/runtime.py b/common/lib/xmodule/xmodule/runtime.py new file mode 100644 index 0000000000..8be4bfe346 --- /dev/null +++ b/common/lib/xmodule/xmodule/runtime.py @@ -0,0 +1,92 @@ +from collections import MutableMapping, namedtuple + +from .model import ModuleScope, ModelType + + +class KeyValueStore(object): + """The abstract interface for Key Value Stores.""" + + # Keys are structured to retain information about the scope of the data. + # Stores can use this information however they like to store and retrieve + # data. + Key = namedtuple("Key", "scope, student_id, module_scope_id, field_name") + + def get(key): + pass + + def set(key, value): + pass + + def delete(key): + pass + + +class DbModel(MutableMapping): + """A dictionary-like interface to the fields on a module.""" + + def __init__(self, kvs, module_cls, student_id, usage): + self._kvs = kvs + self._student_id = student_id + self._module_cls = module_cls + self._usage = usage + + def __repr__(self): + return "<{0.__class__.__name__} {0._module_cls!r}>".format(self) + + def __str__(self): + return str(dict(self.iteritems())) + + def _getfield(self, name): + if (not hasattr(self._module_cls, name) or + not isinstance(getattr(self._module_cls, name), ModelType)): + + raise KeyError(name) + + return getattr(self._module_cls, name) + + def _key(self, name): + field = self._getfield(name) + module = field.scope.module + + if module == ModuleScope.ALL: + module_id = None + elif module == ModuleScope.USAGE: + module_id = self._usage.id + elif module == ModuleScope.DEFINITION: + module_id = self._usage.def_id + elif module == ModuleScope.TYPE: + module_id = self.module_type.__name__ + + if field.scope.student: + student_id = self._student_id + else: + student_id = None + + key = KeyValueStore.Key( + scope=field.scope, + student_id=student_id, + module_scope_id=module_id, + field_name=name + ) + return key + + def __getitem__(self, name): + return self._kvs.get(self._key(name)) + + def __setitem__(self, name, value): + self._kvs.set(self._key(name), value) + + def __delitem__(self, name): + self._kvs.delete(self._key(name)) + + def __iter__(self): + return iter(self.keys()) + + def __len__(self): + return len(self.keys()) + + def keys(self): + fields = [field.name for field in self._module_cls.fields] + for namespace_name in self._module_cls.namespaces: + fields.extend(field.name for field in getattr(self._module_cls, namespace_name)) + return fields diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 9cad93ad5b..a2393aa235 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -417,15 +417,10 @@ class XModuleDescriptor(Plugin, HTMLSnippet, ResourceTemplates): except ItemNotFoundError: log.exception('Unable to load item {loc}, skipping'.format(loc=child_loc)) continue - # TODO (vshnayder): this should go away once we have - # proper inheritance support in mongo. The xml - # datastore does all inheritance on course load. - #child.inherit_metadata(self.metadata) self._child_instances.append(child) return self._child_instances - def get_child_by_url_name(self, url_name): """ Return a child XModuleDescriptor with the specified url_name, if it exists, and None otherwise.