Commit 112da93f by christopher lee

POST EncodedVideo set

EncodedVideo part of the upload. Complete EncodedVideo dicts may
now be uploaded. Rejects all EncodedVideos of a Video when there is
an invalid EncodedVideo. Does not reject other Video when a differet
Video is invalid. Currently, EncodedVideo cannot be updated, they can
only be created.

Other notes:
-Added django-debug-toolbar==1.2.1
-constants.py has been reorganized
parent 7a910438
......@@ -10,7 +10,10 @@ class Profile(models.Model):
"""
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)
width = models.PositiveIntegerField()
height = models.PositiveIntegerField()
......
......@@ -2,29 +2,60 @@
Serializers for Video Abstraction Layer
"""
from rest_framework import serializers
from rest_framework.serializers import ValidationError
from edxval.models import Profile, Video, EncodedVideo
class VideoSerializer(serializers.ModelSerializer):
class DisplayProfileName(serializers.WritableField):
"""
Takes a Profile Object and returns only it's profile_name
"""
def from_native(self, data):
try:
if isinstance(data, int):
profile = Profile.objects.get(pk=data)
elif isinstance(data, unicode):
profile = Profile.objects.get(profile_name=str(data))
return profile
except Profile.DoesNotExist:
error_message = "Profile does not exist: {0}".format(data)
raise ValidationError(error_message)
def restore_object(self, attrs, instance=None):
def to_native(self, data):
return data.profile_name
class DisplayVideoName(serializers.WritableField):
"""
Given a dictionary of deserialized field values, either update
an existing model instance, or create a new model instance.
Takes a Video Object and returns only it's profile_name
"""
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)
def from_native(self, data):
#TODO change doc/function name or modify this function, currently
#TODO takes a video pk and converts it to video Object.
try:
if isinstance(data, int):
video = Video.objects.get(pk=data)
return video
except Video.DoesNotExist:
error_message = "Video does not exist: {0}".format(data)
raise ValidationError(error_message)
def to_native(self, data):
return data.edx_video_id
class ListField(serializers.WritableField):
"""
Allows the use of a list as a serializer field.
"""
def from_native(self, data):
if isinstance(data, list):
return data
else:
error_message = "Expecting a list: {0}".format(type(data))
raise ValidationError(error_message)
class Meta:
model = Video
......@@ -45,17 +76,59 @@ class ProfileSerializer(serializers.ModelSerializer):
)
class OnlyEncodedVideoSerializer(serializers.ModelSerializer):
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 EncodedVideoSerializer(serializers.ModelSerializer):
"""
Used to serialize the EncodedVideo for the EncodedVideoSetSerializer
"""
profile = ProfileSerializer(required=False)
profile = DisplayProfileName()
video = DisplayVideoName()
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:
#TODO Currently not updating Encodedvideo Object.
instance.url = attrs.get('url', instance.url)
instance.file_size = attrs.get('file_size', instance.file_size)
instance.bitrate = attrs.get('bitrate', instance.bitrate)
#TODO profile instance.profile = attrs.get('profile', instance.profile)
return instance
return EncodedVideo(**attrs)
class Meta:
model = EncodedVideo
fields = (
"url",
"file_size",
"bitrate"
"bitrate",
"profile",
"video"
)
......@@ -64,12 +137,114 @@ class EncodedVideoSetSerializer(serializers.ModelSerializer):
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()
encoded_videos = EncodedVideoSerializer(required=False)
class Meta:
model = Video
fields = (
"duration",
"client_video_id"
"client_video_id",
"edx_video_id",
"encoded_videos"
)
class EncodedVideoSetDeserializer(serializers.Serializer):
"""
Deserializes a dict of Video fields and list of EncodedVideos.
Example:
>>>data = dict(
... client_video_id="Shallow Swordfish",
... duration=122.00,
... edx_video_id="supersoaker",
... encoded_videos=[
... dict(
... url="https://www.swordsingers.com",
... file_size=9000,
... bitrate=42,
... profile=1,
... ),
... dict(
... url="https://www.swordsingers.com",
... file_size=9000,
... bitrate=42,
... profile=2,
... )
... ]
... )
>>>serilizer = EncodedVideoSetDeserializer(data=data)
>>>if serilizer.is_valid()
>>> serializer.save()
>>>video = Video.objects.get(edx_video_id="supersoaker")
>>>print EncodedVideoSetDeserializer(video).data
{
'duration': 122.0,
'client_video_id': u'ShallowSwordfish',
'edx_video_id': u'supersoaker',
'encoded_videos': [
{
'url': u'https: //www.swordsingers.com',
'file_size': 9000,
'bitrate': 42,
'profile': u'mobile',
'video': u'supersoaker'
},
{
'url': u'https: //www.swordsingers.com',
'file_size': 9000,
'bitrate': 42,
'profile': u'desktop',
'video': u'supersoaker'
}
]
}
"""
client_video_id = serializers.CharField(max_length=50)
duration = serializers.FloatField()
edx_video_id = serializers.CharField(max_length=50)
encoded_videos = ListField(required=False)
def restore_object(self, attrs, instance=None):
"""
Updates or creates video object and creates valid EncodedVideo objects.
If the Video parameters are not valid, the errors are returns and the
Video object is not created and the desrialization ends. If any of the
EncodedVideo Parameters are invalid, the errors are returns and the
EncodedVideos objects are not created.
"""
#Get or create the Video object, else return errors
try:
instance = Video.objects.get(edx_video_id=attrs.get("edx_video_id"))
except Video.DoesNotExist:
instance = None
video = VideoSerializer(
data=dict(
edx_video_id=attrs.get("edx_video_id"),
duration=attrs.get("duration"),
client_video_id=attrs.get("client_video_id")
),
instance=instance
)
if video.is_valid():
video.save()
else:
for key in video.errors:
self.errors[key] = video.errors[key]
return
#Point encoded_videos to parent video
if not "encoded_videos" in attrs:
return video
for item in attrs.get("encoded_videos"):
item[u"video"] = Video.objects.get(edx_video_id=attrs.get("edx_video_id")).pk
#Serialize EncodedVideos, else raise errors
ev = EncodedVideoSerializer(data=attrs.get("encoded_videos"), many=True)
if ev.is_valid():
ev.save()
else:
self.errors["encoded_videos"] = ev.errors
return video
\ No newline at end of file
......@@ -124,6 +124,7 @@ INSTALLED_APPS = (
'rest_framework',
# Uncomment the next line to enable the admin:
'django.contrib.admin',
'debug_toolbar'
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
)
......
# -*- 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(
url="http://www.meowmix.com",
file_size=25556,
bitrate=9600,
file_size=4545,
bitrate=6767,
)
ENCODED_VIDEO_DICT_DESKTOP = dict(
url="http://www.meowmagic.com",
file_size=25556,
bitrate=9600,
file_size=1212,
bitrate=2323,
)
"""
Validators
"""
VIDEO_DICT_NEGATIVE_DURATION = dict(
client_video_id="Thunder Cats S01E01",
duration=-111,
edx_video_id="thisis12char-thisis7",
)
ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE = dict(
url="http://www.meowmix.com",
file_size=-25556,
......@@ -23,19 +47,18 @@ ENCODED_VIDEO_DICT_NEGATIVE_BITRATE = dict(
file_size=25556,
bitrate=-9600,
)
PROFILE_DICT_MOBILE = dict(
profile_name="mobile",
extension="avi",
width=100,
height=101
"""
Non-latin
"""
VIDEO_DICT_NON_LATIN_TITLE = dict(
client_video_id="배고픈 햄스터",
duration=42,
edx_video_id="ID"
)
PROFILE_DICT_DESKTOP = dict(
profile_name="desktop",
extension="mp4",
width=200,
height=2001
VIDEO_DICT_NON_LATIN_ID = dict(
client_video_id="Hungry Hamster",
duration=42,
edx_video_id="밥줘"
)
PROFILE_DICT_NON_LATIN = dict(
profile_name=u"배고파",
......@@ -43,71 +66,148 @@ PROFILE_DICT_NON_LATIN = dict(
width=100,
height=300
)
VIDEO_DICT_CATS = dict(
client_video_id="Thunder Cats S01E01",
duration=111.00,
edx_video_id="thisis12char-thisis7",
"""
Fish
"""
VIDEO_DICT_FISH = dict(
client_video_id="Shallow Swordfish",
duration=122.00,
edx_video_id="supersoaker"
)
VIDEO_DICT_LION = dict(
client_video_id="Lolcat",
duration=111.00,
edx_video_id="caw",
ENCODED_VIDEO_DICT_FISH_MOBILE = dict(
url="https://www.swordsingers.com",
file_size=9000,
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,
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 = [
dict(
client_video_id="Tipsy Tiger",
duration=111.00,
edx_video_id="meeeeeow",
),
dict(
client_video_id="Boring Bear",
duration=111.00,
edx_video_id="hithar",
)
COMPLETE_SETS_ALL_INVALID = [
COMPLETE_SET_INVALID_VIDEO_FISH,
COMPLETE_SET_INVALID_VIDEO_FISH
]
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",
"""
Star
"""
VIDEO_DICT_STAR = dict(
client_video_id="TWINKLE TWINKLE",
duration=122.00,
edx_video_id="little-star"
)
ENCODED_VIDEO_DICT_STAR = dict(
url="https://www.howIwonder.com",
file_size=9000,
bitrate=42,
profile=1
)
ENCODED_VIDEO_DICT_STAR2 = dict(
url="https://www.whatyouare.com",
file_size=1111,
bitrate=2333,
profile=2
)
COMPLETE_SET_STAR = dict(
encoded_videos=[
ENCODED_VIDEO_DICT_STAR,
ENCODED_VIDEO_DICT_STAR2
],
**VIDEO_DICT_STAR
)
COMPLETE_SET_NOT_A_LIST = dict(
encoded_videos=dict(
url="https://www.howIwonder.com",
file_size=9000,
bitrate=42,
profile=1
),
**VIDEO_DICT_STAR
)
COMPLETE_SET_EXTRA_VIDEO_FIELD = dict(
encoded_videos=[
dict(
url="https://www.vulturevideos.com",
file_size=101010,
bitrate=1234,
profile=1,
video="This should be overridden by parent videofield"
)
],
**VIDEO_DICT_STAR
)
"""
Unsorted
"""
VIDEO_DICT_COAT = dict(
client_video_id="Callous Coat",
duration=111.00,
edx_video_id="not an animal",
)
]
VIDEO_DICT_DUPLICATES = [
dict(
client_video_id="Gaggling gopher",
edx_video_id="itchyjacket",
)
VIDEO_DICT_AVERAGE = dict(
client_video_id="Average Animal",
duration=111.00,
edx_video_id="gg",
),
dict(
client_video_id="Gaggling gopher",
edx_video_id="mediocrity",
)
VIDEO_DICT_AVERAGE2 = dict(
client_video_id="Lolcat",
duration=122.00,
edx_video_id="mediocrity",
)
VIDEO_DICT_CRAYFISH = dict(
client_video_id="Crazy Crayfish",
duration=111.00,
edx_video_id="gg",
),
]
edx_video_id="craycray",
)
VIDEO_DICT_NEGATIVE_DURATION = dict(
client_video_id="Thunder Cats S01E01",
duration=-111,
edx_video_id="thisis12char-thisis7",
VIDEO_DICT_BEE_INVALID = dict(
client_video_id="Barking Bee",
duration=111.00,
edx_video_id="wa/sps",
)
VIDEO_DICT_INVALID_ID = dict(
......@@ -116,14 +216,30 @@ VIDEO_DICT_INVALID_ID = dict(
edx_video_id="sloppy/sloth!!"
)
VIDEO_DICT_NON_LATIN_TITLE = dict(
client_video_id="배고픈 햄스터",
duration=42,
edx_video_id="ID"
)
VIDEO_DICT_DUPLICATES = [
VIDEO_DICT_CRAYFISH,
VIDEO_DICT_CRAYFISH,
VIDEO_DICT_CRAYFISH
]
VIDEO_DICT_NON_LATIN_ID = dict(
client_video_id="Hungry Hamster",
duration=42,
edx_video_id="밥줘"
)
\ No newline at end of file
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_AVERAGE,
VIDEO_DICT_CRAYFISH
]
VIDEO_DICT_INVALID_SET = [
VIDEO_DICT_COAT,
VIDEO_DICT_INVALID_ID,
VIDEO_DICT_BEE_INVALID
]
......@@ -25,17 +25,17 @@ class GetVideoInfoTest(TestCase):
"""
Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
Video.objects.create(**constants.VIDEO_DICT_CATS)
Video.objects.create(**constants.VIDEO_DICT_COAT)
EncodedVideo.objects.create(
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"),
**constants.ENCODED_VIDEO_DICT_MOBILE
)
EncodedVideo.objects.create(
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"),
**constants.ENCODED_VIDEO_DICT_DESKTOP
......
......@@ -6,12 +6,14 @@ Tests the serializers for the Video Abstraction Layer
from django.test import TestCase
from edxval.serializers import (
OnlyEncodedVideoSerializer,
EncodedVideoSerializer,
EncodedVideoSetSerializer,
ProfileSerializer,
VideoSerializer
VideoSerializer,
DisplayProfileName
)
from edxval.models import Profile
from edxval.models import Profile, Video, EncodedVideo
from edxval.tests import constants
......@@ -24,6 +26,7 @@ class SerializerTests(TestCase):
Creates EncodedVideo objects in database
"""
Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
Profile.objects.create(**constants.PROFILE_DICT_NON_LATIN)
def test_negative_fields_only_encoded_video(self):
......@@ -32,11 +35,12 @@ class SerializerTests(TestCase):
Tests negative inputs for bitrate, file_size in EncodedVideo
"""
a = OnlyEncodedVideoSerializer(
a = EncodedVideoSerializer(
data=constants.ENCODED_VIDEO_DICT_NEGATIVE_BITRATE).errors
self.assertEqual(a.get('bitrate')[0],
u"Ensure this value is greater than or equal to 0.")
b = OnlyEncodedVideoSerializer(
b = EncodedVideoSerializer(
data=constants.ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE).errors
self.assertEqual(b.get('file_size')[0],
u"Ensure this value is greater than or equal to 0.")
......@@ -56,6 +60,7 @@ class SerializerTests(TestCase):
"""
Tests if the serializers can accept non-latin chars
"""
#TODO not the best test. Need to understand what result we want
self.assertIsNotNone(
ProfileSerializer(Profile.objects.get(profile_name="배고파"))
)
......@@ -69,3 +74,21 @@ class SerializerTests(TestCase):
self.assertEqual(
message,
u"edx_video_id has invalid characters")
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 = EncodedVideoSetSerializer(video).data
self.assertEqual(len(result.get("encoded_videos")), 2)
......@@ -3,76 +3,129 @@ from rest_framework import status
from rest_framework.test import APITestCase
from edxval.tests import constants
from edxval.models import Profile, Video
from edxval.serializers import EncodedVideoSetSerializer
class VideoListTest(APITestCase):
"""
Tests the creations of Videos via POST/GET
"""
def setUp(self):
"""
SetUp
"""
Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
def test_post_video(self):
"""
Tests creating a new Video object via POST
Tests for successful POST requests.
These tests should be returning HTTP_201_CREATED responses.
"""
def test_complete_set_two_encoded_video_post(self):
"""
Tests POSTing Video and EncodedVideo pair
"""
url = reverse('video_view')
response = self.client.post(
url, [constants.VIDEO_DICT_LION], format='json'
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)
result = EncodedVideoSetSerializer(Video.objects.all()[0]).data
self.assertEqual(len(result.get("encoded_videos")), 2)
self.assertEqual(response.data, "Success")
def test_get_all_videos(self):
"""
Tests getting all Video objects
"""
url = reverse('video_view')
self.client.post(url, [constants.VIDEO_DICT_LION], format='json')
self.client.post(url, [constants.VIDEO_DICT_CATS], format='json')
videos = len(self.client.get("/edxval/video/").data)
self.assertEqual(videos, 2)
# def test_update_already_existing_complete_set(self):
# """
# Tests the update of an already existing Video and its EncodedVideos via POST
# """
# #TODO This test will fail until video/profile are unique_together
# #TODO and when profiles are updated instead of created
# url = reverse('video_view')
# response = self.client.post(
# url, constants.COMPLETE_SET_FISH, format='json'
# )
# self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# response = self.client.post(
# url, constants.COMPLETE_SET_STAR, format='json'
# )
# video = self.client.get("/edxval/video/").data
# self.assertEqual(len(video), 1)
# result = EncodedVideoSetSerializer(Video.objects.all()[0]).data
# self.assertEqual(len(result.get("encoded_videos")), 2)
# self.assertEqual(response.data, "Success")
def test_post_multiple_valid_video_creation(self):
def test_complete_set_with_extra_video_field(self):
"""
Tests the creation of more than one video
Tests the case where there is an additional unneeded video field vis POST
"""
url = reverse('video_view')
response = self.client.post(
url, constants.VIDEO_DICT_TIGERS_BEARS, format='json'
url, constants.COMPLETE_SET_EXTRA_VIDEO_FIELD, format='json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
videos = len(self.client.get("/edxval/video/").data)
self.assertEqual(videos, 2)
video = self.client.get("/edxval/video/").data
self.assertEqual(len(video), 1)
edx_video_id = constants.VIDEO_DICT_STAR.get("edx_video_id")
self.assertEqual(video[0].get("edx_video_id"), edx_video_id)
def test_post_invalid_video_entry(self):
def test_post_video(self):
"""
Tests for invalid video entry for POST
Tests POSTing a new Video object
"""
url = reverse('video_view')
response = self.client.post(url, [constants.VIDEO_DICT_INVALID_ID], format='json')
error = len(response.data)
self.assertEqual(error, 1)
response = self.client.post(
url, [constants.VIDEO_DICT_AVERAGE], format='json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
result = EncodedVideoSetSerializer(Video.objects.all()[0]).data
self.assertEqual(len(result.get("encoded_videos")), 0)
def test_post_invalid_entry(self):
def test_post_videos(self):
"""
Tests when a non list POST request is made
Tests POSTing same video
"""
url = reverse('video_view')
response = self.client.post(url, constants.VIDEO_DICT_CATS, format='json')
self.assertEqual(response.data, "Not a list: <type 'dict'>")
response = self.client.post(
url, [constants.VIDEO_DICT_AVERAGE], format='json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
videos = len(self.client.get("/edxval/video/").data)
self.assertEqual(videos, 1)
response = self.client.post(
url, [constants.VIDEO_DICT_AVERAGE], format='json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
videos = len(self.client.get("/edxval/video/").data)
self.assertEqual(videos, 1)
response = self.client.post(
url, [constants.VIDEO_DICT_AVERAGE], format='json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
videos = len(self.client.get("/edxval/video/").data)
self.assertEqual(videos, 1)
def test_post_invalid_video_dict_list(self):
def test_post_multiple_valid_video_creation(self):
"""
Tests when there are valid and invalid dicts in list
Tests POSTing than one valid videos
Input is a list of video dicts
"""
url = reverse('video_view')
response = self.client.post(url, constants.VIDEO_DICT_INVALID_SET, format='json')
errors = len(response.data)
self.assertEqual(errors, 2)
response = self.client.post(
url, constants.VIDEO_DICT_SET_OF_THREE, format='json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
videos = len(self.client.get("/edxval/video/").data)
self.assertEqual(videos, 1)
self.assertEqual(videos, 3)
def test_post_valid_video_dict_list_duplicates(self):
"""
Tests when valid duplicate dicts are submitted in a list
Tests when POSTing valid duplicate dicts in a list
"""
url = reverse('video_view')
response = self.client.post(url, constants.VIDEO_DICT_DUPLICATES, format='json')
......@@ -80,45 +133,187 @@ class VideoListTest(APITestCase):
videos = len(self.client.get("/edxval/video/").data)
self.assertEqual(videos, 1)
def test_post_non_latin_dict(self):
def test_post_non_latin_client_video_id(self):
"""
Tests a non-latin character input
Tests POSTing non-latin client_video_id
"""
url = reverse('video_view')
response = self.client.post(url, [constants.VIDEO_DICT_NON_LATIN_ID], format='json')
errors = len(response.data)
self.assertEqual(errors, 1)
response = self.client.post(url, [constants.VIDEO_DICT_NON_LATIN_TITLE], format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
def test_post_update_video(self):
"""
Tests video update
Tests POST video update
"""
url = reverse('video_view')
self.client.post(url, [constants.VIDEO_DICT_LION], format='json')
response = self.client.post(url, [constants.VIDEO_DICT_LION2], format='json')
response = self.client.post(url, [constants.VIDEO_DICT_AVERAGE], format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.post(url, [constants.VIDEO_DICT_AVERAGE2], format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
videos = self.client.get("/edxval/video/").data
self.assertEqual(len(videos), 1)
old_duration = constants.VIDEO_DICT_AVERAGE.get("duration")
new_duration = videos[0].get("duration")
self.assertNotEqual(new_duration, old_duration)
old_client_video_id = constants.VIDEO_DICT_AVERAGE.get("client_video_id")
new_client_video_id = videos[0].get("client_video_id")
self.assertNotEqual(new_client_video_id, old_client_video_id)
def test_post_an_encoded_video_for_different_video(self):
"""
Tests POSTing encoded videos for different videos
"""
url = reverse('video_view')
response = self.client.post(
url, constants.COMPLETE_SETS, format='json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
videos = self.client.get("/edxval/video/").data
self.assertEqual(len(videos), 2)
self.assertNotEqual(videos[0], videos[1])
"""
Tests for POSTing invalid data
These tests should be returning HTTP_400_BAD_REQUEST
"""
def test_complete_set_invalid_encoded_video_post(self):
"""
Tests POSTing valid Video and partial valid EncodedVideos.
"""
url = reverse('video_view')
response = self.client.post(
url, constants.COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH, format='json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
video = self.client.get("/edxval/video/").data
self.assertEqual(len(video), 1)
result = EncodedVideoSetSerializer(Video.objects.all()[0]).data
self.assertEqual(len(result.get("encoded_videos")), 0)
self.assertIsNotNone(response.data.get("encoded_videos")[0])
def test_complete_set_invalid_video_post(self):
"""
Tests invalid Video POST
"""
url = reverse('video_view')
response = self.client.post(
url, constants.COMPLETE_SET_INVALID_VIDEO_FISH, format='json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
video = self.client.get("/edxval/video/").data
self.assertEqual(len(video), 0)
self.assertEqual(
response.data.get("edx_video_id")[0],
"edx_video_id has invalid characters"
)
def test_complete_set_not_a_list(self):
"""
Tests POST when the encoded videos are not a list of dicts
"""
url = reverse('video_view')
response = self.client.post(
url, constants.COMPLETE_SET_NOT_A_LIST, format='json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data,
{'encoded_videos':[u"Expecting a list: <type 'dict'>"]}
)
def test_post_invalid_video_entry(self):
"""
Tests for invalid video entry for POST
"""
url = reverse('video_view')
response = self.client.post(url, [constants.VIDEO_DICT_INVALID_ID], format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
errors = response.data
self.assertEqual(
errors[0].get("edx_video_id")[0],
"edx_video_id has invalid characters"
)
def test_post_invalid_video_dict_list(self):
"""
Tests POSTing a list of invalid Video dicts
Input is a list of video dicts where [{valid},{invalid},{invalid}]
"""
url = reverse('video_view')
response = self.client.post(url, constants.VIDEO_DICT_INVALID_SET, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
errors = response.data
self.assertEqual(
errors[1].get("edx_video_id")[0],
"edx_video_id has invalid characters"
)
self.assertEqual(
errors[2].get("edx_video_id")[0],
"edx_video_id has invalid characters"
)
videos = len(self.client.get("/edxval/video/").data)
self.assertEqual(videos, 1)
old_duration = constants.VIDEO_DICT_LION.get("duration")
new_duration = constants.VIDEO_DICT_LION2.get("duration")
self.assertNotEqual(new_duration, old_duration)
def test_post_non_latin_edx_video_id(self):
"""
Tests POSTing non-latin edx_video_id
"""
url = reverse('video_view')
response = self.client.post(url, [constants.VIDEO_DICT_NON_LATIN_ID], format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_post_an_encoded_video_for_different_video(self):
"""
Tests POSTing a list of Videos, [{valid}, {invalid}]
"""
url = reverse('video_view')
response = self.client.post(
url, constants.COMPLETE_SETS_ONE_INVALID, format='json'
)
errors = response.data
self.assertEqual(
errors[1].get("edx_video_id")[0],
"edx_video_id has invalid characters"
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
videos = self.client.get("/edxval/video/").data
self.assertEqual(len(videos), 1)
class VideoDetailTest(APITestCase):
def test_post_all_invalid_videos(self):
"""
Tests for the VideoDetail class
Tests POSTing a list of Videos, [{invalid}, {invalid}]
"""
url = reverse('video_view')
response = self.client.post(
url, constants.COMPLETE_SETS_ALL_INVALID, format='json'
)
errors = response.data
self.assertEqual(
errors[1].get("edx_video_id")[0],
"edx_video_id has invalid characters"
)
self.assertEqual(
errors[0].get("edx_video_id")[0],
"edx_video_id has invalid characters"
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
videos = self.client.get("/edxval/video/").data
self.assertEqual(len(videos), 0)
def test_get_video(self):
"""
Tests retrieving a particular Video Object
Tests for GET
"""
def test_get_all_videos(self):
"""
Tests getting all Video objects
"""
url = reverse('video_view')
self.client.post(url, [constants.VIDEO_DICT_LION], format='json')
search = "/edxval/video/{0}".format(constants.VIDEO_DICT_LION.get("edx_video_id"))
response = self.client.get(search)
a = response.data.get("edx_video_id")
b = constants.VIDEO_DICT_LION.get("edx_video_id")
self.assertEqual(a, b)
\ No newline at end of file
response = self.client.post(url, [constants.VIDEO_DICT_AVERAGE], format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.post(url, [constants.VIDEO_DICT_COAT], format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
videos = len(self.client.get("/edxval/video/").data)
self.assertEqual(videos, 2)
......@@ -3,12 +3,15 @@ from rest_framework.response import Response
from rest_framework import status, generics
from edxval.models import Video
from edxval.serializers import VideoSerializer
from edxval.serializers import VideoSerializer, EncodedVideoSetDeserializer, EncodedVideoSetSerializer
class VideoList(APIView):
"""
HTTP API for Video objects
<<<<<<< HEAD
HTTP API for Video and EncodedVideo objects
"""
def get(self, request, format=None):
......@@ -16,44 +19,27 @@ class VideoList(APIView):
Gets all videos
"""
video = Video.objects.all()
serializer = VideoSerializer(video, many=True)
serializer = EncodedVideoSetSerializer(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.
Takes a Video dict of a list of EncodedVideo dicts and creates the objects
Args:
request (object): Object where we get our information for POST
format (str): format of our data (JSON, XML, etc.)
data_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)
serializer = EncodedVideoSetDeserializer(data=request.DATA)
if serializer.is_valid():
serializer.save()
return Response("Success", status=status.HTTP_201_CREATED)
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)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class VideoDetail(generics.RetrieveUpdateDestroyAPIView):
......
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
admin.autodiscover()
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)),
)
urlpatterns = format_suffix_patterns(urlpatterns)
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