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( ...@@ -59,7 +59,9 @@ TEST_STATIC_TAB1_CONTENT = dedent(
TEST_STATIC_TAB2_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): ...@@ -858,6 +858,24 @@ class CoursesApiTests(ModuleStoreTestCase):
self.assertEqual(tabs[1]['id'], u'readings') self.assertEqual(tabs[1]['id'], u'readings')
self.assertEqual(tabs[1]['content'], self.static_tab2.data) 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): def test_static_tab_list_get_invalid_course(self):
#try a bogus course_id to test failure case #try a bogus course_id to test failure case
test_uri = self.base_courses_uri + '/' + self.test_bogus_course_id + '/static_tabs' test_uri = self.base_courses_uri + '/' + self.test_bogus_course_id + '/static_tabs'
...@@ -873,6 +891,52 @@ class CoursesApiTests(ModuleStoreTestCase): ...@@ -873,6 +891,52 @@ class CoursesApiTests(ModuleStoreTestCase):
self.assertEqual(tab['id'], u'syllabus') self.assertEqual(tab['id'], u'syllabus')
self.assertEqual(tab['content'], self.static_tab1.data) 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' test_uri = self.base_courses_uri + '/' + self.test_course_id + '/static_tabs/readings'
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -881,6 +945,41 @@ class CoursesApiTests(ModuleStoreTestCase): ...@@ -881,6 +945,41 @@ class CoursesApiTests(ModuleStoreTestCase):
self.assertEqual(tab['id'], u'readings') self.assertEqual(tab['id'], u'readings')
self.assertEqual(tab['content'], self.static_tab2.data) 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): def test_static_tab_detail_get_invalid_course(self):
# try a bogus courseId # try a bogus courseId
test_uri = self.base_courses_uri + '/' + self.test_bogus_course_id + '/static_tabs/syllabus' test_uri = self.base_courses_uri + '/' + self.test_bogus_course_id + '/static_tabs/syllabus'
......
...@@ -10,6 +10,7 @@ from datetime import timedelta ...@@ -10,6 +10,7 @@ from datetime import timedelta
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.cache import cache
from django.db.models import Avg, Count, Max, Min from django.db.models import Avg, Count, Max, Min
from django.http import Http404 from django.http import Http404
from django.utils import timezone from django.utils import timezone
...@@ -52,6 +53,7 @@ from api_manager.users.serializers import UserSerializer, UserCountByCitySeriali ...@@ -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 api_manager.utils import generate_base_uri, str2bool, get_time_series_data, parse_datetime
from .serializers import CourseSerializer from .serializers import CourseSerializer
from .serializers import GradeSerializer, CourseLeadersSerializer, CourseCompletionsLeadersSerializer from .serializers import GradeSerializer, CourseLeadersSerializer, CourseCompletionsLeadersSerializer
from .tasks import cache_static_tab_contents
from progress.serializers import CourseModuleCompletionSerializer from progress.serializers import CourseModuleCompletionSerializer
...@@ -372,6 +374,20 @@ def _get_course_data(request, course_key, course_descriptor, depth=0): ...@@ -372,6 +374,20 @@ def _get_course_data(request, course_key, course_descriptor, depth=0):
return data 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): class CourseContentList(SecureAPIView):
""" """
**Use Case** **Use Case**
...@@ -917,11 +933,10 @@ class CoursesStaticTabsList(SecureAPIView): ...@@ -917,11 +933,10 @@ class CoursesStaticTabsList(SecureAPIView):
tab_data['id'] = tab.url_slug tab_data['id'] = tab.url_slug
tab_data['name'] = tab.name tab_data['name'] = tab.name
if request.GET.get('detail') and request.GET.get('detail') in ['True', 'true']: 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, request,
course_descriptor, course_descriptor,
tab, tab
wrap_xmodule_display=False
) )
tabs.append(tab_data) tabs.append(tab_data)
response_data['tabs'] = tabs response_data['tabs'] = tabs
...@@ -963,11 +978,10 @@ class CoursesStaticTabsDetail(SecureAPIView): ...@@ -963,11 +978,10 @@ class CoursesStaticTabsDetail(SecureAPIView):
if tab.type == 'static_tab' and tab.url_slug == tab_id: if tab.type == 'static_tab' and tab.url_slug == tab_id:
response_data['id'] = tab.url_slug response_data['id'] = tab.url_slug
response_data['name'] = tab.name response_data['name'] = tab.name
response_data['content'] = get_static_tab_contents( response_data['content'] = _get_static_tab_contents(
request, request,
course_descriptor, course_descriptor,
tab, tab
wrap_xmodule_display=False
) )
if not response_data: if not response_data:
return Response({}, status=status.HTTP_404_NOT_FOUND) 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