Reduce mongo calls on course outline and container pages

This commit is contained in:
Ben McMorran
2014-08-06 17:47:50 -04:00
committed by cahrens
parent 4b6035bafb
commit f3070e6796
11 changed files with 86 additions and 74 deletions

View File

@@ -80,7 +80,7 @@ class ModuleStoreDraftAndPublished(BranchSettingMixin):
raise NotImplementedError
@abstractmethod
def has_changes(self, usage_key):
def has_changes(self, xblock):
raise NotImplementedError
@abstractmethod

View File

@@ -474,14 +474,14 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
store = self._verify_modulestore_support(location.course_key, 'convert_to_draft')
return store.convert_to_draft(location, user_id)
def has_changes(self, usage_key):
def has_changes(self, xblock):
"""
Checks if the given block has unpublished changes
:param usage_key: the block to check
:param xblock: the block to check
:return: True if the draft and published versions differ
"""
store = self._verify_modulestore_support(usage_key.course_key, 'has_changes')
return store.has_changes(usage_key)
store = self._verify_modulestore_support(xblock.location.course_key, 'has_changes')
return store.has_changes(xblock)
def _verify_modulestore_support(self, course_key, method):
"""

View File

@@ -297,6 +297,19 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
value[key] = self._convert_reference_to_key(subvalue)
return jsonfields
def lookup_item(self, location):
"""
Returns the JSON payload of the xblock at location.
"""
try:
json = self.module_data[location]
except KeyError:
json = self.modulestore._find_one(location)
self.module_data[location] = json
return json
# The only thing using this w/ wildcards is contentstore.mongo for asset retrieval
def location_to_query(location, wildcard=True, tag='i4x'):

View File

@@ -599,25 +599,19 @@ class DraftModuleStore(MongoModuleStore):
_internal([root_usage.to_deprecated_son() for root_usage in root_usages])
self.collection.remove({'_id': {'$in': to_be_deleted}}, safe=self.collection.safe)
def has_changes(self, location):
def has_changes(self, xblock):
"""
Check if the xblock or its children have been changed since the last publish.
:param location: location to check
:param xblock: xblock to check
:return: True if the draft and published versions differ
"""
try:
item = self.get_item(location)
# defensively check that the parent's child actually exists
except ItemNotFoundError:
return False
# don't check children if this block has changes (is not public)
if self.compute_publish_state(item) != PublishState.public:
if self.compute_publish_state(xblock) != PublishState.public:
return True
# if this block doesn't have changes, then check its children
elif item.has_children:
return any([self.has_changes(child) for child in item.children])
elif xblock.has_children:
return any([self.has_changes(child) for child in xblock.get_children()])
# otherwise there are no changes
else:
return False
@@ -799,13 +793,11 @@ class DraftModuleStore(MongoModuleStore):
"""
if getattr(xblock, 'is_draft', False):
published_xblock_location = as_published(xblock.location)
published_item = self.collection.find_one(
{'_id': published_xblock_location.to_deprecated_son()}
)
if published_item is None:
try:
xblock.runtime.lookup_item(published_xblock_location)
except ItemNotFoundError:
return PublishState.private
else:
return PublishState.draft
return PublishState.draft
else:
return PublishState.public

View File

@@ -194,16 +194,16 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
location = self._map_revision_to_branch(location, revision=revision)
return SplitMongoModuleStore.get_parent_location(self, location, **kwargs)
def has_changes(self, usage_key):
def has_changes(self, xblock):
"""
Checks if the given block has unpublished changes
:param usage_key: the block to check
:param xblock: the block to check
:return: True if the draft and published versions differ
"""
# TODO for better performance: lookup the courses and get the block entry, don't create the instances
draft = self.get_item(usage_key.for_branch(ModuleStoreEnum.BranchName.draft))
draft = self.get_item(xblock.location.for_branch(ModuleStoreEnum.BranchName.draft))
try:
published = self.get_item(usage_key.for_branch(ModuleStoreEnum.BranchName.published))
published = self.get_item(xblock.location.for_branch(ModuleStoreEnum.BranchName.published))
except ItemNotFoundError:
return True

View File

@@ -374,8 +374,8 @@ class TestMixedModuleStore(unittest.TestCase):
)
# Check that neither xblock has changes
self.assertFalse(self.store.has_changes(test_course.location))
self.assertFalse(self.store.has_changes(chapter.location))
self.assertFalse(self.store.has_changes(test_course))
self.assertFalse(self.store.has_changes(chapter))
@ddt.data('draft', 'split')
def test_has_changes(self, default_ms):
@@ -395,22 +395,22 @@ class TestMixedModuleStore(unittest.TestCase):
)
# Not yet published, so changes are present
self.assertTrue(self.store.has_changes(xblock.location))
self.assertTrue(self.store.has_changes(xblock))
# Publish and verify that there are no unpublished changes
self.store.publish(xblock.location, self.user_id)
self.assertFalse(self.store.has_changes(xblock.location))
newXBlock = self.store.publish(xblock.location, self.user_id)
self.assertFalse(self.store.has_changes(newXBlock))
# Change the component, then check that there now are changes
component = self.store.get_item(xblock.location)
component.display_name = 'Changed Display Name'
component = self.store.update_item(component, self.user_id)
self.assertTrue(self.store.has_changes(component.location))
self.assertTrue(self.store.has_changes(component))
# Publish and verify again
self.store.publish(component.location, self.user_id)
self.assertFalse(self.store.has_changes(component.location))
component = self.store.publish(component.location, self.user_id)
self.assertFalse(self.store.has_changes(component))
@ddt.data(('draft', 7, 2), ('split', 13, 4))
@ddt.unpack
@@ -994,7 +994,7 @@ class TestMixedModuleStore(unittest.TestCase):
# Draft WITH changes
item.display_name = 'new name'
item = self.store.update_item(item, self.user_id)
self.assertTrue(self.store.has_changes(item.location))
self.assertTrue(self.store.has_changes(item))
self.assertEquals(self.store.compute_publish_state(item), PublishState.draft)
@ddt.data('draft', 'split')

View File

@@ -515,10 +515,10 @@ class TestMongoModuleStore(unittest.TestCase):
block_id=location.block_id
)
parent.children += [Location('edX', 'toy', '2012_Fall', 'vertical', 'does_not_exist')]
self.draft_store.update_item(parent, self.dummy_user)
parent = self.draft_store.update_item(parent, self.dummy_user)
# Check the parent for changes should return False and not throw an exception
self.assertFalse(self.draft_store.has_changes(location))
self.assertFalse(self.draft_store.has_changes(parent))
def _create_test_tree(self, name, user_id=None):
"""
@@ -569,6 +569,11 @@ class TestMongoModuleStore(unittest.TestCase):
return locations
def _has_changes(self, location):
""" Helper that returns True if location has changes, False otherwise """
store = self.draft_store
return store.has_changes(store.get_item(location))
def test_has_changes_ancestors(self):
"""
Tests that has_changes() returns true on ancestors when a child is changed
@@ -577,7 +582,7 @@ class TestMongoModuleStore(unittest.TestCase):
# Verify that there are no unpublished changes
for key in locations:
self.assertFalse(self.draft_store.has_changes(locations[key]))
self.assertFalse(self._has_changes(locations[key]))
# Change the child
child = self.draft_store.get_item(locations['child'])
@@ -585,18 +590,18 @@ class TestMongoModuleStore(unittest.TestCase):
self.draft_store.update_item(child, user_id=self.dummy_user)
# All ancestors should have changes, but not siblings
self.assertTrue(self.draft_store.has_changes(locations['grandparent']))
self.assertTrue(self.draft_store.has_changes(locations['parent']))
self.assertTrue(self.draft_store.has_changes(locations['child']))
self.assertFalse(self.draft_store.has_changes(locations['parent_sibling']))
self.assertFalse(self.draft_store.has_changes(locations['child_sibling']))
self.assertTrue(self._has_changes(locations['grandparent']))
self.assertTrue(self._has_changes(locations['parent']))
self.assertTrue(self._has_changes(locations['child']))
self.assertFalse(self._has_changes(locations['parent_sibling']))
self.assertFalse(self._has_changes(locations['child_sibling']))
# Publish the unit with changes
self.draft_store.publish(locations['parent'], self.dummy_user)
# Verify that there are no unpublished changes
for key in locations:
self.assertFalse(self.draft_store.has_changes(locations[key]))
self.assertFalse(self._has_changes(locations[key]))
def test_has_changes_publish_ancestors(self):
"""
@@ -606,7 +611,7 @@ class TestMongoModuleStore(unittest.TestCase):
# Verify that there are no unpublished changes
for key in locations:
self.assertFalse(self.draft_store.has_changes(locations[key]))
self.assertFalse(self._has_changes(locations[key]))
# Change both children
child = self.draft_store.get_item(locations['child'])
@@ -617,22 +622,22 @@ class TestMongoModuleStore(unittest.TestCase):
self.draft_store.update_item(child_sibling, user_id=self.dummy_user)
# Verify that ancestors have changes
self.assertTrue(self.draft_store.has_changes(locations['grandparent']))
self.assertTrue(self.draft_store.has_changes(locations['parent']))
self.assertTrue(self._has_changes(locations['grandparent']))
self.assertTrue(self._has_changes(locations['parent']))
# Publish one child
self.draft_store.publish(locations['child_sibling'], self.dummy_user)
# Verify that ancestors still have changes
self.assertTrue(self.draft_store.has_changes(locations['grandparent']))
self.assertTrue(self.draft_store.has_changes(locations['parent']))
self.assertTrue(self._has_changes(locations['grandparent']))
self.assertTrue(self._has_changes(locations['parent']))
# Publish the other child
self.draft_store.publish(locations['child'], self.dummy_user)
# Verify that ancestors now have no changes
self.assertFalse(self.draft_store.has_changes(locations['grandparent']))
self.assertFalse(self.draft_store.has_changes(locations['parent']))
self.assertFalse(self._has_changes(locations['grandparent']))
self.assertFalse(self._has_changes(locations['parent']))
def test_has_changes_add_remove_child(self):
"""
@@ -642,8 +647,8 @@ class TestMongoModuleStore(unittest.TestCase):
locations = self._create_test_tree('has_changes_add_remove_child')
# Test that the ancestors don't have changes
self.assertFalse(self.draft_store.has_changes(locations['grandparent']))
self.assertFalse(self.draft_store.has_changes(locations['parent']))
self.assertFalse(self._has_changes(locations['grandparent']))
self.assertFalse(self._has_changes(locations['parent']))
# Create a new child and attach it to parent
new_child_location = Location('edX', 'tree', 'has_changes_add_remove_child', 'vertical', 'new_child')
@@ -655,8 +660,8 @@ class TestMongoModuleStore(unittest.TestCase):
)
# Verify that the ancestors now have changes
self.assertTrue(self.draft_store.has_changes(locations['grandparent']))
self.assertTrue(self.draft_store.has_changes(locations['parent']))
self.assertTrue(self._has_changes(locations['grandparent']))
self.assertTrue(self._has_changes(locations['parent']))
# Remove the child from the parent
parent = self.draft_store.get_item(locations['parent'])
@@ -664,8 +669,8 @@ class TestMongoModuleStore(unittest.TestCase):
self.draft_store.update_item(parent, user_id=self.dummy_user)
# Verify that ancestors now have no changes
self.assertFalse(self.draft_store.has_changes(locations['grandparent']))
self.assertFalse(self.draft_store.has_changes(locations['parent']))
self.assertFalse(self._has_changes(locations['grandparent']))
self.assertFalse(self._has_changes(locations['parent']))
def test_has_changes_non_direct_only_children(self):
"""
@@ -689,16 +694,16 @@ class TestMongoModuleStore(unittest.TestCase):
self.draft_store.publish(parent_location, self.dummy_user)
# Verify that there are no changes
self.assertFalse(self.draft_store.has_changes(parent_location))
self.assertFalse(self.draft_store.has_changes(child_location))
self.assertFalse(self._has_changes(parent_location))
self.assertFalse(self._has_changes(child_location))
# Change the child
child.display_name = 'Changed Display Name'
self.draft_store.update_item(child, user_id=self.dummy_user)
# Verify that both parent and child have changes
self.assertTrue(self.draft_store.has_changes(parent_location))
self.assertTrue(self.draft_store.has_changes(child_location))
self.assertTrue(self._has_changes(parent_location))
self.assertTrue(self._has_changes(child_location))
def test_update_edit_info_ancestors(self):
"""