diff --git a/common/lib/xmodule/xmodule/tests/test_video.py b/common/lib/xmodule/xmodule/tests/test_video.py
index ac1ab1d247..5076f39c79 100644
--- a/common/lib/xmodule/xmodule/tests/test_video.py
+++ b/common/lib/xmodule/xmodule/tests/test_video.py
@@ -264,6 +264,62 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'data': ''
})
+ def test_from_xml_double_quotes(self):
+ """
+ Make sure we can handle the double-quoted string format (which was used for exporting for
+ a few weeks).
+ """
+ module_system = DummySystem(load_error_modules=True)
+ xml_data ='''
+
+ '''
+ output = VideoDescriptor.from_xml(xml_data, module_system)
+ self.assert_attributes_equal(output, {
+ 'youtube_id_0_75': 'OEoXaMPEzf65',
+ 'youtube_id_1_0': 'OEoXaMPEzf10',
+ 'youtube_id_1_25': 'OEoXaMPEzf125',
+ 'youtube_id_1_5': 'OEoXaMPEzf15',
+ 'show_captions': False,
+ 'start_time': 0.0,
+ 'end_time': 0.0,
+ 'track': 'http://download_track',
+ 'source': 'http://download_video',
+ 'html5_sources': ["source_1", "source_2"],
+ 'data': ''
+ })
+
+ def test_from_xml_double_quote_concatenated_youtube(self):
+ module_system = DummySystem(load_error_modules=True)
+ xml_data = '''
+
+ '''
+ output = VideoDescriptor.from_xml(xml_data, module_system)
+ self.assert_attributes_equal(output, {
+ 'youtube_id_0_75': '',
+ 'youtube_id_1_0': 'p2Q6BrNhdh8',
+ 'youtube_id_1_25': '1EeWXzPdhSA',
+ 'youtube_id_1_5': '',
+ 'show_captions': True,
+ 'start_time': 0.0,
+ 'end_time': 0.0,
+ 'track': '',
+ 'source': '',
+ 'html5_sources': [],
+ 'data': ''
+ })
+
def test_old_video_format(self):
"""
Test backwards compatibility with VideoModule's XML format.
diff --git a/common/lib/xmodule/xmodule/video_module.py b/common/lib/xmodule/xmodule/video_module.py
index e0fae8cdbe..805ca48930 100644
--- a/common/lib/xmodule/xmodule/video_module.py
+++ b/common/lib/xmodule/xmodule/video_module.py
@@ -291,19 +291,19 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
XML-based courses.
"""
ret = {'0.75': '', '1.00': '', '1.25': '', '1.50': ''}
- if data == '':
- return ret
+
videos = data.split(',')
for video in videos:
pieces = video.split(':')
- # HACK
- # To elaborate somewhat: in many LMS tests, the keys for
- # Youtube IDs are inconsistent. Sometimes a particular
- # speed isn't present, and formatting is also inconsistent
- # ('1.0' versus '1.00'). So it's necessary to either do
- # something like this or update all the tests to work
- # properly.
- ret['%.2f' % float(pieces[0])] = pieces[1]
+ try:
+ speed = '%.2f' % float(pieces[0]) # normalize speed
+ # Handle the fact that youtube IDs got double-quoted for a period of time.
+ # Note: we pass in "VideoFields.youtube_id_1_0" so we deserialize as a String--
+ # it doesn't matter what the actual speed is for the purposes of deserializing.
+ youtube_id = VideoDescriptor._deserialize(VideoFields.youtube_id_1_0.name, pieces[1])
+ ret[speed] = youtube_id
+ except (ValueError, IndexError):
+ log.warning('Invalid YouTube ID: %s' % video)
return ret
@staticmethod
@@ -316,7 +316,6 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
model_data = {}
conversions = {
- 'show_captions': json.loads,
'start_time': VideoDescriptor._parse_time,
'end_time': VideoDescriptor._parse_time
}
@@ -355,10 +354,21 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
# Convert XML attrs into Python values.
if attr in conversions:
value = conversions[attr](value)
+ else:
+ # We export values with json.dumps (well, except for Strings, but
+ # for about a month we did it for Strings also).
+ value = VideoDescriptor._deserialize(attr, value)
model_data[attr] = value
return model_data
+ @classmethod
+ def _deserialize(cls, attr, value):
+ """
+ Handles deserializing values that may have been encoded with json.dumps.
+ """
+ return cls.get_map_for_field(attr).from_xml(value)
+
@staticmethod
def _parse_time(str_time):
"""Converts s in '12:34:45' format to seconds. If s is
diff --git a/common/lib/xmodule/xmodule/xml_module.py b/common/lib/xmodule/xmodule/xml_module.py
index 5b8d2c8aee..b0b7f300ed 100644
--- a/common/lib/xmodule/xmodule/xml_module.py
+++ b/common/lib/xmodule/xmodule/xml_module.py
@@ -167,6 +167,11 @@ class XmlDescriptor(XModuleDescriptor):
@classmethod
def get_map_for_field(cls, attr):
+ """
+ Returns a serialize/deserialize AttrMap for the given field of a class.
+
+ Searches through fields defined by cls to find one named attr.
+ """
for field in set(cls.fields + cls.lms.fields):
if field.name == attr:
from_xml = lambda val: deserialize_field(field, val)