diff --git a/common/lib/xmodule/xmodule/tests/test_video.py b/common/lib/xmodule/xmodule/tests/test_video.py
index 4a13d565cca4..a8810a1238d1 100644
--- a/common/lib/xmodule/xmodule/tests/test_video.py
+++ b/common/lib/xmodule/xmodule/tests/test_video.py
@@ -263,6 +263,62 @@ def test_from_xml_no_attributes(self):
'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 dd408a6f745a..a87d71246aa1 100644
--- a/common/lib/xmodule/xmodule/video_module.py
+++ b/common/lib/xmodule/xmodule/video_module.py
@@ -293,7 +293,10 @@ def _parse_youtube(data):
pieces = video.split(':')
try:
speed = '%.2f' % float(pieces[0]) # normalize speed
- youtube_id = pieces[1]
+ # 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)
@@ -310,7 +313,6 @@ def _parse_video_xml(xml_data):
model_data = {}
conversions = {
- 'show_captions': json.loads,
'start_time': VideoDescriptor._parse_time,
'end_time': VideoDescriptor._parse_time
}
@@ -349,6 +351,11 @@ def _parse_video_xml(xml_data):
# 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
@@ -367,6 +374,12 @@ def _parse_time(str_time):
seconds=obj_time.tm_sec
).total_seconds()
+ @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)
def _create_youtube_string(module):
"""
diff --git a/common/lib/xmodule/xmodule/xml_module.py b/common/lib/xmodule/xmodule/xml_module.py
index 5b8d2c8aee0c..b0b7f300ed79 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)