diff --git a/openedx/core/djangoapps/catalog/migrations/0005_catalogintegration_long_term_cache_ttl.py b/openedx/core/djangoapps/catalog/migrations/0005_catalogintegration_long_term_cache_ttl.py new file mode 100644 index 0000000000..e0ee395c77 --- /dev/null +++ b/openedx/core/djangoapps/catalog/migrations/0005_catalogintegration_long_term_cache_ttl.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('catalog', '0004_auto_20170616_0618'), + ] + + operations = [ + migrations.AddField( + model_name='catalogintegration', + name='long_term_cache_ttl', + field=models.PositiveIntegerField(default=86400, help_text='Specified in seconds (defaults to 86400s, 24hr). In some cases the cache does needs to be refreshed less frequently. Enable long term caching of API responses by setting this to a value greater than 0.', verbose_name='Long Term Cache Time To Live'), + ), + ] diff --git a/openedx/core/djangoapps/catalog/models.py b/openedx/core/djangoapps/catalog/models.py index 05d2e50e1f..9ee5b263b6 100644 --- a/openedx/core/djangoapps/catalog/models.py +++ b/openedx/core/djangoapps/catalog/models.py @@ -29,6 +29,15 @@ class CatalogIntegration(ConfigurationModel): ) ) + long_term_cache_ttl = models.PositiveIntegerField( + verbose_name=_('Long Term Cache Time To Live'), + default=86400, + help_text=_( + 'Specified in seconds (defaults to 86400s, 24hr). In some cases the cache does needs to be refreshed ' + 'less frequently. Enable long term caching of API responses by setting this to a value greater than 0.' + ) + ) + service_username = models.CharField( max_length=100, default='lms_catalog_service_user', diff --git a/openedx/core/djangoapps/catalog/tests/test_utils.py b/openedx/core/djangoapps/catalog/tests/test_utils.py index b58476749f..109ad7c379 100644 --- a/openedx/core/djangoapps/catalog/tests/test_utils.py +++ b/openedx/core/djangoapps/catalog/tests/test_utils.py @@ -11,10 +11,11 @@ from student.tests.factories import UserFactory from openedx.core.djangoapps.catalog.cache import PROGRAM_CACHE_KEY_TPL, SITE_PROGRAM_UUIDS_CACHE_KEY_TPL from openedx.core.djangoapps.catalog.models import CatalogIntegration -from openedx.core.djangoapps.catalog.tests.factories import CourseRunFactory, ProgramFactory, ProgramTypeFactory +from openedx.core.djangoapps.catalog.tests.factories import CourseFactory, CourseRunFactory, ProgramFactory, ProgramTypeFactory from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin from openedx.core.djangoapps.catalog.utils import ( get_course_runs, + get_course_runs_for_course, get_course_run_details, get_currency_data, get_program_types, @@ -330,6 +331,18 @@ class TestGetCourseRuns(CatalogIntegrationMixin, TestCase): self.assert_contract(mock_get_edx_api_data.call_args) self.assertEqual(data, catalog_course_runs) + def test_get_course_runs_by_course(self, mock_get_edx_api_data): + """ + Test retrievals of run from a Course. + """ + catalog_course_runs = CourseRunFactory.create_batch(10) + catalog_course = CourseFactory(course_runs=catalog_course_runs) + mock_get_edx_api_data.return_value = catalog_course + + data = get_course_runs_for_course(course_uuid=str(catalog_course['uuid'])) + self.assertTrue(mock_get_edx_api_data.called) + self.assertEqual(data, catalog_course_runs) + @skip_unless_lms @mock.patch(UTILS_MODULE + '.get_edx_api_data') diff --git a/openedx/core/djangoapps/catalog/utils.py b/openedx/core/djangoapps/catalog/utils.py index e525e9acf3..0392a5c4a8 100644 --- a/openedx/core/djangoapps/catalog/utils.py +++ b/openedx/core/djangoapps/catalog/utils.py @@ -210,6 +210,38 @@ def get_course_runs(): return course_runs +def get_course_runs_for_course(course_uuid): + catalog_integration = CatalogIntegration.current() + + if catalog_integration.is_enabled(): + try: + user = catalog_integration.get_service_user() + except ObjectDoesNotExist: + logger.error( + 'Catalog service user with username [%s] does not exist. Course runs will not be retrieved.', + catalog_integration.service_username, + ) + return [] + + api = create_catalog_api_client(user) + cache_key = '{base}.course.{uuid}.course_runs'.format( + base=catalog_integration.CACHE_KEY, + uuid=course_uuid + ) + data = get_edx_api_data( + catalog_integration, + 'courses', + resource_id=course_uuid, + api=api, + cache_key=cache_key if catalog_integration.is_cache_enabled else None, + long_term_cache=True + ) + + return data.get('course_runs', []) + else: + return [] + + def get_course_run_details(course_run_key, fields): """ Retrieve information about the course run with the given id diff --git a/openedx/core/lib/edx_api_utils.py b/openedx/core/lib/edx_api_utils.py index 00d009e201..85c9430b8d 100644 --- a/openedx/core/lib/edx_api_utils.py +++ b/openedx/core/lib/edx_api_utils.py @@ -24,7 +24,7 @@ def get_fields(fields, response): def get_edx_api_data(api_config, resource, api, resource_id=None, querystring=None, cache_key=None, many=True, - traverse_pagination=True, fields=None): + traverse_pagination=True, fields=None, long_term_cache=False): """GET data from an edX REST API. DRY utility for handling caching and pagination. @@ -42,6 +42,7 @@ def get_edx_api_data(api_config, resource, api, resource_id=None, querystring=No many (bool): Whether the resource requested is a collection of objects, or a single object. If false, an empty dict will be returned in cases of failure rather than the default empty list. traverse_pagination (bool): Whether to traverse pagination or return paginated response.. + long_term_cache (bool): Whether to use the long term cache ttl or the standard cache ttl Returns: Data returned by the API. When hitting a list endpoint, extracts "results" (list of dict) @@ -81,7 +82,10 @@ def get_edx_api_data(api_config, resource, api, resource_id=None, querystring=No if cache_key: zdata = zpickle(results) - cache.set(cache_key, zdata, api_config.cache_ttl) + cache_ttl = api_config.cache_ttl + if long_term_cache: + cache_ttl = api_config.long_term_cache_ttl + cache.set(cache_key, zdata, cache_ttl) return results