Merge pull request #1252 from MITx/feature/cale/lms-mongo-perf
Decrease the number of queries needed for LMS courseware
This commit is contained in:
@@ -42,28 +42,31 @@ def get_request_for_thread():
|
||||
del frame
|
||||
|
||||
|
||||
def get_course_by_id(course_id):
|
||||
def get_course_by_id(course_id, depth=0):
|
||||
"""
|
||||
Given a course id, return the corresponding course descriptor.
|
||||
|
||||
If course_id is not valid, raises a 404.
|
||||
depth: The number of levels of children for the modulestore to cache. None means infinite depth
|
||||
"""
|
||||
try:
|
||||
course_loc = CourseDescriptor.id_to_location(course_id)
|
||||
return modulestore().get_instance(course_id, course_loc)
|
||||
return modulestore().get_instance(course_id, course_loc, depth=depth)
|
||||
except (KeyError, ItemNotFoundError):
|
||||
raise Http404("Course not found.")
|
||||
|
||||
|
||||
def get_course_with_access(user, course_id, action):
|
||||
def get_course_with_access(user, course_id, action, depth=0):
|
||||
"""
|
||||
Given a course_id, look up the corresponding course descriptor,
|
||||
check that the user has the access to perform the specified action
|
||||
on the course, and return the descriptor.
|
||||
|
||||
Raises a 404 if the course_id is invalid, or the user doesn't have access.
|
||||
|
||||
depth: The number of levels of children for the modulestore to cache. None means infinite depth
|
||||
"""
|
||||
course = get_course_by_id(course_id)
|
||||
course = get_course_by_id(course_id, depth=depth)
|
||||
if not has_access(user, course, action):
|
||||
# Deliberately return a non-specific error message to avoid
|
||||
# leaking info about access control settings
|
||||
|
||||
@@ -36,8 +36,7 @@ def yield_dynamic_descriptor_descendents(descriptor, module_creator):
|
||||
def get_dynamic_descriptor_children(descriptor):
|
||||
if descriptor.has_dynamic_children():
|
||||
module = module_creator(descriptor)
|
||||
child_locations = module.get_children_locations()
|
||||
return [descriptor.system.load_item(child_location) for child_location in child_locations ]
|
||||
return module.get_child_descriptors()
|
||||
else:
|
||||
return descriptor.get_children()
|
||||
|
||||
@@ -291,7 +290,7 @@ def progress_summary(student, request, course, student_module_cache):
|
||||
graded = section_module.metadata.get('graded', False)
|
||||
scores = []
|
||||
|
||||
module_creator = lambda descriptor : section_module.system.get_module(descriptor.location)
|
||||
module_creator = section_module.system.get_module
|
||||
|
||||
for module_descriptor in yield_dynamic_descriptor_descendents(section_module.descriptor, module_creator):
|
||||
|
||||
|
||||
@@ -82,7 +82,8 @@ def toc_for_course(user, request, course, active_chapter, active_section):
|
||||
|
||||
student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(
|
||||
course.id, user, course, depth=2)
|
||||
course_module = get_module(user, request, course.location, student_module_cache, course.id)
|
||||
course_module = get_module_for_descriptor(user, request, course,
|
||||
student_module_cache, course.id)
|
||||
if course_module is None:
|
||||
return None
|
||||
|
||||
@@ -115,7 +116,9 @@ def toc_for_course(user, request, course, active_chapter, active_section):
|
||||
return chapters
|
||||
|
||||
|
||||
def get_module(user, request, location, student_module_cache, course_id, position=None, not_found_ok = False, wrap_xmodule_display = True):
|
||||
def get_module(user, request, location, student_module_cache, course_id,
|
||||
position=None, not_found_ok=False, wrap_xmodule_display=True,
|
||||
depth=0):
|
||||
"""
|
||||
Get an instance of the xmodule class identified by location,
|
||||
setting the state based on an existing StudentModule, or creating one if none
|
||||
@@ -130,13 +133,19 @@ def get_module(user, request, location, student_module_cache, course_id, positio
|
||||
- course_id : the course_id in the context of which to load module
|
||||
- position : extra information from URL for user-specified
|
||||
position within module
|
||||
- depth : number of levels of descendents to cache when loading this module.
|
||||
None means cache all descendents
|
||||
|
||||
Returns: xmodule instance, or None if the user does not have access to the
|
||||
module. If there's an error, will try to return an instance of ErrorModule
|
||||
if possible. If not possible, return None.
|
||||
"""
|
||||
try:
|
||||
return _get_module(user, request, location, student_module_cache, course_id, position, wrap_xmodule_display)
|
||||
location = Location(location)
|
||||
descriptor = modulestore().get_instance(course_id, location, depth=depth)
|
||||
return get_module_for_descriptor(user, request, descriptor, student_module_cache, course_id,
|
||||
position=position, not_found_ok=not_found_ok,
|
||||
wrap_xmodule_display=wrap_xmodule_display)
|
||||
except ItemNotFoundError:
|
||||
if not not_found_ok:
|
||||
log.exception("Error in get_module")
|
||||
@@ -146,12 +155,20 @@ def get_module(user, request, location, student_module_cache, course_id, positio
|
||||
log.exception("Error in get_module")
|
||||
return None
|
||||
|
||||
def _get_module(user, request, location, student_module_cache, course_id, position=None, wrap_xmodule_display = True):
|
||||
|
||||
def get_module_for_descriptor(user, request, descriptor, student_module_cache, course_id,
|
||||
position=None, not_found_ok=False, wrap_xmodule_display=True):
|
||||
"""
|
||||
Actually implement get_module. See docstring there for details.
|
||||
"""
|
||||
return _get_module(user, request, descriptor, student_module_cache, course_id,
|
||||
position=position, wrap_xmodule_display=wrap_xmodule_display)
|
||||
|
||||
def _get_module(user, request, descriptor, student_module_cache, course_id,
|
||||
position=None, wrap_xmodule_display=True):
|
||||
"""
|
||||
Actually implement get_module. See docstring there for details.
|
||||
"""
|
||||
location = Location(location)
|
||||
descriptor = modulestore().get_instance(course_id, location)
|
||||
|
||||
# Short circuit--if the user shouldn't have access, bail without doing any work
|
||||
if not has_access(user, descriptor, 'load', course_id):
|
||||
@@ -206,12 +223,12 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
|
||||
'waittime': settings.XQUEUE_WAITTIME_BETWEEN_REQUESTS
|
||||
}
|
||||
|
||||
def inner_get_module(location):
|
||||
def inner_get_module(descriptor):
|
||||
"""
|
||||
Delegate to get_module. It does an access check, so may return None
|
||||
"""
|
||||
return get_module(user, request, location,
|
||||
student_module_cache, course_id, position)
|
||||
return get_module_for_descriptor(user, request, descriptor,
|
||||
student_module_cache, course_id, position)
|
||||
|
||||
# TODO (cpennington): When modules are shared between courses, the static
|
||||
# prefix is going to have to be specific to the module, not the directory
|
||||
@@ -246,7 +263,7 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
|
||||
|
||||
# make an ErrorDescriptor -- assuming that the descriptor's system is ok
|
||||
import_system = descriptor.system
|
||||
if has_access(user, location, 'staff', course_id):
|
||||
if has_access(user, descriptor.location, 'staff', course_id):
|
||||
err_descriptor = ErrorDescriptor.from_xml(str(descriptor), import_system,
|
||||
error_msg=exc_info_to_str(sys.exc_info()))
|
||||
else:
|
||||
|
||||
@@ -20,7 +20,7 @@ from courseware.access import has_access
|
||||
from courseware.courses import (get_courses, get_course_with_access, get_courses_by_university)
|
||||
import courseware.tabs as tabs
|
||||
from courseware.models import StudentModuleCache
|
||||
from module_render import toc_for_course, get_module, get_instance_module
|
||||
from module_render import toc_for_course, get_module, get_instance_module, get_module_for_descriptor
|
||||
|
||||
from django_comment_client.utils import get_discussion_title
|
||||
|
||||
@@ -180,7 +180,7 @@ def index(request, course_id, chapter=None, section=None,
|
||||
|
||||
- HTTPresponse
|
||||
"""
|
||||
course = get_course_with_access(request.user, course_id, 'load')
|
||||
course = get_course_with_access(request.user, course_id, 'load', depth=2)
|
||||
staff_access = has_access(request.user, course, 'staff')
|
||||
registered = registered_for_course(course, request.user)
|
||||
if not registered:
|
||||
@@ -195,7 +195,8 @@ def index(request, course_id, chapter=None, section=None,
|
||||
# Has this student been in this course before?
|
||||
first_time = student_module_cache.lookup(course_id, 'course', course.location.url()) is None
|
||||
|
||||
course_module = get_module(request.user, request, course.location, student_module_cache, course.id)
|
||||
# Load the module for the course
|
||||
course_module = get_module_for_descriptor(request.user, request, course, student_module_cache, course.id)
|
||||
if course_module is None:
|
||||
log.warning('If you see this, something went wrong: if we got this'
|
||||
' far, should have gotten a course module for this user')
|
||||
@@ -215,30 +216,28 @@ def index(request, course_id, chapter=None, section=None,
|
||||
'xqa_server': settings.MITX_FEATURES.get('USE_XQA_SERVER','http://xqa:server@content-qa.mitx.mit.edu/xqa')
|
||||
}
|
||||
|
||||
chapter_descriptor = course.get_child_by_url_name(chapter)
|
||||
chapter_descriptor = course.get_child_by(lambda m: m.url_name == chapter)
|
||||
if chapter_descriptor is not None:
|
||||
instance_module = get_instance_module(course_id, request.user, course_module, student_module_cache)
|
||||
save_child_position(course_module, chapter, instance_module)
|
||||
else:
|
||||
raise Http404
|
||||
|
||||
chapter_module = get_module(request.user, request, chapter_descriptor.location,
|
||||
student_module_cache, course_id)
|
||||
chapter_module = course_module.get_child_by(lambda m: m.url_name == chapter)
|
||||
if chapter_module is None:
|
||||
# User may be trying to access a chapter that isn't live yet
|
||||
raise Http404
|
||||
|
||||
if section is not None:
|
||||
section_descriptor = chapter_descriptor.get_child_by_url_name(section)
|
||||
section_descriptor = chapter_descriptor.get_child_by(lambda m: m.url_name == section)
|
||||
if section_descriptor is None:
|
||||
# Specifically asked-for section doesn't exist
|
||||
raise Http404
|
||||
|
||||
section_student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(
|
||||
course_id, request.user, section_descriptor)
|
||||
section_module = get_module(request.user, request,
|
||||
section_descriptor.location,
|
||||
section_student_module_cache, course_id, position)
|
||||
# Load all descendents of the section, because we're going to display it's
|
||||
# html, which in general will need all of its children
|
||||
section_module = get_module(request.user, request, section_descriptor.location,
|
||||
student_module_cache, course.id, depth=None)
|
||||
if section_module is None:
|
||||
# User may be trying to be clever and access something
|
||||
# they don't have access to.
|
||||
|
||||
Reference in New Issue
Block a user