diff --git a/common/djangoapps/status/status.py b/common/djangoapps/status/status.py index b3ffd6a84c..4e57930108 100644 --- a/common/djangoapps/status/status.py +++ b/common/djangoapps/status/status.py @@ -3,6 +3,7 @@ A tiny app that checks for a status message. """ from django.conf import settings +from django.core.cache import cache import json import logging import os @@ -25,6 +26,11 @@ def get_site_status_msg(course_id): not allowed to break the entire site). """ try: + # first check for msg in cache + msg = cache.get('site_status_msg') + if msg is not None: + return msg + if os.path.isfile(settings.STATUS_MESSAGE_PATH): with open(settings.STATUS_MESSAGE_PATH) as f: content = f.read() @@ -37,6 +43,8 @@ def get_site_status_msg(course_id): msg = msg + "
" if msg else '' msg += status_dict[course_id] + # set msg to cache, with expiry 5 mins + cache.set('site_status_msg', msg, 60 * 5) return msg except: log.exception("Error while getting a status message.") diff --git a/common/djangoapps/status/tests.py b/common/djangoapps/status/tests.py index 20787e1ed6..8e2e45b7ff 100644 --- a/common/djangoapps/status/tests.py +++ b/common/djangoapps/status/tests.py @@ -1,8 +1,10 @@ from django.conf import settings +from django.core.cache import cache from django.test import TestCase import os from django.test.utils import override_settings from tempfile import NamedTemporaryFile +import ddt from .status import get_site_status_msg @@ -13,6 +15,7 @@ TMP_NAME = TMP_FILE.name TMP_FILE.close() +@ddt.ddt @override_settings(STATUS_MESSAGE_PATH=TMP_NAME) class TestStatus(TestCase): """Test that the get_site_status_msg function does the right thing""" @@ -64,6 +67,13 @@ class TestStatus(TestCase): with open(settings.STATUS_MESSAGE_PATH, 'w') as f: f.write(contents) + def clear_status_cache(self): + """ + Remove the cached status message, if found + """ + if cache.get('site_status_msg') is not None: + cache.delete('site_status_msg') + def remove_status_file(self): """Delete the status file if it exists""" if os.path.exists(settings.STATUS_MESSAGE_PATH): @@ -72,18 +82,19 @@ class TestStatus(TestCase): def tearDown(self): self.remove_status_file() - def test_get_site_status_msg(self): + @ddt.data(*checks) + @ddt.unpack + def test_get_site_status_msg(self, json_str, exp_none, exp_toy, exp_full): """run the tests""" - for (json_str, exp_none, exp_toy, exp_full) in self.checks: - self.remove_status_file() - if json_str: - self.create_status_file(json_str) + self.remove_status_file() + if json_str: + self.create_status_file(json_str) - print "checking results for {0}".format(json_str) - print "course=None:" - self.assertEqual(get_site_status_msg(None), exp_none) - print "course=toy:" - self.assertEqual(get_site_status_msg(self.toy_id), exp_toy) - print "course=full:" - self.assertEqual(get_site_status_msg(self.full_id), exp_full) + for course_id, expected_msg in [(None, exp_none), (self.toy_id, exp_toy), (self.full_id, exp_full)]: + self.assertEqual(get_site_status_msg(course_id), expected_msg) + self.assertEqual(cache.get('site_status_msg'), expected_msg) + # check that `get_site_status_msg` works as expected when the cache + # is warmed, too + self.assertEqual(get_site_status_msg(course_id), expected_msg) + self.clear_status_cache() diff --git a/common/lib/xmodule/xmodule/video_module/video_module.py b/common/lib/xmodule/xmodule/video_module/video_module.py index f12347fd4f..46272814a6 100644 --- a/common/lib/xmodule/xmodule/video_module/video_module.py +++ b/common/lib/xmodule/xmodule/video_module/video_module.py @@ -235,12 +235,15 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers, # CDN_VIDEO_URLS is only to be used here and will be deleted # TODO(ali@edx.org): Delete this after the CDN experiment has completed. + html_id = self.location.html_id() if getattr(settings, 'PERFORMANCE_GRAPHITE_URL', '') != '' and \ self.system.user_location == 'CN' and \ - getattr(settings, 'ENABLE_VIDEO_BEACON', False) and \ - self.edx_video_id in getattr(settings, 'CDN_VIDEO_URLS', {}).keys(): - cdn_urls = getattr(settings, 'CDN_VIDEO_URLS', {})[self.edx_video_id] - cdn_exp_group, sources[0] = random.choice(zip(range(len(cdn_urls)), cdn_urls)) + getattr(settings.FEATURES, 'ENABLE_VIDEO_BEACON', False) and \ + html_id in getattr(settings, 'CDN_VIDEO_URLS', {}).keys(): + cdn_urls = getattr(settings, 'CDN_VIDEO_URLS', {})[html_id] + cdn_exp_group, new_source = random.choice(zip(range(len(cdn_urls)), cdn_urls)) + if cdn_exp_group > 0: + sources[0] = new_source cdn_eval = True else: cdn_eval = False diff --git a/lms/envs/aws.py b/lms/envs/aws.py index f38b46fa5a..db43cf3c56 100644 --- a/lms/envs/aws.py +++ b/lms/envs/aws.py @@ -554,3 +554,7 @@ FACEBOOK_APP_SECRET = AUTH_TOKENS.get("FACEBOOK_APP_SECRET") FACEBOOK_APP_ID = AUTH_TOKENS.get("FACEBOOK_APP_ID") XBLOCK_SETTINGS = ENV_TOKENS.get('XBLOCK_SETTINGS', {}) + +##### CDN EXPERIMENT/MONITORING FLAGS ##### +PERFORMANCE_GRAPHITE_URL = ENV_TOKENS.get('PERFORMANCE_GRAPHITE_URL', PERFORMANCE_GRAPHITE_URL) +CDN_VIDEO_URLS = ENV_TOKENS.get('CDN_VIDEO_URLS', CDN_VIDEO_URLS) diff --git a/lms/envs/common.py b/lms/envs/common.py index a16cd9f347..1fc10c2303 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -348,6 +348,9 @@ FEATURES = { # log all information from cybersource callbacks 'LOG_POSTPAY_CALLBACKS': True, + + # enable beacons for video timing statistics + 'ENABLE_VIDEO_BEACON': False, } # Ignore static asset files on import which match this pattern @@ -2066,6 +2069,10 @@ SEARCH_ENGINE = None # Use the LMS specific result processor SEARCH_RESULT_PROCESSOR = "lms.lib.courseware_search.lms_result_processor.LmsSearchResultProcessor" +##### CDN EXPERIMENT/MONITORING FLAGS ##### +PERFORMANCE_GRAPHITE_URL = '' +CDN_VIDEO_URLS = {} + # The configuration visibility of account fields. ACCOUNT_VISIBILITY_CONFIGURATION = { # Default visibility level for accounts without a specified value diff --git a/lms/templates/navigation-edx.html b/lms/templates/navigation-edx.html index bc586f7664..8cfb9a242d 100644 --- a/lms/templates/navigation-edx.html +++ b/lms/templates/navigation-edx.html @@ -17,7 +17,7 @@ from status.status import get_site_status_msg ## Provide a hook for themes to inject branding on top. <%block name="navigation_top" /> -<%block cached="False"> +<%block> <% try: course_id = course.id.to_deprecated_string() diff --git a/lms/templates/navigation.html b/lms/templates/navigation.html index 6dbfc856cc..2e82e73362 100644 --- a/lms/templates/navigation.html +++ b/lms/templates/navigation.html @@ -17,7 +17,7 @@ from status.status import get_site_status_msg ## Provide a hook for themes to inject branding on top. <%block name="navigation_top" /> -<%block cached="False"> +<%block> <% try: course_id = course.id.to_deprecated_string() diff --git a/lms/templates/video.html b/lms/templates/video.html index 1d6f540e1e..1ad97ee6f0 100644 --- a/lms/templates/video.html +++ b/lms/templates/video.html @@ -166,7 +166,7 @@ $("#video_${id}").bind("html5:canplaythrough", null, function() { if (!beaconSent) { timeElapsed = Date.now() - cdnStartTime; - sendMetricToGraphite("loadtime_${cdn_exp_group}", timeElapsed); + sendMetricToGraphite("videocdnexp.${id}.${cdn_exp_group}.loadtime", timeElapsed); } beaconSent = true; });