Commit e0aa46ab by cahrens

Make split mongo read-only API consistent with other modulestores.

parent 9ea2be53
...@@ -56,21 +56,27 @@ class LMSLinksTestCase(TestCase): ...@@ -56,21 +56,27 @@ class LMSLinksTestCase(TestCase):
def get_about_page_link(self): def get_about_page_link(self):
""" create mock course and return the about page link """ """ create mock course and return the about page link """
location = 'i4x', 'mitX', '101', 'course', 'test' location = 'i4x', 'mitX', '101', 'course', 'test'
utils.get_course_id = mock.Mock(return_value="mitX/101/test")
return utils.get_lms_link_for_about_page(location) return utils.get_lms_link_for_about_page(location)
def lms_link_test(self): def lms_link_test(self):
""" Tests get_lms_link_for_item. """ """ Tests get_lms_link_for_item. """
location = 'i4x', 'mitX', '101', 'vertical', 'contacting_us' location = 'i4x', 'mitX', '101', 'vertical', 'contacting_us'
utils.get_course_id = mock.Mock(return_value="mitX/101/test") link = utils.get_lms_link_for_item(location, False, "mitX/101/test")
link = utils.get_lms_link_for_item(location, False)
self.assertEquals(link, "//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/vertical/contacting_us") self.assertEquals(link, "//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/vertical/contacting_us")
link = utils.get_lms_link_for_item(location, True) link = utils.get_lms_link_for_item(location, True, "mitX/101/test")
self.assertEquals( self.assertEquals(
link, link,
"//preview/courses/mitX/101/test/jump_to/i4x://mitX/101/vertical/contacting_us" "//preview/courses/mitX/101/test/jump_to/i4x://mitX/101/vertical/contacting_us"
) )
# If no course_id is passed in, it is obtained from the location. This is the case for
# Studio dashboard.
location = 'i4x', 'mitX', '101', 'course', 'test'
link = utils.get_lms_link_for_item(location)
self.assertEquals(
link,
"//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/course/test"
)
class ExtraPanelTabTestCase(TestCase): class ExtraPanelTabTestCase(TestCase):
""" Tests adding and removing extra course tabs. """ """ Tests adding and removing extra course tabs. """
......
...@@ -89,8 +89,17 @@ def get_course_for_item(location): ...@@ -89,8 +89,17 @@ def get_course_for_item(location):
def get_lms_link_for_item(location, preview=False, course_id=None): def get_lms_link_for_item(location, preview=False, course_id=None):
"""
Returns an LMS link to the course with a jump_to to the provided location.
:param location: the location to jump to
:param preview: True if the preview version of LMS should be returned. Default value is false.
:param course_id: the course_id within which the location lives. If not specified, the course_id is obtained
by calling Location(location).course_id; note that this only works for locations representing courses
instead of elements within courses.
"""
if course_id is None: if course_id is None:
course_id = get_course_id(location) course_id = Location(location).course_id
if settings.LMS_BASE is not None: if settings.LMS_BASE is not None:
if preview: if preview:
...@@ -136,7 +145,7 @@ def get_lms_link_for_about_page(location): ...@@ -136,7 +145,7 @@ def get_lms_link_for_about_page(location):
if about_base is not None: if about_base is not None:
lms_link = "//{about_base_url}/courses/{course_id}/about".format( lms_link = "//{about_base_url}/courses/{course_id}/about".format(
about_base_url=about_base, about_base_url=about_base,
course_id=get_course_id(location) course_id=Location(location).course_id
) )
else: else:
lms_link = None lms_link = None
...@@ -144,14 +153,6 @@ def get_lms_link_for_about_page(location): ...@@ -144,14 +153,6 @@ def get_lms_link_for_about_page(location):
return lms_link return lms_link
def get_course_id(location):
"""
Returns the course_id from a given the location tuple.
"""
# TODO: These will need to be changed to point to the particular instance of this problem in the particular course
return modulestore().get_containing_courses(Location(location))[0].id
class UnitState(object): class UnitState(object):
draft = 'draft' draft = 'draft'
private = 'private' private = 'private'
......
...@@ -54,8 +54,7 @@ def index(request): ...@@ -54,8 +54,7 @@ def index(request):
'name': course.location.name, 'name': course.location.name,
}), }),
get_lms_link_for_item( get_lms_link_for_item(
course.location, course.location
course_id=course.location.course_id,
), ),
course.display_org_with_default, course.display_org_with_default,
course.display_number_with_default, course.display_number_with_default,
......
...@@ -274,7 +274,7 @@ def dashboard(request): ...@@ -274,7 +274,7 @@ def dashboard(request):
# Global staff can see what courses errored on their dashboard # Global staff can see what courses errored on their dashboard
staff_access = False staff_access = False
errored_courses = {} errored_courses = {}
if has_access(user, 'global', 'staff'): if has_access(user, 'global', 'staff') and callable(getattr(modulestore(), 'get_errored_courses')):
# Show any courses that errored on load # Show any courses that errored on load
staff_access = True staff_access = True
errored_courses = modulestore().get_errored_courses() errored_courses = modulestore().get_errored_courses()
......
...@@ -370,21 +370,6 @@ class ModuleStore(object): ...@@ -370,21 +370,6 @@ class ModuleStore(object):
''' '''
raise NotImplementedError raise NotImplementedError
def get_containing_courses(self, location):
'''
Returns the list of courses that contains the specified location
TODO (cpennington): This should really take a module instance id,
rather than a location
'''
courses = [
course
for course in self.get_courses()
if course.location.org == location.org and course.location.course == location.course
]
return courses
class ModuleStoreBase(ModuleStore): class ModuleStoreBase(ModuleStore):
''' '''
......
...@@ -841,13 +841,6 @@ class MongoModuleStore(ModuleStoreBase): ...@@ -841,13 +841,6 @@ class MongoModuleStore(ModuleStoreBase):
{'_id': True}) {'_id': True})
return [i['_id'] for i in items] return [i['_id'] for i in items]
def get_errored_courses(self):
"""
This function doesn't make sense for the mongo modulestore, as courses
are loaded on demand, rather than up front
"""
return {}
def _create_new_model_data(self, category, location, definition_data, metadata): 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 To instantiate a new xmodule which will be saved latter, set up the dbModel and kvs
......
...@@ -226,7 +226,7 @@ class SplitMongoModuleStore(ModuleStoreBase): ...@@ -226,7 +226,7 @@ class SplitMongoModuleStore(ModuleStoreBase):
entry['branch'] = course_locator.branch entry['branch'] = course_locator.branch
return entry return entry
def get_courses(self, branch, qualifiers=None): def get_courses(self, branch='published', qualifiers=None):
''' '''
Returns a list of course descriptors matching any given qualifiers. Returns a list of course descriptors matching any given qualifiers.
...@@ -235,6 +235,9 @@ class SplitMongoModuleStore(ModuleStoreBase): ...@@ -235,6 +235,9 @@ class SplitMongoModuleStore(ModuleStoreBase):
Note, this is to find the current head of the named branch type Note, this is to find the current head of the named branch type
(e.g., 'draft'). To get specific versions via guid use get_course. (e.g., 'draft'). To get specific versions via guid use get_course.
:param branch: the branch for which to return courses. Default value is 'published'.
:param qualifiers: a optional dict restricting which elements should match
''' '''
if qualifiers is None: if qualifiers is None:
qualifiers = {} qualifiers = {}
...@@ -272,13 +275,6 @@ class SplitMongoModuleStore(ModuleStoreBase): ...@@ -272,13 +275,6 @@ class SplitMongoModuleStore(ModuleStoreBase):
result = self._load_items(course_entry, [root], 0, lazy=True) result = self._load_items(course_entry, [root], 0, lazy=True)
return result[0] return result[0]
def get_course_for_item(self, location):
'''
Provided for backward compatibility. Is equivalent to calling get_course
:param location:
'''
return self.get_course(location)
def has_item(self, block_location): def has_item(self, block_location):
""" """
Returns True if location exists in its course. Returns false if Returns True if location exists in its course. Returns false if
...@@ -313,9 +309,8 @@ class SplitMongoModuleStore(ModuleStoreBase): ...@@ -313,9 +309,8 @@ class SplitMongoModuleStore(ModuleStoreBase):
raise ItemNotFoundError(location) raise ItemNotFoundError(location)
return items[0] return items[0]
# TODO refactor this and get_courses to use a constructed query def get_items(self, locator, course_id=None, depth=0, qualifiers=None):
def get_items(self, locator, qualifiers): """
'''
Get all of the modules in the given course matching the qualifiers. The Get all of the modules in the given course matching the qualifiers. The
qualifiers should only be fields in the structures collection (sorry). qualifiers should only be fields in the structures collection (sorry).
There will be a separate search method for searching through There will be a separate search method for searching through
...@@ -331,9 +326,14 @@ class SplitMongoModuleStore(ModuleStoreBase): ...@@ -331,9 +326,14 @@ class SplitMongoModuleStore(ModuleStoreBase):
try arbitrary queries. try arbitrary queries.
:param locator: CourseLocator or BlockUsageLocator restricting search scope :param locator: CourseLocator or BlockUsageLocator restricting search scope
:param course_id: ignored. Only included for API compatibility.
:param depth: ignored. Only included for API compatibility.
:param qualifiers: a dict restricting which elements should match :param qualifiers: a dict restricting which elements should match
'''
"""
# TODO extend to only search a subdag of the course? # TODO extend to only search a subdag of the course?
if qualifiers is None:
qualifiers = {}
course = self._lookup_course(locator) course = self._lookup_course(locator)
items = [] items = []
for usage_id, value in course['blocks'].iteritems(): for usage_id, value in course['blocks'].iteritems():
...@@ -345,23 +345,22 @@ class SplitMongoModuleStore(ModuleStoreBase): ...@@ -345,23 +345,22 @@ class SplitMongoModuleStore(ModuleStoreBase):
else: else:
return [] return []
# What's the use case for usage_id being separate?
def get_parent_locations(self, locator, usage_id=None): def get_parent_locations(self, locator, usage_id=None):
''' '''
Return the locations (Locators w/ usage_ids) for the parents of this location in this Return the locations (Locators w/ usage_ids) for the parents of this location in this
course. Could use get_items(location, {'children': usage_id}) but this is slightly faster. course. Could use get_items(location, {'children': usage_id}) but this is slightly faster.
NOTE: does not actually ensure usage_id exists NOTE: the locator must contain the usage_id, and this code does not actually ensure usage_id exists
If usage_id is None, then the locator must specify the usage_id
:param locator: BlockUsageLocator restricting search scope
:param usage_id: ignored. Only included for API compatibility. Specify the usage_id within the locator.
''' '''
if usage_id is None:
usage_id = locator.usage_id
course = self._lookup_course(locator) course = self._lookup_course(locator)
items = [] items = []
for parent_id, value in course['blocks'].iteritems(): for parent_id, value in course['blocks'].iteritems():
for child_id in value['children']: for child_id in value['children']:
if usage_id == child_id: if locator.usage_id == child_id:
locator = locator.as_course_locator() items.append(BlockUsageLocator(url=locator.as_course_locator(), usage_id=parent_id))
items.append(BlockUsageLocator(url=locator, usage_id=parent_id))
return items return items
def get_course_index_info(self, course_locator): def get_course_index_info(self, course_locator):
...@@ -1050,14 +1049,6 @@ class SplitMongoModuleStore(ModuleStoreBase): ...@@ -1050,14 +1049,6 @@ class SplitMongoModuleStore(ModuleStoreBase):
# this is the only real delete in the system. should it do something else? # this is the only real delete in the system. should it do something else?
self.course_index.remove(index['_id']) self.course_index.remove(index['_id'])
# TODO remove all callers and then this
def get_errored_courses(self):
"""
This function doesn't make sense for the mongo modulestore, as structures
are loaded on demand, rather than up front
"""
return {}
def inherit_metadata(self, block_map, block, inheriting_metadata=None): def inherit_metadata(self, block_map, block, inheriting_metadata=None):
""" """
Updates block with any value Updates block with any value
......
...@@ -107,7 +107,7 @@ class SplitModuleCourseTests(SplitModuleTest): ...@@ -107,7 +107,7 @@ class SplitModuleCourseTests(SplitModuleTest):
''' '''
def test_get_courses(self): def test_get_courses(self):
courses = modulestore().get_courses('draft') courses = modulestore().get_courses(branch='draft')
# should have gotten 3 draft courses # should have gotten 3 draft courses
self.assertEqual(len(courses), 3, "Wrong number of courses") self.assertEqual(len(courses), 3, "Wrong number of courses")
# check metadata -- NOTE no promised order # check metadata -- NOTE no promised order
...@@ -138,35 +138,40 @@ class SplitModuleCourseTests(SplitModuleTest): ...@@ -138,35 +138,40 @@ class SplitModuleCourseTests(SplitModuleTest):
def test_branch_requests(self): def test_branch_requests(self):
# query w/ branch qualifier (both draft and published) # query w/ branch qualifier (both draft and published)
courses_published = modulestore().get_courses('published') def _verify_published_course(courses_published):
self.assertEqual(len(courses_published), 1, len(courses_published)) """ Helper function for verifying published course. """
course = self.findByIdInResult(courses_published, "head23456") self.assertEqual(len(courses_published), 1, len(courses_published))
self.assertIsNotNone(course, "published courses") course = self.findByIdInResult(courses_published, "head23456")
self.assertEqual(course.location.course_id, "wonderful") self.assertIsNotNone(course, "published courses")
self.assertEqual(str(course.location.version_guid), self.GUID_P, self.assertEqual(course.location.course_id, "wonderful")
course.location.version_guid) self.assertEqual(str(course.location.version_guid), self.GUID_P,
self.assertEqual(course.category, 'course', 'wrong category') course.location.version_guid)
self.assertEqual(len(course.tabs), 4, "wrong number of tabs") self.assertEqual(course.category, 'course', 'wrong category')
self.assertEqual(course.display_name, "The most wonderful course", self.assertEqual(len(course.tabs), 4, "wrong number of tabs")
course.display_name) self.assertEqual(course.display_name, "The most wonderful course",
self.assertIsNone(course.advertised_start) course.display_name)
self.assertEqual(len(course.children), 0, self.assertIsNone(course.advertised_start)
"children") self.assertEqual(len(course.children), 0,
"children")
_verify_published_course(modulestore().get_courses(branch='published'))
# default for branch is 'published'.
_verify_published_course(modulestore().get_courses())
def test_search_qualifiers(self): def test_search_qualifiers(self):
# query w/ search criteria # query w/ search criteria
courses = modulestore().get_courses('draft', qualifiers={'org': 'testx'}) courses = modulestore().get_courses(branch='draft', qualifiers={'org': 'testx'})
self.assertEqual(len(courses), 2) self.assertEqual(len(courses), 2)
self.assertIsNotNone(self.findByIdInResult(courses, "head12345")) self.assertIsNotNone(self.findByIdInResult(courses, "head12345"))
self.assertIsNotNone(self.findByIdInResult(courses, "head23456")) self.assertIsNotNone(self.findByIdInResult(courses, "head23456"))
courses = modulestore().get_courses( courses = modulestore().get_courses(
'draft', branch='draft',
qualifiers={'edited_on': {"$lt": datetime.datetime(2013, 3, 28, 15)}}) qualifiers={'edited_on': {"$lt": datetime.datetime(2013, 3, 28, 15)}})
self.assertEqual(len(courses), 2) self.assertEqual(len(courses), 2)
courses = modulestore().get_courses( courses = modulestore().get_courses(
'draft', branch='draft',
qualifiers={'org': 'testx', "prettyid": "test_course"}) qualifiers={'org': 'testx', "prettyid": "test_course"})
self.assertEqual(len(courses), 1) self.assertEqual(len(courses), 1)
self.assertIsNotNone(self.findByIdInResult(courses, "head12345")) self.assertIsNotNone(self.findByIdInResult(courses, "head12345"))
...@@ -415,14 +420,17 @@ class SplitModuleItemTests(SplitModuleTest): ...@@ -415,14 +420,17 @@ class SplitModuleItemTests(SplitModuleTest):
''' '''
locator = CourseLocator(version_guid=self.GUID_D0) locator = CourseLocator(version_guid=self.GUID_D0)
# get all modules # get all modules
matches = modulestore().get_items(locator, {}) matches = modulestore().get_items(locator)
self.assertEqual(len(matches), 6)
matches = modulestore().get_items(locator, qualifiers={})
self.assertEqual(len(matches), 6) self.assertEqual(len(matches), 6)
matches = modulestore().get_items(locator, {'category': 'chapter'}) matches = modulestore().get_items(locator, qualifiers={'category': 'chapter'})
self.assertEqual(len(matches), 3) self.assertEqual(len(matches), 3)
matches = modulestore().get_items(locator, {'category': 'garbage'}) matches = modulestore().get_items(locator, qualifiers={'category': 'garbage'})
self.assertEqual(len(matches), 0) self.assertEqual(len(matches), 0)
matches = modulestore().get_items( matches = modulestore().get_items(
locator, locator,
qualifiers=
{ {
'category': 'chapter', 'category': 'chapter',
'metadata': {'display_name': {'$regex': 'Hera'}} 'metadata': {'display_name': {'$regex': 'Hera'}}
...@@ -430,7 +438,7 @@ class SplitModuleItemTests(SplitModuleTest): ...@@ -430,7 +438,7 @@ class SplitModuleItemTests(SplitModuleTest):
) )
self.assertEqual(len(matches), 2) self.assertEqual(len(matches), 2)
matches = modulestore().get_items(locator, {'children': 'chapter2'}) matches = modulestore().get_items(locator, qualifiers={'children': 'chapter2'})
self.assertEqual(len(matches), 1) self.assertEqual(len(matches), 1)
self.assertEqual(matches[0].location.usage_id, 'head12345') self.assertEqual(matches[0].location.usage_id, 'head12345')
...@@ -438,8 +446,8 @@ class SplitModuleItemTests(SplitModuleTest): ...@@ -438,8 +446,8 @@ class SplitModuleItemTests(SplitModuleTest):
''' '''
get_parent_locations(locator, [usage_id], [branch]): [BlockUsageLocator] get_parent_locations(locator, [usage_id], [branch]): [BlockUsageLocator]
''' '''
locator = CourseLocator(course_id="GreekHero", branch='draft') locator = BlockUsageLocator(course_id="GreekHero", branch='draft', usage_id='chapter1')
parents = modulestore().get_parent_locations(locator, usage_id='chapter1') parents = modulestore().get_parent_locations(locator)
self.assertEqual(len(parents), 1) self.assertEqual(len(parents), 1)
self.assertEqual(parents[0].usage_id, 'head12345') self.assertEqual(parents[0].usage_id, 'head12345')
self.assertEqual(parents[0].course_id, "GreekHero") self.assertEqual(parents[0].course_id, "GreekHero")
...@@ -447,7 +455,8 @@ class SplitModuleItemTests(SplitModuleTest): ...@@ -447,7 +455,8 @@ class SplitModuleItemTests(SplitModuleTest):
parents = modulestore().get_parent_locations(locator) parents = modulestore().get_parent_locations(locator)
self.assertEqual(len(parents), 1) self.assertEqual(len(parents), 1)
self.assertEqual(parents[0].usage_id, 'head12345') self.assertEqual(parents[0].usage_id, 'head12345')
parents = modulestore().get_parent_locations(locator, usage_id='nosuchblock') locator.usage_id='nosuchblock'
parents = modulestore().get_parent_locations(locator)
self.assertEqual(len(parents), 0) self.assertEqual(len(parents), 0)
def test_get_children(self): def test_get_children(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment