diff --git a/cms/djangoapps/contentstore/tests/tests.py b/cms/djangoapps/contentstore/tests/tests.py
index ade7c4e75d756b0d269cda81746a0a567c3b19ef..6b346046fcd4ee813d24199078adfc9024b13fef 100644
--- a/cms/djangoapps/contentstore/tests/tests.py
+++ b/cms/djangoapps/contentstore/tests/tests.py
@@ -1,13 +1,19 @@
 import json
-from django.test.client import Client
 from django.test import TestCase
+from django.test.client import Client
 from mock import patch, Mock
 from override_settings import override_settings
 from django.conf import settings
 from django.core.urlresolvers import reverse
+from path import path
 
 from student.models import Registration
 from django.contrib.auth.models import User
+from xmodule.modulestore.django import modulestore
+import xmodule.modulestore.django
+from xmodule.modulestore import Location
+from contentstore import import_from_xml
+import copy
 
 
 def parse_json(response):
@@ -25,36 +31,21 @@ def registration(email):
     return Registration.objects.get(user__email=email)
 
 
-class AuthTestCase(TestCase):
-    """Check that various permissions-related things work"""
-
-    def setUp(self):
-        self.email = 'a@b.com'
-        self.pw = 'xyz'
-        self.username = 'testuser'
-        self.client = Client()
-
-    def check_page_get(self, url, expected):
-        resp = self.client.get(url)
-        self.assertEqual(resp.status_code, expected)
+class ContentStoreTestCase(TestCase):
+    def _login(self, email, pw):
+        '''Login.  View should always return 200.  The success/fail is in the
+        returned json'''
+        resp = self.client.post(reverse('login_post'),
+                                {'email': email, 'password': pw})
+        self.assertEqual(resp.status_code, 200)
         return resp
 
-    def test_public_pages_load(self):
-        """Make sure pages that don't require login load without error."""
-        pages = (
-                 reverse('login'),
-                 reverse('signup'),
-                 )
-        for page in pages:
-            print "Checking '{0}'".format(page)
-            self.check_page_get(page, 200)
-
-    def test_create_account_errors(self):
-        # No post data -- should fail
-        resp = self.client.post('/create_account', {})
-        self.assertEqual(resp.status_code, 200)
+    def login(self, email, pw):
+        '''Login, check that it worked.'''
+        resp = self._login(email, pw)
         data = parse_json(resp)
-        self.assertEqual(data['success'], False)
+        self.assertTrue(data['success'])
+        return resp
 
     def _create_account(self, username, email, pw):
         '''Try to create an account.  No error checking'''
@@ -78,7 +69,7 @@ class AuthTestCase(TestCase):
         self.assertEqual(data['success'], True)
 
         # Check both that the user is created, and inactive
-        self.assertFalse(user(self.email).is_active)
+        self.assertFalse(user(email).is_active)
 
         return resp
 
@@ -95,26 +86,43 @@ class AuthTestCase(TestCase):
         resp = self._activate_user(email)
         self.assertEqual(resp.status_code, 200)
         # Now make sure that the user is now actually activated
-        self.assertTrue(user(self.email).is_active)
+        self.assertTrue(user(email).is_active)
 
-    def test_create_account(self):
-        self.create_account(self.username, self.email, self.pw)
-        self.activate_user(self.email)
 
-    def _login(self, email, pw):
-        '''Login.  View should always return 200.  The success/fail is in the
-        returned json'''
-        resp = self.client.post(reverse('login_post'),
-                                {'email': email, 'password': pw})
-        self.assertEqual(resp.status_code, 200)
+class AuthTestCase(ContentStoreTestCase):
+    """Check that various permissions-related things work"""
+
+    def setUp(self):
+        self.email = 'a@b.com'
+        self.pw = 'xyz'
+        self.username = 'testuser'
+        self.client = Client()
+
+    def check_page_get(self, url, expected):
+        resp = self.client.get(url)
+        self.assertEqual(resp.status_code, expected)
         return resp
+        
+    def test_public_pages_load(self):
+        """Make sure pages that don't require login load without error."""
+        pages = (
+                 reverse('login'),
+                 reverse('signup'),
+                 )
+        for page in pages:
+            print "Checking '{0}'".format(page)
+            self.check_page_get(page, 200)
 
-    def login(self, email, pw):
-        '''Login, check that it worked.'''
-        resp = self._login(self.email, self.pw)
+    def test_create_account_errors(self):
+        # No post data -- should fail
+        resp = self.client.post('/create_account', {})
+        self.assertEqual(resp.status_code, 200)
         data = parse_json(resp)
-        self.assertTrue(data['success'])
-        return resp
+        self.assertEqual(data['success'], False)
+
+    def test_create_account(self):
+        self.create_account(self.username, self.email, self.pw)
+        self.activate_user(self.email)
 
     def test_login(self):
         self.create_account(self.username, self.email, self.pw)
@@ -170,3 +178,30 @@ class AuthTestCase(TestCase):
         self.assertEqual(resp.status_code, 302)
 
         # Logged in should work.
+
+TEST_DATA_MODULESTORE = copy.deepcopy(settings.MODULESTORE)
+TEST_DATA_MODULESTORE['default']['OPTIONS']['fs_root'] = path('common/test/data')
+
+@override_settings(MODULESTORE=TEST_DATA_MODULESTORE)
+class EditTestCase(ContentStoreTestCase):
+    """Check that editing functionality works on example courses"""
+
+    def setUp(self):
+        email = 'edit@test.com'
+        password = 'foo'
+        self.create_account('edittest', email, password)
+        self.activate_user(email)
+        self.login(email, password)
+        xmodule.modulestore.django._MODULESTORES = {}
+
+    def check_edit_item(self, test_course_name):
+        import_from_xml('common/test/data/', test_course_name)
+
+        for descriptor in modulestore().get_items(Location(None, None, None, None, None)):
+            print "Checking ", descriptor.location.url()
+            print descriptor.__class__, descriptor.location
+            resp = self.client.get(reverse('edit_item'), {'id': descriptor.location.url()})
+            self.assertEqual(resp.status_code, 200)
+
+    def test_edit_item_toy(self):
+        self.check_edit_item('toy')
diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py
index 2bec0272fa118a62dd82c9fe6759fb1da7618cbd..a79aa0e18f550bde5d6df4dc2459da2bdb6a4701 100644
--- a/cms/djangoapps/contentstore/views.py
+++ b/cms/djangoapps/contentstore/views.py
@@ -92,7 +92,6 @@ def edit_item(request):
     """
     # TODO (vshnayder): change name from id to location in coffee+html as well.
     item_location = request.GET['id']
-    print item_location, request.GET
     if not has_access(request.user, item_location):
         raise Http404  # TODO (vshnayder): better error
 
diff --git a/cms/djangoapps/github_sync/__init__.py b/cms/djangoapps/github_sync/__init__.py
index 108fd5629fa2ec556c43b14d7d7030d229590322..149b92670acba08c2ee77c813dfacfbf18a717c9 100644
--- a/cms/djangoapps/github_sync/__init__.py
+++ b/cms/djangoapps/github_sync/__init__.py
@@ -13,16 +13,17 @@ from .exceptions import GithubSyncError
 log = logging.getLogger(__name__)
 
 
-def import_from_github(repo_settings):
+def setup_repo(repo_settings):
     """
-    Imports data into the modulestore based on the XML stored on github
+    Reset the local github repo specified by repo_settings
 
     repo_settings is a dictionary with the following keys:
         path: file system path to the local git repo
         branch: name of the branch to track on github
+        origin: git url for the repository to track
     """
-    repo_path = repo_settings['path']
-    data_dir, course_dir = os.path.split(repo_path)
+    course_dir = repo_settings['path']
+    repo_path = settings.GITHUB_REPO_ROOT / course_dir
 
     if not os.path.isdir(repo_path):
         Repo.clone_from(repo_settings['origin'], repo_path)
@@ -33,8 +34,29 @@ def import_from_github(repo_settings):
 
     # Do a hard reset to the remote branch so that we have a clean import
     git_repo.git.checkout(repo_settings['branch'])
+
+    return git_repo
+
+
+def load_repo_settings(course_dir):
+    """
+    Returns the repo_settings for the course stored in course_dir
+    """
+    for repo_settings in settings.REPOS.values():
+        if repo_settings['path'] == course_dir:
+            return repo_settings
+    raise InvalidRepo(course_dir)
+
+
+def import_from_github(repo_settings):
+    """
+    Imports data into the modulestore based on the XML stored on github
+    """
+    course_dir = repo_settings['path']
+    git_repo = setup_repo(repo_settings)
     git_repo.head.reset('origin/%s' % repo_settings['branch'], index=True, working_tree=True)
-    module_store = import_from_xml(data_dir, course_dirs=[course_dir])
+
+    module_store = import_from_xml(settings.GITHUB_REPO_ROOT, course_dirs=[course_dir])
     return git_repo.head.commit.hexsha, module_store.courses[course_dir]
 
 
@@ -44,14 +66,16 @@ def export_to_github(course, commit_message, author_str=None):
     and push to github (if MITX_FEATURES['GITHUB_PUSH'] is True).
     If author_str is specified, uses it in the commit.
     '''
-    repo_path = settings.DATA_DIR / course.metadata.get('data_dir', course.location.course)
-    fs = OSFS(repo_path)
+    course_dir = course.metadata.get('data_dir', course.location.course)
+    repo_settings = load_repo_settings(course_dir)
+    git_repo = setup_repo(repo_settings)
+
+    fs = OSFS(git_repo.working_dir)
     xml = course.export_to_xml(fs)
 
     with fs.open('course.xml', 'w') as course_xml:
         course_xml.write(xml)
 
-    git_repo = Repo(repo_path)
     if git_repo.is_dirty():
         git_repo.git.add(A=True)
         if author_str is not None:
diff --git a/cms/djangoapps/github_sync/tests/__init__.py b/cms/djangoapps/github_sync/tests/__init__.py
index 452904ffffc7254cbb73a407c46355646c116b08..c95d5380309d09467cade4a8b955b1d8cbee17fd 100644
--- a/cms/djangoapps/github_sync/tests/__init__.py
+++ b/cms/djangoapps/github_sync/tests/__init__.py
@@ -10,36 +10,37 @@ from xmodule.modulestore import Location
 from override_settings import override_settings
 from github_sync.exceptions import GithubSyncError
 
-
-@override_settings(DATA_DIR=path('test_root'))
+REPO_DIR = settings.GITHUB_REPO_ROOT / 'local_repo'
+WORKING_DIR = path(settings.TEST_ROOT)
+REMOTE_DIR = WORKING_DIR / 'remote_repo'
+
+
+@override_settings(REPOS={
+    'local': {
+        'path': 'local_repo',
+        'origin': REMOTE_DIR,
+        'branch': 'master',
+    }
+})
 class GithubSyncTestCase(TestCase):
 
     def cleanup(self):
-        shutil.rmtree(self.repo_dir, ignore_errors=True)
-        shutil.rmtree(self.remote_dir, ignore_errors=True)
+        shutil.rmtree(REPO_DIR, ignore_errors=True)
+        shutil.rmtree(REMOTE_DIR, ignore_errors=True)
+        modulestore().collection.drop()
 
     def setUp(self):
-        self.working_dir = path(settings.TEST_ROOT)
-        self.repo_dir = self.working_dir / 'local_repo'
-        self.remote_dir = self.working_dir / 'remote_repo'
-
         # make sure there's no stale data lying around
         self.cleanup()
 
-        shutil.copytree('common/test/data/toy', self.remote_dir)
+        shutil.copytree('common/test/data/toy', REMOTE_DIR)
 
-        remote = Repo.init(self.remote_dir)
+        remote = Repo.init(REMOTE_DIR)
         remote.git.add(A=True)
         remote.git.commit(m='Initial commit')
         remote.git.config("receive.denyCurrentBranch", "ignore")
 
-        modulestore().collection.drop()
-
-        self.import_revision, self.import_course = import_from_github({
-            'path': self.repo_dir,
-            'origin': self.remote_dir,
-            'branch': 'master',
-        })
+        self.import_revision, self.import_course = import_from_github(settings.REPOS['local'])
 
     def tearDown(self):
         self.cleanup()
@@ -48,7 +49,7 @@ class GithubSyncTestCase(TestCase):
         """
         Test that importing from github will create a repo if the repo doesn't already exist
         """
-        self.assertEquals(1, len(Repo(self.repo_dir).head.reference.log()))
+        self.assertEquals(1, len(Repo(REPO_DIR).head.reference.log()))
 
     def test_import_contents(self):
         """
@@ -66,7 +67,7 @@ class GithubSyncTestCase(TestCase):
         Test that with the GITHUB_PUSH feature disabled, no content is pushed to the remote
         """
         export_to_github(self.import_course, 'Test no-push')
-        self.assertEquals(1, Repo(self.remote_dir).head.commit.count())
+        self.assertEquals(1, Repo(REMOTE_DIR).head.commit.count())
 
     @override_settings(MITX_FEATURES={'GITHUB_PUSH': True})
     def test_export_push(self):
@@ -75,7 +76,7 @@ class GithubSyncTestCase(TestCase):
         """
         self.import_course.metadata['display_name'] = 'Changed display name'
         export_to_github(self.import_course, 'Test push')
-        self.assertEquals(2, Repo(self.remote_dir).head.commit.count())
+        self.assertEquals(2, Repo(REMOTE_DIR).head.commit.count())
 
     @override_settings(MITX_FEATURES={'GITHUB_PUSH': True})
     def test_export_conflict(self):
@@ -84,7 +85,7 @@ class GithubSyncTestCase(TestCase):
         """
         self.import_course.metadata['display_name'] = 'Changed display name'
 
-        remote = Repo(self.remote_dir)
+        remote = Repo(REMOTE_DIR)
         remote.git.commit(allow_empty=True, m="Testing conflict commit")
 
         self.assertRaises(GithubSyncError, export_to_github, self.import_course, 'Test push')
diff --git a/cms/envs/common.py b/cms/envs/common.py
index 5fb0c82bb7295435630f718a336d5f1f429d3c88..7b1f6e3fcdab3cca6e80bdd544feafbfbd421adf 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -44,10 +44,8 @@ PROJECT_ROOT = path(__file__).abspath().dirname().dirname()  # /mitx/cms
 REPO_ROOT = PROJECT_ROOT.dirname()
 COMMON_ROOT = REPO_ROOT / "common"
 ENV_ROOT = REPO_ROOT.dirname()  # virtualenv dir /mitx is in
-COURSES_ROOT = ENV_ROOT / "data"
 
-# FIXME: To support multiple courses, we should walk the courses dir at startup
-DATA_DIR = COURSES_ROOT
+GITHUB_REPO_ROOT = ENV_ROOT / "data"
 
 sys.path.append(REPO_ROOT)
 sys.path.append(PROJECT_ROOT / 'djangoapps')
diff --git a/cms/envs/dev.py b/cms/envs/dev.py
index 40139a8a22b230e14787dfc640330e96efc63e46..dd12ce5770dcfaf55d4ae37f72a24cd6c4a05320 100644
--- a/cms/envs/dev.py
+++ b/cms/envs/dev.py
@@ -18,6 +18,7 @@ MODULESTORE = {
             'host': 'localhost',
             'db': 'xmodule',
             'collection': 'modulestore',
+            'fs_root': GITHUB_REPO_ROOT,
         }
     }
 }
@@ -31,35 +32,35 @@ DATABASES = {
 
 REPOS = {
     'edx4edx': {
-        'path': DATA_DIR / "edx4edx",
+        'path': "edx4edx",
         'org': 'edx',
         'course': 'edx4edx',
         'branch': 'for_cms',
         'origin': 'git@github.com:MITx/edx4edx.git',
     },
     '6002x-fall-2012': {
-        'path': DATA_DIR / '6002x-fall-2012',
+        'path': '6002x-fall-2012',
         'org': 'mit.edu',
         'course': '6.002x',
         'branch': 'for_cms',
         'origin': 'git@github.com:MITx/6002x-fall-2012.git',
     },
     '6.00x': {
-        'path': DATA_DIR / '6.00x',
+        'path': '6.00x',
         'org': 'mit.edu',
         'course': '6.00x',
         'branch': 'for_cms',
         'origin': 'git@github.com:MITx/6.00x.git',
     },
     '7.00x': {
-        'path': DATA_DIR / '7.00x',
+        'path': '7.00x',
         'org': 'mit.edu',
         'course': '7.00x',
         'branch': 'for_cms',
         'origin': 'git@github.com:MITx/7.00x.git',
     },
     '3.091x': {
-        'path': DATA_DIR / '3.091x',
+        'path': '3.091x',
         'org': 'mit.edu',
         'course': '3.091x',
         'branch': 'for_cms',
diff --git a/cms/envs/test.py b/cms/envs/test.py
index 2a867af91fcfe9c4ad94315ea8d5b27b02aaf047..28dce3668258ceae88f28af4966de0fe18d4dd69 100644
--- a/cms/envs/test.py
+++ b/cms/envs/test.py
@@ -24,6 +24,7 @@ TEST_ROOT = path('test_root')
 # Want static files in the same dir for running on jenkins.
 STATIC_ROOT = TEST_ROOT / "staticfiles" 
 
+GITHUB_REPO_ROOT = TEST_ROOT / "data"
 
 MODULESTORE = {
     'default': {
@@ -33,6 +34,7 @@ MODULESTORE = {
             'host': 'localhost',
             'db': 'test_xmodule',
             'collection': 'modulestore',
+            'fs_root': GITHUB_REPO_ROOT,
         }
     }
 }
diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py
index 30010a09a783444c12d499ec7b399bdd8de62b63..0390c314ab799f9e72ca12321763e3a72e5557e8 100644
--- a/common/lib/xmodule/xmodule/modulestore/__init__.py
+++ b/common/lib/xmodule/xmodule/modulestore/__init__.py
@@ -45,7 +45,7 @@ class Location(_LocationBase):
         """
         return re.sub('_+', '_', INVALID_CHARS.sub('_', value))
 
-    def __new__(_cls, loc_or_tag, org=None, course=None, category=None, name=None, revision=None):
+    def __new__(_cls, loc_or_tag=None, org=None, course=None, category=None, name=None, revision=None):
         """
         Create a new location that is a clone of the specifed one.
 
@@ -70,11 +70,15 @@ class Location(_LocationBase):
         wildcard selection
         """
 
+
         if org is None and course is None and category is None and name is None and revision is None:
             location = loc_or_tag
         else:
             location = (loc_or_tag, org, course, category, name, revision)
 
+        if location is None:
+            return _LocationBase.__new__(_cls, *([None] * 6))
+
         def check_dict(dict_):
             check_list(dict_.itervalues())
 
diff --git a/common/lib/xmodule/xmodule/modulestore/mongo.py b/common/lib/xmodule/xmodule/modulestore/mongo.py
index 7ebee98c16244c103829c777534bc6bc34263332..63c955f6da62c786202bfbd481aba69b68923c42 100644
--- a/common/lib/xmodule/xmodule/modulestore/mongo.py
+++ b/common/lib/xmodule/xmodule/modulestore/mongo.py
@@ -6,6 +6,7 @@ from xmodule.mako_module import MakoDescriptorSystem
 from mitxmako.shortcuts import render_to_string
 from bson.son import SON
 from itertools import repeat
+from fs.osfs import OSFS
 
 from . import ModuleStore, Location
 from .exceptions import ItemNotFoundError, InsufficientSpecificationError
@@ -61,7 +62,9 @@ class MongoModuleStore(ModuleStore):
     """
     A Mongodb backed ModuleStore
     """
-    def __init__(self, host, db, collection, port=27017, default_class=None):
+
+    # TODO (cpennington): Enable non-filesystem filestores
+    def __init__(self, host, db, collection, fs_root, port=27017, default_class=None):
         self.collection = pymongo.connection.Connection(
             host=host,
             port=port
@@ -77,6 +80,7 @@ class MongoModuleStore(ModuleStore):
         module_path, _, class_name = default_class.rpartition('.')
         class_ = getattr(import_module(module_path), class_name)
         self.default_class = class_
+        self.fs_root = fs_root
 
     def _clean_item_data(self, item):
         """
@@ -113,13 +117,27 @@ class MongoModuleStore(ModuleStore):
 
         return data
 
+    def _load_item(self, item, data_cache):
+        """
+        Load an XModuleDescriptor from item, using the children stored in data_cache
+        """
+        resource_fs = OSFS(self.fs_root / item.get('data_dir', item['location']['course']))
+        system = CachingDescriptorSystem(
+            self,
+            data_cache,
+            self.default_class,
+            resource_fs,
+            render_to_string
+        )
+        return system.load_item(item['location'])
+
     def _load_items(self, items, depth=0):
         """
         Load a list of xmodules from the data in items, with children cached up to specified depth
         """
         data_cache = self._cache_children(items, depth)
-        system = CachingDescriptorSystem(self, data_cache, self.default_class, None, render_to_string)
-        return [system.load_item(item['location']) for item in items]
+
+        return [self._load_item(item, data_cache) for item in items]
 
     def get_item(self, location, depth=0):
         """
diff --git a/common/test/data/toy/course.xml b/common/test/data/toy/course.xml
index ecac9a4776868e74d4e0d7179276713e7ebbb19e..645fbd7af8dd6bd270d4cde1efffc59ab4d21792 100644
--- a/common/test/data/toy/course.xml
+++ b/common/test/data/toy/course.xml
@@ -1,11 +1,9 @@
 <course name="Toy Course" graceperiod="1 day 5 hours 59 minutes 59 seconds" showanswer="always" rerandomize="never">
   <chapter name="Overview">
-    <section format="Video" name="Welcome">
-      <video youtube="0.75:izygArpw-Qo,1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8"/>
-    </section>
-    <section format="Lecture Sequence" name="System Usage Sequence">
-      <html id="Lab2A" filename="Lab2A.html"/>
+    <video name="Welcome" youtube="0.75:izygArpw-Qo,1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8"/>
+    <videosequence format="Lecture Sequence" name="System Usage Sequence">
+      <html id="Lab2A" filename="Lab2A"/>
       <video name="S0V1: Video Resources" youtube="0.75:EuzkdzfR0i8,1.0:1bK-WdDi6Qw,1.25:0v1VzoDVUTM,1.50:Bxk_-ZJb240"/>
-    </section>
+    </videosequence>
   </chapter>
 </course>
diff --git a/common/test/data/toy/html/Lab2A.html b/common/test/data/toy/html/Lab2A.html
new file mode 100644
index 0000000000000000000000000000000000000000..7fe52cc1bedde05b91fc2f56e870b28065ecb1eb
--- /dev/null
+++ b/common/test/data/toy/html/Lab2A.html
@@ -0,0 +1,105 @@
+<script type="text/javascript">
+$(document).ready(function() { 
+$("#r1_slider").slider({
+value: 1, min: 1, max: 10, step: 1, slide: schematic.component_slider,
+schematic: "ctrls", component: "R1", property: "r", analysis: "dc",
+})
+
+$("#r2_slider").slider({
+value: 1, min: 1, max: 10, step: 1, slide: schematic.component_slider,
+schematic: "ctrls", component: "R2", property: "r", analysis: "dc",
+})
+
+$("#r3_slider").slider({
+value: 1, min: 1, max: 10, step: 1, slide: schematic.component_slider,
+schematic: "ctrls", component: "R3", property: "r", analysis: "dc",
+})
+
+$("#r4_slider").slider({
+value: 1, min: 1, max: 10, step: 1, slide: schematic.component_slider,
+schematic: "ctrls", component: "R4", property: "r", analysis: "dc",
+})
+
+$("#slider").slider(); });
+</script>
+
+<b>Lab 2A: Superposition Experiment</b>
+
+<br><br><i>Note: This part of the lab is just to develop your intuition about
+superposition.  There are no responses that need to be checked.</i>
+
+<br/><br/>Circuits with multiple sources can be hard to analyze as-is.  For example, what is the voltage
+between the two terminals on the right of Figure 1?
+
+<center>
+<input width="425" type="hidden" height="150" id="schematic1" parts="" analyses="" class="schematic ctrls" name="test2" value="[[&quot;w&quot;,[160,64,184,64]],[&quot;w&quot;,[160,16,184,16]],[&quot;w&quot;,[64,16,112,16]],[&quot;w&quot;,[112,64,88,64]],[&quot;w&quot;,[64,64,88,64]],[&quot;g&quot;,[88,64,0],{},[&quot;0&quot;]],[&quot;w&quot;,[112,64,160,64]],[&quot;w&quot;,[16,64,64,64]],[&quot;r&quot;,[160,16,0],{&quot;name&quot;:&quot;R4&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;1&quot;,&quot;0&quot;]],[&quot;r&quot;,[160,16,1],{&quot;name&quot;:&quot;R3&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;1&quot;,&quot;2&quot;]],[&quot;i&quot;,[112,64,6],{&quot;name&quot;:&quot;&quot;,&quot;value&quot;:&quot;6A&quot;},[&quot;0&quot;,&quot;2&quot;]],[&quot;r&quot;,[64,16,0],{&quot;name&quot;:&quot;R2&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;2&quot;,&quot;0&quot;]],[&quot;r&quot;,[64,16,1],{&quot;name&quot;:&quot;R1&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;2&quot;,&quot;3&quot;]],[&quot;v&quot;,[16,16,0],{&quot;name&quot;:&quot;&quot;,&quot;value&quot;:&quot;8V&quot;},[&quot;3&quot;,&quot;0&quot;]],[&quot;view&quot;,-24,0,2]]"/>
+Figure 1.  Example multi-source circuit
+</center>
+
+<br/><br/>We can use superposition to make the analysis much easier.
+The circuit in Figure 1 can be decomposed into two separate
+subcircuits: one involving only the voltage source and one involving only the
+current source.  We'll analyze each circuit separately and combine the
+results using superposition.  Recall that to decompose a circuit for
+analysis, we'll pick each source in turn and set all the other sources
+to zero (i.e., voltage sources become short circuits and current
+sources become open circuits).  The circuit above has two sources, so
+the decomposition produces two subcircuits, as shown in Figure 2.
+
+<center>
+<table><tr><td>
+<input style="display:inline;" width="425" type="hidden" height="150" id="schematic2" parts="" analyses="" class="schematic ctrls" name="test2" value="[[&quot;w&quot;,[160,64,184,64]],[&quot;w&quot;,[160,16,184,16]],[&quot;w&quot;,[64,16,112,16]],[&quot;w&quot;,[112,64,88,64]],[&quot;w&quot;,[64,64,88,64]],[&quot;g&quot;,[88,64,0],{},[&quot;0&quot;]],[&quot;w&quot;,[112,64,160,64]],[&quot;w&quot;,[16,64,64,64]],[&quot;r&quot;,[160,16,0],{&quot;name&quot;:&quot;R4&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;1&quot;,&quot;0&quot;]],[&quot;r&quot;,[160,16,1],{&quot;name&quot;:&quot;R3&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;1&quot;,&quot;2&quot;]],[&quot;r&quot;,[64,16,0],{&quot;name&quot;:&quot;R2&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;2&quot;,&quot;0&quot;]],[&quot;r&quot;,[64,16,1],{&quot;name&quot;:&quot;R1&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;2&quot;,&quot;3&quot;]],[&quot;v&quot;,[16,16,0],{&quot;name&quot;:&quot;&quot;,&quot;value&quot;:&quot;8V&quot;},[&quot;3&quot;,&quot;0&quot;]],[&quot;view&quot;,-24,0,2]]"/>
+(a) Subcircuit for analyzing contribution of voltage source
+</td><td>
+<input width="425" type="hidden" height="150" id="schematic3" parts="" analyses="" class="schematic ctrls" name="test2" value="[[&quot;w&quot;,[16,16,16,64]],[&quot;w&quot;,[160,64,184,64]],[&quot;w&quot;,[160,16,184,16]],[&quot;w&quot;,[64,16,112,16]],[&quot;w&quot;,[112,64,88,64]],[&quot;w&quot;,[64,64,88,64]],[&quot;g&quot;,[88,64,0],{},[&quot;0&quot;]],[&quot;w&quot;,[112,64,160,64]],[&quot;w&quot;,[16,64,64,64]],[&quot;r&quot;,[160,16,0],{&quot;name&quot;:&quot;R4&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;1&quot;,&quot;0&quot;]],[&quot;r&quot;,[160,16,1],{&quot;name&quot;:&quot;R3&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;1&quot;,&quot;2&quot;]],[&quot;i&quot;,[112,64,6],{&quot;name&quot;:&quot;&quot;,&quot;value&quot;:&quot;6A&quot;},[&quot;0&quot;,&quot;2&quot;]],[&quot;r&quot;,[64,16,0],{&quot;name&quot;:&quot;R2&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;2&quot;,&quot;0&quot;]],[&quot;r&quot;,[64,16,1],{&quot;name&quot;:&quot;R1&quot;,&quot;r&quot;:&quot;1&quot;},[&quot;2&quot;,&quot;3&quot;]],[&quot;view&quot;,-24,0,2]]"/>
+(b) Subcircuit for analyzing contribution of current source
+</td></tr></table>
+<br>Figure 2.  Decomposition of Figure 1 into subcircuits
+</center>
+
+<br/>Let's use the DC analysis capability of the schematic tool to see superposition
+in action.  The sliders below control the resistances of R1, R2, R3 and R4 in all
+the diagrams.  As you move the sliders, the schematic tool will adjust the appropriate
+resistance, perform a DC analysis and display the node voltages on the diagrams.  Here's
+what you want to observe as you play with the sliders:
+
+<ul style="margin-left:2em;margin-top:1em;margin-right:2em;margin-bottom:1em;">
+<i>The voltage for a node in Figure 1 is the sum of the voltages for
+that node in Figures 2(a) and 2(b), just as predicted by
+superposition. (Note that due to round-off in the display of the
+voltages, the sum of the displayed voltages in Figure 2 may only be within
+.01 of the voltages displayed in Figure 1.)</i>
+</ul>
+
+<br>
+<center>
+<table><tr valign="top">
+<td>
+  <table>
+    <tr valign="top">
+      <td>R1</td>
+      <td>
+	<div id="r1_slider" style="width:200px; height:10px; margin-left:15px"></div>
+      </td>
+    </tr>
+    <tr valign="top">
+      <td>R2</td>
+      <td>
+	<div id="r2_slider" style="width:200px; height:10px; margin-left:15px; margin-top:10px;"></div>
+      </td>
+    </tr>
+    <tr valign="top">
+      <td>R3</td>
+      <td>
+	<div id="r3_slider" style="width:200px; height:10px; margin-left:15px; margin-top:10px;"></div>
+      </td>
+    </tr>
+    <tr valign="top">
+      <td>R4</td>
+      <td>
+	<div id="r4_slider" style="width:200px; height:10px; margin-left:15px; margin-top:10px;"></div>
+      </td>
+    </tr>
+  </table>
+</td></tr></table>
+</center>
diff --git a/test_root/data/custom_tags/.git-keep b/test_root/data/.gitkeep
similarity index 100%
rename from test_root/data/custom_tags/.git-keep
rename to test_root/data/.gitkeep
diff --git a/test_root/data/course_settings.py b/test_root/data/course_settings.py
deleted file mode 100644
index f4e9696d1d2d976901563ec71305b85cb563d069..0000000000000000000000000000000000000000
--- a/test_root/data/course_settings.py
+++ /dev/null
@@ -1,28 +0,0 @@
-GRADER = [
-    {
-        'type' : "Homework",
-        'min_count' : 12,
-        'drop_count' : 2,
-        'short_label' : "HW",
-        'weight' : 0.15,
-    },
-    {
-        'type' : "Lab",
-        'min_count' : 12,
-        'drop_count' : 2,
-        'category' : "Labs",
-        'weight' : 0.15
-    },
-    {
-        'type' : "Midterm",
-        'name' : "Midterm Exam",
-        'short_label' : "Midterm",
-        'weight' : 0.3,
-    },
-    {
-        'type' : "Final",
-        'name' : "Final Exam",
-        'short_label' : "Final",
-        'weight' : 0.4,
-    }
-]