Files
edx-platform/lms/djangoapps/grades/course_data.py

133 lines
5.2 KiB
Python

"""
Code used to get and cache the requested course-data
"""
from lms.djangoapps.course_blocks.api import get_course_blocks
from openedx.core.djangoapps.content.block_structure.api import get_block_structure_manager
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
from .transformer import GradesTransformer
class CourseData:
"""
Utility access layer to intelligently get and cache the
requested course data as long as at least one property is
provided upon initialization.
This is an in-memory object that maintains its own internal
cache during its lifecycle.
"""
def __init__(self, user, course=None, collected_block_structure=None, structure=None, course_key=None):
if not any((course, collected_block_structure, structure, course_key)):
raise ValueError(
"You must specify one of course, collected_block_structure, structure, or course_key to this method."
)
self.user = user
self._collected_block_structure = collected_block_structure
self._structure = structure
self._course = course
self._course_key = course_key
self._location = None
@property
def course_key(self): # lint-amnesty, pylint: disable=missing-function-docstring
if not self._course_key:
if self._course:
self._course_key = self._course.id
else:
structure = self.effective_structure
self._course_key = structure.root_block_usage_key.course_key
return self._course_key
@property
def location(self): # lint-amnesty, pylint: disable=missing-function-docstring
if not self._location:
structure = self.effective_structure
if structure:
self._location = structure.root_block_usage_key
elif self._course:
self._location = self._course.location
else:
self._location = modulestore().make_course_usage_key(self.course_key)
return self._location
@property
def structure(self): # lint-amnesty, pylint: disable=missing-function-docstring
if self._structure is None:
self._structure = get_course_blocks(
self.user,
self.location,
collected_block_structure=self._collected_block_structure,
)
return self._structure
@property
def collected_structure(self):
if self._collected_block_structure is None:
self._collected_block_structure = get_block_structure_manager(self.course_key).get_collected()
return self._collected_block_structure
@property
def course(self):
if not self._course:
self._course = modulestore().get_course(self.course_key)
return self._course
@property
def grading_policy_hash(self): # lint-amnesty, pylint: disable=missing-function-docstring
structure = self.effective_structure
if structure:
return structure.get_transformer_block_field(
structure.root_block_usage_key,
GradesTransformer,
'grading_policy_hash',
)
else:
return GradesTransformer.grading_policy_hash(self.course)
@property
def version(self):
structure = self.effective_structure
course_block = structure[self.location] if structure else self.course
return getattr(course_block, 'course_version', None)
@property
def edited_on(self): # lint-amnesty, pylint: disable=missing-function-docstring
# get course block from structure only; subtree_edited_on field on modulestore's course block isn't optimized.
structure = self.effective_structure
if structure:
course_block = structure[self.location]
return getattr(course_block, 'subtree_edited_on', None)
def __str__(self):
"""
Return human-readable string representation.
"""
return f'Course: course_key: {self.course_key}'
def full_string(self): # lint-amnesty, pylint: disable=missing-function-docstring
if self.effective_structure:
return 'Course: course_key: {}, version: {}, edited_on: {}, grading_policy: {}'.format(
self.course_key, self.version, self.edited_on, self.grading_policy_hash,
)
else:
return f'Course: course_key: {self.course_key}, empty course structure'
@property
def effective_structure(self):
"""
Get whichever course block structure is already loaded, if any.
This may give either the user-specific course structure or the generic
structure, depending on which is cached at the moment. Because of that,
this should only be used for queries related to the root block of the
course, which will always exist in either structure.
For anything else, such as queries involving course sections or blocks,
use either .structure or .collected_structure to explicitly state
whether you want the user-specific version of the course or not.
"""
return self._structure or self._collected_block_structure