Commit b518cfa0 by John Eskew

Add wrapper class that enables storage of Mongo document id separately

from dictionary of all asset metadata by asset type.
parent 94ea35d3
...@@ -7,11 +7,12 @@ import dateutil.parser ...@@ -7,11 +7,12 @@ import dateutil.parser
import pytz import pytz
import json import json
from contracts import contract, new_contract from contracts import contract, new_contract
from opaque_keys.edx.keys import AssetKey from opaque_keys.edx.keys import CourseKey, AssetKey
from lxml import etree from lxml import etree
new_contract('AssetKey', AssetKey) new_contract('AssetKey', AssetKey)
new_contract('CourseKey', CourseKey)
new_contract('datetime', datetime) new_contract('datetime', datetime)
new_contract('basestring', basestring) new_contract('basestring', basestring)
new_contract('AssetElement', lambda x: isinstance(x, etree._Element) and x.tag == "asset") # pylint: disable=protected-access, no-member new_contract('AssetElement', lambda x: isinstance(x, etree._Element) and x.tag == "asset") # pylint: disable=protected-access, no-member
...@@ -244,3 +245,57 @@ class AssetMetadata(object): ...@@ -244,3 +245,57 @@ class AssetMetadata(object):
for asset in assets: for asset in assets:
asset_node = etree.SubElement(node, "asset") asset_node = etree.SubElement(node, "asset")
asset.to_xml(asset_node) asset.to_xml(asset_node)
class CourseAssetsFromStorage(object):
"""
Wrapper class for asset metadata lists returned from modulestore storage.
"""
@contract(course_id='CourseKey', asset_md=dict)
def __init__(self, course_id, doc_id, asset_md):
"""
Params:
course_id: Course ID for which the asset metadata is stored.
doc_id: ObjectId of MongoDB document
asset_md: Dict with asset types as keys and lists of storable asset metadata as values.
"""
self.course_id = course_id
self._doc_id = doc_id
self.asset_md = asset_md
@property
def doc_id(self):
"""
Returns the ID associated with the MongoDB document which stores these course assets.
"""
return self._doc_id
def setdefault(self, item, default=None):
"""
Provides dict-equivalent setdefault functionality.
"""
return self.asset_md.setdefault(item, default)
def __getitem__(self, item):
return self.asset_md[item]
def __delitem__(self, item):
del self.asset_md[item]
def __len__(self):
return len(self.asset_md)
def __setitem__(self, key, value):
self.asset_md[key] = value
def get(self, item, default=None):
"""
Provides dict-equivalent get functionality.
"""
return self.asset_md.get(item, default)
def iteritems(self):
"""
Iterates over the items of the asset dict.
"""
return self.asset_md.iteritems()
...@@ -376,10 +376,7 @@ class ModuleStoreAssetInterface(object): ...@@ -376,10 +376,7 @@ class ModuleStoreAssetInterface(object):
# Add assets of all types to the sorted list. # Add assets of all types to the sorted list.
all_assets = SortedListWithKey([], key=key_func) all_assets = SortedListWithKey([], key=key_func)
for asset_type, val in course_assets.iteritems(): for asset_type, val in course_assets.iteritems():
# '_id' is sometimes added to the course_assets for CRUD purposes all_assets.update(val)
# (depending on the modulestore). If it's present, skip it.
if asset_type != '_id':
all_assets.update(val)
else: else:
# Add assets of a single type to the sorted list. # Add assets of a single type to the sorted list.
all_assets = SortedListWithKey(course_assets.get(asset_type, []), key=key_func) all_assets = SortedListWithKey(course_assets.get(asset_type, []), key=key_func)
......
...@@ -40,7 +40,7 @@ from xblock.exceptions import InvalidScopeError ...@@ -40,7 +40,7 @@ from xblock.exceptions import InvalidScopeError
from xblock.fields import Scope, ScopeIds, Reference, ReferenceList, ReferenceValueDict from xblock.fields import Scope, ScopeIds, Reference, ReferenceList, ReferenceValueDict
from xblock.runtime import KvsFieldData from xblock.runtime import KvsFieldData
from xmodule.assetstore import AssetMetadata from xmodule.assetstore import AssetMetadata, CourseAssetsFromStorage
from xmodule.error_module import ErrorDescriptor from xmodule.error_module import ErrorDescriptor
from xmodule.errortracker import null_error_tracker, exc_info_to_str from xmodule.errortracker import null_error_tracker, exc_info_to_str
from xmodule.exceptions import HeartbeatFailure from xmodule.exceptions import HeartbeatFailure
...@@ -1473,7 +1473,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1473,7 +1473,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
course_key (CourseKey): course identifier course_key (CourseKey): course identifier
Returns: Returns:
Dict with (at least) an '_id' key, identifying the relevant Mongo doc. If asset metadata CourseAssetsFromStorage object, wrapping the relevant Mongo doc. If asset metadata
exists, other keys will be the other asset types with values as lists of asset metadata. exists, other keys will be the other asset types with values as lists of asset metadata.
""" """
# Using the course_key, find or insert the course asset metadata document. # Using the course_key, find or insert the course asset metadata document.
...@@ -1483,6 +1483,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1483,6 +1483,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
{'course_id': unicode(course_key)}, {'course_id': unicode(course_key)},
) )
doc_id = None if course_assets is None else course_assets['_id']
if course_assets is None: if course_assets is None:
# Check to see if the course is created in the course collection. # Check to see if the course is created in the course collection.
if self.get_course(course_key) is None: if self.get_course(course_key) is None:
...@@ -1490,22 +1491,19 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1490,22 +1491,19 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
else: else:
# Course exists, so create matching assets document. # Course exists, so create matching assets document.
course_assets = {'course_id': unicode(course_key), 'assets': {}} course_assets = {'course_id': unicode(course_key), 'assets': {}}
# Pass back 'assets' dict but add the '_id' key to it for document update purposes. doc_id = self.asset_collection.insert(course_assets)
course_assets['assets']['_id'] = self.asset_collection.insert(course_assets)
elif isinstance(course_assets['assets'], list): elif isinstance(course_assets['assets'], list):
# This record is in the old course assets format. # This record is in the old course assets format.
# Ensure that no data exists before updating the format. # Ensure that no data exists before updating the format.
assert(len(course_assets['assets']) == 0) assert(len(course_assets['assets']) == 0)
# Update the format to a dict. # Update the format to a dict.
self.asset_collection.update( self.asset_collection.update(
{'_id': course_assets['_id']}, {'_id': doc_id},
{'$set': {'assets': {}}} {'$set': {'assets': {}}}
) )
course_assets['assets'] = {'_id': course_assets['_id']}
else:
course_assets['assets']['_id'] = course_assets['_id']
return course_assets['assets'] # Pass back wrapped 'assets' dict with the '_id' key added to it for document update purposes.
return CourseAssetsFromStorage(course_key, doc_id, course_assets['assets'])
def _make_mongo_asset_key(self, asset_type): def _make_mongo_asset_key(self, asset_type):
""" """
...@@ -1554,7 +1552,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1554,7 +1552,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
# Update the document. # Update the document.
self.asset_collection.update( self.asset_collection.update(
{'_id': course_assets['_id']}, {'_id': course_assets.doc_id},
{'$set': updates_by_type} {'$set': updates_by_type}
) )
return True return True
...@@ -1602,9 +1600,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1602,9 +1600,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
dest_course_key (CourseKey): identifier of course to copy to dest_course_key (CourseKey): identifier of course to copy to
""" """
source_assets = self._find_course_assets(source_course_key) source_assets = self._find_course_assets(source_course_key)
dest_assets = source_assets.copy() dest_assets = {'assets': source_assets.asset_md.copy()}
del dest_assets['_id']
dest_assets = {'assets': dest_assets}
dest_assets['course_id'] = unicode(dest_course_key) dest_assets['course_id'] = unicode(dest_course_key)
self.asset_collection.remove({'course_id': unicode(dest_course_key)}) self.asset_collection.remove({'course_id': unicode(dest_course_key)})
...@@ -1629,7 +1625,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1629,7 +1625,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
raise ItemNotFoundError(asset_key) raise ItemNotFoundError(asset_key)
# Form an AssetMetadata. # Form an AssetMetadata.
all_assets = course_assets[asset_key.block_type] all_assets = course_assets[asset_key.asset_type]
md = AssetMetadata(asset_key, asset_key.path) md = AssetMetadata(asset_key, asset_key.path)
md.from_storable(all_assets[asset_idx]) md.from_storable(all_assets[asset_idx])
md.update(attr_dict) md.update(attr_dict)
...@@ -1638,8 +1634,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1638,8 +1634,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
all_assets[asset_idx] = md.to_storable() all_assets[asset_idx] = md.to_storable()
self.asset_collection.update( self.asset_collection.update(
{'_id': course_assets['_id']}, {'_id': course_assets.doc_id},
{"$set": {self._make_mongo_asset_key(asset_key.block_type): all_assets}} {"$set": {self._make_mongo_asset_key(asset_key.asset_type): all_assets}}
) )
@contract(asset_key='AssetKey') @contract(asset_key='AssetKey')
...@@ -1657,13 +1653,13 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1657,13 +1653,13 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
if asset_idx is None: if asset_idx is None:
return 0 return 0
all_asset_info = course_assets[asset_key.block_type] all_asset_info = course_assets[asset_key.asset_type]
all_asset_info.pop(asset_idx) all_asset_info.pop(asset_idx)
# Update the document. # Update the document.
self.asset_collection.update( self.asset_collection.update(
{'_id': course_assets['_id']}, {'_id': course_assets.doc_id},
{'$set': {self._make_mongo_asset_key(asset_key.block_type): all_asset_info}} {'$set': {self._make_mongo_asset_key(asset_key.asset_type): all_asset_info}}
) )
return 1 return 1
...@@ -1680,7 +1676,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ...@@ -1680,7 +1676,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
# A single document exists per course to store the course asset metadata. # A single document exists per course to store the course asset metadata.
try: try:
course_assets = self._find_course_assets(course_key) course_assets = self._find_course_assets(course_key)
self.asset_collection.remove(course_assets['_id']) self.asset_collection.remove(course_assets.doc_id)
except ItemNotFoundError: except ItemNotFoundError:
# When deleting asset metadata, if a course's asset metadata is not present, no big deal. # When deleting asset metadata, if a course's asset metadata is not present, no big deal.
pass pass
......
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