Commit f317244a by Peter Fogg

Convert Video Alpha to metadata-only.

parent 74b81527
...@@ -42,6 +42,9 @@ Common: Add a manage.py that knows about edx-platform specific settings and proj ...@@ -42,6 +42,9 @@ Common: Add a manage.py that knows about edx-platform specific settings and proj
Common: Added *experimental* support for jsinput type. Common: Added *experimental* support for jsinput type.
Studio: Remove XML from HTML5 video component editor. All settings are
moved to be edited as metadata.
Common: Added setting to specify Celery Broker vhost Common: Added setting to specify Celery Broker vhost
Common: Utilize new XBlock bulk save API in LMS and CMS. Common: Utilize new XBlock bulk save API in LMS and CMS.
......
...@@ -228,6 +228,26 @@ def i_created_a_video_component(step): ...@@ -228,6 +228,26 @@ def i_created_a_video_component(step):
) )
@step('I have created a Video Alpha component$')
def i_created_video_alpha(step):
step.given('I have enabled the videoalpha advanced module')
world.css_click('a.course-link')
step.given('I have added a new subsection')
step.given('I expand the first section')
world.css_click('a.new-unit-item')
world.css_click('.large-advanced-icon')
world.click_component_from_menu('videoalpha', None, '.xmodule_VideoAlphaModule')
@step('I have enabled the (.*) advanced module$')
def i_enabled_the_advanced_module(step, module):
step.given('I have opened a new course section in Studio')
world.css_click('.nav-course-settings')
world.css_click('.nav-course-settings-advanced')
type_in_codemirror(0, '["%s"]' % module)
press_the_notification_button(step, 'Save')
@step('I have clicked the new unit button') @step('I have clicked the new unit button')
def open_new_unit(step): def open_new_unit(step):
step.given('I have opened a new course section in Studio') step.given('I have opened a new course section in Studio')
......
Feature: Video Alpha Component Editor
As a course author, I want to be able to create videoalpha components.
Scenario: User can view metadata
Given I have created a Video Alpha component
And I edit and select Settings
Then I see the correct videoalpha settings and default values
Scenario: User can modify display name
Given I have created a Video Alpha component
And I edit and select Settings
Then I can modify the display name
And my display name change is persisted on save
@skip
Scenario: Captions are hidden when "show captions" is false
Given I have created a Video component
And I have set "show captions" to False
Then when I view the video it does not show the captions
@skip
Scenario: Captions are shown when "show captions" is true
Given I have created a Video component
And I have set "show captions" to True
Then when I view the video it does show the captions
# disable missing docstring
# pylint: disable=C0111
from lettuce import world, step
@step('I see the correct videoalpha settings and default values$')
def correct_videoalpha_settings(_step):
world.verify_all_setting_entries([['Default Speed', '', False],
['Display Name', 'Video Alpha', False],
['Download Track', '', False],
['Download Video', '', False],
['HTML5 Subtitles', '', False],
['Show Captions', 'True', False],
['Speed: .75x', '', False],
['Speed: 1.25x', '', False],
['Speed: 1.5x', '', False],
['Video Sources', '', False]])
Feature: Video Alpha Component
As a course author, I want to be able to view my created videos in Studio.
Scenario: Autoplay is disabled in Studio
Given I have created a Video Alpha component
Then when I view the video alpha it does not have autoplay enabled
# disable missing docstring
# pylint: disable=C0111
from lettuce import world, step
@step('when I view the video alpha it does not have autoplay enabled')
def does_not_autoplay(_step):
assert world.css_find('.videoalpha')[0]['data-autoplay'] == 'False'
assert world.css_has_class('.video_control', 'play')
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
% endfor % endfor
</ul> </ul>
</div> </div>
<div class="${'tabs-wrapper' if (len(tabs) != 1) else 'editor-single-tab' }"> <div class="tabs-wrapper">
% for tab in tabs: % for tab in tabs:
<div class="component-tab ${'is-inactive' if not tab.get('current', False) else ''}" id="tab-${html_id}-${loop.index}" > <div class="component-tab ${'is-inactive' if not tab.get('current', False) else ''}" id="tab-${html_id}-${loop.index}" >
<%include file="${tab['template']}" args="tabName=tab['name']"/> <%include file="${tab['template']}" args="tabName=tab['name']"/>
......
...@@ -20,5 +20,9 @@ ...@@ -20,5 +20,9 @@
<%static:include path="js/metadata-option-entry.underscore" /> <%static:include path="js/metadata-option-entry.underscore" />
</script> </script>
<script id="metadata-list-entry" type="text/template">
<%static:include path="js/metadata-list-entry.underscore" />
</script>
<div class="wrapper-comp-settings metadata_edit" id="settings-tab" data-metadata='${json.dumps(editable_metadata_fields) | h}'/> <div class="wrapper-comp-settings metadata_edit" id="settings-tab" data-metadata='${json.dumps(editable_metadata_fields) | h}'/>
...@@ -81,12 +81,16 @@ class BaseTestXmodule(ModuleStoreTestCase): ...@@ -81,12 +81,16 @@ class BaseTestXmodule(ModuleStoreTestCase):
# Allow us to assert that the template was called in the same way from # Allow us to assert that the template was called in the same way from
# different code paths while maintaining the type returned by render_template # different code paths while maintaining the type returned by render_template
self.runtime.render_template = lambda template, context: u'{!r}, {!r}'.format(template, sorted(context.items())) self.runtime.render_template = lambda template, context: u'{!r}, {!r}'.format(template, sorted(context.items()))
model_data = {'location': self.item_descriptor.location} model_data = {'location': self.item_descriptor.location}
model_data.update(self.MODEL_DATA) model_data.update(self.MODEL_DATA)
self.item_module = self.item_descriptor.module_class( self.item_module = self.item_descriptor.module_class(
self.runtime, self.item_descriptor, model_data self.runtime,
self.item_descriptor,
model_data
) )
self.item_url = Location(self.item_module.location).url() self.item_url = Location(self.item_module.location).url()
# login all users for acces to Xmodule # login all users for acces to Xmodule
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
from . import BaseTestXmodule from . import BaseTestXmodule
from .test_videoalpha_xml import SOURCE_XML from .test_videoalpha_xml import SOURCE_XML
from django.conf import settings from django.conf import settings
from xmodule.videoalpha_module import _create_youtube_string
class TestVideo(BaseTestXmodule): class TestVideo(BaseTestXmodule):
...@@ -15,6 +16,14 @@ class TestVideo(BaseTestXmodule): ...@@ -15,6 +16,14 @@ class TestVideo(BaseTestXmodule):
'data': DATA 'data': DATA
} }
def setUp(self):
# Since the VideoAlphaDescriptor changes `self._model_data`,
# we need to instantiate `self.item_module` through
# `self.item_descriptor` rather than directly constructing it
super(TestVideo, self).setUp()
self.item_module = self.item_descriptor.xmodule(self.runtime)
self.item_module.runtime.render_template = lambda template, context: context
def test_handle_ajax_dispatch(self): def test_handle_ajax_dispatch(self):
responses = { responses = {
user.username: self.clients[user.username].post( user.username: self.clients[user.username].post(
...@@ -34,22 +43,31 @@ class TestVideo(BaseTestXmodule): ...@@ -34,22 +43,31 @@ class TestVideo(BaseTestXmodule):
def test_videoalpha_constructor(self): def test_videoalpha_constructor(self):
"""Make sure that all parameters extracted correclty from xml""" """Make sure that all parameters extracted correclty from xml"""
fragment = self.runtime.render(self.item_module, None, 'student_view') context = self.item_module.get_html()
sources = {
'main': '.../mit-3091x/M-3091X-FA12-L21-3_100.mp4',
'mp4': '.../mit-3091x/M-3091X-FA12-L21-3_100.mp4',
'webm': '.../mit-3091x/M-3091X-FA12-L21-3_100.webm',
'ogv': '.../mit-3091x/M-3091X-FA12-L21-3_100.ogv'
}
expected_context = { expected_context = {
'data_dir': getattr(self, 'data_dir', None), 'data_dir': getattr(self, 'data_dir', None),
'caption_asset_path': '/c4x/MITx/999/asset/subs_', 'caption_asset_path': '/c4x/MITx/999/asset/subs_',
'show_captions': self.item_module.show_captions, 'show_captions': True,
'display_name': self.item_module.display_name_with_default, 'display_name': 'A Name',
'end': self.item_module.end_time, 'end': 3610.0,
'id': self.item_module.location.html_id(), 'id': self.item_module.location.html_id(),
'sources': self.item_module.sources, 'sources': sources,
'start': self.item_module.start_time, 'start': 3603.0,
'sub': self.item_module.sub, 'sub': 'a_sub_file.srt.sjson',
'track': self.item_module.track, 'track': '',
'youtube_streams': self.item_module.youtube_streams, 'youtube_streams': _create_youtube_string(self.item_module),
'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True) 'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True)
} }
self.assertEqual(fragment.content, self.runtime.render_template('videoalpha.html', expected_context))
self.assertEqual(context, expected_context)
class TestVideoNonYouTube(TestVideo): class TestVideoNonYouTube(TestVideo):
...@@ -57,9 +75,8 @@ class TestVideoNonYouTube(TestVideo): ...@@ -57,9 +75,8 @@ class TestVideoNonYouTube(TestVideo):
DATA = """ DATA = """
<videoalpha show_captions="true" <videoalpha show_captions="true"
data_dir="" display_name="A Name"
caption_asset_path="" sub="a_sub_file.srt.sjson"
autoplay="true"
start_time="01:00:03" end_time="01:00:10" start_time="01:00:03" end_time="01:00:10"
> >
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.mp4"/> <source src=".../mit-3091x/M-3091X-FA12-L21-3_100.mp4"/>
...@@ -75,20 +92,28 @@ class TestVideoNonYouTube(TestVideo): ...@@ -75,20 +92,28 @@ class TestVideoNonYouTube(TestVideo):
"""Make sure that if the 'youtube' attribute is omitted in XML, then """Make sure that if the 'youtube' attribute is omitted in XML, then
the template generates an empty string for the YouTube streams. the template generates an empty string for the YouTube streams.
""" """
sources = {
u'main': u'.../mit-3091x/M-3091X-FA12-L21-3_100.mp4',
u'mp4': u'.../mit-3091x/M-3091X-FA12-L21-3_100.mp4',
u'webm': u'.../mit-3091x/M-3091X-FA12-L21-3_100.webm',
u'ogv': u'.../mit-3091x/M-3091X-FA12-L21-3_100.ogv'
}
context = self.item_module.get_html()
fragment = self.runtime.render(self.item_module, None, 'student_view')
expected_context = { expected_context = {
'data_dir': getattr(self, 'data_dir', None), 'data_dir': getattr(self, 'data_dir', None),
'caption_asset_path': '/c4x/MITx/999/asset/subs_', 'caption_asset_path': '/c4x/MITx/999/asset/subs_',
'show_captions': self.item_module.show_captions, 'show_captions': True,
'display_name': self.item_module.display_name_with_default, 'display_name': 'A Name',
'end': self.item_module.end_time, 'end': 3610.0,
'id': self.item_module.location.html_id(), 'id': self.item_module.location.html_id(),
'sources': self.item_module.sources, 'sources': sources,
'start': self.item_module.start_time, 'start': 3603.0,
'sub': self.item_module.sub, 'sub': 'a_sub_file.srt.sjson',
'track': self.item_module.track, 'track': '',
'youtube_streams': '', 'youtube_streams': '',
'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True) 'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True)
} }
self.assertEqual(fragment.content, self.runtime.render_template('videoalpha.html', expected_context))
self.assertEqual(context, expected_context)
...@@ -15,23 +15,19 @@ course, section, subsection, unit, etc. ...@@ -15,23 +15,19 @@ course, section, subsection, unit, etc.
import json import json
import unittest import unittest
from mock import Mock
from lxml import etree
from django.conf import settings from django.conf import settings
from xmodule.videoalpha_module import VideoAlphaDescriptor, VideoAlphaModule from xmodule.videoalpha_module import VideoAlphaDescriptor, _create_youtube_string
from xmodule.modulestore import Location from xmodule.modulestore import Location
from xmodule.tests import get_test_system from xmodule.tests import get_test_system
from xmodule.tests import LogicTest
SOURCE_XML = """ SOURCE_XML = """
<videoalpha show_captions="true" <videoalpha show_captions="true"
display_name="A Name"
youtube="0.75:jNCf2gIqpeE,1.0:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg" youtube="0.75:jNCf2gIqpeE,1.0:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg"
data_dir="" sub="a_sub_file.srt.sjson"
caption_asset_path=""
autoplay="true"
start_time="01:00:03" end_time="01:00:10" start_time="01:00:03" end_time="01:00:10"
> >
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.mp4"/> <source src=".../mit-3091x/M-3091X-FA12-L21-3_100.mp4"/>
...@@ -54,74 +50,53 @@ class VideoAlphaFactory(object): ...@@ -54,74 +50,53 @@ class VideoAlphaFactory(object):
"""Method return VideoAlpha Xmodule instance.""" """Method return VideoAlpha Xmodule instance."""
location = Location(["i4x", "edX", "videoalpha", "default", location = Location(["i4x", "edX", "videoalpha", "default",
"SampleProblem1"]) "SampleProblem1"])
model_data = {'data': VideoAlphaFactory.sample_problem_xml_youtube} model_data = {'data': VideoAlphaFactory.sample_problem_xml_youtube,
'location': location}
descriptor = Mock(weight="1")
system = get_test_system() system = get_test_system()
system.render_template = lambda template, context: context system.render_template = lambda template, context: context
VideoAlphaModule.location = location
module = VideoAlphaModule(system, descriptor, model_data)
return module
descriptor = VideoAlphaDescriptor(system, model_data)
class VideoAlphaModuleTest(LogicTest): module = descriptor.xmodule(system)
"""Tests for logic of VideoAlpha Xmodule."""
descriptor_class = VideoAlphaDescriptor
raw_model_data = {
'data': '<videoalpha />'
}
def test_get_timeframe_no_parameters(self): return module
xmltree = etree.fromstring('<videoalpha>test</videoalpha>')
output = self.xmodule.get_timeframe(xmltree)
self.assertEqual(output, ('', ''))
def test_get_timeframe_with_one_parameter(self):
xmltree = etree.fromstring(
'<videoalpha start_time="00:04:07">test</videoalpha>'
)
output = self.xmodule.get_timeframe(xmltree)
self.assertEqual(output, (247, ''))
def test_get_timeframe_with_two_parameters(self):
xmltree = etree.fromstring(
'''<videoalpha
start_time="00:04:07"
end_time="13:04:39"
>test</videoalpha>'''
)
output = self.xmodule.get_timeframe(xmltree)
self.assertEqual(output, (247, 47079))
class VideoAlphaModuleUnitTest(unittest.TestCase): class VideoAlphaModuleUnitTest(unittest.TestCase):
"""Unit tests for VideoAlpha Xmodule.""" """Unit tests for VideoAlpha Xmodule."""
def test_videoalpha_constructor(self): def test_videoalpha_get_html(self):
"""Make sure that all parameters extracted correclty from xml""" """Make sure that all parameters extracted correclty from xml"""
module = VideoAlphaFactory.create() module = VideoAlphaFactory.create()
module.runtime.render_template = lambda template, context: u'{!r}, {!r}'.format(template, sorted(context.items())) module.runtime.render_template = lambda template, context: context
sources = {
'main': '.../mit-3091x/M-3091X-FA12-L21-3_100.mp4',
'mp4': '.../mit-3091x/M-3091X-FA12-L21-3_100.mp4',
'ogv': '.../mit-3091x/M-3091X-FA12-L21-3_100.ogv',
'webm': '.../mit-3091x/M-3091X-FA12-L21-3_100.webm',
}
fragment = module.runtime.render(module, None, 'student_view')
expected_context = { expected_context = {
'caption_asset_path': '/static/subs/', 'caption_asset_path': '/static/subs/',
'sub': module.sub, 'sub': 'a_sub_file.srt.sjson',
'data_dir': getattr(self, 'data_dir', None), 'data_dir': getattr(self, 'data_dir', None),
'display_name': module.display_name_with_default, 'display_name': 'A Name',
'end': module.end_time, 'end': 3610.0,
'start': module.start_time, 'start': 3603.0,
'id': module.location.html_id(), 'id': module.location.html_id(),
'show_captions': module.show_captions, 'show_captions': True,
'sources': module.sources, 'sources': sources,
'youtube_streams': module.youtube_streams, 'youtube_streams': _create_youtube_string(module),
'track': module.track, 'track': '',
'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True) 'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True)
} }
self.assertEqual(fragment.content, module.runtime.render_template('videoalpha.html', expected_context))
self.assertEqual(module.get_html(), expected_context)
def test_videoalpha_instance_state(self):
module = VideoAlphaFactory.create()
self.assertDictEqual( self.assertDictEqual(
json.loads(module.get_instance_state()), json.loads(module.get_instance_state()),
......
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