diff --git a/cms/djangoapps/contentstore/course_info_model.py b/cms/djangoapps/contentstore/course_info_model.py index e8a359d80564..897306a89faa 100644 --- a/cms/djangoapps/contentstore/course_info_model.py +++ b/cms/djangoapps/contentstore/course_info_model.py @@ -34,10 +34,11 @@ def get_course_updates(location, provided_id, user_id): Retrieve the relevant course_info updates and unpack into the model which the client expects: [{id : index, date : string, content : html string}] """ + store = modulestore() try: - course_updates = modulestore().get_item(location) + course_updates = store.get_item(location) except ItemNotFoundError: - course_updates = modulestore().create_item(user_id, location.course_key, location.block_type, location.block_id) + course_updates = store.create_item(user_id, location.course_key, location.block_type, location.block_id) course_update_items = get_course_update_items(course_updates, _get_index(provided_id)) return _get_visible_update(course_update_items) @@ -52,10 +53,11 @@ def update_course_updates(location, update, passed_id=None, user=None, request_m It will update it if it has a passed_id which has a valid value. Until updates have distinct values, the passed_id is the location url + an index into the html structure. """ + store = modulestore() try: - course_updates = modulestore().get_item(location) + course_updates = store.get_item(location) except ItemNotFoundError: - course_updates = modulestore().create_item(user.id, location.course_key, location.block_type, location.block_id) + course_updates = store.create_item(user.id, location.course_key, location.block_type, location.block_id) course_update_items = list(reversed(get_course_update_items(course_updates))) course_update_dict = None diff --git a/cms/djangoapps/contentstore/exams.py b/cms/djangoapps/contentstore/exams.py index 8a4ddc09425e..61775b4a046e 100644 --- a/cms/djangoapps/contentstore/exams.py +++ b/cms/djangoapps/contentstore/exams.py @@ -31,12 +31,14 @@ def register_exams(course_key): # if feature is not enabled then do a quick exit return - course = modulestore().get_course(course_key) + store = modulestore() + + course = store.get_course(course_key) if course is None: raise ItemNotFoundError("Course {} does not exist", str(course_key)) # lint-amnesty, pylint: disable=raising-format-tuple # get all sequences, since they can be marked as timed/proctored exams - _timed_exams = modulestore().get_items( + _timed_exams = store.get_items( course_key, qualifiers={ 'category': 'sequential', diff --git a/cms/djangoapps/contentstore/helpers.py b/cms/djangoapps/contentstore/helpers.py index 9bfab1f1f385..1f9848036262 100644 --- a/cms/djangoapps/contentstore/helpers.py +++ b/cms/djangoapps/contentstore/helpers.py @@ -70,11 +70,12 @@ def get_parent_xblock(xblock): Returns the xblock that is the parent of the specified xblock, or None if it has no parent. """ locator = xblock.location - parent_location = modulestore().get_parent_location(locator) + store = modulestore() + parent_location = store.get_parent_location(locator) if parent_location is None: return None - return modulestore().get_item(parent_location) + return store.get_item(parent_location) def is_unit(xblock, parent_xblock=None): diff --git a/cms/djangoapps/contentstore/proctoring.py b/cms/djangoapps/contentstore/proctoring.py index bd33049006c4..62a371bcfd51 100644 --- a/cms/djangoapps/contentstore/proctoring.py +++ b/cms/djangoapps/contentstore/proctoring.py @@ -39,7 +39,8 @@ def register_special_exams(course_key): # if feature is not enabled then do a quick exit return - course = modulestore().get_course(course_key) + store = modulestore() + course = store.get_course(course_key) if course is None: raise ItemNotFoundError("Course {} does not exist", str(course_key)) # lint-amnesty, pylint: disable=raising-format-tuple @@ -49,7 +50,7 @@ def register_special_exams(course_key): return # get all sequences, since they can be marked as timed/proctored exams - _timed_exams = modulestore().get_items( + _timed_exams = store.get_items( course_key, qualifiers={ 'category': 'sequential', diff --git a/cms/djangoapps/contentstore/tasks.py b/cms/djangoapps/contentstore/tasks.py index ac9cc3d831ad..35feace56dc5 100644 --- a/cms/djangoapps/contentstore/tasks.py +++ b/cms/djangoapps/contentstore/tasks.py @@ -229,7 +229,7 @@ def rerun_course(source_course_key_string, destination_course_key_string, user_i try: # cleanup any remnants of the course - modulestore().delete_course(destination_course_key, user_id) + store.delete_course(destination_course_key, user_id) except ItemNotFoundError: # it's possible there was an error even before the course block was created pass @@ -380,10 +380,11 @@ def export_olx(self, user_id, course_key_string, language): self.status.fail(UserErrors.PERMISSION_DENIED) return + store = modulestore() if isinstance(courselike_key, LibraryLocator): - courselike_block = modulestore().get_library(courselike_key) + courselike_block = store.get_library(courselike_key) else: - courselike_block = modulestore().get_course(courselike_key) + courselike_block = store.get_course(courselike_key) try: self.status.set_state('Exporting') @@ -413,11 +414,12 @@ def create_export_tarball(course_block, course_key, context, status=None): root_dir = path(mkdtemp()) try: + store = modulestore() if isinstance(course_key, LibraryLocator): - export_library_to_xml(modulestore(), contentstore(), course_key, root_dir, name) + export_library_to_xml(store, contentstore(), course_key, root_dir, name) else: set_custom_attribute("exporting_course_to_xml_started", str(course_key)) - export_course_to_xml(modulestore(), contentstore(), course_block.id, root_dir, name) + export_course_to_xml(store, contentstore(), course_block.id, root_dir, name) set_custom_attribute("exporting_course_to_xml_completed", str(course_key)) if status: @@ -432,11 +434,11 @@ def create_export_tarball(course_block, course_key, context, status=None): LOGGER.exception('There was an error exporting %s', course_key, exc_info=True) parent = None try: - failed_item = modulestore().get_item(exc.location) - parent_loc = modulestore().get_parent_location(failed_item.location) + failed_item = store.get_item(exc.location) + parent_loc = store.get_parent_location(failed_item.location) if parent_loc is not None: - parent = modulestore().get_item(parent_loc) + parent = store.get_item(parent_loc) except: # pylint: disable=bare-except # if we have a nested exception, then we'll show the more generic error message pass @@ -505,7 +507,8 @@ def sync_discussion_settings(course_key, user): """ Syncs the discussion settings for a course with the DiscussionsConfiguration model. """ - course = modulestore().get_course(course_key) + store = modulestore() + course = store.get_course(course_key) try: discussion_config = DiscussionsConfiguration.objects.get(context_key=course_key) discussion_settings = course.discussions_settings @@ -519,7 +522,7 @@ def sync_discussion_settings(course_key, user): course.discussions_settings['enable_graded_units'] = False course.discussions_settings['unit_level_visibility'] = True course.discussions_settings['provider_type'] = Provider.OPEN_EDX - modulestore().update_item(course, user.id) + store.update_item(course, user.id) discussion_config.provider_type = Provider.OPEN_EDX @@ -641,13 +644,14 @@ def get_dir_for_filename(directory, filename): is_library = isinstance(courselike_key, LibraryLocator) is_course = not is_library + store = modulestore() if is_library: root_name = LIBRARY_ROOT - courselike_block = modulestore().get_library(courselike_key) + courselike_block = store.get_library(courselike_key) import_func = import_library_from_xml else: root_name = COURSE_ROOT - courselike_block = modulestore().get_course(courselike_key) + courselike_block = store.get_course(courselike_key) import_func = import_course_from_xml # Locate the uploaded OLX archive (and download it from S3 if necessary) @@ -733,7 +737,7 @@ def read_chunk(): LOGGER.info(f'{log_prefix}: Extracted file verified. Updating course started') courselike_items = import_func( - modulestore(), user.id, + store, user.id, settings.GITHUB_REPO_ROOT, [dirpath], load_error_blocks=False, static_content_store=contentstore(), @@ -757,9 +761,9 @@ def read_chunk(): if self.status.state == 'Updating' and is_course: # Reload the course so we have the latest state - course = modulestore().get_course(courselike_key) + course = store.get_course(courselike_key) if course.entrance_exam_enabled: - entrance_exam_chapter = modulestore().get_items( + entrance_exam_chapter = store.get_items( course.id, qualifiers={'category': 'chapter'}, settings={'is_entrance_exam': True} @@ -1241,14 +1245,15 @@ def _scan_course_for_links(course_key): ... ] """ - verticals = modulestore().get_items( + store = modulestore() + verticals = store.get_items( course_key, qualifiers={'category': 'vertical'}, revision=ModuleStoreEnum.RevisionOption.published_only ) blocks = [] urls_to_validate = [] - course = modulestore().get_course(course_key) + course = store.get_course(course_key) for vertical in verticals: blocks.extend(vertical.get_children()) diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index c79fa7a0feec..3fe0eb5c174f 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -638,13 +638,14 @@ def find_release_date_source(xblock): if xblock.category == 'chapter': return xblock - parent_location = modulestore().get_parent_location(xblock.location, - revision=ModuleStoreEnum.RevisionOption.draft_preferred) + store = modulestore() + parent_location = store.get_parent_location(xblock.location, + revision=ModuleStoreEnum.RevisionOption.draft_preferred) # Orphaned xblocks set their own release date if not parent_location: return xblock - parent = modulestore().get_item(parent_location) + parent = store.get_item(parent_location) if parent.start != xblock.start: return xblock else: @@ -665,13 +666,14 @@ def find_staff_lock_source(xblock): if xblock.category == 'chapter': return None - parent_location = modulestore().get_parent_location(xblock.location, + store = modulestore() + parent_location = store.get_parent_location(xblock.location, revision=ModuleStoreEnum.RevisionOption.draft_preferred) # Orphaned xblocks set their own staff lock if not parent_location: return None - parent = modulestore().get_item(parent_location) + parent = store.get_item(parent_location) return find_staff_lock_source(parent) @@ -680,12 +682,13 @@ def ancestor_has_staff_lock(xblock, parent_xblock=None): Returns True iff one of xblock's ancestors has staff lock. Can avoid mongo query by passing in parent_xblock. """ + store = modulestore() if parent_xblock is None: - parent_location = modulestore().get_parent_location(xblock.location, - revision=ModuleStoreEnum.RevisionOption.draft_preferred) + parent_location = store.get_parent_location(xblock.location, + revision=ModuleStoreEnum.RevisionOption.draft_preferred) if not parent_location: return False - parent_xblock = modulestore().get_item(parent_location) + parent_xblock = store.get_item(parent_location) return parent_xblock.visible_to_staff_only @@ -1116,9 +1119,10 @@ def get_subsections_by_assignment_type(course_key): the display name of the section they are in """ subsections_by_assignment_type = defaultdict(list) + store = modulestore() - with modulestore().bulk_operations(course_key): - course = modulestore().get_course(course_key, depth=3) + with store.bulk_operations(course_key): + course = store.get_course(course_key, depth=3) sections = course.get_children() for section in sections: subsections = section.get_children() diff --git a/xmodule/modulestore/django.py b/xmodule/modulestore/django.py index c15f03a4e33d..ba3b0b231771 100644 --- a/xmodule/modulestore/django.py +++ b/xmodule/modulestore/django.py @@ -324,44 +324,26 @@ def fetch_disabled_xblock_types(): ) -# A singleton instance of the Mixed Modulestore -_MIXED_MODULESTORE = None - - def modulestore(): """ Returns the Mixed modulestore """ - global _MIXED_MODULESTORE # pylint: disable=global-statement - if _MIXED_MODULESTORE is None: - _MIXED_MODULESTORE = create_modulestore_instance( - settings.MODULESTORE['default']['ENGINE'], - contentstore(), - settings.MODULESTORE['default'].get('DOC_STORE_CONFIG', {}), - settings.MODULESTORE['default'].get('OPTIONS', {}) - ) - - if settings.FEATURES.get('CUSTOM_COURSES_EDX'): - # TODO: This import prevents a circular import issue, but is - # symptomatic of a lib having a dependency on code in lms. This - # should be updated to have a setting that enumerates modulestore - # wrappers and then uses that setting to wrap the modulestore in - # appropriate wrappers depending on enabled features. - from lms.djangoapps.ccx.modulestore import CCXModulestoreWrapper - _MIXED_MODULESTORE = CCXModulestoreWrapper(_MIXED_MODULESTORE) - - return _MIXED_MODULESTORE - - -def clear_existing_modulestores(): - """ - Clear the existing modulestore instances, causing - them to be re-created when accessed again. - - This is useful for flushing state between unit tests. - """ - global _MIXED_MODULESTORE # pylint: disable=global-statement - _MIXED_MODULESTORE = None + mixed_modulestore = create_modulestore_instance( + settings.MODULESTORE['default']['ENGINE'], + contentstore(), + settings.MODULESTORE['default'].get('DOC_STORE_CONFIG', {}), + settings.MODULESTORE['default'].get('OPTIONS', {}) + ) + if settings.FEATURES.get('CUSTOM_COURSES_EDX'): + # TODO: This import prevents a circular import issue, but is + # symptomatic of a lib having a dependency on code in lms. This + # should be updated to have a setting that enumerates modulestore + # wrappers and then uses that setting to wrap the modulestore in + # appropriate wrappers depending on enabled features. + from lms.djangoapps.ccx.modulestore import CCXModulestoreWrapper + mixed_modulestore = CCXModulestoreWrapper(mixed_modulestore) + + return mixed_modulestore class XBlockI18nService: diff --git a/xmodule/modulestore/tests/django_utils.py b/xmodule/modulestore/tests/django_utils.py index f20aaa3a56d3..4ffb21ff28ea 100644 --- a/xmodule/modulestore/tests/django_utils.py +++ b/xmodule/modulestore/tests/django_utils.py @@ -19,7 +19,7 @@ from xmodule.contentstore.content import StaticContent from xmodule.contentstore.django import _CONTENTSTORE from xmodule.modulestore import ModuleStoreEnum -from xmodule.modulestore.django import SignalHandler, clear_existing_modulestores, modulestore +from xmodule.modulestore.django import SignalHandler, modulestore from xmodule.modulestore.tests.factories import XMODULE_FACTORY_LOCK from xmodule.modulestore.tests.mongo_connection import MONGO_HOST, MONGO_PORT_NUM @@ -321,7 +321,6 @@ def start_modulestore_isolation(cls): override.__enter__() # pylint: disable=unnecessary-dunder-call cls.__settings_overrides.append(override) XMODULE_FACTORY_LOCK.enable() - clear_existing_modulestores() cls.store = modulestore() @classmethod @@ -525,10 +524,6 @@ class FooTest(ModuleStoreTestCase): The reason is: XML courses are not editable, so to reset a course you have to reload it from disk, which is slow. - - If you do need to reset an XML course, use - `clear_existing_modulestores()` directly in - your `setUp()` method. """ CREATE_USER = True