Commit 538a3d78 by Mushtaq Ali Committed by muzaffaryousaf

Add video transcript config model flags - EDUCATOR-1224

parent 2203de4a
...@@ -554,6 +554,22 @@ class VideosHandlerTestCase(VideoUploadTestMixin, CourseTestCase): ...@@ -554,6 +554,22 @@ class VideosHandlerTestCase(VideoUploadTestMixin, CourseTestCase):
self.assert_video_status(url, edx_video_id, 'Failed') self.assert_video_status(url, edx_video_id, 'Failed')
@ddt.data(True, False)
@patch('openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled')
def test_video_index_transcript_feature_enablement(self, is_video_transcript_enabled, video_transcript_feature):
"""
Test that when video transcript is enabled/disabled, correct response is rendered.
"""
video_transcript_feature.return_value = is_video_transcript_enabled
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
# Verify that course video button is present in the response if videos transcript feature is enabled.
self.assertEqual(
'<button class="button course-video-settings-button">' in response.content,
is_video_transcript_enabled
)
@ddt.ddt @ddt.ddt
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_VIDEO_UPLOAD_PIPELINE': True}) @patch.dict('django.conf.settings.FEATURES', {'ENABLE_VIDEO_UPLOAD_PIPELINE': True})
...@@ -845,7 +861,10 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -845,7 +861,10 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
edx_video_id = 'test1' edx_video_id = 'test1'
video_image_upload_url = self.get_url_for_course_key(self.course.id, {'edx_video_id': edx_video_id}) video_image_upload_url = self.get_url_for_course_key(self.course.id, {'edx_video_id': edx_video_id})
with make_image_file( with make_image_file(
dimensions=(image_data.get('width', settings.VIDEO_IMAGE_MIN_WIDTH), image_data.get('height', settings.VIDEO_IMAGE_MIN_HEIGHT)), dimensions=(
image_data.get('width', settings.VIDEO_IMAGE_MIN_WIDTH),
image_data.get('height', settings.VIDEO_IMAGE_MIN_HEIGHT)
),
prefix=image_data.get('prefix', 'videoimage'), prefix=image_data.get('prefix', 'videoimage'),
extension=image_data.get('extension', '.png'), extension=image_data.get('extension', '.png'),
force_size=image_data.get('size', settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MIN_BYTES']) force_size=image_data.get('size', settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MIN_BYTES'])
...@@ -858,6 +877,10 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -858,6 +877,10 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
@ddt.ddt @ddt.ddt
@patch(
'openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled',
Mock(return_value=True)
)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_VIDEO_UPLOAD_PIPELINE': True}) @patch.dict('django.conf.settings.FEATURES', {'ENABLE_VIDEO_UPLOAD_PIPELINE': True})
class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
""" """
...@@ -867,35 +890,52 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -867,35 +890,52 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
VIEW_NAME = 'transcript_preferences_handler' VIEW_NAME = 'transcript_preferences_handler'
@ddt.data( @ddt.data(
# Video transcript feature disabled
(
{},
False,
'',
404,
),
# Error cases # Error cases
( (
{}, {},
'Invalid provider.' True,
'Invalid provider.',
400
), ),
( (
{ {
'provider': '' 'provider': ''
}, },
'Invalid provider.' True,
'Invalid provider.',
400
), ),
( (
{ {
'provider': 'dummy-provider' 'provider': 'dummy-provider'
}, },
'Invalid provider.' True,
'Invalid provider.',
400
), ),
( (
{ {
'provider': TranscriptProvider.CIELO24 'provider': TranscriptProvider.CIELO24
}, },
'Invalid cielo24 fidelity.' True,
'Invalid cielo24 fidelity.',
400
), ),
( (
{ {
'provider': TranscriptProvider.CIELO24, 'provider': TranscriptProvider.CIELO24,
'cielo24_fidelity': 'PROFESSIONAL', 'cielo24_fidelity': 'PROFESSIONAL',
}, },
'Invalid cielo24 turnaround.' True,
'Invalid cielo24 turnaround.',
400
), ),
( (
{ {
...@@ -903,7 +943,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -903,7 +943,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'cielo24_fidelity': 'PROFESSIONAL', 'cielo24_fidelity': 'PROFESSIONAL',
'cielo24_turnaround': 'STANDARD' 'cielo24_turnaround': 'STANDARD'
}, },
'Invalid languages.' True,
'Invalid languages.',
400
), ),
( (
{ {
...@@ -912,20 +954,26 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -912,20 +954,26 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'cielo24_turnaround': 'STANDARD', 'cielo24_turnaround': 'STANDARD',
'preferred_languages': ['es', 'ur'] 'preferred_languages': ['es', 'ur']
}, },
'Invalid languages.' True,
'Invalid languages.',
400
), ),
( (
{ {
'provider': TranscriptProvider.THREE_PLAY_MEDIA 'provider': TranscriptProvider.THREE_PLAY_MEDIA
}, },
'Invalid 3play turnaround.' True,
'Invalid 3play turnaround.',
400
), ),
( (
{ {
'provider': TranscriptProvider.THREE_PLAY_MEDIA, 'provider': TranscriptProvider.THREE_PLAY_MEDIA,
'three_play_turnaround': 'default' 'three_play_turnaround': 'default'
}, },
'Invalid languages.' True,
'Invalid languages.',
400
), ),
( (
{ {
...@@ -933,7 +981,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -933,7 +981,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'three_play_turnaround': 'default', 'three_play_turnaround': 'default',
'preferred_languages': ['es', 'ur'] 'preferred_languages': ['es', 'ur']
}, },
'Invalid languages.' True,
'Invalid languages.',
400
), ),
# Success # Success
( (
...@@ -943,7 +993,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -943,7 +993,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'cielo24_turnaround': 'STANDARD', 'cielo24_turnaround': 'STANDARD',
'preferred_languages': ['en'] 'preferred_languages': ['en']
}, },
'' True,
'',
200
), ),
( (
{ {
...@@ -951,37 +1003,45 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -951,37 +1003,45 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'three_play_turnaround': 'default', 'three_play_turnaround': 'default',
'preferred_languages': ['en'] 'preferred_languages': ['en']
}, },
'' True,
'',
200
) )
) )
@ddt.unpack @ddt.unpack
def test_video_transcript(self, preferences, error_message): def test_video_transcript(self, preferences, is_video_transcript_enabled, error_message, expected_status_code):
""" """
Tests that transcript handler works correctly. Tests that transcript handler works correctly.
""" """
video_transcript_url = self.get_url_for_course_key(self.course.id) video_transcript_url = self.get_url_for_course_key(self.course.id)
preferences_data = { preferences_data = {
'provider': preferences.get('provider', ''), 'provider': preferences.get('provider'),
'cielo24_fidelity': preferences.get('cielo24_fidelity', ''), 'cielo24_fidelity': preferences.get('cielo24_fidelity'),
'cielo24_turnaround': preferences.get('cielo24_turnaround', ''), 'cielo24_turnaround': preferences.get('cielo24_turnaround'),
'three_play_turnaround': preferences.get('three_play_turnaround', ''), 'three_play_turnaround': preferences.get('three_play_turnaround'),
'preferred_languages': preferences.get('preferred_languages', []), 'preferred_languages': preferences.get('preferred_languages', []),
} }
with patch(
'openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled'
) as video_transcript_feature:
video_transcript_feature.return_value = is_video_transcript_enabled
response = self.client.post( response = self.client.post(
video_transcript_url, video_transcript_url,
json.dumps(preferences_data), json.dumps(preferences_data),
content_type='application/json' content_type='application/json'
) )
status_code = response.status_code status_code = response.status_code
response = json.loads(response.content) response = json.loads(response.content) if is_video_transcript_enabled else response
if error_message: self.assertEqual(status_code, expected_status_code)
self.assertEqual(status_code, 400) self.assertEqual(response.get('error', ''), error_message)
self.assertEqual(response['error'], error_message)
else: # Remove modified and course_id fields from the response so as to check the expected transcript preferences.
self.assertEqual(status_code, 200) response.get('transcript_preferences', {}).pop('modified', None)
self.assertTrue(response['transcript_preferences'], preferences_data) response.get('transcript_preferences', {}).pop('course_id', None)
expected_preferences = preferences_data if is_video_transcript_enabled and not error_message else {}
self.assertDictEqual(response.get('transcript_preferences', {}), expected_preferences)
def test_remove_transcript_preferences(self): def test_remove_transcript_preferences(self):
""" """
...@@ -1008,7 +1068,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -1008,7 +1068,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
""" """
Test that transcript handler works correctly even when no preferences are found. Test that transcript handler works correctly even when no preferences are found.
""" """
course_id = 'dummy+course+id' course_id = 'course-v1:dummy+course+id'
# Verify transcript preferences do not exist # Verify transcript preferences do not exist
preferences = get_transcript_preferences(course_id) preferences = get_transcript_preferences(course_id)
self.assertIsNone(preferences) self.assertIsNone(preferences)
...@@ -1024,23 +1084,39 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -1024,23 +1084,39 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
self.assertIsNone(preferences) self.assertIsNone(preferences)
@ddt.data( @ddt.data(
(
None, None,
False
),
(
{ {
'provider': TranscriptProvider.CIELO24, 'provider': TranscriptProvider.CIELO24,
'cielo24_fidelity': 'PROFESSIONAL', 'cielo24_fidelity': 'PROFESSIONAL',
'cielo24_turnaround': 'STANDARD', 'cielo24_turnaround': 'STANDARD',
'preferred_languages': ['en'] 'preferred_languages': ['en']
} },
False
),
(
{
'provider': TranscriptProvider.CIELO24,
'cielo24_fidelity': 'PROFESSIONAL',
'cielo24_turnaround': 'STANDARD',
'preferred_languages': ['en']
},
True
)
) )
@ddt.unpack
@override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret') @override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret')
@patch('boto.s3.key.Key') @patch('boto.s3.key.Key')
@patch('boto.s3.connection.S3Connection') @patch('boto.s3.connection.S3Connection')
@patch('contentstore.views.videos.get_transcript_preferences') @patch('contentstore.views.videos.get_transcript_preferences')
def test_transcript_preferences_metadata(self, transcript_preferences, mock_transcript_preferences, def test_transcript_preferences_metadata(self, transcript_preferences, is_video_transcript_enabled,
mock_conn, mock_key): mock_transcript_preferences, mock_conn, mock_key):
""" """
Tests that transcript preference metadata is only set if it is transcript Tests that transcript preference metadata is only set if it is video transcript feature is enabled and
preferences are present in request data. transcript preferences are already stored in the system.
""" """
file_name = 'test-video.mp4' file_name = 'test-video.mp4'
request_data = {'files': [{'file_name': file_name, 'content_type': 'video/mp4'}]} request_data = {'files': [{'file_name': file_name, 'content_type': 'video/mp4'}]}
...@@ -1058,11 +1134,16 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -1058,11 +1134,16 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
mock_key.side_effect = [mock_key_instance] + [Mock()] mock_key.side_effect = [mock_key_instance] + [Mock()]
videos_handler_url = reverse_course_url('videos_handler', self.course.id) videos_handler_url = reverse_course_url('videos_handler', self.course.id)
with patch(
'openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled'
) as video_transcript_feature:
video_transcript_feature.return_value = is_video_transcript_enabled
response = self.client.post(videos_handler_url, json.dumps(request_data), content_type='application/json') response = self.client.post(videos_handler_url, json.dumps(request_data), content_type='application/json')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
# Ensure `transcript_preferences` was set up in Key correctly if sent through request. # Ensure `transcript_preferences` was set up in Key correctly if sent through request.
if transcript_preferences: if is_video_transcript_enabled and transcript_preferences:
mock_key_instance.set_metadata.assert_any_call('transcript_preferences', json.dumps(transcript_preferences)) mock_key_instance.set_metadata.assert_any_call('transcript_preferences', json.dumps(transcript_preferences))
else: else:
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
......
...@@ -33,6 +33,7 @@ from edxval.api import ( ...@@ -33,6 +33,7 @@ from edxval.api import (
remove_transcript_preferences, remove_transcript_preferences,
) )
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.video_config.models import VideoTranscriptEnabledFlag
from openedx.core.djangoapps.waffle_utils import WaffleSwitchNamespace from openedx.core.djangoapps.waffle_utils import WaffleSwitchNamespace
from contentstore.models import VideoUploadConfig from contentstore.models import VideoUploadConfig
...@@ -129,7 +130,7 @@ class StatusDisplayStrings(object): ...@@ -129,7 +130,7 @@ class StatusDisplayStrings(object):
"invalid_token": _INVALID_TOKEN, "invalid_token": _INVALID_TOKEN,
"imported": _IMPORTED, "imported": _IMPORTED,
"transcription_in_progress": _TRANSCRIPTION_IN_PROGRESS, "transcription_in_progress": _TRANSCRIPTION_IN_PROGRESS,
"transcription_ready": _TRANSCRIPT_READY, "transcript_ready": _TRANSCRIPT_READY,
} }
@staticmethod @staticmethod
...@@ -339,6 +340,11 @@ def transcript_preferences_handler(request, course_key_string): ...@@ -339,6 +340,11 @@ def transcript_preferences_handler(request, course_key_string):
Returns: valid json response or 400 with error message Returns: valid json response or 400 with error message
""" """
course_key = CourseKey.from_string(course_key_string)
is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course_key)
if not is_video_transcript_enabled:
return HttpResponseNotFound()
if request.method == 'POST': if request.method == 'POST':
data = request.json data = request.json
provider = data.get('provider') provider = data.get('provider')
...@@ -550,6 +556,7 @@ def videos_index_html(course): ...@@ -550,6 +556,7 @@ def videos_index_html(course):
""" """
Returns an HTML page to display previous video uploads and allow new ones Returns an HTML page to display previous video uploads and allow new ones
""" """
is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
context = { context = {
'context_course': course, 'context_course': course,
'image_upload_url': reverse_course_url('video_images_handler', unicode(course.id)), 'image_upload_url': reverse_course_url('video_images_handler', unicode(course.id)),
...@@ -567,19 +574,21 @@ def videos_index_html(course): ...@@ -567,19 +574,21 @@ def videos_index_html(course):
'max_width': settings.VIDEO_IMAGE_MAX_WIDTH, 'max_width': settings.VIDEO_IMAGE_MAX_WIDTH,
'max_height': settings.VIDEO_IMAGE_MAX_HEIGHT, 'max_height': settings.VIDEO_IMAGE_MAX_HEIGHT,
'supported_file_formats': settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS 'supported_file_formats': settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS
} },
'is_video_transcript_enabled': is_video_transcript_enabled,
'video_transcript_settings': None,
'active_transcript_preferences': None
} }
context.update({ if is_video_transcript_enabled:
'video_transcript_settings': { context['video_transcript_settings'] = {
'transcript_preferences_handler_url': reverse_course_url( 'transcript_preferences_handler_url': reverse_course_url(
'transcript_preferences_handler', 'transcript_preferences_handler',
unicode(course.id) unicode(course.id)
), ),
'transcription_plans': get_3rd_party_transcription_plans(), 'transcription_plans': get_3rd_party_transcription_plans(),
}, }
'active_transcript_preferences': get_transcript_preferences(unicode(course.id)) context['active_transcript_preferences'] = get_transcript_preferences(unicode(course.id))
})
return render_to_response('videos_index.html', context) return render_to_response('videos_index.html', context)
...@@ -662,6 +671,8 @@ def videos_post(course, request): ...@@ -662,6 +671,8 @@ def videos_post(course, request):
('course_key', unicode(course.id)), ('course_key', unicode(course.id)),
] ]
is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
if is_video_transcript_enabled:
transcript_preferences = get_transcript_preferences(unicode(course.id)) transcript_preferences = get_transcript_preferences(unicode(course.id))
if transcript_preferences is not None: if transcript_preferences is not None:
metadata_list.append(('transcript_preferences', json.dumps(transcript_preferences))) metadata_list.append(('transcript_preferences', json.dumps(transcript_preferences)))
......
...@@ -16,6 +16,7 @@ define([ ...@@ -16,6 +16,7 @@ define([
videoUploadMaxFileSizeInGB, videoUploadMaxFileSizeInGB,
activeTranscriptPreferences, activeTranscriptPreferences,
videoTranscriptSettings, videoTranscriptSettings,
isVideoTranscriptEnabled,
videoImageSettings videoImageSettings
) { ) {
var activeView = new ActiveVideoUploadListView({ var activeView = new ActiveVideoUploadListView({
...@@ -27,6 +28,7 @@ define([ ...@@ -27,6 +28,7 @@ define([
videoImageSettings: videoImageSettings, videoImageSettings: videoImageSettings,
activeTranscriptPreferences: activeTranscriptPreferences, activeTranscriptPreferences: activeTranscriptPreferences,
videoTranscriptSettings: videoTranscriptSettings, videoTranscriptSettings: videoTranscriptSettings,
isVideoTranscriptEnabled: isVideoTranscriptEnabled,
onFileUploadDone: function(activeVideos) { onFileUploadDone: function(activeVideos) {
$.ajax({ $.ajax({
url: videoHandlerUrl, url: videoHandlerUrl,
......
...@@ -20,6 +20,10 @@ define( ...@@ -20,6 +20,10 @@ define(
fail: 'upload_failed', fail: 'upload_failed',
success: 'upload_completed' success: 'upload_completed'
}, },
videoUploadMaxFileSizeInGB = 5,
videoSupportedFileFormats = ['.mp4', '.mov'],
createActiveUploadListView,
$courseVideoSettingsButton,
makeUploadUrl, makeUploadUrl,
getSentRequests, getSentRequests,
verifyUploadViewInfo, verifyUploadViewInfo,
...@@ -29,6 +33,22 @@ define( ...@@ -29,6 +33,22 @@ define(
verifyA11YMessage, verifyA11YMessage,
verifyUploadPostRequest; verifyUploadPostRequest;
createActiveUploadListView = function(isVideoTranscriptEnabled) {
return new ActiveVideoUploadListView({
concurrentUploadLimit: concurrentUploadLimit,
postUrl: POST_URL,
courseVideoSettingsButton: $courseVideoSettingsButton,
videoSupportedFileFormats: videoSupportedFileFormats,
videoUploadMaxFileSizeInGB: videoUploadMaxFileSizeInGB,
activeTranscriptPreferences: {},
videoTranscriptSettings: {
transcript_preferences_handler_url: '',
transcription_plans: {}
},
isVideoTranscriptEnabled: isVideoTranscriptEnabled
});
};
describe('ActiveVideoUploadListView', function() { describe('ActiveVideoUploadListView', function() {
beforeEach(function() { beforeEach(function() {
setFixtures( setFixtures(
...@@ -40,22 +60,8 @@ define( ...@@ -40,22 +60,8 @@ define(
); );
TemplateHelpers.installTemplate('active-video-upload'); TemplateHelpers.installTemplate('active-video-upload');
TemplateHelpers.installTemplate('active-video-upload-list'); TemplateHelpers.installTemplate('active-video-upload-list');
this.postUrl = POST_URL; $courseVideoSettingsButton = $('.course-video-settings-button');
this.courseVideoSettingsButton = $('.course-video-settings-button'); this.view = createActiveUploadListView(true);
this.videoSupportedFileFormats = ['.mp4', '.mov'];
this.videoUploadMaxFileSizeInGB = 5;
this.view = new ActiveVideoUploadListView({
concurrentUploadLimit: concurrentUploadLimit,
postUrl: this.postUrl,
courseVideoSettingsButton: this.courseVideoSettingsButton,
videoSupportedFileFormats: this.videoSupportedFileFormats,
videoUploadMaxFileSizeInGB: this.videoUploadMaxFileSizeInGB,
activeTranscriptPreferences: {},
videoTranscriptSettings: {
transcript_preferences_handler_url: '',
transcription_plans: {}
}
});
this.view.render(); this.view.render();
jasmine.Ajax.install(); jasmine.Ajax.install();
}); });
...@@ -94,9 +100,15 @@ define( ...@@ -94,9 +100,15 @@ define(
}); });
it('shows course video settings pane when course video settings button is clicked', function() { it('shows course video settings pane when course video settings button is clicked', function() {
expect($('.course-video-settings-container')).not.toExist(); $courseVideoSettingsButton.click();
this.courseVideoSettingsButton.click(); expect(this.view.courseVideoSettingsView).toBeDefined();
expect($('.course-video-settings-container')).toExist(); expect(this.view.courseVideoSettingsView.$el.find('.course-video-settings-container')).toExist();
});
it('should not initiate course video settings view when video transcript is disabled', function() {
this.view = createActiveUploadListView(false);
$courseVideoSettingsButton.click();
expect(this.view.courseVideoSettingsView).toBeUndefined();
}); });
it('should not show a notification message if there are no active video uploads', function() { it('should not show a notification message if there are no active video uploads', function() {
...@@ -328,7 +340,7 @@ define( ...@@ -328,7 +340,7 @@ define(
'Your file could not be uploaded', 'Your file could not be uploaded',
StringUtils.interpolate( StringUtils.interpolate(
'{fileName} is not in a supported file format. Supported file formats are {supportedFormats}.', // eslint-disable-line max-len '{fileName} is not in a supported file format. Supported file formats are {supportedFormats}.', // eslint-disable-line max-len
{fileName: files[index].name, supportedFormats: self.videoSupportedFileFormats.join(' and ')} // eslint-disable-line max-len {fileName: files[index].name, supportedFormats: videoSupportedFileFormats.join(' and ')} // eslint-disable-line max-len
) )
); );
}); });
...@@ -361,7 +373,7 @@ define( ...@@ -361,7 +373,7 @@ define(
verifyUploadViewInfo( verifyUploadViewInfo(
uploadView, uploadView,
'Your file could not be uploaded', 'Your file could not be uploaded',
'file.mp4 exceeds maximum size of ' + this.videoUploadMaxFileSizeInGB + ' GB.' 'file.mp4 exceeds maximum size of ' + videoUploadMaxFileSizeInGB + ' GB.'
); );
verifyA11YMessage( verifyA11YMessage(
StringUtils.interpolate( StringUtils.interpolate(
...@@ -431,7 +443,7 @@ define( ...@@ -431,7 +443,7 @@ define(
expect(jasmine.Ajax.requests.count()).toEqual(caseInfo.numFiles); expect(jasmine.Ajax.requests.count()).toEqual(caseInfo.numFiles);
_.each(_.range(caseInfo.numFiles), function(index) { _.each(_.range(caseInfo.numFiles), function(index) {
request = jasmine.Ajax.requests.at(index); request = jasmine.Ajax.requests.at(index);
expect(request.url).toEqual(self.postUrl); expect(request.url).toEqual(POST_URL);
expect(request.method).toEqual('POST'); expect(request.method).toEqual('POST');
expect(request.requestHeaders['Content-Type']).toEqual('application/json'); expect(request.requestHeaders['Content-Type']).toEqual('application/json');
expect(request.requestHeaders.Accept).toContain('application/json'); expect(request.requestHeaders.Accept).toContain('application/json');
......
...@@ -43,6 +43,7 @@ define([ ...@@ -43,6 +43,7 @@ define([
this.postUrl = options.postUrl; this.postUrl = options.postUrl;
this.activeTranscriptPreferences = options.activeTranscriptPreferences; this.activeTranscriptPreferences = options.activeTranscriptPreferences;
this.videoTranscriptSettings = options.videoTranscriptSettings; this.videoTranscriptSettings = options.videoTranscriptSettings;
this.isVideoTranscriptEnabled = options.isVideoTranscriptEnabled;
this.videoSupportedFileFormats = options.videoSupportedFileFormats; this.videoSupportedFileFormats = options.videoSupportedFileFormats;
this.videoUploadMaxFileSizeInGB = options.videoUploadMaxFileSizeInGB; this.videoUploadMaxFileSizeInGB = options.videoUploadMaxFileSizeInGB;
this.onFileUploadDone = options.onFileUploadDone; this.onFileUploadDone = options.onFileUploadDone;
...@@ -62,6 +63,7 @@ define([ ...@@ -62,6 +63,7 @@ define([
supportedVideoTypes: this.videoSupportedFileFormats.join(', ') supportedVideoTypes: this.videoSupportedFileFormats.join(', ')
} }
); );
if (this.isVideoTranscriptEnabled) {
this.listenTo( this.listenTo(
Backbone, Backbone,
'coursevideosettings:syncActiveTranscriptPreferences', 'coursevideosettings:syncActiveTranscriptPreferences',
...@@ -72,6 +74,7 @@ define([ ...@@ -72,6 +74,7 @@ define([
'coursevideosettings:destroyCourseVideoSettingsView', 'coursevideosettings:destroyCourseVideoSettingsView',
this.destroyCourseVideoSettingsView this.destroyCourseVideoSettingsView
); );
}
}, },
syncActiveTranscriptPreferences: function(activeTranscriptPreferences) { syncActiveTranscriptPreferences: function(activeTranscriptPreferences) {
...@@ -79,12 +82,14 @@ define([ ...@@ -79,12 +82,14 @@ define([
}, },
showCourseVideoSettingsView: function(event) { showCourseVideoSettingsView: function(event) {
if (this.isVideoTranscriptEnabled) {
this.courseVideoSettingsView = new CourseVideoSettingsView({ this.courseVideoSettingsView = new CourseVideoSettingsView({
activeTranscriptPreferences: this.activeTranscriptPreferences, activeTranscriptPreferences: this.activeTranscriptPreferences,
videoTranscriptSettings: this.videoTranscriptSettings videoTranscriptSettings: this.videoTranscriptSettings
}); });
this.courseVideoSettingsView.render(); this.courseVideoSettingsView.render();
event.stopPropagation(); event.stopPropagation();
}
}, },
destroyCourseVideoSettingsView: function() { destroyCourseVideoSettingsView: function() {
......
...@@ -337,6 +337,22 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett ...@@ -337,6 +337,22 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett
}, },
updateSuccessResponseStatus: function(data) { updateSuccessResponseStatus: function(data) {
var dateModified = data ? moment.utc(data.modified).format('ll') : '';
// Update last modified date
if (dateModified) {
HtmlUtils.setHtml(
this.$el.find('.last-updated-text'),
HtmlUtils.interpolateHtml(
HtmlUtils.HTML('{lastUpdateText} {dateModified}'),
{
lastUpdateText: gettext('Last updated'),
dateModified: dateModified
}
)
);
}
this.renderResponseStatus(gettext('Settings updated'), 'success'); this.renderResponseStatus(gettext('Settings updated'), 'success');
// Sync ActiveUploadListView with latest active plan. // Sync ActiveUploadListView with latest active plan.
this.activeTranscriptionPlan = data; this.activeTranscriptionPlan = data;
...@@ -533,6 +549,8 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett ...@@ -533,6 +549,8 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett
}, },
closeCourseVideoSettings: function() { closeCourseVideoSettings: function() {
// TODO: Slide out when closing settings pane. We may need to hide the view instead of destroying it.
// Trigger destroy transcript event. // Trigger destroy transcript event.
Backbone.trigger('coursevideosettings:destroyCourseVideoSettingsView'); Backbone.trigger('coursevideosettings:destroyCourseVideoSettingsView');
......
...@@ -45,9 +45,11 @@ ...@@ -45,9 +45,11 @@
<%- gettext('Update Settings') %> <%- gettext('Update Settings') %>
<span id='update-button-text' class='sr'><%-gettext('Press update settings to update course video settings') %></span> <span id='update-button-text' class='sr'><%-gettext('Press update settings to update course video settings') %></span>
</button> </button>
<span class='last-updated-text'>
<%if (dateModified) { %> <%if (dateModified) { %>
<span class='last-updated-text'><%- gettext('Last updated')%> <%- dateModified %></span> <%- gettext('Last updated')%> <%- dateModified %>
<% } %> <% } %>
</span>
</div> </div>
</div> </div>
</div> </div>
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
${video_upload_max_file_size | n, dump_js_escaped_json}, ${video_upload_max_file_size | n, dump_js_escaped_json},
${active_transcript_preferences | n, dump_js_escaped_json}, ${active_transcript_preferences | n, dump_js_escaped_json},
${video_transcript_settings | n, dump_js_escaped_json}, ${video_transcript_settings | n, dump_js_escaped_json},
${is_video_transcript_enabled | n, dump_js_escaped_json},
${video_image_settings | n, dump_js_escaped_json} ${video_image_settings | n, dump_js_escaped_json}
); );
}); });
...@@ -55,12 +56,14 @@ ...@@ -55,12 +56,14 @@
<span class="sr">&gt; </span>${_("Video Uploads")} <span class="sr">&gt; </span>${_("Video Uploads")}
</h1> </h1>
% if is_video_transcript_enabled :
<nav class="nav-actions" aria-label="${_('Page Actions')}"> <nav class="nav-actions" aria-label="${_('Page Actions')}">
<h3 class="sr">${_("Page Actions")}</h3> <h3 class="sr">${_("Page Actions")}</h3>
<div class="nav-item"> <div class="nav-item">
<button class="button course-video-settings-button"><span class="icon fa fa-cog" aria-hidden="true"></span> ${_("Course Video Settings")}</button> <button class="button course-video-settings-button"><span class="icon fa fa-cog" aria-hidden="true"></span> ${_("Course Video Settings")}</button>
</div> </div>
</nav> </nav>
% endif
</header> </header>
</div> </div>
......
...@@ -5,16 +5,24 @@ Django admin dashboard configuration for Video XModule. ...@@ -5,16 +5,24 @@ Django admin dashboard configuration for Video XModule.
from config_models.admin import ConfigurationModelAdmin, KeyedConfigurationModelAdmin from config_models.admin import ConfigurationModelAdmin, KeyedConfigurationModelAdmin
from django.contrib import admin from django.contrib import admin
from openedx.core.djangoapps.video_config.forms import CourseHLSPlaybackFlagAdminForm from openedx.core.djangoapps.video_config.forms import (
from openedx.core.djangoapps.video_config.models import CourseHLSPlaybackEnabledFlag, HLSPlaybackEnabledFlag CourseHLSPlaybackFlagAdminForm, CourseVideoTranscriptFlagAdminForm
)
from openedx.core.djangoapps.video_config.models import (
CourseHLSPlaybackEnabledFlag, HLSPlaybackEnabledFlag,
CourseVideoTranscriptEnabledFlag, VideoTranscriptEnabledFlag
)
class CourseHLSPlaybackEnabledFlagAdmin(KeyedConfigurationModelAdmin): class CourseSpecificEnabledFlagBaseAdmin(KeyedConfigurationModelAdmin):
""" """
Admin of HLS Playback feature on course-by-course basis. Admin of course specific feature on course-by-course basis.
Allows searching by course id. Allows searching by course id.
""" """
form = CourseHLSPlaybackFlagAdminForm # Make abstract base class
class Meta(object):
abstract = True
search_fields = ['course_id'] search_fields = ['course_id']
fieldsets = ( fieldsets = (
(None, { (None, {
...@@ -23,5 +31,23 @@ class CourseHLSPlaybackEnabledFlagAdmin(KeyedConfigurationModelAdmin): ...@@ -23,5 +31,23 @@ class CourseHLSPlaybackEnabledFlagAdmin(KeyedConfigurationModelAdmin):
}), }),
) )
class CourseHLSPlaybackEnabledFlagAdmin(CourseSpecificEnabledFlagBaseAdmin):
"""
Admin of HLS Playback feature on course-by-course basis.
Allows searching by course id.
"""
form = CourseHLSPlaybackFlagAdminForm
class CourseVideoTranscriptEnabledFlagAdmin(CourseSpecificEnabledFlagBaseAdmin):
"""
Admin of Video Transcript feature on course-by-course basis.
Allows searching by course id.
"""
form = CourseHLSPlaybackFlagAdminForm
admin.site.register(HLSPlaybackEnabledFlag, ConfigurationModelAdmin) admin.site.register(HLSPlaybackEnabledFlag, ConfigurationModelAdmin)
admin.site.register(CourseHLSPlaybackEnabledFlag, CourseHLSPlaybackEnabledFlagAdmin) admin.site.register(CourseHLSPlaybackEnabledFlag, CourseHLSPlaybackEnabledFlagAdmin)
admin.site.register(VideoTranscriptEnabledFlag, ConfigurationModelAdmin)
admin.site.register(CourseVideoTranscriptEnabledFlag, CourseHLSPlaybackEnabledFlagAdmin)
...@@ -7,20 +7,20 @@ from django import forms ...@@ -7,20 +7,20 @@ from django import forms
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.locator import CourseLocator from opaque_keys.edx.locator import CourseLocator
from openedx.core.djangoapps.video_config.models import CourseHLSPlaybackEnabledFlag from openedx.core.djangoapps.video_config.models import CourseHLSPlaybackEnabledFlag, CourseVideoTranscriptEnabledFlag
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class CourseHLSPlaybackFlagAdminForm(forms.ModelForm): class CourseSpecificFlagAdminBaseForm(forms.ModelForm):
""" """
Form for course-specific HLS Playback configuration. Form for course-specific feature configuration.
""" """
# Make abstract base class
class Meta(object): class Meta(object):
model = CourseHLSPlaybackEnabledFlag abstract = True
fields = '__all__'
def clean_course_id(self): def clean_course_id(self):
""" """
...@@ -42,3 +42,23 @@ class CourseHLSPlaybackFlagAdminForm(forms.ModelForm): ...@@ -42,3 +42,23 @@ class CourseHLSPlaybackFlagAdminForm(forms.ModelForm):
raise forms.ValidationError(msg) raise forms.ValidationError(msg)
return course_key return course_key
class CourseHLSPlaybackFlagAdminForm(CourseSpecificFlagAdminBaseForm):
"""
Form for course-specific HLS Playback configuration.
"""
class Meta(object):
model = CourseHLSPlaybackEnabledFlag
fields = '__all__'
class CourseVideoTranscriptFlagAdminForm(CourseSpecificFlagAdminBaseForm):
"""
Form for course-specific Video Transcript configuration.
"""
class Meta(object):
model = CourseVideoTranscriptEnabledFlag
fields = '__all__'
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
import django.db.models.deletion
import openedx.core.djangoapps.xmodule_django.models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('video_config', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='CourseVideoTranscriptEnabledFlag',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
('course_id', openedx.core.djangoapps.xmodule_django.models.CourseKeyField(max_length=255, db_index=True)),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
],
options={
'ordering': ('-change_date',),
'abstract': False,
},
),
migrations.CreateModel(
name='VideoTranscriptEnabledFlag',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
('enabled_for_all_courses', models.BooleanField(default=False)),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
],
options={
'ordering': ('-change_date',),
'abstract': False,
},
),
]
...@@ -66,3 +66,68 @@ class CourseHLSPlaybackEnabledFlag(ConfigurationModel): ...@@ -66,3 +66,68 @@ class CourseHLSPlaybackEnabledFlag(ConfigurationModel):
course_key=unicode(self.course_id), course_key=unicode(self.course_id),
not_enabled=not_en not_enabled=not_en
) )
class VideoTranscriptEnabledFlag(ConfigurationModel):
"""
Enables Video Transcript across the platform.
When this feature flag is set to true, individual courses
must also have Video Transcript enabled for this feature to
take effect.
When this feature is enabled, 3rd party transcript integration functionality would be available accross all
courses or some specific courses and S3 video transcript would be served (currently as a fallback).
"""
# this field overrides course-specific settings
enabled_for_all_courses = BooleanField(default=False)
@classmethod
def feature_enabled(cls, course_id):
"""
Looks at the currently active configuration model to determine whether
the Video Transcript feature is available.
If the feature flag is not enabled, the feature is not available.
If the flag is enabled for all the courses, feature is available.
If the flag is enabled and the provided course_id is for an course
with Video Transcript enabled, the feature is available.
Arguments:
course_id (CourseKey): course id for whom feature will be checked.
"""
if not VideoTranscriptEnabledFlag.is_enabled():
return False
elif not VideoTranscriptEnabledFlag.current().enabled_for_all_courses:
feature = (CourseVideoTranscriptEnabledFlag.objects
.filter(course_id=course_id)
.order_by('-change_date')
.first())
return feature.enabled if feature else False
return True
def __unicode__(self):
current_model = VideoTranscriptEnabledFlag.current()
return u"VideoTranscriptEnabledFlag: enabled {is_enabled}".format(
is_enabled=current_model.is_enabled()
)
class CourseVideoTranscriptEnabledFlag(ConfigurationModel):
"""
Enables Video Transcript for a specific course. Global feature must be
enabled for this to take effect.
When this feature is enabled, 3rd party transcript integration functionality would be available for the
specific course and S3 video transcript would be served (currently as a fallback).
"""
KEY_FIELDS = ('course_id',)
course_id = CourseKeyField(max_length=255, db_index=True)
def __unicode__(self):
not_en = "Not "
if self.enabled:
not_en = ""
return u"Course '{course_key}': Video Transcript {not_enabled}Enabled".format(
course_key=unicode(self.course_id),
not_enabled=not_en
)
...@@ -13,100 +13,198 @@ from openedx.core.djangoapps.video_config.models import CourseHLSPlaybackEnabled ...@@ -13,100 +13,198 @@ from openedx.core.djangoapps.video_config.models import CourseHLSPlaybackEnabled
@contextmanager @contextmanager
def hls_playback_feature_flags( def video_feature_flags(
all_courses_model_class, course_specific_model_class,
global_flag, enabled_for_all_courses=False, global_flag, enabled_for_all_courses=False,
course_id=None, enabled_for_course=False course_id=None, enabled_for_course=False
): ):
""" """
Yields HLS Playback Configuration records for unit tests Yields video feature configuration records for unit tests
Arguments: Arguments:
all_courses_model_class: Model class to enable feature for all courses
course_specific_model_class: Model class to nable feature for course specific
global_flag (bool): Specifies whether feature is enabled globally global_flag (bool): Specifies whether feature is enabled globally
enabled_for_all_courses (bool): Specifies whether feature is enabled for all courses enabled_for_all_courses (bool): Specifies whether feature is enabled for all courses
course_id (CourseLocator): Course locator for course specific configurations course_id (CourseLocator): Course locator for course specific configurations
enabled_for_course (bool): Specifies whether feature should be available for a course enabled_for_course (bool): Specifies whether feature should be available for a course
""" """
HLSPlaybackEnabledFlag.objects.create(enabled=global_flag, enabled_for_all_courses=enabled_for_all_courses) all_courses_model_class.objects.create(enabled=global_flag, enabled_for_all_courses=enabled_for_all_courses)
if course_id: if course_id:
CourseHLSPlaybackEnabledFlag.objects.create(course_id=course_id, enabled=enabled_for_course) course_specific_model_class.objects.create(course_id=course_id, enabled=enabled_for_course)
yield yield
@ddt.ddt class FeatureFlagTestMixin(object):
class TestHLSPlaybackFlag(TestCase):
""" """
Tests the behavior of the flags for HLS Playback feature. Adds util methods to test the behavior of the flags for video feature.
These are set via Django admin settings.
""" """
def setUp(self): course_id_1 = CourseLocator(org="edx", course="course", run="run")
super(TestHLSPlaybackFlag, self).setUp() course_id_2 = CourseLocator(org="edx", course="course2", run="run")
self.course_id_1 = CourseLocator(org="edx", course="course", run="run")
self.course_id_2 = CourseLocator(org="edx", course="course2", run="run")
@ddt.data( def verify_feature_flags(self, all_courses_model_class, course_specific_model_class,
*itertools.product( global_flag, enabled_for_all_courses, enabled_for_course_1):
(True, False),
(True, False),
(True, False),
)
)
@ddt.unpack
def test_hls_playback_feature_flags(self, global_flag, enabled_for_all_courses, enabled_for_course_1):
""" """
Tests that the feature flags works correctly on tweaking global flags in combination Verifies that the feature flags works correctly on tweaking global flags in combination
with course-specific flags. with course-specific flags.
""" """
with hls_playback_feature_flags( with video_feature_flags(
all_courses_model_class=all_courses_model_class,
course_specific_model_class=course_specific_model_class,
global_flag=global_flag, global_flag=global_flag,
enabled_for_all_courses=enabled_for_all_courses, enabled_for_all_courses=enabled_for_all_courses,
course_id=self.course_id_1, course_id=self.course_id_1,
enabled_for_course=enabled_for_course_1 enabled_for_course=enabled_for_course_1
): ):
self.assertEqual( self.assertEqual(
HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1), all_courses_model_class.feature_enabled(self.course_id_1),
global_flag and (enabled_for_all_courses or enabled_for_course_1) global_flag and (enabled_for_all_courses or enabled_for_course_1)
) )
self.assertEqual( self.assertEqual(
HLSPlaybackEnabledFlag.feature_enabled(self.course_id_2), all_courses_model_class.feature_enabled(self.course_id_2),
global_flag and enabled_for_all_courses global_flag and enabled_for_all_courses
) )
def test_enable_disable_course_flag(self): def verify_enable_disable_course_flag(self, all_courses_model_class, course_specific_model_class):
""" """
Ensures that the flag, once enabled for a course, can also be disabled. Verifies that the course specific flag, once enabled for a course, can also be disabled.
""" """
with hls_playback_feature_flags( with video_feature_flags(
all_courses_model_class=all_courses_model_class,
course_specific_model_class=course_specific_model_class,
global_flag=True, global_flag=True,
enabled_for_all_courses=False, enabled_for_all_courses=False,
course_id=self.course_id_1, course_id=self.course_id_1,
enabled_for_course=True enabled_for_course=True
): ):
self.assertTrue(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1)) self.assertTrue(all_courses_model_class.feature_enabled(self.course_id_1))
with hls_playback_feature_flags( with video_feature_flags(
all_courses_model_class=all_courses_model_class,
course_specific_model_class=course_specific_model_class,
global_flag=True, global_flag=True,
enabled_for_all_courses=False, enabled_for_all_courses=False,
course_id=self.course_id_1, course_id=self.course_id_1,
enabled_for_course=False enabled_for_course=False
): ):
self.assertFalse(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1)) self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_1))
def test_enable_disable_globally(self): def verify_enable_disable_globally(self, all_courses_model_class, course_specific_model_class):
""" """
Ensures that the flag, once enabled globally, can also be disabled. Verifies that global flag, once enabled globally, can also be disabled.
""" """
with hls_playback_feature_flags( with video_feature_flags(
all_courses_model_class=all_courses_model_class,
course_specific_model_class=course_specific_model_class,
global_flag=True, global_flag=True,
enabled_for_all_courses=True, enabled_for_all_courses=True,
): ):
self.assertTrue(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1)) self.assertTrue(all_courses_model_class.feature_enabled(self.course_id_1))
self.assertTrue(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_2)) self.assertTrue(all_courses_model_class.feature_enabled(self.course_id_2))
with hls_playback_feature_flags( with video_feature_flags(
all_courses_model_class=all_courses_model_class,
course_specific_model_class=course_specific_model_class,
global_flag=True, global_flag=True,
enabled_for_all_courses=False, enabled_for_all_courses=False,
): ):
self.assertFalse(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1)) self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_1))
self.assertFalse(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_2)) self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_2))
with hls_playback_feature_flags( with video_feature_flags(
all_courses_model_class=all_courses_model_class,
course_specific_model_class=course_specific_model_class,
global_flag=False, global_flag=False,
): ):
self.assertFalse(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1)) self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_1))
self.assertFalse(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_2)) self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_2))
@ddt.ddt
class TestHLSPlaybackFlag(TestCase, FeatureFlagTestMixin):
"""
Tests the behavior of the flags for HLS Playback feature.
These are set via Django admin settings.
"""
@ddt.data(
*itertools.product(
(True, False),
(True, False),
(True, False),
)
)
@ddt.unpack
def test_hls_playback_feature_flags(self, global_flag, enabled_for_all_courses, enabled_for_course_1):
"""
Tests that the HLS Playback feature flags works correctly on tweaking global flags in combination
with course-specific flags.
"""
self.verify_feature_flags(
all_courses_model_class=HLSPlaybackEnabledFlag,
course_specific_model_class=CourseHLSPlaybackEnabledFlag,
global_flag=global_flag,
enabled_for_all_courses=enabled_for_all_courses,
enabled_for_course_1=enabled_for_course_1
)
def test_enable_disable_course_flag(self):
"""
Ensures that the flag, once enabled for a course, can also be disabled.
"""
self.verify_enable_disable_course_flag(
all_courses_model_class=HLSPlaybackEnabledFlag,
course_specific_model_class=CourseHLSPlaybackEnabledFlag
)
def test_enable_disable_globally(self):
"""
Ensures that the flag, once enabled globally, can also be disabled.
"""
self.verify_enable_disable_globally(
all_courses_model_class=HLSPlaybackEnabledFlag,
course_specific_model_class=CourseHLSPlaybackEnabledFlag
)
@ddt.ddt
class TestVideoTranscriptFlag(TestCase, FeatureFlagTestMixin):
"""
Tests the behavior of the flags for Video Transcript feature.
These are set via Django admin settings.
"""
@ddt.data(
*itertools.product(
(True, False),
(True, False),
(True, False),
)
)
@ddt.unpack
def test_video_transcript_feature_flags(self, global_flag, enabled_for_all_courses, enabled_for_course_1):
"""
Tests that Video Transcript feature flags works correctly on tweaking global flags in combination
with course-specific flags.
"""
self.verify_feature_flags(
all_courses_model_class=HLSPlaybackEnabledFlag,
course_specific_model_class=CourseHLSPlaybackEnabledFlag,
global_flag=global_flag,
enabled_for_all_courses=enabled_for_all_courses,
enabled_for_course_1=enabled_for_course_1
)
def test_enable_disable_course_flag(self):
"""
Ensures that the Video Transcript course specific flag, once enabled for a course, can also be disabled.
"""
self.verify_enable_disable_course_flag(
all_courses_model_class=HLSPlaybackEnabledFlag,
course_specific_model_class=CourseHLSPlaybackEnabledFlag
)
def test_enable_disable_globally(self):
"""
Ensures that the Video Transcript flag, once enabled globally, can also be disabled.
"""
self.verify_enable_disable_globally(
all_courses_model_class=HLSPlaybackEnabledFlag,
course_specific_model_class=CourseHLSPlaybackEnabledFlag
)
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