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
025ad8df
Commit
025ad8df
authored
Aug 21, 2014
by
Dave St.Germain
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #11 from edx/dcs/subtitle-support
Added subtitle model and API
parents
df4728fc
7ab8ff9b
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
273 additions
and
36 deletions
+273
-36
edxval/admin.py
+2
-2
edxval/api.py
+6
-1
edxval/models.py
+36
-1
edxval/serializers.py
+35
-1
edxval/tests/constants.py
+40
-8
edxval/tests/test_api.py
+3
-3
edxval/tests/test_serializers.py
+6
-6
edxval/tests/test_views.py
+93
-11
edxval/urls.py
+11
-1
edxval/views.py
+41
-2
No files found.
edxval/admin.py
View file @
025ad8df
...
@@ -3,9 +3,9 @@ Admin file for django app edxval.
...
@@ -3,9 +3,9 @@ Admin file for django app edxval.
"""
"""
from
django.contrib
import
admin
from
django.contrib
import
admin
from
.models
import
Video
,
Profile
,
EncodedVideo
from
.models
import
Video
,
Profile
,
EncodedVideo
,
Subtitle
admin
.
site
.
register
(
Video
)
admin
.
site
.
register
(
Video
)
admin
.
site
.
register
(
Profile
)
admin
.
site
.
register
(
Profile
)
admin
.
site
.
register
(
EncodedVideo
)
admin
.
site
.
register
(
EncodedVideo
)
admin
.
site
.
register
(
Subtitle
)
edxval/api.py
View file @
025ad8df
...
@@ -139,6 +139,11 @@ def get_video_info(edx_video_id, location=None): # pylint: disable=W0613
...
@@ -139,6 +139,11 @@ def get_video_info(edx_video_id, location=None): # pylint: disable=W0613
extension: 3 letter extension of video
extension: 3 letter extension of video
width: horizontal pixel resolution
width: horizontal pixel resolution
height: vertical pixel resolution
height: vertical pixel resolution
subtitles: a list of Subtitle dicts
fmt: file format (SRT or SJSON)
language: language code
content_url: url of file
url: api url to subtitle
}
}
Raises:
Raises:
...
@@ -174,4 +179,4 @@ def get_video_info(edx_video_id, location=None): # pylint: disable=W0613
...
@@ -174,4 +179,4 @@ def get_video_info(edx_video_id, location=None): # pylint: disable=W0613
error_message
=
u"Could not get edx_video_id: {0}"
.
format
(
edx_video_id
)
error_message
=
u"Could not get edx_video_id: {0}"
.
format
(
edx_video_id
)
logger
.
exception
(
error_message
)
logger
.
exception
(
error_message
)
raise
ValInternalError
(
error_message
)
raise
ValInternalError
(
error_message
)
return
result
.
data
# pylint: disable=E1101
return
result
.
data
# pylint: disable=E1101
edxval/models.py
View file @
025ad8df
...
@@ -29,6 +29,7 @@ invalid profile_name will be returned.
...
@@ -29,6 +29,7 @@ invalid profile_name will be returned.
from
django.db
import
models
from
django.db
import
models
from
django.core.validators
import
MinValueValidator
,
RegexValidator
from
django.core.validators
import
MinValueValidator
,
RegexValidator
from
django.core.urlresolvers
import
reverse
url_regex
=
r'^[a-zA-Z0-9\-]*$'
url_regex
=
r'^[a-zA-Z0-9\-]*$'
...
@@ -78,6 +79,9 @@ class Video(models.Model):
...
@@ -78,6 +79,9 @@ class Video(models.Model):
client_video_id
=
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)
client_video_id
=
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)
duration
=
models
.
FloatField
(
validators
=
[
MinValueValidator
(
0
)])
duration
=
models
.
FloatField
(
validators
=
[
MinValueValidator
(
0
)])
def
__str__
(
self
):
return
self
.
edx_video_id
class
CourseVideos
(
models
.
Model
):
class
CourseVideos
(
models
.
Model
):
"""
"""
...
@@ -89,7 +93,7 @@ class CourseVideos(models.Model):
...
@@ -89,7 +93,7 @@ class CourseVideos(models.Model):
course_id
=
models
.
CharField
(
max_length
=
255
)
course_id
=
models
.
CharField
(
max_length
=
255
)
video
=
models
.
ForeignKey
(
Video
)
video
=
models
.
ForeignKey
(
Video
)
class
Meta
:
class
Meta
:
# pylint: disable=C1001
"""
"""
course_id is listed first in this composite index
course_id is listed first in this composite index
"""
"""
...
@@ -108,3 +112,34 @@ class EncodedVideo(models.Model):
...
@@ -108,3 +112,34 @@ class EncodedVideo(models.Model):
profile
=
models
.
ForeignKey
(
Profile
,
related_name
=
"+"
)
profile
=
models
.
ForeignKey
(
Profile
,
related_name
=
"+"
)
video
=
models
.
ForeignKey
(
Video
,
related_name
=
"encoded_videos"
)
video
=
models
.
ForeignKey
(
Video
,
related_name
=
"encoded_videos"
)
SUBTITLE_FORMATS
=
(
(
'srt'
,
'SubRip'
),
(
'sjson'
,
'SRT JSON'
)
)
class
Subtitle
(
models
.
Model
):
"""
Subtitle for video
"""
created
=
models
.
DateTimeField
(
auto_now_add
=
True
)
modified
=
models
.
DateTimeField
(
auto_now
=
True
)
video
=
models
.
ForeignKey
(
Video
,
related_name
=
"subtitles"
)
fmt
=
models
.
CharField
(
max_length
=
20
,
db_index
=
True
,
choices
=
SUBTITLE_FORMATS
)
language
=
models
.
CharField
(
max_length
=
8
,
db_index
=
True
)
content
=
models
.
TextField
(
default
=
''
)
def
__str__
(
self
):
return
'
%
s Subtitle for
%
s'
%
(
self
.
language
,
self
.
video
)
def
get_absolute_url
(
self
):
return
reverse
(
'subtitle-content'
,
args
=
[
self
.
video
.
edx_video_id
,
self
.
language
])
@property
def
content_type
(
self
):
if
self
.
fmt
==
'sjson'
:
return
'application/json'
else
:
return
'text/plain'
edxval/serializers.py
View file @
025ad8df
...
@@ -7,7 +7,7 @@ EncodedVideoSerializer which uses the profile_name as it's profile field.
...
@@ -7,7 +7,7 @@ EncodedVideoSerializer which uses the profile_name as it's profile field.
from
rest_framework
import
serializers
from
rest_framework
import
serializers
from
django.core.exceptions
import
ValidationError
from
django.core.exceptions
import
ValidationError
from
edxval.models
import
Profile
,
Video
,
EncodedVideo
from
edxval.models
import
Profile
,
Video
,
EncodedVideo
,
Subtitle
class
ProfileSerializer
(
serializers
.
ModelSerializer
):
class
ProfileSerializer
(
serializers
.
ModelSerializer
):
...
@@ -51,6 +51,39 @@ class EncodedVideoSerializer(serializers.ModelSerializer):
...
@@ -51,6 +51,39 @@ class EncodedVideoSerializer(serializers.ModelSerializer):
return
data
.
get
(
'profile'
,
None
)
return
data
.
get
(
'profile'
,
None
)
class
SubtitleSerializer
(
serializers
.
ModelSerializer
):
"""
Serializer for Subtitle objects
"""
content_url
=
serializers
.
CharField
(
source
=
'get_absolute_url'
,
read_only
=
True
)
content
=
serializers
.
CharField
(
write_only
=
True
)
def
validate_content
(
self
,
attrs
,
source
):
"""
Validate that the subtitle is in the correct format
"""
value
=
attrs
[
source
]
if
attrs
.
get
(
'fmt'
)
==
'sjson'
:
import
json
try
:
loaded
=
json
.
loads
(
value
)
except
ValueError
:
raise
serializers
.
ValidationError
(
"Not in JSON format"
)
else
:
attrs
[
source
]
=
json
.
dumps
(
loaded
)
return
attrs
class
Meta
:
# pylint: disable=C1001, C0111
model
=
Subtitle
lookup_field
=
"id"
fields
=
(
"fmt"
,
"language"
,
"content_url"
,
"content"
,
)
class
VideoSerializer
(
serializers
.
HyperlinkedModelSerializer
):
class
VideoSerializer
(
serializers
.
HyperlinkedModelSerializer
):
"""
"""
Serializer for Video object
Serializer for Video object
...
@@ -58,6 +91,7 @@ class VideoSerializer(serializers.HyperlinkedModelSerializer):
...
@@ -58,6 +91,7 @@ class VideoSerializer(serializers.HyperlinkedModelSerializer):
encoded_videos takes a list of dicts EncodedVideo data.
encoded_videos takes a list of dicts EncodedVideo data.
"""
"""
encoded_videos
=
EncodedVideoSerializer
(
many
=
True
,
allow_add_remove
=
True
)
encoded_videos
=
EncodedVideoSerializer
(
many
=
True
,
allow_add_remove
=
True
)
subtitles
=
SubtitleSerializer
(
many
=
True
,
allow_add_remove
=
True
,
required
=
False
)
class
Meta
:
# pylint: disable=C0111
class
Meta
:
# pylint: disable=C0111
model
=
Video
model
=
Video
...
...
edxval/tests/constants.py
View file @
025ad8df
...
@@ -39,7 +39,8 @@ VIDEO_DICT_NEGATIVE_DURATION = dict(
...
@@ -39,7 +39,8 @@ VIDEO_DICT_NEGATIVE_DURATION = dict(
client_video_id
=
"Thunder Cats S01E01"
,
client_video_id
=
"Thunder Cats S01E01"
,
duration
=-
111
,
duration
=-
111
,
edx_video_id
=
"thisis12char-thisis7"
,
edx_video_id
=
"thisis12char-thisis7"
,
encoded_videos
=
[]
encoded_videos
=
[],
subtitles
=
[]
)
)
VIDEO_DICT_BEE_INVALID
=
dict
(
VIDEO_DICT_BEE_INVALID
=
dict
(
client_video_id
=
"Barking Bee"
,
client_video_id
=
"Barking Bee"
,
...
@@ -50,7 +51,8 @@ VIDEO_DICT_INVALID_ID = dict(
...
@@ -50,7 +51,8 @@ VIDEO_DICT_INVALID_ID = dict(
client_video_id
=
"SuperSloth"
,
client_video_id
=
"SuperSloth"
,
duration
=
42
,
duration
=
42
,
edx_video_id
=
"sloppy/sloth!!"
,
edx_video_id
=
"sloppy/sloth!!"
,
encoded_videos
=
[]
encoded_videos
=
[],
subtitles
=
[]
)
)
ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE
=
dict
(
ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE
=
dict
(
url
=
"http://www.meowmix.com"
,
url
=
"http://www.meowmix.com"
,
...
@@ -69,13 +71,15 @@ VIDEO_DICT_NON_LATIN_TITLE = dict(
...
@@ -69,13 +71,15 @@ VIDEO_DICT_NON_LATIN_TITLE = dict(
client_video_id
=
u"배고픈 햄스터"
,
client_video_id
=
u"배고픈 햄스터"
,
duration
=
42
,
duration
=
42
,
edx_video_id
=
"ID"
,
edx_video_id
=
"ID"
,
encoded_videos
=
[]
encoded_videos
=
[],
subtitles
=
[]
)
)
VIDEO_DICT_NON_LATIN_ID
=
dict
(
VIDEO_DICT_NON_LATIN_ID
=
dict
(
client_video_id
=
"Hungry Hamster"
,
client_video_id
=
"Hungry Hamster"
,
duration
=
42
,
duration
=
42
,
edx_video_id
=
"밥줘"
,
edx_video_id
=
"밥줘"
,
encoded_videos
=
[]
encoded_videos
=
[],
subtitles
=
[]
)
)
PROFILE_DICT_NON_LATIN
=
dict
(
PROFILE_DICT_NON_LATIN
=
dict
(
profile_name
=
u"배고파"
,
profile_name
=
u"배고파"
,
...
@@ -112,6 +116,19 @@ PROFILE_DICT_MANY_INVALID = dict(
...
@@ -112,6 +116,19 @@ PROFILE_DICT_MANY_INVALID = dict(
height
=
"lol"
,
height
=
"lol"
,
)
)
"""
"""
Subtitles
"""
SUBTITLE_DICT_SRT
=
dict
(
fmt
=
"srt"
,
language
=
"en"
,
content
=
"0:0:0
\n
hello"
)
SUBTITLE_DICT_SJSON
=
dict
(
fmt
=
"sjson"
,
language
=
"fr"
,
content
=
'{"start": "00:00:00"}'
)
"""
Fish
Fish
"""
"""
VIDEO_DICT_FISH
=
dict
(
VIDEO_DICT_FISH
=
dict
(
...
@@ -159,6 +176,7 @@ COMPLETE_SET_FISH = dict(
...
@@ -159,6 +176,7 @@ COMPLETE_SET_FISH = dict(
ENCODED_VIDEO_DICT_FISH_MOBILE
,
ENCODED_VIDEO_DICT_FISH_MOBILE
,
ENCODED_VIDEO_DICT_FISH_DESKTOP
ENCODED_VIDEO_DICT_FISH_DESKTOP
],
],
subtitles
=
[
SUBTITLE_DICT_SRT
,
SUBTITLE_DICT_SJSON
],
**
VIDEO_DICT_FISH
**
VIDEO_DICT_FISH
)
)
COMPLETE_SET_TWO_MOBILE_FISH
=
dict
(
COMPLETE_SET_TWO_MOBILE_FISH
=
dict
(
...
@@ -166,6 +184,7 @@ COMPLETE_SET_TWO_MOBILE_FISH = dict(
...
@@ -166,6 +184,7 @@ COMPLETE_SET_TWO_MOBILE_FISH = dict(
ENCODED_VIDEO_DICT_FISH_MOBILE
,
ENCODED_VIDEO_DICT_FISH_MOBILE
,
ENCODED_VIDEO_DICT_FISH_MOBILE
ENCODED_VIDEO_DICT_FISH_MOBILE
],
],
subtitles
=
[
SUBTITLE_DICT_SRT
],
**
VIDEO_DICT_FISH
**
VIDEO_DICT_FISH
)
)
COMPLETE_SET_UPDATE_FISH
=
dict
(
COMPLETE_SET_UPDATE_FISH
=
dict
(
...
@@ -173,6 +192,7 @@ COMPLETE_SET_UPDATE_FISH = dict(
...
@@ -173,6 +192,7 @@ COMPLETE_SET_UPDATE_FISH = dict(
ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE
,
ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE
,
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
],
],
subtitles
=
[
SUBTITLE_DICT_SRT
],
**
VIDEO_DICT_FISH
**
VIDEO_DICT_FISH
)
)
COMPLETE_SET_DIFFERENT_ID_UPDATE_FISH
=
dict
(
COMPLETE_SET_DIFFERENT_ID_UPDATE_FISH
=
dict
(
...
@@ -180,6 +200,7 @@ COMPLETE_SET_DIFFERENT_ID_UPDATE_FISH = dict(
...
@@ -180,6 +200,7 @@ COMPLETE_SET_DIFFERENT_ID_UPDATE_FISH = dict(
ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE
,
ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE
,
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
],
],
subtitles
=
[
SUBTITLE_DICT_SRT
,
SUBTITLE_DICT_SJSON
],
**
VIDEO_DICT_DIFFERENT_ID_FISH
**
VIDEO_DICT_DIFFERENT_ID_FISH
)
)
COMPLETE_SET_FIRST_HALF_UPDATE_FISH
=
dict
(
COMPLETE_SET_FIRST_HALF_UPDATE_FISH
=
dict
(
...
@@ -187,12 +208,14 @@ COMPLETE_SET_FIRST_HALF_UPDATE_FISH = dict(
...
@@ -187,12 +208,14 @@ COMPLETE_SET_FIRST_HALF_UPDATE_FISH = dict(
ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE
,
ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE
,
ENCODED_VIDEO_DICT_FISH_DESKTOP
ENCODED_VIDEO_DICT_FISH_DESKTOP
],
],
subtitles
=
[
SUBTITLE_DICT_SRT
,
SUBTITLE_DICT_SJSON
],
**
VIDEO_DICT_FISH
**
VIDEO_DICT_FISH
)
)
COMPLETE_SET_UPDATE_ONLY_DESKTOP_FISH
=
dict
(
COMPLETE_SET_UPDATE_ONLY_DESKTOP_FISH
=
dict
(
encoded_videos
=
[
encoded_videos
=
[
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
],
],
subtitles
=
[
SUBTITLE_DICT_SRT
],
**
VIDEO_DICT_FISH
**
VIDEO_DICT_FISH
)
)
COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH
=
dict
(
COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH
=
dict
(
...
@@ -200,6 +223,7 @@ COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH = dict(
...
@@ -200,6 +223,7 @@ COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH = dict(
ENCODED_VIDEO_DICT_FISH_MOBILE
,
ENCODED_VIDEO_DICT_FISH_MOBILE
,
ENCODED_VIDEO_DICT_FISH_INVALID_PROFILE
ENCODED_VIDEO_DICT_FISH_INVALID_PROFILE
],
],
subtitles
=
[
SUBTITLE_DICT_SRT
],
**
VIDEO_DICT_FISH
**
VIDEO_DICT_FISH
)
)
COMPLETE_SET_INVALID_VIDEO_FISH
=
dict
(
COMPLETE_SET_INVALID_VIDEO_FISH
=
dict
(
...
@@ -209,7 +233,8 @@ COMPLETE_SET_INVALID_VIDEO_FISH = dict(
...
@@ -209,7 +233,8 @@ COMPLETE_SET_INVALID_VIDEO_FISH = dict(
encoded_videos
=
[
encoded_videos
=
[
ENCODED_VIDEO_DICT_FISH_MOBILE
,
ENCODED_VIDEO_DICT_FISH_MOBILE
,
ENCODED_VIDEO_DICT_FISH_DESKTOP
ENCODED_VIDEO_DICT_FISH_DESKTOP
]
],
subtitles
=
[
SUBTITLE_DICT_SRT
]
)
)
COMPLETE_SETS_ALL_INVALID
=
[
COMPLETE_SETS_ALL_INVALID
=
[
...
@@ -240,12 +265,14 @@ COMPLETE_SET_STAR = dict(
...
@@ -240,12 +265,14 @@ COMPLETE_SET_STAR = dict(
encoded_videos
=
[
encoded_videos
=
[
ENCODED_VIDEO_DICT_STAR
ENCODED_VIDEO_DICT_STAR
],
],
subtitles
=
[
SUBTITLE_DICT_SRT
],
**
VIDEO_DICT_STAR
**
VIDEO_DICT_STAR
)
)
COMPLETE_SET_UPDATE_STAR
=
dict
(
COMPLETE_SET_UPDATE_STAR
=
dict
(
encoded_videos
=
[
encoded_videos
=
[
ENCODED_VIDEO_UPDATE_DICT_STAR
ENCODED_VIDEO_UPDATE_DICT_STAR
],
],
subtitles
=
[
SUBTITLE_DICT_SRT
],
**
VIDEO_DICT_STAR
**
VIDEO_DICT_STAR
)
)
COMPLETE_SET_NOT_A_LIST
=
dict
(
COMPLETE_SET_NOT_A_LIST
=
dict
(
...
@@ -255,6 +282,7 @@ COMPLETE_SET_NOT_A_LIST = dict(
...
@@ -255,6 +282,7 @@ COMPLETE_SET_NOT_A_LIST = dict(
bitrate
=
42
,
bitrate
=
42
,
profile
=
1
profile
=
1
),
),
subtitles
=
[
SUBTITLE_DICT_SRT
],
**
VIDEO_DICT_STAR
**
VIDEO_DICT_STAR
)
)
COMPLETE_SET_EXTRA_VIDEO_FIELD
=
dict
(
COMPLETE_SET_EXTRA_VIDEO_FIELD
=
dict
(
...
@@ -267,6 +295,7 @@ COMPLETE_SET_EXTRA_VIDEO_FIELD = dict(
...
@@ -267,6 +295,7 @@ COMPLETE_SET_EXTRA_VIDEO_FIELD = dict(
video
=
"This should be overridden by parent video field"
video
=
"This should be overridden by parent video field"
)
)
],
],
subtitles
=
[
SUBTITLE_DICT_SRT
],
**
VIDEO_DICT_STAR
**
VIDEO_DICT_STAR
)
)
"""
"""
...
@@ -276,17 +305,20 @@ VIDEO_DICT_ZEBRA = dict(
...
@@ -276,17 +305,20 @@ VIDEO_DICT_ZEBRA = dict(
client_video_id
=
"Zesty Zebra"
,
client_video_id
=
"Zesty Zebra"
,
duration
=
111.00
,
duration
=
111.00
,
edx_video_id
=
"zestttt"
,
edx_video_id
=
"zestttt"
,
encoded_videos
=
[]
encoded_videos
=
[],
subtitles
=
[]
)
)
VIDEO_DICT_ANIMAL
=
dict
(
VIDEO_DICT_ANIMAL
=
dict
(
client_video_id
=
"Average Animal"
,
client_video_id
=
"Average Animal"
,
duration
=
111.00
,
duration
=
111.00
,
edx_video_id
=
"mediocrity"
,
edx_video_id
=
"mediocrity"
,
encoded_videos
=
[]
encoded_videos
=
[],
subtitles
=
[]
)
)
VIDEO_DICT_UPDATE_ANIMAL
=
dict
(
VIDEO_DICT_UPDATE_ANIMAL
=
dict
(
client_video_id
=
"Above Average Animal"
,
client_video_id
=
"Above Average Animal"
,
duration
=
999.00
,
duration
=
999.00
,
edx_video_id
=
"mediocrity"
,
edx_video_id
=
"mediocrity"
,
encoded_videos
=
[]
encoded_videos
=
[],
subtitles
=
[]
)
)
edxval/tests/test_api.py
View file @
025ad8df
...
@@ -225,7 +225,7 @@ class GetVideoInfoTestWithHttpCalls(APITestCase):
...
@@ -225,7 +225,7 @@ class GetVideoInfoTestWithHttpCalls(APITestCase):
"""
"""
Tests number of queries for a Video/EncodedVideo(1) pair
Tests number of queries for a Video/EncodedVideo(1) pair
"""
"""
with
self
.
assertNumQueries
(
4
):
with
self
.
assertNumQueries
(
7
):
api
.
get_video_info
(
constants
.
COMPLETE_SET_FISH
.
get
(
"edx_video_id"
))
api
.
get_video_info
(
constants
.
COMPLETE_SET_FISH
.
get
(
"edx_video_id"
))
def
test_get_info_queries_for_one_encoded_video
(
self
):
def
test_get_info_queries_for_one_encoded_video
(
self
):
...
@@ -237,7 +237,7 @@ class GetVideoInfoTestWithHttpCalls(APITestCase):
...
@@ -237,7 +237,7 @@ class GetVideoInfoTestWithHttpCalls(APITestCase):
url
,
constants
.
COMPLETE_SET_STAR
,
format
=
'json'
url
,
constants
.
COMPLETE_SET_STAR
,
format
=
'json'
)
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
with
self
.
assertNumQueries
(
3
):
with
self
.
assertNumQueries
(
5
):
api
.
get_video_info
(
constants
.
COMPLETE_SET_STAR
.
get
(
"edx_video_id"
))
api
.
get_video_info
(
constants
.
COMPLETE_SET_STAR
.
get
(
"edx_video_id"
))
def
test_get_info_queries_for_only_video
(
self
):
def
test_get_info_queries_for_only_video
(
self
):
...
@@ -249,6 +249,6 @@ class GetVideoInfoTestWithHttpCalls(APITestCase):
...
@@ -249,6 +249,6 @@ class GetVideoInfoTestWithHttpCalls(APITestCase):
url
,
constants
.
VIDEO_DICT_ZEBRA
,
format
=
'json'
url
,
constants
.
VIDEO_DICT_ZEBRA
,
format
=
'json'
)
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
with
self
.
assertNumQueries
(
2
):
with
self
.
assertNumQueries
(
3
):
api
.
get_video_info
(
constants
.
VIDEO_DICT_ZEBRA
.
get
(
"edx_video_id"
))
api
.
get_video_info
(
constants
.
VIDEO_DICT_ZEBRA
.
get
(
"edx_video_id"
))
edxval/tests/test_serializers.py
View file @
025ad8df
...
@@ -34,11 +34,11 @@ class SerializerTests(TestCase):
...
@@ -34,11 +34,11 @@ class SerializerTests(TestCase):
Tests negative inputs for bitrate, file_size in EncodedVideo
Tests negative inputs for bitrate, file_size in EncodedVideo
"""
"""
errors
=
EncodedVideoSerializer
(
# pylint: disable=E1101
errors
=
EncodedVideoSerializer
(
# pylint: disable=E1101
data
=
constants
.
ENCODED_VIDEO_DICT_NEGATIVE_BITRATE
)
.
errors
data
=
constants
.
ENCODED_VIDEO_DICT_NEGATIVE_BITRATE
)
.
errors
self
.
assertEqual
(
errors
.
get
(
'bitrate'
)[
0
],
self
.
assertEqual
(
errors
.
get
(
'bitrate'
)[
0
],
u"Ensure this value is greater than or equal to 0."
)
u"Ensure this value is greater than or equal to 0."
)
errors
=
EncodedVideoSerializer
(
# pylint: disable=E1101
errors
=
EncodedVideoSerializer
(
# pylint: disable=E1101
data
=
constants
.
ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE
)
.
errors
data
=
constants
.
ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE
)
.
errors
self
.
assertEqual
(
errors
.
get
(
'file_size'
)[
0
],
self
.
assertEqual
(
errors
.
get
(
'file_size'
)[
0
],
u"Ensure this value is greater than or equal to 0."
)
u"Ensure this value is greater than or equal to 0."
)
...
@@ -49,7 +49,7 @@ class SerializerTests(TestCase):
...
@@ -49,7 +49,7 @@ class SerializerTests(TestCase):
Tests negative inputs for duration in model Video
Tests negative inputs for duration in model Video
"""
"""
errors
=
VideoSerializer
(
# pylint: disable=E1101
errors
=
VideoSerializer
(
# pylint: disable=E1101
data
=
constants
.
VIDEO_DICT_NEGATIVE_DURATION
)
.
errors
data
=
constants
.
VIDEO_DICT_NEGATIVE_DURATION
)
.
errors
self
.
assertEqual
(
errors
.
get
(
'duration'
)[
0
],
self
.
assertEqual
(
errors
.
get
(
'duration'
)[
0
],
u"Ensure this value is greater than or equal to 0."
)
u"Ensure this value is greater than or equal to 0."
)
...
@@ -58,7 +58,7 @@ class SerializerTests(TestCase):
...
@@ -58,7 +58,7 @@ class SerializerTests(TestCase):
"""
"""
Tests if the serializers can accept non-latin chars
Tests if the serializers can accept non-latin chars
"""
"""
#TODO not the best test. Need to understand what result we want
#
TODO not the best test. Need to understand what result we want
self
.
assertIsInstance
(
self
.
assertIsInstance
(
ProfileSerializer
(
Profile
.
objects
.
get
(
profile_name
=
"배고파"
)),
ProfileSerializer
(
Profile
.
objects
.
get
(
profile_name
=
"배고파"
)),
ProfileSerializer
ProfileSerializer
...
@@ -68,7 +68,7 @@ class SerializerTests(TestCase):
...
@@ -68,7 +68,7 @@ class SerializerTests(TestCase):
"""
"""
Test the Video model regex validation for edx_video_id field
Test the Video model regex validation for edx_video_id field
"""
"""
error
=
VideoSerializer
(
data
=
constants
.
VIDEO_DICT_INVALID_ID
)
.
errors
# pylint: disable=E1101
error
=
VideoSerializer
(
data
=
constants
.
VIDEO_DICT_INVALID_ID
)
.
errors
# pylint: disable=E1101
message
=
error
.
get
(
"edx_video_id"
)[
0
]
message
=
error
.
get
(
"edx_video_id"
)[
0
]
self
.
assertEqual
(
self
.
assertEqual
(
message
,
message
,
...
@@ -89,7 +89,7 @@ class SerializerTests(TestCase):
...
@@ -89,7 +89,7 @@ class SerializerTests(TestCase):
profile
=
Profile
.
objects
.
get
(
profile_name
=
"mobile"
),
profile
=
Profile
.
objects
.
get
(
profile_name
=
"mobile"
),
**
constants
.
ENCODED_VIDEO_DICT_MOBILE
**
constants
.
ENCODED_VIDEO_DICT_MOBILE
)
)
result
=
VideoSerializer
(
video
)
.
data
# pylint: disable=E1101
result
=
VideoSerializer
(
video
)
.
data
# pylint: disable=E1101
# Check for 2 EncodedVideo entries
# Check for 2 EncodedVideo entries
self
.
assertEqual
(
len
(
result
.
get
(
"encoded_videos"
)),
2
)
self
.
assertEqual
(
len
(
result
.
get
(
"encoded_videos"
)),
2
)
# Check for original Video data
# Check for original Video data
...
...
edxval/tests/test_views.py
View file @
025ad8df
# pylint: disable=E1103, W0106
# pylint: disable=E1103, W0106
"""
"""
Tests for Video Abstraction Layer views
Tests for Video Abstraction Layer views
"""
"""
...
@@ -22,7 +22,7 @@ class VideoDetail(APITestCase):
...
@@ -22,7 +22,7 @@ class VideoDetail(APITestCase):
Profile
.
objects
.
create
(
**
constants
.
PROFILE_DICT_MOBILE
)
Profile
.
objects
.
create
(
**
constants
.
PROFILE_DICT_MOBILE
)
Profile
.
objects
.
create
(
**
constants
.
PROFILE_DICT_DESKTOP
)
Profile
.
objects
.
create
(
**
constants
.
PROFILE_DICT_DESKTOP
)
#Tests for successful PUT requests.
#
Tests for successful PUT requests.
def
test_update_video
(
self
):
def
test_update_video
(
self
):
"""
"""
...
@@ -91,7 +91,7 @@ class VideoDetail(APITestCase):
...
@@ -91,7 +91,7 @@ class VideoDetail(APITestCase):
'video-detail'
,
'video-detail'
,
kwargs
=
{
"edx_video_id"
:
constants
.
COMPLETE_SET_FISH
.
get
(
"edx_video_id"
)}
kwargs
=
{
"edx_video_id"
:
constants
.
COMPLETE_SET_FISH
.
get
(
"edx_video_id"
)}
)
)
response
=
self
.
client
.
patch
(
# pylint: disable=E1101
response
=
self
.
client
.
patch
(
# pylint: disable=E1101
path
=
url
,
path
=
url
,
data
=
constants
.
COMPLETE_SET_UPDATE_FISH
,
data
=
constants
.
COMPLETE_SET_UPDATE_FISH
,
format
=
'json'
format
=
'json'
...
@@ -140,6 +140,7 @@ class VideoDetail(APITestCase):
...
@@ -140,6 +140,7 @@ class VideoDetail(APITestCase):
Tests PUTting one of two EncodedVideo(s) and then a single EncodedVideo PUT back.
Tests PUTting one of two EncodedVideo(s) and then a single EncodedVideo PUT back.
"""
"""
url
=
reverse
(
'video-list'
)
url
=
reverse
(
'video-list'
)
response
=
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
)
response
=
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
url
=
reverse
(
url
=
reverse
(
...
@@ -216,7 +217,7 @@ class VideoDetail(APITestCase):
...
@@ -216,7 +217,7 @@ class VideoDetail(APITestCase):
constants
.
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
.
get
(
"url"
)
constants
.
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
.
get
(
"url"
)
)
)
#Tests for bad PUT requests.
#
Tests for bad PUT requests.
def
test_update_an_invalid_encoded_videos
(
self
):
def
test_update_an_invalid_encoded_videos
(
self
):
"""
"""
...
@@ -304,7 +305,7 @@ class VideoListTest(APITestCase):
...
@@ -304,7 +305,7 @@ class VideoListTest(APITestCase):
def
test_complete_set_two_encoded_video_post
(
self
):
def
test_complete_set_two_encoded_video_post
(
self
):
"""
"""
Tests POSTing Video and EncodedVideo pair
Tests POSTing Video and EncodedVideo pair
"""
#
pylint: disable=R0801
"""
#
pylint: disable=R0801
url
=
reverse
(
'video-list'
)
url
=
reverse
(
'video-list'
)
response
=
self
.
client
.
post
(
response
=
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
...
@@ -434,7 +435,7 @@ class VideoListTest(APITestCase):
...
@@ -434,7 +435,7 @@ class VideoListTest(APITestCase):
Tests number of queries for a Video with no Encoded Videos
Tests number of queries for a Video with no Encoded Videos
"""
"""
url
=
reverse
(
'video-list'
)
url
=
reverse
(
'video-list'
)
with
self
.
assertNumQueries
(
3
):
with
self
.
assertNumQueries
(
4
):
self
.
client
.
post
(
url
,
constants
.
VIDEO_DICT_ZEBRA
,
format
=
'json'
)
self
.
client
.
post
(
url
,
constants
.
VIDEO_DICT_ZEBRA
,
format
=
'json'
)
def
test_queries_for_two_encoded_video
(
self
):
def
test_queries_for_two_encoded_video
(
self
):
...
@@ -442,7 +443,7 @@ class VideoListTest(APITestCase):
...
@@ -442,7 +443,7 @@ class VideoListTest(APITestCase):
Tests number of queries for a Video/EncodedVideo(2) pair
Tests number of queries for a Video/EncodedVideo(2) pair
"""
"""
url
=
reverse
(
'video-list'
)
url
=
reverse
(
'video-list'
)
with
self
.
assertNumQueries
(
1
1
):
with
self
.
assertNumQueries
(
1
6
):
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
)
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
)
def
test_queries_for_single_encoded_videos
(
self
):
def
test_queries_for_single_encoded_videos
(
self
):
...
@@ -450,7 +451,7 @@ class VideoListTest(APITestCase):
...
@@ -450,7 +451,7 @@ class VideoListTest(APITestCase):
Tests number of queries for a Video/EncodedVideo(1) pair
Tests number of queries for a Video/EncodedVideo(1) pair
"""
"""
url
=
reverse
(
'video-list'
)
url
=
reverse
(
'video-list'
)
with
self
.
assertNumQueries
(
7
):
with
self
.
assertNumQueries
(
10
):
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_STAR
,
format
=
'json'
)
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_STAR
,
format
=
'json'
)
...
@@ -486,13 +487,94 @@ class VideoDetailTest(APITestCase):
...
@@ -486,13 +487,94 @@ class VideoDetailTest(APITestCase):
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
response
=
self
.
client
.
post
(
url
,
constants
.
VIDEO_DICT_ZEBRA
,
format
=
'json'
)
response
=
self
.
client
.
post
(
url
,
constants
.
VIDEO_DICT_ZEBRA
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
with
self
.
assertNumQueries
(
2
):
with
self
.
assertNumQueries
(
4
):
self
.
client
.
get
(
"/edxval/video/"
)
.
data
self
.
client
.
get
(
"/edxval/video/"
)
.
data
response
=
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
)
response
=
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
with
self
.
assertNumQueries
(
4
):
with
self
.
assertNumQueries
(
9
):
self
.
client
.
get
(
"/edxval/video/"
)
.
data
self
.
client
.
get
(
"/edxval/video/"
)
.
data
response
=
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_STAR
,
format
=
'json'
)
response
=
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_STAR
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
with
self
.
assertNumQueries
(
5
):
with
self
.
assertNumQueries
(
12
):
self
.
client
.
get
(
"/edxval/video/"
)
.
data
self
.
client
.
get
(
"/edxval/video/"
)
.
data
class
SubtitleDetailTest
(
APITestCase
):
"""
Tests for subtitle API
"""
def
setUp
(
self
):
Profile
.
objects
.
create
(
**
constants
.
PROFILE_DICT_MOBILE
)
Profile
.
objects
.
create
(
**
constants
.
PROFILE_DICT_DESKTOP
)
def
test_get_subtitle_content
(
self
):
"""
Get subtitle content
"""
url
=
reverse
(
'video-list'
)
response
=
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
video
=
self
.
client
.
get
(
"/edxval/video/"
)
.
data
self
.
assertEqual
(
len
(
video
),
1
)
self
.
assertEqual
(
len
(
video
[
0
]
.
get
(
"subtitles"
)),
2
)
st
=
video
[
0
][
'subtitles'
][
0
]
response
=
self
.
client
.
get
(
st
[
'content_url'
])
self
.
assertEqual
(
response
.
content
,
constants
.
SUBTITLE_DICT_SRT
[
'content'
])
self
.
assertEqual
(
response
[
'Content-Type'
],
'text/plain'
)
st
=
video
[
0
][
'subtitles'
][
1
]
response
=
self
.
client
.
get
(
st
[
'content_url'
])
self
.
assertEqual
(
response
.
content
,
constants
.
SUBTITLE_DICT_SJSON
[
'content'
])
self
.
assertEqual
(
response
[
'Content-Type'
],
'application/json'
)
def
test_update_subtitle
(
self
):
"""
Update an SRT subtitle
"""
url
=
reverse
(
'video-list'
)
response
=
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
video
=
response
.
data
st
=
video
[
'subtitles'
][
0
]
url
=
reverse
(
'subtitle-detail'
,
kwargs
=
{
'video__edx_video_id'
:
video
[
'edx_video_id'
],
'language'
:
st
[
'language'
]})
st
[
'content'
]
=
'testing 123'
response
=
self
.
client
.
put
(
url
,
st
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_200_OK
)
self
.
assertEqual
(
self
.
client
.
get
(
st
[
'content_url'
])
.
content
,
'testing 123'
)
def
test_update_json_subtitle
(
self
):
"""
Update a JSON subtitle
"""
url
=
reverse
(
'video-list'
)
response
=
self
.
client
.
post
(
url
,
constants
.
COMPLETE_SET_FISH
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
video
=
response
.
data
st
=
video
[
'subtitles'
][
1
]
url
=
reverse
(
'subtitle-detail'
,
kwargs
=
{
'video__edx_video_id'
:
video
[
'edx_video_id'
],
'language'
:
st
[
'language'
]})
st
[
'content'
]
=
'testing 123'
response
=
self
.
client
.
put
(
url
,
st
,
format
=
'json'
)
# not in json format
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
st
[
'content'
]
=
"""{"start": "00:00:00"
}"""
response
=
self
.
client
.
put
(
url
,
st
,
format
=
'json'
)
self
.
assertEqual
(
self
.
client
.
get
(
st
[
'content_url'
])
.
content
,
'{"start": "00:00:00"}'
)
edxval/urls.py
View file @
025ad8df
...
@@ -14,8 +14,18 @@ urlpatterns = patterns(
...
@@ -14,8 +14,18 @@ urlpatterns = patterns(
name
=
"video-list"
name
=
"video-list"
),
),
url
(
url
(
r'^edxval/video/(?P<edx_video_id>[-\w]+)'
,
r'^edxval/video/(?P<edx_video_id>[-\w]+)
$
'
,
views
.
VideoDetail
.
as_view
(),
views
.
VideoDetail
.
as_view
(),
name
=
"video-detail"
name
=
"video-detail"
),
),
url
(
r'^edxval/video/(?P<video__edx_video_id>[-\w]+)/(?P<language>[-_\w]+)$'
,
views
.
SubtitleDetail
.
as_view
(),
name
=
"subtitle-detail"
),
url
(
r'^edxval/video/(?P<edx_video_id>[-\w]+)/(?P<language>[-_\w]+)/subtitle$'
,
views
.
get_subtitle
,
name
=
"subtitle-content"
),
)
)
edxval/views.py
View file @
025ad8df
...
@@ -3,13 +3,30 @@ Views file for django app edxval.
...
@@ -3,13 +3,30 @@ Views file for django app edxval.
"""
"""
from
rest_framework
import
generics
from
rest_framework
import
generics
from
django.http
import
HttpResponse
from
django.shortcuts
import
get_object_or_404
from
django.views.decorators.http
import
last_modified
from
edxval.models
import
Video
,
Profile
from
edxval.models
import
Video
,
Profile
,
Subtitle
from
edxval.serializers
import
(
from
edxval.serializers
import
(
VideoSerializer
,
VideoSerializer
,
ProfileSerializer
ProfileSerializer
,
SubtitleSerializer
)
)
class
MultipleFieldLookupMixin
(
object
):
"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
"""
def
get_object
(
self
):
queryset
=
self
.
get_queryset
()
# Get the base queryset
queryset
=
self
.
filter_queryset
(
queryset
)
# Apply any filter backends
filter
=
{}
for
field
in
self
.
lookup_fields
:
filter
[
field
]
=
self
.
kwargs
[
field
]
return
get_object_or_404
(
queryset
,
**
filter
)
# Lookup the object
class
VideoList
(
generics
.
ListCreateAPIView
):
class
VideoList
(
generics
.
ListCreateAPIView
):
"""
"""
...
@@ -36,3 +53,25 @@ class VideoDetail(generics.RetrieveUpdateDestroyAPIView):
...
@@ -36,3 +53,25 @@ class VideoDetail(generics.RetrieveUpdateDestroyAPIView):
lookup_field
=
"edx_video_id"
lookup_field
=
"edx_video_id"
queryset
=
Video
.
objects
.
all
()
queryset
=
Video
.
objects
.
all
()
serializer_class
=
VideoSerializer
serializer_class
=
VideoSerializer
class
SubtitleDetail
(
MultipleFieldLookupMixin
,
generics
.
RetrieveUpdateDestroyAPIView
):
"""
Gets a subtitle instance given its id
"""
lookup_fields
=
(
"video__edx_video_id"
,
"language"
)
queryset
=
Subtitle
.
objects
.
all
()
serializer_class
=
SubtitleSerializer
def
_last_modified_subtitle
(
request
,
edx_video_id
,
language
):
return
Subtitle
.
objects
.
get
(
video__edx_video_id
=
edx_video_id
,
language
=
language
)
.
modified
@last_modified
(
last_modified_func
=
_last_modified_subtitle
)
def
get_subtitle
(
request
,
edx_video_id
,
language
):
"""
Return content of subtitle by id
"""
sub
=
Subtitle
.
objects
.
get
(
video__edx_video_id
=
edx_video_id
,
language
=
language
)
response
=
HttpResponse
(
sub
.
content
,
content_type
=
sub
.
content_type
)
return
response
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