Commit 49c58413 by Christopher Lee

Merge pull request #8 from edx/clee/generic_views

POST for single EncodedVideo
parents 7a910438 e793792a
...@@ -5,7 +5,7 @@ The internal API for VAL ...@@ -5,7 +5,7 @@ The internal API for VAL
import logging import logging
from edxval.models import Video from edxval.models import Video
from edxval.serializers import EncodedVideoSetSerializer from edxval.serializers import VideoSerializer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -57,6 +57,7 @@ def get_video_info(edx_video_id, location=None): ...@@ -57,6 +57,7 @@ def get_video_info(edx_video_id, location=None):
Returns all the Video object fields, and it's related EncodedVideo Returns all the Video object fields, and it's related EncodedVideo
objects in a list. objects in a list.
{ {
url: api url to the video
edx_video_id: ID of the video edx_video_id: ID of the video
duration: Length of video in seconds duration: Length of video in seconds
client_video_id: client ID of video client_video_id: client ID of video
...@@ -75,32 +76,27 @@ def get_video_info(edx_video_id, location=None): ...@@ -75,32 +76,27 @@ def get_video_info(edx_video_id, location=None):
ValInternalError: Raised for unknown errors ValInternalError: Raised for unknown errors
Example: Example:
Given one EncodedVideo with edx_video_id "thisis12char-thisis7" Given one EncodedVideo with edx_video_id "example"
>>> >>> get_video_info("example")
>>> get_video_info("thisis12char-thisis7",location)
Returns (dict): Returns (dict):
>>>{ {
>>> 'edx_video_id': u'thisis12char-thisis7', 'url' : '/edxval/video/example'
>>> 'duration': 111.0, 'edx_video_id': u'example',
>>> 'client_video_id': u'Thunder Cats S01E01', 'duration': 111.0,
>>> 'encoded_video': [ 'client_video_id': u'The example video',
>>> { 'encoded_video': [
>>> 'url': u'http://www.meowmix.com', {
>>> 'file_size': 25556, 'url': u'http://www.meowmix.com',
>>> 'bitrate': 9600, 'file_size': 25556,
>>> 'profile': { 'bitrate': 9600,
>>> 'profile_name': u'mobile', 'profile': u'mobile'
>>> 'extension': u'avi', }
>>> 'width': 100, ]
>>> 'height': 101 }
>>> }
>>> },
>>> ]
>>>}
""" """
try: try:
v = Video.objects.get(edx_video_id=edx_video_id) v = Video.objects.get(edx_video_id=edx_video_id)
result = EncodedVideoSetSerializer(v) result = VideoSerializer(v)
except Video.DoesNotExist: except Video.DoesNotExist:
error_message = u"Video not found for edx_video_id: {0}".format(edx_video_id) error_message = u"Video not found for edx_video_id: {0}".format(edx_video_id)
raise ValVideoNotFoundError(error_message) raise ValVideoNotFoundError(error_message)
...@@ -108,4 +104,4 @@ def get_video_info(edx_video_id, location=None): ...@@ -108,4 +104,4 @@ def get_video_info(edx_video_id, location=None):
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 return result.data
\ No newline at end of file
...@@ -10,7 +10,10 @@ class Profile(models.Model): ...@@ -10,7 +10,10 @@ class Profile(models.Model):
""" """
Details for pre-defined encoding format Details for pre-defined encoding format
""" """
profile_name = models.CharField(max_length=50, unique=True) profile_name = models.CharField(
max_length=50,
unique=True,
)
extension = models.CharField(max_length=10) extension = models.CharField(max_length=10)
width = models.PositiveIntegerField() width = models.PositiveIntegerField()
height = models.PositiveIntegerField() height = models.PositiveIntegerField()
...@@ -88,4 +91,4 @@ class EncodedVideo(models.Model): ...@@ -88,4 +91,4 @@ class EncodedVideo(models.Model):
).format(self) ).format(self)
def __unicode__(self): def __unicode__(self):
return repr(self) return repr(self)
\ No newline at end of file
...@@ -6,34 +6,6 @@ from rest_framework import serializers ...@@ -6,34 +6,6 @@ from rest_framework import serializers
from edxval.models import Profile, Video, EncodedVideo from edxval.models import Profile, Video, EncodedVideo
class VideoSerializer(serializers.ModelSerializer):
def restore_object(self, attrs, instance=None):
"""
Given a dictionary of deserialized field values, either update
an existing model instance, or create a new model instance.
"""
if instance is not None:
instance.edx_video_id = attrs.get(
'edx_video_id', instance.edx_video_id
)
instance.duration = attrs.get(
'duration', instance.duration
)
instance.client_video_id = attrs.get(
'client_video_id', instance.client_video_id
)
return instance
return Video(**attrs)
class Meta:
model = Video
fields = (
"client_video_id",
"duration",
"edx_video_id"
)
class ProfileSerializer(serializers.ModelSerializer): class ProfileSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Profile model = Profile
...@@ -45,31 +17,24 @@ class ProfileSerializer(serializers.ModelSerializer): ...@@ -45,31 +17,24 @@ class ProfileSerializer(serializers.ModelSerializer):
) )
class OnlyEncodedVideoSerializer(serializers.ModelSerializer): class EncodedVideoSerializer(serializers.ModelSerializer):
""" profile = serializers.SlugRelatedField(slug_field="profile_name")
Used to serialize the EncodedVideo for the EncodedVideoSetSerializer
"""
profile = ProfileSerializer(required=False)
class Meta: class Meta:
model = EncodedVideo model = EncodedVideo
fields = ( fields = (
"created",
"modified",
"url", "url",
"file_size", "file_size",
"bitrate" "bitrate",
"profile",
) )
class EncodedVideoSetSerializer(serializers.ModelSerializer): class VideoSerializer(serializers.HyperlinkedModelSerializer):
""" encoded_videos = EncodedVideoSerializer(many=True, allow_add_remove=True)
Used to serialize a list of EncodedVideo objects it's foreign key Video Object.
"""
edx_video_id = serializers.CharField(max_length=50)
encoded_videos = OnlyEncodedVideoSerializer()
class Meta: class Meta:
model = Video model = Video
fields = ( lookup_field = "edx_video_id"
"duration",
"client_video_id"
)
...@@ -124,6 +124,7 @@ INSTALLED_APPS = ( ...@@ -124,6 +124,7 @@ INSTALLED_APPS = (
'rest_framework', 'rest_framework',
# Uncomment the next line to enable the admin: # Uncomment the next line to enable the admin:
'django.contrib.admin', 'django.contrib.admin',
'debug_toolbar'
# Uncomment the next line to enable admin documentation: # Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs', # 'django.contrib.admindocs',
) )
...@@ -165,4 +166,4 @@ LOGGING = { ...@@ -165,4 +166,4 @@ LOGGING = {
'propagate': True, 'propagate': True,
}, },
} }
} }
\ No newline at end of file
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
EDX_VIDEO_ID = "thisis12char-thisis7" EDX_VIDEO_ID = "itchyjacket"
"""
Generic Profiles for manually creating profile objects
"""
PROFILE_DICT_MOBILE = dict(
profile_name="mobile",
extension="avi",
width=100,
height=101
)
PROFILE_DICT_DESKTOP = dict(
profile_name="desktop",
extension="mp4",
width=200,
height=2001
)
"""
Encoded_videos for test_api, does not have profile.
"""
ENCODED_VIDEO_DICT_MOBILE = dict( ENCODED_VIDEO_DICT_MOBILE = dict(
url="http://www.meowmix.com", url="http://www.meowmix.com",
file_size=25556, file_size=4545,
bitrate=9600, bitrate=6767,
) )
ENCODED_VIDEO_DICT_DESKTOP = dict( ENCODED_VIDEO_DICT_DESKTOP = dict(
url="http://www.meowmagic.com", url="http://www.meowmagic.com",
file_size=25556, file_size=1212,
bitrate=9600, bitrate=2323,
)
"""
Validators
"""
VIDEO_DICT_NEGATIVE_DURATION = dict(
client_video_id="Thunder Cats S01E01",
duration=-111,
edx_video_id="thisis12char-thisis7",
encoded_videos=[]
) )
ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE = dict( ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE = dict(
url="http://www.meowmix.com", url="http://www.meowmix.com",
file_size=-25556, file_size=-25556,
...@@ -23,19 +48,32 @@ ENCODED_VIDEO_DICT_NEGATIVE_BITRATE = dict( ...@@ -23,19 +48,32 @@ ENCODED_VIDEO_DICT_NEGATIVE_BITRATE = dict(
file_size=25556, file_size=25556,
bitrate=-9600, bitrate=-9600,
) )
VIDEO_DICT_BEE_INVALID = dict(
PROFILE_DICT_MOBILE = dict( client_video_id="Barking Bee",
profile_name="mobile", duration=111.00,
extension="avi", edx_video_id="wa/sps",
width=100,
height=101
) )
PROFILE_DICT_DESKTOP = dict( VIDEO_DICT_INVALID_ID = dict(
profile_name="desktop", client_video_id="SuperSloth",
extension="mp4", duration=42,
width=200, edx_video_id="sloppy/sloth!!",
height=2001 encoded_videos=[]
)
"""
Non-latin/invalid
"""
VIDEO_DICT_NON_LATIN_TITLE = dict(
client_video_id=u"배고픈 햄스터",
duration=42,
edx_video_id="ID",
encoded_videos=[]
)
VIDEO_DICT_NON_LATIN_ID = dict(
client_video_id="Hungry Hamster",
duration=42,
edx_video_id="밥줘",
encoded_videos=[]
) )
PROFILE_DICT_NON_LATIN = dict( PROFILE_DICT_NON_LATIN = dict(
profile_name=u"배고파", profile_name=u"배고파",
...@@ -43,87 +81,168 @@ PROFILE_DICT_NON_LATIN = dict( ...@@ -43,87 +81,168 @@ PROFILE_DICT_NON_LATIN = dict(
width=100, width=100,
height=300 height=300
) )
VIDEO_DICT_CATS = dict( """
client_video_id="Thunder Cats S01E01", Fish
duration=111.00, """
edx_video_id="thisis12char-thisis7", VIDEO_DICT_FISH = dict(
client_video_id="Shallow Swordfish",
duration=122.00,
edx_video_id="supersoaker"
) )
VIDEO_DICT_LION = dict( ENCODED_VIDEO_DICT_FISH_MOBILE = dict(
client_video_id="Lolcat", url="https://www.swordsingers.com",
duration=111.00, file_size=9000,
edx_video_id="caw", bitrate=42,
profile="mobile",
) )
VIDEO_DICT_LION2 = dict(
client_video_id="Lolcat", ENCODED_VIDEO_DICT_FISH_DESKTOP = dict(
url="https://www.swordsplints.com",
file_size=1234,
bitrate=4222,
profile="desktop",
)
ENCODED_VIDEO_DICT_FISH_INVALID_PROFILE = dict(
url="https://www.swordsplints.com",
file_size=1234,
bitrate=4222,
profile=11,
)
COMPLETE_SET_FISH = dict(
encoded_videos=[
ENCODED_VIDEO_DICT_FISH_MOBILE,
ENCODED_VIDEO_DICT_FISH_DESKTOP
],
**VIDEO_DICT_FISH
)
COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH = dict(
encoded_videos=[
ENCODED_VIDEO_DICT_FISH_MOBILE,
ENCODED_VIDEO_DICT_FISH_INVALID_PROFILE
],
**VIDEO_DICT_FISH
)
COMPLETE_SET_INVALID_VIDEO_FISH = dict(
client_video_id="Shallow Swordfish",
duration=122.00, duration=122.00,
edx_video_id="caw", edx_video_id="super/soaker",
encoded_videos=[
ENCODED_VIDEO_DICT_FISH_MOBILE,
ENCODED_VIDEO_DICT_FISH_DESKTOP
]
) )
VIDEO_DICT_TIGERS_BEARS = [ COMPLETE_SETS_ALL_INVALID = [
dict( COMPLETE_SET_INVALID_VIDEO_FISH,
client_video_id="Tipsy Tiger", COMPLETE_SET_INVALID_VIDEO_FISH
duration=111.00,
edx_video_id="meeeeeow",
),
dict(
client_video_id="Boring Bear",
duration=111.00,
edx_video_id="hithar",
)
] ]
VIDEO_DICT_INVALID_SET = [
dict(
client_video_id="Average Animal",
duration=111.00,
edx_video_id="mediocrity",
),
dict(
client_video_id="Barking Bee",
duration=111.00,
edx_video_id="wa/sps",
),
dict(
client_video_id="Callous Coat",
duration=111.00,
edx_video_id="not an animal",
)
]
VIDEO_DICT_DUPLICATES = [ """
dict( Star
client_video_id="Gaggling gopher", """
duration=111.00, VIDEO_DICT_STAR = dict(
edx_video_id="gg", client_video_id="TWINKLE TWINKLE",
), duration=122.00,
dict( edx_video_id="little-star"
client_video_id="Gaggling gopher", )
duration=111.00, ENCODED_VIDEO_DICT_STAR = dict(
edx_video_id="gg", url="https://www.howIwonder.com",
), file_size=9000,
] bitrate=42,
profile="mobile"
)
VIDEO_DICT_NEGATIVE_DURATION = dict( COMPLETE_SET_STAR = dict(
client_video_id="Thunder Cats S01E01", encoded_videos=[
duration=-111, ENCODED_VIDEO_DICT_STAR
edx_video_id="thisis12char-thisis7", ],
**VIDEO_DICT_STAR
) )
VIDEO_DICT_INVALID_ID = dict( COMPLETE_SET_NOT_A_LIST = dict(
client_video_id="SuperSloth", encoded_videos=dict(
duration=42, url="https://www.howIwonder.com",
edx_video_id="sloppy/sloth!!" file_size=9000,
bitrate=42,
profile=1
),
**VIDEO_DICT_STAR
) )
VIDEO_DICT_NON_LATIN_TITLE = dict( COMPLETE_SET_EXTRA_VIDEO_FIELD = dict(
client_video_id="배고픈 햄스터", encoded_videos=[
duration=42, dict(
edx_video_id="ID" url="https://www.vulturevideos.com",
file_size=101010,
bitrate=1234,
profile="mobile",
video="This should be overridden by parent video field"
)
],
**VIDEO_DICT_STAR
)
"""
Unsorted
"""
VIDEO_DICT_COAT = dict(
client_video_id="Callous Coat",
duration=111.00,
edx_video_id="itchyjacket"
)
VIDEO_DICT_ANIMAL = dict(
client_video_id="Average Animal",
duration=111.00,
edx_video_id="mediocrity",
encoded_videos=[]
)
VIDEO_DICT_ZEBRA = dict(
client_video_id="Zesty Zebra",
duration=111.00,
edx_video_id="zestttt",
encoded_videos=[]
)
VIDEO_DICT_UPDATE_ANIMAL = dict(
client_video_id="Lolcat",
duration=122.00,
edx_video_id="mediocrity",
) )
VIDEO_DICT_NON_LATIN_ID = dict( VIDEO_DICT_CRAYFISH = dict(
client_video_id="Hungry Hamster", client_video_id="Crazy Crayfish",
duration=42, duration=111.00,
edx_video_id="밥줘" edx_video_id="craycray",
) )
\ No newline at end of file
VIDEO_DICT_DUPLICATES = [
VIDEO_DICT_CRAYFISH,
VIDEO_DICT_CRAYFISH,
VIDEO_DICT_CRAYFISH
]
COMPLETE_SETS = [
COMPLETE_SET_STAR,
COMPLETE_SET_FISH
]
COMPLETE_SETS_ONE_INVALID = [
COMPLETE_SET_STAR,
COMPLETE_SET_INVALID_VIDEO_FISH
]
VIDEO_DICT_SET_OF_THREE = [
VIDEO_DICT_COAT,
VIDEO_DICT_ANIMAL,
VIDEO_DICT_CRAYFISH
]
VIDEO_DICT_INVALID_SET = [
VIDEO_DICT_COAT,
VIDEO_DICT_INVALID_ID,
VIDEO_DICT_BEE_INVALID
]
...@@ -10,7 +10,7 @@ from django.db import DatabaseError ...@@ -10,7 +10,7 @@ from django.db import DatabaseError
from edxval.models import Profile, Video, EncodedVideo from edxval.models import Profile, Video, EncodedVideo
from edxval import api as api from edxval import api as api
from edxval.serializers import EncodedVideoSetSerializer from edxval.serializers import VideoSerializer
from edxval.tests import constants from edxval.tests import constants
...@@ -25,17 +25,17 @@ class GetVideoInfoTest(TestCase): ...@@ -25,17 +25,17 @@ class GetVideoInfoTest(TestCase):
""" """
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)
Video.objects.create(**constants.VIDEO_DICT_CATS) Video.objects.create(**constants.VIDEO_DICT_COAT)
EncodedVideo.objects.create( EncodedVideo.objects.create(
video=Video.objects.get( video=Video.objects.get(
edx_video_id=constants.VIDEO_DICT_CATS.get("edx_video_id") edx_video_id=constants.VIDEO_DICT_COAT.get("edx_video_id")
), ),
profile=Profile.objects.get(profile_name="mobile"), profile=Profile.objects.get(profile_name="mobile"),
**constants.ENCODED_VIDEO_DICT_MOBILE **constants.ENCODED_VIDEO_DICT_MOBILE
) )
EncodedVideo.objects.create( EncodedVideo.objects.create(
video=Video.objects.get( video=Video.objects.get(
edx_video_id=constants.VIDEO_DICT_CATS.get("edx_video_id") edx_video_id=constants.VIDEO_DICT_COAT.get("edx_video_id")
), ),
profile=Profile.objects.get(profile_name="desktop"), profile=Profile.objects.get(profile_name="desktop"),
**constants.ENCODED_VIDEO_DICT_DESKTOP **constants.ENCODED_VIDEO_DICT_DESKTOP
...@@ -51,8 +51,11 @@ class GetVideoInfoTest(TestCase): ...@@ -51,8 +51,11 @@ class GetVideoInfoTest(TestCase):
""" """
Tests searching for a video that does not exist Tests searching for a video that does not exist
""" """
with self.assertRaises(api.ValVideoNotFoundError):
api.get_video_info("non_existant-video__")
with self.assertRaises(api.ValVideoNotFoundError): with self.assertRaises(api.ValVideoNotFoundError):
api.get_video_info("non_existing-video__") api.get_video_info("")
def test_unicode_input(self): def test_unicode_input(self):
""" """
...@@ -61,7 +64,7 @@ class GetVideoInfoTest(TestCase): ...@@ -61,7 +64,7 @@ class GetVideoInfoTest(TestCase):
with self.assertRaises(api.ValVideoNotFoundError): with self.assertRaises(api.ValVideoNotFoundError):
api.get_video_info(u"๓ﻉѻฝ๓ٱซ") api.get_video_info(u"๓ﻉѻฝ๓ٱซ")
@mock.patch.object(EncodedVideoSetSerializer, '__init__') @mock.patch.object(VideoSerializer, '__init__')
def test_force_internal_error(self, mock_init): def test_force_internal_error(self, mock_init):
""" """
Tests to see if an unknown error will be handled Tests to see if an unknown error will be handled
...@@ -77,4 +80,4 @@ class GetVideoInfoTest(TestCase): ...@@ -77,4 +80,4 @@ class GetVideoInfoTest(TestCase):
""" """
mock_get.side_effect = DatabaseError("DatabaseError") mock_get.side_effect = DatabaseError("DatabaseError")
with self.assertRaises(api.ValInternalError): with self.assertRaises(api.ValInternalError):
api.get_video_info(constants.EDX_VIDEO_ID) api.get_video_info(constants.EDX_VIDEO_ID)
\ No newline at end of file
...@@ -6,12 +6,11 @@ Tests the serializers for the Video Abstraction Layer ...@@ -6,12 +6,11 @@ Tests the serializers for the Video Abstraction Layer
from django.test import TestCase from django.test import TestCase
from edxval.serializers import ( from edxval.serializers import (
OnlyEncodedVideoSerializer, EncodedVideoSerializer,
EncodedVideoSetSerializer,
ProfileSerializer, ProfileSerializer,
VideoSerializer VideoSerializer,
) )
from edxval.models import Profile from edxval.models import Profile, Video, EncodedVideo
from edxval.tests import constants from edxval.tests import constants
...@@ -21,45 +20,56 @@ class SerializerTests(TestCase): ...@@ -21,45 +20,56 @@ class SerializerTests(TestCase):
""" """
def setUp(self): def setUp(self):
""" """
Creates EncodedVideo objects in database Creates Profile objects
""" """
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_NON_LATIN) Profile.objects.create(**constants.PROFILE_DICT_NON_LATIN)
def test_negative_fields_only_encoded_video(self): def test_negative_fields_for_encoded_video_serializer(self):
""" """
Tests negative inputs for OnlyEncodedSerializer Tests negative inputs for EncodedVideoSerializer
Tests negative inputs for bitrate, file_size in EncodedVideo Tests negative inputs for bitrate, file_size in EncodedVideo
""" """
a = OnlyEncodedVideoSerializer( a = EncodedVideoSerializer(
data=constants.ENCODED_VIDEO_DICT_NEGATIVE_BITRATE).errors data=constants.ENCODED_VIDEO_DICT_NEGATIVE_BITRATE).errors
self.assertEqual(a.get('bitrate')[0], self.assertEqual(a.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.")
b = OnlyEncodedVideoSerializer( b = EncodedVideoSerializer(
data=constants.ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE).errors data=constants.ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE).errors
self.assertEqual(b.get('file_size')[0], self.assertEqual(b.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.")
def test_negative_fields_video_set(self): def test_negative_fields_for_video_serializer(self):
""" """
Tests negative inputs for EncodedVideoSetSerializer Tests negative inputs for VideoSerializer
Tests negative inputs for duration in model Video Tests negative inputs for duration in model Video
""" """
c = EncodedVideoSetSerializer( c = VideoSerializer(
data=constants.VIDEO_DICT_NEGATIVE_DURATION).errors data=constants.VIDEO_DICT_NEGATIVE_DURATION).errors
self.assertEqual(c.get('duration')[0], self.assertEqual(c.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.")
def test_unicode_inputs(self): def test_non_latin_serialization(self):
""" """
Tests if the serializers can accept non-latin chars Tests if the serializers can accept non-latin chars
""" """
self.assertIsNotNone( #TODO not the best test. Need to understand what result we want
ProfileSerializer(Profile.objects.get(profile_name="배고파")) self.assertIsInstance(
ProfileSerializer(Profile.objects.get(profile_name="배고파")),
ProfileSerializer
) )
def test_non_latin_deserialization(self):
"""
Tests deserialization of non-latin data
"""
#TODO write a test for this when we understand what we want
pass
def test_invalid_edx_video_id(self): def test_invalid_edx_video_id(self):
""" """
Test the Video model regex validation for edx_video_id field Test the Video model regex validation for edx_video_id field
...@@ -68,4 +78,25 @@ class SerializerTests(TestCase): ...@@ -68,4 +78,25 @@ class SerializerTests(TestCase):
message = error.get("edx_video_id")[0] message = error.get("edx_video_id")[0]
self.assertEqual( self.assertEqual(
message, message,
u"edx_video_id has invalid characters") u"edx_video_id has invalid characters")
\ No newline at end of file
def test_encoded_video_set_output(self):
"""
Tests for basic structure of EncodedVideoSetSerializer
"""
video = Video.objects.create(**constants.VIDEO_DICT_COAT)
EncodedVideo.objects.create(
video=video,
profile=Profile.objects.get(profile_name="desktop"),
**constants.ENCODED_VIDEO_DICT_DESKTOP
)
EncodedVideo.objects.create(
video=video,
profile=Profile.objects.get(profile_name="mobile"),
**constants.ENCODED_VIDEO_DICT_MOBILE
)
result = VideoSerializer(video).data
# Check for 2 EncodedVideo entries
self.assertEqual(len(result.get("encoded_videos")), 2)
# Check for original Video data
self.assertDictContainsSubset(constants.VIDEO_DICT_COAT, result)
...@@ -8,11 +8,9 @@ admin.autodiscover() ...@@ -8,11 +8,9 @@ admin.autodiscover()
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^edxval/video/$', views.VideoList.as_view(), url(r'^edxval/video/$', views.VideoList.as_view(),
name="video_view"), name="video-list"),
url(r'^edxval/video/(?P<edx_video_id>\w+)', url(r'^edxval/video/(?P<edx_video_id>\w+)',
views.VideoDetail.as_view(), views.VideoDetail.as_view(),
name="video_detail_view"), name="video-detail"),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
) )
urlpatterns = format_suffix_patterns(urlpatterns)
\ No newline at end of file
from rest_framework.views import APIView from rest_framework import generics
from rest_framework.response import Response
from rest_framework import status, generics
from edxval.models import Video from edxval.models import Video
from edxval.serializers import VideoSerializer from edxval.serializers import (
VideoSerializer
)
class VideoList(APIView): class VideoList(generics.ListCreateAPIView):
""" """
HTTP API for Video objects GETs or POST video objects
""" """
queryset = Video.objects.all().prefetch_related("encoded_videos")
def get(self, request, format=None): lookup_field = "edx_video_id"
""" serializer_class = VideoSerializer
Gets all videos
"""
video = Video.objects.all()
serializer = VideoSerializer(video, many=True)
return Response(serializer.data)
def post(self, request, format=None):
"""
Takes an object (where we get our list of dict) and creates the objects
Request.DATA is a list of dictionaries. Each item is individually validated
and if valid, saved. All invalid dicts are returned in the error message.
Args:
request (object): Object where we get our information for POST
format (str): format of our data (JSON, XML, etc.)
Returns:
Response(message, HTTP status)
"""
if not isinstance(request.DATA, list):
error_message = "Not a list: {0}".format(type(request.DATA))
return Response(error_message, status=status.HTTP_400_BAD_REQUEST)
invalid_videos = []
for item in request.DATA:
try:
instance = Video.objects.get(
edx_video_id=item.get("edx_video_id")
)
except Video.DoesNotExist:
instance = None
serializer = VideoSerializer(instance, data=item)
if serializer.is_valid():
serializer.save()
else:
invalid_videos.append((serializer.errors, item))
if invalid_videos:
return Response(invalid_videos, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(status=status.HTTP_201_CREATED)
class VideoDetail(generics.RetrieveUpdateDestroyAPIView): class VideoDetail(generics.RetrieveUpdateDestroyAPIView):
...@@ -62,4 +21,4 @@ class VideoDetail(generics.RetrieveUpdateDestroyAPIView): ...@@ -62,4 +21,4 @@ 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
\ No newline at end of file
django-nose==1.2 django-nose==1.2
coverage==3.7.1 coverage==3.7.1
mock==1.0.1 mock==1.0.1
\ No newline at end of file django-debug-toolbar==1.2.1
from django.conf.urls import patterns, include, url from django.conf.urls import patterns, include, url
from rest_framework.urlpatterns import format_suffix_patterns
from edxval import views
from django.contrib import admin from django.contrib import admin
admin.autodiscover() admin.autodiscover()
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^edxval/', include('edxval.urls')), url(r'^edxval/video/$', views.VideoList.as_view(),
name="video_view"),
url(r'^edxval/video/(?P<edx_video_id>\w+)',
views.VideoDetail.as_view(),
name="video_detail_view"),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
) )
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment