Commit b33b5c7b by Vasyl Nakvasiuk Committed by Anton Stupak

Python: videoalpha -> video.

parent dad9f26a
......@@ -107,8 +107,8 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
expected_types is the list of elements that should appear on the page.
expected_types and component_types should be similar, but not
exactly the same -- for example, 'videoalpha' in
component_types should cause 'Video Alpha' to be present.
exactly the same -- for example, 'video' in
component_types should cause 'Video' to be present.
"""
store = modulestore('direct')
import_from_xml(store, 'common/test/data/', ['simple'])
......@@ -143,7 +143,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
'Peer Grading Interface'])
def test_advanced_components_require_two_clicks(self):
self.check_components_on_page(['videoalpha'], ['Video Alpha'])
self.check_components_on_page(['video'], ['Video'])
def test_malformed_edit_unit_request(self):
store = modulestore('direct')
......@@ -1624,7 +1624,7 @@ class MetadataSaveTestCase(ModuleStoreTestCase):
constructor are correctly persisted.
"""
# We should start with a source field, from the XML's <source/> tag
self.assertIn('source', own_metadata(self.descriptor))
self.assertIn('html5_sources', own_metadata(self.descriptor))
attrs_to_strip = {
'show_captions',
'youtube_id_1_0',
......@@ -1634,6 +1634,7 @@ class MetadataSaveTestCase(ModuleStoreTestCase):
'start_time',
'end_time',
'source',
'html5_sources',
'track'
}
# We strip out all metadata fields to reproduce a bug where
......@@ -1646,11 +1647,11 @@ class MetadataSaveTestCase(ModuleStoreTestCase):
field.delete_from(self.descriptor)
# Assert that we correctly stripped the field
self.assertNotIn('source', own_metadata(self.descriptor))
self.assertNotIn('html5_sources', own_metadata(self.descriptor))
get_modulestore(self.descriptor.location).update_metadata(
self.descriptor.location,
own_metadata(self.descriptor)
)
module = get_modulestore(self.descriptor.location).get_item(self.descriptor.location)
# Assert that get_item correctly sets the metadata
self.assertIn('source', own_metadata(module))
self.assertIn('html5_sources', own_metadata(module))
......@@ -49,7 +49,6 @@ NOTE_COMPONENT_TYPES = ['notes']
ADVANCED_COMPONENT_TYPES = [
'annotatable',
'word_cloud',
'videoalpha',
'graphical_slider_tool'
] + OPEN_ENDED_COMPONENT_TYPES + NOTE_COMPONENT_TYPES
ADVANCED_COMPONENT_CATEGORY = 'advanced'
......
......@@ -40,7 +40,7 @@ setup(
"timelimit = xmodule.timelimit_module:TimeLimitDescriptor",
"vertical = xmodule.vertical_module:VerticalDescriptor",
"video = xmodule.video_module:VideoDescriptor",
"videoalpha = xmodule.videoalpha_module:VideoAlphaDescriptor",
"videoalpha = xmodule.video_module:VideoDescriptor",
"videodev = xmodule.backcompat_module:TranslateCustomTagDescriptor",
"videosequence = xmodule.seq_module:SequenceDescriptor",
"discussion = xmodule.discussion_module:DiscussionDescriptor",
......
......@@ -33,7 +33,7 @@ class TabsEditingDescriptorTestCase(unittest.TestCase):
},
{
'name': "Subtitles",
'template': "videoalpha/subtitles.html",
'template': "video/subtitles.html",
},
{
'name': "Settings",
......
# -*- coding: utf-8 -*-
#pylint: disable=W0212
"""Test for Video Alpha Xmodule functional logic.
"""Test for Video Xmodule functional logic.
These test data read from xml, not from mongo.
We have a ModuleStoreTestCase class defined in
......@@ -17,37 +17,36 @@ import unittest
from . import LogicTest
from .import get_test_system
from xmodule.modulestore import Location
from xmodule.videoalpha_module import VideoAlphaDescriptor, _create_youtube_string
from xmodule.video_module import VideoDescriptor
from xmodule.video_module import VideoDescriptor, _create_youtube_string
from .test_import import DummySystem
from textwrap import dedent
class VideoAlphaModuleTest(LogicTest):
"""Logic tests for VideoAlpha Xmodule."""
descriptor_class = VideoAlphaDescriptor
class VideoModuleTest(LogicTest):
"""Logic tests for Video Xmodule."""
descriptor_class = VideoDescriptor
raw_model_data = {
'data': '<videoalpha />'
'data': '<video />'
}
def test_parse_time_empty(self):
"""Ensure parse_time returns correctly with None or empty string."""
expected = ''
self.assertEqual(VideoAlphaDescriptor._parse_time(None), expected)
self.assertEqual(VideoAlphaDescriptor._parse_time(''), expected)
self.assertEqual(VideoDescriptor._parse_time(None), expected)
self.assertEqual(VideoDescriptor._parse_time(''), expected)
def test_parse_time(self):
"""Ensure that times are parsed correctly into seconds."""
expected = 247
output = VideoAlphaDescriptor._parse_time('00:04:07')
output = VideoDescriptor._parse_time('00:04:07')
self.assertEqual(output, expected)
def test_parse_youtube(self):
"""Test parsing old-style Youtube ID strings into a dict."""
youtube_str = '0.75:jNCf2gIqpeE,1.00:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg'
output = VideoAlphaDescriptor._parse_youtube(youtube_str)
output = VideoDescriptor._parse_youtube(youtube_str)
self.assertEqual(output, {'0.75': 'jNCf2gIqpeE',
'1.00': 'ZwkTiUPN0mg',
'1.25': 'rsq9auxASqI',
......@@ -59,7 +58,7 @@ class VideoAlphaModuleTest(LogicTest):
empty string.
"""
youtube_str = '0.75:jNCf2gIqpeE'
output = VideoAlphaDescriptor._parse_youtube(youtube_str)
output = VideoDescriptor._parse_youtube(youtube_str)
self.assertEqual(output, {'0.75': 'jNCf2gIqpeE',
'1.00': '',
'1.25': '',
......@@ -72,8 +71,8 @@ class VideoAlphaModuleTest(LogicTest):
youtube_str = '1.00:p2Q6BrNhdh8'
youtube_str_hack = '1.0:p2Q6BrNhdh8'
self.assertEqual(
VideoAlphaDescriptor._parse_youtube(youtube_str),
VideoAlphaDescriptor._parse_youtube(youtube_str_hack)
VideoDescriptor._parse_youtube(youtube_str),
VideoDescriptor._parse_youtube(youtube_str_hack)
)
def test_parse_youtube_empty(self):
......@@ -82,7 +81,7 @@ class VideoAlphaModuleTest(LogicTest):
that well.
"""
self.assertEqual(
VideoAlphaDescriptor._parse_youtube(''),
VideoDescriptor._parse_youtube(''),
{'0.75': '',
'1.00': '',
'1.25': '',
......@@ -90,12 +89,12 @@ class VideoAlphaModuleTest(LogicTest):
)
class VideoAlphaDescriptorTest(unittest.TestCase):
"""Test for VideoAlphaDescriptor"""
class VideoDescriptorTest(unittest.TestCase):
"""Test for VideoDescriptor"""
def setUp(self):
system = get_test_system()
self.descriptor = VideoAlphaDescriptor(
self.descriptor = VideoDescriptor(
runtime=system,
model_data={})
......@@ -117,9 +116,9 @@ class VideoAlphaDescriptorTest(unittest.TestCase):
back out to XML.
"""
system = DummySystem(load_error_modules=True)
location = Location(["i4x", "edX", "videoalpha", "default", "SampleProblem1"])
location = Location(["i4x", "edX", "video", "default", "SampleProblem1"])
model_data = {'location': location}
descriptor = VideoAlphaDescriptor(system, model_data)
descriptor = VideoDescriptor(system, model_data)
descriptor.youtube_id_0_75 = 'izygArpw-Qo'
descriptor.youtube_id_1_0 = 'p2Q6BrNhdh8'
descriptor.youtube_id_1_25 = '1EeWXzPdhSA'
......@@ -133,9 +132,9 @@ class VideoAlphaDescriptorTest(unittest.TestCase):
in the output string.
"""
system = DummySystem(load_error_modules=True)
location = Location(["i4x", "edX", "videoalpha", "default", "SampleProblem1"])
location = Location(["i4x", "edX", "video", "default", "SampleProblem1"])
model_data = {'location': location}
descriptor = VideoAlphaDescriptor(system, model_data)
descriptor = VideoDescriptor(system, model_data)
descriptor.youtube_id_0_75 = 'izygArpw-Qo'
descriptor.youtube_id_1_0 = 'p2Q6BrNhdh8'
descriptor.youtube_id_1_25 = '1EeWXzPdhSA'
......@@ -143,9 +142,9 @@ class VideoAlphaDescriptorTest(unittest.TestCase):
self.assertEqual(_create_youtube_string(descriptor), expected)
class VideoAlphaDescriptorImportTestCase(unittest.TestCase):
class VideoDescriptorImportTestCase(unittest.TestCase):
"""
Make sure that VideoAlphaDescriptor can import an old XML-based video correctly.
Make sure that VideoDescriptor can import an old XML-based video correctly.
"""
def assert_attributes_equal(self, video, attrs):
......@@ -158,7 +157,7 @@ class VideoAlphaDescriptorImportTestCase(unittest.TestCase):
def test_constructor(self):
sample_xml = '''
<videoalpha display_name="Test Video"
<video display_name="Test Video"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
show_captions="false"
start_time="00:00:01"
......@@ -166,14 +165,14 @@ class VideoAlphaDescriptorImportTestCase(unittest.TestCase):
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.ogg"/>
<track src="http://www.example.com/track"/>
</videoalpha>
</video>
'''
location = Location(["i4x", "edX", "videoalpha", "default",
location = Location(["i4x", "edX", "video", "default",
"SampleProblem1"])
model_data = {'data': sample_xml,
'location': location}
system = DummySystem(load_error_modules=True)
descriptor = VideoAlphaDescriptor(system, model_data)
descriptor = VideoDescriptor(system, model_data)
self.assert_attributes_equal(descriptor, {
'youtube_id_0_75': 'izygArpw-Qo',
'youtube_id_1_0': 'p2Q6BrNhdh8',
......@@ -190,16 +189,16 @@ class VideoAlphaDescriptorImportTestCase(unittest.TestCase):
def test_from_xml(self):
module_system = DummySystem(load_error_modules=True)
xml_data = '''
<videoalpha display_name="Test Video"
<video display_name="Test Video"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
show_captions="false"
start_time="00:00:01"
end_time="00:01:00">
<source src="http://www.example.com/source.mp4"/>
<track src="http://www.example.com/track"/>
</videoalpha>
</video>
'''
output = VideoAlphaDescriptor.from_xml(xml_data, module_system)
output = VideoDescriptor.from_xml(xml_data, module_system)
self.assert_attributes_equal(output, {
'youtube_id_0_75': 'izygArpw-Qo',
'youtube_id_1_0': 'p2Q6BrNhdh8',
......@@ -221,14 +220,14 @@ class VideoAlphaDescriptorImportTestCase(unittest.TestCase):
"""
module_system = DummySystem(load_error_modules=True)
xml_data = '''
<videoalpha display_name="Test Video"
<video display_name="Test Video"
youtube="1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA"
show_captions="true">
<source src="http://www.example.com/source.mp4"/>
<track src="http://www.example.com/track"/>
</videoalpha>
</video>
'''
output = VideoAlphaDescriptor.from_xml(xml_data, module_system)
output = VideoDescriptor.from_xml(xml_data, module_system)
self.assert_attributes_equal(output, {
'youtube_id_0_75': '',
'youtube_id_1_0': 'p2Q6BrNhdh8',
......@@ -248,8 +247,8 @@ class VideoAlphaDescriptorImportTestCase(unittest.TestCase):
Make sure settings are correct if none are explicitly set in XML.
"""
module_system = DummySystem(load_error_modules=True)
xml_data = '<videoalpha></videoalpha>'
output = VideoAlphaDescriptor.from_xml(xml_data, module_system)
xml_data = '<video></video>'
output = VideoDescriptor.from_xml(xml_data, module_system)
self.assert_attributes_equal(output, {
'youtube_id_0_75': '',
'youtube_id_1_0': 'OEoXaMPEzfM',
......@@ -270,16 +269,16 @@ class VideoAlphaDescriptorImportTestCase(unittest.TestCase):
"""
module_system = DummySystem(load_error_modules=True)
xml_data = """
<videoalpha display_name="Test Video"
<video display_name="Test Video"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
show_captions="false"
from="00:00:01"
to="00:01:00">
<source src="http://www.example.com/source.mp4"/>
<track src="http://www.example.com/track"/>
</videoalpha>
</video>
"""
output = VideoAlphaDescriptor.from_xml(xml_data, module_system)
output = VideoDescriptor.from_xml(xml_data, module_system)
self.assert_attributes_equal(output, {
'youtube_id_0_75': 'izygArpw-Qo',
'youtube_id_1_0': 'p2Q6BrNhdh8',
......@@ -295,7 +294,7 @@ class VideoAlphaDescriptorImportTestCase(unittest.TestCase):
def test_old_video_data(self):
"""
Ensure that Video Alpha is able to read VideoModule's model data.
Ensure that Video is able to read VideoModule's model data.
"""
module_system = DummySystem(load_error_modules=True)
xml_data = """
......@@ -309,8 +308,7 @@ class VideoAlphaDescriptorImportTestCase(unittest.TestCase):
</video>
"""
video = VideoDescriptor.from_xml(xml_data, module_system)
video_alpha = VideoAlphaDescriptor(module_system, video._model_data)
self.assert_attributes_equal(video_alpha, {
self.assert_attributes_equal(video, {
'youtube_id_0_75': 'izygArpw-Qo',
'youtube_id_1_0': 'p2Q6BrNhdh8',
'youtube_id_1_25': '1EeWXzPdhSA',
......@@ -324,17 +322,17 @@ class VideoAlphaDescriptorImportTestCase(unittest.TestCase):
})
class VideoAlphaExportTestCase(unittest.TestCase):
class VideoExportTestCase(unittest.TestCase):
"""
Make sure that VideoAlphaDescriptor can export itself to XML
Make sure that VideoDescriptor can export itself to XML
correctly.
"""
def test_export_to_xml(self):
"""Test that we write the correct XML on export."""
module_system = DummySystem(load_error_modules=True)
location = Location(["i4x", "edX", "videoalpha", "default", "SampleProblem1"])
desc = VideoAlphaDescriptor(module_system, {'location': location})
location = Location(["i4x", "edX", "video", "default", "SampleProblem1"])
desc = VideoDescriptor(module_system, {'location': location})
desc.youtube_id_0_75 = 'izygArpw-Qo'
desc.youtube_id_1_0 = 'p2Q6BrNhdh8'
......@@ -348,11 +346,11 @@ class VideoAlphaExportTestCase(unittest.TestCase):
xml = desc.export_to_xml(None) # We don't use the `resource_fs` parameter
expected = dedent('''\
<videoalpha display_name="Video Alpha" start_time="0:00:01" youtube="0.75:izygArpw-Qo,1.00:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8" show_captions="false" end_time="0:01:00">
<video display_name="Video" start_time="0:00:01" youtube="0.75:izygArpw-Qo,1.00:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8" show_captions="false" end_time="0:01:00">
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.ogg"/>
<track src="http://www.example.com/track"/>
</videoalpha>
</video>
''')
self.assertEquals(expected, xml)
......@@ -360,10 +358,10 @@ class VideoAlphaExportTestCase(unittest.TestCase):
def test_export_to_xml_empty_parameters(self):
"""Test XML export with defaults."""
module_system = DummySystem(load_error_modules=True)
location = Location(["i4x", "edX", "videoalpha", "default", "SampleProblem1"])
desc = VideoAlphaDescriptor(module_system, {'location': location})
location = Location(["i4x", "edX", "video", "default", "SampleProblem1"])
desc = VideoDescriptor(module_system, {'location': location})
xml = desc.export_to_xml(None)
expected = '<videoalpha display_name="Video Alpha" youtube="1.00:OEoXaMPEzfM" show_captions="true"/>\n'
expected = '<video display_name="Video" youtube="1.00:OEoXaMPEzfM" show_captions="true"/>\n'
self.assertEquals(expected, xml)
......@@ -16,10 +16,9 @@ from xmodule.gst_module import GraphicalSliderToolDescriptor
from xmodule.html_module import HtmlDescriptor
from xmodule.peer_grading_module import PeerGradingDescriptor
from xmodule.poll_module import PollDescriptor
from xmodule.video_module import VideoDescriptor
from xmodule.word_cloud_module import WordCloudDescriptor
from xmodule.crowdsource_hinter import CrowdsourceHinterDescriptor
from xmodule.videoalpha_module import VideoAlphaDescriptor
from xmodule.video_module import VideoDescriptor
from xmodule.seq_module import SequenceDescriptor
from xmodule.conditional_module import ConditionalDescriptor
from xmodule.randomize_module import RandomizeDescriptor
......@@ -35,9 +34,8 @@ LEAF_XMODULES = (
HtmlDescriptor,
PeerGradingDescriptor,
PollDescriptor,
VideoDescriptor,
# This is being excluded because it has dependencies on django
#VideoAlphaDescriptor,
#VideoDescriptor,
WordCloudDescriptor,
)
......
# pylint: disable=W0223
"""Video is ungraded Xmodule for support video content."""
"""Video is ungraded Xmodule for support video content.
It's new improved video module, which support additional feature:
- Can play non-YouTube video sources via in-browser HTML5 video player.
- YouTube defaults to HTML5 mode from the start.
- Speed changes in both YouTube and non-YouTube videos happen via
in-browser HTML5 video method (when in HTML5 mode).
- Navigational subtitles can be disabled altogether via an attribute
in XML.
"""
import json
import logging
from lxml import etree
from pkg_resources import resource_string, resource_listdir
import datetime
import time
from pkg_resources import resource_string
from django.http import Http404
from django.conf import settings
from xmodule.x_module import XModule
from xmodule.editing_module import TabsEditingDescriptor
from xmodule.raw_module import EmptyDataRawDescriptor
from xmodule.editing_module import MetadataOnlyEditingDescriptor
from xblock.core import Integer, Scope, String, Float, Boolean
from xmodule.modulestore.mongo import MongoModuleStore
from xmodule.modulestore.django import modulestore
from xmodule.contentstore.content import StaticContent
from xblock.core import Scope, String, Boolean, Float, List, Integer
import datetime
import time
log = logging.getLogger(__name__)
......@@ -22,51 +36,118 @@ log = logging.getLogger(__name__)
class VideoFields(object):
"""Fields for `VideoModule` and `VideoDescriptor`."""
display_name = String(
display_name="Display Name",
help="This name appears in the horizontal navigation at the top of the page.",
display_name="Display Name", help="Display name for this module.",
default="Video",
scope=Scope.settings
)
position = Integer(
help="Current position in the video",
scope=Scope.user_state,
default=0
)
show_captions = Boolean(
help="This controls whether or not captions are shown by default.",
display_name="Show Captions",
scope=Scope.settings,
# it'd be nice to have a useful default but it screws up other things; so,
# use display_name_with_default for those
default="Video"
default=True
)
data = String(
help="XML data for the problem",
default='',
scope=Scope.content
# TODO: This should be moved to Scope.content, but this will
# require data migration to support the old video module.
youtube_id_1_0 = String(
help="This is the Youtube ID reference for the normal speed video.",
display_name="Youtube ID",
scope=Scope.settings,
default="OEoXaMPEzfM"
)
youtube_id_0_75 = String(
help="The Youtube ID for the .75x speed video.",
display_name="Youtube ID for .75x speed",
scope=Scope.settings,
default=""
)
youtube_id_1_25 = String(
help="The Youtube ID for the 1.25x speed video.",
display_name="Youtube ID for 1.25x speed",
scope=Scope.settings,
default=""
)
youtube_id_1_5 = String(
help="The Youtube ID for the 1.5x speed video.",
display_name="Youtube ID for 1.5x speed",
scope=Scope.settings,
default=""
)
start_time = Float(
help="Start time for the video.",
display_name="Start Time",
scope=Scope.settings,
default=0.0
)
end_time = Float(
help="End time for the video.",
display_name="End Time",
scope=Scope.settings,
default=0.0
)
source = String(
help="The external URL to download the video. This appears as a link beneath the video.",
display_name="Download Video",
scope=Scope.settings,
default=""
)
html5_sources = List(
help="A list of filenames to be used with HTML5 video. The first supported filetype will be displayed.",
display_name="Video Sources",
scope=Scope.settings,
default=[]
)
track = String(
help="The external URL to download the subtitle track. This appears as a link beneath the video.",
display_name="Download Track",
scope=Scope.settings,
default=""
)
sub = String(
help="The name of the subtitle track (for non-Youtube videos).",
display_name="HTML5 Subtitles",
scope=Scope.settings,
default=""
)
position = Integer(help="Current position in the video", scope=Scope.user_state, default=0)
show_captions = Boolean(help="This controls whether or not captions are shown by default.", display_name="Show Captions", scope=Scope.settings, default=True)
youtube_id_1_0 = String(help="This is the Youtube ID reference for the normal speed video.", display_name="Default Speed", scope=Scope.settings, default="OEoXaMPEzfM")
youtube_id_0_75 = String(help="The Youtube ID for the .75x speed video.", display_name="Speed: .75x", scope=Scope.settings, default="")
youtube_id_1_25 = String(help="The Youtube ID for the 1.25x speed video.", display_name="Speed: 1.25x", scope=Scope.settings, default="")
youtube_id_1_5 = String(help="The Youtube ID for the 1.5x speed video.", display_name="Speed: 1.5x", scope=Scope.settings, default="")
start_time = Float(help="Time the video starts", display_name="Start Time", scope=Scope.settings, default=0.0)
end_time = Float(help="Time the video ends", display_name="End Time", scope=Scope.settings, default=0.0)
source = String(help="The external URL to download the video. This appears as a link beneath the video.", display_name="Download Video", scope=Scope.settings, default="")
track = String(help="The external URL to download the subtitle track. This appears as a link beneath the video.", display_name="Download Track", scope=Scope.settings, default="")
class VideoModule(VideoFields, XModule):
"""Video Xmodule."""
"""
XML source example:
<video show_captions="true"
youtube="0.75:jNCf2gIqpeE,1.0:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg"
url_name="lecture_21_3" display_name="S19V3: Vacancies"
>
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.mp4"/>
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.webm"/>
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.ogv"/>
</video>
"""
video_time = 0
icon_class = 'video'
js = {
'coffee': [
resource_string(__name__, 'js/src/time.coffee'),
resource_string(__name__, 'js/src/video/display.coffee')
] +
[resource_string(__name__, 'js/src/video/display/' + filename)
for filename
in sorted(resource_listdir(__name__, 'js/src/video/display'))
if filename.endswith('.coffee')]
'js': [
resource_string(__name__, 'js/src/video/01_initialize.js'),
resource_string(__name__, 'js/src/video/02_html5_video.js'),
resource_string(__name__, 'js/src/video/03_video_player.js'),
resource_string(__name__, 'js/src/video/04_video_control.js'),
resource_string(__name__, 'js/src/video/05_video_quality_control.js'),
resource_string(__name__, 'js/src/video/06_video_progress_slider.js'),
resource_string(__name__, 'js/src/video/07_video_volume_control.js'),
resource_string(__name__, 'js/src/video/08_video_speed_control.js'),
resource_string(__name__, 'js/src/video/09_video_caption.js'),
resource_string(__name__, 'js/src/video/10_main.js')
]
}
css = {'scss': [resource_string(__name__, 'css/video/display.scss')]}
js_module_name = "Video"
def __init__(self, *args, **kwargs):
XModule.__init__(self, *args, **kwargs)
def handle_ajax(self, dispatch, data):
"""This is not being called right now and we raise 404 error."""
log.debug(u"GET {0}".format(data))
......@@ -78,41 +159,59 @@ class VideoModule(VideoFields, XModule):
return json.dumps({'position': self.position})
def get_html(self):
if isinstance(modulestore(), MongoModuleStore):
caption_asset_path = StaticContent.get_base_url_path_for_course_assets(self.location) + '/subs_'
else:
# VS[compat]
# cdodge: filesystem static content support.
caption_asset_path = "/static/subs/"
get_ext = lambda filename: filename.rpartition('.')[-1]
sources = {get_ext(src): src for src in self.html5_sources}
sources['main'] = self.source
return self.system.render_template('video.html', {
'youtube_id_0_75': self.youtube_id_0_75,
'youtube_id_1_0': self.youtube_id_1_0,
'youtube_id_1_25': self.youtube_id_1_25,
'youtube_id_1_5': self.youtube_id_1_5,
'youtube_streams': _create_youtube_string(self),
'id': self.location.html_id(),
'position': self.position,
'source': self.source,
'sub': self.sub,
'sources': sources,
'track': self.track,
'display_name': self.display_name_with_default,
'caption_asset_path': "/static/subs/",
'show_captions': 'true' if self.show_captions else 'false',
# This won't work when we move to data that
# isn't on the filesystem
'data_dir': getattr(self, 'data_dir', None),
'caption_asset_path': caption_asset_path,
'show_captions': json.dumps(self.show_captions),
'start': self.start_time,
'end': self.end_time
'end': self.end_time,
'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True)
})
class VideoDescriptor(VideoFields,
MetadataOnlyEditingDescriptor,
EmptyDataRawDescriptor):
class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor):
"""Descriptor for `VideoModule`."""
module_class = VideoModule
tabs = [
# {
# 'name': "Subtitles",
# 'template': "video/subtitles.html",
# },
{
'name': "Settings",
'template': "tabs/metadata-edit-tab.html",
'current': True
}
]
def __init__(self, *args, **kwargs):
super(VideoDescriptor, self).__init__(*args, **kwargs)
# If we don't have a `youtube_id_1_0`, this is an XML course
# and we parse out the fields.
if self.data and 'youtube_id_1_0' not in self._model_data:
_parse_video_xml(self, self.data)
@property
def non_editable_metadata_fields(self):
non_editable_fields = super(MetadataOnlyEditingDescriptor, self).non_editable_metadata_fields
non_editable_fields.extend([VideoModule.start_time,
VideoModule.end_time])
return non_editable_fields
# For backwards compatibility -- if we've got XML data, parse
# it out and set the metadata fields
if self.data:
model_data = VideoDescriptor._parse_video_xml(self.data)
self._model_data.update(model_data)
del self.data
@classmethod
def from_xml(cls, xml_data, system, org=None, course=None):
......@@ -126,102 +225,143 @@ class VideoDescriptor(VideoFields,
org and course are optional strings that will be used in the generated modules
url identifiers
"""
# Calling from_xml of XmlDescritor, to get right Location, when importing from XML
video = super(VideoDescriptor, cls).from_xml(xml_data, system, org, course)
_parse_video_xml(video, video.data)
return video
def export_to_xml(self, resource_fs):
"""
Returns an xml string representing this module.
"""
xml = etree.Element('video')
attrs = {
'display_name': self.display_name,
'show_captions': json.dumps(self.show_captions),
'youtube': _create_youtube_string(self),
'start_time': datetime.timedelta(seconds=self.start_time),
'end_time': datetime.timedelta(seconds=self.end_time),
'sub': self.sub
}
for key, value in attrs.items():
if value:
xml.set(key, str(value))
for source in self.html5_sources:
ele = etree.Element('source')
ele.set('src', source)
xml.append(ele)
if self.track:
ele = etree.Element('track')
ele.set('src', self.track)
xml.append(ele)
return etree.tostring(xml, pretty_print=True)
@staticmethod
def _parse_youtube(data):
"""
Parses a string of Youtube IDs such as "1.0:AXdE34_U,1.5:VO3SxfeD"
into a dictionary. Necessary for backwards compatibility with
XML-based courses.
"""
ret = {'0.75': '', '1.00': '', '1.25': '', '1.50': ''}
if data == '':
return ret
videos = data.split(',')
for video in videos:
pieces = video.split(':')
# HACK
# To elaborate somewhat: in many LMS tests, the keys for
# Youtube IDs are inconsistent. Sometimes a particular
# speed isn't present, and formatting is also inconsistent
# ('1.0' versus '1.00'). So it's necessary to either do
# something like this or update all the tests to work
# properly.
ret['%.2f' % float(pieces[0])] = pieces[1]
return ret
def _parse_video_xml(video, xml_data):
"""
Parse video fields out of xml_data. The fields are set if they are
present in the XML.
"""
if not xml_data:
return
xml = etree.fromstring(xml_data)
display_name = xml.get('display_name')
if display_name:
video.display_name = display_name
youtube = xml.get('youtube')
if youtube:
speeds = _parse_youtube(youtube)
if speeds['0.75']:
video.youtube_id_0_75 = speeds['0.75']
if speeds['1.00']:
video.youtube_id_1_0 = speeds['1.00']
if speeds['1.25']:
video.youtube_id_1_25 = speeds['1.25']
if speeds['1.50']:
video.youtube_id_1_5 = speeds['1.50']
show_captions = xml.get('show_captions')
if show_captions:
video.show_captions = json.loads(show_captions)
source = _get_first_external(xml, 'source')
if source:
video.source = source
track = _get_first_external(xml, 'track')
if track:
video.track = track
start_time = _parse_time(xml.get('from'))
if start_time:
video.start_time = start_time
end_time = _parse_time(xml.get('to'))
if end_time:
video.end_time = end_time
def _get_first_external(xmltree, tag):
"""
Returns the src attribute of the nested `tag` in `xmltree`, if it
exists.
"""
for element in xmltree.findall(tag):
src = element.get('src')
if src:
return src
return None
def _parse_youtube(data):
@staticmethod
def _parse_video_xml(xml_data):
"""
Parse video fields out of xml_data. The fields are set if they are
present in the XML.
"""
xml = etree.fromstring(xml_data)
model_data = {}
conversions = {
'show_captions': json.loads,
'start_time': VideoDescriptor._parse_time,
'end_time': VideoDescriptor._parse_time
}
# VideoModule and VideoModule use different names for
# these attributes -- need to convert between them
video_compat = {
'from': 'start_time',
'to': 'end_time'
}
sources = xml.findall('source')
if sources:
model_data['html5_sources'] = [ele.get('src') for ele in sources]
model_data['source'] = model_data['html5_sources'][0]
track = xml.find('track')
if track is not None:
model_data['track'] = track.get('src')
for attr, value in xml.items():
if attr in video_compat:
attr = video_compat[attr]
if attr == 'youtube':
speeds = VideoDescriptor._parse_youtube(value)
for speed, youtube_id in speeds.items():
# should have made these youtube_id_1_00 for
# cleanliness, but hindsight doesn't need glasses
normalized_speed = speed[:-1] if speed.endswith('0') else speed
# If the user has specified html5 sources, make sure we don't use the default video
if youtube_id != '' or 'html5_sources' in model_data:
model_data['youtube_id_{0}'.format(normalized_speed.replace('.', '_'))] = youtube_id
else:
# Convert XML attrs into Python values.
if attr in conversions:
value = conversions[attr](value)
model_data[attr] = value
return model_data
@staticmethod
def _parse_time(str_time):
"""Converts s in '12:34:45' format to seconds. If s is
None, returns empty string"""
if not str_time:
return ''
else:
obj_time = time.strptime(str_time, '%H:%M:%S')
return datetime.timedelta(
hours=obj_time.tm_hour,
minutes=obj_time.tm_min,
seconds=obj_time.tm_sec
).total_seconds()
def _create_youtube_string(module):
"""
Parses a string of Youtube IDs such as "1.0:AXdE34_U,1.5:VO3SxfeD"
into a dictionary. Necessary for backwards compatibility with
XML-based courses.
Create a string of Youtube IDs from `module`'s metadata
attributes. Only writes a speed if an ID is present in the
module. Necessary for backwards compatibility with XML-based
courses.
"""
ret = {'0.75': '', '1.00': '', '1.25': '', '1.50': ''}
if data == '':
return ret
videos = data.split(',')
for video in videos:
pieces = video.split(':')
# HACK
# To elaborate somewhat: in many LMS tests, the keys for
# Youtube IDs are inconsistent. Sometimes a particular
# speed isn't present, and formatting is also inconsistent
# ('1.0' versus '1.00'). So it's necessary to either do
# something like this or update all the tests to work
# properly.
ret['%.2f' % float(pieces[0])] = pieces[1]
return ret
def _parse_time(str_time):
"""Converts s in '12:34:45' format to seconds. If s is
None, returns empty string"""
if str_time is None or str_time == '':
return ''
else:
obj_time = time.strptime(str_time, '%H:%M:%S')
return datetime.timedelta(
hours=obj_time.tm_hour,
minutes=obj_time.tm_min,
seconds=obj_time.tm_sec
).total_seconds()
youtube_ids = [
module.youtube_id_0_75,
module.youtube_id_1_0,
module.youtube_id_1_25,
module.youtube_id_1_5
]
youtube_speeds = ['0.75', '1.00', '1.25', '1.50']
return ','.join([':'.join(pair)
for pair
in zip(youtube_speeds, youtube_ids)
if pair[1]])
# pylint: disable=W0223
"""VideoAlpha is ungraded Xmodule for support video content.
It's new improved video module, which support additional feature:
- Can play non-YouTube video sources via in-browser HTML5 video player.
- YouTube defaults to HTML5 mode from the start.
- Speed changes in both YouTube and non-YouTube videos happen via
in-browser HTML5 video method (when in HTML5 mode).
- Navigational subtitles can be disabled altogether via an attribute
in XML.
"""
import json
import logging
from lxml import etree
from pkg_resources import resource_string
from django.http import Http404
from django.conf import settings
from xmodule.x_module import XModule
from xmodule.editing_module import TabsEditingDescriptor
from xmodule.raw_module import EmptyDataRawDescriptor
from xmodule.modulestore.mongo import MongoModuleStore
from xmodule.modulestore.django import modulestore
from xmodule.contentstore.content import StaticContent
from xblock.core import Scope, String, Boolean, Float, List, Integer
import datetime
import time
log = logging.getLogger(__name__)
class VideoAlphaFields(object):
"""Fields for `VideoAlphaModule` and `VideoAlphaDescriptor`."""
display_name = String(
display_name="Display Name", help="Display name for this module.",
default="Video Alpha",
scope=Scope.settings
)
position = Integer(
help="Current position in the video",
scope=Scope.user_state,
default=0
)
show_captions = Boolean(
help="This controls whether or not captions are shown by default.",
display_name="Show Captions",
scope=Scope.settings,
default=True
)
# TODO: This should be moved to Scope.content, but this will
# require data migration to support the old video module.
youtube_id_1_0 = String(
help="This is the Youtube ID reference for the normal speed video.",
display_name="Youtube ID",
scope=Scope.settings,
default="OEoXaMPEzfM"
)
youtube_id_0_75 = String(
help="The Youtube ID for the .75x speed video.",
display_name="Youtube ID for .75x speed",
scope=Scope.settings,
default=""
)
youtube_id_1_25 = String(
help="The Youtube ID for the 1.25x speed video.",
display_name="Youtube ID for 1.25x speed",
scope=Scope.settings,
default=""
)
youtube_id_1_5 = String(
help="The Youtube ID for the 1.5x speed video.",
display_name="Youtube ID for 1.5x speed",
scope=Scope.settings,
default=""
)
start_time = Float(
help="Start time for the video.",
display_name="Start Time",
scope=Scope.settings,
default=0.0
)
end_time = Float(
help="End time for the video.",
display_name="End Time",
scope=Scope.settings,
default=0.0
)
source = String(
help="The external URL to download the video. This appears as a link beneath the video.",
display_name="Download Video",
scope=Scope.settings,
default=""
)
html5_sources = List(
help="A list of filenames to be used with HTML5 video. The first supported filetype will be displayed.",
display_name="Video Sources",
scope=Scope.settings,
default=[]
)
track = String(
help="The external URL to download the subtitle track. This appears as a link beneath the video.",
display_name="Download Track",
scope=Scope.settings,
default=""
)
sub = String(
help="The name of the subtitle track (for non-Youtube videos).",
display_name="HTML5 Subtitles",
scope=Scope.settings,
default=""
)
class VideoAlphaModule(VideoAlphaFields, XModule):
"""
XML source example:
<videoalpha show_captions="true"
youtube="0.75:jNCf2gIqpeE,1.0:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg"
url_name="lecture_21_3" display_name="S19V3: Vacancies"
>
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.mp4"/>
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.webm"/>
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.ogv"/>
</videoalpha>
"""
video_time = 0
icon_class = 'video'
js = {
'js': [
resource_string(__name__, 'js/src/videoalpha/01_initialize.js'),
resource_string(__name__, 'js/src/videoalpha/02_html5_video.js'),
resource_string(__name__, 'js/src/videoalpha/03_video_player.js'),
resource_string(__name__, 'js/src/videoalpha/04_video_control.js'),
resource_string(__name__, 'js/src/videoalpha/05_video_quality_control.js'),
resource_string(__name__, 'js/src/videoalpha/06_video_progress_slider.js'),
resource_string(__name__, 'js/src/videoalpha/07_video_volume_control.js'),
resource_string(__name__, 'js/src/videoalpha/08_video_speed_control.js'),
resource_string(__name__, 'js/src/videoalpha/09_video_caption.js'),
resource_string(__name__, 'js/src/videoalpha/10_main.js')
]
}
css = {'scss': [resource_string(__name__, 'css/videoalpha/display.scss')]}
js_module_name = "VideoAlpha"
def handle_ajax(self, dispatch, data):
"""This is not being called right now and we raise 404 error."""
log.debug(u"GET {0}".format(data))
log.debug(u"DISPATCH {0}".format(dispatch))
raise Http404()
def get_instance_state(self):
"""Return information about state (position)."""
return json.dumps({'position': self.position})
def get_html(self):
if isinstance(modulestore(), MongoModuleStore):
caption_asset_path = StaticContent.get_base_url_path_for_course_assets(self.location) + '/subs_'
else:
# VS[compat]
# cdodge: filesystem static content support.
caption_asset_path = "/static/subs/"
get_ext = lambda filename: filename.rpartition('.')[-1]
sources = {get_ext(src): src for src in self.html5_sources}
sources['main'] = self.source
return self.system.render_template('videoalpha.html', {
'youtube_streams': _create_youtube_string(self),
'id': self.location.html_id(),
'sub': self.sub,
'sources': sources,
'track': self.track,
'display_name': self.display_name_with_default,
# This won't work when we move to data that
# isn't on the filesystem
'data_dir': getattr(self, 'data_dir', None),
'caption_asset_path': caption_asset_path,
'show_captions': json.dumps(self.show_captions),
'start': self.start_time,
'end': self.end_time,
'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True)
})
class VideoAlphaDescriptor(VideoAlphaFields, TabsEditingDescriptor, EmptyDataRawDescriptor):
"""Descriptor for `VideoAlphaModule`."""
module_class = VideoAlphaModule
tabs = [
# {
# 'name': "Subtitles",
# 'template': "videoalpha/subtitles.html",
# },
{
'name': "Settings",
'template': "tabs/metadata-edit-tab.html",
'current': True
}
]
def __init__(self, *args, **kwargs):
super(VideoAlphaDescriptor, self).__init__(*args, **kwargs)
# For backwards compatibility -- if we've got XML data, parse
# it out and set the metadata fields
if self.data:
model_data = VideoAlphaDescriptor._parse_video_xml(self.data)
self._model_data.update(model_data)
del self.data
@classmethod
def from_xml(cls, xml_data, system, org=None, course=None):
"""
Creates an instance of this descriptor from the supplied xml_data.
This may be overridden by subclasses
xml_data: A string of xml that will be translated into data and children for
this module
system: A DescriptorSystem for interacting with external resources
org and course are optional strings that will be used in the generated modules
url identifiers
"""
# Calling from_xml of XmlDescritor, to get right Location, when importing from XML
video = super(VideoAlphaDescriptor, cls).from_xml(xml_data, system, org, course)
return video
def export_to_xml(self, resource_fs):
"""
Returns an xml string representing this module.
"""
xml = etree.Element('videoalpha')
attrs = {
'display_name': self.display_name,
'show_captions': json.dumps(self.show_captions),
'youtube': _create_youtube_string(self),
'start_time': datetime.timedelta(seconds=self.start_time),
'end_time': datetime.timedelta(seconds=self.end_time),
'sub': self.sub
}
for key, value in attrs.items():
if value:
xml.set(key, str(value))
for source in self.html5_sources:
ele = etree.Element('source')
ele.set('src', source)
xml.append(ele)
if self.track:
ele = etree.Element('track')
ele.set('src', self.track)
xml.append(ele)
return etree.tostring(xml, pretty_print=True)
@staticmethod
def _parse_youtube(data):
"""
Parses a string of Youtube IDs such as "1.0:AXdE34_U,1.5:VO3SxfeD"
into a dictionary. Necessary for backwards compatibility with
XML-based courses.
"""
ret = {'0.75': '', '1.00': '', '1.25': '', '1.50': ''}
if data == '':
return ret
videos = data.split(',')
for video in videos:
pieces = video.split(':')
# HACK
# To elaborate somewhat: in many LMS tests, the keys for
# Youtube IDs are inconsistent. Sometimes a particular
# speed isn't present, and formatting is also inconsistent
# ('1.0' versus '1.00'). So it's necessary to either do
# something like this or update all the tests to work
# properly.
ret['%.2f' % float(pieces[0])] = pieces[1]
return ret
@staticmethod
def _parse_video_xml(xml_data):
"""
Parse video fields out of xml_data. The fields are set if they are
present in the XML.
"""
xml = etree.fromstring(xml_data)
model_data = {}
conversions = {
'show_captions': json.loads,
'start_time': VideoAlphaDescriptor._parse_time,
'end_time': VideoAlphaDescriptor._parse_time
}
# VideoModule and VideoAlphaModule use different names for
# these attributes -- need to convert between them
video_compat = {
'from': 'start_time',
'to': 'end_time'
}
sources = xml.findall('source')
if sources:
model_data['html5_sources'] = [ele.get('src') for ele in sources]
model_data['source'] = model_data['html5_sources'][0]
track = xml.find('track')
if track is not None:
model_data['track'] = track.get('src')
for attr, value in xml.items():
if attr in video_compat:
attr = video_compat[attr]
if attr == 'youtube':
speeds = VideoAlphaDescriptor._parse_youtube(value)
for speed, youtube_id in speeds.items():
# should have made these youtube_id_1_00 for
# cleanliness, but hindsight doesn't need glasses
normalized_speed = speed[:-1] if speed.endswith('0') else speed
# If the user has specified html5 sources, make sure we don't use the default video
if youtube_id != '' or 'html5_sources' in model_data:
model_data['youtube_id_{0}'.format(normalized_speed.replace('.', '_'))] = youtube_id
else:
# Convert XML attrs into Python values.
if attr in conversions:
value = conversions[attr](value)
model_data[attr] = value
return model_data
@staticmethod
def _parse_time(str_time):
"""Converts s in '12:34:45' format to seconds. If s is
None, returns empty string"""
if not str_time:
return ''
else:
obj_time = time.strptime(str_time, '%H:%M:%S')
return datetime.timedelta(
hours=obj_time.tm_hour,
minutes=obj_time.tm_min,
seconds=obj_time.tm_sec
).total_seconds()
def _create_youtube_string(module):
"""
Create a string of Youtube IDs from `module`'s metadata
attributes. Only writes a speed if an ID is present in the
module. Necessary for backwards compatibility with XML-based
courses.
"""
youtube_ids = [
module.youtube_id_0_75,
module.youtube_id_1_0,
module.youtube_id_1_25,
module.youtube_id_1_5
]
youtube_speeds = ['0.75', '1.00', '1.25', '1.50']
return ','.join([':'.join(pair)
for pair
in zip(youtube_speeds, youtube_ids)
if pair[1]])
......@@ -2,22 +2,22 @@
"""Video xmodule tests in mongo."""
from . import BaseTestXmodule
from .test_videoalpha_xml import SOURCE_XML
from .test_video_xml import SOURCE_XML
from django.conf import settings
from xmodule.videoalpha_module import _create_youtube_string
from xmodule.video_module import _create_youtube_string
class TestVideo(BaseTestXmodule):
"""Integration tests: web client + mongo."""
CATEGORY = "videoalpha"
CATEGORY = "video"
DATA = SOURCE_XML
MODEL_DATA = {
'data': DATA
}
def setUp(self):
# Since the VideoAlphaDescriptor changes `self._model_data`,
# Since the VideoDescriptor changes `self._model_data`,
# we need to instantiate `self.item_module` through
# `self.item_descriptor` rather than directly constructing it
super(TestVideo, self).setUp()
......@@ -40,7 +40,7 @@ class TestVideo(BaseTestXmodule):
]).pop(),
404)
def test_videoalpha_constructor(self):
def test_video_constructor(self):
"""Make sure that all parameters extracted correclty from xml"""
context = self.item_module.get_html()
......@@ -74,7 +74,7 @@ class TestVideoNonYouTube(TestVideo):
"""Integration tests: web client + mongo."""
DATA = """
<videoalpha show_captions="true"
<video show_captions="true"
display_name="A Name"
sub="a_sub_file.srt.sjson"
start_time="01:00:03" end_time="01:00:10"
......@@ -82,13 +82,13 @@ class TestVideoNonYouTube(TestVideo):
<source src="example.mp4"/>
<source src="example.webm"/>
<source src="example.ogv"/>
</videoalpha>
</video>
"""
MODEL_DATA = {
'data': DATA
}
def test_videoalpha_constructor(self):
def test_video_constructor(self):
"""Make sure that if the 'youtube' attribute is omitted in XML, then
the template generates an empty string for the YouTube streams.
"""
......
# -*- coding: utf-8 -*-
# pylint: disable=W0212
"""Test for VideoAlpha Xmodule functional logic.
"""Test for Video Xmodule functional logic.
These test data read from xml, not from mongo.
We have a ModuleStoreTestCase class defined in
......@@ -20,14 +20,14 @@ import unittest
from django.conf import settings
from xmodule.videoalpha_module import (
VideoAlphaDescriptor, _create_youtube_string)
from xmodule.video_module import (
VideoDescriptor, _create_youtube_string)
from xmodule.modulestore import Location
from xmodule.tests import get_test_system, LogicTest
SOURCE_XML = """
<videoalpha show_captions="true"
<video show_captions="true"
display_name="A Name"
youtube="0.75:jNCf2gIqpeE,1.0:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg"
sub="a_sub_file.srt.sjson"
......@@ -36,12 +36,12 @@ SOURCE_XML = """
<source src="example.mp4"/>
<source src="example.webm"/>
<source src="example.ogv"/>
</videoalpha>
</video>
"""
class VideoAlphaFactory(object):
"""A helper class to create videoalpha modules with various parameters
class VideoFactory(object):
"""A helper class to create video modules with various parameters
for testing.
"""
......@@ -50,28 +50,28 @@ class VideoAlphaFactory(object):
@staticmethod
def create():
"""Method return VideoAlpha Xmodule instance."""
location = Location(["i4x", "edX", "videoalpha", "default",
"""Method return Video Xmodule instance."""
location = Location(["i4x", "edX", "video", "default",
"SampleProblem1"])
model_data = {'data': VideoAlphaFactory.sample_problem_xml_youtube,
model_data = {'data': VideoFactory.sample_problem_xml_youtube,
'location': location}
system = get_test_system()
system.render_template = lambda template, context: context
descriptor = VideoAlphaDescriptor(system, model_data)
descriptor = VideoDescriptor(system, model_data)
module = descriptor.xmodule(system)
return module
class VideoAlphaModuleUnitTest(unittest.TestCase):
"""Unit tests for VideoAlpha Xmodule."""
class VideoModuleUnitTest(unittest.TestCase):
"""Unit tests for Video Xmodule."""
def test_videoalpha_get_html(self):
def test_video_get_html(self):
"""Make sure that all parameters extracted correclty from xml"""
module = VideoAlphaFactory.create()
module = VideoFactory.create()
module.runtime.render_template = lambda template, context: context
sources = {
......@@ -98,18 +98,18 @@ class VideoAlphaModuleUnitTest(unittest.TestCase):
self.assertEqual(module.get_html(), expected_context)
def test_videoalpha_instance_state(self):
module = VideoAlphaFactory.create()
def test_video_instance_state(self):
module = VideoFactory.create()
self.assertDictEqual(
json.loads(module.get_instance_state()),
{'position': 0})
class VideoAlphaModuleLogicTest(LogicTest):
"""Tests for logic of VideoAlpha Xmodule."""
class VideoModuleLogicTest(LogicTest):
"""Tests for logic of Video Xmodule."""
descriptor_class = VideoAlphaDescriptor
descriptor_class = VideoDescriptor
raw_model_data = {
'data': '<video />'
......@@ -117,23 +117,23 @@ class VideoAlphaModuleLogicTest(LogicTest):
def test_parse_time(self):
"""Ensure that times are parsed correctly into seconds."""
output = VideoAlphaDescriptor._parse_time('00:04:07')
output = VideoDescriptor._parse_time('00:04:07')
self.assertEqual(output, 247)
def test_parse_time_none(self):
"""Check parsing of None."""
output = VideoAlphaDescriptor._parse_time(None)
output = VideoDescriptor._parse_time(None)
self.assertEqual(output, '')
def test_parse_time_empty(self):
"""Check parsing of the empty string."""
output = VideoAlphaDescriptor._parse_time('')
output = VideoDescriptor._parse_time('')
self.assertEqual(output, '')
def test_parse_youtube(self):
"""Test parsing old-style Youtube ID strings into a dict."""
youtube_str = '0.75:jNCf2gIqpeE,1.00:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg'
output = VideoAlphaDescriptor._parse_youtube(youtube_str)
output = VideoDescriptor._parse_youtube(youtube_str)
self.assertEqual(output, {'0.75': 'jNCf2gIqpeE',
'1.00': 'ZwkTiUPN0mg',
'1.25': 'rsq9auxASqI',
......@@ -145,7 +145,7 @@ class VideoAlphaModuleLogicTest(LogicTest):
empty string.
"""
youtube_str = '0.75:jNCf2gIqpeE'
output = VideoAlphaDescriptor._parse_youtube(youtube_str)
output = VideoDescriptor._parse_youtube(youtube_str)
self.assertEqual(output, {'0.75': 'jNCf2gIqpeE',
'1.00': '',
'1.25': '',
......@@ -158,8 +158,8 @@ class VideoAlphaModuleLogicTest(LogicTest):
youtube_str = '1.00:p2Q6BrNhdh8'
youtube_str_hack = '1.0:p2Q6BrNhdh8'
self.assertEqual(
VideoAlphaDescriptor._parse_youtube(youtube_str),
VideoAlphaDescriptor._parse_youtube(youtube_str_hack)
VideoDescriptor._parse_youtube(youtube_str),
VideoDescriptor._parse_youtube(youtube_str_hack)
)
def test_parse_youtube_empty(self):
......@@ -167,7 +167,7 @@ class VideoAlphaModuleLogicTest(LogicTest):
Some courses have empty youtube attributes, so we should handle
that well.
"""
self.assertEqual(VideoAlphaDescriptor._parse_youtube(''),
self.assertEqual(VideoDescriptor._parse_youtube(''),
{'0.75': '',
'1.00': '',
'1.25': '',
......
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