Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-val
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-val
Commits
727e643d
Unverified
Commit
727e643d
authored
Jan 26, 2018
by
M. Rehan
Committed by
GitHub
Jan 26, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #117 from edx/mrehan/transcript-foreign-key
Make Video an explicit foreign key in VideoTranscript model
parents
7d5c04df
eee4c2f0
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
207 additions
and
260 deletions
+207
-260
edxval/admin.py
+1
-1
edxval/api.py
+17
-32
edxval/migrations/0010_add_video_as_foreign_key.py
+31
-0
edxval/models.py
+9
-17
edxval/serializers.py
+7
-1
edxval/tests/constants.py
+0
-8
edxval/tests/test_api.py
+135
-198
edxval/tests/test_views.py
+7
-3
No files found.
edxval/admin.py
View file @
727e643d
...
...
@@ -69,7 +69,7 @@ class CourseVideoAdmin(admin.ModelAdmin):
class
VideoTranscriptAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'video
_id
'
,
'language_code'
,
'provider'
,
'file_format'
)
list_display
=
(
'video'
,
'language_code'
,
'provider'
,
'file_format'
)
model
=
VideoTranscript
...
...
edxval/api.py
View file @
727e643d
...
...
@@ -192,7 +192,7 @@ def is_transcript_available(video_id, language_code=None):
video_id: it can be an edx_video_id or an external_id extracted from external sources in a video component.
language_code: it will the language code of the requested transcript.
"""
filter_attrs
=
{
'video_id'
:
video_id
}
filter_attrs
=
{
'video_
_edx_video_
id'
:
video_id
}
if
language_code
:
filter_attrs
[
'language_code'
]
=
language_code
...
...
@@ -200,22 +200,6 @@ def is_transcript_available(video_id, language_code=None):
return
transcript_set
.
exists
()
def
get_video_transcripts
(
video_id
):
"""
Get a video's transcripts
Arguments:
video_id: it can be an edx_video_id or an external_id extracted from external sources in a video component.
"""
transcripts_set
=
VideoTranscript
.
objects
.
filter
(
video_id
=
video_id
)
transcripts
=
[]
if
transcripts_set
.
exists
():
transcripts
=
TranscriptSerializer
(
transcripts_set
,
many
=
True
)
.
data
return
transcripts
def
get_video_transcript
(
video_id
,
language_code
):
"""
Get video transcript info
...
...
@@ -245,7 +229,7 @@ def get_video_transcript_data(video_ids, language_code):
transcript_data
=
None
for
video_id
in
video_ids
:
try
:
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_id
=
video_id
,
language_code
=
language_code
)
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_
_edx_video_
id
=
video_id
,
language_code
=
language_code
)
transcript_data
=
dict
(
file_name
=
video_transcript
.
filename
,
content
=
video_transcript
.
transcript
.
file
.
read
()
...
...
@@ -276,7 +260,7 @@ def get_available_transcript_languages(video_ids):
A list containing unique transcript language codes for the video ids.
"""
available_languages
=
VideoTranscript
.
objects
.
filter
(
video_id__in
=
video_ids
video_
_edx_video_
id__in
=
video_ids
)
.
values_list
(
'language_code'
,
flat
=
True
)
...
...
@@ -324,7 +308,12 @@ def create_or_update_video_transcript(video_id, language_code, metadata, file_da
if
provider
and
provider
not
in
dict
(
TranscriptProviderType
.
CHOICES
)
.
keys
():
raise
InvalidTranscriptProvider
(
'{} transcript provider is not supported'
.
format
(
provider
))
video_transcript
,
__
=
VideoTranscript
.
create_or_update
(
video_id
,
language_code
,
metadata
,
file_data
)
try
:
# Video should be present in edxval in order to attach transcripts to it.
video
=
Video
.
objects
.
get
(
edx_video_id
=
video_id
)
video_transcript
,
__
=
VideoTranscript
.
create_or_update
(
video
,
language_code
,
metadata
,
file_data
)
except
Video
.
DoesNotExist
:
return
None
return
video_transcript
.
url
()
...
...
@@ -338,7 +327,7 @@ def delete_video_transcript(video_id, language_code):
language_code: language code of a video transcript
"""
try
:
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_id
=
video_id
,
language_code
=
language_code
)
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_
_edx_video_
id
=
video_id
,
language_code
=
language_code
)
# delete the actual transcript file from storage
video_transcript
.
transcript
.
delete
()
# delete the record from db
...
...
@@ -775,10 +764,9 @@ def export_to_xml(video_ids, course_id=None, external=False):
Raises:
ValVideoNotFoundError: if the video does not exist
"""
#
val does not store external videos, so construct transcripts information only.
#
TODO: This will be removed as a part of EDUCATOR-1789
if
external
:
video_el
=
Element
(
'video_asset'
)
return
create_transcripts_xml
(
video_ids
,
video_el
)
return
Element
(
'video_asset'
)
# for an internal video, first video id must be edx_video_id
video_id
=
video_ids
[
0
]
...
...
@@ -824,7 +812,7 @@ def create_transcripts_xml(video_ids, video_el):
Returns:
lxml Element object with transcripts information
"""
video_transcripts
=
VideoTranscript
.
objects
.
filter
(
video_
id__in
=
video_ids
)
video_transcripts
=
VideoTranscript
.
objects
.
filter
(
video_
_edx_video_id__in
=
video_ids
)
.
order_by
(
'language_code'
)
# create transcripts node only when we have transcripts for a video
if
video_transcripts
.
exists
():
transcripts_el
=
SubElement
(
video_el
,
'transcripts'
)
...
...
@@ -836,7 +824,7 @@ def create_transcripts_xml(video_ids, video_el):
transcripts_el
,
'transcript'
,
{
'video_id'
:
video_transcript
.
video_id
,
'video_id'
:
video_transcript
.
video
.
edx_video
_id
,
'file_name'
:
video_transcript
.
transcript
.
name
,
'language_code'
:
video_transcript
.
language_code
,
'file_format'
:
video_transcript
.
file_format
,
...
...
@@ -866,9 +854,9 @@ def import_from_xml(xml, edx_video_id, course_id=None):
if
xml
.
tag
!=
'video_asset'
:
raise
ValCannotCreateError
(
'Invalid XML'
)
#
if edx_video_id does not exist then create video transcripts only
#
TODO this will be moved as a part of EDUCATOR-2173
if
not
edx_video_id
:
return
create_transcript_objects
(
xml
)
return
# If video with edx_video_id already exists, associate it with the given course_id.
try
:
...
...
@@ -885,9 +873,6 @@ def import_from_xml(xml, edx_video_id, course_id=None):
if
image_file_name
:
VideoImage
.
create_or_update
(
course_video
,
image_file_name
)
# import transcripts
create_transcript_objects
(
xml
)
return
except
ValidationError
as
err
:
logger
.
exception
(
err
.
message
)
...
...
@@ -934,7 +919,7 @@ def create_transcript_objects(xml):
"""
for
transcript
in
xml
.
findall
(
'.//transcripts/transcript'
):
try
:
VideoTranscript
.
create_or_update
(
create_or_update_video_transcript
(
transcript
.
attrib
[
'video_id'
],
transcript
.
attrib
[
'language_code'
],
metadata
=
dict
(
...
...
edxval/migrations/0010_add_video_as_foreign_key.py
0 → 100644
View file @
727e643d
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'edxval'
,
'0009_auto_20171127_0406'
),
]
operations
=
[
migrations
.
AlterUniqueTogether
(
name
=
'videotranscript'
,
unique_together
=
set
([(
'language_code'
,)]),
),
migrations
.
RemoveField
(
model_name
=
'videotranscript'
,
name
=
'video_id'
,
),
migrations
.
AddField
(
model_name
=
'videotranscript'
,
name
=
'video'
,
field
=
models
.
ForeignKey
(
related_name
=
'video_transcripts'
,
to
=
'edxval.Video'
,
null
=
True
),
),
migrations
.
AlterUniqueTogether
(
name
=
'videotranscript'
,
unique_together
=
set
([(
'video'
,
'language_code'
)]),
),
]
edxval/models.py
View file @
727e643d
...
...
@@ -404,7 +404,7 @@ class VideoTranscript(TimeStampedModel):
"""
Transcript for a video
"""
video
_id
=
models
.
CharField
(
max_length
=
255
,
help_text
=
'It can be an edx_video_id or an external video id'
)
video
=
models
.
ForeignKey
(
Video
,
related_name
=
'video_transcripts'
,
null
=
True
)
transcript
=
CustomizableFileField
()
language_code
=
models
.
CharField
(
max_length
=
50
,
db_index
=
True
)
provider
=
models
.
CharField
(
...
...
@@ -415,27 +415,19 @@ class VideoTranscript(TimeStampedModel):
file_format
=
models
.
CharField
(
max_length
=
20
,
db_index
=
True
,
choices
=
TranscriptFormat
.
CHOICES
)
class
Meta
:
unique_together
=
(
'video
_id
'
,
'language_code'
)
unique_together
=
(
'video'
,
'language_code'
)
@property
def
filename
(
self
):
"""
Returns readable filename for a transcript
"""
try
:
video
=
Video
.
objects
.
get
(
edx_video_id
=
self
.
video_id
)
client_id
,
__
=
os
.
path
.
splitext
(
video
.
client_video_id
)
client_id
,
__
=
os
.
path
.
splitext
(
self
.
video
.
client_video_id
)
file_name
=
u'{name}-{language}.{format}'
.
format
(
name
=
client_id
,
language
=
self
.
language_code
,
format
=
self
.
file_format
)
except
Video
.
DoesNotExist
:
file_name
=
u'{name}-{language}.{format}'
.
format
(
name
=
self
.
video_id
,
language
=
self
.
language_code
,
format
=
self
.
file_format
)
return
file_name
...
...
@@ -449,19 +441,19 @@ class VideoTranscript(TimeStampedModel):
language_code(unicode): language of the requested transcript
"""
try
:
transcript
=
cls
.
objects
.
get
(
video_id
=
video_id
,
language_code
=
language_code
)
transcript
=
cls
.
objects
.
get
(
video_
_edx_video_
id
=
video_id
,
language_code
=
language_code
)
except
cls
.
DoesNotExist
:
transcript
=
None
return
transcript
@classmethod
def
create_or_update
(
cls
,
video
_id
,
language_code
,
metadata
,
file_data
=
None
):
def
create_or_update
(
cls
,
video
,
language_code
,
metadata
,
file_data
=
None
):
"""
Create or update Transcript object.
Arguments:
video
_id (str): unique id for a video
video
(Video): Video for which transcript is going to be saved.
language_code (str): language code for (to be created/updated) transcript
metadata (dict): A dict containing (to be overwritten) properties
file_data (InMemoryUploadedFile): File data to be saved
...
...
@@ -469,7 +461,7 @@ class VideoTranscript(TimeStampedModel):
Returns:
Returns a tuple of (video_transcript, created).
"""
video_transcript
,
created
=
cls
.
objects
.
get_or_create
(
video
_id
=
video_id
,
language_code
=
language_code
)
video_transcript
,
created
=
cls
.
objects
.
get_or_create
(
video
=
video
,
language_code
=
language_code
)
for
prop
,
value
in
metadata
.
iteritems
():
if
prop
in
[
'language_code'
,
'file_format'
,
'provider'
]:
...
...
@@ -489,7 +481,7 @@ class VideoTranscript(TimeStampedModel):
try
:
video_transcript
.
transcript
.
save
(
file_name
,
transcript_file_data
)
except
Exception
:
logger
.
exception
(
'VAL: Transcript save failed to storage for video_id [
%
s]'
,
video_id
)
logger
.
exception
(
'VAL: Transcript save failed to storage for video_id [
%
s]'
,
video
.
edx_video
_id
)
raise
video_transcript
.
save
()
...
...
@@ -503,7 +495,7 @@ class VideoTranscript(TimeStampedModel):
return
storage
.
url
(
self
.
transcript
.
name
)
def
__unicode__
(
self
):
return
u'{lang} Transcript for {video}'
.
format
(
lang
=
self
.
language_code
,
video
=
self
.
video_id
)
return
u'{lang} Transcript for {video}'
.
format
(
lang
=
self
.
language_code
,
video
=
self
.
video
.
edx_video
_id
)
class
Cielo24Turnaround
(
object
):
...
...
edxval/serializers.py
View file @
727e643d
...
...
@@ -57,11 +57,17 @@ class TranscriptSerializer(serializers.ModelSerializer):
"""
class
Meta
:
# pylint: disable=C1001, C0111
model
=
VideoTranscript
lookup_field
=
'video_id'
fields
=
(
'video_id'
,
'url'
,
'language_code'
,
'provider'
,
'file_format'
)
video_id
=
serializers
.
SerializerMethodField
()
url
=
serializers
.
SerializerMethodField
()
def
get_video_id
(
self
,
transcript
):
"""
Returns an edx video ID for the related video.
"""
return
transcript
.
video
.
edx_video_id
def
get_url
(
self
,
transcript
):
"""
Retrieves the transcript url.
...
...
edxval/tests/constants.py
View file @
727e643d
...
...
@@ -378,14 +378,6 @@ VIDEO_TRANSCRIPT_3PLAY = dict(
file_format
=
TranscriptFormat
.
SJSON
,
)
VIDEO_TRANSCRIPT_CUSTOM
=
dict
(
video_id
=
'external_video_id'
,
language_code
=
'de'
,
transcript
=
'wow.srt'
,
provider
=
TranscriptProviderType
.
CUSTOM
,
file_format
=
TranscriptFormat
.
SRT
,
)
TRANSCRIPT_PREFERENCES_CIELO24
=
dict
(
course_id
=
'edX/DemoX/Demo_Course'
,
provider
=
TranscriptProviderType
.
CIELO24
,
...
...
edxval/tests/test_api.py
View file @
727e643d
...
...
@@ -4,7 +4,6 @@ Tests for the API for Video Abstraction Layer
"""
import
json
import
os
import
tempfile
import
mock
from
ddt
import
data
,
ddt
,
unpack
...
...
@@ -908,16 +907,13 @@ class ExportTest(TestCase):
**
constants
.
ENCODED_VIDEO_DICT_HLS
)
# create external video transcripts
VideoTranscript
.
objects
.
create
(
**
constants
.
VIDEO_TRANSCRIPT_CUSTOM
)
video_transcript
=
dict
(
constants
.
VIDEO_TRANSCRIPT_CUSTOM
,
language_code
=
u'ar'
)
VideoTranscript
.
objects
.
create
(
**
video_transcript
)
video_transcript
=
dict
(
constants
.
VIDEO_TRANSCRIPT_CUSTOM
,
video_id
=
u'external_video_id2'
,
language_code
=
u'fr'
)
VideoTranscript
.
objects
.
create
(
**
video_transcript
)
# create internal video transcripts
VideoTranscript
.
objects
.
create
(
**
constants
.
VIDEO_TRANSCRIPT_CIELO24
)
VideoTranscript
.
objects
.
create
(
**
constants
.
VIDEO_TRANSCRIPT_3PLAY
)
transcript_data
=
dict
(
constants
.
VIDEO_TRANSCRIPT_CIELO24
,
video
=
video
)
transcript_data
.
pop
(
'video_id'
)
VideoTranscript
.
objects
.
create
(
**
transcript_data
)
transcript_data
=
dict
(
constants
.
VIDEO_TRANSCRIPT_3PLAY
,
video
=
video
)
transcript_data
.
pop
(
'video_id'
)
VideoTranscript
.
objects
.
create
(
**
transcript_data
)
def
assert_xml_equal
(
self
,
left
,
right
):
"""
...
...
@@ -949,8 +945,8 @@ class ExportTest(TestCase):
)
@data
(
{
'course_id'
:
None
,
'image'
:
''
},
{
'course_id'
:
'test-course'
,
'image'
:
'image.jpg'
},
{
'course_id'
:
None
,
'image'
:
''
},
{
'course_id'
:
'test-course'
,
'image'
:
'image.jpg'
},
)
@unpack
def
test_basic
(
self
,
course_id
,
image
):
...
...
@@ -960,8 +956,8 @@ class ExportTest(TestCase):
<encoded_video url="http://www.meowmagic.com" file_size="33" bitrate="44" profile="desktop"/>
<encoded_video url="https://www.tmnt.com/tmnt101.m3u8" file_size="100" bitrate="0" profile="hls"/>
<transcripts>
<transcript file_format="sjson" file_name="edxval/tests/data/wow.sjson" language_code="de" provider="3PlayMedia" video_id="
super-soaker
"/>
<transcript file_format="srt" file_name="wow.srt" language_code="en" provider="Cielo24" video_id="
super-soaker
" />
<transcript file_format="sjson" file_name="edxval/tests/data/wow.sjson" language_code="de" provider="3PlayMedia" video_id="
{video_id}
"/>
<transcript file_format="srt" file_name="wow.srt" language_code="en" provider="Cielo24" video_id="
{video_id}
" />
</transcripts>
</video_asset>
"""
.
format
(
image
=
image
,
video_id
=
constants
.
VIDEO_DICT_FISH
[
'edx_video_id'
]))
...
...
@@ -975,26 +971,6 @@ class ExportTest(TestCase):
with
self
.
assertRaises
(
ValVideoNotFoundError
):
api
.
export_to_xml
([
"unknown_video"
])
def
test_external_video_transcript
(
self
):
"""
Verify that transcript export for multiple external videos is working as expected.
"""
video_ids
=
[
'missing'
,
'external_video_id'
,
'missing2'
,
'external_video_id2'
]
expected
=
self
.
parse_xml
(
"""
<video_asset>
<transcripts>
<transcript file_format="srt" file_name="wow.srt" language_code="ar" provider="Custom" video_id="external_video_id"/>
<transcript file_format="srt" file_name="wow.srt" language_code="de" provider="Custom" video_id="external_video_id"/>
<transcript file_format="srt" file_name="wow.srt" language_code="fr" provider="Custom" video_id="external_video_id2"/>
</transcripts>
</video_asset>
"""
.
format
(
video_id
=
''
))
self
.
assert_xml_equal
(
api
.
export_to_xml
(
video_ids
,
external
=
True
),
expected
)
def
test_with_multiple_video_ids
(
self
):
"""
Verify that transcript export with multiple video ids is working as expected.
...
...
@@ -1006,8 +982,7 @@ class ExportTest(TestCase):
<encoded_video bitrate="44" file_size="33" profile="desktop" url="http://www.meowmagic.com" />
<encoded_video bitrate="0" file_size="100" profile="hls" url="https://www.tmnt.com/tmnt101.m3u8" />
<transcripts>
<transcript file_format="srt" file_name="wow.srt" language_code="ar" provider="Custom" video_id="external_video_id" />
<transcript file_format="srt" file_name="wow.srt" language_code="de" provider="Custom" video_id="external_video_id"/>
<transcript file_format="sjson" file_name="edxval/tests/data/wow.sjson" language_code="de" provider="3PlayMedia" video_id="super-soaker"/>
<transcript file_format="srt" file_name="wow.srt" language_code="en" provider="Cielo24" video_id="super-soaker" />
</transcripts>
</video_asset>
...
...
@@ -1115,7 +1090,7 @@ class ImportTest(TestCase):
Compare `received` with `expected` and assert if not equal
"""
# Verify total number of expected transcripts for a video
video_transcripts
=
VideoTranscript
.
objects
.
filter
(
video_id
=
video_id
)
video_transcripts
=
VideoTranscript
.
objects
.
filter
(
video_
_edx_video_
id
=
video_id
)
self
.
assertEqual
(
video_transcripts
.
count
(),
len
(
expected_transcripts
))
# Verify data for each transcript
...
...
@@ -1125,7 +1100,7 @@ class ImportTest(TestCase):
# get the imported transcript and rename `url` key
received
=
api
.
TranscriptSerializer
(
VideoTranscript
.
objects
.
get
(
video_id
=
video_id
,
language_code
=
language_code
)
VideoTranscript
.
objects
.
get
(
video_
_edx_video_
id
=
video_id
,
language_code
=
language_code
)
)
.
data
received
[
'name'
]
=
received
.
pop
(
'url'
)
...
...
@@ -1143,7 +1118,7 @@ class ImportTest(TestCase):
# there must not be any transcript before import
with
self
.
assertRaises
(
VideoTranscript
.
DoesNotExist
):
VideoTranscript
.
objects
.
get
(
video_id
=
constants
.
VIDEO_DICT_STAR
[
'edx_video_id'
])
VideoTranscript
.
objects
.
get
(
video_
_edx_video_
id
=
constants
.
VIDEO_DICT_STAR
[
'edx_video_id'
])
api
.
import_from_xml
(
xml
,
constants
.
VIDEO_DICT_STAR
[
'edx_video_id'
],
new_course_id
)
...
...
@@ -1209,7 +1184,7 @@ class ImportTest(TestCase):
# there must not be any transcript before import
with
self
.
assertRaises
(
VideoTranscript
.
DoesNotExist
):
VideoTranscript
.
objects
.
get
(
video_id
=
constants
.
VIDEO_DICT_FISH
[
"edx_video_id"
])
VideoTranscript
.
objects
.
get
(
video_
_edx_video_
id
=
constants
.
VIDEO_DICT_FISH
[
"edx_video_id"
])
api
.
import_from_xml
(
xml
,
constants
.
VIDEO_DICT_FISH
[
"edx_video_id"
],
course_id
)
...
...
@@ -1228,7 +1203,7 @@ class ImportTest(TestCase):
self
.
assert_transcripts
(
constants
.
VIDEO_DICT_FISH
[
"edx_video_id"
],
[
transcript_data
]
[]
)
def
test_existing_video_with_invalid_course_id
(
self
):
...
...
@@ -1290,26 +1265,6 @@ class ImportTest(TestCase):
xml
=
self
.
make_import_xml
(
video_dict
=
constants
.
VIDEO_DICT_FISH
)
self
.
assert_invalid_import
(
xml
,
"x"
*
300
)
def
test_external_video_transcript
(
self
):
"""
Verify that transcript import for external video working as expected.
"""
external_video_id
=
'little-star'
xml
=
etree
.
fromstring
(
"""
<video_asset>
<transcripts>
<transcript file_name="wow.srt" language_code="en" file_format="srt" provider='Cielo24' video_id="{video_id}"/>
<transcript file_name="edxval/tests/data/wow.sjson" language_code="de" file_format="sjson" provider='3PlayMedia' video_id="{video_id}"/>
</transcripts>
</video_asset>
"""
.
format
(
video_id
=
external_video_id
))
with
self
.
assertRaises
(
VideoTranscript
.
DoesNotExist
):
VideoTranscript
.
objects
.
get
(
video_id
=
external_video_id
)
api
.
import_from_xml
(
xml
,
''
)
self
.
assert_transcripts
(
external_video_id
,
[
self
.
transcript_data1
,
self
.
transcript_data2
])
def
test_external_no_video_transcript
(
self
):
"""
Verify that transcript import for external video working as expected when there is no transcript.
...
...
@@ -1325,7 +1280,7 @@ class ImportTest(TestCase):
"""
Verify that video transcript import working as expected if transcript xml data is missing.
"""
video_id
=
'
little-sta
r'
video_id
=
'
super-soake
r'
transcript_xml
=
'<transcript file_name="wow.srt" language_code="en" file_format="srt" provider="Cielo24"/>'
xml
=
etree
.
fromstring
(
"""
<video_asset>
...
...
@@ -1338,7 +1293,7 @@ class ImportTest(TestCase):
# there should be no video transcript before import
with
self
.
assertRaises
(
VideoTranscript
.
DoesNotExist
):
VideoTranscript
.
objects
.
get
(
video_id
=
video_id
)
VideoTranscript
.
objects
.
get
(
video_
_edx_video_
id
=
video_id
)
api
.
create_transcript_objects
(
xml
)
...
...
@@ -1347,7 +1302,7 @@ class ImportTest(TestCase):
transcript_xml
)
self
.
assert_transcripts
(
video_id
,
[
self
.
transcript_data
2
])
self
.
assert_transcripts
(
video_id
,
[
self
.
transcript_data
3
])
class
GetCourseVideoRemoveTest
(
TestCase
):
...
...
@@ -1676,60 +1631,94 @@ class TranscriptTest(TestCase):
"""
Creates video and video transcript objects.
"""
self
.
video1
=
Video
.
objects
.
create
(
**
constants
.
VIDEO_DICT_FISH
)
self
.
edx_video_id1
=
self
.
video1
.
edx_video_id
self
.
video2
=
Video
.
objects
.
create
(
**
constants
.
VIDEO_DICT_DIFFERENT_ID_FISH
)
self
.
edx_video_id2
=
self
.
video2
.
edx_video_id
self
.
transcript_data1
=
dict
(
constants
.
VIDEO_TRANSCRIPT_CIELO24
)
self
.
transcript_data1
[
'name'
]
=
self
.
transcript_data1
.
pop
(
'transcript'
)
self
.
transcript_data2
=
dict
(
constants
.
VIDEO_TRANSCRIPT_3PLAY
)
self
.
transcript_data2
[
'name'
]
=
self
.
transcript_data2
.
pop
(
'transcript'
)
self
.
transcript1
=
VideoTranscript
.
objects
.
create
(
**
constants
.
VIDEO_TRANSCRIPT_CIELO24
)
self
.
transcript2
=
VideoTranscript
.
objects
.
create
(
**
constants
.
VIDEO_TRANSCRIPT_3PLAY
)
self
.
video_id
=
'0987654321'
self
.
arrow_transcript_path
=
'edxval/tests/data/The_Arrow.srt'
self
.
flash_transcript_path
=
'edxval/tests/data/The_Flash.srt'
self
.
transcript_url
=
api
.
create_or_update_video_transcript
(
self
.
video_id
,
'ur'
,
metadata
=
{
'file_format'
:
TranscriptFormat
.
SRT
},
file_data
=
File
(
open
(
self
.
arrow_transcript_path
))
self
.
arrow_transcript_path
=
'edxval/tests/data/The_Arrow.srt'
# Set up a video (video_id -> super-soaker) with 'en' and 'fr' transcripts.
video_and_transcripts
=
self
.
setup_video_with_transcripts
(
video_data
=
constants
.
VIDEO_DICT_FISH
,
transcripts_data
=
[
{
'language_code'
:
'en'
,
'provider'
:
TranscriptProviderType
.
THREE_PLAY_MEDIA
,
'file_name'
:
None
,
'file_format'
:
TranscriptFormat
.
SRT
,
'file_data'
:
File
(
open
(
self
.
flash_transcript_path
))
},
{
'language_code'
:
'fr'
,
'provider'
:
TranscriptProviderType
.
CIELO24
,
'file_name'
:
None
,
'file_format'
:
TranscriptFormat
.
SRT
,
'file_data'
:
ContentFile
(
FILE_DATA
)
}
]
)
self
.
video1
=
video_and_transcripts
[
'video'
]
self
.
v1_transcript1
=
video_and_transcripts
[
'transcripts'
][
'en'
]
self
.
v1_transcript2
=
video_and_transcripts
[
'transcripts'
][
'fr'
]
# create a temporary transcript file
_
,
self
.
transcript_file
=
tempfile
.
mkstemp
(
suffix
=
'.srt'
,
dir
=
'edxval/tests/data/'
# Set up another video (video_id -> medium-soaker) with 'de' and 'zh' transcripts.
video_and_transcripts
=
self
.
setup_video_with_transcripts
(
video_data
=
constants
.
VIDEO_DICT_DIFFERENT_ID_FISH
,
transcripts_data
=
[
{
'language_code'
:
'de'
,
'provider'
:
TranscriptProviderType
.
CUSTOM
,
'file_name'
:
None
,
'file_format'
:
TranscriptFormat
.
SRT
,
'file_data'
:
File
(
open
(
self
.
arrow_transcript_path
))
},
{
'language_code'
:
'zh'
,
'provider'
:
TranscriptProviderType
.
CUSTOM
,
'file_name'
:
'non/existent/transcript/path'
,
'file_format'
:
TranscriptFormat
.
SRT
,
'file_data'
:
None
}
]
)
with
open
(
self
.
transcript_file
,
'w'
)
as
outfile
:
outfile
.
write
(
FILE_DATA
)
self
.
video2
=
video_and_transcripts
[
'video'
]
self
.
v2_transcript1
=
video_and_transcripts
[
'transcripts'
][
'de'
]
self
.
v2_transcript2
=
video_and_transcripts
[
'transcripts'
][
'zh'
]
self
.
transcript3
=
VideoTranscript
.
objects
.
create
(
video_id
=
'super-soaker'
,
language_code
=
'fr'
,
transcript
=
self
.
transcript_file
,
provider
=
TranscriptProviderType
.
THREE_PLAY_MEDIA
,
file_format
=
TranscriptFormat
.
SRT
,
def
setup_video_with_transcripts
(
self
,
video_data
,
transcripts_data
):
"""
Setup a video with transcripts and returns them
"""
result
=
dict
(
video
=
None
,
transcripts
=
{})
result
[
'video'
]
=
Video
.
objects
.
create
(
**
video_data
)
for
transcript_data
in
transcripts_data
:
language_code
=
transcript_data
[
'language_code'
]
transcript
,
__
=
VideoTranscript
.
create_or_update
(
video
=
result
[
'video'
],
language_code
=
language_code
,
metadata
=
{
'file_name'
:
transcript_data
[
'file_name'
],
'file_format'
:
transcript_data
[
'file_format'
],
'provider'
:
transcript_data
[
'provider'
]
},
file_data
=
transcript_data
[
'file_data'
]
)
result
[
'transcripts'
][
language_code
]
=
transcript
return
result
def
tearDown
(
self
):
"""
Reverse the setup
"""
# Remove the temporary transcript file
if
os
.
path
.
exists
(
self
.
transcript_file
):
os
.
remove
(
self
.
transcript_file
)
# Remove the transcript files
self
.
v1_transcript1
.
transcript
.
delete
()
self
.
v1_transcript2
.
transcript
.
delete
()
self
.
v2_transcript1
.
transcript
.
delete
()
@data
(
{
'video_id'
:
'super-soaker'
,
'language_code'
:
'en'
,
'expected_availability'
:
True
},
{
'video_id'
:
'super-soaker'
,
'language_code'
:
None
,
'expected_availability'
:
True
},
{
'video_id'
:
'super123'
,
'language_code'
:
'en'
,
'expected_availability'
:
False
},
{
'video_id'
:
'super-soaker'
,
'language_code'
:
'ro'
,
'expected_availability'
:
False
},
{
'video_id'
:
'medium-soaker'
,
'language_code'
:
'de'
,
'expected_availability'
:
True
},
)
@unpack
def
test_is_transcript_available
(
self
,
video_id
,
language_code
,
expected_availability
):
...
...
@@ -1754,13 +1743,13 @@ class TranscriptTest(TestCase):
"""
Verify that `get_video_transcript` works as expected if transcript is found.
"""
transcript
=
api
.
get_video_transcript
(
u'
0987654321'
,
u'u
r'
)
transcript
=
api
.
get_video_transcript
(
u'
super-soaker'
,
u'f
r'
)
expectation
=
{
'video_id'
:
u'
0987654321
'
,
'url'
:
self
.
transcript_url
,
'video_id'
:
u'
super-soaker
'
,
'url'
:
self
.
v1_transcript2
.
url
()
,
'file_format'
:
TranscriptFormat
.
SRT
,
'provider'
:
TranscriptProviderType
.
C
USTOM
,
'language_code'
:
u'
u
r'
'provider'
:
TranscriptProviderType
.
C
IELO24
,
'language_code'
:
u'
f
r'
}
self
.
assertDictEqual
(
transcript
,
expectation
)
...
...
@@ -1770,17 +1759,17 @@ class TranscriptTest(TestCase):
Verify that `get_video_transcript_data` logs and raises an exception.
"""
with
self
.
assertRaises
(
IOError
):
api
.
get_video_transcript_data
(
video_ids
=
[
'
super-soaker'
],
language_code
=
u'en
'
)
api
.
get_video_transcript_data
(
video_ids
=
[
'
medium-soaker'
],
language_code
=
u'zh
'
)
mock_logger
.
exception
.
assert_called_with
(
'[edx-val] Error while retrieving transcript for video=
%
s -- language_code=
%
s'
,
'
super
-soaker'
,
'
en
'
,
'
medium
-soaker'
,
'
zh
'
,
)
@data
(
{
'video_ids'
:
[
'non-existant-video'
,
'another-non-existant-id'
],
'language_code'
:
'en'
,
'result'
:
None
},
{
'video_ids'
:
[
'non-existant-video'
,
'
0987654321'
],
'language_code'
:
'en
'
,
'result'
:
None
},
{
'video_ids'
:
[
'non-existant-video'
,
'
super-soaker'
],
'language_code'
:
'zh
'
,
'result'
:
None
},
)
@unpack
def
test_get_video_transcript_data_not_found
(
self
,
video_ids
,
language_code
,
result
):
...
...
@@ -1791,11 +1780,11 @@ class TranscriptTest(TestCase):
self
.
assertEqual
(
transcript
,
result
)
@data
(
(
'
de'
,
'Shallow Swordfish-de.sjson'
,
'edxval/tests/data/wow.sjson
'
),
(
'
ur'
,
'0987654321-ur
.srt'
,
'edxval/tests/data/The_Arrow.srt'
)
(
'
super-soaker'
,
'en'
,
'Shallow Swordfish-en.srt'
,
'edxval/tests/data/The_Flash.srt
'
),
(
'
medium-soaker'
,
'de'
,
'Shallow Swordfish-de
.srt'
,
'edxval/tests/data/The_Arrow.srt'
)
)
@unpack
def
test_get_video_transcript_data
(
self
,
language_code
,
expected_file_name
,
expected_transcript_path
):
def
test_get_video_transcript_data
(
self
,
video_id
,
language_code
,
expected_file_name
,
expected_transcript_path
):
"""
Verify that `get_video_transcript_data` api function works as expected.
"""
...
...
@@ -1804,75 +1793,17 @@ class TranscriptTest(TestCase):
'content'
:
File
(
open
(
expected_transcript_path
))
.
read
()
}
transcript
=
api
.
get_video_transcript_data
(
video_ids
=
[
'super-soaker'
,
'0987654321'
],
video_ids
=
[
video_id
,
'0987654321'
],
language_code
=
language_code
)
self
.
assertDictEqual
(
transcript
,
expected_transcript
)
@data
(
{
'video_id'
:
'super-soaker'
,
'result'
:
True
},
{
'video_id'
:
'super-soaker1'
,
'result'
:
False
},
)
@unpack
def
test_get_video_transcripts
(
self
,
video_id
,
result
):
"""
Verify that `get_video_transcripts` api function works as expected.
"""
transcripts
=
api
.
get_video_transcripts
(
video_id
)
if
result
:
self
.
assertEqual
(
len
(
transcripts
),
3
)
for
transcript
,
transcript_data
in
zip
(
transcripts
,
[
self
.
transcript_data2
,
self
.
transcript_data1
]):
transcript_data
[
'url'
]
=
transcript_data
.
pop
(
'name'
)
self
.
assertEqual
(
transcript
,
transcript_data
)
else
:
self
.
assertEqual
(
transcripts
,
[])
def
test_create_video_transcript
(
self
):
"""
Verify that `create_or_update_video_transcript` api function creates transcript if there is no already.
"""
transcript_data
=
dict
(
self
.
transcript_data1
)
transcript_data
[
'language_code'
]
=
'ur'
with
self
.
assertRaises
(
VideoTranscript
.
DoesNotExist
):
VideoTranscript
.
objects
.
get
(
video_id
=
transcript_data
[
'video_id'
],
language_code
=
transcript_data
[
'language_code'
]
)
transcript_url
=
api
.
create_or_update_video_transcript
(
video_id
=
transcript_data
[
'video_id'
],
language_code
=
transcript_data
[
'language_code'
],
metadata
=
dict
(
file_name
=
transcript_data
[
'name'
],
file_format
=
transcript_data
[
'file_format'
],
provider
=
transcript_data
[
'provider'
]
)
)
self
.
assertEqual
(
transcript_url
,
transcript_data
[
'name'
])
expected_transcript
=
api
.
get_video_transcript
(
video_id
=
transcript_data
[
'video_id'
],
language_code
=
transcript_data
[
'language_code'
]
)
transcript_data
[
'url'
]
=
transcript_data
.
pop
(
'name'
)
self
.
assertEqual
(
transcript_data
,
expected_transcript
)
@data
(
{
'language_code'
:
'ur'
,
'has_url'
:
True
},
{
'language_code'
:
'xyz'
,
'has_url'
:
False
},
)
@unpack
def
test_get_video_transcript_url
(
self
,
language_code
,
has_url
):
def
test_get_video_transcript_url
(
self
):
"""
Verify that `get_video_transcript_url` api function works as expected.
"""
transcript_url
=
api
.
get_video_transcript_url
(
self
.
video_id
,
language_code
)
if
has_url
:
self
.
assertEqual
(
self
.
transcript_url
,
transcript_url
)
else
:
self
.
assertIsNone
(
transcript_url
)
transcript_url
=
api
.
get_video_transcript_url
(
'super-soaker'
,
'fr'
)
self
.
assertEqual
(
transcript_url
,
self
.
v1_transcript2
.
url
())
@data
(
{
...
...
@@ -1895,12 +1826,13 @@ class TranscriptTest(TestCase):
"""
Verify that `create_or_update_video_transcript` api function updates existing transcript as expected.
"""
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_id
=
self
.
video_id
,
language_code
=
'ur'
)
edx_video_id
=
'super-soaker'
video_transcript
=
VideoTranscript
.
objects
.
get
(
video__edx_video_id
=
edx_video_id
,
language_code
=
'en'
)
self
.
assertIsNotNone
(
video_transcript
)
transcript_url
=
api
.
create_or_update_video_transcript
(
video_id
=
self
.
video_id
,
language_code
=
'
ur
'
,
video_id
=
edx_
video_id
,
language_code
=
'
en
'
,
metadata
=
dict
(
provider
=
provider
,
language_code
=
language_code
,
...
...
@@ -1912,10 +1844,10 @@ class TranscriptTest(TestCase):
# Now, Querying Video Transcript with previous/old language code leads to DoesNotExist
with
self
.
assertRaises
(
VideoTranscript
.
DoesNotExist
):
VideoTranscript
.
objects
.
get
(
video_
id
=
self
.
video_id
,
language_code
=
'ur
'
)
VideoTranscript
.
objects
.
get
(
video_
_edx_video_id
=
edx_video_id
,
language_code
=
'en
'
)
# Assert the updates to the transcript object
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_
id
=
self
.
video_id
,
language_code
=
language_code
)
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_
_edx_video_id
=
edx_
video_id
,
language_code
=
language_code
)
self
.
assertEqual
(
transcript_url
,
video_transcript
.
url
())
self
.
assertEqual
(
video_transcript
.
file_format
,
file_format
)
self
.
assertEqual
(
video_transcript
.
provider
,
provider
)
...
...
@@ -1931,12 +1863,14 @@ class TranscriptTest(TestCase):
@data
(
{
'video_id'
:
'super-soaker'
,
'file_format'
:
'123'
,
'provider'
:
TranscriptProviderType
.
CIELO24
,
'exception'
:
InvalidTranscriptFormat
,
'exception_message'
:
'123 transcript format is not supported'
,
},
{
'video_id'
:
'medium-soaker'
,
'file_format'
:
TranscriptFormat
.
SRT
,
'provider'
:
123
,
'exception'
:
InvalidTranscriptProvider
,
...
...
@@ -1944,12 +1878,12 @@ class TranscriptTest(TestCase):
},
)
@unpack
def
test_create_or_update_video_exceptions
(
self
,
file_format
,
provider
,
exception
,
exception_message
):
def
test_create_or_update_video_exceptions
(
self
,
video_id
,
file_format
,
provider
,
exception
,
exception_message
):
"""
Verify that `create_or_update_video_transcript` api function raise exceptions on invalid values.
"""
with
self
.
assertRaises
(
exception
)
as
transcript_exception
:
api
.
create_or_update_video_transcript
(
self
.
video_id
,
'ur'
,
metadata
=
{
api
.
create_or_update_video_transcript
(
video_id
,
'ur'
,
metadata
=
{
'provider'
:
provider
,
'file_format'
:
file_format
})
...
...
@@ -1960,21 +1894,22 @@ class TranscriptTest(TestCase):
"""
Test video transcript deletion works as expected.
"""
edx_video_id
=
'super-soaker'
# get an existing video transcript
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_
id
=
self
.
video_id
,
language_code
=
'ur
'
)
existing_transcript_url
=
video_transcript
.
transcript
.
name
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_
_edx_video_id
=
edx_video_id
,
language_code
=
'en
'
)
existing_transcript_url
=
video_transcript
.
url
()
# This will replace the transcript for an existing video and delete the existing transcript
new_transcript_url
=
api
.
create_or_update_video_transcript
(
video_id
=
self
.
video_id
,
language_code
=
'
ur
'
,
video_id
=
edx_
video_id
,
language_code
=
'
en
'
,
metadata
=
dict
(
provider
=
TranscriptProviderType
.
CIELO24
),
file_data
=
ContentFile
(
FILE_DATA
)
)
# Verify that new transcript is set to video
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_
id
=
self
.
video_id
,
language_code
=
'ur
'
)
self
.
assertEqual
(
video_transcript
.
transcript
.
name
,
new_transcript_url
)
video_transcript
=
VideoTranscript
.
objects
.
get
(
video_
_edx_video_id
=
edx_video_id
,
language_code
=
'en
'
)
self
.
assertEqual
(
video_transcript
.
url
()
,
new_transcript_url
)
# verify that new data is written correctly
with
open
(
video_transcript
.
transcript
.
name
)
as
saved_transcript
:
...
...
@@ -1990,28 +1925,30 @@ class TranscriptTest(TestCase):
"""
Verify that `get_available_transcript_languages` works as expected.
"""
dupe_lang_video_id
=
'duplicate_lang_video'
VideoTranscript
.
objects
.
create
(
**
dict
(
constants
.
VIDEO_TRANSCRIPT_CIELO24
,
video_id
=
dupe_lang_video_id
))
# `super-soaker` has got 'en' and 'de' transcripts
# `self.video_id` has got 'ur' transcript
# `duplicate_lang_video` has got 'en' transcript
# `super-soaker` has got 'en' and 'fr' transcripts
# `non_existent_video_id` that does not have transcript
video_ids
=
[
'super-soaker'
,
self
.
video_id
,
dupe_lang_video_id
,
'non_existent_video_id'
]
video_ids
=
[
'super-soaker'
,
'non_existent_video_id'
]
transcript_languages
=
api
.
get_available_transcript_languages
(
video_ids
=
video_ids
)
self
.
assertItemsEqual
(
transcript_languages
,
[
'
de'
,
'en'
,
'ur
'
,
'fr'
])
self
.
assertItemsEqual
(
transcript_languages
,
[
'
en
'
,
'fr'
])
def
test_delete_video_transcript
(
self
):
"""
Verify that `delete_video_transcript` works as expected.
"""
query_filter
=
{
'video_id'
:
'super-soaker'
,
'video_
_edx_video_
id'
:
'super-soaker'
,
'language_code'
:
'fr'
}
self
.
assertEqual
(
VideoTranscript
.
objects
.
filter
(
**
query_filter
)
.
count
(),
1
)
api
.
delete_video_transcript
(
**
query_filter
)
self
.
assertFalse
(
os
.
path
.
exists
(
self
.
transcript_file
))
# current transcript path
transcript_path
=
self
.
v1_transcript2
.
transcript
.
name
api
.
delete_video_transcript
(
video_id
=
query_filter
[
'video__edx_video_id'
],
language_code
=
query_filter
[
'language_code'
]
)
# assert that the transcript does not exist on the path anymore.
self
.
assertFalse
(
os
.
path
.
exists
(
transcript_path
))
self
.
assertEqual
(
VideoTranscript
.
objects
.
filter
(
**
query_filter
)
.
count
(),
0
)
...
...
edxval/tests/test_views.py
View file @
727e643d
...
...
@@ -3,7 +3,6 @@
Tests for Video Abstraction Layer views
"""
import
json
import
unittest
from
ddt
import
data
,
ddt
,
unpack
from
django.core.urlresolvers
import
reverse
...
...
@@ -821,13 +820,18 @@ class VideoTranscriptViewTest(APIAuthTestCase):
serialized_data
=
TranscriptSerializer
(
VideoTranscript
.
objects
.
first
())
.
data
post_transcript_data
[
'url'
]
=
post_transcript_data
.
pop
(
'name'
)
self
.
assertEqual
(
serialized_data
,
post_transcript_data
)
self
.
assert
Dict
Equal
(
serialized_data
,
post_transcript_data
)
def
test_update_existing_transcript
(
self
):
"""
Tests updating existing transcript works as expected.
"""
VideoTranscript
.
objects
.
create
(
**
self
.
transcript_data
)
VideoTranscript
.
objects
.
create
(
video
=
self
.
video
,
language_code
=
self
.
transcript_data
[
'language_code'
],
file_format
=
self
.
transcript_data
[
'file_format'
],
provider
=
self
.
transcript_data
[
'provider'
],
)
post_transcript_data
=
dict
(
self
.
transcript_data
)
post_transcript_data
[
'name'
]
=
post_transcript_data
.
pop
(
'transcript'
)
...
...
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