From eb5989aa992b646e5335f43d0cd4aa0001218255 Mon Sep 17 00:00:00 2001 From: Victor Shnayder Date: Thu, 19 Jul 2012 19:22:03 -0400 Subject: [PATCH] Ready to implement path_to_location * Clean up test data for simple, toy courses * clean up test_mongo.py * write initial test for path_to_location * hook up view to use path_to_location Next: actually implement it :) --- .../xmodule/xmodule/modulestore/__init__.py | 23 +++++ .../xmodule/xmodule/modulestore/exceptions.py | 3 + .../lib/xmodule/xmodule/modulestore/mongo.py | 24 +++++- .../xmodule/modulestore/tests/test_mongo.py | 84 ++++++++++++------- common/test/data/simple/course.xml | 24 ++++++ common/test/data/simple/html/toylab.html | 3 + .../data/simple/problems/L1_Problem_1.xml | 43 ++++++++++ .../test/data/simple/problems/ps01-simple.xml | 62 ++++++++++++++ common/test/data/toy/course.xml | 4 +- lms/djangoapps/courseware/views.py | 11 ++- 10 files changed, 244 insertions(+), 37 deletions(-) create mode 100644 common/test/data/simple/course.xml create mode 100644 common/test/data/simple/html/toylab.html create mode 100644 common/test/data/simple/problems/L1_Problem_1.xml create mode 100644 common/test/data/simple/problems/ps01-simple.xml 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 @@ + + + + +
+ + + +
+
+
diff --git a/common/test/data/simple/html/toylab.html b/common/test/data/simple/html/toylab.html new file mode 100644 index 0000000000..81df84bd63 --- /dev/null +++ b/common/test/data/simple/html/toylab.html @@ -0,0 +1,3 @@ +Lab 2A: Superposition Experiment + +

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 @@ + + +

+

Finger Exercise 1

+

+

+Here are two definitions:

+
    +
  1. +

    +Declarative knowledge refers to statements of fact.

    +
  2. +
  3. +

    +Imperative knowledge refers to 'how to' methods.

    +
  4. +
+

+Which of the following choices is correct?

+
    +
  1. +

    +Statement 1 is true, Statement 2 is false

    +
  2. +
  3. +

    +Statement 1 is false, Statement 2 is true

    +
  4. +
  5. +

    +Statement 1 and Statement 2 are both false

    +
  6. +
  7. +

    +Statement 1 and Statement 2 are both true

    +
  8. +
+

+ + + +

+
diff --git a/common/test/data/simple/problems/ps01-simple.xml b/common/test/data/simple/problems/ps01-simple.xml new file mode 100644 index 0000000000..e70d8f2c8d --- /dev/null +++ b/common/test/data/simple/problems/ps01-simple.xml @@ -0,0 +1,62 @@ +