Commit fdf81042 by Calen Pennington

Pass XBlock parents down to their children when constructing them, for caching

parent 324d4785
...@@ -80,7 +80,18 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor): ...@@ -80,7 +80,18 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor):
return u'' return u''
@classmethod @classmethod
def _construct(cls, system, contents, error_msg, location): def _construct(cls, system, contents, error_msg, location, for_parent=None):
"""
Build a new ErrorDescriptor. using ``system``.
Arguments:
system (:class:`DescriptorSystem`): The :class:`DescriptorSystem` used
to construct the XBlock that had an error.
contents (unicode): An encoding of the content of the xblock that had an error.
error_msg (unicode): A message describing the error.
location (:class:`UsageKey`): The usage key of the XBlock that had an error.
for_parent (:class:`XBlock`): Optional. The parent of this error block.
"""
if error_msg is None: if error_msg is None:
# this string is not marked for translation because we don't have # this string is not marked for translation because we don't have
...@@ -110,6 +121,7 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor): ...@@ -110,6 +121,7 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor):
# real scope keys # real scope keys
ScopeIds(None, 'error', location, location), ScopeIds(None, 'error', location, location),
field_data, field_data,
for_parent=for_parent,
) )
def get_context(self): def get_context(self):
...@@ -139,6 +151,7 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor): ...@@ -139,6 +151,7 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor):
str(descriptor), str(descriptor),
error_msg, error_msg,
location=descriptor.location, location=descriptor.location,
for_parent=descriptor.get_parent() if descriptor.has_cached_parent else None
) )
@classmethod @classmethod
......
...@@ -223,7 +223,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): ...@@ -223,7 +223,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin):
self.course_id = course_key self.course_id = course_key
self.cached_metadata = cached_metadata self.cached_metadata = cached_metadata
def load_item(self, location): def load_item(self, location, for_parent=None): # pylint: disable=method-hidden
""" """
Return an XModule instance for the specified location Return an XModule instance for the specified location
""" """
...@@ -292,7 +292,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): ...@@ -292,7 +292,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin):
field_data = KvsFieldData(kvs) field_data = KvsFieldData(kvs)
scope_ids = ScopeIds(None, category, location, location) scope_ids = ScopeIds(None, category, location, location)
module = self.construct_xblock_from_class(class_, scope_ids, field_data) module = self.construct_xblock_from_class(class_, scope_ids, field_data, for_parent=for_parent)
if self.cached_metadata is not None: if self.cached_metadata is not None:
# parent container pointers don't differentiate between draft and non-draft # parent container pointers don't differentiate between draft and non-draft
# so when we do the lookup, we should do so with a non-draft location # so when we do the lookup, we should do so with a non-draft location
...@@ -883,7 +883,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -883,7 +883,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
apply_cached_metadata=bool, apply_cached_metadata=bool,
using_descriptor_system="None|CachingDescriptorSystem" using_descriptor_system="None|CachingDescriptorSystem"
) )
def _load_item(self, course_key, item, data_cache, apply_cached_metadata=True, using_descriptor_system=None): def _load_item(self, course_key, item, data_cache,
apply_cached_metadata=True, using_descriptor_system=None, for_parent=None):
""" """
Load an XModuleDescriptor from item, using the children stored in data_cache Load an XModuleDescriptor from item, using the children stored in data_cache
...@@ -898,6 +899,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -898,6 +899,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
purposes. purposes.
using_descriptor_system (CachingDescriptorSystem): The existing CachingDescriptorSystem using_descriptor_system (CachingDescriptorSystem): The existing CachingDescriptorSystem
to add data to, and to load the XBlocks from. to add data to, and to load the XBlocks from.
for_parent (:class:`XBlock`): The parent of the XBlock being loaded.
""" """
course_key = self.fill_in_run(course_key) course_key = self.fill_in_run(course_key)
location = Location._from_deprecated_son(item['location'], course_key.run) location = Location._from_deprecated_son(item['location'], course_key.run)
...@@ -942,9 +944,9 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -942,9 +944,9 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
system.module_data.update(data_cache) system.module_data.update(data_cache)
system.cached_metadata.update(cached_metadata) system.cached_metadata.update(cached_metadata)
return system.load_item(location) return system.load_item(location, for_parent=for_parent)
def _load_items(self, course_key, items, depth=0, using_descriptor_system=None): def _load_items(self, course_key, items, depth=0, using_descriptor_system=None, for_parent=None):
""" """
Load a list of xmodules from the data in items, with children cached up Load a list of xmodules from the data in items, with children cached up
to specified depth to specified depth
...@@ -960,7 +962,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -960,7 +962,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
item, item,
data_cache, data_cache,
using_descriptor_system=using_descriptor_system, using_descriptor_system=using_descriptor_system,
apply_cached_metadata=self._should_apply_cached_metadata(item, depth) apply_cached_metadata=self._should_apply_cached_metadata(item, depth),
for_parent=for_parent,
) )
for item in items for item in items
] ]
...@@ -1078,7 +1081,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1078,7 +1081,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
except ItemNotFoundError: except ItemNotFoundError:
return False return False
def get_item(self, usage_key, depth=0, using_descriptor_system=None): def get_item(self, usage_key, depth=0, using_descriptor_system=None, for_parent=None, **kwargs):
""" """
Returns an XModuleDescriptor instance for the item at location. Returns an XModuleDescriptor instance for the item at location.
...@@ -1101,7 +1104,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1101,7 +1104,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
usage_key.course_key, usage_key.course_key,
[item], [item],
depth, depth,
using_descriptor_system=using_descriptor_system using_descriptor_system=using_descriptor_system,
for_parent=for_parent,
)[0] )[0]
return module return module
...@@ -1293,6 +1297,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1293,6 +1297,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
# so we use the location for both. # so we use the location for both.
ScopeIds(None, block_type, location, location), ScopeIds(None, block_type, location, location),
dbmodel, dbmodel,
for_parent=kwargs.get('for_parent'),
) )
if fields is not None: if fields is not None:
for key, value in fields.iteritems(): for key, value in fields.iteritems():
...@@ -1341,11 +1346,16 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1341,11 +1346,16 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
block_id: a unique identifier for the new item. If not supplied, block_id: a unique identifier for the new item. If not supplied,
a new identifier will be generated a new identifier will be generated
""" """
xblock = self.create_item(user_id, parent_usage_key.course_key, block_type, block_id=block_id, **kwargs)
# attach to parent if given # attach to parent if given
if 'detached' not in xblock._class_tags: parent = None
if parent_usage_key is not None:
parent = self.get_item(parent_usage_key) parent = self.get_item(parent_usage_key)
kwargs.setdefault('for_parent', parent)
xblock = self.create_item(user_id, parent_usage_key.course_key, block_type, block_id=block_id, **kwargs)
if parent is not None and 'detached' not in xblock._class_tags:
# Originally added to support entrance exams (settings.FEATURES.get('ENTRANCE_EXAMS')) # Originally added to support entrance exams (settings.FEATURES.get('ENTRANCE_EXAMS'))
if kwargs.get('position') is None: if kwargs.get('position') is None:
parent.children.append(xblock.location) parent.children.append(xblock.location)
......
...@@ -82,12 +82,14 @@ class DraftModuleStore(MongoModuleStore): ...@@ -82,12 +82,14 @@ class DraftModuleStore(MongoModuleStore):
""" """
def get_published(): def get_published():
return wrap_draft(super(DraftModuleStore, self).get_item( return wrap_draft(super(DraftModuleStore, self).get_item(
usage_key, depth=depth, using_descriptor_system=using_descriptor_system usage_key, depth=depth, using_descriptor_system=using_descriptor_system,
for_parent=kwargs.get('for_parent'),
)) ))
def get_draft(): def get_draft():
return wrap_draft(super(DraftModuleStore, self).get_item( return wrap_draft(super(DraftModuleStore, self).get_item(
as_draft(usage_key), depth=depth, using_descriptor_system=using_descriptor_system as_draft(usage_key), depth=depth, using_descriptor_system=using_descriptor_system,
for_parent=kwargs.get('for_parent')
)) ))
# return the published version if ModuleStoreEnum.RevisionOption.published_only is requested # return the published version if ModuleStoreEnum.RevisionOption.published_only is requested
......
...@@ -227,6 +227,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): ...@@ -227,6 +227,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin):
class_, class_,
ScopeIds(None, block_key.type, definition_id, block_locator), ScopeIds(None, block_key.type, definition_id, block_locator),
field_data, field_data,
for_parent=kwargs.get('for_parent')
) )
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
log.warning("Failed to load descriptor", exc_info=True) log.warning("Failed to load descriptor", exc_info=True)
......
...@@ -351,6 +351,8 @@ class StackTraceCounter(object): ...@@ -351,6 +351,8 @@ class StackTraceCounter(object):
args: The positional arguments to capture at this stack frame args: The positional arguments to capture at this stack frame
kwargs: The keyword arguments to capture at this stack frame kwargs: The keyword arguments to capture at this stack frame
""" """
# pylint: disable=broad-except
stack = traceback.extract_stack()[:-2] stack = traceback.extract_stack()[:-2]
if self._top_of_stack in stack: if self._top_of_stack in stack:
...@@ -419,6 +421,7 @@ class StackTraceCounter(object): ...@@ -419,6 +421,7 @@ class StackTraceCounter(object):
""" """
stacks = StackTraceCounter(stack_depth, include_arguments) stacks = StackTraceCounter(stack_depth, include_arguments)
# pylint: disable=missing-docstring
@functools.wraps(func) @functools.wraps(func)
def capture(*args, **kwargs): def capture(*args, **kwargs):
stacks.capture_stack(args, kwargs) stacks.capture_stack(args, kwargs)
......
...@@ -250,9 +250,9 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): ...@@ -250,9 +250,9 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
# TODO (vshnayder): we are somewhat architecturally confused in the loading code: # TODO (vshnayder): we are somewhat architecturally confused in the loading code:
# load_item should actually be get_instance, because it expects the course-specific # load_item should actually be get_instance, because it expects the course-specific
# policy to be loaded. For now, just add the course_id here... # policy to be loaded. For now, just add the course_id here...
def load_item(usage_key): def load_item(usage_key, for_parent=None):
"""Return the XBlock for the specified location""" """Return the XBlock for the specified location"""
return xmlstore.get_item(usage_key) return xmlstore.get_item(usage_key, for_parent=for_parent)
resources_fs = OSFS(xmlstore.data_dir / course_dir) resources_fs = OSFS(xmlstore.data_dir / course_dir)
......
...@@ -79,10 +79,14 @@ class ConditionalFactory(object): ...@@ -79,10 +79,14 @@ class ConditionalFactory(object):
child_descriptor.render = lambda view, context=None: descriptor_system.render(child_descriptor, view, context) child_descriptor.render = lambda view, context=None: descriptor_system.render(child_descriptor, view, context)
child_descriptor.location = source_location.replace(category='html', name='child') child_descriptor.location = source_location.replace(category='html', name='child')
descriptor_system.load_item = { def load_item(usage_id, for_parent=None): # pylint: disable=unused-argument
child_descriptor.location: child_descriptor, """Test-only implementation of load_item that simply returns static xblocks."""
source_location: source_descriptor return {
}.get child_descriptor.location: child_descriptor,
source_location: source_descriptor
}.get(usage_id)
descriptor_system.load_item = load_item
system.descriptor_runtime = descriptor_system system.descriptor_runtime = descriptor_system
......
...@@ -9,7 +9,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey, Location ...@@ -9,7 +9,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey, Location
from xmodule.x_module import XModuleDescriptor, XModule, STUDENT_VIEW from xmodule.x_module import XModuleDescriptor, XModule, STUDENT_VIEW
from mock import MagicMock, Mock, patch from mock import MagicMock, Mock, patch
from xblock.runtime import Runtime, IdReader from xblock.runtime import Runtime, IdReader
from xblock.field_data import FieldData from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds from xblock.fields import ScopeIds
from xblock.test.tools import unabc from xblock.test.tools import unabc
...@@ -43,10 +43,11 @@ class TestErrorModule(SetupTestErrorModules): ...@@ -43,10 +43,11 @@ class TestErrorModule(SetupTestErrorModules):
self.assertIn(repr(self.valid_xml), context_repr) self.assertIn(repr(self.valid_xml), context_repr)
def test_error_module_from_descriptor(self): def test_error_module_from_descriptor(self):
descriptor = MagicMock([XModuleDescriptor], descriptor = MagicMock(
runtime=self.system, spec=XModuleDescriptor,
location=self.location, runtime=self.system,
_field_data=self.valid_xml) location=self.location,
)
error_descriptor = ErrorDescriptor.from_descriptor( error_descriptor = ErrorDescriptor.from_descriptor(
descriptor, self.error_msg) descriptor, self.error_msg)
...@@ -81,10 +82,11 @@ class TestNonStaffErrorModule(SetupTestErrorModules): ...@@ -81,10 +82,11 @@ class TestNonStaffErrorModule(SetupTestErrorModules):
self.assertNotIn(repr(self.valid_xml), context_repr) self.assertNotIn(repr(self.valid_xml), context_repr)
def test_error_module_from_descriptor(self): def test_error_module_from_descriptor(self):
descriptor = MagicMock([XModuleDescriptor], descriptor = MagicMock(
runtime=self.system, spec=XModuleDescriptor,
location=self.location, runtime=self.system,
_field_data=self.valid_xml) location=self.location,
)
error_descriptor = NonStaffErrorDescriptor.from_descriptor( error_descriptor = NonStaffErrorDescriptor.from_descriptor(
descriptor, self.error_msg) descriptor, self.error_msg)
...@@ -122,7 +124,7 @@ class TestErrorModuleConstruction(unittest.TestCase): ...@@ -122,7 +124,7 @@ class TestErrorModuleConstruction(unittest.TestCase):
def setUp(self): def setUp(self):
# pylint: disable=abstract-class-instantiated # pylint: disable=abstract-class-instantiated
super(TestErrorModuleConstruction, self).setUp() super(TestErrorModuleConstruction, self).setUp()
field_data = Mock(spec=FieldData) field_data = DictFieldData({})
self.descriptor = BrokenDescriptor( self.descriptor = BrokenDescriptor(
TestRuntime(Mock(spec=IdReader), field_data), TestRuntime(Mock(spec=IdReader), field_data),
field_data, field_data,
......
...@@ -49,7 +49,7 @@ class InMemorySystem(XMLParsingSystem, MakoDescriptorSystem): # pylint: disable ...@@ -49,7 +49,7 @@ class InMemorySystem(XMLParsingSystem, MakoDescriptorSystem): # pylint: disable
self._descriptors[descriptor.location.to_deprecated_string()] = descriptor self._descriptors[descriptor.location.to_deprecated_string()] = descriptor
return descriptor return descriptor
def load_item(self, location): # pylint: disable=method-hidden def load_item(self, location, for_parent=None): # pylint: disable=method-hidden, unused-argument
"""Return the descriptor loaded for `location`""" """Return the descriptor loaded for `location`"""
return self._descriptors[location.to_deprecated_string()] return self._descriptors[location.to_deprecated_string()]
......
...@@ -295,7 +295,6 @@ class XModuleMixin(XModuleFields, XBlockMixin): ...@@ -295,7 +295,6 @@ class XModuleMixin(XModuleFields, XBlockMixin):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.xmodule_runtime = None self.xmodule_runtime = None
self._child_instances = None
super(XModuleMixin, self).__init__(*args, **kwargs) super(XModuleMixin, self).__init__(*args, **kwargs)
...@@ -424,39 +423,45 @@ class XModuleMixin(XModuleFields, XBlockMixin): ...@@ -424,39 +423,45 @@ class XModuleMixin(XModuleFields, XBlockMixin):
else: else:
return [self.display_name_with_default] return [self.display_name_with_default]
def get_children(self, usage_key_filter=None): def get_children(self, usage_id_filter=None, usage_key_filter=None): # pylint: disable=arguments-differ
"""Returns a list of XBlock instances for the children of """Returns a list of XBlock instances for the children of
this module""" this module"""
if not self.has_children: # Be backwards compatible with callers using usage_key_filter
return [] if usage_id_filter is None and usage_key_filter is not None:
usage_id_filter = usage_key_filter
if self._child_instances is None: return [
self._child_instances = [] # pylint: disable=attribute-defined-outside-init child
for child_loc in self.children: for child
# Skip if it doesn't satisfy the filter function in super(XModuleMixin, self).get_children(usage_id_filter)
if usage_key_filter and not usage_key_filter(child_loc): if child is not None
continue ]
try:
child = self.runtime.get_block(child_loc)
if child is None:
continue
child.runtime.export_fs = self.runtime.export_fs def get_child(self, usage_id):
except ItemNotFoundError: """
log.warning(u'Unable to load item {loc}, skipping'.format(loc=child_loc)) Return the child XBlock identified by ``usage_id``, or ``None`` if there
dog_stats_api.increment( is an error while retrieving the block.
"xmodule.item_not_found_error", """
tags=[ try:
u"course_id:{}".format(child_loc.course_key), child = super(XModuleMixin, self).get_child(usage_id)
u"block_type:{}".format(child_loc.block_type), except ItemNotFoundError:
u"parent_block_type:{}".format(self.location.block_type), log.warning(u'Unable to load item %s, skipping', usage_id)
] dog_stats_api.increment(
) "xmodule.item_not_found_error",
continue tags=[
self._child_instances.append(child) u"course_id:{}".format(usage_id.course_key),
u"block_type:{}".format(usage_id.block_type),
u"parent_block_type:{}".format(self.location.block_type),
]
)
return None
if child is None:
return None
return self._child_instances child.runtime.export_fs = self.runtime.export_fs
return child
def get_required_module_descriptors(self): def get_required_module_descriptors(self):
"""Returns a list of XModuleDescriptor instances upon which this module depends, but are """Returns a list of XModuleDescriptor instances upon which this module depends, but are
...@@ -573,7 +578,7 @@ class XModuleMixin(XModuleFields, XBlockMixin): ...@@ -573,7 +578,7 @@ class XModuleMixin(XModuleFields, XBlockMixin):
self.scope_ids = self.scope_ids._replace(user_id=user_id) self.scope_ids = self.scope_ids._replace(user_id=user_id)
# Clear out any cached instantiated children. # Clear out any cached instantiated children.
self._child_instances = None self.clear_child_cache()
# Clear out any cached field data scoped to the old user. # Clear out any cached field data scoped to the old user.
for field in self.fields.values(): for field in self.fields.values():
...@@ -767,7 +772,6 @@ class XModule(XModuleMixin, HTMLSnippet, XBlock): # pylint: disable=abstract-me ...@@ -767,7 +772,6 @@ class XModule(XModuleMixin, HTMLSnippet, XBlock): # pylint: disable=abstract-me
# Set the descriptor first so that we can proxy to it # Set the descriptor first so that we can proxy to it
self.descriptor = descriptor self.descriptor = descriptor
self._loaded_children = None
self._runtime = None self._runtime = None
super(XModule, self).__init__(*args, **kwargs) super(XModule, self).__init__(*args, **kwargs)
self.runtime.xmodule_instance = self self.runtime.xmodule_instance = self
...@@ -820,6 +824,19 @@ class XModule(XModuleMixin, HTMLSnippet, XBlock): # pylint: disable=abstract-me ...@@ -820,6 +824,19 @@ class XModule(XModuleMixin, HTMLSnippet, XBlock): # pylint: disable=abstract-me
response_data = self.handle_ajax(suffix, request_post) response_data = self.handle_ajax(suffix, request_post)
return Response(response_data, content_type='application/json') return Response(response_data, content_type='application/json')
def get_child(self, usage_id):
if usage_id in self._child_cache:
return self._child_cache[usage_id]
# Take advantage of the children cache that the descriptor might have
child_descriptor = self.descriptor.get_child(usage_id)
child_block = None
if child_descriptor is not None:
child_block = self.system.get_module(child_descriptor)
self._child_cache[usage_id] = child_block
return child_block
def get_child_descriptors(self): def get_child_descriptors(self):
""" """
Returns the descriptors of the child modules Returns the descriptors of the child modules
...@@ -1103,6 +1120,7 @@ class XModuleDescriptor(XModuleMixin, HTMLSnippet, ResourceTemplates, XBlock): ...@@ -1103,6 +1120,7 @@ class XModuleDescriptor(XModuleMixin, HTMLSnippet, ResourceTemplates, XBlock):
descriptor=self, descriptor=self,
scope_ids=self.scope_ids, scope_ids=self.scope_ids,
field_data=self._field_data, field_data=self._field_data,
for_parent=self.get_parent() if self.has_cached_parent else None
) )
self.xmodule_runtime.xmodule_instance.save() self.xmodule_runtime.xmodule_instance.save()
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
...@@ -1340,9 +1358,9 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime): # p ...@@ -1340,9 +1358,9 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime): # p
else: else:
self.get_policy = lambda u: {} self.get_policy = lambda u: {}
def get_block(self, usage_id): def get_block(self, usage_id, for_parent=None):
"""See documentation for `xblock.runtime:Runtime.get_block`""" """See documentation for `xblock.runtime:Runtime.get_block`"""
return self.load_item(usage_id) return self.load_item(usage_id, for_parent=for_parent)
def get_field_provenance(self, xblock, field): def get_field_provenance(self, xblock, field):
""" """
...@@ -1681,8 +1699,8 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime): # pylin ...@@ -1681,8 +1699,8 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime): # pylin
assert self.xmodule_instance is not None assert self.xmodule_instance is not None
return self.handler_url(self.xmodule_instance, 'xmodule_handler', '', '').rstrip('/?') return self.handler_url(self.xmodule_instance, 'xmodule_handler', '', '').rstrip('/?')
def get_block(self, block_id): def get_block(self, block_id, for_parent=None):
return self.get_module(self.descriptor_runtime.get_block(block_id)) return self.get_module(self.descriptor_runtime.get_block(block_id, for_parent=for_parent))
def resource_url(self, resource): def resource_url(self, resource):
raise NotImplementedError("edX Platform doesn't currently implement XBlock resource urls") raise NotImplementedError("edX Platform doesn't currently implement XBlock resource urls")
......
...@@ -173,18 +173,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase): ...@@ -173,18 +173,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase):
TEST_DATA = { TEST_DATA = {
# (providers, course_width, enable_ccx): # of sql queries, # of mongo queries, # of xblocks # (providers, course_width, enable_ccx): # of sql queries, # of mongo queries, # of xblocks
('no_overrides', 1, True): (26, 7, 19), ('no_overrides', 1, True): (26, 7, 14),
('no_overrides', 2, True): (134, 7, 131), ('no_overrides', 2, True): (134, 7, 85),
('no_overrides', 3, True): (594, 7, 537), ('no_overrides', 3, True): (594, 7, 336),
('ccx', 1, True): (26, 7, 47), ('ccx', 1, True): (26, 7, 14),
('ccx', 2, True): (134, 7, 455), ('ccx', 2, True): (134, 7, 85),
('ccx', 3, True): (594, 7, 2037), ('ccx', 3, True): (594, 7, 336),
('no_overrides', 1, False): (26, 7, 19), ('no_overrides', 1, False): (26, 7, 14),
('no_overrides', 2, False): (134, 7, 131), ('no_overrides', 2, False): (134, 7, 85),
('no_overrides', 3, False): (594, 7, 537), ('no_overrides', 3, False): (594, 7, 336),
('ccx', 1, False): (26, 7, 19), ('ccx', 1, False): (26, 7, 14),
('ccx', 2, False): (134, 7, 131), ('ccx', 2, False): (134, 7, 85),
('ccx', 3, False): (594, 7, 537), ('ccx', 3, False): (594, 7, 336),
} }
......
...@@ -560,7 +560,12 @@ class CourseBlocksAndNavigation(ListAPIView): ...@@ -560,7 +560,12 @@ class CourseBlocksAndNavigation(ListAPIView):
) )
# verify the user has access to this block # verify the user has access to this block
if not has_access(request_info.request.user, 'load', block_info.block, course_key=request_info.course.id): if (block_info.block is None or not has_access(
request_info.request.user,
'load',
block_info.block,
course_key=request_info.course.id
)):
return return
# add the block's value to the result # add the block's value to the result
......
...@@ -610,11 +610,11 @@ class TestTOC(ModuleStoreTestCase): ...@@ -610,11 +610,11 @@ class TestTOC(ModuleStoreTestCase):
# Split makes 6 queries to load the course to depth 2: # Split makes 6 queries to load the course to depth 2:
# - load the structure # - load the structure
# - load 5 definitions # - load 5 definitions
# Split makes 6 queries to render the toc: # Split makes 5 queries to render the toc:
# - it loads the active version at the start of the bulk operation # - it loads the active version at the start of the bulk operation
# - it loads 5 definitions, because it instantiates the a CourseModule and 4 VideoModules # - it loads 4 definitions, because it instantiates 4 VideoModules
# each of which access a Scope.content field in __init__ # each of which access a Scope.content field in __init__
@ddt.data((ModuleStoreEnum.Type.mongo, 3, 0, 0), (ModuleStoreEnum.Type.split, 6, 0, 6)) @ddt.data((ModuleStoreEnum.Type.mongo, 3, 0, 0), (ModuleStoreEnum.Type.split, 6, 0, 5))
@ddt.unpack @ddt.unpack
def test_toc_toy_from_chapter(self, default_ms, setup_finds, setup_sends, toc_finds): def test_toc_toy_from_chapter(self, default_ms, setup_finds, setup_sends, toc_finds):
with self.store.default_store(default_ms): with self.store.default_store(default_ms):
...@@ -634,9 +634,10 @@ class TestTOC(ModuleStoreTestCase): ...@@ -634,9 +634,10 @@ class TestTOC(ModuleStoreTestCase):
'format': '', 'due': None, 'active': False}], 'format': '', 'due': None, 'active': False}],
'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) 'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
course = self.store.get_course(self.toy_course.id, depth=2)
with check_mongo_calls(toc_finds): with check_mongo_calls(toc_finds):
actual = render.toc_for_course( actual = render.toc_for_course(
self.request, self.toy_course, self.chapter, None, self.field_data_cache self.request, course, self.chapter, None, self.field_data_cache
) )
for toc_section in expected: for toc_section in expected:
self.assertIn(toc_section, actual) self.assertIn(toc_section, actual)
...@@ -648,11 +649,11 @@ class TestTOC(ModuleStoreTestCase): ...@@ -648,11 +649,11 @@ class TestTOC(ModuleStoreTestCase):
# Split makes 6 queries to load the course to depth 2: # Split makes 6 queries to load the course to depth 2:
# - load the structure # - load the structure
# - load 5 definitions # - load 5 definitions
# Split makes 2 queries to render the toc: # Split makes 5 queries to render the toc:
# - it loads the active version at the start of the bulk operation # - it loads the active version at the start of the bulk operation
# - it loads 5 definitions, because it instantiates the a CourseModule and 4 VideoModules # - it loads 4 definitions, because it instantiates 4 VideoModules
# each of which access a Scope.content field in __init__ # each of which access a Scope.content field in __init__
@ddt.data((ModuleStoreEnum.Type.mongo, 3, 0, 0), (ModuleStoreEnum.Type.split, 6, 0, 6)) @ddt.data((ModuleStoreEnum.Type.mongo, 3, 0, 0), (ModuleStoreEnum.Type.split, 6, 0, 5))
@ddt.unpack @ddt.unpack
def test_toc_toy_from_section(self, default_ms, setup_finds, setup_sends, toc_finds): def test_toc_toy_from_section(self, default_ms, setup_finds, setup_sends, toc_finds):
with self.store.default_store(default_ms): with self.store.default_store(default_ms):
......
...@@ -32,7 +32,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c ...@@ -32,7 +32,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c
-e git+https://github.com/jazkarta/ccx-keys.git@e6b03704b1bb97c1d2f31301ecb4e3a687c536ea#egg=ccx-keys -e git+https://github.com/jazkarta/ccx-keys.git@e6b03704b1bb97c1d2f31301ecb4e3a687c536ea#egg=ccx-keys
# Our libraries: # Our libraries:
-e git+https://github.com/edx/XBlock.git@e1831fa86bff778ffe1308e00d8ed51b26f7c047#egg=XBlock -e git+https://github.com/cpennington/XBlock.git@64f6bdaf6c987cdea4798d8b1a1fa8e471c20a21#egg=XBlock
-e git+https://github.com/edx/codejail.git@6b17c33a89bef0ac510926b1d7fea2748b73aadd#egg=codejail -e git+https://github.com/edx/codejail.git@6b17c33a89bef0ac510926b1d7fea2748b73aadd#egg=codejail
-e git+https://github.com/edx/js-test-tool.git@v0.1.6#egg=js_test_tool -e git+https://github.com/edx/js-test-tool.git@v0.1.6#egg=js_test_tool
-e git+https://github.com/edx/event-tracking.git@0.2.0#egg=event-tracking -e git+https://github.com/edx/event-tracking.git@0.2.0#egg=event-tracking
......
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