Merge pull request #16536 from edx/enable-videos-upload-by-default
Enable Video Uploads by default
This commit is contained in:
@@ -672,7 +672,6 @@ def videos_post(course, request):
|
|||||||
return JsonResponse({'error': error}, status=400)
|
return JsonResponse({'error': error}, status=400)
|
||||||
|
|
||||||
bucket = storage_service_bucket()
|
bucket = storage_service_bucket()
|
||||||
course_video_upload_token = course.video_upload_pipeline['course_video_upload_token']
|
|
||||||
req_files = data['files']
|
req_files = data['files']
|
||||||
resp_files = []
|
resp_files = []
|
||||||
|
|
||||||
@@ -689,11 +688,16 @@ def videos_post(course, request):
|
|||||||
key = storage_service_key(bucket, file_name=edx_video_id)
|
key = storage_service_key(bucket, file_name=edx_video_id)
|
||||||
|
|
||||||
metadata_list = [
|
metadata_list = [
|
||||||
('course_video_upload_token', course_video_upload_token),
|
|
||||||
('client_video_id', file_name),
|
('client_video_id', file_name),
|
||||||
('course_key', unicode(course.id)),
|
('course_key', unicode(course.id)),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Only include `course_video_upload_token` if its set, as it won't be required if video uploads
|
||||||
|
# are enabled by default.
|
||||||
|
course_video_upload_token = course.video_upload_pipeline.get('course_video_upload_token')
|
||||||
|
if course_video_upload_token:
|
||||||
|
metadata_list.append(('course_video_upload_token', course_video_upload_token))
|
||||||
|
|
||||||
is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
|
is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
|
||||||
if is_video_transcript_enabled:
|
if is_video_transcript_enabled:
|
||||||
transcript_preferences = get_transcript_preferences(unicode(course.id))
|
transcript_preferences = get_transcript_preferences(unicode(course.id))
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from django.conf import settings
|
|||||||
import requests
|
import requests
|
||||||
from lazy import lazy
|
from lazy import lazy
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
from openedx.core.djangoapps.video_pipeline.models import VideoUploadsEnabledByDefault
|
||||||
from openedx.core.lib.license import LicenseMixin
|
from openedx.core.lib.license import LicenseMixin
|
||||||
from path import Path as path
|
from path import Path as path
|
||||||
from pytz import utc
|
from pytz import utc
|
||||||
@@ -1337,7 +1338,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor, LicenseMixin):
|
|||||||
"""
|
"""
|
||||||
Returns whether the video pipeline advanced setting is configured for this course.
|
Returns whether the video pipeline advanced setting is configured for this course.
|
||||||
"""
|
"""
|
||||||
return (
|
return VideoUploadsEnabledByDefault.feature_enabled(course_id=self.id) or (
|
||||||
self.video_upload_pipeline is not None and
|
self.video_upload_pipeline is not None and
|
||||||
'course_video_upload_token' in self.video_upload_pipeline
|
'course_video_upload_token' in self.video_upload_pipeline
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ from django.contrib import admin
|
|||||||
|
|
||||||
from openedx.core.djangoapps.video_config.forms import (
|
from openedx.core.djangoapps.video_config.forms import (
|
||||||
CourseHLSPlaybackFlagAdminForm,
|
CourseHLSPlaybackFlagAdminForm,
|
||||||
|
CourseVideoTranscriptFlagAdminForm,
|
||||||
)
|
)
|
||||||
from openedx.core.djangoapps.video_config.models import (
|
from openedx.core.djangoapps.video_config.models import (
|
||||||
CourseHLSPlaybackEnabledFlag, HLSPlaybackEnabledFlag,
|
CourseHLSPlaybackEnabledFlag, HLSPlaybackEnabledFlag,
|
||||||
CourseVideoTranscriptEnabledFlag, VideoTranscriptEnabledFlag
|
CourseVideoTranscriptEnabledFlag, VideoTranscriptEnabledFlag,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -45,9 +46,10 @@ class CourseVideoTranscriptEnabledFlagAdmin(CourseSpecificEnabledFlagBaseAdmin):
|
|||||||
Admin of Video Transcript feature on course-by-course basis.
|
Admin of Video Transcript feature on course-by-course basis.
|
||||||
Allows searching by course id.
|
Allows searching by course id.
|
||||||
"""
|
"""
|
||||||
form = CourseHLSPlaybackFlagAdminForm
|
form = CourseVideoTranscriptFlagAdminForm
|
||||||
|
|
||||||
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(VideoTranscriptEnabledFlag, ConfigurationModelAdmin)
|
||||||
admin.site.register(CourseVideoTranscriptEnabledFlag, CourseHLSPlaybackEnabledFlagAdmin)
|
admin.site.register(CourseVideoTranscriptEnabledFlag, CourseVideoTranscriptEnabledFlagAdmin)
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ 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, CourseVideoTranscriptEnabledFlag
|
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__)
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ from contextlib import contextmanager
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from opaque_keys.edx.locator import CourseLocator
|
from opaque_keys.edx.locator import CourseLocator
|
||||||
from openedx.core.djangoapps.video_config.models import CourseHLSPlaybackEnabledFlag, HLSPlaybackEnabledFlag
|
from openedx.core.djangoapps.video_config.models import (
|
||||||
|
CourseHLSPlaybackEnabledFlag, HLSPlaybackEnabledFlag,
|
||||||
|
CourseVideoTranscriptEnabledFlag, VideoTranscriptEnabledFlag,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
@@ -184,8 +187,8 @@ class TestVideoTranscriptFlag(TestCase, FeatureFlagTestMixin):
|
|||||||
with course-specific flags.
|
with course-specific flags.
|
||||||
"""
|
"""
|
||||||
self.verify_feature_flags(
|
self.verify_feature_flags(
|
||||||
all_courses_model_class=HLSPlaybackEnabledFlag,
|
all_courses_model_class=VideoTranscriptEnabledFlag,
|
||||||
course_specific_model_class=CourseHLSPlaybackEnabledFlag,
|
course_specific_model_class=CourseVideoTranscriptEnabledFlag,
|
||||||
global_flag=global_flag,
|
global_flag=global_flag,
|
||||||
enabled_for_all_courses=enabled_for_all_courses,
|
enabled_for_all_courses=enabled_for_all_courses,
|
||||||
enabled_for_course_1=enabled_for_course_1
|
enabled_for_course_1=enabled_for_course_1
|
||||||
@@ -196,8 +199,8 @@ class TestVideoTranscriptFlag(TestCase, FeatureFlagTestMixin):
|
|||||||
Ensures that the Video Transcript course specific flag, once enabled for a course, can also be disabled.
|
Ensures that the Video Transcript course specific flag, once enabled for a course, can also be disabled.
|
||||||
"""
|
"""
|
||||||
self.verify_enable_disable_course_flag(
|
self.verify_enable_disable_course_flag(
|
||||||
all_courses_model_class=HLSPlaybackEnabledFlag,
|
all_courses_model_class=VideoTranscriptEnabledFlag,
|
||||||
course_specific_model_class=CourseHLSPlaybackEnabledFlag
|
course_specific_model_class=CourseVideoTranscriptEnabledFlag
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_enable_disable_globally(self):
|
def test_enable_disable_globally(self):
|
||||||
@@ -205,6 +208,6 @@ class TestVideoTranscriptFlag(TestCase, FeatureFlagTestMixin):
|
|||||||
Ensures that the Video Transcript flag, once enabled globally, can also be disabled.
|
Ensures that the Video Transcript flag, once enabled globally, can also be disabled.
|
||||||
"""
|
"""
|
||||||
self.verify_enable_disable_globally(
|
self.verify_enable_disable_globally(
|
||||||
all_courses_model_class=HLSPlaybackEnabledFlag,
|
all_courses_model_class=VideoTranscriptEnabledFlag,
|
||||||
course_specific_model_class=CourseHLSPlaybackEnabledFlag
|
course_specific_model_class=CourseVideoTranscriptEnabledFlag
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,23 @@ Django admin for Video Pipeline models.
|
|||||||
from config_models.admin import ConfigurationModelAdmin
|
from config_models.admin import ConfigurationModelAdmin
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from openedx.core.djangoapps.video_pipeline.models import VideoPipelineIntegration
|
from openedx.core.djangoapps.video_config.admin import CourseSpecificEnabledFlagBaseAdmin
|
||||||
|
from openedx.core.djangoapps.video_pipeline.forms import CourseVideoUploadsEnabledByDefaultAdminForm
|
||||||
|
from openedx.core.djangoapps.video_pipeline.models import (
|
||||||
|
VideoPipelineIntegration,
|
||||||
|
VideoUploadsEnabledByDefault,
|
||||||
|
CourseVideoUploadsEnabledByDefault,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CourseVideoUploadsEnabledByDefaultAdmin(CourseSpecificEnabledFlagBaseAdmin):
|
||||||
|
"""
|
||||||
|
Admin of video uploads enabled by default feature on course-by-course basis.
|
||||||
|
Allows searching by course id.
|
||||||
|
"""
|
||||||
|
form = CourseVideoUploadsEnabledByDefaultAdminForm
|
||||||
|
|
||||||
admin.site.register(VideoPipelineIntegration, ConfigurationModelAdmin)
|
admin.site.register(VideoPipelineIntegration, ConfigurationModelAdmin)
|
||||||
|
|
||||||
|
admin.site.register(VideoUploadsEnabledByDefault, ConfigurationModelAdmin)
|
||||||
|
admin.site.register(CourseVideoUploadsEnabledByDefault, CourseVideoUploadsEnabledByDefaultAdmin)
|
||||||
|
|||||||
15
openedx/core/djangoapps/video_pipeline/forms.py
Normal file
15
openedx/core/djangoapps/video_pipeline/forms.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
"""
|
||||||
|
Defines a form to provide validations for course-specific configuration.
|
||||||
|
"""
|
||||||
|
from openedx.core.djangoapps.video_config.forms import CourseSpecificFlagAdminBaseForm
|
||||||
|
from openedx.core.djangoapps.video_pipeline.models import CourseVideoUploadsEnabledByDefault
|
||||||
|
|
||||||
|
|
||||||
|
class CourseVideoUploadsEnabledByDefaultAdminForm(CourseSpecificFlagAdminBaseForm):
|
||||||
|
"""
|
||||||
|
Form for course-specific Video Uploads enabled by default configuration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
model = CourseVideoUploadsEnabledByDefault
|
||||||
|
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_pipeline', '0002_auto_20171114_0704'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CourseVideoUploadsEnabledByDefault',
|
||||||
|
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='VideoUploadsEnabledByDefault',
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -6,6 +6,8 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from openedx.core.djangoapps.xmodule_django.models import CourseKeyField
|
||||||
|
|
||||||
|
|
||||||
class VideoPipelineIntegration(ConfigurationModel):
|
class VideoPipelineIntegration(ConfigurationModel):
|
||||||
"""
|
"""
|
||||||
@@ -37,3 +39,62 @@ class VideoPipelineIntegration(ConfigurationModel):
|
|||||||
# in lms/startup.py.
|
# in lms/startup.py.
|
||||||
User = get_user_model() # pylint: disable=invalid-name
|
User = get_user_model() # pylint: disable=invalid-name
|
||||||
return User.objects.get(username=self.service_username)
|
return User.objects.get(username=self.service_username)
|
||||||
|
|
||||||
|
|
||||||
|
class VideoUploadsEnabledByDefault(ConfigurationModel):
|
||||||
|
"""
|
||||||
|
Enables video uploads enabled By default feature across the platform.
|
||||||
|
"""
|
||||||
|
# this field overrides course-specific settings
|
||||||
|
enabled_for_all_courses = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def feature_enabled(cls, course_id):
|
||||||
|
"""
|
||||||
|
Looks at the currently active configuration model to determine whether
|
||||||
|
the VideoUploadsEnabledByDefault 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 a course
|
||||||
|
with CourseVideoUploadsEnabledByDefault enabled, then the
|
||||||
|
feature is available.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
course_id (CourseKey): course id for whom feature will be checked.
|
||||||
|
"""
|
||||||
|
if not cls.is_enabled():
|
||||||
|
return False
|
||||||
|
elif not cls.current().enabled_for_all_courses:
|
||||||
|
feature = (CourseVideoUploadsEnabledByDefault.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 = VideoUploadsEnabledByDefault.current()
|
||||||
|
return u"VideoUploadsEnabledByDefault: enabled {is_enabled}".format(
|
||||||
|
is_enabled=current_model.is_enabled()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CourseVideoUploadsEnabledByDefault(ConfigurationModel):
|
||||||
|
"""
|
||||||
|
Enables video uploads enabled by default feature for a specific course. Its global feature must be
|
||||||
|
enabled for this to take effect.
|
||||||
|
"""
|
||||||
|
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 Uploads {not_enabled}Enabled by default.".format(
|
||||||
|
course_key=unicode(self.course_id),
|
||||||
|
not_enabled=not_en
|
||||||
|
)
|
||||||
|
|||||||
59
openedx/core/djangoapps/video_pipeline/tests/test_models.py
Normal file
59
openedx/core/djangoapps/video_pipeline/tests/test_models.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
"""
|
||||||
|
Tests for the models that configures 'VideoUploadsEnabledByDefault' feature.
|
||||||
|
"""
|
||||||
|
import ddt
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from openedx.core.djangoapps.video_config.tests.test_models import FeatureFlagTestMixin
|
||||||
|
from openedx.core.djangoapps.video_pipeline.models import (
|
||||||
|
CourseVideoUploadsEnabledByDefault, VideoUploadsEnabledByDefault,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
|
class TestVideoUploadsEnabledByDefault(TestCase, FeatureFlagTestMixin):
|
||||||
|
"""
|
||||||
|
Tests the behavior of the flags for video uploads enabled by default feature.
|
||||||
|
These are set via Django admin settings.
|
||||||
|
"""
|
||||||
|
@ddt.data(
|
||||||
|
*itertools.product(
|
||||||
|
(True, False),
|
||||||
|
(True, False),
|
||||||
|
(True, False),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ddt.unpack
|
||||||
|
def test_video_upload_enabled_by_default_feature_flags(self, global_flag,
|
||||||
|
enabled_for_all_courses, enabled_for_course_1):
|
||||||
|
"""
|
||||||
|
Tests that video uploads enabled by default feature flags works correctly on tweaking global flags
|
||||||
|
in combination with course-specific flags.
|
||||||
|
"""
|
||||||
|
self.verify_feature_flags(
|
||||||
|
all_courses_model_class=VideoUploadsEnabledByDefault,
|
||||||
|
course_specific_model_class=CourseVideoUploadsEnabledByDefault,
|
||||||
|
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 uploads enabled by default course specific flag, once enabled for a course,
|
||||||
|
can also be disabled.
|
||||||
|
"""
|
||||||
|
self.verify_enable_disable_course_flag(
|
||||||
|
all_courses_model_class=VideoUploadsEnabledByDefault,
|
||||||
|
course_specific_model_class=CourseVideoUploadsEnabledByDefault
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_enable_disable_globally(self):
|
||||||
|
"""
|
||||||
|
Ensures that the video uploads enabled by default flag, once enabled globally, can also be disabled.
|
||||||
|
"""
|
||||||
|
self.verify_enable_disable_globally(
|
||||||
|
all_courses_model_class=VideoUploadsEnabledByDefault,
|
||||||
|
course_specific_model_class=CourseVideoUploadsEnabledByDefault
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user