Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
4a36fa89
Commit
4a36fa89
authored
Dec 26, 2013
by
polesye
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
BLD-368: Turn "download transcript" into a dropdown.
parent
2536224b
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
508 additions
and
44 deletions
+508
-44
CHANGELOG.rst
+3
-0
cms/djangoapps/contentstore/features/component_settings_editor_helpers.py
+1
-1
cms/djangoapps/contentstore/features/video-editor.py
+1
-1
cms/static/js/views/metadata.js
+13
-0
cms/static/sass/views/_unit.scss
+6
-0
common/lib/xmodule/xmodule/tests/test_video.py
+17
-5
common/lib/xmodule/xmodule/video_module.py
+99
-4
lms/djangoapps/courseware/tests/__init__.py
+30
-22
lms/djangoapps/courseware/tests/test_video_mongo.py
+337
-7
lms/djangoapps/courseware/tests/test_video_xml.py
+1
-4
No files found.
CHANGELOG.rst
View file @
4a36fa89
...
@@ -5,6 +5,9 @@ These are notable changes in edx-platform. This is a rolling list of changes,
...
@@ -5,6 +5,9 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near
in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected.
the top. Include a label indicating the component affected.
Blades: Change the track field to a dropdown that will allow students
to download the transcript of the video without timecodes. BLD-368.
Blades: Video player start-end time range is now shown even before Play is
Blades: Video player start-end time range is now shown even before Play is
clicked. Video player VCR time shows correct non-zero total time for YouTube
clicked. Video player VCR time shows correct non-zero total time for YouTube
videos even before Play is clicked. BLD-529.
videos even before Play is clicked. BLD-529.
...
...
cms/djangoapps/contentstore/features/component_settings_editor_helpers.py
View file @
4a36fa89
...
@@ -140,7 +140,7 @@ def verify_setting_entry(setting, display_name, value, explicitly_set):
...
@@ -140,7 +140,7 @@ def verify_setting_entry(setting, display_name, value, explicitly_set):
for the problem, rather than derived from the defaults. This is verified
for the problem, rather than derived from the defaults. This is verified
by the existence of a "Clear" button next to the field value.
by the existence of a "Clear" button next to the field value.
"""
"""
assert_equal
(
display_name
,
setting
.
find_by_css
(
'.setting-label'
)[
0
]
.
html
)
assert_equal
(
display_name
,
setting
.
find_by_css
(
'.setting-label'
)[
0
]
.
html
.
strip
()
)
# Check if the web object is a list type
# Check if the web object is a list type
# If so, we use a slightly different mechanism for determining its value
# If so, we use a slightly different mechanism for determining its value
...
...
cms/djangoapps/contentstore/features/video-editor.py
View file @
4a36fa89
...
@@ -40,12 +40,12 @@ def correct_video_settings(_step):
...
@@ -40,12 +40,12 @@ def correct_video_settings(_step):
# advanced
# advanced
[
'Display Name'
,
'Video'
,
False
],
[
'Display Name'
,
'Video'
,
False
],
[
'Download Transcript'
,
''
,
False
],
[
'Download Video'
,
''
,
False
],
[
'Download Video'
,
''
,
False
],
[
'End Time'
,
'00:00:00'
,
False
],
[
'End Time'
,
'00:00:00'
,
False
],
[
'HTML5 Transcript'
,
''
,
False
],
[
'HTML5 Transcript'
,
''
,
False
],
[
'Show Transcript'
,
'True'
,
False
],
[
'Show Transcript'
,
'True'
,
False
],
[
'Start Time'
,
'00:00:00'
,
False
],
[
'Start Time'
,
'00:00:00'
,
False
],
[
'Transcript Download Allowed'
,
'False'
,
False
],
[
'Video Sources'
,
''
,
False
],
[
'Video Sources'
,
''
,
False
],
[
'Youtube ID'
,
'OEoXaMPEzfM'
,
False
],
[
'Youtube ID'
,
'OEoXaMPEzfM'
,
False
],
[
'Youtube ID for .75x speed'
,
''
,
False
],
[
'Youtube ID for .75x speed'
,
''
,
False
],
...
...
cms/static/js/views/metadata.js
View file @
4a36fa89
...
@@ -94,6 +94,19 @@ function(BaseView, _, MetadataModel, AbstractEditor, VideoList) {
...
@@ -94,6 +94,19 @@ function(BaseView, _, MetadataModel, AbstractEditor, VideoList) {
templateName
:
"metadata-string-entry"
,
templateName
:
"metadata-string-entry"
,
render
:
function
()
{
AbstractEditor
.
prototype
.
render
.
apply
(
this
);
// If the model has property `non editable` equals `true`,
// the field is disabled, but user is able to clear it.
if
(
this
.
model
.
get
(
'non_editable'
))
{
this
.
$el
.
find
(
'#'
+
this
.
uniqueId
)
.
prop
(
'readonly'
,
true
)
.
addClass
(
'is-disabled'
);
}
},
getValueFromEditor
:
function
()
{
getValueFromEditor
:
function
()
{
return
this
.
$el
.
find
(
'#'
+
this
.
uniqueId
).
val
();
return
this
.
$el
.
find
(
'#'
+
this
.
uniqueId
).
val
();
},
},
...
...
cms/static/sass/views/_unit.scss
View file @
4a36fa89
...
@@ -708,6 +708,12 @@ body.course.unit,.view-unit {
...
@@ -708,6 +708,12 @@ body.course.unit,.view-unit {
text-overflow
:
ellipsis
;
text-overflow
:
ellipsis
;
}
}
//Allows users to copy full value of disabled inputs.
input
.is-disabled
{
text-overflow
:
clip
;
opacity
:
.5
;
}
input
[
type
=
"number"
]
{
input
[
type
=
"number"
]
{
width
:
38
.5%
;
width
:
38
.5%
;
...
...
common/lib/xmodule/xmodule/tests/test_video.py
View file @
4a36fa89
...
@@ -25,7 +25,6 @@ from .test_import import DummySystem
...
@@ -25,7 +25,6 @@ from .test_import import DummySystem
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
textwrap
import
dedent
from
xmodule.tests
import
get_test_descriptor_system
from
xmodule.tests
import
get_test_descriptor_system
...
@@ -187,6 +186,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -187,6 +186,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
<video display_name="Test Video"
<video display_name="Test Video"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
show_captions="false"
show_captions="false"
download_track="true"
start_time="00:00:01"
start_time="00:00:01"
end_time="00:01:00">
end_time="00:01:00">
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
...
@@ -211,6 +211,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -211,6 +211,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'track'
:
'http://www.example.com/track'
,
'track'
:
'http://www.example.com/track'
,
'download_track'
:
True
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
],
'data'
:
''
'data'
:
''
})
})
...
@@ -221,6 +222,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -221,6 +222,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
<video display_name="Test Video"
<video display_name="Test Video"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
show_captions="false"
show_captions="false"
download_track="false"
start_time="00:00:01"
start_time="00:00:01"
end_time="00:01:00">
end_time="00:01:00">
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
...
@@ -237,6 +239,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -237,6 +239,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'track'
:
'http://www.example.com/track'
,
'track'
:
'http://www.example.com/track'
,
'download_track'
:
False
,
'source'
:
'http://www.example.com/source.mp4'
,
'source'
:
'http://www.example.com/source.mp4'
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
'data'
:
''
...
@@ -253,7 +256,6 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -253,7 +256,6 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
youtube="1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA"
youtube="1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA"
show_captions="true">
show_captions="true">
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
<track src="http://www.example.com/track"/>
</video>
</video>
'''
'''
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
())
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
())
...
@@ -265,7 +267,8 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -265,7 +267,8 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'show_captions'
:
True
,
'show_captions'
:
True
,
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'track'
:
'http://www.example.com/track'
,
'track'
:
''
,
'download_track'
:
False
,
'source'
:
'http://www.example.com/source.mp4'
,
'source'
:
'http://www.example.com/source.mp4'
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
'data'
:
''
...
@@ -287,6 +290,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -287,6 +290,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'track'
:
''
,
'track'
:
''
,
'download_track'
:
False
,
'source'
:
''
,
'source'
:
''
,
'html5_sources'
:
[],
'html5_sources'
:
[],
'data'
:
''
'data'
:
''
...
@@ -305,6 +309,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -305,6 +309,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
source=""http://download_video""
source=""http://download_video""
sub=""html5_subtitles""
sub=""html5_subtitles""
track=""http://download_track""
track=""http://download_track""
download_track="true"
youtube_id_0_75=""OEoXaMPEzf65""
youtube_id_0_75=""OEoXaMPEzf65""
youtube_id_1_25=""OEoXaMPEzf125""
youtube_id_1_25=""OEoXaMPEzf125""
youtube_id_1_5=""OEoXaMPEzf15""
youtube_id_1_5=""OEoXaMPEzf15""
...
@@ -321,6 +326,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -321,6 +326,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'track'
:
'http://download_track'
,
'track'
:
'http://download_track'
,
'download_track'
:
True
,
'source'
:
'http://download_video'
,
'source'
:
'http://download_video'
,
'html5_sources'
:
[
"source_1"
,
"source_2"
],
'html5_sources'
:
[
"source_1"
,
"source_2"
],
'data'
:
''
'data'
:
''
...
@@ -343,6 +349,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -343,6 +349,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'track'
:
''
,
'track'
:
''
,
'download_track'
:
False
,
'source'
:
''
,
'source'
:
''
,
'html5_sources'
:
[],
'html5_sources'
:
[],
'data'
:
''
'data'
:
''
...
@@ -373,6 +380,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -373,6 +380,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'track'
:
'http://www.example.com/track'
,
'track'
:
'http://www.example.com/track'
,
'download_track'
:
True
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
'data'
:
''
})
})
...
@@ -402,6 +410,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -402,6 +410,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'track'
:
'http://www.example.com/track'
,
'track'
:
'http://www.example.com/track'
,
'download_track'
:
True
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
'data'
:
''
})
})
...
@@ -431,6 +440,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -431,6 +440,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'track'
:
'http://www.example.com/track'
,
'track'
:
'http://www.example.com/track'
,
'download_track'
:
True
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
'data'
:
''
})
})
...
@@ -461,11 +471,12 @@ class VideoExportTestCase(unittest.TestCase):
...
@@ -461,11 +471,12 @@ class VideoExportTestCase(unittest.TestCase):
desc
.
start_time
=
datetime
.
timedelta
(
seconds
=
1.0
)
desc
.
start_time
=
datetime
.
timedelta
(
seconds
=
1.0
)
desc
.
end_time
=
datetime
.
timedelta
(
seconds
=
60
)
desc
.
end_time
=
datetime
.
timedelta
(
seconds
=
60
)
desc
.
track
=
'http://www.example.com/track'
desc
.
track
=
'http://www.example.com/track'
desc
.
download_track
=
True
desc
.
html5_sources
=
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
]
desc
.
html5_sources
=
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
]
xml
=
desc
.
definition_to_xml
(
None
)
# We don't use the `resource_fs` parameter
xml
=
desc
.
definition_to_xml
(
None
)
# We don't use the `resource_fs` parameter
expected
=
etree
.
fromstring
(
'''
\
expected
=
etree
.
fromstring
(
'''
\
<video url_name="SampleProblem1" 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 url_name="SampleProblem1" 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"
download_track="true"
>
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.ogg"/>
<source src="http://www.example.com/source.ogg"/>
<track src="http://www.example.com/track"/>
<track src="http://www.example.com/track"/>
...
@@ -488,11 +499,12 @@ class VideoExportTestCase(unittest.TestCase):
...
@@ -488,11 +499,12 @@ class VideoExportTestCase(unittest.TestCase):
desc
.
start_time
=
datetime
.
timedelta
(
seconds
=
5.0
)
desc
.
start_time
=
datetime
.
timedelta
(
seconds
=
5.0
)
desc
.
end_time
=
datetime
.
timedelta
(
seconds
=
0.0
)
desc
.
end_time
=
datetime
.
timedelta
(
seconds
=
0.0
)
desc
.
track
=
'http://www.example.com/track'
desc
.
track
=
'http://www.example.com/track'
desc
.
download_track
=
True
desc
.
html5_sources
=
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
]
desc
.
html5_sources
=
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
]
xml
=
desc
.
definition_to_xml
(
None
)
# We don't use the `resource_fs` parameter
xml
=
desc
.
definition_to_xml
(
None
)
# We don't use the `resource_fs` parameter
expected
=
etree
.
fromstring
(
'''
\
expected
=
etree
.
fromstring
(
'''
\
<video url_name="SampleProblem1" start_time="0:00:05" youtube="0.75:izygArpw-Qo,1.00:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8" show_captions="false">
<video url_name="SampleProblem1" start_time="0:00:05" youtube="0.75:izygArpw-Qo,1.00:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8" show_captions="false"
download_track="true"
>
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.ogg"/>
<source src="http://www.example.com/source.ogg"/>
<track src="http://www.example.com/track"/>
<track src="http://www.example.com/track"/>
...
...
common/lib/xmodule/xmodule/video_module.py
View file @
4a36fa89
...
@@ -13,18 +13,24 @@ in XML.
...
@@ -13,18 +13,24 @@ in XML.
import
json
import
json
import
logging
import
logging
from
HTMLParser
import
HTMLParser
from
lxml
import
etree
from
lxml
import
etree
from
pkg_resources
import
resource_string
from
pkg_resources
import
resource_string
import
datetime
import
datetime
import
copy
import
copy
from
webob
import
Response
from
django.http
import
Http404
from
django.http
import
Http404
from
django.conf
import
settings
from
django.conf
import
settings
from
xmodule.x_module
import
XModule
from
xmodule.x_module
import
XModule
,
module_attr
from
xmodule.editing_module
import
TabsEditingDescriptor
from
xmodule.editing_module
import
TabsEditingDescriptor
from
xmodule.raw_module
import
EmptyDataRawDescriptor
from
xmodule.raw_module
import
EmptyDataRawDescriptor
from
xmodule.xml_module
import
is_pointer_tag
,
name_to_pathname
,
deserialize_field
from
xmodule.xml_module
import
is_pointer_tag
,
name_to_pathname
,
deserialize_field
from
xmodule.contentstore.django
import
contentstore
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.exceptions
import
NotFoundError
from
xblock.core
import
XBlock
from
xblock.fields
import
Scope
,
String
,
Boolean
,
List
,
Integer
,
ScopeIds
from
xblock.fields
import
Scope
,
String
,
Boolean
,
List
,
Integer
,
ScopeIds
from
xmodule.fields
import
RelativeTime
from
xmodule.fields
import
RelativeTime
...
@@ -103,11 +109,19 @@ class VideoFields(object):
...
@@ -103,11 +109,19 @@ class VideoFields(object):
display_name
=
"Video Sources"
,
display_name
=
"Video Sources"
,
scope
=
Scope
.
settings
,
scope
=
Scope
.
settings
,
)
)
# `track` is deprecated field and should not be used in future.
# `download_track` is used instead.
track
=
String
(
track
=
String
(
help
=
"The external URL to download the timed transcript track.
This appears as a link beneath the video.
"
,
help
=
"The external URL to download the timed transcript track."
,
display_name
=
"Download Transcript"
,
display_name
=
"Download Transcript"
,
scope
=
Scope
.
settings
,
scope
=
Scope
.
settings
,
default
=
""
default
=
''
)
download_track
=
Boolean
(
help
=
"Show a link beneath the video to allow students to download the transcript. Note: You must add a link to the HTML5 Transcript field above."
,
display_name
=
"Transcript Download Allowed"
,
scope
=
Scope
.
settings
,
default
=
False
)
)
sub
=
String
(
sub
=
String
(
help
=
"The name of the timed transcript track (for non-Youtube videos)."
,
help
=
"The name of the timed transcript track (for non-Youtube videos)."
,
...
@@ -162,18 +176,25 @@ class VideoModule(VideoFields, XModule):
...
@@ -162,18 +176,25 @@ class VideoModule(VideoFields, XModule):
raise
Http404
()
raise
Http404
()
def
get_html
(
self
):
def
get_html
(
self
):
track_url
=
None
caption_asset_path
=
"/static/subs/"
caption_asset_path
=
"/static/subs/"
get_ext
=
lambda
filename
:
filename
.
rpartition
(
'.'
)[
-
1
]
get_ext
=
lambda
filename
:
filename
.
rpartition
(
'.'
)[
-
1
]
sources
=
{
get_ext
(
src
):
src
for
src
in
self
.
html5_sources
}
sources
=
{
get_ext
(
src
):
src
for
src
in
self
.
html5_sources
}
sources
[
'main'
]
=
self
.
source
sources
[
'main'
]
=
self
.
source
if
self
.
download_track
:
if
self
.
track
:
track_url
=
self
.
track
elif
self
.
sub
:
track_url
=
self
.
runtime
.
handler_url
(
self
,
'download_transcript'
)
return
self
.
system
.
render_template
(
'video.html'
,
{
return
self
.
system
.
render_template
(
'video.html'
,
{
'youtube_streams'
:
_create_youtube_string
(
self
),
'youtube_streams'
:
_create_youtube_string
(
self
),
'id'
:
self
.
location
.
html_id
(),
'id'
:
self
.
location
.
html_id
(),
'sub'
:
self
.
sub
,
'sub'
:
self
.
sub
,
'sources'
:
sources
,
'sources'
:
sources
,
'track'
:
self
.
track
,
'track'
:
track_url
,
'display_name'
:
self
.
display_name_with_default
,
'display_name'
:
self
.
display_name_with_default
,
# This won't work when we move to data that
# This won't work when we move to data that
# isn't on the filesystem
# isn't on the filesystem
...
@@ -189,10 +210,58 @@ class VideoModule(VideoFields, XModule):
...
@@ -189,10 +210,58 @@ class VideoModule(VideoFields, XModule):
'yt_test_url'
:
settings
.
YOUTUBE_TEST_URL
'yt_test_url'
:
settings
.
YOUTUBE_TEST_URL
})
})
def
get_transcript
(
self
,
subs_id
):
'''
Returns transcript without timecodes.
Args:
`subs_id`: str, subtitles id
Raises:
- NotFoundError if cannot find transcript file in storage.
- ValueError if transcript file is incorrect JSON.
- KeyError if transcript file has incorrect format.
'''
filename
=
'subs_{0}.srt.sjson'
.
format
(
subs_id
)
content_location
=
StaticContent
.
compute_location
(
self
.
location
.
org
,
self
.
location
.
course
,
filename
)
data
=
contentstore
()
.
find
(
content_location
)
.
data
text
=
json
.
loads
(
data
)[
'text'
]
return
HTMLParser
()
.
unescape
(
"
\n
"
.
join
(
text
))
@XBlock.handler
def
download_transcript
(
self
,
__
,
___
):
"""
This is called to get transcript file without timecodes to student.
"""
try
:
subs
=
self
.
get_transcript
(
self
.
sub
)
except
(
NotFoundError
):
log
.
debug
(
"Can't find content in storage for
%
s transcript"
,
self
.
sub
)
return
Response
(
status
=
404
)
except
(
ValueError
,
KeyError
):
log
.
debug
(
"Invalid transcript JSON."
)
return
Response
(
status
=
400
)
response
=
Response
(
subs
,
headerlist
=
[
(
'Content-Disposition'
,
'attachment; filename="{0}.txt"'
.
format
(
self
.
sub
)),
])
response
.
content_type
=
"text/plain; charset=utf-8"
return
response
class
VideoDescriptor
(
VideoFields
,
TabsEditingDescriptor
,
EmptyDataRawDescriptor
):
class
VideoDescriptor
(
VideoFields
,
TabsEditingDescriptor
,
EmptyDataRawDescriptor
):
"""Descriptor for `VideoModule`."""
"""Descriptor for `VideoModule`."""
module_class
=
VideoModule
module_class
=
VideoModule
download_transcript
=
module_attr
(
'download_transcript'
)
tabs
=
[
tabs
=
[
{
{
...
@@ -207,6 +276,12 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -207,6 +276,12 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
]
]
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
'''
`track` is deprecated field.
If `track` field exists show `track` field on front-end as not-editable
but clearable. Dropdown `download_track` is a new field and it has value
True.
'''
super
(
VideoDescriptor
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
VideoDescriptor
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
# For backwards compatibility -- if we've got XML data, parse
# For backwards compatibility -- if we've got XML data, parse
# it out and set the metadata fields
# it out and set the metadata fields
...
@@ -215,6 +290,24 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -215,6 +290,24 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
self
.
_field_data
.
set_many
(
self
,
field_data
)
self
.
_field_data
.
set_many
(
self
,
field_data
)
del
self
.
data
del
self
.
data
self
.
track_visible
=
False
if
self
.
track
:
self
.
track_visible
=
True
download_track
=
self
.
editable_metadata_fields
[
'download_track'
]
if
not
download_track
[
'explicitly_set'
]:
self
.
download_track
=
True
@property
def
editable_metadata_fields
(
self
):
editable_fields
=
super
(
VideoDescriptor
,
self
)
.
editable_metadata_fields
if
self
.
track_visible
:
editable_fields
[
'track'
][
'non_editable'
]
=
True
else
:
editable_fields
.
pop
(
'track'
)
return
editable_fields
@classmethod
@classmethod
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
"""
"""
...
@@ -265,6 +358,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -265,6 +358,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
'start_time'
:
self
.
start_time
,
'start_time'
:
self
.
start_time
,
'end_time'
:
self
.
end_time
,
'end_time'
:
self
.
end_time
,
'sub'
:
self
.
sub
,
'sub'
:
self
.
sub
,
'download_track'
:
json
.
dumps
(
self
.
download_track
),
}
}
for
key
,
value
in
attrs
.
items
():
for
key
,
value
in
attrs
.
items
():
# Mild workaround to ensure that tests pass -- if a field
# Mild workaround to ensure that tests pass -- if a field
...
@@ -282,6 +376,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -282,6 +376,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
ele
=
etree
.
Element
(
'track'
)
ele
=
etree
.
Element
(
'track'
)
ele
.
set
(
'src'
,
self
.
track
)
ele
.
set
(
'src'
,
self
.
track
)
xml
.
append
(
ele
)
xml
.
append
(
ele
)
return
xml
return
xml
def
get_context
(
self
):
def
get_context
(
self
):
...
...
lms/djangoapps/courseware/tests/__init__.py
View file @
4a36fa89
...
@@ -15,7 +15,6 @@ from edxmako.shortcuts import render_to_string
...
@@ -15,7 +15,6 @@ from edxmako.shortcuts import render_to_string
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
courseware.tests.modulestore_config
import
TEST_DATA_MIXED_MODULESTORE
from
courseware.tests.modulestore_config
import
TEST_DATA_MIXED_MODULESTORE
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xblock.fields
import
Scope
from
xmodule.tests
import
get_test_system
,
get_test_descriptor_system
from
xmodule.tests
import
get_test_system
,
get_test_descriptor_system
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
...
@@ -35,7 +34,7 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -35,7 +34,7 @@ class BaseTestXmodule(ModuleStoreTestCase):
Any xmodule should overwrite only next parameters for test:
Any xmodule should overwrite only next parameters for test:
1. CATEGORY
1. CATEGORY
2. DATA
2. DATA
or METADATA
3. MODEL_DATA
3. MODEL_DATA
4. COURSE_DATA and USER_COUNT if needed
4. COURSE_DATA and USER_COUNT if needed
...
@@ -48,6 +47,10 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -48,6 +47,10 @@ class BaseTestXmodule(ModuleStoreTestCase):
# Data from YAML common/lib/xmodule/xmodule/templates/NAME/default.yaml
# Data from YAML common/lib/xmodule/xmodule/templates/NAME/default.yaml
CATEGORY
=
"vertical"
CATEGORY
=
"vertical"
DATA
=
''
DATA
=
''
# METADATA must be overwritten for every instance that uses it. Otherwise,
# if we'll change it in the tests, it will be changed for all other instances
# of parent class.
METADATA
=
{}
MODEL_DATA
=
{
'data'
:
'<some_module></some_module>'
}
MODEL_DATA
=
{
'data'
:
'<some_module></some_module>'
}
def
new_module_runtime
(
self
):
def
new_module_runtime
(
self
):
...
@@ -71,8 +74,27 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -71,8 +74,27 @@ class BaseTestXmodule(ModuleStoreTestCase):
runtime
.
get_block
=
modulestore
()
.
get_item
runtime
.
get_block
=
modulestore
()
.
get_item
return
runtime
return
runtime
def
setUp
(
self
):
def
initialize_module
(
self
,
**
kwargs
):
kwargs
.
update
({
'parent_location'
:
self
.
section
.
location
,
'category'
:
self
.
CATEGORY
})
self
.
item_descriptor
=
ItemFactory
.
create
(
**
kwargs
)
self
.
runtime
=
self
.
new_descriptor_runtime
()
field_data
=
{}
field_data
.
update
(
self
.
MODEL_DATA
)
student_data
=
DictFieldData
(
field_data
)
self
.
item_descriptor
.
_field_data
=
LmsFieldData
(
self
.
item_descriptor
.
_field_data
,
student_data
)
self
.
item_descriptor
.
xmodule_runtime
=
self
.
new_module_runtime
()
self
.
item_module
=
self
.
item_descriptor
self
.
item_url
=
Location
(
self
.
item_module
.
location
)
.
url
()
def
setup_course
(
self
):
self
.
course
=
CourseFactory
.
create
(
data
=
self
.
COURSE_DATA
)
self
.
course
=
CourseFactory
.
create
(
data
=
self
.
COURSE_DATA
)
# Turn off cache.
# Turn off cache.
...
@@ -83,7 +105,7 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -83,7 +105,7 @@ class BaseTestXmodule(ModuleStoreTestCase):
parent_location
=
self
.
course
.
location
,
parent_location
=
self
.
course
.
location
,
category
=
"sequential"
,
category
=
"sequential"
,
)
)
section
=
ItemFactory
.
create
(
se
lf
.
se
ction
=
ItemFactory
.
create
(
parent_location
=
chapter
.
location
,
parent_location
=
chapter
.
location
,
category
=
"sequential"
category
=
"sequential"
)
)
...
@@ -97,24 +119,6 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -97,24 +119,6 @@ class BaseTestXmodule(ModuleStoreTestCase):
for
user
in
self
.
users
:
for
user
in
self
.
users
:
CourseEnrollmentFactory
.
create
(
user
=
user
,
course_id
=
self
.
course
.
id
)
CourseEnrollmentFactory
.
create
(
user
=
user
,
course_id
=
self
.
course
.
id
)
self
.
item_descriptor
=
ItemFactory
.
create
(
parent_location
=
section
.
location
,
category
=
self
.
CATEGORY
,
data
=
self
.
DATA
)
self
.
runtime
=
self
.
new_descriptor_runtime
()
field_data
=
{}
field_data
.
update
(
self
.
MODEL_DATA
)
student_data
=
DictFieldData
(
field_data
)
self
.
item_descriptor
.
_field_data
=
LmsFieldData
(
self
.
item_descriptor
.
_field_data
,
student_data
)
self
.
item_descriptor
.
xmodule_runtime
=
self
.
new_module_runtime
()
self
.
item_module
=
self
.
item_descriptor
self
.
item_url
=
Location
(
self
.
item_module
.
location
)
.
url
()
# login all users for acces to Xmodule
# login all users for acces to Xmodule
self
.
clients
=
{
user
.
username
:
Client
()
for
user
in
self
.
users
}
self
.
clients
=
{
user
.
username
:
Client
()
for
user
in
self
.
users
}
self
.
login_statuses
=
[
self
.
login_statuses
=
[
...
@@ -125,6 +129,10 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -125,6 +129,10 @@ class BaseTestXmodule(ModuleStoreTestCase):
self
.
assertTrue
(
all
(
self
.
login_statuses
))
self
.
assertTrue
(
all
(
self
.
login_statuses
))
def
setUp
(
self
):
self
.
setup_course
();
self
.
initialize_module
(
metadata
=
self
.
METADATA
,
data
=
self
.
DATA
)
def
get_url
(
self
,
dispatch
):
def
get_url
(
self
,
dispatch
):
"""Return item url with dispatch."""
"""Return item url with dispatch."""
return
reverse
(
return
reverse
(
...
...
lms/djangoapps/courseware/tests/test_video_mongo.py
View file @
4a36fa89
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
"""Video xmodule tests in mongo."""
"""Video xmodule tests in mongo."""
from
mock
import
patch
,
PropertyMock
import
os
import
tempfile
import
textwrap
from
functools
import
partial
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.modulestore
import
Location
from
xmodule.contentstore.django
import
contentstore
from
.
import
BaseTestXmodule
from
.
import
BaseTestXmodule
from
.test_video_xml
import
SOURCE_XML
from
.test_video_xml
import
SOURCE_XML
from
django.conf
import
settings
from
django.conf
import
settings
from
xmodule.video_module
import
_create_youtube_string
from
xmodule.video_module
import
_create_youtube_string
from
cache_toolbox.core
import
del_cached_content
from
xmodule.exceptions
import
NotFoundError
class
TestVideo
(
BaseTestXmodule
):
class
TestVideo
(
BaseTestXmodule
):
"""Integration tests: web client + mongo."""
"""Integration tests: web client + mongo."""
CATEGORY
=
"video"
CATEGORY
=
"video"
DATA
=
SOURCE_XML
DATA
=
SOURCE_XML
METADATA
=
{}
def
test_handle_ajax_dispatch
(
self
):
def
test_handle_ajax_dispatch
(
self
):
responses
=
{
responses
=
{
...
@@ -29,16 +39,21 @@ class TestVideo(BaseTestXmodule):
...
@@ -29,16 +39,21 @@ class TestVideo(BaseTestXmodule):
])
.
pop
(),
])
.
pop
(),
404
)
404
)
def
tearDown
(
self
):
_clear_assets
(
self
.
item_module
.
location
)
class
TestVideoYouTube
(
TestVideo
):
METADATA
=
{}
def
test_video_constructor
(
self
):
def
test_video_constructor
(
self
):
"""Make sure that all parameters extracted correclty from xml"""
"""Make sure that all parameters extracted correclty from xml"""
context
=
self
.
item_module
.
render
(
'student_view'
)
.
content
context
=
self
.
item_module
.
render
(
'student_view'
)
.
content
sources
=
{
sources
=
{
'main'
:
u'example.mp4'
,
'main'
:
u'example.mp4'
,
u'mp4'
:
u'example.mp4'
,
u'mp4'
:
u'example.mp4'
,
u'webm'
:
u'example.webm'
,
u'webm'
:
u'example.webm'
,
u'ogv'
:
u'example.ogv'
}
}
expected_context
=
{
expected_context
=
{
...
@@ -51,7 +66,7 @@ class TestVideo(BaseTestXmodule):
...
@@ -51,7 +66,7 @@ class TestVideo(BaseTestXmodule):
'sources'
:
sources
,
'sources'
:
sources
,
'start'
:
3603.0
,
'start'
:
3603.0
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'track'
:
''
,
'track'
:
None
,
'youtube_streams'
:
_create_youtube_string
(
self
.
item_module
),
'youtube_streams'
:
_create_youtube_string
(
self
.
item_module
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
False
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
False
),
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
...
@@ -75,12 +90,12 @@ class TestVideoNonYouTube(TestVideo):
...
@@ -75,12 +90,12 @@ class TestVideoNonYouTube(TestVideo):
>
>
<source src="example.mp4"/>
<source src="example.mp4"/>
<source src="example.webm"/>
<source src="example.webm"/>
<source src="example.ogv"/>
</video>
</video>
"""
"""
MODEL_DATA
=
{
MODEL_DATA
=
{
'data'
:
DATA
'data'
:
DATA
}
}
METADATA
=
{}
def
test_video_constructor
(
self
):
def
test_video_constructor
(
self
):
"""Make sure that if the 'youtube' attribute is omitted in XML, then
"""Make sure that if the 'youtube' attribute is omitted in XML, then
...
@@ -90,7 +105,6 @@ class TestVideoNonYouTube(TestVideo):
...
@@ -90,7 +105,6 @@ class TestVideoNonYouTube(TestVideo):
'main'
:
u'example.mp4'
,
'main'
:
u'example.mp4'
,
u'mp4'
:
u'example.mp4'
,
u'mp4'
:
u'example.mp4'
,
u'webm'
:
u'example.webm'
,
u'webm'
:
u'example.webm'
,
u'ogv'
:
u'example.ogv'
}
}
context
=
self
.
item_module
.
render
(
'student_view'
)
.
content
context
=
self
.
item_module
.
render
(
'student_view'
)
.
content
...
@@ -105,6 +119,178 @@ class TestVideoNonYouTube(TestVideo):
...
@@ -105,6 +119,178 @@ class TestVideoNonYouTube(TestVideo):
'sources'
:
sources
,
'sources'
:
sources
,
'start'
:
3603.0
,
'start'
:
3603.0
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'track'
:
None
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'yt_test_timeout'
:
1500
,
'yt_test_url'
:
'https://gdata.youtube.com/feeds/api/videos/'
}
self
.
assertEqual
(
context
,
self
.
item_module
.
xmodule_runtime
.
render_template
(
'video.html'
,
expected_context
)
)
class
TestVideoGetTranscriptsMethod
(
TestVideo
):
"""
Make sure that `get_transcript` method works correctly
"""
DATA
=
"""
<video show_captions="true"
display_name="A Name"
>
<source src="example.mp4"/>
<source src="example.webm"/>
</video>
"""
MODEL_DATA
=
{
'data'
:
DATA
}
METADATA
=
{}
def
test_good_transcript
(
self
):
self
.
item_module
.
render
(
'student_view'
)
item
=
self
.
item_descriptor
.
xmodule_runtime
.
xmodule_instance
good_sjson
=
_create_file
(
content
=
"""
{
"start": [
270,
2720
],
"end": [
2720,
5430
],
"text": [
"Hi, welcome to Edx.",
"Let's start with what is on your screen right now."
]
}
"""
)
_upload_file
(
good_sjson
,
self
.
item_module
.
location
)
subs_id
=
_get_subs_id
(
good_sjson
.
name
)
text
=
item
.
get_transcript
(
subs_id
)
expected_text
=
"Hi, welcome to Edx.
\n
Let's start with what is on your screen right now."
self
.
assertEqual
(
text
,
expected_text
)
def
test_not_found_error
(
self
):
self
.
item_module
.
render
(
'student_view'
)
item
=
self
.
item_descriptor
.
xmodule_runtime
.
xmodule_instance
with
self
.
assertRaises
(
NotFoundError
):
item
.
get_transcript
(
'wrong'
)
def
test_value_error
(
self
):
self
.
item_module
.
render
(
'student_view'
)
item
=
self
.
item_descriptor
.
xmodule_runtime
.
xmodule_instance
good_sjson
=
_create_file
(
content
=
"""
bad content
"""
)
_upload_file
(
good_sjson
,
self
.
item_module
.
location
)
subs_id
=
_get_subs_id
(
good_sjson
.
name
)
with
self
.
assertRaises
(
ValueError
):
item
.
get_transcript
(
subs_id
)
def
test_key_error
(
self
):
self
.
item_module
.
render
(
'student_view'
)
item
=
self
.
item_descriptor
.
xmodule_runtime
.
xmodule_instance
good_sjson
=
_create_file
(
content
=
"""
{
"start": [
270,
2720
],
"end": [
2720,
5430
]
}
"""
)
_upload_file
(
good_sjson
,
self
.
item_module
.
location
)
subs_id
=
_get_subs_id
(
good_sjson
.
name
)
with
self
.
assertRaises
(
KeyError
):
item
.
get_transcript
(
subs_id
)
class
TestGetHtmlMethod
(
BaseTestXmodule
):
"""
Make sure that `get_html` works correctly.
"""
CATEGORY
=
"video"
DATA
=
SOURCE_XML
METADATA
=
{}
def
setUp
(
self
):
self
.
setup_course
();
def
test_get_html_track
(
self
):
SOURCE_XML
=
"""
<video show_captions="true"
display_name="A Name"
sub="{sub}" download_track="{download_track}"
start_time="01:00:03" end_time="01:00:10"
>
<source src="example.mp4"/>
<source src="example.webm"/>
{track}
</video>
"""
cases
=
[
{
'download_track'
:
u'true'
,
'track'
:
u'<track src="http://www.example.com/track"/>'
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'expected_track_url'
:
u'http://www.example.com/track'
,
},
{
'download_track'
:
u'true'
,
'track'
:
u''
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'expected_track_url'
:
u'a_sub_file.srt.sjson'
,
},
{
'download_track'
:
u'true'
,
'track'
:
u''
,
'sub'
:
u''
,
'expected_track_url'
:
None
},
{
'download_track'
:
u'false'
,
'track'
:
u'<track src="http://www.example.com/track"/>'
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'expected_track_url'
:
None
,
}
]
expected_context
=
{
'data_dir'
:
getattr
(
self
,
'data_dir'
,
None
),
'caption_asset_path'
:
'/static/subs/'
,
'show_captions'
:
'true'
,
'display_name'
:
u'A Name'
,
'end'
:
3610.0
,
'id'
:
None
,
'sources'
:
{
'main'
:
u'example.mp4'
,
u'mp4'
:
u'example.mp4'
,
u'webm'
:
u'example.webm'
},
'start'
:
3603.0
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'track'
:
''
,
'track'
:
''
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
...
@@ -112,7 +298,151 @@ class TestVideoNonYouTube(TestVideo):
...
@@ -112,7 +298,151 @@ class TestVideoNonYouTube(TestVideo):
'yt_test_url'
:
'https://gdata.youtube.com/feeds/api/videos/'
'yt_test_url'
:
'https://gdata.youtube.com/feeds/api/videos/'
}
}
for
data
in
cases
:
DATA
=
SOURCE_XML
.
format
(
download_track
=
data
[
'download_track'
],
track
=
data
[
'track'
],
sub
=
data
[
'sub'
],
)
self
.
initialize_module
(
data
=
DATA
)
track_url
=
self
.
item_descriptor
.
xmodule_runtime
.
handler_url
(
self
.
item_module
,
'download_transcript'
)
expected_context
.
update
({
'track'
:
track_url
if
data
[
'expected_track_url'
]
==
u'a_sub_file.srt.sjson'
else
data
[
'expected_track_url'
],
'sub'
:
data
[
'sub'
],
'id'
:
self
.
item_module
.
location
.
html_id
(),
})
context
=
self
.
item_module
.
render
(
'student_view'
)
.
content
self
.
assertEqual
(
self
.
assertEqual
(
context
,
context
,
self
.
item_module
.
xmodule_runtime
.
render_template
(
'video.html'
,
expected_context
)
self
.
item_module
.
xmodule_runtime
.
render_template
(
'video.html'
,
expected_context
)
)
)
class
TestVideoDescriptorInitialization
(
BaseTestXmodule
):
"""
Make sure that module initialization works correctly.
"""
CATEGORY
=
"video"
DATA
=
SOURCE_XML
METADATA
=
{}
def
setUp
(
self
):
self
.
setup_course
();
def
test_track_is_not_empty
(
self
):
metatdata
=
{
'track'
:
'http://example.org/track'
,
}
self
.
initialize_module
(
metadata
=
metatdata
)
fields
=
self
.
item_descriptor
.
editable_metadata_fields
self
.
assertIn
(
'track'
,
fields
)
self
.
assertEqual
(
self
.
item_module
.
track
,
'http://example.org/track'
)
self
.
assertTrue
(
self
.
item_module
.
download_track
)
self
.
assertTrue
(
self
.
item_module
.
track_visible
)
@patch
(
'xmodule.x_module.XModuleDescriptor.editable_metadata_fields'
,
new_callable
=
PropertyMock
)
def
test_download_track_is_explicitly_set
(
self
,
mock_editable_fields
):
mock_editable_fields
.
return_value
=
{
'download_track'
:
{
'default_value'
:
False
,
'explicitly_set'
:
True
,
'display_name'
:
'Transcript Download Allowed'
,
'help'
:
'Show a link beneath the video to allow students to download the transcript.'
,
'type'
:
'Boolean'
,
'value'
:
False
,
'field_name'
:
'download_track'
,
'options'
:
[
{
'display_name'
:
"True"
,
"value"
:
True
},
{
'display_name'
:
"False"
,
"value"
:
False
}
]
},
'track'
:
{
'default_value'
:
''
,
'explicitly_set'
:
False
,
'display_name'
:
'Download Transcript'
,
'help'
:
'The external URL to download the timed transcript track.'
,
'type'
:
'Generic'
,
'value'
:
u'http://example.org/track'
,
'field_name'
:
'track'
,
'options'
:
[]
},
}
metadata
=
{
'track'
:
'http://example.org/track'
,
}
self
.
initialize_module
(
metadata
=
metadata
)
fields
=
self
.
item_descriptor
.
editable_metadata_fields
self
.
assertIn
(
'track'
,
fields
)
self
.
assertEqual
(
self
.
item_module
.
track
,
'http://example.org/track'
)
self
.
assertFalse
(
self
.
item_module
.
download_track
)
self
.
assertTrue
(
self
.
item_module
.
track_visible
)
def
test_track_is_empty
(
self
):
metatdata
=
{
'track'
:
''
,
}
self
.
initialize_module
(
metadata
=
metatdata
)
fields
=
self
.
item_descriptor
.
editable_metadata_fields
self
.
assertNotIn
(
'track'
,
fields
)
self
.
assertEqual
(
self
.
item_module
.
track
,
''
)
self
.
assertFalse
(
self
.
item_module
.
download_track
)
self
.
assertFalse
(
self
.
item_module
.
track_visible
)
def
_clear_assets
(
location
):
store
=
contentstore
()
content_location
=
StaticContent
.
compute_location
(
location
.
org
,
location
.
course
,
location
.
name
)
assets
,
__
=
store
.
get_all_content_for_course
(
content_location
)
for
asset
in
assets
:
asset_location
=
Location
(
asset
[
"_id"
])
id
=
StaticContent
.
get_id_from_location
(
asset_location
)
store
.
delete
(
id
)
def
_get_subs_id
(
filename
):
basename
=
os
.
path
.
splitext
(
os
.
path
.
basename
(
filename
))[
0
]
return
basename
.
replace
(
'subs_'
,
''
)
.
replace
(
'.srt'
,
''
)
def
_create_file
(
content
=
''
):
sjson_file
=
tempfile
.
NamedTemporaryFile
(
prefix
=
"subs_"
,
suffix
=
".srt.sjson"
)
sjson_file
.
content_type
=
'application/json'
sjson_file
.
write
(
textwrap
.
dedent
(
content
))
sjson_file
.
seek
(
0
)
return
sjson_file
def
_upload_file
(
file
,
location
):
filename
=
'subs_{}.srt.sjson'
.
format
(
_get_subs_id
(
file
.
name
))
mime_type
=
file
.
content_type
content_location
=
StaticContent
.
compute_location
(
location
.
org
,
location
.
course
,
filename
)
sc_partial
=
partial
(
StaticContent
,
content_location
,
filename
,
mime_type
)
content
=
sc_partial
(
file
.
read
())
(
thumbnail_content
,
thumbnail_location
)
=
contentstore
()
.
generate_thumbnail
(
content
,
tempfile_path
=
None
)
del_cached_content
(
thumbnail_location
)
if
thumbnail_content
is
not
None
:
content
.
thumbnail_location
=
thumbnail_location
contentstore
()
.
save
(
content
)
del_cached_content
(
content
.
location
)
lms/djangoapps/courseware/tests/test_video_xml.py
View file @
4a36fa89
...
@@ -35,7 +35,6 @@ SOURCE_XML = """
...
@@ -35,7 +35,6 @@ SOURCE_XML = """
>
>
<source src="example.mp4"/>
<source src="example.mp4"/>
<source src="example.webm"/>
<source src="example.webm"/>
<source src="example.ogv"/>
</video>
</video>
"""
"""
...
@@ -68,12 +67,10 @@ class VideoModuleUnitTest(unittest.TestCase):
...
@@ -68,12 +67,10 @@ class VideoModuleUnitTest(unittest.TestCase):
def
test_video_get_html
(
self
):
def
test_video_get_html
(
self
):
"""Make sure that all parameters extracted correclty from xml"""
"""Make sure that all parameters extracted correclty from xml"""
module
=
VideoFactory
.
create
()
module
=
VideoFactory
.
create
()
sources
=
{
sources
=
{
'main'
:
'example.mp4'
,
'main'
:
'example.mp4'
,
'mp4'
:
'example.mp4'
,
'mp4'
:
'example.mp4'
,
'webm'
:
'example.webm'
,
'webm'
:
'example.webm'
,
'ogv'
:
'example.ogv'
}
}
expected_context
=
{
expected_context
=
{
...
@@ -87,7 +84,7 @@ class VideoModuleUnitTest(unittest.TestCase):
...
@@ -87,7 +84,7 @@ class VideoModuleUnitTest(unittest.TestCase):
'show_captions'
:
'true'
,
'show_captions'
:
'true'
,
'sources'
:
sources
,
'sources'
:
sources
,
'youtube_streams'
:
_create_youtube_string
(
module
),
'youtube_streams'
:
_create_youtube_string
(
module
),
'track'
:
''
,
'track'
:
None
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
False
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
False
),
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
'yt_test_url'
:
'https://gdata.youtube.com/feeds/api/videos/'
'yt_test_url'
:
'https://gdata.youtube.com/feeds/api/videos/'
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment