Commit 6bf0e093 by Calen Pennington

Add tests for the edit page for the toy course, and make sure that exporting to…

Add tests for the edit page for the toy course, and make sure that exporting to github sets up the git repo properly
parent 39b59484
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')
......@@ -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
......
......@@ -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:
......
......@@ -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')
......
......@@ -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')
......
......@@ -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',
......
......@@ -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,
}
}
}
......
......@@ -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())
......
......@@ -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):
"""
......
<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>
<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>
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,
}
]
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