Commit 929597ce by Don Mitchell

Move load_from_json to the test file only

It's a reasonable demo of in memory xblock creation, but doesn't fit
the xblock pattern. Moving temporarily to keep the dag persistence test.
parent 857431f7
'''
Created on May 7, 2013
@author: dmitchell
'''
import unittest import unittest
from xmodule import templates from xmodule import templates
from xmodule.modulestore.tests import persistent_factories from xmodule.modulestore.tests import persistent_factories
from xmodule.course_module import CourseDescriptor from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.seq_module import SequenceDescriptor from xmodule.seq_module import SequenceDescriptor
from xmodule.x_module import XModuleDescriptor
from xmodule.capa_module import CapaDescriptor from xmodule.capa_module import CapaDescriptor
from xmodule.modulestore.locator import CourseLocator, BlockUsageLocator from xmodule.modulestore.locator import CourseLocator, BlockUsageLocator
from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.html_module import HtmlDescriptor from xmodule.html_module import HtmlDescriptor
from xmodule.modulestore import inheritance
from xmodule.x_module import XModuleDescriptor
class TemplateTests(unittest.TestCase): class TemplateTests(unittest.TestCase):
...@@ -74,7 +70,7 @@ class TemplateTests(unittest.TestCase): ...@@ -74,7 +70,7 @@ class TemplateTests(unittest.TestCase):
test_course = persistent_factories.PersistentCourseFactory.create(org='testx', prettyid='tempcourse', test_course = persistent_factories.PersistentCourseFactory.create(org='testx', prettyid='tempcourse',
display_name='fun test course', user_id='testbot') display_name='fun test course', user_id='testbot')
test_chapter = XModuleDescriptor.load_from_json({'category': 'chapter', test_chapter = self.load_from_json({'category': 'chapter',
'fields': {'display_name': 'chapter n'}}, 'fields': {'display_name': 'chapter n'}},
test_course.system, parent_xblock=test_course) test_course.system, parent_xblock=test_course)
self.assertIsInstance(test_chapter, SequenceDescriptor) self.assertIsInstance(test_chapter, SequenceDescriptor)
...@@ -83,7 +79,7 @@ class TemplateTests(unittest.TestCase): ...@@ -83,7 +79,7 @@ class TemplateTests(unittest.TestCase):
# test w/ a definition (e.g., a problem) # test w/ a definition (e.g., a problem)
test_def_content = '<problem>boo</problem>' test_def_content = '<problem>boo</problem>'
test_problem = XModuleDescriptor.load_from_json({'category': 'problem', test_problem = self.load_from_json({'category': 'problem',
'fields': {'data': test_def_content}}, 'fields': {'data': test_def_content}},
test_course.system, parent_xblock=test_chapter) test_course.system, parent_xblock=test_chapter)
self.assertIsInstance(test_problem, CapaDescriptor) self.assertIsInstance(test_problem, CapaDescriptor)
...@@ -98,12 +94,12 @@ class TemplateTests(unittest.TestCase): ...@@ -98,12 +94,12 @@ class TemplateTests(unittest.TestCase):
""" """
test_course = persistent_factories.PersistentCourseFactory.create(org='testx', prettyid='tempcourse', test_course = persistent_factories.PersistentCourseFactory.create(org='testx', prettyid='tempcourse',
display_name='fun test course', user_id='testbot') display_name='fun test course', user_id='testbot')
test_chapter = XModuleDescriptor.load_from_json({'category': 'chapter', test_chapter = self.load_from_json({'category': 'chapter',
'fields': {'display_name': 'chapter n'}}, 'fields': {'display_name': 'chapter n'}},
test_course.system, parent_xblock=test_course) test_course.system, parent_xblock=test_course)
test_def_content = '<problem>boo</problem>' test_def_content = '<problem>boo</problem>'
# create child # create child
_ = XModuleDescriptor.load_from_json({'category': 'problem', _ = self.load_from_json({'category': 'problem',
'fields': {'data': test_def_content}}, 'fields': {'data': test_def_content}},
test_course.system, parent_xblock=test_chapter) test_course.system, parent_xblock=test_chapter)
# better to pass in persisted parent over the subdag so # better to pass in persisted parent over the subdag so
...@@ -194,3 +190,48 @@ class TemplateTests(unittest.TestCase): ...@@ -194,3 +190,48 @@ class TemplateTests(unittest.TestCase):
version_history = modulestore('split').get_block_generations(second_problem.location) version_history = modulestore('split').get_block_generations(second_problem.location)
self.assertNotEqual(version_history.locator.version_guid, first_problem.location.version_guid) self.assertNotEqual(version_history.locator.version_guid, first_problem.location.version_guid)
# ================================= JSON PARSING ===========================
# These are example methods for creating xmodules in memory w/o persisting them.
# They were in x_module but since xblock is not planning to support them but will
# allow apps to use this type of thing, I put it here.
@staticmethod
def load_from_json(json_data, system, default_class=None, parent_xblock=None):
"""
This method instantiates the correct subclass of XModuleDescriptor based
on the contents of json_data. It does not persist it and can create one which
has no usage id.
parent_xblock is used to compute inherited metadata as well as to append the new xblock.
json_data:
- 'location' : must have this field
- 'category': the xmodule category (required or location must be a Location)
- 'metadata': a dict of locally set metadata (not inherited)
- 'children': a list of children's usage_ids w/in this course
- 'definition':
- '_id' (optional): the usage_id of this. Will generate one if not given one.
"""
class_ = XModuleDescriptor.load_class(
json_data.get('category', json_data.get('location', {}).get('category')),
default_class
)
usage_id = json_data.get('_id', None)
if not '_inherited_settings' in json_data and parent_xblock is not None:
json_data['_inherited_settings'] = parent_xblock.xblock_kvs.get_inherited_settings().copy()
json_fields = json_data.get('fields', {})
for field in inheritance.INHERITABLE_METADATA:
if field in json_fields:
json_data['_inherited_settings'][field] = json_fields[field]
new_block = system.xblock_from_json(class_, usage_id, json_data)
if parent_xblock is not None:
children = parent_xblock.children
children.append(new_block)
# trigger setter method by using top level field access
parent_xblock.children = children
# decache pending children field settings (Note, truly persisting at this point would break b/c
# persistence assumes children is a list of ids not actual xblocks)
parent_xblock.save()
return new_block
...@@ -7,7 +7,7 @@ from lxml import etree ...@@ -7,7 +7,7 @@ from lxml import etree
from collections import namedtuple from collections import namedtuple
from pkg_resources import resource_listdir, resource_string, resource_isdir from pkg_resources import resource_listdir, resource_string, resource_isdir
from xmodule.modulestore import inheritance, Location from xmodule.modulestore import Location
from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecificationError, InvalidLocationError from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecificationError, InvalidLocationError
from xblock.core import XBlock, Scope, String, Integer, Float, List, ModelType from xblock.core import XBlock, Scope, String, Integer, Float, List, ModelType
...@@ -557,62 +557,6 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): ...@@ -557,62 +557,6 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
""" """
return False return False
# ================================= JSON PARSING ===========================
@staticmethod
def load_from_json(json_data, system, default_class=None, parent_xblock=None):
"""
This method instantiates the correct subclass of XModuleDescriptor based
on the contents of json_data. It does not persist it and can create one which
has no usage id.
parent_xblock is used to compute inherited metadata as well as to append the new xblock.
json_data:
- 'location' : must have this field
- 'category': the xmodule category (required or location must be a Location)
- 'metadata': a dict of locally set metadata (not inherited)
- 'children': a list of children's usage_ids w/in this course
- 'definition':
- '_id' (optional): the usage_id of this. Will generate one if not given one.
"""
class_ = XModuleDescriptor.load_class(
json_data.get('category', json_data.get('location', {}).get('category')),
default_class
)
return class_.from_json(json_data, system, parent_xblock)
@classmethod
def from_json(cls, json_data, system, parent_xblock=None):
"""
Creates an instance of this descriptor from the supplied json_data.
This may be overridden by subclasses
json_data:
- 'category': the xmodule category (required)
- 'fields': a dict of locally set fields (not inherited)
- 'definition': (optional) the db id for the definition record (not the definition content) or a
definitionLazyLoader
- '_id' (optional): the usage_id of this. Will generate one if not given one.
"""
usage_id = json_data.get('_id', None)
if not '_inherited_settings' in json_data and parent_xblock is not None:
json_data['_inherited_settings'] = parent_xblock.xblock_kvs.get_inherited_settings().copy()
json_fields = json_data.get('fields', {})
for field in inheritance.INHERITABLE_METADATA:
if field in json_fields:
json_data['_inherited_settings'][field] = json_fields[field]
new_block = system.xblock_from_json(cls, usage_id, json_data)
if parent_xblock is not None:
children = parent_xblock.children
children.append(new_block)
# trigger setter method by using top level field access
parent_xblock.children = children
# decache pending children field settings (Note, truly persisting at this point would break b/c
# persistence assumes children is a list of ids not actual xblocks)
parent_xblock.save()
return new_block
@classmethod @classmethod
def _translate(cls, key): def _translate(cls, key):
'VS[compat]' 'VS[compat]'
......
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