From 6f7c73cd4fab23d65bbb03f278abc319bc047e25 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Tue, 18 Mar 2014 11:49:37 -0400 Subject: [PATCH] Fix split_test import and export. LMS-2405 --- common/lib/xmodule/xmodule/course_module.py | 6 ++-- .../lib/xmodule/xmodule/split_test_module.py | 33 +++++++++++++++++-- .../xmodule/tests/test_split_module.py | 29 +++++++++++++++- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 78ca359de6..4b9bcc0002 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -172,12 +172,12 @@ class CourseFields(object): textbooks = TextbookList(help="List of pairs of (title, url) for textbooks used in this course", default=[], scope=Scope.content) - # This field is intended for Studio to update, not to be exposed directly via - # advanced_settings. + # This is should be scoped to content, but since it's defined in the policy + # file, it is currently scoped to settings. user_partitions = UserPartitionList( help="List of user partitions of this course into groups, used e.g. for experiments", default=[], - scope=Scope.content + scope=Scope.settings ) wiki_slug = String(help="Slug that points to the wiki for this course", scope=Scope.content) diff --git a/common/lib/xmodule/xmodule/split_test_module.py b/common/lib/xmodule/xmodule/split_test_module.py index 27c3e56217..cf088a255a 100644 --- a/common/lib/xmodule/xmodule/split_test_module.py +++ b/common/lib/xmodule/xmodule/split_test_module.py @@ -3,6 +3,7 @@ Module for running content split tests """ import logging +import json from webob import Response from xmodule.progress import Progress @@ -251,12 +252,38 @@ class SplitTestDescriptor(SplitTestFields, SequenceDescriptor): def definition_to_xml(self, resource_fs): xml_object = etree.Element('split_test') - # TODO: also save the experiment id and the condition map + xml_object.set('group_id_to_child', json.dumps(self.group_id_to_child)) + xml_object.set('user_partition_id', str(self.user_partition_id)) for child in self.get_children(): - xml_object.append( - etree.fromstring(child.export_to_xml(resource_fs))) + self.runtime.add_block_as_child_node(child, xml_object) return xml_object + @classmethod + def definition_from_xml(cls, xml_object, system): + children = [] + raw_group_id_to_child = xml_object.attrib.get('group_id_to_child', None) + user_partition_id = xml_object.attrib.get('user_partition_id', None) + try: + group_id_to_child = json.loads(raw_group_id_to_child) + except ValueError: + msg = "group_id_to_child is not valid json" + log.exception(msg) + system.error_tracker(msg) + + for child in xml_object: + try: + descriptor = system.process_xml(etree.tostring(child)) + children.append(descriptor.scope_ids.usage_id) + except Exception: + msg = "Unable to load child when parsing split_test module." + log.exception(msg) + system.error_tracker(msg) + + return ({ + 'group_id_to_child': group_id_to_child, + 'user_partition_id': user_partition_id + }, children) + def has_dynamic_children(self): """ Grading needs to know that only one of the children is actually "real". This diff --git a/common/lib/xmodule/xmodule/tests/test_split_module.py b/common/lib/xmodule/xmodule/tests/test_split_module.py index 48a640382e..05223800f1 100644 --- a/common/lib/xmodule/xmodule/tests/test_split_module.py +++ b/common/lib/xmodule/xmodule/tests/test_split_module.py @@ -2,11 +2,14 @@ Tests for the Split Testing Module """ import ddt -from mock import Mock +import lxml +from mock import Mock, patch +from fs.memoryfs import MemoryFS from xmodule.tests.xml import factories as xml from xmodule.tests.xml import XModuleXmlImportTest from xmodule.tests import get_test_system +from xmodule.split_test_module import SplitTestDescriptor from xmodule.partitions.partitions import Group, UserPartition from xmodule.partitions.test_partitions import StaticPartitionService, MemoryUserTagsService @@ -52,6 +55,7 @@ class SplitTestModuleTest(XModuleXmlImportTest): self.module_system.get_module = get_module self.module_system.descriptor_system = self.course.runtime + self.course.runtime.export_fs = MemoryFS() self.tags_service = MemoryUserTagsService() self.module_system._services['user_tags'] = self.tags_service # pylint: disable=protected-access @@ -120,3 +124,26 @@ class SplitTestModuleTest(XModuleXmlImportTest): # So, we check that we get the same url_name when we call on the url_name twice. # We run the test ten times so that, if our storage is failing, we'll be most likely to notice it. self.assertEquals(self.split_test_module.child_descriptor.url_name, self.split_test_module.child_descriptor.url_name) + + # Patch the definition_to_xml for the html children. + @patch('xmodule.html_module.HtmlDescriptor.definition_to_xml') + def test_export_import_round_trip(self, def_to_xml): + # The HtmlDescriptor definition_to_xml tries to write to the filesystem + # before returning an xml object. Patch this to just return the xml. + def_to_xml.return_value = lxml.etree.Element('html') + + # Mock out the process_xml + # Expect it to return a child descriptor for the SplitTestDescriptor when called. + self.module_system.process_xml = Mock() + + # Write out the xml. + xml_obj = self.split_test_module.definition_to_xml(MemoryFS()) + + self.assertEquals(xml_obj.get('user_partition_id'), '0') + self.assertIsNotNone(xml_obj.get('group_id_to_child')) + + # Read the xml back in. + fields, children = SplitTestDescriptor.definition_from_xml(xml_obj, self.module_system) + self.assertEquals(fields.get('user_partition_id'), '0') + self.assertIsNotNone(fields.get('group_id_to_child')) + self.assertEquals(len(children), 2)