Cache Enrollment state for (user, course) in request cache.

This commit is contained in:
Nimisha Asthagiri
2016-09-11 19:47:27 -04:00
parent 2251097c56
commit 5822919549
8 changed files with 100 additions and 55 deletions

View File

@@ -10,7 +10,7 @@ file and check it in at the same time as your model changes. To do that,
2. ./manage.py lms schemamigration student --auto description_of_your_change
3. Add the migration file created in edx-platform/common/djangoapps/student/migrations/
"""
from collections import defaultdict, OrderedDict
from collections import defaultdict, OrderedDict, namedtuple
from datetime import datetime, timedelta
from functools import total_ordering
import hashlib
@@ -54,6 +54,7 @@ from enrollment.api import _default_course_mode
import lms.lib.comment_client as cc
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client, ECOMMERCE_DATE_FORMAT
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
import request_cache
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from util.model_utils import emit_field_changed_events, get_changed_fields_dict
from util.query import use_read_replica_if_available
@@ -974,6 +975,12 @@ class CourseEnrollmentManager(models.Manager):
)
# Named tuple for fields pertaining to the state of
# CourseEnrollment for a user in a course. This type
# is used to cache the state in the request cache.
CourseEnrollmentState = namedtuple('CourseEnrollmentState', 'mode, is_active')
class CourseEnrollment(models.Model):
"""
Represents a Student's Enrollment record for a single Course. You should
@@ -1120,6 +1127,11 @@ class CourseEnrollment(models.Model):
if activation_changed or mode_changed:
self.save()
self._update_enrollment_in_request_cache(
self.user,
self.course_id,
CourseEnrollmentState(self.mode, self.is_active),
)
if activation_changed:
if self.is_active:
@@ -1389,12 +1401,9 @@ class CourseEnrollment(models.Model):
"""
if not user.is_authenticated():
return False
try:
record = cls.objects.get(user=user, course_id=course_key)
return record.is_active
except cls.DoesNotExist:
return False
else:
enrollment_state = cls._get_enrollment_state(user, course_key)
return enrollment_state.is_active or False
@classmethod
def is_enrolled_by_partial(cls, user, course_id_partial):
@@ -1436,11 +1445,8 @@ class CourseEnrollment(models.Model):
and is_active is whether the enrollment is active.
Returns (None, None) if the courseenrollment record does not exist.
"""
try:
record = cls.objects.get(user=user, course_id=course_id)
return (record.mode, record.is_active)
except cls.DoesNotExist:
return (None, None)
enrollment_state = cls._get_enrollment_state(user, course_id)
return enrollment_state.mode, enrollment_state.is_active
@classmethod
def enrollments_for_user(cls, user):
@@ -1593,6 +1599,45 @@ class CourseEnrollment(models.Model):
"""
return cls.COURSE_ENROLLMENT_CACHE_KEY.format(user_id, unicode(course_key))
@classmethod
def _get_enrollment_state(cls, user, course_key):
"""
Returns the CourseEnrollmentState for the given user
and course_key, caching the result for later retrieval.
"""
enrollment_state = cls._get_enrollment_in_request_cache(user, course_key)
if not enrollment_state:
try:
record = cls.objects.get(user=user, course_id=course_key)
enrollment_state = CourseEnrollmentState(record.mode, record.is_active)
except cls.DoesNotExist:
enrollment_state = CourseEnrollmentState(None, None)
cls._update_enrollment_in_request_cache(user, course_key, enrollment_state)
return enrollment_state
@classmethod
def _get_mode_active_request_cache(cls):
"""
Returns the request-specific cache for CourseEnrollment
"""
return request_cache.get_cache('CourseEnrollment.mode_and_active')
@classmethod
def _get_enrollment_in_request_cache(cls, user, course_key):
"""
Returns the cached value (CourseEnrollmentState) for the user's
enrollment in the request cache. If not cached, returns None.
"""
return cls._get_mode_active_request_cache().get((user.id, course_key))
@classmethod
def _update_enrollment_in_request_cache(cls, user, course_key, enrollment_state):
"""
Updates the cached value for the user's enrollment in the
request cache.
"""
cls._get_mode_active_request_cache()[(user.id, course_key)] = enrollment_state
@receiver(models.signals.post_save, sender=CourseEnrollment)
@receiver(models.signals.post_delete, sender=CourseEnrollment)

View File

@@ -6,7 +6,6 @@ from datetime import datetime, timedelta
import json
import logging
import unittest
from urlparse import urljoin
import ddt
from django.conf import settings
@@ -30,6 +29,7 @@ from certificates.tests.factories import GeneratedCertificateFactory # pylint:
from config_models.models import cache
from course_modes.models import CourseMode
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangoapps.programs.tests import factories as programs_factories
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
@@ -589,7 +589,7 @@ class EnrollmentEventTestMixin(EventTestMixin):
self.mock_tracker.reset_mock()
class EnrollInCourseTest(EnrollmentEventTestMixin, TestCase):
class EnrollInCourseTest(EnrollmentEventTestMixin, CacheIsolationTestCase):
"""Tests enrolling and unenrolling in courses."""
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')