Commit eb5989aa by Victor Shnayder

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 :)
parent f10c4c06
......@@ -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
......@@ -13,3 +13,6 @@ class InsufficientSpecificationError(Exception):
class InvalidLocationError(Exception):
pass
class NoPathToItem(Exception):
pass
......@@ -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
......@@ -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_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)
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
@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)
<course name="A Simple Course" org="edX" course="simple" graceperiod="1 day 5 hours 59 minutes 59 seconds" slug="2012_Fall">
<chapter name="Overview">
<video name="Welcome" youtube="0.75:izygArpw-Qo,1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8"/>
<videosequence format="Lecture Sequence" name="A simple sequence">
<html id="toylab" filename="toylab"/>
<video name="S0V1: Video Resources" youtube="0.75:EuzkdzfR0i8,1.0:1bK-WdDi6Qw,1.25:0v1VzoDVUTM,1.50:Bxk_-ZJb240"/>
</videosequence>
<section name="Lecture 2">
<sequential>
<video youtube="1.0:TBvX7HzxexQ"/>
<problem name="L1 Problem 1" points="1" type="lecture" showanswer="attempted" filename="L1_Problem_1" rerandomize="never"/>
</sequential>
</section>
</chapter>
<chapter name="Chapter 2">
<section name="Problem Set 1">
<sequential>
<problem type="lecture" showanswer="attempted" rerandomize="true" title="A simple coding problem" name="Simple coding problem" filename="ps01-simple"/>
</sequential>
</section>
</chapter>
<video name="Lost Video" youtube="1.0:TBvX7HzxexQ"/>
</course>
<b>Lab 2A: Superposition Experiment</b>
<p>Isn't the toy course great?</p>
<?xml version="1.0"?>
<problem>
<p>
<h1>Finger Exercise 1</h1>
</p>
<p>
Here are two definitions: </p>
<ol class="enumerate">
<li>
<p>
Declarative knowledge refers to statements of fact. </p>
</li>
<li>
<p>
Imperative knowledge refers to 'how to' methods. </p>
</li>
</ol>
<p>
Which of the following choices is correct? </p>
<ol class="enumerate">
<li>
<p>
Statement 1 is true, Statement 2 is false </p>
</li>
<li>
<p>
Statement 1 is false, Statement 2 is true </p>
</li>
<li>
<p>
Statement 1 and Statement 2 are both false </p>
</li>
<li>
<p>
Statement 1 and Statement 2 are both true </p>
</li>
</ol>
<p>
<symbolicresponse answer="4">
<textline size="90" math="1"/>
</symbolicresponse>
</p>
</problem>
<problem><style media="all" type="text/css"/>
<text><h2>Paying Off Credit Card Debt</h2>
<p> 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.</p>
<p >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.</p>
<blockquote>
<p><strong>Minimum monthly payment</strong>
= (Minimum monthly payment rate) x (Balance)<br/>
(Minimum monthly payment gets split into interest paid and
principal paid)<br/>
<strong>Interest Paid</strong> = (Annual interest rate) / (12
months) x (Balance)<br/>
<strong>Principal paid</strong> = (Minimum monthly payment) -
(Interest paid)<br/>
<strong>Remaining balance</strong> = Balance - (Principal
paid)</p>
</blockquote>
<p >For month 1, compute the minimum monthly payment by taking 2% of the balance.</p>
<blockquote xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:x="urn:schemas-microsoft-com:office:excel">
<p><strong>Minimum monthly payment</strong>
= .02 x $5000 = $100</p>
<p>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.</p>
<p><strong>Interest paid</strong> = .18/12 x $5000 =
$75<br/>
<strong>Principal paid</strong> = $100 - $75 = $25</p>
<p>The 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.</p>
<p><strong>Remaining balance</strong> = $5000 - $25 =
$4975</p>
</blockquote>
<p xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:x="urn:schemas-microsoft-com:office:excel">For month 2, we
repeat the same steps.</p>
<blockquote xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:x="urn:schemas-microsoft-com:office:excel">
<p><strong>Minimum monthly payment</strong>
= .02 x $4975 = $99.50<br/>
<strong>Interest Paid</strong> = .18/12 x $4975 =
$74.63<br/>
<strong>Principal Paid</strong> = $99.50 - $74.63 =
$24.87<br/>
<strong>Remaining Balance</strong> = $4975 - $24.87 =
$4950.13</p>
</blockquote>
<p xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:x="urn:schemas-microsoft-com:office:excel">After 12 months, the
total amount paid is $1167.55, leaving an outstanding balance
of $4708.10. Pretty depressing!</p>
</text></problem>
<course name="Toy Course" org="edX" course="toy" graceperiod="1 day 5 hours 59 minutes 59 seconds" slug="2012_Fall" start="2015-07-17T12:00">
<chapter name="Overview">
<video name="Welcome" youtube="0.75:izygArpw-Qo,1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8"/>
<video name="Welcome" youtube="1.0:p2Q6BrNhdh8"/>
<videosequence format="Lecture Sequence" name="System Usage Sequence">
<html id="toylab" filename="toylab"/>
<video name="S0V1: Video Resources" youtube="0.75:EuzkdzfR0i8,1.0:1bK-WdDi6Qw,1.25:0v1VzoDVUTM,1.50:Bxk_-ZJb240"/>
<video name="S0V1: Video Resources" youtube="1.0:1bK-WdDi6Qw"/>
</videosequence>
</chapter>
</course>
......@@ -21,7 +21,7 @@ from models import StudentModuleCache
from student.models import UserProfile
from multicourse import multicourse_settings
from xmodule.modulestore import Location
from xmodule.modulestore.exceptions import InvalidLocationError, ItemNotFoundError
from xmodule.modulestore.exceptions import InvalidLocationError, ItemNotFoundError, NoPathToItem
from xmodule.modulestore.django import modulestore
from xmodule.course_module import CourseDescriptor
......@@ -228,11 +228,14 @@ def jump_to(request, location):
# Complain if there's not data for this location
try:
item = modulestore().get_item(location)
(course, chapter, section, position) = modulestore().path_to_location(location)
except ItemNotFoundError:
raise Http404("No data at this location: {0}".format(location))
return HttpResponse("O hai")
except NoPathToItem:
raise Http404("This location is not in any class: {0}".format(location))
return index(course, chapter, section, position)
@ensure_csrf_cookie
def course_info(request, course_id):
......
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