Commit 6e45db65 by Don Mitchell

Merge pull request #1820 from MITx/fix/cdodge/draft-metadata-inheritance

Fix/cdodge/draft metadata inheritance
parents f7094803 93e27c5f
...@@ -92,6 +92,69 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase): ...@@ -92,6 +92,69 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
return cnt return cnt
def test_draft_metadata(self):
'''
This verifies a bug we had where inherited metadata was getting written to the
module as 'own-metadata' when publishing. Also verifies the metadata inheritance is
properly computed
'''
store = modulestore()
draft_store = modulestore('draft')
import_from_xml(store, 'common/test/data/', ['simple'])
course = draft_store.get_item(Location(['i4x', 'edX', 'simple',
'course', '2012_Fall', None]), depth=None)
html_module = draft_store.get_item(['i4x', 'edX', 'simple', 'html', 'test_html', None])
self.assertEqual(html_module.lms.graceperiod, course.lms.graceperiod)
self.assertNotIn('graceperiod', own_metadata(html_module))
draft_store.clone_item(html_module.location, html_module.location)
# refetch to check metadata
html_module = draft_store.get_item(['i4x', 'edX', 'simple', 'html', 'test_html', None])
self.assertEqual(html_module.lms.graceperiod, course.lms.graceperiod)
self.assertNotIn('graceperiod', own_metadata(html_module))
# publish module
draft_store.publish(html_module.location, 0)
# refetch to check metadata
html_module = draft_store.get_item(['i4x', 'edX', 'simple', 'html', 'test_html', None])
self.assertEqual(html_module.lms.graceperiod, course.lms.graceperiod)
self.assertNotIn('graceperiod', own_metadata(html_module))
# put back in draft and change metadata and see if it's now marked as 'own_metadata'
draft_store.clone_item(html_module.location, html_module.location)
html_module = draft_store.get_item(['i4x', 'edX', 'simple', 'html', 'test_html', None])
new_graceperiod = timedelta(**{'hours': 1})
self.assertNotIn('graceperiod', own_metadata(html_module))
html_module.lms.graceperiod = new_graceperiod
self.assertIn('graceperiod', own_metadata(html_module))
self.assertEqual(html_module.lms.graceperiod, new_graceperiod)
draft_store.update_metadata(html_module.location, own_metadata(html_module))
# read back to make sure it reads as 'own-metadata'
html_module = draft_store.get_item(['i4x', 'edX', 'simple', 'html', 'test_html', None])
self.assertIn('graceperiod', own_metadata(html_module))
self.assertEqual(html_module.lms.graceperiod, new_graceperiod)
# republish
draft_store.publish(html_module.location, 0)
# and re-read and verify 'own-metadata'
draft_store.clone_item(html_module.location, html_module.location)
html_module = draft_store.get_item(['i4x', 'edX', 'simple', 'html', 'test_html', None])
self.assertIn('graceperiod', own_metadata(html_module))
self.assertEqual(html_module.lms.graceperiod, new_graceperiod)
def test_get_depth_with_drafts(self): def test_get_depth_with_drafts(self):
import_from_xml(modulestore(), 'common/test/data/', ['simple']) import_from_xml(modulestore(), 'common/test/data/', ['simple'])
......
...@@ -2,6 +2,7 @@ from datetime import datetime ...@@ -2,6 +2,7 @@ from datetime import datetime
from . import ModuleStoreBase, Location, namedtuple_to_son from . import ModuleStoreBase, Location, namedtuple_to_son
from .exceptions import ItemNotFoundError from .exceptions import ItemNotFoundError
from .inheritance import own_metadata
import logging import logging
DRAFT = 'draft' DRAFT = 'draft'
...@@ -181,7 +182,7 @@ class DraftModuleStore(ModuleStoreBase): ...@@ -181,7 +182,7 @@ class DraftModuleStore(ModuleStoreBase):
draft.cms.published_by = published_by_id draft.cms.published_by = published_by_id
super(DraftModuleStore, self).update_item(location, draft._model_data._kvs._data) super(DraftModuleStore, self).update_item(location, draft._model_data._kvs._data)
super(DraftModuleStore, self).update_children(location, draft._model_data._kvs._children) super(DraftModuleStore, self).update_children(location, draft._model_data._kvs._children)
super(DraftModuleStore, self).update_metadata(location, draft._model_data._kvs._metadata) super(DraftModuleStore, self).update_metadata(location, own_metadata(draft))
self.delete_item(location) self.delete_item(location)
def unpublish(self, location): def unpublish(self, location):
......
...@@ -171,7 +171,10 @@ class CachingDescriptorSystem(MakoDescriptorSystem): ...@@ -171,7 +171,10 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
model_data = DbModel(kvs, class_, None, MongoUsage(self.course_id, location)) model_data = DbModel(kvs, class_, None, MongoUsage(self.course_id, location))
module = class_(self, location, model_data) module = class_(self, location, model_data)
if self.cached_metadata is not None: if self.cached_metadata is not None:
metadata_to_inherit = self.cached_metadata.get(location.url(), {}) # 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
non_draft_loc = location._replace(revision=None)
metadata_to_inherit = self.cached_metadata.get(non_draft_loc.url(), {})
inherit_metadata(module, metadata_to_inherit) inherit_metadata(module, metadata_to_inherit)
return module return module
except: except:
...@@ -277,6 +280,17 @@ class MongoModuleStore(ModuleStoreBase): ...@@ -277,6 +280,17 @@ class MongoModuleStore(ModuleStoreBase):
# now go through the results and order them by the location url # now go through the results and order them by the location url
for result in resultset: for result in resultset:
location = Location(result['_id']) location = Location(result['_id'])
# We need to collate between draft and non-draft
# i.e. draft verticals can have children which are not in non-draft versions
location = location._replace(revision=None)
location_url = location.url()
if location_url in results_by_url:
existing_children = results_by_url[location_url].get('definition', {}).get('children', [])
additional_children = result.get('definition', {}).get('children', [])
total_children = existing_children + additional_children
if 'definition' not in results_by_url[location_url]:
results_by_url[location_url]['definition'] = {}
results_by_url[location_url]['definition']['children'] = total_children
results_by_url[location.url()] = result results_by_url[location.url()] = result
if location.category == 'course': if location.category == 'course':
root = location.url() root = location.url()
...@@ -288,17 +302,12 @@ class MongoModuleStore(ModuleStoreBase): ...@@ -288,17 +302,12 @@ class MongoModuleStore(ModuleStoreBase):
""" """
Helper method for computing inherited metadata for a specific location url Helper method for computing inherited metadata for a specific location url
""" """
my_metadata = {}
# check for presence of metadata key. Note that a given module may not yet be fully formed. # check for presence of metadata key. Note that a given module may not yet be fully formed.
# example: update_item -> update_children -> update_metadata sequence on new item create # example: update_item -> update_children -> update_metadata sequence on new item create
# if we get called here without update_metadata called first then 'metadata' hasn't been set # if we get called here without update_metadata called first then 'metadata' hasn't been set
# as we're not fully transactional at the DB layer. Same comment applies to below key name # as we're not fully transactional at the DB layer. Same comment applies to below key name
# check # check
my_metadata = results_by_url[url].get('metadata', {}) my_metadata = results_by_url[url].get('metadata', {})
for key in my_metadata.keys():
if key not in INHERITABLE_METADATA:
del my_metadata[key]
results_by_url[url]['metadata'] = my_metadata
# go through all the children and recurse, but only if we have # go through all the children and recurse, but only if we have
# in the result set. Remember results will not contain leaf nodes # in the result set. Remember results will not contain leaf nodes
......
...@@ -19,6 +19,13 @@ ...@@ -19,6 +19,13 @@
</sequential> </sequential>
</section> </section>
<video name="Lost Video" youtube="1.0:TBvX7HzxexQ"/> <video name="Lost Video" youtube="1.0:TBvX7HzxexQ"/>
<sequential format="Lecture Sequence">
<vertical url_name='test_verical'>
<html url_name='test_html'>
Foobar
</html>
</vertical>
</sequential>
</chapter> </chapter>
</course> </course>
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