Revert "Revert "refactor: move xmodule folder to root""
This commit is contained in:
committed by
GitHub
parent
46d848be41
commit
a389a9ff10
193
xmodule/modulestore/split_mongo/split_mongo_kvs.py
Normal file
193
xmodule/modulestore/split_mongo/split_mongo_kvs.py
Normal file
@@ -0,0 +1,193 @@
|
||||
# lint-amnesty, pylint: disable=missing-module-docstring
|
||||
|
||||
import copy
|
||||
from collections import namedtuple
|
||||
|
||||
from xblock.core import XBlockAside
|
||||
from xblock.exceptions import InvalidScopeError
|
||||
from xblock.fields import Scope
|
||||
|
||||
from xmodule.modulestore.inheritance import InheritanceKeyValueStore
|
||||
|
||||
from .definition_lazy_loader import DefinitionLazyLoader
|
||||
|
||||
# id is a BlockUsageLocator, def_id is the definition's guid
|
||||
SplitMongoKVSid = namedtuple('SplitMongoKVSid', 'id, def_id')
|
||||
|
||||
|
||||
class SplitMongoKVS(InheritanceKeyValueStore):
|
||||
"""
|
||||
A KeyValueStore that maps keyed data access to one of the 3 data areas
|
||||
known to the MongoModuleStore (data, children, and metadata)
|
||||
"""
|
||||
|
||||
VALID_SCOPES = (Scope.parent, Scope.children, Scope.settings, Scope.content)
|
||||
|
||||
def __init__(self, definition, initial_values, default_values, parent, aside_fields=None, field_decorator=None):
|
||||
"""
|
||||
|
||||
:param definition: either a lazyloader or definition id for the definition
|
||||
:param initial_values: a dictionary of the locally set values
|
||||
:param default_values: any Scope.settings field defaults that are set locally
|
||||
(copied from a template block with copy_from_template)
|
||||
"""
|
||||
# deepcopy so that manipulations of fields does not pollute the source
|
||||
super().__init__(copy.deepcopy(initial_values))
|
||||
self._definition = definition # either a DefinitionLazyLoader or the db id of the definition.
|
||||
# if the db id, then the definition is presumed to be loaded into _fields
|
||||
|
||||
self._defaults = default_values
|
||||
# a decorator function for field values (to be called when a field is accessed)
|
||||
if field_decorator is None:
|
||||
self.field_decorator = lambda x: x
|
||||
else:
|
||||
self.field_decorator = field_decorator
|
||||
|
||||
self.parent = parent
|
||||
self.aside_fields = aside_fields if aside_fields else {}
|
||||
|
||||
def get(self, key):
|
||||
if key.block_family == XBlockAside.entry_point: # lint-amnesty, pylint: disable=no-else-raise
|
||||
if key.scope not in self.VALID_SCOPES:
|
||||
raise InvalidScopeError(key, self.VALID_SCOPES)
|
||||
|
||||
if key.block_scope_id.block_type not in self.aside_fields:
|
||||
# load the definition to see if it has the aside_fields
|
||||
self._load_definition()
|
||||
if key.block_scope_id.block_type not in self.aside_fields:
|
||||
raise KeyError()
|
||||
aside_fields = self.aside_fields[key.block_scope_id.block_type]
|
||||
# load the field, if needed
|
||||
if key.field_name not in aside_fields:
|
||||
self._load_definition()
|
||||
|
||||
if key.field_name in aside_fields:
|
||||
return self.field_decorator(aside_fields[key.field_name])
|
||||
|
||||
raise KeyError()
|
||||
else:
|
||||
# load the field, if needed
|
||||
if key.field_name not in self._fields:
|
||||
if key.scope == Scope.parent:
|
||||
return self.parent
|
||||
if key.scope == Scope.children: # lint-amnesty, pylint: disable=no-else-raise
|
||||
# didn't find children in _fields; so, see if there's a default
|
||||
raise KeyError()
|
||||
elif key.scope == Scope.settings:
|
||||
# get default which may be the inherited value
|
||||
raise KeyError()
|
||||
elif key.scope == Scope.content:
|
||||
if isinstance(self._definition, DefinitionLazyLoader):
|
||||
self._load_definition()
|
||||
else:
|
||||
raise KeyError()
|
||||
else:
|
||||
raise InvalidScopeError(key)
|
||||
|
||||
if key.field_name in self._fields:
|
||||
field_value = self._fields[key.field_name]
|
||||
# return the "decorated" field value
|
||||
return self.field_decorator(field_value)
|
||||
|
||||
return None
|
||||
|
||||
def set(self, key, value):
|
||||
# handle any special cases
|
||||
if key.scope not in self.VALID_SCOPES:
|
||||
raise InvalidScopeError(key, self.VALID_SCOPES)
|
||||
if key.scope == Scope.content:
|
||||
self._load_definition()
|
||||
|
||||
if key.block_family == XBlockAside.entry_point:
|
||||
if key.scope == Scope.children:
|
||||
raise InvalidScopeError(key)
|
||||
|
||||
self.aside_fields.setdefault(key.block_scope_id.block_type, {})[key.field_name] = value
|
||||
else:
|
||||
# set the field
|
||||
self._fields[key.field_name] = value
|
||||
|
||||
# This function is currently incomplete: it doesn't handle side effects.
|
||||
# To complete this function, here is some pseudocode for what should happen:
|
||||
#
|
||||
# if key.scope == Scope.children:
|
||||
# remove inheritance from any exchildren
|
||||
# add inheritance to any new children
|
||||
# if key.scope == Scope.settings:
|
||||
# if inheritable, push down to children
|
||||
|
||||
def delete(self, key):
|
||||
# handle any special cases
|
||||
if key.scope not in self.VALID_SCOPES:
|
||||
raise InvalidScopeError(key, self.VALID_SCOPES)
|
||||
if key.scope == Scope.content:
|
||||
self._load_definition()
|
||||
|
||||
if key.block_family == XBlockAside.entry_point:
|
||||
if key.scope == Scope.children:
|
||||
raise InvalidScopeError(key)
|
||||
|
||||
if key.block_scope_id.block_type in self.aside_fields \
|
||||
and key.field_name in self.aside_fields[key.block_scope_id.block_type]:
|
||||
del self.aside_fields[key.block_scope_id.block_type][key.field_name]
|
||||
else:
|
||||
# delete the field value
|
||||
if key.field_name in self._fields:
|
||||
del self._fields[key.field_name]
|
||||
|
||||
def has(self, key):
|
||||
"""
|
||||
Is the given field explicitly set in this kvs (neither inherited nor default)
|
||||
"""
|
||||
# handle any special cases
|
||||
if key.scope not in self.VALID_SCOPES:
|
||||
return False
|
||||
|
||||
if key.scope == Scope.content:
|
||||
self._load_definition()
|
||||
elif key.scope == Scope.parent:
|
||||
return True
|
||||
|
||||
if key.block_family == XBlockAside.entry_point:
|
||||
if key.scope == Scope.children:
|
||||
return False
|
||||
|
||||
b_type = key.block_scope_id.block_type
|
||||
return b_type in self.aside_fields and key.field_name in self.aside_fields[b_type]
|
||||
else:
|
||||
# it's not clear whether inherited values should return True. Right now they don't
|
||||
# if someone changes it so that they do, then change any tests of field.name in xx._field_data
|
||||
return key.field_name in self._fields
|
||||
|
||||
def has_default_value(self, field_name):
|
||||
"""
|
||||
Is the given field has default value in this kvs
|
||||
"""
|
||||
return field_name in self._defaults
|
||||
|
||||
def default(self, key):
|
||||
"""
|
||||
Check to see if the default should be from the template's defaults (if any)
|
||||
rather than the global default or inheritance.
|
||||
"""
|
||||
if self._defaults and key.field_name in self._defaults:
|
||||
return self._defaults[key.field_name]
|
||||
# If not, try inheriting from a parent, then use the XBlock type's normal default value:
|
||||
return super().default(key)
|
||||
|
||||
def _load_definition(self):
|
||||
"""
|
||||
Update fields w/ the lazily loaded definitions
|
||||
"""
|
||||
if isinstance(self._definition, DefinitionLazyLoader):
|
||||
persisted_definition = self._definition.fetch()
|
||||
if persisted_definition is not None:
|
||||
fields = self._definition.field_converter(persisted_definition.get('fields'))
|
||||
self._fields.update(fields)
|
||||
aside_fields_p = persisted_definition.get('aside_fields')
|
||||
if aside_fields_p:
|
||||
aside_fields = self._definition.field_converter(aside_fields_p)
|
||||
for aside_type, fields in aside_fields.items():
|
||||
self.aside_fields.setdefault(aside_type, {}).update(fields)
|
||||
# do we want to cache any of the edit_info?
|
||||
self._definition = None # already loaded
|
||||
Reference in New Issue
Block a user