"""
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

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

"""
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

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

Code used to get and cache the requested course-data 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. # lint-amnesty, pylint: disable=missing-function-docstring # lint-amnesty, pylint: disable=missing-function-docstring # lint-amnesty, pylint: disable=missing-function-docstring # lint-amnesty, pylint: disable=missing-function-docstring # 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. Return human-readable string representation. # lint-amnesty, pylint: disable=missing-function-docstring 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.