Commit 86d952bf by cahrens

Change to putting serialize/de-serialize directly in xml_module.

parent 17c9c104
...@@ -19,6 +19,24 @@ class ChecklistTestCase(CourseTestCase): ...@@ -19,6 +19,24 @@ class ChecklistTestCase(CourseTestCase):
modulestore = get_modulestore(self.course.location) modulestore = get_modulestore(self.course.location)
return modulestore.get_item(self.course.location).checklists return modulestore.get_item(self.course.location).checklists
def compare_checklists(self, persisted, request):
"""
Handles url expansion as possible difference and descends into guts
:param persisted:
:param request:
"""
self.assertEqual(persisted['short_description'], request['short_description'])
compare_urls = (persisted.get('action_urls_expanded') == request.get('action_urls_expanded'))
for pers, req in zip(persisted['items'], request['items']):
self.assertEqual(pers['short_description'], req['short_description'])
self.assertEqual(pers['long_description'], req['long_description'])
self.assertEqual(pers['is_checked'], req['is_checked'])
if compare_urls:
self.assertEqual(pers['action_url'], req['action_url'])
self.assertEqual(pers['action_text'], req['action_text'])
self.assertEqual(pers['action_external'], req['action_external'])
def test_get_checklists(self): def test_get_checklists(self):
""" Tests the get checklists method. """ """ Tests the get checklists method. """
checklists_url = get_url_reverse('Checklists', self.course) checklists_url = get_url_reverse('Checklists', self.course)
...@@ -31,9 +49,9 @@ class ChecklistTestCase(CourseTestCase): ...@@ -31,9 +49,9 @@ class ChecklistTestCase(CourseTestCase):
self.course.checklists = None self.course.checklists = None
modulestore = get_modulestore(self.course.location) modulestore = get_modulestore(self.course.location)
modulestore.update_metadata(self.course.location, own_metadata(self.course)) modulestore.update_metadata(self.course.location, own_metadata(self.course))
self.assertEquals(self.get_persisted_checklists(), None) self.assertEqual(self.get_persisted_checklists(), None)
response = self.client.get(checklists_url) response = self.client.get(checklists_url)
self.assertEquals(payload, response.content) self.assertEqual(payload, response.content)
def test_update_checklists_no_index(self): def test_update_checklists_no_index(self):
""" No checklist index, should return all of them. """ """ No checklist index, should return all of them. """
...@@ -43,7 +61,8 @@ class ChecklistTestCase(CourseTestCase): ...@@ -43,7 +61,8 @@ class ChecklistTestCase(CourseTestCase):
'name': self.course.location.name}) 'name': self.course.location.name})
returned_checklists = json.loads(self.client.get(update_url).content) returned_checklists = json.loads(self.client.get(update_url).content)
self.assertListEqual(self.get_persisted_checklists(), returned_checklists) for pay, resp in zip(self.get_persisted_checklists(), returned_checklists):
self.compare_checklists(pay, resp)
def test_update_checklists_index_ignored_on_get(self): def test_update_checklists_index_ignored_on_get(self):
""" Checklist index ignored on get. """ """ Checklist index ignored on get. """
...@@ -53,7 +72,8 @@ class ChecklistTestCase(CourseTestCase): ...@@ -53,7 +72,8 @@ class ChecklistTestCase(CourseTestCase):
'checklist_index': 1}) 'checklist_index': 1})
returned_checklists = json.loads(self.client.get(update_url).content) returned_checklists = json.loads(self.client.get(update_url).content)
self.assertListEqual(self.get_persisted_checklists(), returned_checklists) for pay, resp in zip(self.get_persisted_checklists(), returned_checklists):
self.compare_checklists(pay, resp)
def test_update_checklists_post_no_index(self): def test_update_checklists_post_no_index(self):
""" No checklist index, will error on post. """ """ No checklist index, will error on post. """
...@@ -78,13 +98,18 @@ class ChecklistTestCase(CourseTestCase): ...@@ -78,13 +98,18 @@ class ChecklistTestCase(CourseTestCase):
'course': self.course.location.course, 'course': self.course.location.course,
'name': self.course.location.name, 'name': self.course.location.name,
'checklist_index': 2}) 'checklist_index': 2})
def get_first_item(checklist):
return checklist['items'][0]
payload = self.course.checklists[2] payload = self.course.checklists[2]
self.assertFalse(payload.get('is_checked')) self.assertFalse(get_first_item(payload).get('is_checked'))
payload['is_checked'] = True get_first_item(payload)['is_checked'] = True
returned_checklist = json.loads(self.client.post(update_url, json.dumps(payload), "application/json").content) returned_checklist = json.loads(self.client.post(update_url, json.dumps(payload), "application/json").content)
self.assertTrue(returned_checklist.get('is_checked')) self.assertTrue(get_first_item(returned_checklist).get('is_checked'))
self.assertEqual(self.get_persisted_checklists()[2], returned_checklist) pers = self.get_persisted_checklists()
self.compare_checklists(pers[2], returned_checklist)
def test_update_checklists_delete_unsupported(self): def test_update_checklists_delete_unsupported(self):
""" Delete operation is not supported. """ """ Delete operation is not supported. """
...@@ -93,4 +118,4 @@ class ChecklistTestCase(CourseTestCase): ...@@ -93,4 +118,4 @@ class ChecklistTestCase(CourseTestCase):
'name': self.course.location.name, 'name': self.course.location.name,
'checklist_index': 100}) 'checklist_index': 100})
response = self.client.delete(update_url) response = self.client.delete(update_url)
self.assertContains(response, 'Unsupported request', status_code=400) self.assertContains(response, 'Unsupported request', status_code=400)
\ No newline at end of file
...@@ -78,22 +78,6 @@ class DateTest(unittest.TestCase): ...@@ -78,22 +78,6 @@ class DateTest(unittest.TestCase):
DateTest.date.from_json("2012-12-31T23:00:01-01:00")), DateTest.date.from_json("2012-12-31T23:00:01-01:00")),
"2013-01-01T00:00:01Z") "2013-01-01T00:00:01Z")
def test_serialize(self):
self.assertEqual(
DateTest.date.serialize("2012-12-31T23:59:59Z"),
'"2012-12-31T23:59:59Z"'
)
def test_deserialize(self):
self.assertEqual(
'2012-12-31T23:59:59Z',
DateTest.date.deserialize("2012-12-31T23:59:59Z"),
)
self.assertEqual(
'2012-12-31T23:59:59Z',
DateTest.date.deserialize('"2012-12-31T23:59:59Z"'),
)
class TimedeltaTest(unittest.TestCase): class TimedeltaTest(unittest.TestCase):
delta = Timedelta() delta = Timedelta()
...@@ -114,19 +98,3 @@ class TimedeltaTest(unittest.TestCase): ...@@ -114,19 +98,3 @@ class TimedeltaTest(unittest.TestCase):
'1 days 46799 seconds', '1 days 46799 seconds',
TimedeltaTest.delta.to_json(datetime.timedelta(days=1, hours=12, minutes=59, seconds=59)) TimedeltaTest.delta.to_json(datetime.timedelta(days=1, hours=12, minutes=59, seconds=59))
) )
def test_serialize(self):
self.assertEqual(
TimedeltaTest.delta.serialize('1 day 12 hours 59 minutes 59 seconds'),
'"1 day 12 hours 59 minutes 59 seconds"'
)
def test_deserialize(self):
self.assertEqual(
'1 day 12 hours 59 minutes 59 seconds',
TimedeltaTest.delta.deserialize('1 day 12 hours 59 minutes 59 seconds')
)
self.assertEqual(
'1 day 12 hours 59 minutes 59 seconds',
TimedeltaTest.delta.deserialize('"1 day 12 hours 59 minutes 59 seconds"')
)
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
#pylint: disable=C0111 #pylint: disable=C0111
from xmodule.x_module import XModuleFields from xmodule.x_module import XModuleFields
from xblock.core import Scope, String, Object, Boolean, Integer, Float from xblock.core import Scope, String, Object, Boolean, Integer, Float, Any, List
from xmodule.fields import Date from xmodule.fields import Date, Timedelta
from xmodule.xml_module import XmlDescriptor from xmodule.xml_module import XmlDescriptor, serialize_field, deserialize_field
import unittest import unittest
from .import test_system from .import test_system
from nose.tools import assert_equals
from mock import Mock from mock import Mock
...@@ -171,3 +172,186 @@ class EditableMetadataFieldsTest(unittest.TestCase): ...@@ -171,3 +172,186 @@ class EditableMetadataFieldsTest(unittest.TestCase):
self.assertEqual(explicitly_set, test_field['explicitly_set']) self.assertEqual(explicitly_set, test_field['explicitly_set'])
self.assertEqual(inheritable, test_field['inheritable']) self.assertEqual(inheritable, test_field['inheritable'])
def assertSerializeEqual(expected, arg):
"""
Asserts the result of serialize_field.
"""
assert_equals(expected, serialize_field(arg))
def assertDeserializeEqual(field, expected, arg):
"""
Asserts the result of deserialize_field.
"""
assert_equals(expected, deserialize_field(field, arg))
class TestSerializeInteger(unittest.TestCase):
""" Tests serialize/deserialize as related to Integer type. """
def test_serialize(self):
assertSerializeEqual('-2', -2)
assertSerializeEqual('"2"', '2')
assertSerializeEqual('null', None)
def test_deserialize(self):
assertDeserializeEqual(Integer(), None, 'null')
assertDeserializeEqual(Integer(), -2, '-2')
assertDeserializeEqual(Integer(), "450", '"450"')
# False can be parsed as a int (converts to 0)
assertDeserializeEqual(Integer(), False, 'false')
# True can be parsed as a int (converts to 1)
assertDeserializeEqual(Integer(), True, 'true')
def test_deserialize_unsupported_types(self):
assertDeserializeEqual(Integer(), '[3]', '[3]')
self.assertRaises(TypeError, deserialize_field, None)
class FloatTest(unittest.TestCase):
""" Tests serialize/deserialize as related to Float type. """
def test_serialize(self):
assertSerializeEqual('-2', -2)
assertSerializeEqual('"2"', '2')
assertSerializeEqual('-3.41', -3.41)
assertSerializeEqual('"2.589"', '2.589')
assertSerializeEqual('null', None)
def test_deserialize(self):
assertDeserializeEqual(Float(), None, 'null')
assertDeserializeEqual(Float(), -2, '-2')
assertDeserializeEqual(Float(), "450", '"450"')
assertDeserializeEqual(Float(), -2.78, '-2.78')
assertDeserializeEqual(Float(), "0.45", '"0.45"')
# False can be parsed as a float (converts to 0)
assertDeserializeEqual(Float(), False, 'false')
# True can be parsed as a float (converts to 1)
assertDeserializeEqual(Float(), True, 'true')
def test_deserialize_unsupported_types(self):
assertDeserializeEqual(Float(), '[3]', '[3]')
self.assertRaises(TypeError, deserialize_field, None)
class BooleanTest(unittest.TestCase):
""" Tests serialize/deserialize as related to Boolean type. """
def test_serialize(self):
assertSerializeEqual('false', False)
assertSerializeEqual('"false"', 'false')
assertSerializeEqual('"fAlse"', 'fAlse')
assertSerializeEqual('null', None)
def test_deserialize(self):
# json.loads converts the value to Python bool
assertDeserializeEqual(Boolean(), False, 'false')
assertDeserializeEqual(Boolean(), True, 'true')
# json.loads fails, string value is returned.
assertDeserializeEqual(Boolean(), 'False', 'False')
assertDeserializeEqual(Boolean(), 'True', 'True')
# json.loads deserializes 'null' to None
assertDeserializeEqual(Boolean(), None, 'null')
# json.loads deserializes as a string
assertDeserializeEqual(Boolean(), 'false', '"false"')
assertDeserializeEqual(Boolean(), 'fAlse', '"fAlse"')
assertDeserializeEqual(Boolean(), "TruE", '"TruE"')
def test_deserialize_unsupported_types(self):
self.assertRaises(TypeError, deserialize_field, None)
class StringTest(unittest.TestCase):
""" Tests serialize/deserialize as related to String type. """
def test_serialize(self):
assertSerializeEqual('"hat box"', 'hat box')
assertSerializeEqual('null', None)
def test_deserialize(self):
assertDeserializeEqual(String(), 'hAlf', '"hAlf"')
assertDeserializeEqual(String(), 'false', '"false"')
assertDeserializeEqual(String(), 'single quote', 'single quote')
assertDeserializeEqual(String(), None, 'null')
def test_deserialize_unsupported_types(self):
assertDeserializeEqual(String(), '3.4', '3.4')
assertDeserializeEqual(String(), 'false', 'false')
assertDeserializeEqual(String(), '2', '2')
assertDeserializeEqual(String(), '[3]', '[3]')
self.assertRaises(TypeError, deserialize_field, None)
class AnyTest(unittest.TestCase):
""" Tests serialize/deserialize as related to Any type. """
def test_serialize(self):
assertSerializeEqual('{"bar": "hat", "frog": "green"}', {'bar': 'hat', 'frog' : 'green'})
assertSerializeEqual('[3.5, 5.6]', [3.5, 5.6])
assertSerializeEqual('"hat box"', 'hat box')
assertSerializeEqual('null', None)
def test_deserialize(self):
assertDeserializeEqual(Any(), 'hAlf', '"hAlf"')
assertDeserializeEqual(Any(), 'false', '"false"')
assertDeserializeEqual(Any(), None, 'null')
assertDeserializeEqual(Any(), {'bar': 'hat', 'frog' : 'green'}, '{"bar": "hat", "frog": "green"}')
assertDeserializeEqual(Any(), [3.5, 5.6], '[3.5, 5.6]')
assertDeserializeEqual(Any(), '[', '[')
assertDeserializeEqual(Any(), False, 'false')
assertDeserializeEqual(Any(), 3.4, '3.4')
def test_deserialize_unsupported_types(self):
self.assertRaises(TypeError, deserialize_field, None)
class ListTest(unittest.TestCase):
""" Tests serialize/deserialize as related to Any type. """
def test_serialize(self):
assertSerializeEqual('["foo", "bar"]', ['foo', 'bar'])
assertSerializeEqual('[3.5, 5.6]', [3.5, 5.6])
assertSerializeEqual('null', None)
def test_deserialize(self):
assertDeserializeEqual(List(), ['foo', 'bar'], '["foo", "bar"]')
assertDeserializeEqual(List(), [3.5, 5.6], '[3.5, 5.6]')
assertDeserializeEqual(List(), [], '[]')
assertDeserializeEqual(List(), None, 'null')
def test_deserialize_unsupported_types(self):
assertDeserializeEqual(List(), '3.4', '3.4')
assertDeserializeEqual(List(), 'false', 'false')
assertDeserializeEqual(List(), '2', '2')
self.assertRaises(TypeError, deserialize_field, None)
class DateTest(unittest.TestCase):
""" Tests serialize/deserialize as related to Date type. """
def test_serialize(self):
assertSerializeEqual('"2012-12-31T23:59:59Z"', "2012-12-31T23:59:59Z")
def test_deserialize(self):
assertDeserializeEqual(Date(), '2012-12-31T23:59:59Z', "2012-12-31T23:59:59Z")
assertDeserializeEqual(Date(), '2012-12-31T23:59:59Z', '"2012-12-31T23:59:59Z"')
class TimedeltaTest(unittest.TestCase):
def test_serialize(self):
assertSerializeEqual('"1 day 12 hours 59 minutes 59 seconds"',
"1 day 12 hours 59 minutes 59 seconds")
def test_deserialize(self):
assertDeserializeEqual(Timedelta(), '1 day 12 hours 59 minutes 59 seconds',
'1 day 12 hours 59 minutes 59 seconds')
assertDeserializeEqual(Timedelta(), '1 day 12 hours 59 minutes 59 seconds',
'"1 day 12 hours 59 minutes 59 seconds"')
...@@ -79,6 +79,42 @@ class AttrMap(_AttrMapBase): ...@@ -79,6 +79,42 @@ class AttrMap(_AttrMapBase):
return _AttrMapBase.__new__(_cls, from_xml, to_xml) return _AttrMapBase.__new__(_cls, from_xml, to_xml)
def serialize_field(value):
"""
Return a string version of the value (where value is the JSON-formatted, internally stored value).
By default, this is the result of calling json.dumps on the input value.
"""
return json.dumps(value)
def deserialize_field(field, value):
"""
Deserialize the string version to the value stored internally.
Note that this is not the same as the value returned by from_json, as model types typically store
their value internally as JSON. By default, this method will return the result of calling json.loads
on the supplied value, unless json.loads throws a TypeError, or the type of the value returned by json.loads
is not supported for this class (see 'is_type_supported'). In either of those cases, this method returns
the input value.
"""
try:
deserialized = json.loads(value)
if deserialized is None:
return deserialized
try:
field.from_json(deserialized)
return deserialized
except (ValueError, TypeError):
# Support older serialized forms by simply returning the String representation
return value
except (ValueError, TypeError):
# Support older serialized version, which was just the String (not the result of json.dumps).
return value
class XmlDescriptor(XModuleDescriptor): class XmlDescriptor(XModuleDescriptor):
""" """
Mixin class for standardized parsing of from xml Mixin class for standardized parsing of from xml
...@@ -124,8 +160,8 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -124,8 +160,8 @@ class XmlDescriptor(XModuleDescriptor):
def get_map_for_field(cls, attr): def get_map_for_field(cls, attr):
for field in set(cls.fields + cls.lms.fields): for field in set(cls.fields + cls.lms.fields):
if field.name == attr: if field.name == attr:
from_xml = lambda val: field.deserialize(val) from_xml = lambda val: deserialize_field(field, val)
to_xml = lambda val : field.serialize(val) to_xml = lambda val : serialize_field(val)
return AttrMap(from_xml, to_xml) return AttrMap(from_xml, to_xml)
return AttrMap() return AttrMap()
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
-e git://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk -e git://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
# Our libraries: # Our libraries:
-e git+https://github.com/edx/XBlock.git@4c5d2397#egg=XBlock
-e git+https://github.com/edx/XBlock.git@2abd2937e134e2f78dfa118d6cfd56ec510fd3a4#egg=XBlock
-e git+https://github.com/edx/codejail.git@5fb5fa0#egg=codejail -e git+https://github.com/edx/codejail.git@5fb5fa0#egg=codejail
-e git+https://github.com/edx/diff-cover.git@v0.1.0#egg=diff_cover -e git+https://github.com/edx/diff-cover.git@v0.1.0#egg=diff_cover
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