Add video transcript config model flags - EDUCATOR-1224
This commit is contained in:
committed by
muzaffaryousaf
parent
2203de4a85
commit
538a3d7801
@@ -554,6 +554,22 @@ class VideosHandlerTestCase(VideoUploadTestMixin, CourseTestCase):
|
||||
|
||||
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
|
||||
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_VIDEO_UPLOAD_PIPELINE': True})
|
||||
@@ -845,7 +861,10 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
edx_video_id = 'test1'
|
||||
video_image_upload_url = self.get_url_for_course_key(self.course.id, {'edx_video_id': edx_video_id})
|
||||
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'),
|
||||
extension=image_data.get('extension', '.png'),
|
||||
force_size=image_data.get('size', settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MIN_BYTES'])
|
||||
@@ -858,6 +877,10 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
|
||||
|
||||
@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})
|
||||
class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
"""
|
||||
@@ -867,35 +890,52 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
VIEW_NAME = 'transcript_preferences_handler'
|
||||
|
||||
@ddt.data(
|
||||
# Video transcript feature disabled
|
||||
(
|
||||
{},
|
||||
False,
|
||||
'',
|
||||
404,
|
||||
),
|
||||
# Error cases
|
||||
(
|
||||
{},
|
||||
'Invalid provider.'
|
||||
True,
|
||||
'Invalid provider.',
|
||||
400
|
||||
),
|
||||
(
|
||||
{
|
||||
'provider': ''
|
||||
},
|
||||
'Invalid provider.'
|
||||
True,
|
||||
'Invalid provider.',
|
||||
400
|
||||
),
|
||||
(
|
||||
{
|
||||
'provider': 'dummy-provider'
|
||||
},
|
||||
'Invalid provider.'
|
||||
True,
|
||||
'Invalid provider.',
|
||||
400
|
||||
),
|
||||
(
|
||||
{
|
||||
'provider': TranscriptProvider.CIELO24
|
||||
},
|
||||
'Invalid cielo24 fidelity.'
|
||||
True,
|
||||
'Invalid cielo24 fidelity.',
|
||||
400
|
||||
),
|
||||
(
|
||||
{
|
||||
'provider': TranscriptProvider.CIELO24,
|
||||
'cielo24_fidelity': 'PROFESSIONAL',
|
||||
},
|
||||
'Invalid cielo24 turnaround.'
|
||||
True,
|
||||
'Invalid cielo24 turnaround.',
|
||||
400
|
||||
),
|
||||
(
|
||||
{
|
||||
@@ -903,7 +943,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
'cielo24_fidelity': 'PROFESSIONAL',
|
||||
'cielo24_turnaround': 'STANDARD'
|
||||
},
|
||||
'Invalid languages.'
|
||||
True,
|
||||
'Invalid languages.',
|
||||
400
|
||||
),
|
||||
(
|
||||
{
|
||||
@@ -912,20 +954,26 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
'cielo24_turnaround': 'STANDARD',
|
||||
'preferred_languages': ['es', 'ur']
|
||||
},
|
||||
'Invalid languages.'
|
||||
True,
|
||||
'Invalid languages.',
|
||||
400
|
||||
),
|
||||
(
|
||||
{
|
||||
'provider': TranscriptProvider.THREE_PLAY_MEDIA
|
||||
},
|
||||
'Invalid 3play turnaround.'
|
||||
True,
|
||||
'Invalid 3play turnaround.',
|
||||
400
|
||||
),
|
||||
(
|
||||
{
|
||||
'provider': TranscriptProvider.THREE_PLAY_MEDIA,
|
||||
'three_play_turnaround': 'default'
|
||||
},
|
||||
'Invalid languages.'
|
||||
True,
|
||||
'Invalid languages.',
|
||||
400
|
||||
),
|
||||
(
|
||||
{
|
||||
@@ -933,7 +981,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
'three_play_turnaround': 'default',
|
||||
'preferred_languages': ['es', 'ur']
|
||||
},
|
||||
'Invalid languages.'
|
||||
True,
|
||||
'Invalid languages.',
|
||||
400
|
||||
),
|
||||
# Success
|
||||
(
|
||||
@@ -943,7 +993,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
'cielo24_turnaround': 'STANDARD',
|
||||
'preferred_languages': ['en']
|
||||
},
|
||||
''
|
||||
True,
|
||||
'',
|
||||
200
|
||||
),
|
||||
(
|
||||
{
|
||||
@@ -951,37 +1003,45 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
'three_play_turnaround': 'default',
|
||||
'preferred_languages': ['en']
|
||||
},
|
||||
''
|
||||
True,
|
||||
'',
|
||||
200
|
||||
)
|
||||
)
|
||||
@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.
|
||||
"""
|
||||
video_transcript_url = self.get_url_for_course_key(self.course.id)
|
||||
preferences_data = {
|
||||
'provider': preferences.get('provider', ''),
|
||||
'cielo24_fidelity': preferences.get('cielo24_fidelity', ''),
|
||||
'cielo24_turnaround': preferences.get('cielo24_turnaround', ''),
|
||||
'three_play_turnaround': preferences.get('three_play_turnaround', ''),
|
||||
'provider': preferences.get('provider'),
|
||||
'cielo24_fidelity': preferences.get('cielo24_fidelity'),
|
||||
'cielo24_turnaround': preferences.get('cielo24_turnaround'),
|
||||
'three_play_turnaround': preferences.get('three_play_turnaround'),
|
||||
'preferred_languages': preferences.get('preferred_languages', []),
|
||||
}
|
||||
|
||||
response = self.client.post(
|
||||
video_transcript_url,
|
||||
json.dumps(preferences_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
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(
|
||||
video_transcript_url,
|
||||
json.dumps(preferences_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
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, 400)
|
||||
self.assertEqual(response['error'], error_message)
|
||||
else:
|
||||
self.assertEqual(status_code, 200)
|
||||
self.assertTrue(response['transcript_preferences'], preferences_data)
|
||||
self.assertEqual(status_code, expected_status_code)
|
||||
self.assertEqual(response.get('error', ''), error_message)
|
||||
|
||||
# Remove modified and course_id fields from the response so as to check the expected transcript preferences.
|
||||
response.get('transcript_preferences', {}).pop('modified', None)
|
||||
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):
|
||||
"""
|
||||
@@ -1008,7 +1068,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
"""
|
||||
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
|
||||
preferences = get_transcript_preferences(course_id)
|
||||
self.assertIsNone(preferences)
|
||||
@@ -1024,23 +1084,39 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
self.assertIsNone(preferences)
|
||||
|
||||
@ddt.data(
|
||||
None,
|
||||
{
|
||||
'provider': TranscriptProvider.CIELO24,
|
||||
'cielo24_fidelity': 'PROFESSIONAL',
|
||||
'cielo24_turnaround': 'STANDARD',
|
||||
'preferred_languages': ['en']
|
||||
}
|
||||
(
|
||||
None,
|
||||
False
|
||||
),
|
||||
(
|
||||
{
|
||||
'provider': TranscriptProvider.CIELO24,
|
||||
'cielo24_fidelity': 'PROFESSIONAL',
|
||||
'cielo24_turnaround': 'STANDARD',
|
||||
'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')
|
||||
@patch('boto.s3.key.Key')
|
||||
@patch('boto.s3.connection.S3Connection')
|
||||
@patch('contentstore.views.videos.get_transcript_preferences')
|
||||
def test_transcript_preferences_metadata(self, transcript_preferences, mock_transcript_preferences,
|
||||
mock_conn, mock_key):
|
||||
def test_transcript_preferences_metadata(self, transcript_preferences, is_video_transcript_enabled,
|
||||
mock_transcript_preferences, mock_conn, mock_key):
|
||||
"""
|
||||
Tests that transcript preference metadata is only set if it is transcript
|
||||
preferences are present in request data.
|
||||
Tests that transcript preference metadata is only set if it is video transcript feature is enabled and
|
||||
transcript preferences are already stored in the system.
|
||||
"""
|
||||
file_name = 'test-video.mp4'
|
||||
request_data = {'files': [{'file_name': file_name, 'content_type': 'video/mp4'}]}
|
||||
@@ -1058,11 +1134,16 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
|
||||
mock_key.side_effect = [mock_key_instance] + [Mock()]
|
||||
|
||||
videos_handler_url = reverse_course_url('videos_handler', self.course.id)
|
||||
response = self.client.post(videos_handler_url, json.dumps(request_data), content_type='application/json')
|
||||
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')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# 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))
|
||||
else:
|
||||
with self.assertRaises(AssertionError):
|
||||
|
||||
@@ -33,6 +33,7 @@ from edxval.api import (
|
||||
remove_transcript_preferences,
|
||||
)
|
||||
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 contentstore.models import VideoUploadConfig
|
||||
@@ -129,7 +130,7 @@ class StatusDisplayStrings(object):
|
||||
"invalid_token": _INVALID_TOKEN,
|
||||
"imported": _IMPORTED,
|
||||
"transcription_in_progress": _TRANSCRIPTION_IN_PROGRESS,
|
||||
"transcription_ready": _TRANSCRIPT_READY,
|
||||
"transcript_ready": _TRANSCRIPT_READY,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
@@ -339,6 +340,11 @@ def transcript_preferences_handler(request, course_key_string):
|
||||
|
||||
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':
|
||||
data = request.json
|
||||
provider = data.get('provider')
|
||||
@@ -550,6 +556,7 @@ def videos_index_html(course):
|
||||
"""
|
||||
Returns an HTML page to display previous video uploads and allow new ones
|
||||
"""
|
||||
is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
|
||||
context = {
|
||||
'context_course': course,
|
||||
'image_upload_url': reverse_course_url('video_images_handler', unicode(course.id)),
|
||||
@@ -567,19 +574,21 @@ def videos_index_html(course):
|
||||
'max_width': settings.VIDEO_IMAGE_MAX_WIDTH,
|
||||
'max_height': settings.VIDEO_IMAGE_MAX_HEIGHT,
|
||||
'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({
|
||||
'video_transcript_settings': {
|
||||
if is_video_transcript_enabled:
|
||||
context['video_transcript_settings'] = {
|
||||
'transcript_preferences_handler_url': reverse_course_url(
|
||||
'transcript_preferences_handler',
|
||||
unicode(course.id)
|
||||
),
|
||||
'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)
|
||||
|
||||
@@ -662,9 +671,11 @@ def videos_post(course, request):
|
||||
('course_key', unicode(course.id)),
|
||||
]
|
||||
|
||||
transcript_preferences = get_transcript_preferences(unicode(course.id))
|
||||
if transcript_preferences is not None:
|
||||
metadata_list.append(('transcript_preferences', json.dumps(transcript_preferences)))
|
||||
is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
|
||||
if is_video_transcript_enabled:
|
||||
transcript_preferences = get_transcript_preferences(unicode(course.id))
|
||||
if transcript_preferences is not None:
|
||||
metadata_list.append(('transcript_preferences', json.dumps(transcript_preferences)))
|
||||
|
||||
for metadata_name, value in metadata_list:
|
||||
key.set_metadata(metadata_name, value)
|
||||
|
||||
@@ -16,6 +16,7 @@ define([
|
||||
videoUploadMaxFileSizeInGB,
|
||||
activeTranscriptPreferences,
|
||||
videoTranscriptSettings,
|
||||
isVideoTranscriptEnabled,
|
||||
videoImageSettings
|
||||
) {
|
||||
var activeView = new ActiveVideoUploadListView({
|
||||
@@ -27,6 +28,7 @@ define([
|
||||
videoImageSettings: videoImageSettings,
|
||||
activeTranscriptPreferences: activeTranscriptPreferences,
|
||||
videoTranscriptSettings: videoTranscriptSettings,
|
||||
isVideoTranscriptEnabled: isVideoTranscriptEnabled,
|
||||
onFileUploadDone: function(activeVideos) {
|
||||
$.ajax({
|
||||
url: videoHandlerUrl,
|
||||
|
||||
@@ -20,6 +20,10 @@ define(
|
||||
fail: 'upload_failed',
|
||||
success: 'upload_completed'
|
||||
},
|
||||
videoUploadMaxFileSizeInGB = 5,
|
||||
videoSupportedFileFormats = ['.mp4', '.mov'],
|
||||
createActiveUploadListView,
|
||||
$courseVideoSettingsButton,
|
||||
makeUploadUrl,
|
||||
getSentRequests,
|
||||
verifyUploadViewInfo,
|
||||
@@ -29,6 +33,22 @@ define(
|
||||
verifyA11YMessage,
|
||||
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() {
|
||||
beforeEach(function() {
|
||||
setFixtures(
|
||||
@@ -40,22 +60,8 @@ define(
|
||||
);
|
||||
TemplateHelpers.installTemplate('active-video-upload');
|
||||
TemplateHelpers.installTemplate('active-video-upload-list');
|
||||
this.postUrl = POST_URL;
|
||||
this.courseVideoSettingsButton = $('.course-video-settings-button');
|
||||
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: {}
|
||||
}
|
||||
});
|
||||
$courseVideoSettingsButton = $('.course-video-settings-button');
|
||||
this.view = createActiveUploadListView(true);
|
||||
this.view.render();
|
||||
jasmine.Ajax.install();
|
||||
});
|
||||
@@ -94,9 +100,15 @@ define(
|
||||
});
|
||||
|
||||
it('shows course video settings pane when course video settings button is clicked', function() {
|
||||
expect($('.course-video-settings-container')).not.toExist();
|
||||
this.courseVideoSettingsButton.click();
|
||||
expect($('.course-video-settings-container')).toExist();
|
||||
$courseVideoSettingsButton.click();
|
||||
expect(this.view.courseVideoSettingsView).toBeDefined();
|
||||
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() {
|
||||
@@ -328,7 +340,7 @@ define(
|
||||
'Your file could not be uploaded',
|
||||
StringUtils.interpolate(
|
||||
'{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(
|
||||
verifyUploadViewInfo(
|
||||
uploadView,
|
||||
'Your file could not be uploaded',
|
||||
'file.mp4 exceeds maximum size of ' + this.videoUploadMaxFileSizeInGB + ' GB.'
|
||||
'file.mp4 exceeds maximum size of ' + videoUploadMaxFileSizeInGB + ' GB.'
|
||||
);
|
||||
verifyA11YMessage(
|
||||
StringUtils.interpolate(
|
||||
@@ -431,7 +443,7 @@ define(
|
||||
expect(jasmine.Ajax.requests.count()).toEqual(caseInfo.numFiles);
|
||||
_.each(_.range(caseInfo.numFiles), function(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.requestHeaders['Content-Type']).toEqual('application/json');
|
||||
expect(request.requestHeaders.Accept).toContain('application/json');
|
||||
|
||||
@@ -43,6 +43,7 @@ define([
|
||||
this.postUrl = options.postUrl;
|
||||
this.activeTranscriptPreferences = options.activeTranscriptPreferences;
|
||||
this.videoTranscriptSettings = options.videoTranscriptSettings;
|
||||
this.isVideoTranscriptEnabled = options.isVideoTranscriptEnabled;
|
||||
this.videoSupportedFileFormats = options.videoSupportedFileFormats;
|
||||
this.videoUploadMaxFileSizeInGB = options.videoUploadMaxFileSizeInGB;
|
||||
this.onFileUploadDone = options.onFileUploadDone;
|
||||
@@ -62,16 +63,18 @@ define([
|
||||
supportedVideoTypes: this.videoSupportedFileFormats.join(', ')
|
||||
}
|
||||
);
|
||||
this.listenTo(
|
||||
Backbone,
|
||||
'coursevideosettings:syncActiveTranscriptPreferences',
|
||||
this.syncActiveTranscriptPreferences
|
||||
);
|
||||
this.listenTo(
|
||||
Backbone,
|
||||
'coursevideosettings:destroyCourseVideoSettingsView',
|
||||
this.destroyCourseVideoSettingsView
|
||||
);
|
||||
if (this.isVideoTranscriptEnabled) {
|
||||
this.listenTo(
|
||||
Backbone,
|
||||
'coursevideosettings:syncActiveTranscriptPreferences',
|
||||
this.syncActiveTranscriptPreferences
|
||||
);
|
||||
this.listenTo(
|
||||
Backbone,
|
||||
'coursevideosettings:destroyCourseVideoSettingsView',
|
||||
this.destroyCourseVideoSettingsView
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
syncActiveTranscriptPreferences: function(activeTranscriptPreferences) {
|
||||
@@ -79,12 +82,14 @@ define([
|
||||
},
|
||||
|
||||
showCourseVideoSettingsView: function(event) {
|
||||
this.courseVideoSettingsView = new CourseVideoSettingsView({
|
||||
activeTranscriptPreferences: this.activeTranscriptPreferences,
|
||||
videoTranscriptSettings: this.videoTranscriptSettings
|
||||
});
|
||||
this.courseVideoSettingsView.render();
|
||||
event.stopPropagation();
|
||||
if (this.isVideoTranscriptEnabled) {
|
||||
this.courseVideoSettingsView = new CourseVideoSettingsView({
|
||||
activeTranscriptPreferences: this.activeTranscriptPreferences,
|
||||
videoTranscriptSettings: this.videoTranscriptSettings
|
||||
});
|
||||
this.courseVideoSettingsView.render();
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
destroyCourseVideoSettingsView: function() {
|
||||
|
||||
@@ -337,6 +337,22 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett
|
||||
},
|
||||
|
||||
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');
|
||||
// Sync ActiveUploadListView with latest active plan.
|
||||
this.activeTranscriptionPlan = data;
|
||||
@@ -533,6 +549,8 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett
|
||||
},
|
||||
|
||||
closeCourseVideoSettings: function() {
|
||||
// TODO: Slide out when closing settings pane. We may need to hide the view instead of destroying it.
|
||||
|
||||
// Trigger destroy transcript event.
|
||||
Backbone.trigger('coursevideosettings:destroyCourseVideoSettingsView');
|
||||
|
||||
|
||||
@@ -45,9 +45,11 @@
|
||||
<%- gettext('Update Settings') %>
|
||||
<span id='update-button-text' class='sr'><%-gettext('Press update settings to update course video settings') %></span>
|
||||
</button>
|
||||
<span class='last-updated-text'>
|
||||
<%if (dateModified) { %>
|
||||
<span class='last-updated-text'><%- gettext('Last updated')%> <%- dateModified %></span>
|
||||
<%- gettext('Last updated')%> <%- dateModified %>
|
||||
<% } %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
${video_upload_max_file_size | n, dump_js_escaped_json},
|
||||
${active_transcript_preferences | 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}
|
||||
);
|
||||
});
|
||||
@@ -55,12 +56,14 @@
|
||||
<span class="sr">> </span>${_("Video Uploads")}
|
||||
</h1>
|
||||
|
||||
% if is_video_transcript_enabled :
|
||||
<nav class="nav-actions" aria-label="${_('Page Actions')}">
|
||||
<h3 class="sr">${_("Page Actions")}</h3>
|
||||
<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>
|
||||
</div>
|
||||
</nav>
|
||||
% endif
|
||||
</header>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,16 +5,24 @@ Django admin dashboard configuration for Video XModule.
|
||||
from config_models.admin import ConfigurationModelAdmin, KeyedConfigurationModelAdmin
|
||||
from django.contrib import admin
|
||||
|
||||
from openedx.core.djangoapps.video_config.forms import CourseHLSPlaybackFlagAdminForm
|
||||
from openedx.core.djangoapps.video_config.models import CourseHLSPlaybackEnabledFlag, HLSPlaybackEnabledFlag
|
||||
from openedx.core.djangoapps.video_config.forms import (
|
||||
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.
|
||||
"""
|
||||
form = CourseHLSPlaybackFlagAdminForm
|
||||
# Make abstract base class
|
||||
class Meta(object):
|
||||
abstract = True
|
||||
|
||||
search_fields = ['course_id']
|
||||
fieldsets = (
|
||||
(None, {
|
||||
@@ -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(CourseHLSPlaybackEnabledFlag, CourseHLSPlaybackEnabledFlagAdmin)
|
||||
admin.site.register(VideoTranscriptEnabledFlag, ConfigurationModelAdmin)
|
||||
admin.site.register(CourseVideoTranscriptEnabledFlag, CourseHLSPlaybackEnabledFlagAdmin)
|
||||
|
||||
@@ -7,20 +7,20 @@ from django import forms
|
||||
from opaque_keys import InvalidKeyError
|
||||
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
|
||||
|
||||
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):
|
||||
model = CourseHLSPlaybackEnabledFlag
|
||||
fields = '__all__'
|
||||
abstract = True
|
||||
|
||||
def clean_course_id(self):
|
||||
"""
|
||||
@@ -42,3 +42,23 @@ class CourseHLSPlaybackFlagAdminForm(forms.ModelForm):
|
||||
raise forms.ValidationError(msg)
|
||||
|
||||
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__'
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# -*- 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):
|
||||
course_key=unicode(self.course_id),
|
||||
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,34 +13,115 @@ from openedx.core.djangoapps.video_config.models import CourseHLSPlaybackEnabled
|
||||
|
||||
|
||||
@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,
|
||||
course_id=None, enabled_for_course=False
|
||||
):
|
||||
"""
|
||||
Yields HLS Playback Configuration records for unit tests
|
||||
Yields video feature configuration records for unit tests
|
||||
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
|
||||
enabled_for_all_courses (bool): Specifies whether feature is enabled for all courses
|
||||
course_id (CourseLocator): Course locator for course specific configurations
|
||||
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:
|
||||
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
|
||||
|
||||
|
||||
class FeatureFlagTestMixin(object):
|
||||
"""
|
||||
Adds util methods to test the behavior of the flags for video feature.
|
||||
"""
|
||||
course_id_1 = CourseLocator(org="edx", course="course", run="run")
|
||||
course_id_2 = CourseLocator(org="edx", course="course2", run="run")
|
||||
|
||||
def verify_feature_flags(self, all_courses_model_class, course_specific_model_class,
|
||||
global_flag, enabled_for_all_courses, enabled_for_course_1):
|
||||
"""
|
||||
Verifies that the feature flags works correctly on tweaking global flags in combination
|
||||
with course-specific 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,
|
||||
enabled_for_all_courses=enabled_for_all_courses,
|
||||
course_id=self.course_id_1,
|
||||
enabled_for_course=enabled_for_course_1
|
||||
):
|
||||
self.assertEqual(
|
||||
all_courses_model_class.feature_enabled(self.course_id_1),
|
||||
global_flag and (enabled_for_all_courses or enabled_for_course_1)
|
||||
)
|
||||
self.assertEqual(
|
||||
all_courses_model_class.feature_enabled(self.course_id_2),
|
||||
global_flag and enabled_for_all_courses
|
||||
)
|
||||
|
||||
def verify_enable_disable_course_flag(self, all_courses_model_class, course_specific_model_class):
|
||||
"""
|
||||
Verifies that the course specific flag, once enabled for a course, can also be disabled.
|
||||
"""
|
||||
with video_feature_flags(
|
||||
all_courses_model_class=all_courses_model_class,
|
||||
course_specific_model_class=course_specific_model_class,
|
||||
global_flag=True,
|
||||
enabled_for_all_courses=False,
|
||||
course_id=self.course_id_1,
|
||||
enabled_for_course=True
|
||||
):
|
||||
self.assertTrue(all_courses_model_class.feature_enabled(self.course_id_1))
|
||||
with video_feature_flags(
|
||||
all_courses_model_class=all_courses_model_class,
|
||||
course_specific_model_class=course_specific_model_class,
|
||||
global_flag=True,
|
||||
enabled_for_all_courses=False,
|
||||
course_id=self.course_id_1,
|
||||
enabled_for_course=False
|
||||
):
|
||||
self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_1))
|
||||
|
||||
def verify_enable_disable_globally(self, all_courses_model_class, course_specific_model_class):
|
||||
"""
|
||||
Verifies that global flag, once enabled globally, can also be disabled.
|
||||
"""
|
||||
with video_feature_flags(
|
||||
all_courses_model_class=all_courses_model_class,
|
||||
course_specific_model_class=course_specific_model_class,
|
||||
global_flag=True,
|
||||
enabled_for_all_courses=True,
|
||||
):
|
||||
self.assertTrue(all_courses_model_class.feature_enabled(self.course_id_1))
|
||||
self.assertTrue(all_courses_model_class.feature_enabled(self.course_id_2))
|
||||
with video_feature_flags(
|
||||
all_courses_model_class=all_courses_model_class,
|
||||
course_specific_model_class=course_specific_model_class,
|
||||
global_flag=True,
|
||||
enabled_for_all_courses=False,
|
||||
):
|
||||
self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_1))
|
||||
self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_2))
|
||||
with video_feature_flags(
|
||||
all_courses_model_class=all_courses_model_class,
|
||||
course_specific_model_class=course_specific_model_class,
|
||||
global_flag=False,
|
||||
):
|
||||
self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_1))
|
||||
self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_2))
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestHLSPlaybackFlag(TestCase):
|
||||
class TestHLSPlaybackFlag(TestCase, FeatureFlagTestMixin):
|
||||
"""
|
||||
Tests the behavior of the flags for HLS Playback feature.
|
||||
These are set via Django admin settings.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestHLSPlaybackFlag, self).setUp()
|
||||
self.course_id_1 = CourseLocator(org="edx", course="course", run="run")
|
||||
self.course_id_2 = CourseLocator(org="edx", course="course2", run="run")
|
||||
|
||||
@ddt.data(
|
||||
*itertools.product(
|
||||
@@ -52,61 +133,78 @@ class TestHLSPlaybackFlag(TestCase):
|
||||
@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
|
||||
Tests that the HLS Playback feature flags works correctly on tweaking global flags in combination
|
||||
with course-specific flags.
|
||||
"""
|
||||
with hls_playback_feature_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,
|
||||
course_id=self.course_id_1,
|
||||
enabled_for_course=enabled_for_course_1
|
||||
):
|
||||
self.assertEqual(
|
||||
HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1),
|
||||
global_flag and (enabled_for_all_courses or enabled_for_course_1)
|
||||
)
|
||||
self.assertEqual(
|
||||
HLSPlaybackEnabledFlag.feature_enabled(self.course_id_2),
|
||||
global_flag and 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.
|
||||
"""
|
||||
with hls_playback_feature_flags(
|
||||
global_flag=True,
|
||||
enabled_for_all_courses=False,
|
||||
course_id=self.course_id_1,
|
||||
enabled_for_course=True
|
||||
):
|
||||
self.assertTrue(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1))
|
||||
with hls_playback_feature_flags(
|
||||
global_flag=True,
|
||||
enabled_for_all_courses=False,
|
||||
course_id=self.course_id_1,
|
||||
enabled_for_course=False
|
||||
):
|
||||
self.assertFalse(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1))
|
||||
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.
|
||||
"""
|
||||
with hls_playback_feature_flags(
|
||||
global_flag=True,
|
||||
enabled_for_all_courses=True,
|
||||
):
|
||||
self.assertTrue(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1))
|
||||
self.assertTrue(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_2))
|
||||
with hls_playback_feature_flags(
|
||||
global_flag=True,
|
||||
enabled_for_all_courses=False,
|
||||
):
|
||||
self.assertFalse(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1))
|
||||
self.assertFalse(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_2))
|
||||
with hls_playback_feature_flags(
|
||||
global_flag=False,
|
||||
):
|
||||
self.assertFalse(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_1))
|
||||
self.assertFalse(HLSPlaybackEnabledFlag.feature_enabled(self.course_id_2))
|
||||
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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user