diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py
index 77dfacf372..279782b61a 100644
--- a/common/lib/xmodule/xmodule/modulestore/__init__.py
+++ b/common/lib/xmodule/xmodule/modulestore/__init__.py
@@ -253,3 +253,26 @@ class ModuleStore(object):
in this modulestore.
'''
raise NotImplementedError
+
+ def path_to_location(self, location, course=None, chapter=None, section=None):
+ '''
+ Try to find a course/chapter/section[/position] path to this location.
+
+ raise ItemNotFoundError if the location doesn't exist.
+
+ If course, chapter, section are not None, restrict search to paths with those
+ components as specified.
+
+ raise NoPathToItem if the location exists, but isn't accessible via
+ a path that matches the course/chapter/section restrictions.
+
+ In general, a location may be accessible via many paths. This method may
+ return any valid path.
+
+ Return a tuple (course, chapter, section, position).
+
+ If the section a sequence, position should be the position of this location
+ in that sequence. Otherwise, position should be None.
+ '''
+ raise NotImplementedError
+
diff --git a/common/lib/xmodule/xmodule/modulestore/exceptions.py b/common/lib/xmodule/xmodule/modulestore/exceptions.py
index a6dc99883f..d860a1d263 100644
--- a/common/lib/xmodule/xmodule/modulestore/exceptions.py
+++ b/common/lib/xmodule/xmodule/modulestore/exceptions.py
@@ -13,3 +13,6 @@ class InsufficientSpecificationError(Exception):
class InvalidLocationError(Exception):
pass
+
+class NoPathToItem(Exception):
+ pass
diff --git a/common/lib/xmodule/xmodule/modulestore/mongo.py b/common/lib/xmodule/xmodule/modulestore/mongo.py
index 95c882824b..d2b68d03ef 100644
--- a/common/lib/xmodule/xmodule/modulestore/mongo.py
+++ b/common/lib/xmodule/xmodule/modulestore/mongo.py
@@ -251,7 +251,7 @@ class MongoModuleStore(ModuleStore):
def update_metadata(self, location, metadata):
"""
- Set the children for the item specified by the location to
+ Set the metadata for the item specified by the location to
metadata
location: Something that can be passed to Location
@@ -264,3 +264,25 @@ class MongoModuleStore(ModuleStore):
{'_id': Location(location).dict()},
{'$set': {'metadata': metadata}}
)
+
+ def path_to_location(self, location, course=None):
+ '''
+ Try to find a course/chapter/section[/position] path to this location.
+
+ raise ItemNotFoundError if the location doesn't exist.
+
+ If course is not None, restrict search to paths in that course.
+
+ raise NoPathToItem if the location exists, but isn't accessible via
+ a chapter/section path in the course(s) being searched.
+
+ In general, a location may be accessible via many paths. This method may
+ return any valid path.
+
+ Return a tuple (course, chapter, section, position).
+
+ If the section a sequence, position should be the position of this location
+ in that sequence. Otherwise, position should be None.
+ '''
+ raise NotImplementedError
+
diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
index 55e573677f..493b8a385d 100644
--- a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
+++ b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
@@ -4,7 +4,7 @@ from nose.tools import assert_equals, assert_raises, assert_not_equals, with_set
from path import path
from xmodule.modulestore import Location
-from xmodule.modulestore.exceptions import InvalidLocationError
+from xmodule.modulestore.exceptions import InvalidLocationError, ItemNotFoundError
from xmodule.modulestore.mongo import MongoModuleStore
from xmodule.modulestore.xml_importer import import_from_xml
@@ -26,36 +26,60 @@ FS_ROOT = DATA_DIR # TODO (vshnayder): will need a real fs_root for testing loa
DEFAULT_CLASS = 'xmodule.raw_module.RawDescriptor'
-connection = None
+class TestMongoModuleStore(object):
-def setup():
- global connection
- connection = pymongo.connection.Connection(HOST, PORT)
+ @classmethod
+ def setupClass(cls):
+ cls.connection = pymongo.connection.Connection(HOST, PORT)
+ cls.connection.drop_database(DB)
+
+ @classmethod
+ def teardownClass(cls):
+ pass
+
+ def setUp(self):
+ # connect to the db
+ self.store = MongoModuleStore(HOST, DB, COLLECTION, FS_ROOT, default_class=DEFAULT_CLASS)
+ # Explicitly list the courses to load (don't want the big one)
+ courses = ['toy', 'simple']
+ import_from_xml(self.store, DATA_DIR, courses)
+ self.connection = TestMongoModuleStore.connection
+ def tearDown(self):
+ # Destroy the test db.
+ self.connection.drop_database(DB)
+ self.store = None
-
-def setup_func():
- # connect to the db
- global store
- store = MongoModuleStore(HOST, DB, COLLECTION, FS_ROOT, default_class=DEFAULT_CLASS)
- print 'data_dir: {0}'.format(DATA_DIR)
- import_from_xml(store, DATA_DIR)
-
-def teardown_func():
- global store
- store = None
- # Destroy the test db.
- connection.drop_database(DB)
-
-
-@with_setup(setup_func, teardown_func)
-def test_init():
- '''Just make sure the db loads'''
- pass
-
-@with_setup(setup_func, teardown_func)
-def test_get_courses():
- '''Make sure the course objects loaded properly'''
- courses = store.get_courses()
- print courses
+ def test_init(self):
+ '''Just make sure the db loads'''
+ ids = list(self.connection[DB][COLLECTION].find({}, {'_id': True}))
+ print len(ids)
+ def test_get_courses(self):
+ '''Make sure the course objects loaded properly'''
+ courses = self.store.get_courses()
+ assert_equals(len(courses), 2)
+ courses.sort(key=lambda c: c.id)
+ assert_equals(courses[0].id, 'edX/simple/2012_Fall')
+ assert_equals(courses[1].id, 'edX/toy/2012_Fall')
+
+ def Xtest_path_to_location(self):
+ '''Make sure that path_to_location works'''
+ should_work = (
+ ("i4x://edX/toy/video/Welcome", ("toy", "Overview", None, None)),
+ )
+ for location, expected in should_work:
+ assert_equals(self.store.path_to_location(location), expected)
+
+ not_found = (
+ "i4x://edX/toy/video/WelcomeX",
+ )
+ for location in not_found:
+ assert_raises(ItemNotFoundError, self.store.path_to_location, location)
+
+ no_path = (
+ "i4x://edX/toy/video/Lost_Video",
+ )
+ for location in not_found:
+ assert_raises(ItemNotFoundError, self.store.path_to_location, location)
+
diff --git a/common/test/data/simple/course.xml b/common/test/data/simple/course.xml
new file mode 100644
index 0000000000..59a0a0c5bc
--- /dev/null
+++ b/common/test/data/simple/course.xml
@@ -0,0 +1,24 @@
+
Isn't the toy course great?
diff --git a/common/test/data/simple/problems/L1_Problem_1.xml b/common/test/data/simple/problems/L1_Problem_1.xml new file mode 100644 index 0000000000..2ba0617904 --- /dev/null +++ b/common/test/data/simple/problems/L1_Problem_1.xml @@ -0,0 +1,43 @@ + ++
+Here are two definitions:
++Declarative knowledge refers to statements of fact.
++Imperative knowledge refers to 'how to' methods.
++Which of the following choices is correct?
++Statement 1 is true, Statement 2 is false
++Statement 1 is false, Statement 2 is true
++Statement 1 and Statement 2 are both false
++Statement 1 and Statement 2 are both true
+
+
Each month, a credit + card statement will come with the option for you to pay a + minimum amount of your charge, usually 2% of the balance due. + However, the credit card company earns money by charging + interest on the balance that you don't pay. So even if you + pay credit card payments on time, interest is still accruing + on the outstanding balance.
+Say you've made a + $5,000 purchase on a credit card with 18% annual interest + rate and 2% minimum monthly payment rate. After a year, how + much is the remaining balance? Use the following + equations.
+++Minimum monthly payment += (Minimum monthly payment rate) x (Balance)
+
+ (Minimum monthly payment gets split into interest paid and + principal paid)
+Interest Paid = (Annual interest rate) / (12 + months) x (Balance)
+Principal paid = (Minimum monthly payment) - + (Interest paid)
+Remaining balance = Balance - (Principal + paid)
For month 1, compute the minimum monthly payment by taking 2% of the balance.
+++Minimum monthly payment += .02 x $5000 = $100
+We can't simply deduct this from the balance because + there is compounding interest. Of this $100 monthly + payment, compute how much will go to paying off interest + and how much will go to paying off the principal. Remember + that it's the annual interest rate that is given, so we + need to divide it by 12 to get the monthly interest + rate.
+Interest paid = .18/12 x $5000 = + $75
+
+Principal paid = $100 - $75 = $25The remaining balance at the end of the first month will + be the principal paid this month subtracted from the + balance at the start of the month.
+Remaining balance = $5000 - $25 = + $4975
+
For month 2, we + repeat the same steps.
+++Minimum monthly payment += .02 x $4975 = $99.50
+
+Interest Paid = .18/12 x $4975 = + $74.63
+Principal Paid = $99.50 - $74.63 = + $24.87
+Remaining Balance = $4975 - $24.87 = + $4950.13
After 12 months, the + total amount paid is $1167.55, leaving an outstanding balance + of $4708.10. Pretty depressing!
+