Teach LMS how to render XBlockAsides

[PLAT-217]
This commit is contained in:
Calen Pennington
2014-12-05 13:39:53 -05:00
parent e20fe2b879
commit d919d2ae46
47 changed files with 669 additions and 183 deletions

View File

@@ -12,16 +12,17 @@ from .models import (
XModuleStudentInfoField
)
import logging
from opaque_keys.edx.locations import SlashSeparatedCourseKey, Location
from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.block_types import BlockTypeKeyV1
from opaque_keys.edx.asides import AsideUsageKeyV1
from django.db import DatabaseError
from django.contrib.auth.models import User
from xblock.runtime import KeyValueStore
from xblock.exceptions import KeyValueMultiSaveError, InvalidScopeError
from xblock.fields import Scope, UserScope
from xmodule.modulestore.django import modulestore
from xblock.core import XBlockAside
log = logging.getLogger(__name__)
@@ -46,7 +47,7 @@ class FieldDataCache(object):
A cache of django model objects needed to supply the data
for a module and its decendants
"""
def __init__(self, descriptors, course_id, user, select_for_update=False):
def __init__(self, descriptors, course_id, user, select_for_update=False, asides=None):
'''
Find any courseware.models objects that are needed by any descriptor
in descriptors. Attempts to minimize the number of queries to the database.
@@ -58,11 +59,17 @@ class FieldDataCache(object):
course_id: The id of the current course
user: The user for which to cache data
select_for_update: True if rows should be locked until end of transaction
asides: The list of aside types to load, or None to prefetch no asides.
'''
self.cache = {}
self.descriptors = descriptors
self.select_for_update = select_for_update
if asides is None:
self.asides = []
else:
self.asides = asides
assert isinstance(course_id, CourseKey)
self.course_id = course_id
self.user = user
@@ -75,7 +82,7 @@ class FieldDataCache(object):
@classmethod
def cache_for_descriptor_descendents(cls, course_id, user, descriptor, depth=None,
descriptor_filter=lambda descriptor: True,
select_for_update=False):
select_for_update=False, asides=None):
"""
course_id: the course in the context of which we want StudentModules.
user: the django user for whom to load modules.
@@ -113,7 +120,7 @@ class FieldDataCache(object):
with modulestore().bulk_operations(descriptor.location.course_key):
descriptors = get_child_descriptors(descriptor, depth, descriptor_filter)
return FieldDataCache(descriptors, course_id, user, select_for_update)
return FieldDataCache(descriptors, course_id, user, select_for_update, asides=asides)
def _query(self, model_class, **kwargs):
"""
@@ -140,6 +147,35 @@ class FieldDataCache(object):
)
return res
@property
def _all_usage_ids(self):
"""
Return a set of all usage_ids for the descriptors that this FieldDataCache is caching
against, and well as all asides for those descriptors.
"""
usage_ids = set()
for descriptor in self.descriptors:
usage_ids.add(descriptor.scope_ids.usage_id)
for aside_type in self.asides:
usage_ids.add(AsideUsageKeyV1(descriptor.scope_ids.usage_id, aside_type))
return usage_ids
@property
def _all_block_types(self):
"""
Return a set of all block_types that are cached by this FieldDataCache.
"""
block_types = set()
for descriptor in self.descriptors:
block_types.add(BlockTypeKeyV1(descriptor.entry_point, descriptor.scope_ids.block_type))
for aside_type in self.asides:
block_types.add(BlockTypeKeyV1(XBlockAside.entry_point, aside_type))
return block_types
def _retrieve_fields(self, scope, fields):
"""
Queries the database for all of the fields in the specified scope
@@ -148,7 +184,7 @@ class FieldDataCache(object):
return self._chunked_query(
StudentModule,
'module_state_key__in',
(descriptor.scope_ids.usage_id for descriptor in self.descriptors),
self._all_usage_ids,
course_id=self.course_id,
student=self.user.pk,
)
@@ -156,14 +192,14 @@ class FieldDataCache(object):
return self._chunked_query(
XModuleUserStateSummaryField,
'usage_id__in',
(descriptor.scope_ids.usage_id for descriptor in self.descriptors),
self._all_usage_ids,
field_name__in=set(field.name for field in fields),
)
elif scope == Scope.preferences:
return self._chunked_query(
XModuleStudentPrefsField,
'module_type__in',
set(descriptor.scope_ids.block_type for descriptor in self.descriptors),
self._all_block_types,
student=self.user.pk,
field_name__in=set(field.name for field in fields),
)
@@ -195,7 +231,7 @@ class FieldDataCache(object):
elif key.scope == Scope.user_state_summary:
return (key.scope, key.block_scope_id, key.field_name)
elif key.scope == Scope.preferences:
return (key.scope, key.block_scope_id, key.field_name)
return (key.scope, BlockTypeKeyV1(key.block_family, key.block_scope_id), key.field_name)
elif key.scope == Scope.user_info:
return (key.scope, key.field_name)
@@ -239,31 +275,28 @@ class FieldDataCache(object):
return field_object
if key.scope == Scope.user_state:
# When we start allowing block_scope_ids to be either Locations or Locators,
# this assertion will fail. Fix the code here when that happens!
assert(isinstance(key.block_scope_id, UsageKey))
field_object, _ = StudentModule.objects.get_or_create(
field_object, __ = StudentModule.objects.get_or_create(
course_id=self.course_id,
student_id=key.user_id,
module_state_key=key.block_scope_id,
defaults={
'state': json.dumps({}),
'module_type': key.block_scope_id.category,
'module_type': key.block_scope_id.block_type,
},
)
elif key.scope == Scope.user_state_summary:
field_object, _ = XModuleUserStateSummaryField.objects.get_or_create(
field_object, __ = XModuleUserStateSummaryField.objects.get_or_create(
field_name=key.field_name,
usage_id=key.block_scope_id
)
elif key.scope == Scope.preferences:
field_object, _ = XModuleStudentPrefsField.objects.get_or_create(
field_object, __ = XModuleStudentPrefsField.objects.get_or_create(
field_name=key.field_name,
module_type=key.block_scope_id,
module_type=BlockTypeKeyV1(key.block_family, key.block_scope_id),
student_id=key.user_id,
)
elif key.scope == Scope.user_info:
field_object, _ = XModuleStudentInfoField.objects.get_or_create(
field_object, __ = XModuleStudentInfoField.objects.get_or_create(
field_name=key.field_name,
student_id=key.user_id,
)