Commit eb53ca0b by Nimisha Asthagiri

MA-110 VAL support for export-import.

parent f338a709
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
The internal API for VAL. This is not yet stable The internal API for VAL. This is not yet stable
""" """
import logging import logging
from lxml.etree import SubElement
from enum import Enum from enum import Enum
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
...@@ -365,3 +365,111 @@ def copy_course_videos(source_course_id, destination_course_id): ...@@ -365,3 +365,111 @@ def copy_course_videos(source_course_id, destination_course_id):
video=video, video=video,
course_id=destination_course_id course_id=destination_course_id
) )
def get_video_or_none(edx_video_id):
"""
Returns video object for the given edx_video_id if it exists, otherwise returns None.
"""
try:
return Video.objects.prefetch_related("encoded_videos", "courses").get(edx_video_id=edx_video_id)
except Video.DoesNotExist:
return None
def export_to_xml(xml, edx_video_id):
"""
Exports data about the given edx_video_id into the given xml object.
"""
video = get_video_or_none(edx_video_id=edx_video_id)
if not video:
return
video_el = SubElement(
xml,
'edx_video',
attrib={
'client_video_id': video.client_video_id,
'duration': unicode(video.duration),
'status': video.status,
# Note: we intentionally do not put course information in the
# exported data so we don't leak information about other courses.
# 'courses': [course.course_id for course in video.courses]
}
)
encoded_videos_el = SubElement(video_el, 'encoded_videos')
for encoded_video in video.encoded_videos.all():
SubElement(
encoded_videos_el,
'encoded_video',
{
name: unicode(getattr(encoded_video, name))
for name in ['profile', 'url', 'file_size', 'bitrate']
}
)
# Note: we are *not* exporting Subtitle data since it is not currently updated by VEDA or used by LMS/Studio.
def import_from_xml(xml, edx_video_id, course_id):
"""
Imports data from the given xml object about the given edx_video_id.
If the edx_video_id already exists,
then the existing data in the database is kept and not overridden.
However, any additional encoded_videos in the xml that are not in the database would be added.
The edx_video_id is associated with the given course, regardless of whether it already existed.
"""
video = get_video_or_none(edx_video_id=edx_video_id)
video_el = xml.find('edx_video')
if video_el:
if video:
logger.info(
"edx_video_id '%s' present in course '%s' not imported because it exists in VAL.",
edx_video_id,
course_id,
)
else:
# create the edx_video_id entry since it doesn't already exist
video = Video.objects.create(
edx_video_id=edx_video_id,
client_video_id=video_el.get('client_video_id'),
duration=video_el.get('duration'),
status=video_el.get('status'),
)
encoded_videos_el = video_el.find('encoded_videos')
if encoded_videos_el:
# We iterate through all the encoded videos regardless of whether a video entry already existed.
# If a video entry didn't exist, then the encoded_videos will be imported.
# If a video entry did exist, then only new encoded_videos will be imported.
for encoded_video_el in encoded_videos_el.iterfind('encoded_video'):
# If profile doesn't exist, create it.
# This may happen if the profile existed on the server/instance where the xml
# was exported, but doesn't exist in the local database.
# We create the profile entry with default values and log that it was created.
profile_name = encoded_video_el.get('profile')
profile, created = Profile.objects.get_or_create(profile_name=)
if created:
logger.info(
"New profile '%s' was created for edx_video_id '%s' for course '%s.",
profile_name,
edx_video_id,
course_id,
)
# only add the encoded video if one for its profile doesn't already exist.
if not EncodedVideo.objects.filter(profile__profile_name=profile).exists():
EncodedVideo.objects.create(
video=video,
profile=profile,
url=encoded_video_el.get('url'),
file_size=encoded_video_el.get('file_size'),
bitrate=encoded_video_el.get('bitrate'),
)
# Associate the (existing or new) video with the given course_id.
CourseVideo.objects.get_or_create(
video=video,
course_id=course_id
)
...@@ -4,6 +4,7 @@ Tests for the API for Video Abstraction Layer ...@@ -4,6 +4,7 @@ Tests for the API for Video Abstraction Layer
""" """
import mock import mock
from lxml import etree
from django.test import TestCase from django.test import TestCase
from django.db import DatabaseError from django.db import DatabaseError
...@@ -674,3 +675,37 @@ class TestCopyCourse(TestCase): ...@@ -674,3 +675,37 @@ class TestCopyCourse(TestCase):
self.assertLessEqual(set(original_videos), set(copied_videos)) self.assertLessEqual(set(original_videos), set(copied_videos))
self.assertEqual(len(copied_videos), 3) self.assertEqual(len(copied_videos), 3)
class ExportImportTest(TestCase):
"""
Tests the export import functions in api.py.
"""
def setUp(self):
Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
video = Video.objects.create(**constants.VIDEO_DICT_FISH)
self.edx_video_id = constants.VIDEO_DICT_FISH.get("edx_video_id")
EncodedVideo.objects.create(
video=Video.objects.get(self.edx_video_id),
profile=Profile.objects.get(profile_name="mobile"),
**constants.ENCODED_VIDEO_DICT_MOBILE
)
EncodedVideo.objects.create(
video=Video.objects.get(self.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 assertXmlEqual(self, expected_xml_string, xml):
parser = etree.XMLParser(remove_blank_text=True)
expected = etree.XML(expected_xml_string, parser=parser)
for attr in ['tag', 'attrib', 'text', 'tail']:
self.assertEqual(getattr(expected, attr), getattr(xml, attr))
for left, right in zip(expected, xml):
self.assertXmlEqual(left, right)
def test_success(self):
xml = etree.Element('val_test')
api.export_to_xml(xml, self.edx_video_id)
...@@ -4,3 +4,4 @@ mock==1.0.1 ...@@ -4,3 +4,4 @@ mock==1.0.1
django-debug-toolbar==1.2.1 django-debug-toolbar==1.2.1
pylint==1.3.0 pylint==1.3.0
ddt==0.8.0 ddt==0.8.0
lxml==3.3.6
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