Commit 19e0e23f by christopher lee

Organized and added tests. Removed colon

-Organized tests for consistency and to increase coverage.
 Added more tests for get_video_for_ids, get_url_for_profile(s),
 and having a colon in edx_video_id.
-Deleted unused test_models.py
-Updated outdated readme note
-Added missing documentation for some functions
-Removed colon
parent 28993bd5
edx-val edx-val
======= =======
Note:
The naming convention for the `profile_name` in `Profile` objects will be the
medium of the profile (a-z), an underscore, and then the quality (a-z).
Examples:
desktop_high
mobile_medium
somethingelse_low
...@@ -22,7 +22,7 @@ class CourseVideoInline(admin.TabularInline): # pylint: disable=C0111 ...@@ -22,7 +22,7 @@ class CourseVideoInline(admin.TabularInline): # pylint: disable=C0111
class VideoAdmin(admin.ModelAdmin): # pylint: disable=C0111 class VideoAdmin(admin.ModelAdmin): # pylint: disable=C0111
list_display = ( list_display = (
'id', 'edx_video_id', 'client_video_id', 'duration' 'id', 'edx_video_id', 'client_video_id', 'duration', 'created', 'status'
) )
list_display_links = ('id', 'edx_video_id') list_display_links = ('id', 'edx_video_id')
search_fields = ('id', 'edx_video_id', 'client_video_id') search_fields = ('id', 'edx_video_id', 'client_video_id')
......
...@@ -51,6 +51,7 @@ class ValCannotCreateError(ValError): ...@@ -51,6 +51,7 @@ class ValCannotCreateError(ValError):
""" """
pass pass
def create_video(video_data): def create_video(video_data):
""" """
Called on to create Video objects in the database Called on to create Video objects in the database
...@@ -85,6 +86,7 @@ def create_video(video_data): ...@@ -85,6 +86,7 @@ def create_video(video_data):
else: else:
raise ValCannotCreateError(serializer.errors) raise ValCannotCreateError(serializer.errors)
def create_profile(profile_data): def create_profile(profile_data):
""" """
Used to create Profile objects in the database Used to create Profile objects in the database
...@@ -182,10 +184,19 @@ def get_video_info(edx_video_id, location=None): # pylint: disable=W0613 ...@@ -182,10 +184,19 @@ def get_video_info(edx_video_id, location=None): # pylint: disable=W0613
raise ValInternalError(error_message) raise ValInternalError(error_message)
return result.data # pylint: disable=E1101 return result.data # pylint: disable=E1101
def get_urls_for_profiles(edx_video_id, profiles): def get_urls_for_profiles(edx_video_id, profiles):
"""Returns a dict mapping profiles to URLs. """
Returns a dict mapping profiles to URLs.
If the profiles or video is not found, urls will be blank. If the profiles or video is not found, urls will be blank.
Args:
edx_video_id (str): id of the video
profiles (list): list of profiles we want to search for
Returns:
profiles_to_urls (dict): A dict containing the profile to url pair
""" """
profiles_to_urls = {profile: None for profile in profiles} profiles_to_urls = {profile: None for profile in profiles}
try: try:
...@@ -199,11 +210,23 @@ def get_urls_for_profiles(edx_video_id, profiles): ...@@ -199,11 +210,23 @@ def get_urls_for_profiles(edx_video_id, profiles):
return profiles_to_urls return profiles_to_urls
def get_url_for_profile(edx_video_id, profile): def get_url_for_profile(edx_video_id, profile):
""" """
Uses get_urls_for_profile to obtain a single profile Uses get_urls_for_profile to obtain a single profile
Args:
edx_video_id (str): id of the video
profile (str): a string of the profile we are searching
Returns:
A dict containing the profile to url. The return type is the same as
get_urls_for_profiles for consistency.
""" """
return get_urls_for_profiles(edx_video_id, [profile])[profile] url = get_urls_for_profiles(edx_video_id, [profile])[profile]
return {profile: url}
def get_videos_for_course(course_id): def get_videos_for_course(course_id):
""" """
...@@ -212,15 +235,24 @@ def get_videos_for_course(course_id): ...@@ -212,15 +235,24 @@ def get_videos_for_course(course_id):
videos = Video.objects.filter(courses__course_id=unicode(course_id)) videos = Video.objects.filter(courses__course_id=unicode(course_id))
return (VideoSerializer(video).data for video in videos) return (VideoSerializer(video).data for video in videos)
def get_videos_for_ids(edx_video_ids): def get_videos_for_ids(edx_video_ids):
""" """
Returns an iterator of videos that match the given list of ids Returns an iterator of videos that match the given list of ids
Args:
edx_video_ids (list)
Returns:
A generator expression that contains the videos found
""" """
videos = Video.objects.filter(edx_video_id__in=edx_video_ids) videos = Video.objects.filter(edx_video_id__in=edx_video_ids)
return (VideoSerializer(video).data for video in videos) return (VideoSerializer(video).data for video in videos)
def get_video_info_for_course_and_profile(course_id, profile_name): def get_video_info_for_course_and_profile(course_id, profile_name):
"""Returns a dict mapping profiles to URLs. """
Returns a dict mapping profiles to URLs.
If the profiles or video is not found, urls will be blank. If the profiles or video is not found, urls will be blank.
""" """
......
""" """
Django models for videos for Video Abstraction Layer (VAL) Django models for videos for Video Abstraction Layer (VAL)
When calling a serializers' .errors function for objects, there is an When calling a serializers' .errors field, there is a priority in which the
order in which the errors are returned. This may cause a partial return of errors errors are returned. This may cause a partial return of errors, starting with
the highest priority.
Example: Example:
class Profile(models.Model) class Profile(models.Model)
...@@ -32,7 +33,7 @@ from django.core.validators import MinValueValidator, RegexValidator ...@@ -32,7 +33,7 @@ from django.core.validators import MinValueValidator, RegexValidator
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
URL_REGEX = r'^[a-zA-Z0-9\-_\:]*$' URL_REGEX = r'^[a-zA-Z0-9\-_]*$'
class Profile(models.Model): class Profile(models.Model):
...@@ -67,6 +68,10 @@ class Video(models.Model): ...@@ -67,6 +68,10 @@ class Video(models.Model):
A video can have multiple formats. This model are the fields that represent A video can have multiple formats. This model are the fields that represent
the collection of those videos that do not change across formats. the collection of those videos that do not change across formats.
Attributes:
status: Used to keep track of the processing video as it goes through
the video pipeline, e.g., "Uploading", "File Complete"...
""" """
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
edx_video_id = models.CharField( edx_video_id = models.CharField(
...@@ -109,8 +114,8 @@ class CourseVideo(models.Model): ...@@ -109,8 +114,8 @@ class CourseVideo(models.Model):
""" """
Model for the course_id associated with the video content. Model for the course_id associated with the video content.
Every course-semester has a unique course_id. A video can be paired with multiple Every course-semester has a unique course_id. A video can be paired with
course_id's but each pair is unique together. multiple course_id's but each pair is unique together.
""" """
course_id = models.CharField(max_length=255) course_id = models.CharField(max_length=255)
video = models.ForeignKey(Video, related_name='courses') video = models.ForeignKey(Video, related_name='courses')
...@@ -148,6 +153,10 @@ SUBTITLE_FORMATS = ( ...@@ -148,6 +153,10 @@ SUBTITLE_FORMATS = (
class Subtitle(models.Model): class Subtitle(models.Model):
""" """
Subtitle for video Subtitle for video
Attributes:
video: the video that the subtitles are for
fmt: the format of the subttitles file
""" """
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
......
...@@ -30,4 +30,4 @@ class APIAuthTestCase(APITestCase): ...@@ -30,4 +30,4 @@ class APIAuthTestCase(APITestCase):
username = password = 'unauthorized' username = password = 'unauthorized'
else: else:
username, password = self.username, self.password username, password = self.username, self.password
print self.client.login(username=username, password=password) self.client.login(username=username, password=password)
...@@ -161,17 +161,6 @@ class GetVideoInfoTest(TestCase): ...@@ -161,17 +161,6 @@ class GetVideoInfoTest(TestCase):
videos = list(api.get_videos_for_course('unknown')) videos = list(api.get_videos_for_course('unknown'))
self.assertEqual(len(videos), 0) self.assertEqual(len(videos), 0)
def test_get_videos_for_ids(self):
"""
Tests retrieving videos for ids
"""
edx_video_id = constants.VIDEO_DICT_FISH['edx_video_id']
videos = list(api.get_videos_for_ids([edx_video_id]))
self.assertEqual(len(videos), 1)
self.assertEqual(videos[0]['edx_video_id'], edx_video_id)
videos = list(api.get_videos_for_ids(['unknown']))
self.assertEqual(len(videos), 0)
def test_no_such_video(self): def test_no_such_video(self):
""" """
Tests searching for a video that does not exist Tests searching for a video that does not exist
...@@ -212,6 +201,141 @@ class GetVideoInfoTest(TestCase): ...@@ -212,6 +201,141 @@ class GetVideoInfoTest(TestCase):
) )
class GetUrlsForProfileTest(TestCase):
"""
Tests the get_urls_for_profile(s) function in api.py
"""
def setUp(self):
"""
Creates EncodedVideo objects in database
"""
Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
video = Video.objects.create(**constants.VIDEO_DICT_FISH)
EncodedVideo.objects.create(
video=Video.objects.get(
edx_video_id=constants.VIDEO_DICT_FISH.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_FISH.get("edx_video_id")
),
profile=Profile.objects.get(profile_name="desktop"),
**constants.ENCODED_VIDEO_DICT_DESKTOP
)
self.course_id = 'test-course'
CourseVideo.objects.create(video=video, course_id=self.course_id)
def test_get_urls_for_profiles(self):
"""
Tests when the profiles to the video are found
"""
profiles = ["mobile", "desktop"]
edx_video_id = constants.VIDEO_DICT_FISH['edx_video_id']
urls = api.get_urls_for_profiles(edx_video_id, profiles)
self.assertEqual(len(urls), 2)
self.assertEqual(urls["mobile"], u'http://www.meowmix.com')
self.assertEqual(urls["desktop"], u'http://www.meowmagic.com')
def test_get_urls_for_profiles_no_video(self):
"""
Tests when there is no video found.
"""
urls = api.get_urls_for_profiles("not found", ["mobile"])
self.assertEqual(urls["mobile"], None)
def test_get_urls_for_profiles_no_profiles(self):
"""
Tests when the video is found, but not hte profiles.
"""
profiles = ["not", "found"]
edx_video_id = constants.VIDEO_DICT_FISH['edx_video_id']
urls = api.get_urls_for_profiles(edx_video_id, profiles)
self.assertEqual(len(urls), 2)
self.assertEqual(urls["not"], None)
self.assertEqual(urls["found"], None)
def test_get_url_for_profile(self):
"""
Tests get_url_for_profile
"""
profile = "mobile"
edx_video_id = constants.VIDEO_DICT_FISH['edx_video_id']
urls = api.get_url_for_profile(edx_video_id, profile)
self.assertEqual(len(urls), 1)
self.assertEqual(urls["mobile"], u'http://www.meowmix.com')
class GetVideosForIds(TestCase):
"""
Tests the get_videos_for_ids function in api.py
"""
def setUp(self):
"""
Creates EncodedVideo objects in database
"""
Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
video = Video.objects.create(**constants.VIDEO_DICT_FISH)
EncodedVideo.objects.create(
video=Video.objects.get(
edx_video_id=constants.VIDEO_DICT_FISH.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_FISH.get("edx_video_id")
),
profile=Profile.objects.get(profile_name="desktop"),
**constants.ENCODED_VIDEO_DICT_DESKTOP
)
self.course_id = 'test-course'
CourseVideo.objects.create(video=video, course_id=self.course_id)
def test_get_videos_for_id(self):
"""
Tests retrieving videos for id
"""
edx_video_id = constants.VIDEO_DICT_FISH['edx_video_id']
videos = list(api.get_videos_for_ids([edx_video_id]))
self.assertEqual(len(videos), 1)
self.assertEqual(videos[0]['edx_video_id'], edx_video_id)
videos = list(api.get_videos_for_ids(['unknown']))
self.assertEqual(len(videos), 0)
def test_get_videos_for_ids(self):
"""
Tests retrieving videos for ids
"""
Video.objects.create(**constants.VIDEO_DICT_DIFFERENT_ID_FISH)
EncodedVideo.objects.create(
video=Video.objects.get(
edx_video_id=constants.VIDEO_DICT_DIFFERENT_ID_FISH.get("edx_video_id")
),
profile=Profile.objects.get(profile_name="mobile"),
**constants.ENCODED_VIDEO_DICT_MOBILE
)
edx_video_id = constants.VIDEO_DICT_FISH['edx_video_id']
edx_video_id_2 = constants.VIDEO_DICT_DIFFERENT_ID_FISH['edx_video_id']
videos = list(api.get_videos_for_ids([edx_video_id, edx_video_id_2]))
self.assertEqual(len(videos), 2)
def test_get_videos_for_ids_duplicates(self):
"""
Tests retrieving videos for ids when there are duplicate ids
"""
edx_video_id = constants.VIDEO_DICT_FISH['edx_video_id']
videos = list(api.get_videos_for_ids([edx_video_id, edx_video_id]))
self.assertEqual(len(videos), 1)
class GetVideoInfoTestWithHttpCalls(APIAuthTestCase): class GetVideoInfoTestWithHttpCalls(APIAuthTestCase):
""" """
Tests for the get_info_video, using the HTTP requests to populate database Tests for the get_info_video, using the HTTP requests to populate database
......
"""
Tests for Video Abstraction Layer models
"""
...@@ -72,7 +72,8 @@ class SerializerTests(TestCase): ...@@ -72,7 +72,8 @@ 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"
)
def test_encoded_video_set_output(self): def test_encoded_video_set_output(self):
""" """
......
...@@ -14,17 +14,17 @@ urlpatterns = patterns( ...@@ -14,17 +14,17 @@ urlpatterns = patterns(
name="video-list" name="video-list"
), ),
url( url(
r'^videos/(?P<edx_video_id>[-\:\w]+)$', r'^videos/(?P<edx_video_id>[-\w]+)$',
views.VideoDetail.as_view(), views.VideoDetail.as_view(),
name="video-detail" name="video-detail"
), ),
url( url(
r'^videos/(?P<video__edx_video_id>[-\:\w]+)/(?P<language>[-_\w]+)$', r'^videos/(?P<video__edx_video_id>[-\w]+)/(?P<language>[-_\w]+)$',
views.SubtitleDetail.as_view(), views.SubtitleDetail.as_view(),
name="subtitle-detail" name="subtitle-detail"
), ),
url( url(
r'^videos/(?P<edx_video_id>[-\:\w]+)/(?P<language>[-_\w]+)/subtitle$', r'^videos/(?P<edx_video_id>[-\w]+)/(?P<language>[-_\w]+)/subtitle$',
views.get_subtitle, views.get_subtitle,
name="subtitle-content" name="subtitle-content"
), ),
......
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