Add video transcript config model flags - EDUCATOR-1224

This commit is contained in:
Mushtaq Ali
2017-09-11 19:04:47 +05:00
committed by muzaffaryousaf
parent 2203de4a85
commit 538a3d7801
13 changed files with 545 additions and 156 deletions

View File

@@ -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)

View File

@@ -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__'

View File

@@ -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,
},
),
]

View File

@@ -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
)

View File

@@ -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
)