Separate all db ops from modulestore ops
This commit is contained in:
@@ -88,7 +88,7 @@ class SplitMigrator(object):
|
|||||||
index_info = self.split_modulestore.get_course_index_info(course_version_locator)
|
index_info = self.split_modulestore.get_course_index_info(course_version_locator)
|
||||||
versions = index_info['versions']
|
versions = index_info['versions']
|
||||||
versions['draft'] = versions['published']
|
versions['draft'] = versions['published']
|
||||||
self.split_modulestore.update_course_index(course_version_locator, {'versions': versions}, update_versions=True)
|
self.split_modulestore.update_course_index(index_info)
|
||||||
|
|
||||||
# clean up orphans in published version: in old mongo, parents pointed to the union of their published and draft
|
# clean up orphans in published version: in old mongo, parents pointed to the union of their published and draft
|
||||||
# children which meant some pointers were to non-existent locations in 'direct'
|
# children which meant some pointers were to non-existent locations in 'direct'
|
||||||
|
|||||||
@@ -22,5 +22,4 @@ class DefinitionLazyLoader(object):
|
|||||||
Fetch the definition. Note, the caller should replace this lazy
|
Fetch the definition. Note, the caller should replace this lazy
|
||||||
loader pointer with the result so as not to fetch more than once
|
loader pointer with the result so as not to fetch more than once
|
||||||
"""
|
"""
|
||||||
return self.modulestore.definitions.find_one(
|
return self.modulestore.db_connection.get_definition(self.definition_locator.definition_id)
|
||||||
{'_id': self.definition_locator.definition_id})
|
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
"""
|
||||||
|
Segregation of pymongo functions from the data modeling mechanisms for split modulestore.
|
||||||
|
"""
|
||||||
|
import pymongo
|
||||||
|
|
||||||
|
class MongoConnection(object):
|
||||||
|
"""
|
||||||
|
Segregation of pymongo functions from the data modeling mechanisms for split modulestore.
|
||||||
|
"""
|
||||||
|
def __init__(
|
||||||
|
self, db, collection, host, port=27017, tz_aware=True, user=None, password=None, **kwargs
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Create & open the connection, authenticate, and provide pointers to the collections
|
||||||
|
"""
|
||||||
|
self.database = pymongo.database.Database(
|
||||||
|
pymongo.MongoClient(
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
tz_aware=tz_aware,
|
||||||
|
**kwargs
|
||||||
|
),
|
||||||
|
db
|
||||||
|
)
|
||||||
|
|
||||||
|
if user is not None and password is not None:
|
||||||
|
self.database.authenticate(user, password)
|
||||||
|
|
||||||
|
self.course_index = self.database[collection + '.active_versions']
|
||||||
|
self.structures = self.database[collection + '.structures']
|
||||||
|
self.definitions = self.database[collection + '.definitions']
|
||||||
|
|
||||||
|
# every app has write access to the db (v having a flag to indicate r/o v write)
|
||||||
|
# Force mongo to report errors, at the expense of performance
|
||||||
|
# pymongo docs suck but explanation:
|
||||||
|
# http://api.mongodb.org/java/2.10.1/com/mongodb/WriteConcern.html
|
||||||
|
self.course_index.write_concern = {'w': 1}
|
||||||
|
self.structures.write_concern = {'w': 1}
|
||||||
|
self.definitions.write_concern = {'w': 1}
|
||||||
|
|
||||||
|
def get_structure(self, key):
|
||||||
|
"""
|
||||||
|
Get the structure from the persistence mechanism whose id is the given key
|
||||||
|
"""
|
||||||
|
return self.structures.find_one({'_id': key})
|
||||||
|
|
||||||
|
def find_matching_structures(self, query):
|
||||||
|
"""
|
||||||
|
Find the structure matching the query. Right now the query must be a legal mongo query
|
||||||
|
:param query: a mongo-style query of {key: [value|{$in ..}|..], ..}
|
||||||
|
"""
|
||||||
|
return self.structures.find(query)
|
||||||
|
|
||||||
|
def insert_structure(self, structure):
|
||||||
|
"""
|
||||||
|
Create the structure in the db
|
||||||
|
"""
|
||||||
|
self.structures.insert(structure)
|
||||||
|
|
||||||
|
def update_structure(self, structure):
|
||||||
|
"""
|
||||||
|
Update the db record for structure
|
||||||
|
"""
|
||||||
|
self.structures.update({'_id': structure['_id']}, structure)
|
||||||
|
|
||||||
|
def get_course_index(self, key):
|
||||||
|
"""
|
||||||
|
Get the course_index from the persistence mechanism whose id is the given key
|
||||||
|
"""
|
||||||
|
return self.course_index.find_one({'_id': key})
|
||||||
|
|
||||||
|
def find_matching_course_indexes(self, query):
|
||||||
|
"""
|
||||||
|
Find the course_index matching the query. Right now the query must be a legal mongo query
|
||||||
|
:param query: a mongo-style query of {key: [value|{$in ..}|..], ..}
|
||||||
|
"""
|
||||||
|
return self.course_index.find(query)
|
||||||
|
|
||||||
|
def insert_course_index(self, course_index):
|
||||||
|
"""
|
||||||
|
Create the course_index in the db
|
||||||
|
"""
|
||||||
|
self.course_index.insert(course_index)
|
||||||
|
|
||||||
|
def update_course_index(self, course_index):
|
||||||
|
"""
|
||||||
|
Update the db record for course_index
|
||||||
|
"""
|
||||||
|
self.course_index.update({'_id': course_index['_id']}, course_index)
|
||||||
|
|
||||||
|
def delete_course_index(self, key):
|
||||||
|
"""
|
||||||
|
Delete the course_index from the persistence mechanism whose id is the given key
|
||||||
|
"""
|
||||||
|
return self.course_index.remove({'_id': key})
|
||||||
|
|
||||||
|
def get_definition(self, key):
|
||||||
|
"""
|
||||||
|
Get the definition from the persistence mechanism whose id is the given key
|
||||||
|
"""
|
||||||
|
return self.definitions.find_one({'_id': key})
|
||||||
|
|
||||||
|
def find_matching_definitions(self, query):
|
||||||
|
"""
|
||||||
|
Find the definitions matching the query. Right now the query must be a legal mongo query
|
||||||
|
:param query: a mongo-style query of {key: [value|{$in ..}|..], ..}
|
||||||
|
"""
|
||||||
|
return self.definitions.find(query)
|
||||||
|
|
||||||
|
def insert_definition(self, definition):
|
||||||
|
"""
|
||||||
|
Create the definition in the db
|
||||||
|
"""
|
||||||
|
self.definitions.insert(definition)
|
||||||
|
|
||||||
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import threading
|
import threading
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import pymongo
|
|
||||||
import re
|
import re
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from path import path
|
from path import path
|
||||||
@@ -21,6 +20,7 @@ from .caching_descriptor_system import CachingDescriptorSystem
|
|||||||
from xblock.fields import Scope
|
from xblock.fields import Scope
|
||||||
from xblock.runtime import Mixologist
|
from xblock.runtime import Mixologist
|
||||||
from bson.objectid import ObjectId
|
from bson.objectid import ObjectId
|
||||||
|
from xmodule.modulestore.split_mongo.mongo_connection import MongoConnection
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
@@ -49,7 +49,6 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
A Mongodb backed ModuleStore supporting versions, inheritance,
|
A Mongodb backed ModuleStore supporting versions, inheritance,
|
||||||
and sharing.
|
and sharing.
|
||||||
"""
|
"""
|
||||||
# pylint: disable=W0201
|
|
||||||
def __init__(self, doc_store_config, fs_root, render_template,
|
def __init__(self, doc_store_config, fs_root, render_template,
|
||||||
default_class=None,
|
default_class=None,
|
||||||
error_tracker=null_error_tracker,
|
error_tracker=null_error_tracker,
|
||||||
@@ -62,44 +61,13 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
super(SplitMongoModuleStore, self).__init__(**kwargs)
|
super(SplitMongoModuleStore, self).__init__(**kwargs)
|
||||||
self.loc_mapper = loc_mapper
|
self.loc_mapper = loc_mapper
|
||||||
|
|
||||||
def do_connection(
|
self.db_connection = MongoConnection(**doc_store_config)
|
||||||
db, collection, host, port=27017, tz_aware=True, user=None, password=None, **kwargs
|
self.db = self.db_connection.database
|
||||||
):
|
|
||||||
"""
|
|
||||||
Create & open the connection, authenticate, and provide pointers to the collections
|
|
||||||
"""
|
|
||||||
self.db = pymongo.database.Database(
|
|
||||||
pymongo.MongoClient(
|
|
||||||
host=host,
|
|
||||||
port=port,
|
|
||||||
tz_aware=tz_aware,
|
|
||||||
**kwargs
|
|
||||||
),
|
|
||||||
db
|
|
||||||
)
|
|
||||||
|
|
||||||
if user is not None and password is not None:
|
|
||||||
self.db.authenticate(user, password)
|
|
||||||
|
|
||||||
self.course_index = self.db[collection + '.active_versions']
|
|
||||||
self.structures = self.db[collection + '.structures']
|
|
||||||
self.definitions = self.db[collection + '.definitions']
|
|
||||||
|
|
||||||
do_connection(**doc_store_config)
|
|
||||||
|
|
||||||
# Code review question: How should I expire entries?
|
# Code review question: How should I expire entries?
|
||||||
# _add_cache could use a lru mechanism to control the cache size?
|
# _add_cache could use a lru mechanism to control the cache size?
|
||||||
self.thread_cache = threading.local()
|
self.thread_cache = threading.local()
|
||||||
|
|
||||||
|
|
||||||
# every app has write access to the db (v having a flag to indicate r/o v write)
|
|
||||||
# Force mongo to report errors, at the expense of performance
|
|
||||||
# pymongo docs suck but explanation:
|
|
||||||
# http://api.mongodb.org/java/2.10.1/com/mongodb/WriteConcern.html
|
|
||||||
self.course_index.write_concern = {'w': 1}
|
|
||||||
self.structures.write_concern = {'w': 1}
|
|
||||||
self.definitions.write_concern = {'w': 1}
|
|
||||||
|
|
||||||
if default_class is not None:
|
if default_class is not None:
|
||||||
module_path, _, class_name = default_class.rpartition('.')
|
module_path, _, class_name = default_class.rpartition('.')
|
||||||
class_ = getattr(import_module(module_path), class_name)
|
class_ = getattr(import_module(module_path), class_name)
|
||||||
@@ -138,7 +106,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
block['definition'] = DefinitionLazyLoader(self, block['definition'])
|
block['definition'] = DefinitionLazyLoader(self, block['definition'])
|
||||||
else:
|
else:
|
||||||
# Load all descendants by id
|
# Load all descendants by id
|
||||||
descendent_definitions = self.definitions.find({
|
descendent_definitions = self.db_connection.find_matching_definitions({
|
||||||
'_id': {'$in': [block['definition']
|
'_id': {'$in': [block['definition']
|
||||||
for block in new_module_data.itervalues()]}})
|
for block in new_module_data.itervalues()]}})
|
||||||
# turn into a map
|
# turn into a map
|
||||||
@@ -226,7 +194,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
|
|
||||||
if course_locator.course_id is not None and course_locator.branch is not None:
|
if course_locator.course_id is not None and course_locator.branch is not None:
|
||||||
# use the course_id
|
# use the course_id
|
||||||
index = self.course_index.find_one({'_id': course_locator.course_id})
|
index = self.db_connection.get_course_index(course_locator.course_id)
|
||||||
if index is None:
|
if index is None:
|
||||||
raise ItemNotFoundError(course_locator)
|
raise ItemNotFoundError(course_locator)
|
||||||
if course_locator.branch not in index['versions']:
|
if course_locator.branch not in index['versions']:
|
||||||
@@ -241,7 +209,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
|
|
||||||
# cast string to ObjectId if necessary
|
# cast string to ObjectId if necessary
|
||||||
version_guid = course_locator.as_object_id(version_guid)
|
version_guid = course_locator.as_object_id(version_guid)
|
||||||
entry = self.structures.find_one({'_id': version_guid})
|
entry = self.db_connection.get_structure(version_guid)
|
||||||
|
|
||||||
# b/c more than one course can use same structure, the 'course_id' and 'branch' are not intrinsic to structure
|
# b/c more than one course can use same structure, the 'course_id' and 'branch' are not intrinsic to structure
|
||||||
# and the one assoc'd w/ it by another fetch may not be the one relevant to this fetch; so,
|
# and the one assoc'd w/ it by another fetch may not be the one relevant to this fetch; so,
|
||||||
@@ -269,7 +237,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
if qualifiers is None:
|
if qualifiers is None:
|
||||||
qualifiers = {}
|
qualifiers = {}
|
||||||
qualifiers.update({"versions.{}".format(branch): {"$exists": True}})
|
qualifiers.update({"versions.{}".format(branch): {"$exists": True}})
|
||||||
matching = self.course_index.find(qualifiers)
|
matching = self.db_connection.find_matching_course_indexes(qualifiers)
|
||||||
|
|
||||||
# collect ids and then query for those
|
# collect ids and then query for those
|
||||||
version_guids = []
|
version_guids = []
|
||||||
@@ -279,7 +247,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
version_guids.append(version_guid)
|
version_guids.append(version_guid)
|
||||||
id_version_map[version_guid] = structure['_id']
|
id_version_map[version_guid] = structure['_id']
|
||||||
|
|
||||||
course_entries = self.structures.find({'_id': {'$in': version_guids}})
|
course_entries = self.db_connection.find_matching_structures({'_id': {'$in': version_guids}})
|
||||||
|
|
||||||
# get the block for the course element (s/b the root)
|
# get the block for the course element (s/b the root)
|
||||||
result = []
|
result = []
|
||||||
@@ -455,7 +423,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
"""
|
"""
|
||||||
if course_locator.course_id is None:
|
if course_locator.course_id is None:
|
||||||
return None
|
return None
|
||||||
index = self.course_index.find_one({'_id': course_locator.course_id})
|
index = self.db_connection.get_course_index(course_locator.course_id)
|
||||||
return index
|
return index
|
||||||
|
|
||||||
# TODO figure out a way to make this info accessible from the course descriptor
|
# TODO figure out a way to make this info accessible from the course descriptor
|
||||||
@@ -487,7 +455,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
'edited_on': when the change was made
|
'edited_on': when the change was made
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
definition = self.definitions.find_one({'_id': definition_locator.definition_id})
|
definition = self.db_connection.get_definition(definition_locator.definition_id)
|
||||||
if definition is None:
|
if definition is None:
|
||||||
return None
|
return None
|
||||||
return definition['edit_info']
|
return definition['edit_info']
|
||||||
@@ -509,14 +477,14 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
|
|
||||||
# TODO if depth is significant, it may make sense to get all that have the same original_version
|
# TODO if depth is significant, it may make sense to get all that have the same original_version
|
||||||
# and reconstruct the subtree from version_guid
|
# and reconstruct the subtree from version_guid
|
||||||
next_entries = self.structures.find({'previous_version' : version_guid})
|
next_entries = self.db_connection.find_matching_structures({'previous_version' : version_guid})
|
||||||
# must only scan cursor's once
|
# must only scan cursor's once
|
||||||
next_versions = [struct for struct in next_entries]
|
next_versions = [struct for struct in next_entries]
|
||||||
result = {version_guid: [CourseLocator(version_guid=struct['_id']) for struct in next_versions]}
|
result = {version_guid: [CourseLocator(version_guid=struct['_id']) for struct in next_versions]}
|
||||||
depth = 1
|
depth = 1
|
||||||
while depth < version_history_depth and len(next_versions) > 0:
|
while depth < version_history_depth and len(next_versions) > 0:
|
||||||
depth += 1
|
depth += 1
|
||||||
next_entries = self.structures.find({'previous_version':
|
next_entries = self.db_connection.find_matching_structures({'previous_version':
|
||||||
{'$in': [struct['_id'] for struct in next_versions]}})
|
{'$in': [struct['_id'] for struct in next_versions]}})
|
||||||
next_versions = [struct for struct in next_entries]
|
next_versions = [struct for struct in next_entries]
|
||||||
for course_structure in next_versions:
|
for course_structure in next_versions:
|
||||||
@@ -537,7 +505,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
course_struct = self._lookup_course(block_locator.version_agnostic())['structure']
|
course_struct = self._lookup_course(block_locator.version_agnostic())['structure']
|
||||||
usage_id = block_locator.usage_id
|
usage_id = block_locator.usage_id
|
||||||
update_version_field = 'blocks.{}.edit_info.update_version'.format(usage_id)
|
update_version_field = 'blocks.{}.edit_info.update_version'.format(usage_id)
|
||||||
all_versions_with_block = self.structures.find({'original_version': course_struct['original_version'],
|
all_versions_with_block = self.db_connection.find_matching_structures({'original_version': course_struct['original_version'],
|
||||||
update_version_field: {'$exists': True}})
|
update_version_field: {'$exists': True}})
|
||||||
# find (all) root versions and build map previous: [successors]
|
# find (all) root versions and build map previous: [successors]
|
||||||
possible_roots = []
|
possible_roots = []
|
||||||
@@ -596,7 +564,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
"original_version": new_id,
|
"original_version": new_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.definitions.insert(document)
|
self.db_connection.insert_definition(document)
|
||||||
definition_locator = DefinitionLocator(new_id)
|
definition_locator = DefinitionLocator(new_id)
|
||||||
return definition_locator
|
return definition_locator
|
||||||
|
|
||||||
@@ -618,7 +586,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
|
|
||||||
# if this looks in cache rather than fresh fetches, then it will probably not detect
|
# if this looks in cache rather than fresh fetches, then it will probably not detect
|
||||||
# actual change b/c the descriptor and cache probably point to the same objects
|
# actual change b/c the descriptor and cache probably point to the same objects
|
||||||
old_definition = self.definitions.find_one({'_id': definition_locator.definition_id})
|
old_definition = self.db_connection.get_definition(definition_locator.definition_id)
|
||||||
if old_definition is None:
|
if old_definition is None:
|
||||||
raise ItemNotFoundError(definition_locator.url())
|
raise ItemNotFoundError(definition_locator.url())
|
||||||
|
|
||||||
@@ -630,7 +598,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
old_definition['edit_info']['edited_on'] = datetime.datetime.now(UTC)
|
old_definition['edit_info']['edited_on'] = datetime.datetime.now(UTC)
|
||||||
# previous version id
|
# previous version id
|
||||||
old_definition['edit_info']['previous_version'] = definition_locator.definition_id
|
old_definition['edit_info']['previous_version'] = definition_locator.definition_id
|
||||||
self.definitions.insert(old_definition)
|
self.db_connection.insert_definition(old_definition)
|
||||||
return DefinitionLocator(old_definition['_id']), True
|
return DefinitionLocator(old_definition['_id']), True
|
||||||
else:
|
else:
|
||||||
return definition_locator, False
|
return definition_locator, False
|
||||||
@@ -657,7 +625,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
:param course_blocks: the current list of blocks.
|
:param course_blocks: the current list of blocks.
|
||||||
:param category:
|
:param category:
|
||||||
"""
|
"""
|
||||||
existing_uses = self.course_index.find({"_id": {"$regex": id_root}})
|
existing_uses = self.db_connection.find_matching_course_indexes({"_id": {"$regex": id_root}})
|
||||||
if existing_uses.count() > 0:
|
if existing_uses.count() > 0:
|
||||||
max_found = 0
|
max_found = 0
|
||||||
matcher = re.compile(id_root + r'(\d+)')
|
matcher = re.compile(id_root + r'(\d+)')
|
||||||
@@ -779,11 +747,11 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
parent['edit_info']['update_version'] = new_id
|
parent['edit_info']['update_version'] = new_id
|
||||||
if continue_version:
|
if continue_version:
|
||||||
# db update
|
# db update
|
||||||
self.structures.update({'_id': new_id}, new_structure)
|
self.db_connection.update_structure(new_structure)
|
||||||
# clear cache so things get refetched and inheritance recomputed
|
# clear cache so things get refetched and inheritance recomputed
|
||||||
self._clear_cache(new_id)
|
self._clear_cache(new_id)
|
||||||
else:
|
else:
|
||||||
self.structures.insert(new_structure)
|
self.db_connection.insert_structure(new_structure)
|
||||||
|
|
||||||
# update the index entry if appropriate
|
# update the index entry if appropriate
|
||||||
if index_entry is not None:
|
if index_entry is not None:
|
||||||
@@ -856,7 +824,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
'original_version': definition_id,
|
'original_version': definition_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.definitions.insert(definition_entry)
|
self.db_connection.insert_definition(definition_entry)
|
||||||
|
|
||||||
new_id = ObjectId()
|
new_id = ObjectId()
|
||||||
draft_structure = {
|
draft_structure = {
|
||||||
@@ -880,7 +848,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.structures.insert(draft_structure)
|
self.db_connection.insert_structure(draft_structure)
|
||||||
|
|
||||||
if versions_dict is None:
|
if versions_dict is None:
|
||||||
versions_dict = {master_branch: new_id}
|
versions_dict = {master_branch: new_id}
|
||||||
@@ -898,20 +866,20 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
if block_fields is not None:
|
if block_fields is not None:
|
||||||
root_block['fields'].update(block_fields)
|
root_block['fields'].update(block_fields)
|
||||||
if definition_fields is not None:
|
if definition_fields is not None:
|
||||||
definition = self.definitions.find_one({'_id': root_block['definition']})
|
definition = self.db_connection.get_definition(root_block['definition'])
|
||||||
definition['fields'].update(definition_fields)
|
definition['fields'].update(definition_fields)
|
||||||
definition['edit_info']['previous_version'] = definition['_id']
|
definition['edit_info']['previous_version'] = definition['_id']
|
||||||
definition['edit_info']['edited_by'] = user_id
|
definition['edit_info']['edited_by'] = user_id
|
||||||
definition['edit_info']['edited_on'] = datetime.datetime.now(UTC)
|
definition['edit_info']['edited_on'] = datetime.datetime.now(UTC)
|
||||||
definition['_id'] = ObjectId()
|
definition['_id'] = ObjectId()
|
||||||
self.definitions.insert(definition)
|
self.db_connection.insert_definition(definition)
|
||||||
root_block['definition'] = definition['_id']
|
root_block['definition'] = definition['_id']
|
||||||
root_block['edit_info']['edited_on'] = datetime.datetime.now(UTC)
|
root_block['edit_info']['edited_on'] = datetime.datetime.now(UTC)
|
||||||
root_block['edit_info']['edited_by'] = user_id
|
root_block['edit_info']['edited_by'] = user_id
|
||||||
root_block['edit_info']['previous_version'] = root_block['edit_info'].get('update_version')
|
root_block['edit_info']['previous_version'] = root_block['edit_info'].get('update_version')
|
||||||
root_block['edit_info']['update_version'] = new_id
|
root_block['edit_info']['update_version'] = new_id
|
||||||
|
|
||||||
self.structures.insert(draft_structure)
|
self.db_connection.insert_structure(draft_structure)
|
||||||
versions_dict[master_branch] = new_id
|
versions_dict[master_branch] = new_id
|
||||||
|
|
||||||
# create the index entry
|
# create the index entry
|
||||||
@@ -926,7 +894,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
'edited_by': user_id,
|
'edited_by': user_id,
|
||||||
'edited_on': datetime.datetime.now(UTC),
|
'edited_on': datetime.datetime.now(UTC),
|
||||||
'versions': versions_dict}
|
'versions': versions_dict}
|
||||||
self.course_index.insert(index_entry)
|
self.db_connection.insert_course_index(index_entry)
|
||||||
return self.get_course(CourseLocator(course_id=new_id, branch=master_branch))
|
return self.get_course(CourseLocator(course_id=new_id, branch=master_branch))
|
||||||
|
|
||||||
def update_item(self, descriptor, user_id, force=False):
|
def update_item(self, descriptor, user_id, force=False):
|
||||||
@@ -978,7 +946,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
'previous_version': block_data['edit_info']['update_version'],
|
'previous_version': block_data['edit_info']['update_version'],
|
||||||
'update_version': new_id,
|
'update_version': new_id,
|
||||||
}
|
}
|
||||||
self.structures.insert(new_structure)
|
self.db_connection.insert_structure(new_structure)
|
||||||
# update the index entry if appropriate
|
# update the index entry if appropriate
|
||||||
if index_entry is not None:
|
if index_entry is not None:
|
||||||
self._update_head(index_entry, descriptor.location.branch, new_id)
|
self._update_head(index_entry, descriptor.location.branch, new_id)
|
||||||
@@ -1016,7 +984,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
is_updated = self._persist_subdag(xblock, user_id, new_structure['blocks'], new_id)
|
is_updated = self._persist_subdag(xblock, user_id, new_structure['blocks'], new_id)
|
||||||
|
|
||||||
if is_updated:
|
if is_updated:
|
||||||
self.structures.insert(new_structure)
|
self.db_connection.insert_structure(new_structure)
|
||||||
|
|
||||||
# update the index entry if appropriate
|
# update the index entry if appropriate
|
||||||
if index_entry is not None:
|
if index_entry is not None:
|
||||||
@@ -1115,31 +1083,18 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
'''Deprecated, use update_item.'''
|
'''Deprecated, use update_item.'''
|
||||||
raise NotImplementedError('use update_item')
|
raise NotImplementedError('use update_item')
|
||||||
|
|
||||||
def update_course_index(self, course_locator, new_values_dict, update_versions=False):
|
def update_course_index(self, updated_index_entry):
|
||||||
"""
|
"""
|
||||||
Change the given course's index entry for the given fields. new_values_dict
|
Change the given course's index entry.
|
||||||
should be a subset of the dict returned by get_course_index_info.
|
|
||||||
It cannot include '_id' (will raise IllegalArgument).
|
|
||||||
Provide update_versions=True if you intend this to replace the versions hash.
|
|
||||||
Note, this operation can be dangerous and break running courses.
|
Note, this operation can be dangerous and break running courses.
|
||||||
|
|
||||||
If the dict includes versions and not update_versions, it will raise an exception.
|
|
||||||
|
|
||||||
If the dict includes edited_on or edited_by, it will raise an exception
|
|
||||||
|
|
||||||
Does not return anything useful.
|
Does not return anything useful.
|
||||||
"""
|
"""
|
||||||
# TODO how should this log the change? edited_on and edited_by for this entry
|
# TODO how should this log the change? edited_on and edited_by for this entry
|
||||||
# has the semantic of who created the course and when; so, changing those will lose
|
# has the semantic of who created the course and when; so, changing those will lose
|
||||||
# that information.
|
# that information.
|
||||||
if '_id' in new_values_dict:
|
self.db_connection.update_course_index(updated_index_entry)
|
||||||
raise ValueError("Cannot override _id")
|
|
||||||
if 'edited_on' in new_values_dict or 'edited_by' in new_values_dict:
|
|
||||||
raise ValueError("Cannot set edited_on or edited_by")
|
|
||||||
if not update_versions and 'versions' in new_values_dict:
|
|
||||||
raise ValueError("Cannot override versions without setting update_versions")
|
|
||||||
self.course_index.update({'_id': course_locator.course_id},
|
|
||||||
{'$set': new_values_dict})
|
|
||||||
|
|
||||||
def delete_item(self, usage_locator, user_id, delete_children=False, force=False):
|
def delete_item(self, usage_locator, user_id, delete_children=False, force=False):
|
||||||
"""
|
"""
|
||||||
@@ -1182,7 +1137,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
remove_subtree(usage_locator.usage_id)
|
remove_subtree(usage_locator.usage_id)
|
||||||
|
|
||||||
# update index if appropriate and structures
|
# update index if appropriate and structures
|
||||||
self.structures.insert(new_structure)
|
self.db_connection.insert_structure(new_structure)
|
||||||
|
|
||||||
result = CourseLocator(version_guid=new_id)
|
result = CourseLocator(version_guid=new_id)
|
||||||
|
|
||||||
@@ -1204,11 +1159,11 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
|
|
||||||
:param course_id: uses course_id rather than locator to emphasize its global effect
|
:param course_id: uses course_id rather than locator to emphasize its global effect
|
||||||
"""
|
"""
|
||||||
index = self.course_index.find_one({'_id': course_id})
|
index = self.db_connection.get_course_index(course_id)
|
||||||
if index is None:
|
if index is None:
|
||||||
raise ItemNotFoundError(course_id)
|
raise ItemNotFoundError(course_id)
|
||||||
# this is the only real delete in the system. should it do something else?
|
# this is the only real delete in the system. should it do something else?
|
||||||
self.course_index.remove(index['_id'])
|
self.db_connection.delete_course_index(index['_id'])
|
||||||
|
|
||||||
def get_errored_courses(self):
|
def get_errored_courses(self):
|
||||||
"""
|
"""
|
||||||
@@ -1296,7 +1251,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
block['fields']["children"] = [
|
block['fields']["children"] = [
|
||||||
usage_id for usage_id in block['fields']["children"] if usage_id in original_structure['blocks']
|
usage_id for usage_id in block['fields']["children"] if usage_id in original_structure['blocks']
|
||||||
]
|
]
|
||||||
self.structures.update({'_id': original_structure['_id']}, original_structure)
|
self.db_connection.update_structure(original_structure)
|
||||||
# clear cache again b/c inheritance may be wrong over orphans
|
# clear cache again b/c inheritance may be wrong over orphans
|
||||||
self._clear_cache(original_structure['_id'])
|
self._clear_cache(original_structure['_id'])
|
||||||
|
|
||||||
@@ -1379,7 +1334,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
index_entry = self.course_index.find_one({'_id': locator.course_id})
|
index_entry = self.db_connection.get_course_index(locator.course_id)
|
||||||
is_head = (
|
is_head = (
|
||||||
locator.version_guid is None or
|
locator.version_guid is None or
|
||||||
index_entry['versions'][locator.branch] == locator.version_guid
|
index_entry['versions'][locator.branch] == locator.version_guid
|
||||||
@@ -1424,9 +1379,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
|||||||
:param course_locator:
|
:param course_locator:
|
||||||
:param new_id:
|
:param new_id:
|
||||||
"""
|
"""
|
||||||
self.course_index.update(
|
index_entry['versions'][branch] = new_id
|
||||||
{"_id": index_entry["_id"]},
|
self.db_connection.update_course_index(index_entry)
|
||||||
{"$set": {"versions.{}".format(branch): new_id}})
|
|
||||||
|
|
||||||
def _partition_fields_by_scope(self, category, fields):
|
def _partition_fields_by_scope(self, category, fields):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -59,9 +59,9 @@ class TestMigration(unittest.TestCase):
|
|||||||
dbref = self.loc_mapper.db
|
dbref = self.loc_mapper.db
|
||||||
dbref.drop_collection(self.loc_mapper.location_map)
|
dbref.drop_collection(self.loc_mapper.location_map)
|
||||||
split_db = self.split_mongo.db
|
split_db = self.split_mongo.db
|
||||||
split_db.drop_collection(split_db.course_index)
|
split_db.drop_collection(self.split_mongo.db_connection.course_index)
|
||||||
split_db.drop_collection(split_db.structures)
|
split_db.drop_collection(self.split_mongo.db_connection.structures)
|
||||||
split_db.drop_collection(split_db.definitions)
|
split_db.drop_collection(self.split_mongo.db_connection.definitions)
|
||||||
# old_mongo doesn't give a db attr, but all of the dbs are the same
|
# old_mongo doesn't give a db attr, but all of the dbs are the same
|
||||||
dbref.drop_collection(self.old_mongo.collection)
|
dbref.drop_collection(self.old_mongo.collection)
|
||||||
|
|
||||||
|
|||||||
@@ -1018,41 +1018,29 @@ class TestCourseCreation(SplitModuleTest):
|
|||||||
Test changing the org, pretty id, etc of a course. Test that it doesn't allow changing the id, etc.
|
Test changing the org, pretty id, etc of a course. Test that it doesn't allow changing the id, etc.
|
||||||
"""
|
"""
|
||||||
locator = CourseLocator(course_id="GreekHero", branch='draft')
|
locator = CourseLocator(course_id="GreekHero", branch='draft')
|
||||||
modulestore().update_course_index(locator, {'org': 'funkyU'})
|
course_info = modulestore().get_course_index_info(locator)
|
||||||
|
course_info['org'] = 'funkyU'
|
||||||
|
modulestore().update_course_index(course_info)
|
||||||
course_info = modulestore().get_course_index_info(locator)
|
course_info = modulestore().get_course_index_info(locator)
|
||||||
self.assertEqual(course_info['org'], 'funkyU')
|
self.assertEqual(course_info['org'], 'funkyU')
|
||||||
|
|
||||||
modulestore().update_course_index(locator, {'org': 'moreFunky', 'prettyid': 'Ancient Greek Demagods'})
|
course_info['org'] = 'moreFunky'
|
||||||
|
course_info['prettyid'] = 'Ancient Greek Demagods'
|
||||||
|
modulestore().update_course_index(course_info)
|
||||||
course_info = modulestore().get_course_index_info(locator)
|
course_info = modulestore().get_course_index_info(locator)
|
||||||
self.assertEqual(course_info['org'], 'moreFunky')
|
self.assertEqual(course_info['org'], 'moreFunky')
|
||||||
self.assertEqual(course_info['prettyid'], 'Ancient Greek Demagods')
|
self.assertEqual(course_info['prettyid'], 'Ancient Greek Demagods')
|
||||||
|
|
||||||
self.assertRaises(ValueError, modulestore().update_course_index, locator, {'_id': 'funkygreeks'})
|
|
||||||
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
modulestore().update_course_index(
|
|
||||||
locator,
|
|
||||||
{'edited_on': datetime.datetime.now(UTC)}
|
|
||||||
)
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
modulestore().update_course_index(
|
|
||||||
locator,
|
|
||||||
{'edited_by': 'sneak'}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertRaises(ValueError, modulestore().update_course_index, locator,
|
|
||||||
{'versions': {'draft': self.GUID_D1}})
|
|
||||||
|
|
||||||
# an allowed but not necessarily recommended way to revert the draft version
|
# an allowed but not necessarily recommended way to revert the draft version
|
||||||
versions = course_info['versions']
|
versions = course_info['versions']
|
||||||
versions['draft'] = self.GUID_D1
|
versions['draft'] = self.GUID_D1
|
||||||
modulestore().update_course_index(locator, {'versions': versions}, update_versions=True)
|
modulestore().update_course_index(course_info)
|
||||||
course = modulestore().get_course(locator)
|
course = modulestore().get_course(locator)
|
||||||
self.assertEqual(str(course.location.version_guid), self.GUID_D1)
|
self.assertEqual(str(course.location.version_guid), self.GUID_D1)
|
||||||
|
|
||||||
# an allowed but not recommended way to publish a course
|
# an allowed but not recommended way to publish a course
|
||||||
versions['published'] = self.GUID_D1
|
versions['published'] = self.GUID_D1
|
||||||
modulestore().update_course_index(locator, {'versions': versions}, update_versions=True)
|
modulestore().update_course_index(course_info)
|
||||||
course = modulestore().get_course(CourseLocator(course_id=locator.course_id, branch="published"))
|
course = modulestore().get_course(CourseLocator(course_id=locator.course_id, branch="published"))
|
||||||
self.assertEqual(str(course.location.version_guid), self.GUID_D1)
|
self.assertEqual(str(course.location.version_guid), self.GUID_D1)
|
||||||
|
|
||||||
@@ -1068,9 +1056,9 @@ class TestCourseCreation(SplitModuleTest):
|
|||||||
self.assertEqual(new_course.location.usage_id, 'top')
|
self.assertEqual(new_course.location.usage_id, 'top')
|
||||||
self.assertEqual(new_course.category, 'chapter')
|
self.assertEqual(new_course.category, 'chapter')
|
||||||
# look at db to verify
|
# look at db to verify
|
||||||
db_structure = modulestore().structures.find_one({
|
db_structure = modulestore().db_connection.get_structure(
|
||||||
'_id': new_course.location.as_object_id(new_course.location.version_guid)
|
new_course.location.as_object_id(new_course.location.version_guid)
|
||||||
})
|
)
|
||||||
self.assertIsNotNone(db_structure, "Didn't find course")
|
self.assertIsNotNone(db_structure, "Didn't find course")
|
||||||
self.assertNotIn('course', db_structure['blocks'])
|
self.assertNotIn('course', db_structure['blocks'])
|
||||||
self.assertIn('top', db_structure['blocks'])
|
self.assertIn('top', db_structure['blocks'])
|
||||||
|
|||||||
Reference in New Issue
Block a user