diff --git a/cms/djangoapps/contentstore/management/commands/course_id_clash.py b/cms/djangoapps/contentstore/management/commands/course_id_clash.py index d8ca0128f5..791a5bf1bd 100644 --- a/cms/djangoapps/contentstore/management/commands/course_id_clash.py +++ b/cms/djangoapps/contentstore/management/commands/course_id_clash.py @@ -3,7 +3,7 @@ Script for finding all courses whose org/name pairs == other courses when ignori """ from django.core.management.base import BaseCommand from xmodule.modulestore.django import modulestore -from xmodule.modulestore import MONGO_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum # @@ -16,7 +16,7 @@ class Command(BaseCommand): help = 'List all courses ids in the Mongo Modulestore which may collide when ignoring case' def handle(self, *args, **options): - mstore = modulestore()._get_modulestore_by_type(MONGO_MODULESTORE_TYPE) # pylint: disable=protected-access + mstore = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) # pylint: disable=protected-access if hasattr(mstore, 'collection'): map_fn = ''' function () { diff --git a/cms/djangoapps/contentstore/management/commands/tests/test_migrate_to_split.py b/cms/djangoapps/contentstore/management/commands/tests/test_migrate_to_split.py index bf237310b6..cbc47f6ee7 100644 --- a/cms/djangoapps/contentstore/management/commands/tests/test_migrate_to_split.py +++ b/cms/djangoapps/contentstore/management/commands/tests/test_migrate_to_split.py @@ -6,7 +6,7 @@ import unittest from django.contrib.auth.models import User from django.core.management import CommandError, call_command from contentstore.management.commands.migrate_to_split import Command -from xmodule.modulestore import SPLIT_MONGO_MODULESTORE_TYPE, REVISION_OPTION_PUBLISHED_ONLY +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.django import modulestore, clear_existing_modulestores @@ -56,7 +56,7 @@ class TestMigrateToSplit(ModuleStoreTestCase): password = 'foo' self.user = User.objects.create_user(uname, email, password) self.course = CourseFactory() - self.addCleanup(ModuleStoreTestCase.drop_mongo_collections, SPLIT_MONGO_MODULESTORE_TYPE) + self.addCleanup(ModuleStoreTestCase.drop_mongo_collections, ModuleStoreEnum.Type.split) self.addCleanup(clear_existing_modulestores) def test_user_email(self): @@ -84,6 +84,6 @@ class TestMigrateToSplit(ModuleStoreTestCase): str(self.user.id), "org.dept+name.run", ) - locator = CourseLocator(org="org.dept", offering="name.run", branch=REVISION_OPTION_PUBLISHED_ONLY) + locator = CourseLocator(org="org.dept", offering="name.run", branch=ModuleStoreEnum.RevisionOption.published_only) course_from_split = modulestore('split').get_course(locator) self.assertIsNotNone(course_from_split) diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index d8010e3d06..8665d59542 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -27,10 +27,8 @@ from xmodule.contentstore.content import StaticContent from xmodule.contentstore.django import contentstore, _CONTENTSTORE from xmodule.contentstore.utils import restore_asset_from_trashcan, empty_asset_trashcan from xmodule.exceptions import NotFoundError, InvalidVersionError -from xmodule.modulestore import ( - mongo, MONGO_MODULESTORE_TYPE, PublishState, - REVISION_OPTION_PUBLISHED_ONLY, REVISION_OPTION_DRAFT_ONLY, KEY_REVISION_PUBLISHED, BRANCH_PUBLISHED_ONLY -) +from xmodule.modulestore import mongo, PublishState, ModuleStoreEnum +from xmodule.modulestore.mongo.base import MongoRevisionKey from xmodule.modulestore.mixed import store_branch_setting from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError @@ -204,13 +202,13 @@ class ContentStoreToyCourseTest(ContentStoreTestCase): store.convert_to_draft(html_module_from_draft_store.location, self.user.id) # Query get_items() and find the html item. This should just return back a single item (not 2). - direct_store_items = store.get_items(course_key, revision=REVISION_OPTION_PUBLISHED_ONLY) + direct_store_items = store.get_items(course_key, revision=ModuleStoreEnum.RevisionOption.published_only) html_items_from_direct_store = [item for item in direct_store_items if (item.location == html_usage_key)] self.assertEqual(len(html_items_from_direct_store), 1) self.assertFalse(getattr(html_items_from_direct_store[0], 'is_draft', False)) # Fetch from the draft store. - draft_store_items = store.get_items(course_key, revision=REVISION_OPTION_DRAFT_ONLY) + draft_store_items = store.get_items(course_key, revision=ModuleStoreEnum.RevisionOption.draft_only) html_items_from_draft_store = [item for item in draft_store_items if (item.location == html_usage_key)] self.assertEqual(len(html_items_from_draft_store), 1) self.assertTrue(getattr(html_items_from_draft_store[0], 'is_draft', False)) @@ -663,9 +661,9 @@ class ContentStoreToyCourseTest(ContentStoreTestCase): clone_course(module_store, content_store, source_course_id, dest_course_id, self.user.id) # first assert that all draft content got cloned as well - draft_items = module_store.get_items(source_course_id, revision=REVISION_OPTION_DRAFT_ONLY) + draft_items = module_store.get_items(source_course_id, revision=ModuleStoreEnum.RevisionOption.draft_only) self.assertGreater(len(draft_items), 0) - draft_clone_items = module_store.get_items(dest_course_id, revision=REVISION_OPTION_DRAFT_ONLY) + draft_clone_items = module_store.get_items(dest_course_id, revision=ModuleStoreEnum.RevisionOption.draft_only) self.assertGreater(len(draft_clone_items), 0) self.assertEqual(len(draft_items), len(draft_clone_items)) @@ -868,7 +866,7 @@ class ContentStoreToyCourseTest(ContentStoreTestCase): # add the new private and new public to list of children sequential = module_store.get_item(course_id.make_usage_key('sequential', 'vertical_sequential')) - private_location_no_draft = private_vertical.location.replace(revision=KEY_REVISION_PUBLISHED) + private_location_no_draft = private_vertical.location.replace(revision=MongoRevisionKey.published) sequential.children.append(private_location_no_draft) sequential.children.append(public_vertical_location) module_store.update_item(sequential, self.user.id) @@ -939,7 +937,11 @@ class ContentStoreToyCourseTest(ContentStoreTestCase): target_course_id=course_id, ) - items = module_store.get_items(course_id, category='vertical', revision=REVISION_OPTION_PUBLISHED_ONLY) + items = module_store.get_items( + course_id, + category='vertical', + revision=ModuleStoreEnum.RevisionOption.published_only + ) self._check_verticals(items) def verify_item_publish_state(item, publish_state): @@ -1124,7 +1126,7 @@ class ContentStoreToyCourseTest(ContentStoreTestCase): self.assertContains(resp, '/c4x/edX/toy/asset/handouts_sample_handout.txt') def test_prefetch_children(self): - mongo_store = modulestore()._get_modulestore_by_type(MONGO_MODULESTORE_TYPE) + mongo_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) import_from_xml(modulestore(), self.user.id, 'common/test/data/', ['toy']) course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') @@ -1132,7 +1134,7 @@ class ContentStoreToyCourseTest(ContentStoreTestCase): mongo_store.collection.find = wrapper.find # set the branch to 'publish' in order to prevent extra lookups of draft versions - with store_branch_setting(mongo_store, BRANCH_PUBLISHED_ONLY): + with store_branch_setting(mongo_store, ModuleStoreEnum.Branch.published_only): course = mongo_store.get_course(course_id, depth=2) # make sure we haven't done too many round trips to DB diff --git a/cms/djangoapps/contentstore/tests/test_course_listing.py b/cms/djangoapps/contentstore/tests/test_course_listing.py index 387b2678e5..7d8c726db9 100644 --- a/cms/djangoapps/contentstore/tests/test_course_listing.py +++ b/cms/djangoapps/contentstore/tests/test_course_listing.py @@ -17,7 +17,7 @@ from student.tests.factories import UserFactory from student.roles import CourseInstructorRole, CourseStaffRole, GlobalStaff, OrgStaffRole, OrgInstructorRole from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, check_mongo_calls -from xmodule.modulestore import MONGO_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from opaque_keys.edx.locations import SlashSeparatedCourseKey from xmodule.modulestore.django import modulestore from xmodule.error_module import ErrorDescriptor @@ -199,7 +199,7 @@ class TestCourseListing(ModuleStoreTestCase): self.assertGreaterEqual(iteration_over_courses_time_2.elapsed, iteration_over_groups_time_2.elapsed) # Now count the db queries - store = modulestore()._get_modulestore_by_type(MONGO_MODULESTORE_TYPE) + store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) with check_mongo_calls(store.collection, USER_COURSES_COUNT): courses_list = _accessible_courses_list_from_groups(self.request) @@ -262,7 +262,7 @@ class TestCourseListing(ModuleStoreTestCase): Create good courses, courses that won't load, and deleted courses which still have roles. Test course listing. """ - store = modulestore()._get_modulestore_by_type(MONGO_MODULESTORE_TYPE) + store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) course_location = SlashSeparatedCourseKey('testOrg', 'testCourse', 'RunBabyRun') self._create_course_with_access_groups(course_location, self.user) diff --git a/cms/djangoapps/contentstore/tests/test_crud.py b/cms/djangoapps/contentstore/tests/test_crud.py index 9431016b6c..0accd31b8e 100644 --- a/cms/djangoapps/contentstore/tests/test_crud.py +++ b/cms/djangoapps/contentstore/tests/test_crud.py @@ -1,7 +1,7 @@ import unittest from xmodule import templates -from xmodule.modulestore import SPLIT_MONGO_MODULESTORE_TYPE, BRANCH_NAME_DRAFT +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests import persistent_factories from xmodule.course_module import CourseDescriptor from xmodule.modulestore.django import modulestore, clear_existing_modulestores, _MIXED_MODULESTORE, \ @@ -21,9 +21,9 @@ class TemplateTests(unittest.TestCase): def setUp(self): clear_existing_modulestores() # redundant w/ cleanup but someone was getting errors - self.addCleanup(ModuleStoreTestCase.drop_mongo_collections, SPLIT_MONGO_MODULESTORE_TYPE) + self.addCleanup(ModuleStoreTestCase.drop_mongo_collections, ModuleStoreEnum.Type.split) self.addCleanup(clear_existing_modulestores) - self.split_store = modulestore()._get_modulestore_by_type(SPLIT_MONGO_MODULESTORE_TYPE) + self.split_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.split) def test_get_templates(self): found = templates.all_templates() @@ -156,7 +156,7 @@ class TemplateTests(unittest.TestCase): persistent_factories.ItemFactory.create(display_name='chapter 1', parent_location=test_course.location) - id_locator = test_course.id.for_branch(BRANCH_NAME_DRAFT) + id_locator = test_course.id.for_branch(ModuleStoreEnum.BranchName.draft) guid_locator = test_course.location.course_agnostic() # verify it can be retrieved by id self.assertIsInstance(self.split_store.get_course(id_locator), CourseDescriptor) @@ -241,7 +241,7 @@ class SplitAndLocMapperTests(unittest.TestCase): mapper = loc_mapper() # instantiate mixed modulestore and thus split - split_store = modulestore()._get_modulestore_by_type(SPLIT_MONGO_MODULESTORE_TYPE) + split_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.split) # split must inject the same location mapper object since the mapper existed before it did self.assertEqual(split_store.loc_mapper, mapper) @@ -254,7 +254,7 @@ class SplitAndLocMapperTests(unittest.TestCase): self.assertIsNone(_loc_singleton) # instantiate split before location mapper - split_store = modulestore()._get_modulestore_by_type(SPLIT_MONGO_MODULESTORE_TYPE) + split_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.split) # split must have instantiated loc_mapper mapper = loc_mapper() diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index e55f12dfdb..34b982cea5 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -21,7 +21,7 @@ from xblock.fragment import Fragment import xmodule from xmodule.tabs import StaticTab, CourseTabList -from xmodule.modulestore import PublishState, REVISION_OPTION_ALL +from xmodule.modulestore import PublishState, ModuleStoreEnum from xmodule.modulestore.django import modulestore from xmodule.modulestore.draft import DIRECT_ONLY_CATEGORIES from xmodule.modulestore.exceptions import ItemNotFoundError, InvalidLocationError, DuplicateItemError @@ -527,7 +527,7 @@ def orphan_handler(request, course_key_string): # get_orphans returns the deprecated string format w/o revision usage_key = course_usage_key.make_usage_key_from_deprecated_string(itemloc) # need to delete all versions - store.delete_item(usage_key, request.user.id, revision=REVISION_OPTION_ALL) + store.delete_item(usage_key, request.user.id, revision=ModuleStoreEnum.RevisionOption.all) return JsonResponse({'deleted': items}) else: raise PermissionDenied() diff --git a/common/djangoapps/static_replace/__init__.py b/common/djangoapps/static_replace/__init__.py index 56f31b2e36..cd2e077444 100644 --- a/common/djangoapps/static_replace/__init__.py +++ b/common/djangoapps/static_replace/__init__.py @@ -6,7 +6,7 @@ from staticfiles import finders from django.conf import settings from xmodule.modulestore.django import modulestore -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.contentstore.content import StaticContent log = logging.getLogger(__name__) @@ -119,7 +119,9 @@ def replace_static_urls(text, data_directory, course_id=None, static_asset_path= if settings.DEBUG and finders.find(rest, True): return original # if we're running with a MongoBacked store course_namespace is not None, then use studio style urls - elif (not static_asset_path) and course_id and modulestore().get_modulestore_type(course_id) != XML_MODULESTORE_TYPE: + elif (not static_asset_path) \ + and course_id \ + and modulestore().get_modulestore_type(course_id) != ModuleStoreEnum.Type.xml: # first look in the static file pipeline and see if we are trying to reference # a piece of static content which is in the edx-platform repo (e.g. JS associated with an xmodule) diff --git a/common/djangoapps/student/migrations/0036_access_roles_orgless.py b/common/djangoapps/student/migrations/0036_access_roles_orgless.py index 19687e4cdb..7ae056d403 100644 --- a/common/djangoapps/student/migrations/0036_access_roles_orgless.py +++ b/common/djangoapps/student/migrations/0036_access_roles_orgless.py @@ -7,7 +7,7 @@ from opaque_keys import InvalidKeyError import logging from django.db.models.query_utils import Q from django.db.utils import IntegrityError -from xmodule.modulestore import XML_MODULESTORE_TYPE, MONGO_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.mixed import MixedModuleStore log = logging.getLogger(__name__) @@ -27,8 +27,8 @@ class Migration(DataMigration): # Note: Remember to use orm['appname.ModelName'] rather than "from appname.models..." loc_map_collection = loc_mapper().location_map mixed_ms = modulestore() - xml_ms = mixed_ms._get_modulestore_by_type(XML_MODULESTORE_TYPE) - mongo_ms = mixed_ms._get_modulestore_by_type(MONGO_MODULESTORE_TYPE) + xml_ms = mixed_ms._get_modulestore_by_type(ModuleStoreEnum.Type.xml) + mongo_ms = mixed_ms._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) query = Q(name__startswith='staff') | Q(name__startswith='instructor') | Q(name__startswith='beta_testers') for group in orm['auth.Group'].objects.filter(query).exclude(name__contains="/").all(): diff --git a/common/djangoapps/student/tests/test_course_listing.py b/common/djangoapps/student/tests/test_course_listing.py index e1bdfd839d..4625660913 100644 --- a/common/djangoapps/student/tests/test_course_listing.py +++ b/common/djangoapps/student/tests/test_course_listing.py @@ -6,7 +6,7 @@ from mock import patch, Mock from student.tests.factories import UserFactory from student.roles import GlobalStaff -from xmodule.modulestore import MONGO_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory from opaque_keys.edx.locations import SlashSeparatedCourseKey @@ -90,7 +90,7 @@ class TestCourseListing(ModuleStoreTestCase): Create good courses, courses that won't load, and deleted courses which still have roles. Test course listing. """ - mongo_store = modulestore()._get_modulestore_by_type(MONGO_MODULESTORE_TYPE) + mongo_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) good_location = SlashSeparatedCourseKey('testOrg', 'testCourse', 'RunBabyRun') self._create_course_with_access_groups(good_location) diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index 9b9dea4a48..e82482afef 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -53,7 +53,7 @@ from dark_lang.models import DarkLangConfig from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.django import modulestore from opaque_keys.edx.locations import SlashSeparatedCourseKey -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from collections import namedtuple @@ -462,7 +462,7 @@ def dashboard(request): show_email_settings_for = frozenset( course.id for course, _enrollment in course_enrollment_pairs if ( settings.FEATURES['ENABLE_INSTRUCTOR_EMAIL'] and - modulestore().get_modulestore_type(course.id) != XML_MODULESTORE_TYPE and + modulestore().get_modulestore_type(course.id) != ModuleStoreEnum.Type.xml and CourseAuthorization.instructor_email_enabled(course.id) ) ) diff --git a/common/djangoapps/terrain/browser.py b/common/djangoapps/terrain/browser.py index 5f9040c699..f31057c455 100644 --- a/common/djangoapps/terrain/browser.py +++ b/common/djangoapps/terrain/browser.py @@ -19,7 +19,7 @@ from json import dumps from pymongo import MongoClient import xmodule.modulestore.django from xmodule.contentstore.django import _CONTENTSTORE -from xmodule.modulestore import MONGO_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum # There is an import issue when using django-staticfiles with lettuce # Lettuce assumes that we are using django.contrib.staticfiles, @@ -190,7 +190,7 @@ def reset_databases(scenario): mongo.drop_database(settings.CONTENTSTORE['DOC_STORE_CONFIG']['db']) _CONTENTSTORE.clear() - modulestore = xmodule.modulestore.django.modulestore()._get_modulestore_by_type(MONGO_MODULESTORE_TYPE) + modulestore = xmodule.modulestore.django.modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) modulestore.collection.drop() xmodule.modulestore.django.clear_existing_modulestores() diff --git a/common/djangoapps/terrain/course_helpers.py b/common/djangoapps/terrain/course_helpers.py index e28671e558..092792dab6 100644 --- a/common/djangoapps/terrain/course_helpers.py +++ b/common/djangoapps/terrain/course_helpers.py @@ -6,7 +6,7 @@ from lettuce import world from django.contrib.auth.models import User, Group from student.models import CourseEnrollment from xmodule.modulestore.django import modulestore -from xmodule.modulestore import MONGO_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.contentstore.django import contentstore @@ -72,6 +72,6 @@ def clear_courses(): # (though it shouldn't), do this manually # from the bash shell to drop it: # $ mongo test_xmodule --eval "db.dropDatabase()" - store = modulestore()._get_modulestore_by_type(MONGO_MODULESTORE_TYPE) + store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) store.collection.drop() contentstore().fs_files.drop() diff --git a/common/djangoapps/xmodule_modifiers.py b/common/djangoapps/xmodule_modifiers.py index 1baffd2e59..21061da606 100644 --- a/common/djangoapps/xmodule_modifiers.py +++ b/common/djangoapps/xmodule_modifiers.py @@ -16,7 +16,7 @@ from xblock.fragment import Fragment from xmodule.seq_module import SequenceModule from xmodule.vertical_module import VerticalModule from xmodule.x_module import shim_xmodule_js, XModuleDescriptor, XModule, PREVIEW_VIEWS, STUDIO_VIEW -from xmodule.modulestore import MONGO_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore log = logging.getLogger(__name__) @@ -169,7 +169,7 @@ def add_staff_markup(user, has_instructor_access, block, view, frag, context): # TODO: make this more general, eg use an XModule attribute instead if isinstance(block, VerticalModule) and (not context or not context.get('child_of_vertical', False)): # check that the course is a mongo backed Studio course before doing work - is_mongo_course = modulestore().get_modulestore_type(block.location.course_key) == MONGO_MODULESTORE_TYPE + is_mongo_course = modulestore().get_modulestore_type(block.location.course_key) == ModuleStoreEnum.Type.mongo is_studio_course = block.course_edit_method == "Studio" if is_studio_course and is_mongo_course: diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py index 00c233182c..73b5536202 100644 --- a/common/lib/xmodule/xmodule/modulestore/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/__init__.py @@ -25,44 +25,51 @@ from xblock.core import XBlock log = logging.getLogger('edx.modulestore') -# Modulestore Types -SPLIT_MONGO_MODULESTORE_TYPE = 'split' -MONGO_MODULESTORE_TYPE = 'mongo' -XML_MODULESTORE_TYPE = 'xml' +class ModuleStoreEnum(object): + """ + A class to encapsulate common constants that are used with the various modulestores. + """ + class Type(object): + """ + The various types of modulestores provided + """ + split = 'split' + mongo = 'mongo' + xml = 'xml' -# Key Revision constants to use for Location and Usage Keys -# Note: These values are persisted in the database, so should not be changed without migrations -KEY_REVISION_DRAFT = 'draft' -KEY_REVISION_PUBLISHED = None + class RevisionOption(object): + """ + Revision constants to use for Module Store operations + Note: These values are passed into store APIs and only used at run time + """ + # both DRAFT and PUBLISHED versions are queried, with preference to DRAFT versions + draft_preferred = 'rev-opt-draft-preferred' + # only DRAFT versions are queried and no PUBLISHED versions + draft_only = 'rev-opt-draft-only' -# Revision constants to use for Module Store operations -# Note: These values are passed into store APIs and only used at run time + # # only PUBLISHED versions are queried and no DRAFT versions + published_only = 'rev-opt-published-only' -# both DRAFT and PUBLISHED versions are queried, with preference to DRAFT versions -REVISION_OPTION_DRAFT_PREFERRED = 'rev-opt-draft-preferred' + # all revisions are queried + all = 'rev-opt-all' -# only DRAFT versions are queried and no PUBLISHED versions -REVISION_OPTION_DRAFT_ONLY = 'rev-opt-draft-only' + class Branch(object): + """ + Branch constants to use for stores, such as Mongo, that have only 2 branches: DRAFT and PUBLISHED + Note: These values are taken from server configuration settings, so should not be changed without alerting DevOps + """ + draft_preferred = 'draft-preferred' + published_only = 'published-only' -# # only PUBLISHED versions are queried and no DRAFT versions -REVISION_OPTION_PUBLISHED_ONLY = 'rev-opt-published-only' - -# all revisions are queried -REVISION_OPTION_ALL = 'rev-opt-all' - - -# Branch constants to use for stores, such as Mongo, that have only 2 branches: DRAFT and PUBLISHED -# Note: These values are taken from server configuration settings, so should not be changed without alerting DevOps -BRANCH_DRAFT_PREFERRED = 'draft' -BRANCH_PUBLISHED_ONLY = 'published' - - -# Branch constants to use for stores, such as Split, that have named branches -BRANCH_NAME_DRAFT = 'draft' -BRANCH_NAME_PUBLISHED = 'published' + class BranchName(object): + """ + Branch constants to use for stores, such as Split, that have named branches + """ + draft = 'draft-branch' + published = 'published-branch' class PublishState(object): diff --git a/common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py b/common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py index fb6af5d640..0a7610b85f 100644 --- a/common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py +++ b/common/lib/xmodule/xmodule/modulestore/loc_mapper_store.py @@ -7,7 +7,7 @@ import pymongo import bson.son import urllib -from xmodule.modulestore import BRANCH_NAME_PUBLISHED, BRANCH_NAME_DRAFT +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.exceptions import InvalidLocationError, ItemNotFoundError from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator from opaque_keys.edx.locations import SlashSeparatedCourseKey @@ -55,7 +55,9 @@ class LocMapperStore(object): # location_map functions def create_map_entry(self, course_key, org=None, offering=None, - draft_branch=BRANCH_NAME_DRAFT, prod_branch=BRANCH_NAME_PUBLISHED, block_map=None): + draft_branch=ModuleStoreEnum.BranchName.draft, + prod_branch=ModuleStoreEnum.BranchName.published, + block_map=None): """ Add a new entry to map this SlashSeparatedCourseKey to the new style CourseLocator.org & offering. If org and offering are not provided, it defaults them based on course_key. @@ -245,7 +247,7 @@ class LocMapperStore(object): for old_name, cat_to_usage in entry['block_map'].iteritems(): for category, block_id in cat_to_usage.iteritems(): # cache all entries and then figure out if we have the one we want - # Always return revision=KEY_REVISION_PUBLISHED because the + # Always return revision=MongoRevisionKey.published because the # old draft module store wraps locations as draft before # trying to access things. location = old_course_id.make_usage_key( diff --git a/common/lib/xmodule/xmodule/modulestore/mixed.py b/common/lib/xmodule/xmodule/modulestore/mixed.py index 96049002d3..be68c3a3e1 100644 --- a/common/lib/xmodule/xmodule/modulestore/mixed.py +++ b/common/lib/xmodule/xmodule/modulestore/mixed.py @@ -110,11 +110,7 @@ class MixedModuleStore(ModuleStoreWriteBase): def _get_modulestore_by_type(self, modulestore_type): """ This method should only really be used by tests and migration scripts when necessary. - Returns the module store as requested by type. The type can be: - - SPLIT_MONGO_MODULESTORE_TYPE - MONGO_MODULESTORE_TYPE - XML_MODULESTORE_TYPE + Returns the module store as requested by type. The type can be a value from ModuleStoreEnum.Type. """ for store in self.modulestores: if store.get_modulestore_type() == modulestore_type: diff --git a/common/lib/xmodule/xmodule/modulestore/mongo/base.py b/common/lib/xmodule/xmodule/modulestore/mongo/base.py index dcc109dff1..d9c9270a95 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo/base.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo/base.py @@ -33,11 +33,7 @@ from xblock.runtime import KvsFieldData from xblock.exceptions import InvalidScopeError from xblock.fields import Scope, ScopeIds, Reference, ReferenceList, ReferenceValueDict -from xmodule.modulestore import ( - ModuleStoreWriteBase, MONGO_MODULESTORE_TYPE, - REVISION_OPTION_PUBLISHED_ONLY, REVISION_OPTION_DRAFT_PREFERRED, - KEY_REVISION_DRAFT, KEY_REVISION_PUBLISHED -) +from xmodule.modulestore import ModuleStoreWriteBase, ModuleStoreEnum from opaque_keys.edx.locations import Location from xmodule.modulestore.exceptions import ItemNotFoundError, InvalidLocationError, ReferentialIntegrityError from xmodule.modulestore.inheritance import own_metadata, InheritanceMixin, inherit_metadata, InheritanceKeyValueStore @@ -58,6 +54,15 @@ SORT_REVISION_FAVOR_DRAFT = ('_id.revision', pymongo.DESCENDING) SORT_REVISION_FAVOR_PUBLISHED = ('_id.revision', pymongo.ASCENDING) +class MongoRevisionKey(object): + """ + Key Revision constants to use for Location and Usage Keys in the Mongo modulestore + Note: These values are persisted in the database, so should not be changed without migrations + """ + draft = 'draft' + published = None + + class InvalidWriteError(Exception): """ Raised to indicate that writing to a particular key @@ -303,14 +308,14 @@ def as_draft(location): """ if location.category in DIRECT_ONLY_CATEGORIES: return location - return location.replace(revision=KEY_REVISION_DRAFT) + return location.replace(revision=MongoRevisionKey.draft) def as_published(location): """ Returns the Location that is the published version for `location` """ - return location.replace(revision=KEY_REVISION_PUBLISHED) + return location.replace(revision=MongoRevisionKey.published) class MongoModuleStore(ModuleStoreWriteBase): @@ -744,7 +749,7 @@ class MongoModuleStore(ModuleStoreWriteBase): for key in ('tag', 'org', 'course', 'category', 'name', 'revision') ]) - def get_items(self, course_id, settings=None, content=None, key_revision=KEY_REVISION_PUBLISHED, **kwargs): + def get_items(self, course_id, settings=None, content=None, key_revision=MongoRevisionKey.published, **kwargs): """ Returns: list of XModuleDescriptor instances for the matching items within the course with @@ -763,10 +768,10 @@ class MongoModuleStore(ModuleStoreWriteBase): content (dict): fields to look for which have content scope. Follows same syntax and rules as kwargs below. key_revision (str): the revision of the items you're looking for. - KEY_REVISION_DRAFT - only returns drafts - KEY_REVISION_PUBLISHED (equates to None) - only returns published + MongoRevisionKey.draft - only returns drafts + MongoRevisionKey.published (equates to None) - only returns published If you want one of each matching xblock but preferring draft to published, call this same method - on the draft modulestore with REVISION_OPTION_DRAFT_PREFERRED. + on the draft modulestore with ModuleStoreEnum.RevisionOption.draft_preferred. kwargs (key=value): what to look for within the course. Common qualifiers are ``category`` or any field name. if the target field is a list, then it searches for the given value in the list not list equivalence. @@ -1003,7 +1008,7 @@ class MongoModuleStore(ModuleStoreWriteBase): value[key] = subvalue.to_deprecated_string() return jsonfields - def get_parent_location(self, location, revision=REVISION_OPTION_PUBLISHED_ONLY, **kwargs): + def get_parent_location(self, location, revision=ModuleStoreEnum.RevisionOption.published_only, **kwargs): ''' Find the location that is the parent of this location in this course. @@ -1011,21 +1016,24 @@ class MongoModuleStore(ModuleStoreWriteBase): Args: revision: - REVISION_OPTION_PUBLISHED_ONLY - return only the PUBLISHED parent if it exists, else returns None - REVISION_OPTION_DRAFT_PREFERRED - return either the DRAFT or PUBLISHED parent, - preferring DRAFT, if parent(s) exists, - else returns None + ModuleStoreEnum.RevisionOption.published_only + - return only the PUBLISHED parent if it exists, else returns None + ModuleStoreEnum.RevisionOption.draft_preferred + - return either the DRAFT or PUBLISHED parent, + preferring DRAFT, if parent(s) exists, + else returns None ''' assert location.revision is None - assert revision == REVISION_OPTION_PUBLISHED_ONLY or revision == REVISION_OPTION_DRAFT_PREFERRED + assert revision == ModuleStoreEnum.RevisionOption.published_only \ + or revision == ModuleStoreEnum.RevisionOption.draft_preferred # create a query with tag, org, course, and the children field set to the given location query = self._course_key_to_son(location.course_key) query['definition.children'] = location.to_deprecated_string() # if only looking for the PUBLISHED parent, set the revision in the query to None - if revision == REVISION_OPTION_PUBLISHED_ONLY: - query['_id.revision'] = KEY_REVISION_PUBLISHED + if revision == ModuleStoreEnum.RevisionOption.published_only: + query['_id.revision'] = MongoRevisionKey.published # query the collection, sorting by DRAFT first parents = self.collection.find(query, {'_id': True}, sort=[SORT_REVISION_FAVOR_DRAFT]) @@ -1034,7 +1042,7 @@ class MongoModuleStore(ModuleStoreWriteBase): # no parents were found return None - if revision == REVISION_OPTION_PUBLISHED_ONLY: + if revision == ModuleStoreEnum.RevisionOption.published_only: if parents.count() > 1: # should never have multiple PUBLISHED parents raise ReferentialIntegrityError( @@ -1055,16 +1063,11 @@ class MongoModuleStore(ModuleStoreWriteBase): def get_modulestore_type(self, course_key=None): """ - Returns an enumeration-like type reflecting the type of this modulestore - The return can be one of: - "xml" (for XML based courses), - "mongo" for old-style MongoDB backed courses, - "split" for new-style split MongoDB backed courses. - + Returns an enumeration-like type reflecting the type of this modulestore per ModuleStoreEnum.Type Args: course_key: just for signature compatibility """ - return MONGO_MODULESTORE_TYPE + return ModuleStoreEnum.Type.mongo def get_orphans(self, course_key): """ @@ -1114,6 +1117,6 @@ class MongoModuleStore(ModuleStoreWriteBase): Check that the db is reachable. """ if self.database.connection.alive(): - return {MONGO_MODULESTORE_TYPE: True} + return {ModuleStoreEnum.Type.mongo: True} else: raise HeartbeatFailure("Can't connect to {}".format(self.database.name), 'mongo') diff --git a/common/lib/xmodule/xmodule/modulestore/mongo/draft.py b/common/lib/xmodule/xmodule/modulestore/mongo/draft.py index f34a0871d8..99bda91cbe 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo/draft.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo/draft.py @@ -9,15 +9,11 @@ and otherwise returns i4x://org/course/cat/name). import pymongo from xmodule.exceptions import InvalidVersionError -from xmodule.modulestore import ( - PublishState, - REVISION_OPTION_DRAFT_PREFERRED, REVISION_OPTION_DRAFT_ONLY, REVISION_OPTION_PUBLISHED_ONLY, REVISION_OPTION_ALL, - KEY_REVISION_PUBLISHED, KEY_REVISION_DRAFT, BRANCH_PUBLISHED_ONLY, BRANCH_DRAFT_PREFERRED -) +from xmodule.modulestore import PublishState, ModuleStoreEnum from xmodule.modulestore.exceptions import ItemNotFoundError, DuplicateItemError, InvalidBranchSetting from xmodule.modulestore.mongo.base import ( - MongoModuleStore, as_draft, as_published, - DIRECT_ONLY_CATEGORIES, SORT_REVISION_FAVOR_DRAFT, + MongoModuleStore, MongoRevisionKey, as_draft, as_published, + DIRECT_ONLY_CATEGORIES, SORT_REVISION_FAVOR_DRAFT ) from opaque_keys.edx.locations import Location @@ -29,8 +25,8 @@ def wrap_draft(item): Sets `item.is_draft` to `True` if the item is DRAFT, and `False` otherwise. Sets the item's location to the non-draft location in either case. """ - setattr(item, 'is_draft', item.location.revision == KEY_REVISION_DRAFT) - item.location = item.location.replace(revision=KEY_REVISION_PUBLISHED) + setattr(item, 'is_draft', item.location.revision == MongoRevisionKey.draft) + item.location = item.location.replace(revision=MongoRevisionKey.published) return item @@ -51,7 +47,7 @@ class DraftModuleStore(MongoModuleStore): branch_setting_func: a function that returns the branch setting to use for this store's operations """ super(DraftModuleStore, self).__init__(*args, **kwargs) - self.branch_setting_func = kwargs.pop('branch_setting_func', lambda: BRANCH_PUBLISHED_ONLY) + self.branch_setting_func = kwargs.pop('branch_setting_func', lambda: ModuleStoreEnum.Branch.published_only) def get_item(self, usage_key, depth=0, revision=None): """ @@ -66,11 +62,11 @@ class DraftModuleStore(MongoModuleStore): get_children() to cache. None indicates to cache all descendants. revision: - REVISION_OPTION_PUBLISHED_ONLY - returns only the published item. - REVISION_OPTION_DRAFT_ONLY - returns only the draft item. + ModuleStoreEnum.RevisionOption.published_only - returns only the published item. + ModuleStoreEnum.RevisionOption.draft_only - returns only the draft item. None - uses the branch setting as follows: - if branch setting is BRANCH_PUBLISHED_ONLY, returns only the published item. - if branch setting is BRANCH_DRAFT_PREFERRED, returns either draft or published item, + if branch setting is ModuleStoreEnum.Branch.published_only, returns only the published item. + if branch setting is ModuleStoreEnum.Branch.draft_preferred, returns either draft or published item, preferring draft. Note: If the item is in DIRECT_ONLY_CATEGORIES, then returns only the PUBLISHED @@ -89,8 +85,8 @@ class DraftModuleStore(MongoModuleStore): def get_draft(): return wrap_draft(super(DraftModuleStore, self).get_item(as_draft(usage_key), depth=depth)) - # return the published version if REVISION_OPTION_PUBLISHED_ONLY is requested - if revision == REVISION_OPTION_PUBLISHED_ONLY: + # return the published version if ModuleStoreEnum.RevisionOption.published_only is requested + if revision == ModuleStoreEnum.RevisionOption.published_only: return get_published() # if the item is direct-only, there can only be a published version @@ -98,10 +94,10 @@ class DraftModuleStore(MongoModuleStore): return get_published() # return the draft version (without any fallback to PUBLISHED) if DRAFT-ONLY is requested - elif revision == REVISION_OPTION_DRAFT_ONLY: + elif revision == ModuleStoreEnum.RevisionOption.draft_only: return get_draft() - elif self.branch_setting_func() == BRANCH_PUBLISHED_ONLY: + elif self.branch_setting_func() == ModuleStoreEnum.Branch.published_only: return get_published() else: @@ -120,11 +116,11 @@ class DraftModuleStore(MongoModuleStore): Args: revision: - REVISION_OPTION_PUBLISHED_ONLY - checks for the published item only - REVISION_OPTION_DRAFT_ONLY - checks for the draft item only + ModuleStoreEnum.RevisionOption.published_only - checks for the published item only + ModuleStoreEnum.RevisionOption.draft_only - checks for the draft item only None - uses the branch setting, as follows: - if branch setting is BRANCH_PUBLISHED_ONLY, checks for the published item only - if branch setting is BRANCH_DRAFT_PREFERRED, checks whether draft or published item exists + if branch setting is ModuleStoreEnum.Branch.published_only, checks for the published item only + if branch setting is ModuleStoreEnum.Branch.draft_preferred, checks whether draft or published item exists """ def has_published(): return super(DraftModuleStore, self).has_item(usage_key) @@ -132,9 +128,10 @@ class DraftModuleStore(MongoModuleStore): def has_draft(): return super(DraftModuleStore, self).has_item(as_draft(usage_key)) - if revision == REVISION_OPTION_DRAFT_ONLY: + if revision == ModuleStoreEnum.RevisionOption.draft_only: return has_draft() - elif revision == REVISION_OPTION_PUBLISHED_ONLY or self.branch_setting_func() == BRANCH_PUBLISHED_ONLY: + elif revision == ModuleStoreEnum.RevisionOption.published_only \ + or self.branch_setting_func() == ModuleStoreEnum.Branch.published_only: return has_published() else: key = usage_key.to_deprecated_son(prefix='_id.') @@ -150,9 +147,9 @@ class DraftModuleStore(MongoModuleStore): Args: location (UsageKey): assumes the location's revision is None; so, uses revision keyword solely key_revision: - KEY_REVISION_DRAFT - return only the draft parent - KEY_REVISION_PUBLISHED - return only the published parent - REVISION_OPTION_ALL - return both draft and published parents + MongoRevisionKey.draft - return only the draft parent + MongoRevisionKey.published - return only the published parent + ModuleStoreEnum.RevisionOption.all - return both draft and published parents """ _verify_revision_is_published(location) @@ -168,8 +165,8 @@ class DraftModuleStore(MongoModuleStore): Location._from_deprecated_son(parent['_id'], location.course_key.run) for parent in parents if ( - # return all versions of the parent if revision is REVISION_OPTION_ALL - key_revision == REVISION_OPTION_ALL or + # return all versions of the parent if revision is ModuleStoreEnum.RevisionOption.all + key_revision == ModuleStoreEnum.RevisionOption.all or # return this parent if it's direct-only, regardless of which revision is requested parent['_id']['category'] in DIRECT_ONLY_CATEGORIES or # return this parent only if its revision matches the requested one @@ -186,9 +183,11 @@ class DraftModuleStore(MongoModuleStore): Args: revision: None - uses the branch setting for the revision - REVISION_OPTION_PUBLISHED_ONLY - return only the PUBLISHED parent if it exists, else returns None - REVISION_OPTION_DRAFT_PREFERRED - return either the DRAFT or PUBLISHED parent, preferring DRAFT, if parent(s) exists, - else returns None + ModuleStoreEnum.RevisionOption.published_only + - return only the PUBLISHED parent if it exists, else returns None + ModuleStoreEnum.RevisionOption.draft_preferred + - return either the DRAFT or PUBLISHED parent, preferring DRAFT, if parent(s) exists, + else returns None If the draft has a different parent than the published, it returns only the draft's parent. Because parents don't record their children's revisions, this @@ -197,9 +196,9 @@ class DraftModuleStore(MongoModuleStore): Only xml_exporter currently uses this argument. Others should avoid it. ''' if revision is None: - revision = REVISION_OPTION_PUBLISHED_ONLY \ - if self.branch_setting_func() == BRANCH_PUBLISHED_ONLY \ - else REVISION_OPTION_DRAFT_PREFERRED + revision = ModuleStoreEnum.RevisionOption.published_only \ + if self.branch_setting_func() == ModuleStoreEnum.Branch.published_only \ + else ModuleStoreEnum.RevisionOption.draft_preferred return super(DraftModuleStore, self).get_parent_location(location, revision, **kwargs) def create_xmodule(self, location, definition_data=None, metadata=None, runtime=None, fields={}): @@ -213,7 +212,7 @@ class DraftModuleStore(MongoModuleStore): :param runtime: if you already have an xmodule from the course, the xmodule.runtime value :param fields: a dictionary of field names and values for the new xmodule """ - self._verify_branch_setting(BRANCH_DRAFT_PREFERRED) + self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred) if location.category not in DIRECT_ONLY_CATEGORIES: location = as_draft(location) @@ -236,12 +235,12 @@ class DraftModuleStore(MongoModuleStore): settings: not used content: not used revision: - REVISION_OPTION_PUBLISHED_ONLY - returns only Published items - REVISION_OPTION_DRAFT_ONLY - returns only Draft items + ModuleStoreEnum.RevisionOption.published_only - returns only Published items + ModuleStoreEnum.RevisionOption.draft_only - returns only Draft items None - uses the branch setting, as follows: - if the branch setting is BRANCH_PUBLISHED_ONLY, + if the branch setting is ModuleStoreEnum.Branch.published_only, returns only Published items - if the branch setting is BRANCH_DRAFT_PREFERRED, + if the branch setting is ModuleStoreEnum.Branch.draft_preferred, returns either Draft or Published, preferring Draft items. kwargs (key=value): what to look for within the course. Common qualifiers are ``category`` or any field name. if the target field is a list, @@ -253,20 +252,21 @@ class DraftModuleStore(MongoModuleStore): return super(DraftModuleStore, self).get_items(course_key, key_revision=key_revision, **kwargs) def draft_items(): - return [wrap_draft(item) for item in base_get_items(KEY_REVISION_DRAFT)] + return [wrap_draft(item) for item in base_get_items(MongoRevisionKey.draft)] def published_items(draft_items): # filters out items that are not already in draft_items draft_items_locations = {item.location for item in draft_items} return [ item for item in - base_get_items(KEY_REVISION_PUBLISHED) + base_get_items(MongoRevisionKey.published) if item.location not in draft_items_locations ] - if revision == REVISION_OPTION_DRAFT_ONLY: + if revision == ModuleStoreEnum.RevisionOption.draft_only: return draft_items() - elif revision == REVISION_OPTION_PUBLISHED_ONLY or self.branch_setting_func() == BRANCH_PUBLISHED_ONLY: + elif revision == ModuleStoreEnum.RevisionOption.published_only \ + or self.branch_setting_func() == ModuleStoreEnum.Branch.published_only: return published_items([]) else: draft_items = draft_items() @@ -293,7 +293,7 @@ class DraftModuleStore(MongoModuleStore): Internal method with additional internal parameters to convert a subtree to draft. Args: - location: the location of the source (its revision must be KEY_REVISION_PUBLISHED) + location: the location of the source (its revision must be MongoRevisionKey.published) user_id: the ID of the user doing the operation delete_published (Boolean): intended for use by unpublish ignore_if_draft(Boolean): for internal use only as part of depth first change @@ -304,7 +304,7 @@ class DraftModuleStore(MongoModuleStore): DuplicateItemError: if the source or any of its descendants already has a draft copy """ # verify input conditions - self._verify_branch_setting(BRANCH_DRAFT_PREFERRED) + self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred) _verify_revision_is_published(location) # ensure we are not creating a DRAFT of an item that is direct-only @@ -322,7 +322,7 @@ class DraftModuleStore(MongoModuleStore): next_tier.append(child_loc.to_deprecated_son()) # insert a new DRAFT version of the item - item['_id']['revision'] = KEY_REVISION_DRAFT + item['_id']['revision'] = MongoRevisionKey.draft # ensure keys are in fixed and right order before inserting item['_id'] = self._id_dict_to_son(item['_id']) try: @@ -334,7 +334,7 @@ class DraftModuleStore(MongoModuleStore): # delete the old PUBLISHED version if requested if delete_published: - item['_id']['revision'] = KEY_REVISION_PUBLISHED + item['_id']['revision'] = MongoRevisionKey.published to_be_deleted.append(item['_id']) return next_tier @@ -352,7 +352,7 @@ class DraftModuleStore(MongoModuleStore): In addition to the superclass's behavior, this method converts the unit to draft if it's not direct-only and not already draft. """ - self._verify_branch_setting(BRANCH_DRAFT_PREFERRED) + self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred) # if the xblock is direct-only, update the PUBLISHED version if xblock.location.category in DIRECT_ONLY_CATEGORIES: @@ -380,7 +380,7 @@ class DraftModuleStore(MongoModuleStore): The method determines which revisions to delete. It disconnects and deletes the subtree. In general, it assumes deletes only occur on drafts except for direct_only. The only exceptions are internal calls like deleting orphans (during publishing as well as from delete_orphan view). - To indicate that all versions should be deleted, pass the keyword revision=REVISION_OPTION_ALL. + To indicate that all versions should be deleted, pass the keyword revision=ModuleStoreEnum.RevisionOption.all. * Deleting a DIRECT_ONLY_CATEGORIES block, deletes both draft and published children and removes from parent. * Deleting a specific version of block whose parent is of DIRECT_ONLY_CATEGORIES, only removes it from parent if @@ -392,32 +392,34 @@ class DraftModuleStore(MongoModuleStore): user_id: id of the user deleting the item revision: None - deletes the item and its subtree, and updates the parents per description above - REVISION_OPTION_PUBLISHED_ONLY - removes only Published versions - REVISION_OPTION_ALL - removes both Draft and Published parents + ModuleStoreEnum.RevisionOption.published_only - removes only Published versions + ModuleStoreEnum.RevisionOption.all - removes both Draft and Published parents currently only provided by contentstore.views.item.orphan_handler """ - self._verify_branch_setting(BRANCH_DRAFT_PREFERRED) + self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred) _verify_revision_is_published(location) is_item_direct_only = location.category in DIRECT_ONLY_CATEGORIES - if is_item_direct_only or revision == REVISION_OPTION_PUBLISHED_ONLY: - parent_revision = KEY_REVISION_PUBLISHED - elif revision == REVISION_OPTION_ALL: - parent_revision = REVISION_OPTION_ALL + if is_item_direct_only or revision == ModuleStoreEnum.RevisionOption.published_only: + parent_revision = MongoRevisionKey.published + elif revision == ModuleStoreEnum.RevisionOption.all: + parent_revision = ModuleStoreEnum.RevisionOption.all else: - parent_revision = KEY_REVISION_DRAFT + parent_revision = MongoRevisionKey.draft # remove subtree from its parent parent_locations = self._get_raw_parent_locations(location, key_revision=parent_revision) # there could be 2 parents if # Case 1: the draft item moved from one parent to another - # Case 2: revision==REVISION_OPTION_ALL and the single parent has 2 versions: draft and published + # Case 2: revision==ModuleStoreEnum.RevisionOption.all and the single parent has 2 versions: draft and published for parent_location in parent_locations: # don't remove from direct_only parent if other versions of this still exists if not is_item_direct_only and parent_location.category in DIRECT_ONLY_CATEGORIES: # see if other version of root exists alt_location = location.replace( - revision=KEY_REVISION_PUBLISHED if location.revision == KEY_REVISION_DRAFT else KEY_REVISION_DRAFT + revision=MongoRevisionKey.published + if location.revision == MongoRevisionKey.draft + else MongoRevisionKey.draft ) if super(DraftModuleStore, self).has_item(alt_location): continue @@ -427,9 +429,9 @@ class DraftModuleStore(MongoModuleStore): parent_block.location = parent_location # ensure the location is with the correct revision self.update_item(parent_block, user_id) - if is_item_direct_only or revision == REVISION_OPTION_ALL: + if is_item_direct_only or revision == ModuleStoreEnum.RevisionOption.all: as_functions = [as_draft, as_published] - elif revision == REVISION_OPTION_PUBLISHED_ONLY: + elif revision == ModuleStoreEnum.RevisionOption.published_only: as_functions = [as_published] else: as_functions = [as_draft] @@ -574,7 +576,7 @@ class DraftModuleStore(MongoModuleStore): to_be_deleted.append(as_draft(item_location).to_deprecated_son()) # verify input conditions - self._verify_branch_setting(BRANCH_DRAFT_PREFERRED) + self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred) _verify_revision_is_published(location) _internal_depth_first(location) @@ -589,7 +591,7 @@ class DraftModuleStore(MongoModuleStore): NOTE: unlike publish, this gives an error if called above the draftable level as it's intended to remove things from the published version """ - self._verify_branch_setting(BRANCH_DRAFT_PREFERRED) + self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred) return self._convert_to_draft(location, user_id, delete_published=True) def _query_children_for_cache_children(self, course_key, items): @@ -600,7 +602,7 @@ class DraftModuleStore(MongoModuleStore): for non_draft in to_process_non_drafts: to_process_dict[Location._from_deprecated_son(non_draft["_id"], course_key.run)] = non_draft - if self.branch_setting_func() == BRANCH_DRAFT_PREFERRED: + if self.branch_setting_func() == ModuleStoreEnum.Branch.draft_preferred: # now query all draft content in another round-trip query = [] for item in items: @@ -664,6 +666,6 @@ class DraftModuleStore(MongoModuleStore): def _verify_revision_is_published(location): """ - Asserts that the revision set on the given location is KEY_REVISION_PUBLISHED + Asserts that the revision set on the given location is MongoRevisionKey.published """ - assert location.revision == KEY_REVISION_PUBLISHED + assert location.revision == MongoRevisionKey.published diff --git a/common/lib/xmodule/xmodule/modulestore/split_migrator.py b/common/lib/xmodule/xmodule/modulestore/split_migrator.py index dddc8f2f2f..e1bdf4c5ab 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_migrator.py +++ b/common/lib/xmodule/xmodule/modulestore/split_migrator.py @@ -7,7 +7,7 @@ In general, it's strategy is to treat the other modulestores as read-only and to manipulate storage but use existing api's. ''' from xblock.fields import Reference, ReferenceList, ReferenceValueDict -from xmodule.modulestore import BRANCH_NAME_DRAFT, BRANCH_NAME_PUBLISHED, REVISION_OPTION_DRAFT_ONLY +from xmodule.modulestore import ModuleStoreEnum class SplitMigrator(object): @@ -85,7 +85,7 @@ class SplitMigrator(object): # after done w/ published items, add version for DRAFT pointing to the published structure index_info = self.split_modulestore.get_course_index_info(course_version_locator) versions = index_info['versions'] - versions[BRANCH_NAME_DRAFT] = versions[BRANCH_NAME_PUBLISHED] + versions[ModuleStoreEnum.BranchName.draft] = versions[ModuleStoreEnum.BranchName.published] 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 @@ -98,11 +98,11 @@ class SplitMigrator(object): """ # each true update below will trigger a new version of the structure. We may want to just have one new version # but that's for a later date. - new_draft_course_loc = published_course_key.for_branch(BRANCH_NAME_DRAFT) + new_draft_course_loc = published_course_key.for_branch(ModuleStoreEnum.BranchName.draft) # to prevent race conditions of grandchilden being added before their parents and thus having no parent to # add to awaiting_adoption = {} - for module in self.draft_modulestore.get_items(course_key, revision=REVISION_OPTION_DRAFT_ONLY): + for module in self.draft_modulestore.get_items(course_key, revision=ModuleStoreEnum.RevisionOption.draft_only): new_locator = self.loc_mapper.translate_location( module.location, False, add_entry_if_missing=True ) diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py index 90d0481a39..1c212a2985 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py @@ -61,7 +61,7 @@ from opaque_keys.edx.locator import ( from xmodule.modulestore.exceptions import InsufficientSpecificationError, VersionConflictError, DuplicateItemError, \ DuplicateCourseError from xmodule.modulestore import ( - inheritance, ModuleStoreWriteBase, SPLIT_MONGO_MODULESTORE_TYPE, BRANCH_NAME_DRAFT, BRANCH_NAME_PUBLISHED + inheritance, ModuleStoreWriteBase, ModuleStoreEnum ) from ..exceptions import ItemNotFoundError @@ -296,7 +296,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): } return envelope - def get_courses(self, branch=BRANCH_NAME_DRAFT, qualifiers=None): + def get_courses(self, branch=ModuleStoreEnum.BranchName.draft, qualifiers=None): ''' Returns a list of course descriptors matching any given qualifiers. @@ -304,9 +304,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): legal query for mongo to use against the active_versions collection. Note, this is to find the current head of the named branch type - (e.g., BRANCH_NAME_DRAFT). To get specific versions via guid use get_course. + (e.g., ModuleStoreEnum.BranchName.draft). To get specific versions via guid use get_course. - :param branch: the branch for which to return courses. Default value is BRANCH_NAME_DRAFT. + :param branch: the branch for which to return courses. Default value is ModuleStoreEnum.BranchName.draft. :param qualifiers: an optional dict restricting which elements should match ''' if qualifiers is None: @@ -385,9 +385,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): :param usage_key: the block to check :return: True if the draft and published versions differ """ - draft = self.get_item(usage_key.for_branch(BRANCH_NAME_DRAFT)) + draft = self.get_item(usage_key.for_branch(ModuleStoreEnum.BranchName.draft)) try: - published = self.get_item(usage_key.for_branch(BRANCH_NAME_PUBLISHED)) + published = self.get_item(usage_key.for_branch(ModuleStoreEnum.BranchName.published)) except ItemNotFoundError: return True @@ -872,7 +872,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): def create_course( self, org, offering, user_id, fields=None, - master_branch=BRANCH_NAME_DRAFT, versions_dict=None, root_category='course', + master_branch=ModuleStoreEnum.BranchName.draft, versions_dict=None, root_category='course', root_block_id='course', **kwargs ): """ @@ -908,7 +908,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): master_branch: the tag (key) for the version name in the dict which is the DRAFT version. Not the actual version guid, but what to call it. - versions_dict: the starting version ids where the keys are the tags such as DRAFT and REVISION_OPTION_PUBLISHED_ONLY + versions_dict: the starting version ids where the keys are the tags such as DRAFT and PUBLISHED and the values are structure guids. If provided, the new course will reuse this version (unless you also provide any fields overrides, see above). if not provided, will create a mostly empty course structure with just a category course root xblock. @@ -1298,7 +1298,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): self._update_head(index_entry, destination_course.branch, destination_structure['_id']) def unpublish(self, location, user_id): - published_location = location.replace(branch=REVISION_OPTION_PUBLISHED_ONLY) + published_location = location.replace(branch=ModuleStoreEnum.RevisionOption.published_only) self.delete_item(published_location, user_id) def update_course_index(self, updated_index_entry): @@ -1465,7 +1465,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): Args: course_key: just for signature compatibility """ - return SPLIT_MONGO_MODULESTORE_TYPE + return ModuleStoreEnum.Type.split def internal_clean_children(self, course_locator): """ @@ -1797,7 +1797,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): """ Check that the db is reachable. """ - return {SPLIT_MONGO_MODULESTORE_TYPE: self.db_connection.heartbeat()} + return {ModuleStoreEnum.Type.split: self.db_connection.heartbeat()} def compute_publish_state(self, xblock): """ diff --git a/common/lib/xmodule/xmodule/modulestore/store_utilities.py b/common/lib/xmodule/xmodule/modulestore/store_utilities.py index f1ca3065b6..dd48f1d60c 100644 --- a/common/lib/xmodule/xmodule/modulestore/store_utilities.py +++ b/common/lib/xmodule/xmodule/modulestore/store_utilities.py @@ -2,7 +2,7 @@ import re import logging from xmodule.contentstore.content import StaticContent -from xmodule.modulestore import REVISION_OPTION_PUBLISHED_ONLY, REVISION_OPTION_DRAFT_ONLY +from xmodule.modulestore import ModuleStoreEnum def _prefix_only_url_replace_regex(prefix): @@ -136,12 +136,12 @@ def clone_course(modulestore, contentstore, source_course_id, dest_course_id, us raise Exception("Cannot find a course at {0}. Aborting".format(source_course_id)) # Get all modules under this namespace which is (tag, org, course) tuple - modules = modulestore.get_items(source_course_id, revision=REVISION_OPTION_PUBLISHED_ONLY) + modules = modulestore.get_items(source_course_id, revision=ModuleStoreEnum.RevisionOption.published_only) _clone_modules(modulestore, modules, source_course_id, dest_course_id, user_id) course_location = dest_course_id.make_usage_key('course', dest_course_id.run) modulestore.publish(course_location, user_id) - modules = modulestore.get_items(source_course_id, revision=REVISION_OPTION_DRAFT_ONLY) + modules = modulestore.get_items(source_course_id, revision=ModuleStoreEnum.RevisionOption.draft_only) _clone_modules(modulestore, modules, source_course_id, dest_course_id, user_id) # now iterate through all of the assets and clone them diff --git a/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py b/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py index 7ef9fb9080..3acbb29249 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py @@ -6,7 +6,7 @@ from uuid import uuid4 from django.test import TestCase from xmodule.modulestore.django import ( modulestore, clear_existing_modulestores, loc_mapper) -from xmodule.modulestore import MONGO_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.contentstore.django import contentstore @@ -142,7 +142,7 @@ class ModuleStoreTestCase(TestCase): return updated_course @staticmethod - def drop_mongo_collections(modulestore_type=MONGO_MODULESTORE_TYPE): + def drop_mongo_collections(modulestore_type=ModuleStoreEnum.Type.mongo): """ If using a Mongo-backed modulestore & contentstore, drop the collections. """ diff --git a/common/lib/xmodule/xmodule/modulestore/tests/persistent_factories.py b/common/lib/xmodule/xmodule/modulestore/tests/persistent_factories.py index f86de2b211..e7490e1442 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/persistent_factories.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/persistent_factories.py @@ -1,4 +1,4 @@ -from xmodule.modulestore import SPLIT_MONGO_MODULESTORE_TYPE, BRANCH_NAME_DRAFT +from xmodule.modulestore import ModuleStoreEnum from xmodule.course_module import CourseDescriptor from xmodule.x_module import XModuleDescriptor import factory @@ -15,7 +15,7 @@ class SplitFactory(factory.Factory): # Delayed import so that we only depend on django if the caller # hasn't provided their own modulestore from xmodule.modulestore.django import modulestore - return modulestore()._get_modulestore_by_type(SPLIT_MONGO_MODULESTORE_TYPE) + return modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.split) class PersistentCourseFactory(SplitFactory): @@ -25,7 +25,7 @@ class PersistentCourseFactory(SplitFactory): keywords: any xblock field plus (note, the below are filtered out; so, if they become legitimate xblock fields, they won't be settable via this factory) * org: defaults to textX - * master_branch: (optional) defaults to BRANCH_NAME_DRAFT + * master_branch: (optional) defaults to ModuleStoreEnum.BranchName.draft * user_id: (optional) defaults to 'test_user' * display_name (xblock field): will default to 'Robot Super Course' unless provided """ @@ -34,7 +34,7 @@ class PersistentCourseFactory(SplitFactory): # pylint: disable=W0613 @classmethod def _create(cls, target_class, offering='999', org='testX', user_id='test_user', - master_branch=BRANCH_NAME_DRAFT, **kwargs): + master_branch=ModuleStoreEnum.BranchName.draft, **kwargs): modulestore = kwargs.pop('modulestore') root_block_id = kwargs.pop('root_block_id', 'course') diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_location_mapper.py b/common/lib/xmodule/xmodule/modulestore/tests/test_location_mapper.py index b34d0ccbe0..591f9c892b 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_location_mapper.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_location_mapper.py @@ -5,7 +5,8 @@ import unittest import uuid from opaque_keys.edx.locations import Location from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator -from xmodule.modulestore import BRANCH_NAME_PUBLISHED, BRANCH_NAME_DRAFT, KEY_REVISION_PUBLISHED +from xmodule.modulestore import ModuleStoreEnum +from xmodule.modulestore.mongo.base import MongoRevisionKey from xmodule.modulestore.exceptions import ItemNotFoundError, InvalidLocationError from xmodule.modulestore.loc_mapper_store import LocMapperStore from mock import Mock @@ -63,8 +64,8 @@ class TestLocationMapper(LocMapperSetupSansDjango): self.assertIsNotNone(entry, "Didn't find entry") self.assertEqual(entry['org'], org) self.assertEqual(entry['offering'], '{}.{}'.format(course1, run)) - self.assertEqual(entry['draft_branch'], BRANCH_NAME_DRAFT) - self.assertEqual(entry['prod_branch'], BRANCH_NAME_PUBLISHED) + self.assertEqual(entry['draft_branch'], ModuleStoreEnum.BranchName.draft) + self.assertEqual(entry['prod_branch'], ModuleStoreEnum.BranchName.published) self.assertEqual(entry['block_map'], {}) course2 = 'quux_course' @@ -124,7 +125,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): """ prob_locator = loc_mapper().translate_location( location, - published=(branch == BRANCH_NAME_PUBLISHED), + published=(branch == ModuleStoreEnum.BranchName.published), add_entry_if_missing=add_entry ) self.assertEqual(prob_locator.org, org) @@ -134,7 +135,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): course_locator = loc_mapper().translate_location_to_course_locator( location.course_key, - published=(branch == BRANCH_NAME_PUBLISHED), + published=(branch == ModuleStoreEnum.BranchName.published), ) self.assertEqual(course_locator.org, org) self.assertEqual(course_locator.offering, offering) @@ -169,7 +170,8 @@ class TestLocationMapper(LocMapperSetupSansDjango): ) test_problem_locn = Location(org, course, run, 'problem', 'abc123') - self.translate_n_check(test_problem_locn, new_style_org, new_style_offering, 'problem2', BRANCH_NAME_PUBLISHED) + self.translate_n_check(test_problem_locn, new_style_org, new_style_offering, 'problem2', + ModuleStoreEnum.BranchName.published) # look for non-existent problem with self.assertRaises(ItemNotFoundError): loc_mapper().translate_location( @@ -184,7 +186,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): test_no_cat_locn = test_no_cat_locn.replace(name='def456') self.translate_n_check( - test_no_cat_locn, new_style_org, new_style_offering, 'problem4', BRANCH_NAME_PUBLISHED + test_no_cat_locn, new_style_org, new_style_offering, 'problem4', ModuleStoreEnum.BranchName.published ) # add a distractor course (note that abc123 has a different translation in this one) @@ -203,12 +205,12 @@ class TestLocationMapper(LocMapperSetupSansDjango): ) # test that old translation still works self.translate_n_check( - test_problem_locn, new_style_org, new_style_offering, 'problem2', BRANCH_NAME_PUBLISHED + test_problem_locn, new_style_org, new_style_offering, 'problem2', ModuleStoreEnum.BranchName.published ) # and new returns new id self.translate_n_check( test_problem_locn.replace(run=run), test_delta_new_org, test_delta_new_offering, - 'problem3', BRANCH_NAME_PUBLISHED + 'problem3', ModuleStoreEnum.BranchName.published ) def test_translate_location_dwim(self): @@ -222,11 +224,11 @@ class TestLocationMapper(LocMapperSetupSansDjango): problem_name = 'abc123abc123abc123abc123abc123f9' location = Location(org, course, run, 'problem', problem_name) new_offering = '{}.{}'.format(course, run) - self.translate_n_check(location, org, new_offering, 'problemabc', BRANCH_NAME_PUBLISHED, True) + self.translate_n_check(location, org, new_offering, 'problemabc', ModuleStoreEnum.BranchName.published, True) # create an entry w/o a guid name other_location = Location(org, course, run, 'chapter', 'intro') - self.translate_n_check(other_location, org, new_offering, 'intro', BRANCH_NAME_PUBLISHED, True) + self.translate_n_check(other_location, org, new_offering, 'intro', ModuleStoreEnum.BranchName.published, True) # add a distractor course delta_new_org = '{}.geek_dept'.format(org) @@ -238,7 +240,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): delta_new_org, delta_new_offering, block_map={problem_name: {'problem': 'problem3'}} ) - self.translate_n_check(location, org, new_offering, 'problemabc', BRANCH_NAME_PUBLISHED, True) + self.translate_n_check(location, org, new_offering, 'problemabc', ModuleStoreEnum.BranchName.published, True) # add a new one to both courses (ensure name doesn't have same beginning) new_prob_name = uuid.uuid4().hex @@ -246,10 +248,10 @@ class TestLocationMapper(LocMapperSetupSansDjango): new_prob_name = uuid.uuid4().hex new_prob_locn = location.replace(name=new_prob_name) new_usage_id = 'problem{}'.format(new_prob_name[:3]) - self.translate_n_check(new_prob_locn, org, new_offering, new_usage_id, BRANCH_NAME_PUBLISHED, True) + self.translate_n_check(new_prob_locn, org, new_offering, new_usage_id, ModuleStoreEnum.BranchName.published, True) new_prob_locn = new_prob_locn.replace(run=run) self.translate_n_check( - new_prob_locn, delta_new_org, delta_new_offering, new_usage_id, BRANCH_NAME_PUBLISHED, True + new_prob_locn, delta_new_org, delta_new_offering, new_usage_id, ModuleStoreEnum.BranchName.published, True ) def test_translate_locator(self): @@ -264,7 +266,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): new_style_offering = '{}.{}'.format(course, run) prob_course_key = CourseLocator( org=new_style_org, offering=new_style_offering, - branch=BRANCH_NAME_PUBLISHED, + branch=ModuleStoreEnum.BranchName.published, ) prob_locator = BlockUsageLocator( prob_course_key, @@ -286,22 +288,22 @@ class TestLocationMapper(LocMapperSetupSansDjango): # only one course matches prob_location = loc_mapper().translate_locator_to_location(prob_locator) # default branch - self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', KEY_REVISION_PUBLISHED)) + self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', MongoRevisionKey.published)) # test get_course keyword prob_location = loc_mapper().translate_locator_to_location(prob_locator, get_course=True) self.assertEqual(prob_location, SlashSeparatedCourseKey(org, course, run)) # explicit branch - prob_locator = prob_locator.for_branch(BRANCH_NAME_DRAFT) + prob_locator = prob_locator.for_branch(ModuleStoreEnum.BranchName.draft) prob_location = loc_mapper().translate_locator_to_location(prob_locator) - # Even though the problem was set as draft, we always return revision= KEY_REVISION_PUBLISHED to work + # Even though the problem was set as draft, we always return revision= MongoRevisionKey.published to work # with old mongo/draft modulestores. - self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', KEY_REVISION_PUBLISHED)) + self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', MongoRevisionKey.published)) prob_locator = BlockUsageLocator( prob_course_key.for_branch('production'), block_type='problem', block_id='problem2' ) prob_location = loc_mapper().translate_locator_to_location(prob_locator) - self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', KEY_REVISION_PUBLISHED)) + self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', MongoRevisionKey.published)) # same for chapter except chapter cannot be draft in old system chap_locator = BlockUsageLocator( prob_course_key.for_branch('production'), @@ -310,7 +312,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): chap_location = loc_mapper().translate_locator_to_location(chap_locator) self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234')) # explicit branch - chap_locator = chap_locator.for_branch(BRANCH_NAME_DRAFT) + chap_locator = chap_locator.for_branch(ModuleStoreEnum.BranchName.draft) chap_location = loc_mapper().translate_locator_to_location(chap_locator) self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234')) chap_locator = BlockUsageLocator( @@ -321,7 +323,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): # look for non-existent problem prob_locator2 = BlockUsageLocator( - prob_course_key.for_branch(BRANCH_NAME_DRAFT), + prob_course_key.for_branch(ModuleStoreEnum.BranchName.draft), block_type='problem', block_id='problem3' ) prob_location = loc_mapper().translate_locator_to_location(prob_locator2) @@ -336,7 +338,7 @@ class TestLocationMapper(LocMapperSetupSansDjango): block_map={'abc123': {'problem': 'problem3'}} ) prob_location = loc_mapper().translate_locator_to_location(prob_locator) - self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', KEY_REVISION_PUBLISHED)) + self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', MongoRevisionKey.published)) def test_special_chars(self): """ diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py b/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py index af333db3dd..6266793651 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py @@ -7,10 +7,7 @@ from collections import namedtuple from xmodule.tests import DATA_DIR from opaque_keys.edx.locations import Location -from xmodule.modulestore import ( - MONGO_MODULESTORE_TYPE, SPLIT_MONGO_MODULESTORE_TYPE, XML_MODULESTORE_TYPE, - REVISION_OPTION_DRAFT_PREFERRED, REVISION_OPTION_PUBLISHED_ONLY, BRANCH_DRAFT_PREFERRED -) +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.exceptions import ItemNotFoundError from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator @@ -247,12 +244,12 @@ class TestMixedModuleStore(LocMapperSetupSansDjango): """ self.initdb(default_ms) self.assertEqual(self.store.get_modulestore_type( - self._course_key_from_string(self.XML_COURSEID1)), XML_MODULESTORE_TYPE + self._course_key_from_string(self.XML_COURSEID1)), ModuleStoreEnum.Type.xml ) self.assertEqual(self.store.get_modulestore_type( - self._course_key_from_string(self.XML_COURSEID2)), XML_MODULESTORE_TYPE + self._course_key_from_string(self.XML_COURSEID2)), ModuleStoreEnum.Type.xml ) - mongo_ms_type = MONGO_MODULESTORE_TYPE if default_ms == 'draft' else SPLIT_MONGO_MODULESTORE_TYPE + mongo_ms_type = ModuleStoreEnum.Type.mongo if default_ms == 'draft' else ModuleStoreEnum.Type.split self.assertEqual(self.store.get_modulestore_type( self._course_key_from_string(self.MONGO_COURSEID)), mongo_ms_type ) @@ -352,7 +349,7 @@ class TestMixedModuleStore(LocMapperSetupSansDjango): Test that the xml modulestore only loaded the courses from the maps. """ self.initdb('draft') - xml_store = self.store._get_modulestore_by_type(XML_MODULESTORE_TYPE) + xml_store = self.store._get_modulestore_by_type(ModuleStoreEnum.Type.xml) courses = xml_store.get_courses() self.assertEqual(len(courses), 2) course_ids = [course.id for course in courses] @@ -366,7 +363,7 @@ class TestMixedModuleStore(LocMapperSetupSansDjango): Test that the xml modulestore doesn't allow write ops. """ self.initdb('draft') - xml_store = self.store._get_modulestore_by_type(XML_MODULESTORE_TYPE) + xml_store = self.store._get_modulestore_by_type(ModuleStoreEnum.Type.xml) # the important thing is not which exception it raises but that it raises an exception with self.assertRaises(AttributeError): xml_store.create_course("org", "course/run", 999) @@ -420,16 +417,16 @@ class TestMixedModuleStore(LocMapperSetupSansDjango): self.verify_get_parent_locations_results([ (child_to_move, new_parent, None), - (child_to_move, new_parent, REVISION_OPTION_DRAFT_PREFERRED), - (child_to_move, old_parent, REVISION_OPTION_PUBLISHED_ONLY), + (child_to_move, new_parent, ModuleStoreEnum.RevisionOption.draft_preferred), + (child_to_move, old_parent, ModuleStoreEnum.RevisionOption.published_only), ]) # publish the course again self.store.publish(self.course.location, self.user_id) self.verify_get_parent_locations_results([ (child_to_move, new_parent, None), - (child_to_move, new_parent, REVISION_OPTION_DRAFT_PREFERRED), - (child_to_move, new_parent, REVISION_OPTION_PUBLISHED_ONLY), + (child_to_move, new_parent, ModuleStoreEnum.RevisionOption.draft_preferred), + (child_to_move, new_parent, ModuleStoreEnum.RevisionOption.published_only), ]) @ddt.data('draft') @@ -451,16 +448,16 @@ class TestMixedModuleStore(LocMapperSetupSansDjango): self.verify_get_parent_locations_results([ (child_to_delete, old_parent, None), # Note: The following could be an unexpected result, but we want to avoid an extra database call - (child_to_delete, old_parent, REVISION_OPTION_DRAFT_PREFERRED), - (child_to_delete, old_parent, REVISION_OPTION_PUBLISHED_ONLY), + (child_to_delete, old_parent, ModuleStoreEnum.RevisionOption.draft_preferred), + (child_to_delete, old_parent, ModuleStoreEnum.RevisionOption.published_only), ]) # publish the course again self.store.publish(self.course.location, self.user_id) self.verify_get_parent_locations_results([ (child_to_delete, None, None), - (child_to_delete, None, REVISION_OPTION_DRAFT_PREFERRED), - (child_to_delete, None, REVISION_OPTION_PUBLISHED_ONLY), + (child_to_delete, None, ModuleStoreEnum.RevisionOption.draft_preferred), + (child_to_delete, None, ModuleStoreEnum.RevisionOption.published_only), ]) @ddt.data('draft', 'split') @@ -529,6 +526,6 @@ def create_modulestore_instance(engine, doc_store_config, options, i18n_service= return class_( doc_store_config=doc_store_config, - branch_setting_func=lambda: BRANCH_DRAFT_PREFERRED, + branch_setting_func=lambda: ModuleStoreEnum.Branch.draft_preferred, **options ) diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py index ed8ba612b3..313740fd98 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py @@ -20,7 +20,7 @@ from xblock.plugin import Plugin from xmodule.tests import DATA_DIR from opaque_keys.edx.locations import Location -from xmodule.modulestore import MONGO_MODULESTORE_TYPE, BRANCH_DRAFT_PREFERRED +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.mongo import MongoModuleStore, MongoKeyValueStore from xmodule.modulestore.draft import DraftModuleStore from opaque_keys.edx.locations import SlashSeparatedCourseKey, AssetLocation @@ -103,7 +103,7 @@ class TestMongoModuleStore(unittest.TestCase): draft_store = DraftModuleStore( doc_store_config, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS, - branch_setting_func=lambda: BRANCH_DRAFT_PREFERRED + branch_setting_func=lambda: ModuleStoreEnum.Branch.draft_preferred ) import_from_xml( draft_store, @@ -148,7 +148,7 @@ class TestMongoModuleStore(unittest.TestCase): {'host': HOST, 'db': DB, 'collection': COLLECTION}, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS ) - assert_equals(store.get_modulestore_type(''), MONGO_MODULESTORE_TYPE) + assert_equals(store.get_modulestore_type(''), ModuleStoreEnum.Type.mongo) def test_get_courses(self): '''Make sure the course objects loaded properly''' diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_split_migrator.py b/common/lib/xmodule/xmodule/modulestore/tests/test_split_migrator.py index 3144eb6aab..c842b657a7 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_split_migrator.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_split_migrator.py @@ -5,7 +5,7 @@ Tests for split_migrator import uuid import random import mock -from xmodule.modulestore import KEY_REVISION_PUBLISHED +from xmodule.modulestore.mongo.base import MongoRevisionKey from xmodule.modulestore.loc_mapper_store import LocMapperStore from xmodule.modulestore.split_migrator import SplitMigrator from xmodule.modulestore.tests import test_location_mapper @@ -178,7 +178,7 @@ class TestMigration(SplitWMongoCourseBoostrapper): self.assertEqual( presplit_dag_root.location, self.loc_mapper.translate_locator_to_location(split_dag_root.location).replace( - revision=KEY_REVISION_PUBLISHED + revision=MongoRevisionKey.published ) ) # compare all fields but children diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py b/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py index fe69b4c39c..64af2b81ef 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py @@ -11,7 +11,7 @@ import random from xblock.fields import Scope from xmodule.course_module import CourseDescriptor -from xmodule.modulestore import BRANCH_NAME_PUBLISHED, BRANCH_NAME_DRAFT +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.exceptions import (InsufficientSpecificationError, ItemNotFoundError, VersionConflictError, DuplicateItemError, DuplicateCourseError) from opaque_keys.edx.locator import CourseLocator, BlockUsageLocator, VersionTree, LocalId @@ -22,6 +22,10 @@ from xmodule.modulestore.split_mongo.split import SplitMongoModuleStore from xmodule.modulestore.tests.test_modulestore import check_has_course_method +BRANCH_NAME_DRAFT = ModuleStoreEnum.BranchName.draft +BRANCH_NAME_PUBLISHED = ModuleStoreEnum.BranchName.published + + class SplitModuleTest(unittest.TestCase): ''' The base set of tests manually populates a db w/ courses which have diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_split_w_old_mongo.py b/common/lib/xmodule/xmodule/modulestore/tests/test_split_w_old_mongo.py index 5101123ab3..e7c4d3fb59 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_split_w_old_mongo.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_split_w_old_mongo.py @@ -9,7 +9,7 @@ from opaque_keys.edx.locator import CourseLocator, BlockUsageLocator from xmodule.modulestore.split_mongo.split import SplitMongoModuleStore from xmodule.modulestore.mongo import MongoModuleStore, DraftMongoModuleStore from xmodule.modulestore.mongo.draft import DIRECT_ONLY_CATEGORIES -from xmodule.modulestore import BRANCH_DRAFT_PREFERRED, BRANCH_NAME_DRAFT +from xmodule.modulestore import ModuleStoreEnum from mock import Mock @@ -41,7 +41,7 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase): 'xblock_mixins': (InheritanceMixin,) } - split_course_key = CourseLocator('test_org', 'test_course.runid', branch=BRANCH_NAME_DRAFT) + split_course_key = CourseLocator('test_org', 'test_course.runid', branch=ModuleStoreEnum.BranchName.draft) def setUp(self): self.db_config['collection'] = 'modulestore{0}'.format(uuid.uuid4().hex[:5]) @@ -56,7 +56,7 @@ class SplitWMongoCourseBoostrapper(unittest.TestCase): self.addCleanup(self.tear_down_split) self.old_mongo = MongoModuleStore(self.db_config, **self.modulestore_options) self.draft_mongo = DraftMongoModuleStore( - self.db_config, branch_setting_func=lambda: BRANCH_DRAFT_PREFERRED, **self.modulestore_options + self.db_config, branch_setting_func=lambda: ModuleStoreEnum.Branch.draft_preferred, **self.modulestore_options ) self.addCleanup(self.tear_down_mongo) self.old_course_key = None diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py b/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py index 4a8cf57061..9aa7041be6 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py @@ -9,7 +9,7 @@ from mock import patch from xmodule.modulestore.xml import XMLModuleStore from opaque_keys.edx.locations import Location -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from .test_modulestore import check_path_to_location from xmodule.tests import DATA_DIR @@ -43,7 +43,7 @@ class TestXMLModuleStore(unittest.TestCase): def test_xml_modulestore_type(self): store = XMLModuleStore(DATA_DIR, course_dirs=['toy', 'simple']) - self.assertEqual(store.get_modulestore_type(), XML_MODULESTORE_TYPE) + self.assertEqual(store.get_modulestore_type(), ModuleStoreEnum.Type.xml) def test_unicode_chars_in_xml_content(self): # edX/full/6.002_Spring_2012 has non-ASCII chars, and during diff --git a/common/lib/xmodule/xmodule/modulestore/xml.py b/common/lib/xmodule/xmodule/modulestore/xml.py index c50cdeb991..e3c58f531f 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml.py +++ b/common/lib/xmodule/xmodule/modulestore/xml.py @@ -19,7 +19,7 @@ from xmodule.errortracker import make_error_tracker, exc_info_to_str from xmodule.mako_module import MakoDescriptorSystem from xmodule.x_module import XMLParsingSystem, policy_key from xmodule.modulestore.xml_exporter import DEFAULT_CONTENT_FIELDS -from xmodule.modulestore import REVISION_OPTION_PUBLISHED_ONLY +from xmodule.modulestore import ModuleStoreEnum from xmodule.tabs import CourseTabList from opaque_keys.edx.keys import UsageKey from opaque_keys.edx.locations import SlashSeparatedCourseKey @@ -27,7 +27,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey from xblock.field_data import DictFieldData from xblock.runtime import DictKeyValueStore, IdGenerator -from . import ModuleStoreReadBase, Location, XML_MODULESTORE_TYPE +from . import ModuleStoreReadBase, Location, ModuleStoreEnum from .exceptions import ItemNotFoundError from .inheritance import compute_inherited_metadata, inheriting_field_data @@ -411,7 +411,7 @@ class XMLModuleStore(ModuleStoreReadBase): self.i18n_service = i18n_service # The XML Module Store is a read-only store and only handles published content - self.branch_setting_func = lambda: REVISION_OPTION_PUBLISHED_ONLY + self.branch_setting_func = lambda: ModuleStoreEnum.RevisionOption.published_only # If we are specifically asked for missing courses, that should # be an error. If we are asked for "all" courses, find the ones @@ -800,16 +800,11 @@ class XMLModuleStore(ModuleStoreReadBase): def get_modulestore_type(self, course_key=None): """ - Returns an enumeration-like type reflecting the type of this modulestore - The return can be one of: - "xml" (for XML based courses), - "mongo" for old-style MongoDB backed courses, - "split" for new-style split MongoDB backed courses. - + Returns an enumeration-like type reflecting the type of this modulestore, per ModuleStoreEnum.Type Args: course_key: just for signature compatibility """ - return XML_MODULESTORE_TYPE + return ModuleStoreEnum.Type.xml def get_courses_for_wiki(self, wiki_slug): """ @@ -827,5 +822,5 @@ class XMLModuleStore(ModuleStoreReadBase): Returns the course count """ - return {XML_MODULESTORE_TYPE: True} + return {ModuleStoreEnum.Type.xml: True} diff --git a/common/lib/xmodule/xmodule/modulestore/xml_exporter.py b/common/lib/xmodule/xmodule/modulestore/xml_exporter.py index 0b9dd261b9..063364236e 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml_exporter.py +++ b/common/lib/xmodule/xmodule/modulestore/xml_exporter.py @@ -7,9 +7,7 @@ import lxml.etree from xblock.fields import Scope from xmodule.contentstore.content import StaticContent from xmodule.exceptions import NotFoundError -from xmodule.modulestore import ( - EdxJSONEncoder, BRANCH_PUBLISHED_ONLY, REVISION_OPTION_DRAFT_PREFERRED, REVISION_OPTION_DRAFT_ONLY -) +from xmodule.modulestore import EdxJSONEncoder, ModuleStoreEnum from xmodule.modulestore.inheritance import own_metadata from xmodule.modulestore.mixed import store_branch_setting from fs.osfs import OSFS @@ -47,7 +45,7 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir): root = lxml.etree.Element('unknown') # export only the published content - with store_branch_setting(course.runtime.modulestore, BRANCH_PUBLISHED_ONLY): + with store_branch_setting(course.runtime.modulestore, ModuleStoreEnum.Branch.published_only): course.add_xml_to_node(root) with export_fs.open('course.xml', 'w') as course_xml: @@ -107,11 +105,18 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir): # should we change the application, then this assumption will no longer be valid # NOTE: we need to explicitly implement the logic for setting the vertical's parent # and index here since the XML modulestore cannot load draft modules - draft_verticals = modulestore.get_items(course_key, category='vertical', revision=REVISION_OPTION_DRAFT_ONLY) + draft_verticals = modulestore.get_items( + course_key, + category='vertical', + revision=ModuleStoreEnum.RevisionOption.draft_only + ) if len(draft_verticals) > 0: draft_course_dir = export_fs.makeopendir(DRAFT_DIR) for draft_vertical in draft_verticals: - parent_loc = modulestore.get_parent_location(draft_vertical.location, revision=REVISION_OPTION_DRAFT_PREFERRED) + parent_loc = modulestore.get_parent_location( + draft_vertical.location, + revision=ModuleStoreEnum.RevisionOption.draft_preferred + ) # Don't try to export orphaned items. if parent_loc is not None: logging.debug('parent_loc = {0}'.format(parent_loc)) diff --git a/common/lib/xmodule/xmodule/modulestore/xml_importer.py b/common/lib/xmodule/xmodule/modulestore/xml_importer.py index 66e580424c..48964e1666 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml_importer.py +++ b/common/lib/xmodule/xmodule/modulestore/xml_importer.py @@ -17,7 +17,7 @@ from .store_utilities import rewrite_nonportable_content_links import xblock from xmodule.tabs import CourseTabList from xmodule.modulestore.exceptions import InvalidLocationError -from xmodule.modulestore import KEY_REVISION_PUBLISHED, KEY_REVISION_DRAFT +from xmodule.modulestore.mongo.base import MongoRevisionKey log = logging.getLogger(__name__) @@ -505,14 +505,14 @@ def _import_course_draft( # Update the module's location to DRAFT revision # We need to call this method (instead of updating the location directly) # to ensure that pure XBlock field data is updated correctly. - _update_module_location(module, module.location.replace(revision=KEY_REVISION_DRAFT)) + _update_module_location(module, module.location.replace(revision=MongoRevisionKey.draft)) # make sure our parent has us in its list of children # this is to make sure private only verticals show up # in the list of children since they would have been # filtered out from the non-draft store export if module.location.category == 'vertical': - non_draft_location = module.location.replace(revision=KEY_REVISION_PUBLISHED) + non_draft_location = module.location.replace(revision=MongoRevisionKey.published) sequential_url = module.xml_attributes['parent_sequential_url'] index = int(module.xml_attributes['index_in_children_list']) diff --git a/lms/djangoapps/bulk_email/forms.py b/lms/djangoapps/bulk_email/forms.py index cef116d6f4..be0771d814 100644 --- a/lms/djangoapps/bulk_email/forms.py +++ b/lms/djangoapps/bulk_email/forms.py @@ -9,7 +9,7 @@ from django.core.exceptions import ValidationError from bulk_email.models import CourseEmailTemplate, COURSE_EMAIL_MESSAGE_BODY_TAG, CourseAuthorization from opaque_keys import InvalidKeyError -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey @@ -78,7 +78,7 @@ class CourseAuthorizationAdminForm(forms.ModelForm): # pylint: disable=R0924 raise forms.ValidationError(msg) # Now, try and discern if it is a Studio course - HTML editor doesn't work with XML courses - is_studio_course = modulestore().get_modulestore_type(course_key) != XML_MODULESTORE_TYPE + is_studio_course = modulestore().get_modulestore_type(course_key) != ModuleStoreEnum.Type.xml if not is_studio_course: msg = "Course Email feature is only available for courses authored in Studio. " msg += '"{0}" appears to be an XML backed course.'.format(course_key.to_deprecated_string()) diff --git a/lms/djangoapps/bulk_email/tests/test_forms.py b/lms/djangoapps/bulk_email/tests/test_forms.py index 4d75715ce8..22e2d93630 100644 --- a/lms/djangoapps/bulk_email/tests/test_forms.py +++ b/lms/djangoapps/bulk_email/tests/test_forms.py @@ -11,7 +11,7 @@ from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE from xmodule.modulestore.django import modulestore -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from mock import patch @@ -132,7 +132,7 @@ class CourseAuthorizationXMLFormTest(ModuleStoreTestCase): def test_xml_course_authorization(self): course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') # Assert this is an XML course - self.assertEqual(modulestore().get_modulestore_type(course_id), XML_MODULESTORE_TYPE) + self.assertEqual(modulestore().get_modulestore_type(course_id), ModuleStoreEnum.Type.xml) form_data = {'course_id': course_id.to_deprecated_string(), 'email_enabled': True} form = CourseAuthorizationAdminForm(data=form_data) diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index 81e7a2f072..86ec963a71 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -8,13 +8,13 @@ from django.http import Http404 from django.conf import settings from edxmako.shortcuts import render_to_string -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from opaque_keys.edx.keys import CourseKey from xmodule.modulestore.django import modulestore from xmodule.contentstore.content import StaticContent from xmodule.modulestore.exceptions import ItemNotFoundError from static_replace import replace_static_urls -from xmodule.modulestore import MONGO_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.x_module import STUDENT_VIEW from courseware.access import has_access @@ -106,7 +106,7 @@ def get_opt_course_with_access(user, action, course_key): def course_image_url(course): """Try to look up the image url for the course. If it's not found, log an error and return the dead link""" - if course.static_asset_path or modulestore().get_modulestore_type(course.id) == XML_MODULESTORE_TYPE: + if course.static_asset_path or modulestore().get_modulestore_type(course.id) == ModuleStoreEnum.Type.xml: # If we are a static course with the course_image attribute # set different than the default, return that path so that # courses can use custom course image paths, otherwise just @@ -369,7 +369,7 @@ def get_studio_url(course_key, page): assert(isinstance(course_key, CourseKey)) course = get_course_by_id(course_key) is_studio_course = course.course_edit_method == "Studio" - is_mongo_course = modulestore().get_modulestore_type(course_key) == MONGO_MODULESTORE_TYPE + is_mongo_course = modulestore().get_modulestore_type(course_key) == ModuleStoreEnum.Type.mongo studio_link = None if is_studio_course and is_mongo_course: studio_link = get_cms_course_link(course, page) diff --git a/lms/djangoapps/courseware/tests/test_courses.py b/lms/djangoapps/courseware/tests/test_courses.py index 5b9e9ba37e..24915b1b6c 100644 --- a/lms/djangoapps/courseware/tests/test_courses.py +++ b/lms/djangoapps/courseware/tests/test_courses.py @@ -7,7 +7,7 @@ import mock from django.test.utils import override_settings from student.tests.factories import UserFactory import xmodule.modulestore.django as store_django -from xmodule.modulestore import BRANCH_DRAFT_PREFERRED +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory from xmodule.tests.xml import factories as xml @@ -62,18 +62,18 @@ class ModuleStoreBranchSettingTest(ModuleStoreTestCase): mock.Mock(return_value='preview.localhost') ) @override_settings( - HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS={r'preview\.': BRANCH_DRAFT_PREFERRED}, + HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS={r'preview\.': ModuleStoreEnum.Branch.draft_preferred}, MODULESTORE_BRANCH='fake_default_branch', ) def test_default_modulestore_preview_mapping(self): - self.assertEqual(store_django._get_modulestore_branch_setting(), BRANCH_DRAFT_PREFERRED) + self.assertEqual(store_django._get_modulestore_branch_setting(), ModuleStoreEnum.Branch.draft_preferred) @mock.patch( 'xmodule.modulestore.django.get_current_request_hostname', mock.Mock(return_value='localhost') ) @override_settings( - HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS={r'preview\.': BRANCH_DRAFT_PREFERRED}, + HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS={r'preview\.': ModuleStoreEnum.Branch.draft_preferred}, MODULESTORE_BRANCH='fake_default_branch', ) def test_default_modulestore_branch_mapping(self): diff --git a/lms/djangoapps/courseware/tests/test_lti_integration.py b/lms/djangoapps/courseware/tests/test_lti_integration.py index c2814000da..edea26e3e5 100644 --- a/lms/djangoapps/courseware/tests/test_lti_integration.py +++ b/lms/djangoapps/courseware/tests/test_lti_integration.py @@ -10,7 +10,7 @@ from django.test.utils import override_settings from django.core.urlresolvers import reverse from django.conf import settings -from xmodule.modulestore import KEY_REVISION_DRAFT +from xmodule.modulestore.mongo.base import MongoRevisionKey from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.x_module import STUDENT_VIEW @@ -160,7 +160,7 @@ class TestLTIModuleListing(ModuleStoreTestCase): parent_location=self.section2.location, display_name="lti draft", category="lti", - location=self.course.id.make_usage_key('lti', 'lti_published').replace(revision=KEY_REVISION_DRAFT), + location=self.course.id.make_usage_key('lti', 'lti_published').replace(revision=MongoRevisionKey.draft), ) def expected_handler_url(self, handler): diff --git a/lms/djangoapps/dashboard/sysadmin.py b/lms/djangoapps/dashboard/sysadmin.py index ce5193204e..79f790ac04 100644 --- a/lms/djangoapps/dashboard/sysadmin.py +++ b/lms/djangoapps/dashboard/sysadmin.py @@ -39,7 +39,7 @@ from external_auth.views import generate_password from student.models import CourseEnrollment, UserProfile, Registration import track.views from xmodule.contentstore.django import contentstore -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from xmodule.modulestore.store_utilities import delete_course from xmodule.modulestore.xml import XMLModuleStore @@ -573,7 +573,7 @@ class Courses(SysadminDashboardView): escape(str(err)) ) - is_xml_course = (modulestore().get_modulestore_type(course_key) == XML_MODULESTORE_TYPE) + is_xml_course = (modulestore().get_modulestore_type(course_key) == ModuleStoreEnum.Type.xml) if course_found and is_xml_course: cdir = course.data_dir self.def_ms.courses.pop(cdir) diff --git a/lms/djangoapps/instructor/tests/test_legacy_email.py b/lms/djangoapps/instructor/tests/test_legacy_email.py index 7a5bb183e8..2d7942b4c1 100644 --- a/lms/djangoapps/instructor/tests/test_legacy_email.py +++ b/lms/djangoapps/instructor/tests/test_legacy_email.py @@ -12,7 +12,7 @@ from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE from student.tests.factories import AdminFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from bulk_email.models import CourseAuthorization @@ -103,7 +103,7 @@ class TestInstructorDashboardEmailView(ModuleStoreTestCase): # in `instructor/views/legacy.py` is doing the correct thing. with patch('xmodule.modulestore.mongo.base.MongoModuleStore.get_modulestore_type') as mock_modulestore: - mock_modulestore.return_value = XML_MODULESTORE_TYPE + mock_modulestore.return_value = ModuleStoreEnum.Type.xml # Assert that the URL for the email view is not in the response response = self.client.get(self.url) diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py index 4293e60ea3..939c1e03dc 100644 --- a/lms/djangoapps/instructor/views/instructor_dashboard.py +++ b/lms/djangoapps/instructor/views/instructor_dashboard.py @@ -15,7 +15,7 @@ from django.conf import settings from lms.lib.xblock.runtime import quote_slashes from xmodule_modifiers import wrap_xblock from xmodule.html_module import HtmlDescriptor -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from xblock.field_data import DictFieldData from xblock.fields import ScopeIds @@ -37,7 +37,7 @@ def instructor_dashboard_2(request, course_id): """ Display the instructor dashboard for a course. """ course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_by_id(course_key, depth=None) - is_studio_course = (modulestore().get_modulestore_type(course_key) != XML_MODULESTORE_TYPE) + is_studio_course = (modulestore().get_modulestore_type(course_key) != ModuleStoreEnum.Type.xml) access = { 'admin': request.user.is_staff, diff --git a/lms/djangoapps/instructor/views/legacy.py b/lms/djangoapps/instructor/views/legacy.py index cdab0986e1..4dd7d355af 100644 --- a/lms/djangoapps/instructor/views/legacy.py +++ b/lms/djangoapps/instructor/views/legacy.py @@ -28,7 +28,7 @@ from django.utils import timezone from xmodule_modifiers import wrap_xblock import xmodule.graders as xmgraders -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from opaque_keys.edx.locations import SlashSeparatedCourseKey from xmodule.modulestore.exceptions import ItemNotFoundError @@ -968,7 +968,7 @@ def instructor_dashboard(request, course_id): instructor_tasks = None # determine if this is a studio-backed course so we can provide a link to edit this course in studio - is_studio_course = modulestore().get_modulestore_type(course_key) != XML_MODULESTORE_TYPE + is_studio_course = modulestore().get_modulestore_type(course_key) != ModuleStoreEnum.Type.xml studio_url = None if is_studio_course: studio_url = get_cms_course_link(course) diff --git a/lms/djangoapps/instructor/views/tools.py b/lms/djangoapps/instructor/views/tools.py index ce79f7c916..4b6a621b97 100644 --- a/lms/djangoapps/instructor/views/tools.py +++ b/lms/djangoapps/instructor/views/tools.py @@ -12,7 +12,7 @@ from django.utils.translation import ugettext as _ from courseware.models import StudentModule from xmodule.fields import Date -from xmodule.modulestore import XML_MODULESTORE_TYPE +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from bulk_email.models import CourseAuthorization @@ -59,7 +59,7 @@ def bulk_email_is_enabled_for_course(course_id): """ bulk_email_enabled_globally = (settings.FEATURES['ENABLE_INSTRUCTOR_EMAIL'] == True) - is_studio_course = (modulestore().get_modulestore_type(course_id) != XML_MODULESTORE_TYPE) + is_studio_course = (modulestore().get_modulestore_type(course_id) != ModuleStoreEnum.Type.xml) bulk_email_enabled_for_course = CourseAuthorization.instructor_email_enabled(course_id) if bulk_email_enabled_globally and is_studio_course and bulk_email_enabled_for_course: diff --git a/lms/envs/cms/dev.py b/lms/envs/cms/dev.py index de6c189e01..d975f52e68 100644 --- a/lms/envs/cms/dev.py +++ b/lms/envs/cms/dev.py @@ -44,5 +44,5 @@ DEBUG_TOOLBAR_PANELS += ( # what the 'default' modulestore to use while processing the request # for example 'preview.edx.org' should use the draft modulestore HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS = { - 'preview\.': 'draft' + 'preview\.': 'draft-preferred' }