diff --git a/cms/djangoapps/contentstore/management/commands/import.py b/cms/djangoapps/contentstore/management/commands/import.py
index e24111dbb7..f97ac10d41 100644
--- a/cms/djangoapps/contentstore/management/commands/import.py
+++ b/cms/djangoapps/contentstore/management/commands/import.py
@@ -3,19 +3,15 @@
###
from django.core.management.base import BaseCommand, CommandError
-from keystore.django import keystore
-from lxml import etree
-from keystore.xml import XMLModuleStore
+from xmodule.modulestore.django import modulestore
+from xmodule.modulestore.xml import XMLModuleStore
unnamed_modules = 0
-etree.set_default_parser(etree.XMLParser(dtd_validation=False, load_dtd=False,
- remove_comments=True))
-
class Command(BaseCommand):
help = \
-'''Import the specified data directory into the default keystore'''
+'''Import the specified data directory into the default ModuleStore'''
def handle(self, *args, **options):
if len(args) != 3:
@@ -23,10 +19,11 @@ class Command(BaseCommand):
org, course, data_dir = args
- module_store = XMLModuleStore(org, course, data_dir, 'xmodule.raw_module.RawDescriptor')
+ module_store = XMLModuleStore(org, course, data_dir, 'xmodule.raw_module.RawDescriptor', eager=True)
for module in module_store.modules.itervalues():
- keystore().create_item(module.location)
+ modulestore().create_item(module.location)
if 'data' in module.definition:
- keystore().update_item(module.location, module.definition['data'])
+ modulestore().update_item(module.location, module.definition['data'])
if 'children' in module.definition:
- keystore().update_children(module.location, module.definition['children'])
+ modulestore().update_children(module.location, module.definition['children'])
+ modulestore().update_metadata(module.location, dict(module.metadata))
diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py
index f7d5efe22a..76a904a403 100644
--- a/cms/djangoapps/contentstore/views.py
+++ b/cms/djangoapps/contentstore/views.py
@@ -1,9 +1,10 @@
from mitxmako.shortcuts import render_to_response
-from keystore.django import keystore
+from xmodule.modulestore.django import modulestore
from django_future.csrf import ensure_csrf_cookie
from django.http import HttpResponse
import json
+from fs.osfs import OSFS
@ensure_csrf_cookie
def index(request):
@@ -11,14 +12,14 @@ def index(request):
org = 'mit.edu'
course = '6002xs12'
name = '6.002_Spring_2012'
- course = keystore().get_item(['i4x', org, course, 'course', name])
+ course = modulestore().get_item(['i4x', org, course, 'course', name])
weeks = course.get_children()
return render_to_response('index.html', {'weeks': weeks})
def edit_item(request):
item_id = request.GET['id']
- item = keystore().get_item(item_id)
+ item = modulestore().get_item(item_id)
return render_to_response('unit.html', {
'contents': item.get_html(),
'js_module': item.js_module_name(),
@@ -30,5 +31,18 @@ def edit_item(request):
def save_item(request):
item_id = request.POST['id']
data = json.loads(request.POST['data'])
- keystore().update_item(item_id, data)
+ modulestore().update_item(item_id, data)
return HttpResponse(json.dumps({}))
+
+
+def temp_force_export(request):
+ org = 'mit.edu'
+ course = '6002xs12'
+ name = '6.002_Spring_2012'
+ course = modulestore().get_item(['i4x', org, course, 'course', name])
+ fs = OSFS('../data-export-test')
+ xml = course.export_to_xml(fs)
+ with fs.open('course.xml', 'w') as course_xml:
+ course_xml.write(xml)
+
+ return HttpResponse('Done')
diff --git a/cms/envs/dev.py b/cms/envs/dev.py
index ce775d962a..b4bcbfa9ce 100644
--- a/cms/envs/dev.py
+++ b/cms/envs/dev.py
@@ -3,12 +3,16 @@ This config file runs the simplest dev environment"""
from .common import *
+import logging
+import sys
+logging.basicConfig(stream=sys.stdout, )
+
DEBUG = True
TEMPLATE_DEBUG = DEBUG
-KEYSTORE = {
+MODULESTORE = {
'default': {
- 'ENGINE': 'keystore.mongo.MongoModuleStore',
+ 'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
'OPTIONS': {
'default_class': 'xmodule.raw_module.RawDescriptor',
'host': 'localhost',
diff --git a/cms/envs/test.py b/cms/envs/test.py
index 1a20d9e6f8..032de92953 100644
--- a/cms/envs/test.py
+++ b/cms/envs/test.py
@@ -17,7 +17,7 @@ for app in os.listdir(PROJECT_ROOT / 'djangoapps'):
NOSE_ARGS += ['--cover-package', app]
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
-KEYSTORE = {
+MODULESTORE = {
'host': 'localhost',
'db': 'mongo_base',
'collection': 'key_store',
diff --git a/cms/templates/widgets/html-edit.html b/cms/templates/widgets/html-edit.html
index 666aa1de81..f0f63ea905 100644
--- a/cms/templates/widgets/html-edit.html
+++ b/cms/templates/widgets/html-edit.html
@@ -33,8 +33,8 @@
-
-
${module.definition['data']}
+
+ ${data}
Save & Update
diff --git a/cms/urls.py b/cms/urls.py
index d7314aafae..9d827c3fe3 100644
--- a/cms/urls.py
+++ b/cms/urls.py
@@ -8,4 +8,5 @@ urlpatterns = patterns('',
url(r'^$', 'contentstore.views.index', name='index'),
url(r'^edit_item$', 'contentstore.views.edit_item', name='edit_item'),
url(r'^save_item$', 'contentstore.views.save_item', name='save_item'),
+ url(r'^temp_force_export$', 'contentstore.views.temp_force_export')
)
diff --git a/common/lib/capa/capa_problem.py b/common/lib/capa/capa_problem.py
index a06ac1d7b6..2e3620021b 100644
--- a/common/lib/capa/capa_problem.py
+++ b/common/lib/capa/capa_problem.py
@@ -68,14 +68,13 @@ class LoncapaProblem(object):
Main class for capa Problems.
'''
- def __init__(self, fileobject, id, state=None, seed=None, system=None):
+ def __init__(self, problem_text, id, state=None, seed=None, system=None):
'''
- Initializes capa Problem. The problem itself is defined by the XML file
- pointed to by fileobject.
+ Initializes capa Problem.
Arguments:
- - filesobject : an OSFS instance: see fs.osfs
+ - problem_text : xml defining the problem
- id : string used as the identifier for this problem; often a filename (no spaces)
- state : student state (represented as a dict)
- seed : random number generator seed (int)
@@ -103,14 +102,11 @@ class LoncapaProblem(object):
if not self.seed:
self.seed = struct.unpack('i', os.urandom(4))[0]
- self.fileobject = fileobject # save problem file object, so we can use for debugging information later
- if getattr(system, 'DEBUG', False): # get the problem XML string from the problem file
- log.info("[courseware.capa.capa_problem.lcp.init] fileobject = %s" % fileobject)
- file_text = fileobject.read()
- file_text = re.sub("startouttext\s*/", "text", file_text) # Convert startouttext and endouttext to proper
- file_text = re.sub("endouttext\s*/", "/text", file_text)
+ problem_text = re.sub("startouttext\s*/", "text", problem_text) # Convert startouttext and endouttext to proper
+ problem_text = re.sub("endouttext\s*/", "/text", problem_text)
+ self.problem_text = problem_text
- self.tree = etree.XML(file_text) # parse problem XML file into an element tree
+ self.tree = etree.XML(problem_text) # parse problem XML file into an element tree
self._process_includes() # handle any
tags
# construct script processor context (eg for customresponse problems)
@@ -130,7 +126,7 @@ class LoncapaProblem(object):
self.done = False
def __unicode__(self):
- return u"LoncapaProblem ({0})".format(self.fileobject)
+ return u"LoncapaProblem ({0})".format(self.problem_text)
def get_state(self):
''' Stored per-user session data neeeded to:
@@ -272,7 +268,7 @@ class LoncapaProblem(object):
parent = inc.getparent() # insert new XML into tree in place of inlcude
parent.insert(parent.index(inc),incxml)
parent.remove(inc)
- log.debug('Included %s into %s' % (file,self.fileobject))
+ log.debug('Included %s into %s' % (file, self.id))
def _extract_context(self, tree, seed=struct.unpack('i', os.urandom(4))[0]): # private
'''
diff --git a/common/lib/keystore/django.py b/common/lib/keystore/django.py
deleted file mode 100644
index 89aa9d07b0..0000000000
--- a/common/lib/keystore/django.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""
-Module that provides a connection to the keystore specified in the django settings.
-
-Passes settings.KEYSTORE as kwargs to MongoModuleStore
-"""
-
-from __future__ import absolute_import
-
-from importlib import import_module
-
-from django.conf import settings
-
-_KEYSTORES = {}
-
-
-def keystore(name='default'):
- global _KEYSTORES
-
- if name not in _KEYSTORES:
- class_path = settings.KEYSTORE[name]['ENGINE']
- module_path, _, class_name = class_path.rpartition('.')
- class_ = getattr(import_module(module_path), class_name)
- _KEYSTORES[name] = class_(
- **settings.KEYSTORE[name]['OPTIONS'])
-
- return _KEYSTORES[name]
diff --git a/common/lib/keystore/xml.py b/common/lib/keystore/xml.py
deleted file mode 100644
index e7adb56ad6..0000000000
--- a/common/lib/keystore/xml.py
+++ /dev/null
@@ -1,96 +0,0 @@
-import logging
-from fs.osfs import OSFS
-from importlib import import_module
-from lxml import etree
-from path import path
-from xmodule.x_module import XModuleDescriptor, XMLParsingSystem
-
-from . import ModuleStore, Location
-from .exceptions import ItemNotFoundError
-
-etree.set_default_parser(etree.XMLParser(dtd_validation=False, load_dtd=False,
- remove_comments=True))
-
-log = logging.getLogger(__name__)
-
-
-class XMLModuleStore(ModuleStore):
- """
- An XML backed ModuleStore
- """
- def __init__(self, org, course, data_dir, default_class=None):
- self.data_dir = path(data_dir)
- self.modules = {}
-
- module_path, _, class_name = default_class.rpartition('.')
- class_ = getattr(import_module(module_path), class_name)
- self.default_class = class_
-
- with open(self.data_dir / "course.xml") as course_file:
- class ImportSystem(XMLParsingSystem):
- def __init__(self, modulestore):
- """
- modulestore: the XMLModuleStore to store the loaded modules in
- """
- self.unnamed_modules = 0
-
- def process_xml(xml):
- try:
- xml_data = etree.fromstring(xml)
- except:
- log.exception("Unable to parse xml: {xml}".format(xml=xml))
- raise
- if xml_data.get('name'):
- xml_data.set('slug', Location.clean(xml_data.get('name')))
- else:
- self.unnamed_modules += 1
- xml_data.set('slug', '{tag}_{count}'.format(tag=xml_data.tag, count=self.unnamed_modules))
-
- module = XModuleDescriptor.load_from_xml(etree.tostring(xml_data), self, org, course, modulestore.default_class)
- modulestore.modules[module.location] = module
- return module
-
- XMLParsingSystem.__init__(self, modulestore.get_item, OSFS(data_dir), process_xml)
-
- ImportSystem(self).process_xml(course_file.read())
-
- def get_item(self, location):
- """
- Returns an XModuleDescriptor instance for the item at location.
- If location.revision is None, returns the most item with the most
- recent revision
-
- If any segment of the location is None except revision, raises
- keystore.exceptions.InsufficientSpecificationError
- If no object is found at that location, raises keystore.exceptions.ItemNotFoundError
-
- location: Something that can be passed to Location
- """
- location = Location(location)
- try:
- return self.modules[location]
- except KeyError:
- raise ItemNotFoundError(location)
-
- def create_item(self, location):
- raise NotImplementedError("XMLModuleStores are read-only")
-
- def update_item(self, location, data):
- """
- Set the data in the item specified by the location to
- data
-
- location: Something that can be passed to Location
- data: A nested dictionary of problem data
- """
- raise NotImplementedError("XMLModuleStores are read-only")
-
- def update_children(self, location, children):
- """
- Set the children for the item specified by the location to
- data
-
- location: Something that can be passed to Location
- children: A list of child item identifiers
- """
- raise NotImplementedError("XMLModuleStores are read-only")
diff --git a/common/lib/xmodule/mako_module.py b/common/lib/xmodule/mako_module.py
deleted file mode 100644
index 2260dddd92..0000000000
--- a/common/lib/xmodule/mako_module.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from x_module import XModuleDescriptor
-from mitxmako.shortcuts import render_to_string
-
-
-class MakoModuleDescriptor(XModuleDescriptor):
- """
- Module descriptor intended as a mixin that uses a mako template
- to specify the module html.
-
- Expects the descriptor to have the `mako_template` attribute set
- with the name of the template to render, and it will pass
- the descriptor as the `module` parameter to that template
- """
-
- def get_context(self):
- """
- Return the context to render the mako template with
- """
- return {'module': self}
-
- def get_html(self):
- return render_to_string(self.mako_template, self.get_context())
diff --git a/common/lib/xmodule/setup.py b/common/lib/xmodule/setup.py
index e45e6654c2..77b0838ff2 100644
--- a/common/lib/xmodule/setup.py
+++ b/common/lib/xmodule/setup.py
@@ -3,10 +3,10 @@ from setuptools import setup, find_packages
setup(
name="XModule",
version="0.1",
- packages=find_packages(),
+ packages=find_packages(exclude=["tests"]),
install_requires=['distribute'],
package_data={
- '': ['js/*']
+ 'xmodule': ['js/module/*']
},
# See http://guide.python-distribute.org/creation.html#entry-points
diff --git a/common/lib/xmodule/tests.py b/common/lib/xmodule/tests/__init__.py
similarity index 96%
rename from common/lib/xmodule/tests.py
rename to common/lib/xmodule/tests/__init__.py
index 90187abc2a..4fb270df13 100644
--- a/common/lib/xmodule/tests.py
+++ b/common/lib/xmodule/tests/__init__.py
@@ -43,12 +43,10 @@ class ModelsTest(unittest.TestCase):
def setUp(self):
pass
- def test_get_module_class(self):
- vc = xmodule.get_module_class('video')
- vc_str = ""
+ def test_load_class(self):
+ vc = xmodule.x_module.XModuleDescriptor.load_class('video')
+ vc_str = ""
self.assertEqual(str(vc), vc_str)
- video_id = xmodule.get_default_ids()['video']
- self.assertEqual(video_id, 'youtube')
def test_calc(self):
variables={'R1':2.0, 'R3':4.0}
@@ -98,7 +96,7 @@ class ModelsTest(unittest.TestCase):
class MultiChoiceTest(unittest.TestCase):
def test_MC_grade(self):
multichoice_file = os.path.dirname(__file__)+"/test_files/multichoice.xml"
- test_lcp = lcp.LoncapaProblem(open(multichoice_file), '1', system=i4xs)
+ test_lcp = lcp.LoncapaProblem(open(multichoice_file).read(), '1', system=i4xs)
correct_answers = {'1_2_1':'choice_foil3'}
self.assertEquals(test_lcp.grade_answers(correct_answers).get_correctness('1_2_1'), 'correct')
false_answers = {'1_2_1':'choice_foil2'}
@@ -106,7 +104,7 @@ class MultiChoiceTest(unittest.TestCase):
def test_MC_bare_grades(self):
multichoice_file = os.path.dirname(__file__)+"/test_files/multi_bare.xml"
- test_lcp = lcp.LoncapaProblem(open(multichoice_file), '1', system=i4xs)
+ test_lcp = lcp.LoncapaProblem(open(multichoice_file).read(), '1', system=i4xs)
correct_answers = {'1_2_1':'choice_2'}
self.assertEquals(test_lcp.grade_answers(correct_answers).get_correctness('1_2_1'), 'correct')
false_answers = {'1_2_1':'choice_1'}
@@ -114,7 +112,7 @@ class MultiChoiceTest(unittest.TestCase):
def test_TF_grade(self):
truefalse_file = os.path.dirname(__file__)+"/test_files/truefalse.xml"
- test_lcp = lcp.LoncapaProblem(open(truefalse_file), '1', system=i4xs)
+ test_lcp = lcp.LoncapaProblem(open(truefalse_file).read(), '1', system=i4xs)
correct_answers = {'1_2_1':['choice_foil2', 'choice_foil1']}
self.assertEquals(test_lcp.grade_answers(correct_answers).get_correctness('1_2_1'), 'correct')
false_answers = {'1_2_1':['choice_foil1']}
@@ -129,7 +127,7 @@ class MultiChoiceTest(unittest.TestCase):
class ImageResponseTest(unittest.TestCase):
def test_ir_grade(self):
imageresponse_file = os.path.dirname(__file__)+"/test_files/imageresponse.xml"
- test_lcp = lcp.LoncapaProblem(open(imageresponse_file), '1', system=i4xs)
+ test_lcp = lcp.LoncapaProblem(open(imageresponse_file).read(), '1', system=i4xs)
correct_answers = {'1_2_1':'(490,11)-(556,98)',
'1_2_2':'(242,202)-(296,276)'}
test_answers = {'1_2_1':'[500,20]',
@@ -142,7 +140,7 @@ class SymbolicResponseTest(unittest.TestCase):
def test_sr_grade(self):
raise SkipTest() # This test fails due to dependencies on a local copy of snuggletex-webapp. Until we have figured that out, we'll just skip this test
symbolicresponse_file = os.path.dirname(__file__)+"/test_files/symbolicresponse.xml"
- test_lcp = lcp.LoncapaProblem(open(symbolicresponse_file), '1', system=i4xs)
+ test_lcp = lcp.LoncapaProblem(open(symbolicresponse_file).read(), '1', system=i4xs)
correct_answers = {'1_2_1':'cos(theta)*[[1,0],[0,1]] + i*sin(theta)*[[0,1],[1,0]]',
'1_2_1_dynamath': '''