Beispiel #1
0
 def _update_cache(cls, course_key, visible_blocks):
     """
     Adds a specific set of visible blocks to the request cache.
     This assumes that prefetch has already been called.
     """
     get_cache(cls.CACHE_NAMESPACE)[cls._cache_key(course_key)].update(
         {visible_block.hashed: visible_block for visible_block in visible_blocks}
     )
Beispiel #2
0
 def _update_cache(cls, course_key, visible_blocks):
     """
     Adds a specific set of visible blocks to the request cache.
     This assumes that prefetch has already been called.
     """
     get_cache(cls.CACHE_NAMESPACE)[cls._cache_key(course_key)].update(
         {visible_block.hashed: visible_block for visible_block in visible_blocks}
     )
Beispiel #3
0
    def __enter__(self):

        connection = transaction.get_connection(self.using)

        cache = request_cache.get_cache(OUTER_ATOMIC_CACHE_NAME)

        # By default it is enabled.
        enable = True
        # If name is set it is only enabled if requested by calling enable_named_outer_atomic().
        if self.name:
            enable = cache.get(self.name, False)

        if enable:
            # TestCase setup nests tests in two atomics - one for the test class and one for the individual test.
            # The outermost atomic starts a transaction - so does not have a savepoint.
            # The inner atomic starts a savepoint around the test.
            # So, for tests only, there should be exactly one savepoint_id and two atomic_for_testcase_calls.
            # atomic_for_testcase_calls below is added in a monkey-patch for tests only.
            if self.ALLOW_NESTED and (self.atomic_for_testcase_calls - len(connection.savepoint_ids)) < 1:
                raise transaction.TransactionManagementError('Cannot be inside an atomic block.')

            # Otherwise, this shouldn't be nested in any atomic block.
            if not self.ALLOW_NESTED and connection.in_atomic_block:
                raise transaction.TransactionManagementError('Cannot be inside an atomic block.')

            # This will set the transaction isolation level to READ COMMITTED for the next transaction.
            if self.read_committed is True:
                if connection.vendor == 'mysql':
                    cursor = connection.cursor()
                    cursor.execute("SET TRANSACTION ISOLATION LEVEL READ COMMITTED")

        super(OuterAtomic, self).__enter__()
Beispiel #4
0
def bulk_cache_cohorts(course_key, users):
    """
    Pre-fetches and caches the cohort assignments for the
    given users, for later fast retrieval by get_cohort.
    """
    # before populating the cache with another bulk set of data,
    # remove previously cached entries to keep memory usage low.
    request_cache.clear_cache(COHORT_CACHE_NAMESPACE)
    cache = request_cache.get_cache(COHORT_CACHE_NAMESPACE)

    if is_course_cohorted(course_key):
        cohorts_by_user = {
            membership.user: membership
            for membership in CohortMembership.objects.filter(
                user__in=users, course_id=course_key).select_related('user')
        }
        for user, membership in cohorts_by_user.iteritems():
            cache[_cohort_cache_key(user.id,
                                    course_key)] = membership.course_user_group
        uncohorted_users = filter(lambda u: u not in cohorts_by_user, users)
    else:
        uncohorted_users = users

    for user in uncohorted_users:
        cache[_cohort_cache_key(user.id, course_key)] = None
Beispiel #5
0
    def process_response(self, __, response):
        """
        If request is from mobile native app, then add version related info to response headers.

        Returns:
            Http response: with additional headers;
                1. EDX-APP-LATEST-VERSION; if user app version < latest available version
                2. EDX-APP-VERSION-LAST-SUPPORTED-DATE; if user app version < min supported version and
                   timestamp < expiry of that version
        """
        request_cache_dict = request_cache.get_cache(self.REQUEST_CACHE_NAME)
        if request_cache_dict:
            last_supported_date = request_cache_dict[
                self.LAST_SUPPORTED_DATE_HEADER]
            if last_supported_date != self.NO_LAST_SUPPORTED_DATE:
                response[
                    self.
                    LAST_SUPPORTED_DATE_HEADER] = last_supported_date.isoformat(
                    )
            latest_version = request_cache_dict[self.LATEST_VERSION_HEADER]
            user_app_version = request_cache_dict[self.USER_APP_VERSION]
            if (latest_version != self.NO_LATEST_VERSION
                    and parsed_version(user_app_version) <
                    parsed_version(latest_version)):
                response[self.LATEST_VERSION_HEADER] = latest_version
        return response
Beispiel #6
0
def get_group_info_for_cohort(cohort, use_cached=False):
    """
    Get the ids of the group and partition to which this cohort has been linked
    as a tuple of (int, int).

    If the cohort has not been linked to any group/partition, both values in the
    tuple will be None.

    The partition group info is cached for the duration of a request. Pass
    use_cached=True to use the cached value instead of fetching from the
    database.
    """
    cache = request_cache.get_cache(u"cohorts.get_group_info_for_cohort")
    cache_key = unicode(cohort.id)

    if use_cached and cache_key in cache:
        return cache[cache_key]

    cache.pop(cache_key, None)

    try:
        partition_group = CourseUserGroupPartitionGroup.objects.get(
            course_user_group=cohort)
        return cache.setdefault(
            cache_key,
            (partition_group.group_id, partition_group.partition_id))
    except CourseUserGroupPartitionGroup.DoesNotExist:
        pass

    return cache.setdefault(cache_key, (None, None))
Beispiel #7
0
def get_group_info_for_cohort(cohort, use_cached=False):
    """
    Get the ids of the group and partition to which this cohort has been linked
    as a tuple of (int, int).

    If the cohort has not been linked to any group/partition, both values in the
    tuple will be None.

    The partition group info is cached for the duration of a request. Pass
    use_cached=True to use the cached value instead of fetching from the
    database.
    """
    cache = request_cache.get_cache(u"cohorts.get_group_info_for_cohort")
    cache_key = unicode(cohort.id)

    if use_cached and cache_key in cache:
        return cache[cache_key]

    cache.pop(cache_key, None)

    try:
        partition_group = CourseUserGroupPartitionGroup.objects.get(course_user_group=cohort)
        return cache.setdefault(cache_key, (partition_group.group_id, partition_group.partition_id))
    except CourseUserGroupPartitionGroup.DoesNotExist:
        pass

    return cache.setdefault(cache_key, (None, None))
def get_template_request_context(request=None):
    """
    Returns the template processing context to use for the current request,
    or returns None if there is not a current request.
    """

    if request is None:
        request = get_current_request()

    if request is None:
        return None

    request_cache_dict = request_cache.get_cache('edxmako')
    cache_key = "request_context"
    if cache_key in request_cache_dict:
        return request_cache_dict[cache_key]

    context = RequestContext(request)

    context['is_secure'] = request.is_secure()
    context['site'] = safe_get_host(request)

    # This used to happen when a RequestContext object was initialized but was
    # moved to a different part of the logic when template engines were introduced.
    # Since we are not using template engines we do this here.
    # https://github.com/django/django/commit/37505b6397058bcc3460f23d48a7de9641cd6ef0
    for processor in get_template_context_processors():
        context.update(processor(request))

    request_cache_dict[cache_key] = context

    return context
def get_course_content_milestones(course_id,
                                  content_id,
                                  relationship,
                                  user_id=None):
    """
    Client API operation adapter/wrapper
    Uses the request cache to store all of a user's
    milestones
    """
    if not settings.FEATURES.get('MILESTONES_APP'):
        return []

    if user_id is None:
        return milestones_api.get_course_content_milestones(
            course_id, content_id, relationship)

    request_cache_dict = request_cache.get_cache(REQUEST_CACHE_NAME)
    if user_id not in request_cache_dict:
        request_cache_dict[user_id] = {}

    if relationship not in request_cache_dict[user_id]:
        request_cache_dict[user_id][
            relationship] = milestones_api.get_course_content_milestones(
                course_key=course_id,
                relationship=relationship,
                user={"id": user_id})

    return [
        m for m in request_cache_dict[user_id][relationship]
        if m['content_id'] == unicode(content_id)
    ]
Beispiel #10
0
    def __enter__(self):

        connection = transaction.get_connection(self.using)

        cache = request_cache.get_cache(OUTER_ATOMIC_CACHE_NAME)

        # By default it is enabled.
        enable = True
        # If name is set it is only enabled if requested by calling enable_named_outer_atomic().
        if self.name:
            enable = cache.get(self.name, False)

        if enable:
            # TestCase setup nests tests in two atomics - one for the test class and one for the individual test.
            # The outermost atomic starts a transaction - so does not have a savepoint.
            # The inner atomic starts a savepoint around the test.
            # So, for tests only, there should be exactly one savepoint_id and two atomic_for_testcase_calls.
            # atomic_for_testcase_calls below is added in a monkey-patch for tests only.
            if self.ALLOW_NESTED and (self.atomic_for_testcase_calls - len(connection.savepoint_ids)) < 1:
                raise transaction.TransactionManagementError('Cannot be inside an atomic block.')

            # Otherwise, this shouldn't be nested in any atomic block.
            if not self.ALLOW_NESTED and connection.in_atomic_block:
                raise transaction.TransactionManagementError('Cannot be inside an atomic block.')

            # This will set the transaction isolation level to READ COMMITTED for the next transaction.
            if self.read_committed is True:
                if connection.vendor == 'mysql':
                    cursor = connection.cursor()
                    cursor.execute("SET TRANSACTION ISOLATION LEVEL READ COMMITTED")

        super(OuterAtomic, self).__enter__()
def get_template_request_context(request=None):
    """
    Returns the template processing context to use for the current request,
    or returns None if there is not a current request.
    """

    if request is None:
        request = get_current_request()

    if request is None:
        return None

    request_cache_dict = request_cache.get_cache('edxmako')
    cache_key = "request_context"
    if cache_key in request_cache_dict:
        return request_cache_dict[cache_key]

    context = RequestContext(request)

    context['is_secure'] = request.is_secure()
    context['site'] = safe_get_host(request)

    request_cache_dict[cache_key] = context

    return context
Beispiel #12
0
def user_timezone_locale_prefs(request):
    """
    Checks if request has an authenticated user.
    If so, sends set (or none if unset) time_zone and language prefs.

    This interacts with the DateUtils to either display preferred or attempt to determine
    system/browser set time_zones and languages

    """
    cached_value = request_cache.get_cache(CACHE_NAME)
    if not cached_value:
        user_prefs = {
            'user_timezone': None,
            'user_language': None,
        }
        if hasattr(request, 'user') and request.user.is_authenticated():
            try:
                user_preferences = get_user_preferences(request.user)
            except (UserNotFound, UserAPIInternalError):
                cached_value.update(user_prefs)
            else:
                user_prefs = {
                    key: user_preferences.get(pref_name, None)
                    for key, pref_name in RETRIEVABLE_PREFERENCES.iteritems()
                }

        cached_value.update(user_prefs)
    return cached_value
def get_template_request_context(request=None):
    """
    Returns the template processing context to use for the current request,
    or returns None if there is not a current request.
    """

    if request is None:
        request = get_current_request()

    if request is None:
        return None

    request_cache_dict = request_cache.get_cache('edxmako')
    cache_key = "request_context"
    if cache_key in request_cache_dict:
        return request_cache_dict[cache_key]

    context = RequestContext(request)

    context['is_secure'] = request.is_secure()
    context['site'] = safe_get_host(request)

    request_cache_dict[cache_key] = context

    return context
Beispiel #14
0
def get_template_request_context(request=None):
    """
    Returns the template processing context to use for the current request,
    or returns None if there is not a current request.
    """

    if request is None:
        request = get_current_request()

    if request is None:
        return None

    request_cache_dict = request_cache.get_cache('edxmako')
    cache_key = "request_context"
    if cache_key in request_cache_dict:
        return request_cache_dict[cache_key]

    context = RequestContext(request)

    context['is_secure'] = request.is_secure()
    context['site'] = safe_get_host(request)

    # This used to happen when a RequestContext object was initialized but was
    # moved to a different part of the logic when template engines were introduced.
    # Since we are not using template engines we do this here.
    # https://github.com/django/django/commit/37505b6397058bcc3460f23d48a7de9641cd6ef0
    for processor in get_template_context_processors():
        context.update(processor(request))

    request_cache_dict[cache_key] = context

    return context
Beispiel #15
0
def create_new_event_transaction_id():
    """
    Sets the event transaction id to a newly-
    generated UUID.
    """
    new_id = uuid4()
    get_cache('event_transaction')['id'] = new_id
    return new_id
Beispiel #16
0
def set_event_transaction_id(new_id):
    """
    Sets the event transaction id to a UUID object
    generated from new_id.
    new_id must be a parsable string version
    of a UUID.
    """
    get_cache('event_transaction')['id'] = UUID(new_id)
Beispiel #17
0
 def prefetch(cls, course_id, users):
     """
     Prefetches grades for the given users for the given course.
     """
     get_cache(cls.CACHE_NAMESPACE)[cls._cache_key(course_id)] = {
         grade.user_id: grade
         for grade in
         cls.objects.filter(user_id__in=[user.id for user in users], course_id=course_id)
     }
Beispiel #18
0
 def _initialize_cache(cls, course_key):
     """
     Prefetches visible blocks for the given course and stores in the cache.
     Returns a dictionary mapping hashes of these block records to the
     block record objects.
     """
     prefetched = {record.hashed: record for record in cls.objects.filter(course_id=course_key)}
     get_cache(cls.CACHE_NAMESPACE)[cls._cache_key(course_key)] = prefetched
     return prefetched
Beispiel #19
0
 def prefetch(cls, course_id, users):
     """
     Prefetches grades for the given users for the given course.
     """
     get_cache(cls.CACHE_NAMESPACE)[cls._cache_key(course_id)] = {
         grade.user_id: grade
         for grade in
         cls.objects.filter(user_id__in=[user.id for user in users], course_id=course_id)
     }
Beispiel #20
0
 def _initialize_cache(cls, course_key):
     """
     Prefetches visible blocks for the given course and stores in the cache.
     Returns a dictionary mapping hashes of these block records to the
     block record objects.
     """
     prefetched = {record.hashed: record for record in cls.objects.filter(course_id=course_key)}
     get_cache(cls.CACHE_NAMESPACE)[cls._cache_key(course_key)] = prefetched
     return prefetched
Beispiel #21
0
    def prefetch(cls, users):
        roles_by_user = defaultdict(set)
        get_cache(cls.CACHE_NAMESPACE)[cls.CACHE_KEY] = roles_by_user

        for role in CourseAccessRole.objects.filter(user__in=users).select_related('user'):
            roles_by_user[role.user.id].add(role)

        users_without_roles = filter(lambda u: u.id not in roles_by_user, users)
        for user in users_without_roles:
            roles_by_user[user.id] = set()
Beispiel #22
0
 def get_override(cls, user_id, usage_key):
     prefetch_values = get_cache(cls._CACHE_NAMESPACE).get((user_id, str(usage_key.course_key)), None)
     if prefetch_values is not None:
         return prefetch_values.get(usage_key)
     try:
         return cls.objects.get(
             grade__user_id=user_id,
             grade__course_id=usage_key.course_key,
             grade__usage_key=usage_key,
         )
     except PersistentSubsectionGradeOverride.DoesNotExist:
         pass
Beispiel #23
0
    def prefetch(cls, users):
        roles_by_user = defaultdict(set)
        get_cache(cls.CACHE_NAMESPACE)[cls.CACHE_KEY] = roles_by_user

        for role in CourseAccessRole.objects.filter(
                user__in=users).select_related('user__id'):
            roles_by_user[role.user.id].add(role)

        users_without_roles = filter(lambda u: u.id not in roles_by_user,
                                     users)
        for user in users_without_roles:
            roles_by_user[user.id] = set()
Beispiel #24
0
 def get_override(cls, user_id, usage_key):
     prefetch_values = get_cache(cls._CACHE_NAMESPACE).get(
         (user_id, str(usage_key.course_key)), None)
     if prefetch_values is not None:
         return prefetch_values.get(usage_key)
     try:
         return cls.objects.get(
             grade__user_id=user_id,
             grade__course_id=usage_key.course_key,
             grade__usage_key=usage_key,
         )
     except PersistentSubsectionGradeOverride.DoesNotExist:
         pass
Beispiel #25
0
    def bulk_read(cls, course_key):
        """
        Reads and returns all visible block records for the given course from
        the cache.  The cache is initialize with the visible blocks for this
        course if no entry currently exists.has no entry for this course,
        the cache is updated.

        Arguments:
            course_key: The course identifier for the desired records
        """
        prefetched = get_cache(cls.CACHE_NAMESPACE).get(cls._cache_key(course_key))
        if not prefetched:
            prefetched = cls._initialize_cache(course_key)
        return prefetched
Beispiel #26
0
    def bulk_read(cls, course_key):
        """
        Reads and returns all visible block records for the given course from
        the cache.  The cache is initialize with the visible blocks for this
        course if no entry currently exists.has no entry for this course,
        the cache is updated.

        Arguments:
            course_key: The course identifier for the desired records
        """
        prefetched = get_cache(cls.CACHE_NAMESPACE).get(cls._cache_key(course_key))
        if not prefetched:
            prefetched = cls._initialize_cache(course_key)
        return prefetched
Beispiel #27
0
 def cached_get_or_create(cls, blocks):
     prefetched = get_cache(cls._CACHE_NAMESPACE).get(cls._cache_key(blocks.course_key))
     if prefetched is not None:
         model = prefetched.get(blocks.hash_value)
         if not model:
             model = cls.objects.create(
                 hashed=blocks.hash_value, blocks_json=blocks.json_value, course_id=blocks.course_key,
             )
             cls._update_cache(blocks.course_key, [model])
     else:
         model, _ = cls.objects.get_or_create(
             hashed=blocks.hash_value,
             defaults={u'blocks_json': blocks.json_value, u'course_id': blocks.course_key},
         )
     return model
Beispiel #28
0
def get_current_ccx(course_key):
    """
    Return the ccx that is active for this course.

    course_key is expected to be an instance of an opaque CourseKey, a
    ValueError is raised if this expectation is not met.
    """
    if not isinstance(course_key, CourseKey):
        raise ValueError("get_current_ccx requires a CourseKey instance")

    if not isinstance(course_key, CCXLocator):
        return None

    ccx_cache = request_cache.get_cache('ccx')
    if course_key not in ccx_cache:
        ccx_cache[course_key] = CustomCourseForEdX.objects.get(pk=course_key.ccx)

    return ccx_cache[course_key]
Beispiel #29
0
def get_current_ccx(course_key):
    """
    Return the ccx that is active for this course.

    course_key is expected to be an instance of an opaque CourseKey, a
    ValueError is raised if this expectation is not met.
    """
    if not isinstance(course_key, CourseKey):
        raise ValueError("get_current_ccx requires a CourseKey instance")

    if not isinstance(course_key, CCXLocator):
        return None

    ccx_cache = request_cache.get_cache('ccx')
    if course_key not in ccx_cache:
        ccx_cache[course_key] = CustomCourseForEdX.objects.get(pk=course_key.ccx)

    return ccx_cache[course_key]
Beispiel #30
0
def _get_overrides_for_ccx(ccx):
    """
    Returns a dictionary mapping field name to overriden value for any
    overrides set on this block for this CCX.
    """
    overrides_cache = request_cache.get_cache('ccx-overrides')

    if ccx not in overrides_cache:
        overrides = {}
        query = CcxFieldOverride.objects.filter(ccx=ccx, )

        for override in query:
            block_overrides = overrides.setdefault(override.location, {})
            block_overrides[override.field] = json.loads(override.value)

        overrides_cache[ccx] = overrides

    return overrides_cache[ccx]
Beispiel #31
0
    def prefetch(cls, course_id, users):
        """
        Prefetches the value of the course tags for the specified users
        for the specified course_id.

        Args:
            users: iterator of User objects
            course_id: course identifier (CourseKey)

        Returns:
            course_tags: a dict of dicts,
                where the primary key is the user's id
                and the secondary key is the course tag's key
        """
        course_tags = defaultdict(dict)
        for tag in UserCourseTag.objects.filter(user__in=users, course_id=course_id).select_related('user'):
            course_tags[tag.user.id][tag.key] = tag.value
        get_cache(cls.CACHE_NAMESPACE)[cls._cache_key(course_id)] = course_tags
Beispiel #32
0
    def prefetch(cls, course_id, users):
        """
        Prefetches the value of the course tags for the specified users
        for the specified course_id.

        Args:
            users: iterator of User objects
            course_id: course identifier (CourseKey)

        Returns:
            course_tags: a dict of dicts,
                where the primary key is the user's id
                and the secondary key is the course tag's key
        """
        course_tags = defaultdict(dict)
        for tag in UserCourseTag.objects.filter(user__in=users, course_id=course_id).select_related('user__id'):
            course_tags[tag.user.id][tag.key] = tag.value
        get_cache(cls.CACHE_NAMESPACE)[cls._cache_key(course_id)] = course_tags
Beispiel #33
0
    def _get_version_info(self, request):
        """
        Gets and Sets version related info in mem cache and request cache; and returns a dict of it.

        It sets request cache data for last_supported_date and latest_version with memcached values if exists against
        user app properties else computes the values for specific platform and sets it in both memcache (for next
        server interaction from same app version/platform) and request cache

        Returns:
            dict: Containing app version info
        """
        user_agent = request.META.get('HTTP_USER_AGENT')
        if user_agent:
            platform = self._get_platform(request, user_agent)
            if platform:
                request_cache_dict = request_cache.get_cache(
                    self.REQUEST_CACHE_NAME)
                request_cache_dict[self.USER_APP_VERSION] = platform.version
                last_supported_date_cache_key = self._get_cache_key_name(
                    self.LAST_SUPPORTED_DATE_HEADER, platform.version)
                latest_version_cache_key = self._get_cache_key_name(
                    self.LATEST_VERSION_HEADER, platform.NAME)
                cached_data = cache.get_many(
                    [last_supported_date_cache_key, latest_version_cache_key])

                last_supported_date = cached_data.get(
                    last_supported_date_cache_key)
                if not last_supported_date:
                    last_supported_date = self._get_last_supported_date(
                        platform.NAME, platform.version)
                    cache.set(last_supported_date_cache_key,
                              last_supported_date, self.CACHE_TIMEOUT)
                request_cache_dict[
                    self.LAST_SUPPORTED_DATE_HEADER] = last_supported_date

                latest_version = cached_data.get(latest_version_cache_key)
                if not latest_version:
                    latest_version = self._get_latest_version(platform.NAME)
                    cache.set(latest_version_cache_key, latest_version,
                              self.CACHE_TIMEOUT)
                request_cache_dict[self.LATEST_VERSION_HEADER] = latest_version

                return request_cache_dict
Beispiel #34
0
def enable_named_outer_atomic(*names):
    """
    Enable outer_atomics with names.

    Can be used either as a decorator or a context manager.
    See docstring of outer_atomic for details.

    Arguments:
        names (variable-lenght argument list): Names of outer_atomics.
    """
    if len(names) == 0:
        raise ValueError("At least one name must be specified.")

    cache = request_cache.get_cache(OUTER_ATOMIC_CACHE_NAME)

    for name in names:
        cache[name] = True
    yield
    for name in names:
        del cache[name]
Beispiel #35
0
def enable_named_outer_atomic(*names):
    """
    Enable outer_atomics with names.

    Can be used either as a decorator or a context manager.
    See docstring of outer_atomic for details.

    Arguments:
        names (variable-lenght argument list): Names of outer_atomics.
    """
    if len(names) == 0:
        raise ValueError("At least one name must be specified.")

    cache = request_cache.get_cache(OUTER_ATOMIC_CACHE_NAME)

    for name in names:
        cache[name] = True
    yield
    for name in names:
        del cache[name]
Beispiel #36
0
def _get_overrides_for_ccx(ccx):
    """
    Returns a dictionary mapping field name to overriden value for any
    overrides set on this block for this CCX.
    """
    overrides_cache = request_cache.get_cache('ccx-overrides')

    if ccx not in overrides_cache:
        overrides = {}
        query = CcxFieldOverride.objects.filter(
            ccx=ccx,
        )

        for override in query:
            block_overrides = overrides.setdefault(override.location, {})
            block_overrides[override.field] = json.loads(override.value)

        overrides_cache[ccx] = overrides

    return overrides_cache[ccx]
Beispiel #37
0
    def read(cls, user_id, course_id):
        """
        Reads a grade from database

        Arguments:
            user_id: The user associated with the desired grade
            course_id: The id of the course associated with the desired grade

        Raises PersistentCourseGrade.DoesNotExist if applicable
        """
        try:
            prefetched_grades = get_cache(cls.CACHE_NAMESPACE)[cls._cache_key(course_id)]
            try:
                return prefetched_grades[user_id]
            except KeyError:
                # user's grade is not in the prefetched list, so
                # assume they have no grade
                raise cls.DoesNotExist
        except KeyError:
            # grades were not prefetched for the course, so fetch it
            return cls.objects.get(user_id=user_id, course_id=course_id)
Beispiel #38
0
    def read(cls, user_id, course_id):
        """
        Reads a grade from database

        Arguments:
            user_id: The user associated with the desired grade
            course_id: The id of the course associated with the desired grade

        Raises PersistentCourseGrade.DoesNotExist if applicable
        """
        try:
            prefetched_grades = get_cache(cls.CACHE_NAMESPACE)[cls._cache_key(course_id)]
            try:
                return prefetched_grades[user_id]
            except KeyError:
                # user's grade is not in the prefetched list, so
                # assume they have no grade
                raise cls.DoesNotExist
        except KeyError:
            # grades were not prefetched for the course, so fetch it
            return cls.objects.get(user_id=user_id, course_id=course_id)
Beispiel #39
0
    def process_response(self, __, response):
        """
        If request is from mobile native app, then add version related info to response headers.

        Returns:
            Http response: with additional headers;
                1. EDX-APP-LATEST-VERSION; if user app version < latest available version
                2. EDX-APP-VERSION-LAST-SUPPORTED-DATE; if user app version < min supported version and
                   timestamp < expiry of that version
        """
        request_cache_dict = request_cache.get_cache(self.REQUEST_CACHE_NAME)
        if request_cache_dict:
            last_supported_date = request_cache_dict[self.LAST_SUPPORTED_DATE_HEADER]
            if last_supported_date != self.NO_LAST_SUPPORTED_DATE:
                response[self.LAST_SUPPORTED_DATE_HEADER] = last_supported_date.isoformat()
            latest_version = request_cache_dict[self.LATEST_VERSION_HEADER]
            user_app_version = request_cache_dict[self.USER_APP_VERSION]
            if (latest_version != self.NO_LATEST_VERSION and
                    parsed_version(user_app_version) < parsed_version(latest_version)):
                response[self.LATEST_VERSION_HEADER] = latest_version
        return response
Beispiel #40
0
 def cached_get_or_create(cls, blocks):
     prefetched = get_cache(cls._CACHE_NAMESPACE).get(
         cls._cache_key(blocks.course_key))
     if prefetched is not None:
         model = prefetched.get(blocks.hash_value)
         if not model:
             model = cls.objects.create(
                 hashed=blocks.hash_value,
                 blocks_json=blocks.json_value,
                 course_id=blocks.course_key,
             )
             cls._update_cache(blocks.course_key, [model])
     else:
         model, _ = cls.objects.get_or_create(
             hashed=blocks.hash_value,
             defaults={
                 u'blocks_json': blocks.json_value,
                 u'course_id': blocks.course_key
             },
         )
     return model
Beispiel #41
0
    def _get_version_info(self, request):
        """
        Gets and Sets version related info in mem cache and request cache; and returns a dict of it.

        It sets request cache data for last_supported_date and latest_version with memcached values if exists against
        user app properties else computes the values for specific platform and sets it in both memcache (for next
        server interaction from same app version/platform) and request cache

        Returns:
            dict: Containing app version info
        """
        user_agent = request.META.get('HTTP_USER_AGENT')
        if user_agent:
            platform = self._get_platform(request, user_agent)
            if platform:
                request_cache_dict = request_cache.get_cache(self.REQUEST_CACHE_NAME)
                request_cache_dict[self.USER_APP_VERSION] = platform.version
                last_supported_date_cache_key = self._get_cache_key_name(
                    self.LAST_SUPPORTED_DATE_HEADER,
                    platform.version
                )
                latest_version_cache_key = self._get_cache_key_name(self.LATEST_VERSION_HEADER, platform.NAME)
                cached_data = cache.get_many([last_supported_date_cache_key, latest_version_cache_key])

                last_supported_date = cached_data.get(last_supported_date_cache_key)
                if not last_supported_date:
                    last_supported_date = self._get_last_supported_date(platform.NAME, platform.version)
                    cache.set(last_supported_date_cache_key, last_supported_date, self.CACHE_TIMEOUT)
                request_cache_dict[self.LAST_SUPPORTED_DATE_HEADER] = last_supported_date

                latest_version = cached_data.get(latest_version_cache_key)
                if not latest_version:
                    latest_version = self._get_latest_version(platform.NAME)
                    cache.set(latest_version_cache_key, latest_version, self.CACHE_TIMEOUT)
                request_cache_dict[self.LATEST_VERSION_HEADER] = latest_version

                return request_cache_dict
def get_course_content_milestones(course_id, content_id, relationship, user_id=None):
    """
    Client API operation adapter/wrapper
    Uses the request cache to store all of a user's
    milestones
    """
    if not settings.FEATURES.get('MILESTONES_APP'):
        return []

    if user_id is None:
        return milestones_api.get_course_content_milestones(course_id, content_id, relationship)

    request_cache_dict = request_cache.get_cache(REQUEST_CACHE_NAME)
    if user_id not in request_cache_dict:
        request_cache_dict[user_id] = {}

    if relationship not in request_cache_dict[user_id]:
        request_cache_dict[user_id][relationship] = milestones_api.get_course_content_milestones(
            course_key=course_id,
            relationship=relationship,
            user={"id": user_id}
        )

    return [m for m in request_cache_dict[user_id][relationship] if m['content_id'] == content_id]
Beispiel #43
0
def bulk_cache_cohorts(course_key, users):
    """
    Pre-fetches and caches the cohort assignments for the
    given users, for later fast retrieval by get_cohort.
    """
    # before populating the cache with another bulk set of data,
    # remove previously cached entries to keep memory usage low.
    request_cache.clear_cache(COHORT_CACHE_NAMESPACE)
    cache = request_cache.get_cache(COHORT_CACHE_NAMESPACE)

    if is_course_cohorted(course_key):
        cohorts_by_user = {
            membership.user: membership
            for membership in
            CohortMembership.objects.filter(user__in=users, course_id=course_key).select_related('user__id')
        }
        for user, membership in cohorts_by_user.iteritems():
            cache[_cohort_cache_key(user.id, course_key)] = membership.course_user_group
        uncohorted_users = filter(lambda u: u not in cohorts_by_user, users)
    else:
        uncohorted_users = users

    for user in uncohorted_users:
        cache[_cohort_cache_key(user.id, course_key)] = None
Beispiel #44
0
def get_cohort(user, course_key, assign=True, use_cached=False):
    """
    Returns the user's cohort for the specified course.

    The cohort for the user is cached for the duration of a request. Pass
    use_cached=True to use the cached value instead of fetching from the
    database.

    Arguments:
        user: a Django User object.
        course_key: CourseKey
        assign (bool): if False then we don't assign a group to user
        use_cached (bool): Whether to use the cached value or fetch from database.

    Returns:
        A CourseUserGroup object if the course is cohorted and the User has a
        cohort, else None.

    Raises:
       ValueError if the CourseKey doesn't exist.
    """
    cache = request_cache.get_cache(COHORT_CACHE_NAMESPACE)
    cache_key = _cohort_cache_key(user.id, course_key)

    if use_cached and cache_key in cache:
        return cache[cache_key]

    cache.pop(cache_key, None)

    # First check whether the course is cohorted (users shouldn't be in a cohort
    # in non-cohorted courses, but settings can change after course starts)
    if not is_course_cohorted(course_key):
        return cache.setdefault(cache_key, None)

    # If course is cohorted, check if the user already has a cohort.
    try:
        membership = CohortMembership.objects.get(
            course_id=course_key,
            user_id=user.id,
        )
        return cache.setdefault(cache_key, membership.course_user_group)
    except CohortMembership.DoesNotExist:
        # Didn't find the group. If we do not want to assign, return here.
        if not assign:
            # Do not cache the cohort here, because in the next call assign
            # may be True, and we will have to assign the user a cohort.
            return None

    # Otherwise assign the user a cohort.
    try:
        with transaction.atomic():
            # If learner has been pre-registered in a cohort, get that cohort. Otherwise assign to a random cohort.
            course_user_group = None
            for assignment in UnregisteredLearnerCohortAssignments.objects.filter(
                    email=user.email, course_id=course_key):
                course_user_group = assignment.course_user_group
                unregistered_learner = assignment

            if course_user_group:
                unregistered_learner.delete()
            else:
                course_user_group = get_random_cohort(course_key)

            membership = CohortMembership.objects.create(
                user=user,
                course_user_group=course_user_group,
            )

            return cache.setdefault(cache_key, membership.course_user_group)
    except IntegrityError as integrity_error:
        # An IntegrityError is raised when multiple workers attempt to
        # create the same row in one of the cohort model entries:
        # CourseCohort, CohortMembership.
        log.info(
            "HANDLING_INTEGRITY_ERROR: IntegrityError encountered for course '%s' and user '%s': %s",
            course_key, user.id, unicode(integrity_error))
        return get_cohort(user, course_key, assign, use_cached)
Beispiel #45
0
 def clear_cache(cls, course_key):
     """
     Clears the cache of all contents for a given course.
     """
     cache = get_cache(cls.CACHE_NAMESPACE)
     cache.pop(cls._cache_key(course_key), None)
Beispiel #46
0
 def clear_cache(cls, course_key):
     """
     Clears the cache of all contents for a given course.
     """
     cache = get_cache(cls.CACHE_NAMESPACE)
     cache.pop(cls._cache_key(course_key), None)
Beispiel #47
0
 def _get_metrics_cache(cls):
     """
     Get a reference to the part of the request cache wherein we store New
     Relic custom metrics related to the current request.
     """
     return request_cache.get_cache(name=REQUEST_CACHE_KEY)
Beispiel #48
0
 def _get_metrics_cache(cls):
     """
     Get a reference to the part of the request cache wherein we store New
     Relic custom metrics related to the current request.
     """
     return request_cache.get_cache(name=REQUEST_CACHE_KEY)
Beispiel #49
0
def get_event_transaction_type():
    """
    Retrieves the current event transaction type from the request
    cache.
    """
    return get_cache('event_transaction').get('type', None)
Beispiel #50
0
def get_event_transaction_id():
    """
    Retrieves the current event transaction id from the request
    cache.
    """
    return get_cache('event_transaction').get('id', None)
Beispiel #51
0
def set_event_transaction_type(action_type):
    """
    Takes a string and stores it in the request cache
    as the user action type.
    """
    get_cache('event_transaction')['type'] = action_type
Beispiel #52
0
def get_cohort(user, course_key, assign=True, use_cached=False):
    """
    Returns the user's cohort for the specified course.

    The cohort for the user is cached for the duration of a request. Pass
    use_cached=True to use the cached value instead of fetching from the
    database.

    Arguments:
        user: a Django User object.
        course_key: CourseKey
        assign (bool): if False then we don't assign a group to user
        use_cached (bool): Whether to use the cached value or fetch from database.

    Returns:
        A CourseUserGroup object if the course is cohorted and the User has a
        cohort, else None.

    Raises:
       ValueError if the CourseKey doesn't exist.
    """
    cache = request_cache.get_cache(COHORT_CACHE_NAMESPACE)
    cache_key = _cohort_cache_key(user.id, course_key)

    if use_cached and cache_key in cache:
        return cache[cache_key]

    cache.pop(cache_key, None)

    # First check whether the course is cohorted (users shouldn't be in a cohort
    # in non-cohorted courses, but settings can change after course starts)
    if not is_course_cohorted(course_key):
        return cache.setdefault(cache_key, None)

    # If course is cohorted, check if the user already has a cohort.
    try:
        membership = CohortMembership.objects.get(
            course_id=course_key,
            user_id=user.id,
        )
        return cache.setdefault(cache_key, membership.course_user_group)
    except CohortMembership.DoesNotExist:
        # Didn't find the group. If we do not want to assign, return here.
        if not assign:
            # Do not cache the cohort here, because in the next call assign
            # may be True, and we will have to assign the user a cohort.
            return None

    # Otherwise assign the user a cohort.
    try:
        with transaction.atomic():
            # If learner has been pre-registered in a cohort, get that cohort. Otherwise assign to a random cohort.
            course_user_group = None
            for assignment in UnregisteredLearnerCohortAssignments.objects.filter(email=user.email, course_id=course_key):
                course_user_group = assignment.course_user_group
                unregistered_learner = assignment

            if course_user_group:
                unregistered_learner.delete()
            else:
                course_user_group = get_random_cohort(course_key)

            membership = CohortMembership.objects.create(
                user=user,
                course_user_group=course_user_group,
            )

            return cache.setdefault(cache_key, membership.course_user_group)
    except IntegrityError as integrity_error:
        # An IntegrityError is raised when multiple workers attempt to
        # create the same row in one of the cohort model entries:
        # CourseCohort, CohortMembership.
        log.info(
            "HANDLING_INTEGRITY_ERROR: IntegrityError encountered for course '%s' and user '%s': %s",
            course_key, user.id, unicode(integrity_error)
        )
        return get_cohort(user, course_key, assign, use_cached)
Beispiel #53
0
 def get_user_roles(cls, user):
     return get_cache(cls.CACHE_NAMESPACE)[cls.CACHE_KEY][user.id]
Beispiel #54
0
 def _cache(self):
     """
     Namespaced request cache for tracking memory usage.
     """
     return request_cache.get_cache(name='monitoring_memory')