Commit 37c873db by Don Mitchell

Serialize using to_json on fields

parent ee9fd348
......@@ -568,10 +568,10 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
:param category: the xblock category
:param fields: the dictionary of {fieldname: value}
"""
result = collections.defaultdict(dict)
if fields is None:
return {}
return result
cls = self.mixologist.mix(XBlock.load_class(category, select=prefer_xmodules))
result = collections.defaultdict(dict)
for field_name, value in fields.iteritems():
field = getattr(cls, field_name)
result[field.scope][field_name] = value
......
......@@ -53,7 +53,7 @@ class SplitMigrator(object):
new_run = source_course_key.run
new_course_key = CourseLocator(new_org, new_course, new_run, branch=ModuleStoreEnum.BranchName.published)
new_fields = self._get_json_fields_translate_references(original_course, new_course_key, None)
new_fields = self._get_fields_translate_references(original_course, new_course_key, None)
if fields:
new_fields.update(fields)
new_course = self.split_modulestore.create_course(
......@@ -92,7 +92,7 @@ class SplitMigrator(object):
course_version_locator,
module.location.block_type,
block_id=module.location.block_id,
fields=self._get_json_fields_translate_references(
fields=self._get_fields_translate_references(
module, course_version_locator, new_course.location.block_id
),
continue_version=True
......@@ -127,7 +127,7 @@ class SplitMigrator(object):
if field.is_set_on(split_module) and not module.fields[name].is_set_on(module):
field.delete_from(split_module)
for field, value in self._get_fields_translate_references(
module, new_draft_course_loc, published_course_usage_key.block_id
module, new_draft_course_loc, published_course_usage_key.block_id, field_names=False
).iteritems():
field.write_to(split_module, value)
......@@ -138,7 +138,7 @@ class SplitMigrator(object):
user_id, new_draft_course_loc,
new_locator.block_type,
block_id=new_locator.block_id,
fields=self._get_json_fields_translate_references(
fields=self._get_fields_translate_references(
module, new_draft_course_loc, published_course_usage_key.block_id
)
)
......@@ -173,43 +173,13 @@ class SplitMigrator(object):
new_parent.children.insert(new_parent_cursor, new_locator)
new_parent = self.split_modulestore.update_item(new_parent, user_id)
def _get_json_fields_translate_references(self, xblock, new_course_key, course_block_id):
"""
Return the json repr for explicitly set fields but convert all references to their Locators
"""
def get_translation(location):
"""
Convert the location
"""
return new_course_key.make_usage_key(
location.category,
location.block_id if location.category != 'course' else course_block_id
)
result = {}
for field_name, field in xblock.fields.iteritems():
if field.is_set_on(xblock):
field_value = getattr(xblock, field_name)
if isinstance(field, Reference) and field_value is not None:
result[field_name] = get_translation(field_value)
elif isinstance(field, ReferenceList):
result[field_name] = [
get_translation(ele) for ele in field_value
]
elif isinstance(field, ReferenceValueDict):
result[field_name] = {
key: get_translation(subvalue)
for key, subvalue in field_value.iteritems()
}
else:
result[field_name] = field.read_json(xblock)
return result
def _get_fields_translate_references(self, xblock, new_course_key, course_block_id):
def _get_fields_translate_references(self, xblock, new_course_key, course_block_id, field_names=True):
"""
Return a dictionary of field: value pairs for explicitly set fields
but convert all references to their BlockUsageLocators
Args:
field_names: if Truthy, the dictionary keys are the field names. If falsey, the keys are the
field objects.
"""
def get_translation(location):
"""
......@@ -223,19 +193,20 @@ class SplitMigrator(object):
result = {}
for field_name, field in xblock.fields.iteritems():
if field.is_set_on(xblock):
field_value = getattr(xblock, field_name)
field_value = field.read_from(xblock)
field_key = field_name if field_names else field
if isinstance(field, Reference) and field_value is not None:
result[field] = get_translation(field_value)
result[field_key] = get_translation(field_value)
elif isinstance(field, ReferenceList):
result[field] = [
result[field_key] = [
get_translation(ele) for ele in field_value
]
elif isinstance(field, ReferenceValueDict):
result[field] = {
result[field_key] = {
key: get_translation(subvalue)
for key, subvalue in field_value.iteritems()
}
else:
result[field] = field_value
result[field_key] = field_value
return result
......@@ -77,6 +77,8 @@ from .caching_descriptor_system import CachingDescriptorSystem
from xmodule.modulestore.split_mongo.mongo_connection import MongoConnection
from xmodule.error_module import ErrorDescriptor
from xmodule.modulestore.split_mongo import encode_key_for_mongo, decode_key_from_mongo
import types
from _collections import defaultdict
log = logging.getLogger(__name__)
......@@ -1006,7 +1008,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
raise DuplicateCourseError(locator, index)
partitioned_fields = self.partition_fields_by_scope(root_category, fields)
block_fields = partitioned_fields.setdefault(Scope.settings, {})
block_fields = partitioned_fields[Scope.settings]
if Scope.children in partitioned_fields:
block_fields.update(partitioned_fields[Scope.children])
definition_fields = self._serialize_fields(root_category, partitioned_fields.get(Scope.content, {}))
......@@ -1106,14 +1108,15 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
original_structure = self._lookup_course(descriptor.location)['structure']
index_entry = self._get_index_if_valid(descriptor.location, force)
definition_fields = descriptor.get_explicitly_set_fields_by_scope(Scope.content)
partitioned_fields = self.partition_xblock_fields_by_scope(descriptor)
definition_fields = partitioned_fields[Scope.content]
descriptor.definition_locator, is_updated = self.update_definition_from_data(
descriptor.definition_locator, definition_fields, user_id
)
original_entry = self._get_block_from_structure(original_structure, descriptor.location.block_id)
# check metadata
settings = descriptor.get_explicitly_set_fields_by_scope(Scope.settings)
settings = partitioned_fields[Scope.settings]
settings = self._serialize_fields(descriptor.category, settings)
if not is_updated:
is_updated = self._compare_settings(settings, original_entry['fields'])
......@@ -1235,7 +1238,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
def _persist_subdag(self, xblock, user_id, structure_blocks, new_id):
# persist the definition if persisted != passed
new_def_data = self._serialize_fields(xblock.category, xblock.get_explicitly_set_fields_by_scope(Scope.content))
partitioned_fields = self.partition_xblock_fields_by_scope(xblock)
new_def_data = self._serialize_fields(xblock.category, partitioned_fields[Scope.content])
is_updated = False
if xblock.definition_locator is None or isinstance(xblock.definition_locator.definition_id, LocalId):
xblock.definition_locator = self.create_definition_from_data(
......@@ -1270,7 +1274,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
children.append(child.block_id)
is_updated = is_updated or structure_blocks[encoded_block_id]['fields']['children'] != children
block_fields = xblock.get_explicitly_set_fields_by_scope(Scope.settings)
block_fields = partitioned_fields[Scope.settings]
block_fields = self._serialize_fields(xblock.category, block_fields)
if not is_new and not is_updated:
is_updated = self._compare_settings(block_fields, structure_blocks[encoded_block_id]['fields'])
......@@ -1697,6 +1701,18 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
index_entry['versions'][branch] = new_id
self.db_connection.update_course_index(index_entry)
def partition_xblock_fields_by_scope(self, xblock):
"""
Return a dictionary of scopes mapped to this xblock's explicitly set fields w/o any conversions
"""
# explicitly_set_fields_by_scope converts to json; so, avoiding it
# the existing partition_fields_by_scope works on a dict not an xblock
result = defaultdict(dict)
for field in xblock.fields.itervalues():
if field.is_set_on(xblock):
result[field.scope][field.name] = field.read_from(xblock)
return result
def _serialize_fields(self, category, fields):
"""
Convert any references to their serialized form.
......@@ -1708,6 +1724,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
assert isinstance(fields, dict)
xblock_class = XBlock.load_class(category, self.default_class)
xblock_class = self.mixologist.mix(xblock_class)
for field_name, value in fields.iteritems():
if value:
if isinstance(xblock_class.fields[field_name], Reference):
......@@ -1719,6 +1736,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
elif isinstance(xblock_class.fields[field_name], ReferenceValueDict):
for key, subvalue in value.iteritems():
value[key] = subvalue.block_id
# should this recurse down dicts and lists just in case they contain datetime?
elif not isinstance(value, datetime.datetime): # don't convert datetimes!
fields[field_name] = xblock_class.fields[field_name].to_json(value)
# I think these are obsolete conditions; so, I want to confirm that. Thus the warnings
if 'location' in fields:
......
......@@ -1464,19 +1464,11 @@ class TestCourseCreation(SplitModuleTest):
original_locator = CourseLocator(org='guestx', course='contender', run="run", branch=BRANCH_NAME_DRAFT)
original = modulestore().get_course(original_locator)
original_index = modulestore().get_course_index_info(original_locator)
fields = {}
for field in original.fields.values():
value = getattr(original, field.name)
if not isinstance(value, datetime.datetime):
json_value = field.to_json(value)
else:
json_value = value
if field.scope == Scope.content and field.name != 'location':
fields[field.name] = json_value
elif field.scope == Scope.settings:
fields[field.name] = json_value
fields = {
'grading_policy': original.grading_policy,
'display_name': 'Derivative',
}
fields['grading_policy']['GRADE_CUTOFFS'] = {'A': .9, 'B': .8, 'C': .65}
fields['display_name'] = 'Derivative'
new_draft = modulestore().create_course(
'counter', 'leech', 'leech_run', 'leech_master', BRANCH_NAME_DRAFT,
versions_dict={BRANCH_NAME_DRAFT: original_index['versions'][BRANCH_NAME_DRAFT]},
......
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