Commit 500933e7 by Saqib

Merge pull request #651 from edx-solutions/msaqib52/YONK-278

Update static tabs APIs to cache tab contents
parents 86683400 ca77882c
......@@ -59,7 +59,9 @@ TEST_STATIC_TAB1_CONTENT = dedent(
TEST_STATIC_TAB2_CONTENT = dedent(
"""
<div>This is static tab2</div>
<div>
This is static tab2 with content size greater than 200 bytes to test static tab content cache max size limit
</div>
"""
)
......
"""
This file contains celery tasks for api_manager courses
"""
import sys
from celery.task import task # pylint: disable=import-error,no-name-in-module
from django.conf import settings
from django.core.cache import cache
@task(name=u'lms.djangoapps.api_manager.courses.tasks.cache_static_tab_content')
def cache_static_tab_contents(cache_key, contents):
"""
Caches course static tab contents.
"""
cache_expiration = getattr(settings, 'STATIC_TAB_CONTENTS_CACHE_TTL', 60 * 5)
contents_max_size_limit = getattr(settings, 'STATIC_TAB_CONTENTS_CACHE_MAX_SIZE_LIMIT', 4000)
if not sys.getsizeof(contents) > contents_max_size_limit:
cache.set(cache_key, contents, cache_expiration)
......@@ -858,6 +858,24 @@ class CoursesApiTests(ModuleStoreTestCase):
self.assertEqual(tabs[1]['id'], u'readings')
self.assertEqual(tabs[1]['content'], self.static_tab2.data)
# get syllabus tab contents from cache
cache_key = u'course.{course_id}.static.tab.{url_slug}.contents'.format(
course_id=self.test_course_id,
url_slug=tabs[0]['id']
)
tab1_content = cache.get(cache_key)
self.assertTrue(tab1_content is not None)
self.assertEqual(tab1_content, self.static_tab1.data)
# get readings tab contents from cache
cache_key = u'course.{course_id}.static.tab.{url_slug}.contents'.format(
course_id=self.test_course_id,
url_slug=tabs[1]['id']
)
tab2_content = cache.get(cache_key)
self.assertTrue(tab2_content is not None)
self.assertEqual(tab2_content, self.static_tab2.data)
def test_static_tab_list_get_invalid_course(self):
#try a bogus course_id to test failure case
test_uri = self.base_courses_uri + '/' + self.test_bogus_course_id + '/static_tabs'
......@@ -873,6 +891,52 @@ class CoursesApiTests(ModuleStoreTestCase):
self.assertEqual(tab['id'], u'syllabus')
self.assertEqual(tab['content'], self.static_tab1.data)
# now try to get syllabus tab contents from cache
cache_key = u'course.{course_id}.static.tab.{url_slug}.contents'.format(
course_id=self.test_course_id,
url_slug=tab['id']
)
tab_contents = cache.get(cache_key)
self.assertTrue(tab_contents is not None)
self.assertEqual(tab_contents, self.static_tab1.data)
test_uri = self.base_courses_uri + '/' + self.test_course_id + '/static_tabs/readings'
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0)
tab = response.data
self.assertEqual(tab['id'], u'readings')
self.assertEqual(tab['content'], self.static_tab2.data)
# now try to get readings tab contents from cache
cache_key = u'course.{course_id}.static.tab.{url_slug}.contents'.format(
course_id=self.test_course_id,
url_slug=tab['id']
)
tab_contents = cache.get(cache_key)
self.assertTrue(tab_contents is not None)
self.assertEqual(tab_contents, self.static_tab2.data)
@override_settings(STATIC_TAB_CONTENTS_CACHE_MAX_SIZE_LIMIT=200)
def test_static_tab_content_cache_max_size_limit(self):
test_uri = self.base_courses_uri + '/' + self.test_course_id + '/static_tabs/syllabus'
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0)
tab = response.data
self.assertEqual(tab['id'], u'syllabus')
self.assertEqual(tab['content'], self.static_tab1.data)
# try to get syllabus tab contents from cache
cache_key = u'course.{course_id}.static.tab.{url_slug}.contents'.format(
course_id=self.test_course_id,
url_slug=tab['id']
)
tab_contents = cache.get(cache_key)
self.assertTrue(tab_contents is not None)
self.assertEqual(tab_contents, self.static_tab1.data)
# now test static tab with content size greater than 200 bytes
test_uri = self.base_courses_uri + '/' + self.test_course_id + '/static_tabs/readings'
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
......@@ -881,6 +945,41 @@ class CoursesApiTests(ModuleStoreTestCase):
self.assertEqual(tab['id'], u'readings')
self.assertEqual(tab['content'], self.static_tab2.data)
# try to get readings tab contents from cache
cache_key = u'course.{course_id}.static.tab.{url_slug}.contents'.format(
course_id=self.test_course_id,
url_slug=tab['id']
)
tab_contents = cache.get(cache_key)
self.assertTrue(tab_contents is None)
@override_settings(STATIC_TAB_CONTENTS_CACHE_TTL=60)
def test_static_tab_content_cache_time_to_live(self):
test_uri = self.base_courses_uri + '/' + self.test_course_id + '/static_tabs/syllabus'
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0)
tab = response.data
self.assertEqual(tab['id'], u'syllabus')
self.assertEqual(tab['content'], self.static_tab1.data)
cache_key = u'course.{course_id}.static.tab.{url_slug}.contents'.format(
course_id=self.test_course_id,
url_slug=tab['id']
)
# try to get syllabus tab contents from cache
tab_contents = cache.get(cache_key)
self.assertTrue(tab_contents is not None)
self.assertEqual(tab_contents, self.static_tab1.data)
# now reset the time to 1 minute and 5 seconds from now in future to expire cache
reset_time = datetime.now(pytz.UTC) + timedelta(seconds=65)
with freeze_time(reset_time):
# try to get syllabus tab contents from cache again
tab_contents = cache.get(cache_key)
self.assertTrue(tab_contents is None)
def test_static_tab_detail_get_invalid_course(self):
# try a bogus courseId
test_uri = self.base_courses_uri + '/' + self.test_bogus_course_id + '/static_tabs/syllabus'
......
......@@ -10,6 +10,7 @@ from datetime import timedelta
from django.conf import settings
from django.contrib.auth.models import Group, User
from django.core.exceptions import ObjectDoesNotExist
from django.core.cache import cache
from django.db.models import Avg, Count, Max, Min
from django.http import Http404
from django.utils import timezone
......@@ -52,6 +53,7 @@ from api_manager.users.serializers import UserSerializer, UserCountByCitySeriali
from api_manager.utils import generate_base_uri, str2bool, get_time_series_data, parse_datetime
from .serializers import CourseSerializer
from .serializers import GradeSerializer, CourseLeadersSerializer, CourseCompletionsLeadersSerializer
from .tasks import cache_static_tab_contents
from progress.serializers import CourseModuleCompletionSerializer
......@@ -372,6 +374,20 @@ def _get_course_data(request, course_key, course_descriptor, depth=0):
return data
def _get_static_tab_contents(request, course, tab):
"""
Wrapper around get_static_tab_contents to cache contents for the given static tab
"""
cache_key = u'course.{course_id}.static.tab.{url_slug}.contents'.format(course_id=course.id, url_slug=tab.url_slug)
contents = cache.get(cache_key)
if contents is None:
contents = get_static_tab_contents(request, course, tab, wrap_xmodule_display=False)
cache_static_tab_contents.delay(cache_key, contents)
return contents
class CourseContentList(SecureAPIView):
"""
**Use Case**
......@@ -917,11 +933,10 @@ class CoursesStaticTabsList(SecureAPIView):
tab_data['id'] = tab.url_slug
tab_data['name'] = tab.name
if request.GET.get('detail') and request.GET.get('detail') in ['True', 'true']:
tab_data['content'] = get_static_tab_contents(
tab_data['content'] = _get_static_tab_contents(
request,
course_descriptor,
tab,
wrap_xmodule_display=False
tab
)
tabs.append(tab_data)
response_data['tabs'] = tabs
......@@ -963,11 +978,10 @@ class CoursesStaticTabsDetail(SecureAPIView):
if tab.type == 'static_tab' and tab.url_slug == tab_id:
response_data['id'] = tab.url_slug
response_data['name'] = tab.name
response_data['content'] = get_static_tab_contents(
response_data['content'] = _get_static_tab_contents(
request,
course_descriptor,
tab,
wrap_xmodule_display=False
tab
)
if not response_data:
return Response({}, status=status.HTTP_404_NOT_FOUND)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment