From 82988972ade7452ca59dbeab63ce974e80afa972 Mon Sep 17 00:00:00 2001 From: Chris Dodge Date: Wed, 24 Jul 2013 08:40:04 -0400 Subject: [PATCH] WIP: added a get_modulestore_type. Added some unit tests. changed over the isinstance() with respect to modulestores to use this get_modulestore_type() --- common/djangoapps/static_replace/__init__.py | 10 +++++----- .../lib/xmodule/xmodule/modulestore/__init__.py | 9 +++++++++ common/lib/xmodule/xmodule/modulestore/mixed.py | 7 +++++++ .../lib/xmodule/xmodule/modulestore/mongo/base.py | 15 +++++++++++---- .../xmodule/modulestore/tests/test_mongo.py | 8 ++++++-- .../xmodule/xmodule/modulestore/tests/test_xml.py | 11 ++++++++--- common/lib/xmodule/xmodule/modulestore/xml.py | 9 ++++++++- common/lib/xmodule/xmodule/video_module.py | 7 +------ lms/djangoapps/courseware/courses.py | 4 ++-- 9 files changed, 57 insertions(+), 23 deletions(-) diff --git a/common/djangoapps/static_replace/__init__.py b/common/djangoapps/static_replace/__init__.py index 9e50d73b26..c08db34349 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.xml import XMLModuleStore +from xmodule.modulestore import XML_MODULESTORE_TYPE from xmodule.contentstore.content import StaticContent log = logging.getLogger(__name__) @@ -90,7 +90,7 @@ def replace_course_urls(text, course_id): return re.sub(_url_replace_regex('/course/'), replace_course_url, text) -def replace_static_urls(text, data_directory, course_namespace=None): +def replace_static_urls(text, data_directory, course_id=None): """ Replace /static/$stuff urls either with their correct url as generated by collectstatic, (/static/$md5_hashed_stuff) or by the course-specific content static url @@ -99,7 +99,7 @@ def replace_static_urls(text, data_directory, course_namespace=None): text: The source text to do the substitution in data_directory: The directory in which course data is stored - course_namespace: The course identifier used to distinguish static content for this course in studio + course_id: The course identifier used to distinguish static content for this course in studio """ def replace_static_url(match): @@ -116,7 +116,7 @@ def replace_static_urls(text, data_directory, course_namespace=None): 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 course_namespace is not None and not isinstance(modulestore(), XMLModuleStore): + elif course_id and modulestore().get_modulestore_type(course_id) != XML_MODULESTORE_TYPE: # first look in the static file pipeline and see if we are trying to reference # a piece of static content which is in the mitx repo (e.g. JS associated with an xmodule) if staticfiles_storage.exists(rest): @@ -124,7 +124,7 @@ def replace_static_urls(text, data_directory, course_namespace=None): else: # if not, then assume it's courseware specific content and then look in the # Mongo-backed database - url = StaticContent.convert_legacy_static_url(rest, course_namespace) + url = StaticContent.convert_legacy_static_url(rest, course_id) # Otherwise, look the file up in staticfiles_storage, and append the data directory if needed else: course_path = "/".join((data_directory, rest)) diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py index a9848d6c05..ee61998931 100644 --- a/common/lib/xmodule/xmodule/modulestore/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/__init__.py @@ -14,6 +14,8 @@ from bson.son import SON log = logging.getLogger('mitx.' + 'modulestore') +MONGO_MODULESTORE_TYPE = 'mongo' +XML_MODULESTORE_TYPE = 'xml' URL_RE = re.compile(""" (?P[^:]+)://? @@ -391,6 +393,13 @@ class ModuleStore(object): ''' raise NotImplementedError + def get_modulestore_type(self, course_id): + """ + Returns a type which identifies which modulestore is servicing the given + course_id. The return can be either "xml" (for XML based courses) or "mongo" for MongoDB backed courses + """ + raise NotImplementedError + class ModuleStoreBase(ModuleStore): ''' diff --git a/common/lib/xmodule/xmodule/modulestore/mixed.py b/common/lib/xmodule/xmodule/modulestore/mixed.py index fe4d4d63be..253852d1db 100644 --- a/common/lib/xmodule/xmodule/modulestore/mixed.py +++ b/common/lib/xmodule/xmodule/modulestore/mixed.py @@ -118,3 +118,10 @@ class MixedModuleStore(ModuleStoreBase): """ for store in self.modulestores.values(): store.set_modulestore_configuration(config_dict) + + def get_modulestore_type(self, course_id): + """ + Returns a type which identifies which modulestore is servicing the given + course_id. The return can be either "xml" (for XML based courses) or "mongo" for MongoDB backed courses + """ + return self._get_modulestore_for_courseid(course_id).get_modulestore_type(course_id) diff --git a/common/lib/xmodule/xmodule/modulestore/mongo/base.py b/common/lib/xmodule/xmodule/modulestore/mongo/base.py index 8b4ce23ba7..de946f76e1 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo/base.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo/base.py @@ -32,7 +32,7 @@ from xmodule.error_module import ErrorDescriptor from xblock.runtime import DbModel, KeyValueStore, InvalidScopeError from xblock.core import Scope -from xmodule.modulestore import ModuleStoreBase, Location, namedtuple_to_son +from xmodule.modulestore import ModuleStoreBase, Location, namedtuple_to_son, MONGO_MODULESTORE_TYPE from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.inheritance import own_metadata, INHERITABLE_METADATA, inherit_metadata @@ -841,6 +841,13 @@ class MongoModuleStore(ModuleStoreBase): {'_id': True}) return [i['_id'] for i in items] + def get_modulestore_type(self, course_id): + """ + Returns a type which identifies which modulestore is servicing the given + course_id. The return can be either "xml" (for XML based courses) or "mongo" for MongoDB backed courses + """ + return MONGO_MODULESTORE_TYPE + def _create_new_model_data(self, category, location, definition_data, metadata): """ To instantiate a new xmodule which will be saved latter, set up the dbModel and kvs @@ -854,9 +861,9 @@ class MongoModuleStore(ModuleStoreBase): ) class_ = XModuleDescriptor.load_class( - category, - self.default_class - ) + category, + self.default_class + ) model_data = DbModel(kvs, class_, None, MongoUsage(None, location)) model_data['category'] = category model_data['location'] = location diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py index 69ba9ad94b..58c48f673e 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py @@ -45,8 +45,7 @@ class TestMongoModuleStore(object): @staticmethod def initdb(): # connect to the db - store = MongoModuleStore(HOST, DB, COLLECTION, FS_ROOT, RENDER_TEMPLATE, - default_class=DEFAULT_CLASS) + store = MongoModuleStore(HOST, DB, COLLECTION, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS) # Explicitly list the courses to load (don't want the big one) courses = ['toy', 'simple'] import_from_xml(store, DATA_DIR, courses) @@ -71,6 +70,10 @@ class TestMongoModuleStore(object): pprint([Location(i['_id']).url() for i in ids]) + def test_mongo_modulestore_type(self): + store = MongoModuleStore(HOST, DB, COLLECTION, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS) + assert_equals(store.get_modulestore_type('foo/bar/baz'), 'mongo') + def test_get_courses(self): '''Make sure the course objects loaded properly''' courses = self.store.get_courses() @@ -117,6 +120,7 @@ class TestMongoModuleStore(object): '{0} is a template course'.format(course) ) + class TestMongoKeyValueStore(object): def setUp(self): diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py b/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py index 1819850614..676d928bb7 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py @@ -1,12 +1,13 @@ import os.path -from nose.tools import assert_raises +from nose.tools import assert_raises, assert_equals from xmodule.course_module import CourseDescriptor from xmodule.modulestore.xml import XMLModuleStore +from xmodule.modulestore import XML_MODULESTORE_TYPE -from xmodule.tests import DATA_DIR -from xmodule.modulestore.tests.test_modulestore import check_path_to_location +from .test_modulestore import check_path_to_location +from . import DATA_DIR class TestXMLModuleStore(object): @@ -19,6 +20,10 @@ class TestXMLModuleStore(object): check_path_to_location(modulestore) + def test_xml_modulestore_type(self): + store = XMLModuleStore(DATA_DIR, course_dirs=['toy', 'simple']) + assert_equals(store.get_modulestore_type('foo/bar/baz'), XML_MODULESTORE_TYPE) + def test_unicode_chars_in_xml_content(self): # edX/full/6.002_Spring_2012 has non-ASCII chars, and during # uniquification of names, would raise a UnicodeError. It no longer does. diff --git a/common/lib/xmodule/xmodule/modulestore/xml.py b/common/lib/xmodule/xmodule/modulestore/xml.py index ae726041ab..fd04982f81 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml.py +++ b/common/lib/xmodule/xmodule/modulestore/xml.py @@ -21,7 +21,7 @@ from xmodule.x_module import XModuleDescriptor, XMLParsingSystem from xmodule.html_module import HtmlDescriptor -from . import ModuleStoreBase, Location +from . import ModuleStoreBase, Location, XML_MODULESTORE_TYPE from .exceptions import ItemNotFoundError from .inheritance import compute_inherited_metadata @@ -601,3 +601,10 @@ class XMLModuleStore(ModuleStoreBase): raise ItemNotFoundError("{0} not in {1}".format(location, course_id)) return self.parent_trackers[course_id].parents(location) + + def get_modulestore_type(self, course_id): + """ + Returns a type which identifies which modulestore is servicing the given + course_id. The return can be either "xml" (for XML based courses) or "mongo" for MongoDB backed courses + """ + return XML_MODULESTORE_TYPE diff --git a/common/lib/xmodule/xmodule/video_module.py b/common/lib/xmodule/xmodule/video_module.py index dd408a6f74..e847638af1 100644 --- a/common/lib/xmodule/xmodule/video_module.py +++ b/common/lib/xmodule/xmodule/video_module.py @@ -161,12 +161,7 @@ class VideoModule(VideoFields, XModule): return json.dumps({'position': self.position}) def get_html(self): - if isinstance(modulestore(), MongoModuleStore): - caption_asset_path = StaticContent.get_base_url_path_for_course_assets(self.location) + '/subs_' - else: - # VS[compat] - # cdodge: filesystem static content support. - caption_asset_path = "/static/subs/" + caption_asset_path = "/static/subs/" get_ext = lambda filename: filename.rpartition('.')[-1] sources = {get_ext(src): src for src in self.html5_sources} diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index 086f92a123..34de2eb958 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -8,7 +8,7 @@ from django.http import Http404 from .module_render import get_module from xmodule.course_module import CourseDescriptor -from xmodule.modulestore import Location +from xmodule.modulestore import Location, MONGO_MODULESTORE_TYPE from xmodule.modulestore.django import modulestore from xmodule.contentstore.content import StaticContent from xmodule.modulestore.xml import XMLModuleStore @@ -82,7 +82,7 @@ def get_opt_course_with_access(user, course_id, action): 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 isinstance(modulestore(), XMLModuleStore): + if modulestore().get_modulestore_type(course.course_id) == MONGO_MODULESTORE_TYPE: return '/static/' + course.data_dir + "/images/course_image.jpg" else: loc = course.location._replace(tag='c4x', category='asset', name='images_course_image.jpg')