Commit 5df381e7 by Bridger Maxwell

Merge remote-tracking branch 'origin/master' into feature/bridger/course_grading

parents 87da1049 9266bcca
......@@ -35,6 +35,7 @@ from path import path
MITX_FEATURES = {
'USE_DJANGO_PIPELINE': True,
'GITHUB_PUSH': False,
'ENABLE_DISCUSSION_SERVICE': False
}
# needed to use lms student app
......
......@@ -282,6 +282,9 @@ def add_user_to_default_group(user, group):
@receiver(post_save, sender=User)
def update_user_information(sender, instance, created, **kwargs):
if not settings.MITX_FEATURES['ENABLE_DISCUSSION_SERVICE']:
# Don't try--it won't work, and it will fill the logs with lots of errors
return
try:
cc_user = cc.User.from_django_user(instance)
cc_user.save()
......
......@@ -99,7 +99,14 @@ class CourseDescriptor(SequenceDescriptor):
def definition_from_xml(cls, xml_object, system):
textbooks = []
for textbook in xml_object.findall("textbook"):
textbooks.append(cls.Textbook.from_xml_object(textbook))
try:
txt = cls.Textbook.from_xml_object(textbook)
except:
# If we can't get to S3 (e.g. on a train with no internet), don't break
# the rest of the courseware.
log.exception("Couldn't load textbook")
continue
textbooks.append(txt)
xml_object.remove(textbook)
#Load the wiki tag if it exists
......
......@@ -29,6 +29,8 @@ class SequenceModule(XModule):
shared_state=None, **kwargs):
XModule.__init__(self, system, location, definition, descriptor,
instance_state, shared_state, **kwargs)
# NOTE: Position is 1-indexed. This is silly, but there are now student
# positions saved on prod, so it's not easy to fix.
self.position = 1
if instance_state is not None:
......
......@@ -212,7 +212,7 @@ class XModule(HTMLSnippet):
return self.metadata.get('display_name',
self.url_name.replace('_', ' '))
def __unicode__(self):
return '<x_module(name=%s, category=%s, id=%s)>' % (self.name, self.category, self.id)
return '<x_module(id={0})>'.format(self.id)
def get_children(self):
'''
......@@ -465,6 +465,16 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
return self._child_instances
def get_child_by_url_name(self, url_name):
"""
Return a child XModuleDescriptor with the specified url_name, if it exists, and None otherwise.
"""
for c in self.get_children():
if c.url_name == url_name:
return c
return None
def xmodule_constructor(self, system):
"""
Returns a constructor for an XModule. This constructor takes two
......
<course filename="6.002_Spring_2012" slug="6.002_Spring_2012" graceperiod="1 day 12 hours 59 minutes 59 seconds" showanswer="attempted" rerandomize="never" name="6.002 Spring 2012" start="2015-07-17T12:00" course="full" org="edx"/>
<course filename="6.002_Spring_2012" slug="6.002_Spring_2012" graceperiod="1 day 12 hours 59 minutes 59 seconds" showanswer="attempted" rerandomize="never" name="6.002 Spring 2012" start="2015-07-17T12:00" course="full" org="edX"/>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/sound.js"></script>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/plotter.js"></script>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/circuit.js"></script>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/mosfet_amplifier.js"></script>
<h2>LAB 5B: MOSFET AMPLIFIER EXPERIMENT</h2>
<section class="problem">
<startouttext />
<p>Note: This part of the lab is just to develop your intuition about
amplifiers and biasing, and to have fun with music! There are no responses
that need to be checked.</p>
<p>The graph plots the selected voltages from the amplifier circuit below. You
can also listen to various signals by selecting from the radio buttons to
the right of the graph. This way you can both see and hear various signals.
You can use the sliders to the right of the amplifier circuit to control
various parameters of the MOSFET and the amplifier. The parameter \(V_{MAX}\)
sets the maximum range on the plots. You can also select an input voltage
type (e.g., sine wave, square wave, various types of music) using the drop
down menu to the right of the graph. When describing AC signals, the
voltages on the sliders refer to peak-to-peak values.</p>
<p>1. To begin your first experiment, go ahead and use the pull down menu to
select a sine wave input. Then, adjust the sliders to an approximate
baseline setting shown below.</p>
<p>Baseline setting of sliders:
<br />
\(V_{S}=1.6V\), \(v_{IN}=3V\), \(Frequency=1000Hz\), \(V_{BIAS}=2.5V\), \(R=10K\Omega\), \(k=1mA/V^{2}\), \(V_{T}=1V\), \(V_{MAX}=2V\).</p>
<p>You will observe in the plot that the baseline setting of the sliders for
the various amplifiers parameters produces a distorted sine wave signal for
\(v_{OUT}\). Next, go ahead and select one of the music signals as the input and
listen to each of \(v_{IN}\) and \(v_{OUT}\), and confirm for yourself that the
output sounds distorted for the chosen slider settings. You will notice
that the graph now plots the music signal waveforms. Think about all the
reasons why the amplifier is producing a distorted output.</p>
<p>2. For the second experiment, we will study the amplifier's small signal
behavior. Select a sine wave as the input signal. To study the small
signal behavior, reduce the value of \(v_{IN}\) to 0.1V (peak-to-peak) by
using the \(v_{IN}\) slider. Keeping the rest of the parameters at their
baseline settings, derive an appropriate value of \(V_{BIAS}\) that will ensure
saturation region operation for the MOSFET for the 0.1V peak-to-peak swing
for \(v_{IN}\). Make sure to think about both positive and negative excursions
of the signals.</p>
</p>Next, use the \(V_{BIAS}\) slider to choose your computed value for \(V_{BIAS}\) and
see if the observed plot of \(v_{OUT}\) is more or less distortion free. If
your calculation was right, then the output will indeed be distortion free.</p>
<p>Next, select one of the music signals as the input and listen to each of
\(v_{IN}\) and \(v_{OUT}\), and confirm for yourself that the output sounds much
better than in Step 1. Also, based on sound volume, confirm that \(v_{OUT}\) is
an amplified version of \(v_{IN}\).</p>
<p>3. Now go ahead and experiment with various other settings while listening
to the music signal at \(v_{OUT}\). Observe the plots and listen to \(v_{OUT}\) as
you change, for example, the bias voltage \(V_{BIAS}\). You will notice that
the amplifier distorts the input signal when \(V_{BIAS}\) becomes too small, or
when it becomes too large. You can also experiment with various values of
\(v_{IN}\), \(R_{L}\), etc., and see how they affect the amplification and distortion.</p>
<endouttext />
</section>
<section class="tool-wrapper">
<div id="controlls-container">
<div class="graph-controls">
<div class="music-wrapper">
<select id="musicTypeSelect" size="1">
<option value = "0">Zero Input</option>
<option value = "1">Unit Impulse</option>
<option value = "2">Unit Step</option>
<option selected="selected" value = "3">Sine Wave</option>
<option value = "4">Square Wave</option>
<option value = "5">Classical Music</option>
<option value = "6">Folk Music</option>
<option value = "7">Jazz Music</option>
<option value = "8">Reggae Music</option>
</select>
<input id="playButton" type="button" value="Play" />
</div>
<div class="inputs-wrapper">
<div id="graph-output">
<p>Graph:</p>
<ul>
<li><label for="vinCheckbox"><input id="vinCheckbox" type="checkbox" checked="yes"/>v<sub>IN</sub></label></li>
<li><label for="voutCheckbox"><input id="voutCheckbox" type="checkbox" checked="yes"/>v<sub>OUT</sub></label> </li>
<li><label for="vrCheckbox"><input id="vrCheckbox" type="checkbox"/>v<sub>R</sub></label></li>
</ul>
</div>
<div id="graph-listen">
<p>Listen to:</p>
<ul>
<li><label for="vinRadioButton"><input id="vinRadioButton" type="radio" checked="yes" name="listenToWhat"/>v<sub>IN</sub></label></li>
<li><label for="voutRadioButton"><input id="voutRadioButton" type="radio" name="listenToWhat"/>v<sub>OUT</sub></label></li>
<li><label for="vrRadioButton"><input id="vrRadioButton" type="radio" name="listenToWhat"/>v<sub>R</sub></label></li>
</ul>
</div>
</div>
</div>
<div class="schematic-sliders">
<div class="slider-label" id="vs"></div>
<div class="slider" id="vsSlider"></div>
<div class="slider-label" id="vin"></div>
<div class="slider" id="vinSlider"></div>
<div class="slider-label" id="freq"></div>
<div class="slider" id="freqSlider"></div>
<div class="slider-label" id="vbias"></div>
<div class="slider" id="vbiasSlider"></div>
<div class="slider-label" id="r"></div>
<div class="slider" id="rSlider"></div>
<div class="slider-label" id="k"></div>
<div class="slider" id="kSlider"></div>
<div class="slider-label" id="vt"></div>
<div class="slider" id="vtSlider"></div>
<div class="slider-label" id="vmax"></div>
<div class="slider" id="vmaxSlider"></div>
</div>
</div>
<div id="graph-container">
<canvas id="graph" width="500" height="500">Your browser must support the Canvas element and have JavaScript enabled to view this tool.</canvas>
<canvas id="diag1" width="500" height="500">Your browser must support the Canvas element and have JavaScript enabled to view this tool.</canvas>
</div>
</section>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/sound.js"></script>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/plotter.js"></script>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/circuit.js"></script>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/rc_filters.js"></script>
<h2>LAB 10B: RC FILTERS WITH FREQUENCY RESPONSE EXPERIMENT</h2>
<section class="problem">
<startouttext />
<p>Note: Use this part of the lab to build your intuition about filters and frequency response, and to have fun with music! There are no responses that need to be checked.</p>
<p>Recall from the audio lab in Week 5 that the graph plots the selected voltages from the circuit shown below. This week the circuit is an RC filter. You can also listen to various signals by selecting from the radio buttons to the right of the graph. This way you can both see and hear various signals. You can use the sliders to the right of the circuit to control various circuit and input signal parameters. (Note that you can get finer control of some of the slider values by clicking on the slider and using the arrow keys). Recall that the parameter \(V_{MAX}\) sets the maximum range on the graph. You can also select an input voltage type (e.g., sine wave, square wave, various types of music) using the drop down menu to the right of the graph. When describing AC signals, the voltages on the sliders refer to peak-to-peak values.</p>
<p>1. To begin your first experiment, use the pull down menu to select a sine wave input. Then, adjust the sliders to these approximate baseline settings:
<br />
\(v_{IN} = 3V\), \(Frequency = 1000 Hz\), \(V_{BIAS} = 0V\), \(R = 1K\Omega\), \(v_C(0) = 0V\), \(C = 110nF\), \(V_{MAX} = 2V\).
<br />
Observe the waveforms for \(v_{IN}\) and \(v_C\) in the graph. You can also listen to \(v_{IN}\) and \(v_C\). You will observe that the amplitude of \(v_C\) is slightly smaller than the amplitude of \(v_{IN}\).
<br />
Compute the break frequency of the filter circuit for the given circuit parameters. (Note that the break frequency is also called the cutoff frequency or the corner frequency).
<br />
Change the frequency of the sinusoid so that it is approximately 3 times the break frequency.
<br />
Observe the waveforms for \(v_{IN}\) and \(v_C\) in the graph. Also listen to \(v_{IN}\) and \(v_C\). Think about why the sinusoid at \(v_C\) is significantly more attenuated than the original 1KHz sinusoid.
<br />
Keeping the input signal unchanged, observe the waveforms for \(v_{IN}\) and \(v_R\) in the graph. Also listen to \(v_{IN}\) and \(v_R\). Think about why the sinusoid at \(v_R\) is significantly louder than the sinusoid at \(v_C\).</p>
<p>2. Next, use the pull down menu to select a music signal of your choice. Adjust the sliders to the approximate baseline settings:
<br />
\(v_{IN} = 3V\), \(V_{BIAS} = 0V\), \(R = 1K\Omega\), \(v_C(0) = 0V\), \(C = 110nF\), \(V_{MAX} = 2V\).
<br />
Listen to the signals at \(v_{IN}\) and \(v_C\). Notice any difference between the signals?
<br />
Next, increase the capacitance value and observe the difference in the sound of \(v_{IN}\) and \(v_C\) as the capacitance increases. You should notice that the higher frequency components of \(v_C\) are attenuated as the capacitance is increased.
Convince yourself that when the signal is taken at \(v_C\), the circuit behaves like a low-pass filter.</p>
<p>3. Re-adjust the sliders to the approximate baseline settings:
<br />
\(v_{IN} = 3V\), \(V_{BIAS} = 0V\), \(R = 1K\Omega\), \(v_C(0) = 0V\), \(C = 110nF\), \(V_{MAX} = 2V\).
<br />
Try to create a high-pass filter from the same circuit by taking the signal output across a different element and possibly changing some of the element values.
</p>
<endouttext />
</section>
<section class="tool-wrapper">
<div id="controlls-container">
<div class="graph-controls">
<div class="music-wrapper">
<select id="musicTypeSelect" size="1">
<option value = "0">Zero Input</option>
<option value = "1">Unit Impulse</option>
<option value = "2">Unit Step</option>
<option selected="selected" value = "3">Sine Wave</option>
<option value = "4">Square Wave</option>
<option value = "5">Classical Music</option>
<option value = "6">Folk Music</option>
<option value = "7">Jazz Music</option>
<option value = "8">Reggae Music</option>
</select>
<input id="playButton" type="button" value="Play" />
</div>
<div class="inputs-wrapper">
<div id="graph-output">
<p>Graph:</p>
<ul>
<li><label for="vinCheckbox"><input id="vinCheckbox" type="checkbox" checked="yes"/>v<sub>IN</sub></label></li>
<li><label for="vcCheckbox"><input id="vcCheckbox" type="checkbox" checked="yes"/>v<sub>C</sub></label> </li>
<li><label for="vrCheckbox"><input id="vrCheckbox" type="checkbox"/>v<sub>R</sub></label></li>
</ul>
</div>
<div id="graph-listen">
<p>Listen to:</p>
<ul>
<li><label for="vinRadioButton"><input id="vinRadioButton" type="radio" checked="yes" name="listenToWhat"/>v<sub>IN</sub></label></li>
<li><label for="vcRadioButton"><input id="vcRadioButton" type="radio" name="listenToWhat"/>v<sub>C</sub></label></li>
<li><label for="vrRadioButton"><input id="vrRadioButton" type="radio" name="listenToWhat"/>v<sub>R</sub></label></li>
</ul>
</div>
</div>
</div>
<div class="schematic-sliders">
<div class="slider-label" id="fc">f<sub>C</sub> = </div>
<div class="slider-label" id="vin"></div>
<div class="slider" id="vinSlider"></div>
<div class="slider-label" id="freq"></div>
<div class="slider" id="freqSlider"></div>
<div class="slider-label" id="vbias"></div>
<div class="slider" id="vbiasSlider"></div>
<div class="slider-label" id="r"></div>
<div class="slider" id="rSlider"></div>
<div class="slider-label" id="vc0"></div>
<div class="slider" id="vc0Slider"></div>
<div class="slider-label" id="c"></div>
<div class="slider" id="cSlider"></div>
<div class="slider-label" id="vmax"></div>
<div class="slider" id="vmaxSlider"></div>
</div>
</div>
<div id="graph-container">
<div id="graphTabs">
<ul>
<li><a href="#time">Time</a></li>
<li><a href="#magnitude">Magnitude</a></li>
<li><a href="#phase">Phase</a></li>
</ul>
<canvas id="time" width="500" height="500">Your browser must support the Canvas element and have JavaScript enabled to view this tool.</canvas>
<canvas id="magnitude" width="500" height="500">Your browser must support the Canvas element and have JavaScript enabled to view this tool.</canvas>
<canvas id="phase" width="500" height="500">Your browser must support the Canvas element and have JavaScript enabled to view this tool.</canvas>
</div>
<canvas id="diag2" width="500" height="500">Your browser must support the Canvas element and have JavaScript enabled to view this tool.</canvas>
</div>
</section>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/sound.js"></script>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/plotter.js"></script>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/circuit.js"></script>
<script type="text/javascript" src="/static/courses/6002/js/sound_labs/series_rlc.js"></script>
<h2>SERIES RLC CIRCUIT WITH FREQUENCY RESPONSE EXPERIMENT</h2>
<section class="problem">
<startouttext />
<p>\(I(s) = \frac{1}{R + Ls + 1/Cs}V_{in}(s) = \frac{s/L}{s^2 + sR/L + 1/LC}V_{in}(s)\)</p>
<p>\(I(s) = \frac{s/L}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s)\)</p>
<p>\(\omega_0 = \frac{1}{\sqrt{LC}} , \alpha = \frac{R}{2L}\)</p>
<p>Band-Pass Filter:</p>
<p>\(V_r(s) = RI(s) = \frac{sR/L}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s) = \frac{2\alpha s}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s) = \frac{2\alpha s}{(s-s_1)(s-s_2)}V_{in}(s)\)</p>
<p>Gain magnitude: \(G_R = \frac{2\alpha w}{|j\omega - s_1||j\omega - s_2|}\)</p>
<p>Phase: \(\Phi_R = \pi/2-\Phi(j\omega - s_1) -\Phi(j\omega - s_2)\)</p>
<p>Low-Pass Filter:</p>
<p>\(V_c(s) = I(s)/sC = \frac{1/LC}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s) = \frac{\omega_0^2}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s) = \frac{\omega_0^2}{(s-s_1)(s-s_2)}V_{in}(s)\)</p>
<p>Gain magnitude: \(G_C = \frac{\omega_0^2}{|j\omega - s_1||j\omega - s_2|}\)</p>
<p>Phase: \(\Phi_C = -\Phi(j\omega - s_1) -\Phi(j\omega - s_2)\)</p>
<p>High-Pass Filter:</p>
<p>\(V_l(s) = sLI(s) = \frac{s^2}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s) = \frac{s^2}{(s-s_1)(s-s_2)}V_{in}(s)\)</p>
<p>Gain magnitude: \(G_L = \frac{\omega^2}{|j\omega - s_1||j\omega - s_2|}\)</p>
<p>Phase: \(\Phi_L = -\Phi(j\omega - s_1) -\Phi(j\omega - s_2)\)</p>
<br />
<p>Under-Damped: \(\alpha < \omega_0\)</p>
<p>Complex roots: \(s_{1,2} = -\alpha \pm j\sqrt{\omega_0^2 - \alpha^2}\)</p>
<p>Critically-Damped: \(\alpha = \omega_0\)</p>
<p>Double real root: \(s_{1,2} = -\alpha\)</p>
<p>Over-Damped: \(\alpha > \omega_0\)</p>
<p>Real roots: \(s_{1,2} = -\alpha \pm\sqrt{\alpha^2 - \omega_0^2}\)</p>
<endouttext />
</section>
<section class="tool-wrapper">
<div id="controlls-container">
<div class="graph-controls">
<div class="music-wrapper">
<select id="musicTypeSelect" size="1">
<option value = "0">Zero Input</option>
<option value = "1">Unit Impulse</option>
<option value = "2">Unit Step</option>
<option selected="selected" value = "3">Sine Wave</option>
<option value = "4">Square Wave</option>
<option value = "5">Classical Music</option>
<option value = "6">Folk Music</option>
<option value = "7">Jazz Music</option>
<option value = "8">Reggae Music</option>
</select>
<input id="playButton" type="button" value="Play" />
</div>
<div class="inputs-wrapper">
<div id="graph-output">
<p>Graph:</p>
<ul>
<li><label for="vinCheckbox"><input id="vinCheckbox" type="checkbox" checked="yes"/>v<sub>IN</sub></label></li>
<li><label for="vrCheckbox"><input id="vrCheckbox" type="checkbox"/>v<sub>R</sub></label></li>
<li><label for="vlCheckbox"><input id="vlCheckbox" type="checkbox"/>v<sub>L</sub></label></li>
<li><label for="vcCheckbox"><input id="vcCheckbox" type="checkbox" checked="yes"/>v<sub>C</sub></label> </li>
</ul>
</div>
<div id="graph-listen">
<p>Listen to:</p>
<ul>
<li><label for="vinRadioButton"><input id="vinRadioButton" type="radio" checked="yes" name="listenToWhat"/>v<sub>IN</sub></label></li>
<li><label for="vrRadioButton"><input id="vrRadioButton" type="radio" name="listenToWhat"/>v<sub>R</sub></label></li>
<li><label for="vlRadioButton"><input id="vlRadioButton" type="radio" name="listenToWhat"/>v<sub>L</sub></label></li>
<li><label for="vcRadioButton"><input id="vcRadioButton" type="radio" name="listenToWhat"/>v<sub>C</sub></label></li>
</ul>
</div>
</div>
</div>
<div class="schematic-sliders">
<div class="slider-label" id="vin"></div>
<div class="slider" id="vinSlider"></div>
<div class="slider-label" id="freq"></div>
<div class="slider" id="freqSlider"></div>
<div class="slider-label" id="vbias"></div>
<div class="slider" id="vbiasSlider"></div>
<div class="slider-label" id="r"></div>
<div class="slider" id="rSlider"></div>
<div class="slider-label" id="l"></div>
<div class="slider" id="lSlider"></div>
<div class="slider-label" id="c"></div>
<div class="slider" id="cSlider"></div>
<div class="slider-label" id="vc0"></div>
<div class="slider" id="vc0Slider"></div>
<div class="slider-label" id="i0"></div>
<div class="slider" id="i0Slider"></div>
<div class="slider-label" id="vmax"></div>
<div class="slider" id="vmaxSlider"></div>
</div>
</div>
<div id="graph-container">
<div id="graphTabs">
<ul>
<li><a href="#time">Time</a></li>
<li><a href="#magnitude">Magnitude</a></li>
<li><a href="#phase">Phase</a></li>
</ul>
<canvas id="time" width="500" height="500">Your browser must support the Canvas element and have JavaScript enabled to view this tool.</canvas>
<canvas id="magnitude" width="500" height="500">Your browser must support the Canvas element and have JavaScript enabled to view this tool.</canvas>
<canvas id="phase" width="500" height="500">Your browser must support the Canvas element and have JavaScript enabled to view this tool.</canvas>
</div>
<canvas id="diag3" width="500" height="500">Your browser must support the Canvas element and have JavaScript enabled to view this tool.</canvas>
</div>
</section>
......@@ -52,7 +52,7 @@ def make_track_function(request):
return f
def toc_for_course(user, request, course, active_chapter, active_section, course_id=None):
def toc_for_course(user, request, course, active_chapter, active_section):
'''
Create a table of contents from the module store
......@@ -75,13 +75,13 @@ def toc_for_course(user, request, course, active_chapter, active_section, course
'''
student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(
course_id, user, course, depth=2)
course = get_module(user, request, course.location, student_module_cache, course_id)
if course is None:
course.id, user, course, depth=2)
course_module = get_module(user, request, course.location, student_module_cache, course.id)
if course_module is None:
return None
chapters = list()
for chapter in course.get_display_items():
for chapter in course_module.get_display_items():
hide_from_toc = chapter.metadata.get('hide_from_toc','false').lower() == 'true'
if hide_from_toc:
continue
......@@ -109,36 +109,6 @@ def toc_for_course(user, request, course, active_chapter, active_section, course
return chapters
def get_section(course_module, chapter, section):
"""
Returns the xmodule descriptor for the name course > chapter > section,
or None if this doesn't specify a valid section
course: Course url
chapter: Chapter url_name
section: Section url_name
"""
if course_module is None:
return
chapter_module = None
for _chapter in course_module.get_children():
if _chapter.url_name == chapter:
chapter_module = _chapter
break
if chapter_module is None:
return
section_module = None
for _section in chapter_module.get_children():
if _section.url_name == section:
section_module = _section
break
return section_module
def get_module(user, request, location, student_module_cache, course_id, position=None):
"""
Get an instance of the xmodule class identified by location,
......@@ -293,9 +263,10 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
return module
# TODO (vshnayder): Rename this? It's very confusing.
def get_instance_module(course_id, user, module, student_module_cache):
"""
Returns instance_module is a StudentModule specific to this module for this student,
Returns the StudentModule specific to this module for this student,
or None if this is an anonymous user
"""
if user.is_authenticated():
......
......@@ -7,6 +7,7 @@ import time
from nose import SkipTest
from path import path
from pprint import pprint
from urlparse import urlsplit, urlunsplit
from django.contrib.auth.models import User, Group
from django.test import TestCase
......@@ -83,6 +84,27 @@ REAL_DATA_MODULESTORE = mongo_store_config(REAL_DATA_DIR)
class ActivateLoginTestCase(TestCase):
'''Check that we can activate and log in'''
def assertRedirectsNoFollow(self, response, expected_url):
"""
http://devblog.point2.com/2010/04/23/djangos-assertredirects-little-gotcha/
Don't check that the redirected-to page loads--there should be other tests for that.
Some of the code taken from django.test.testcases.py
"""
self.assertEqual(response.status_code, 302,
'Response status code was {0} instead of 302'.format(response.status_code))
url = response['Location']
e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit(
expected_url)
if not (e_scheme or e_netloc):
expected_url = urlunsplit(('http', 'testserver', e_path,
e_query, e_fragment))
self.assertEqual(url, expected_url, "Response redirected to '{0}', expected '{1}'".format(
url, expected_url))
def setUp(self):
email = 'view@test.com'
password = 'foo'
......@@ -193,6 +215,18 @@ class PageLoader(ActivateLoginTestCase):
data = parse_json(resp)
self.assertTrue(data['success'])
def check_for_get_code(self, code, url):
"""
Check that we got the expected code. Hacks around our broken 404
handling.
"""
resp = self.client.get(url)
self.assertEqual(resp.status_code, code,
"got code {0} for url '{1}'. Expected code {2}"
.format(resp.status_code, url, code))
def check_pages_load(self, course_name, data_dir, modstore):
"""Make all locations in course load"""
print "Checking course {0} in {1}".format(course_name, data_dir)
......@@ -204,7 +238,7 @@ class PageLoader(ActivateLoginTestCase):
course = courses[0]
self.enroll(course)
course_id = course.id
n = 0
num_bad = 0
all_ok = True
......@@ -246,6 +280,61 @@ class TestCoursesLoadTestCase(PageLoader):
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
class TestNavigation(PageLoader):
"""Check that navigation state is saved properly"""
def setUp(self):
xmodule.modulestore.django._MODULESTORES = {}
courses = modulestore().get_courses()
def find_course(course_id):
"""Assumes the course is present"""
return [c for c in courses if c.id==course_id][0]
self.full = find_course("edX/full/6.002_Spring_2012")
self.toy = find_course("edX/toy/2012_Fall")
# Create two accounts
self.student = 'view@test.com'
self.student2 = 'view2@test.com'
self.password = 'foo'
self.create_account('u1', self.student, self.password)
self.create_account('u2', self.student2, self.password)
self.activate_user(self.student)
self.activate_user(self.student2)
def test_accordion_state(self):
"""Make sure that the accordion remembers where you were properly"""
self.login(self.student, self.password)
self.enroll(self.toy)
self.enroll(self.full)
# First request should redirect to ToyVideos
resp = self.client.get(reverse('courseware', kwargs={'course_id': self.toy.id}))
# Don't use no-follow, because state should only be saved once we actually hit the section
self.assertRedirects(resp, reverse(
'courseware_section', kwargs={'course_id': self.toy.id,
'chapter': 'Overview',
'section': 'Toy_Videos'}))
# Hitting the couseware tab again should redirect to the first chapter: 'Overview'
resp = self.client.get(reverse('courseware', kwargs={'course_id': self.toy.id}))
self.assertRedirectsNoFollow(resp, reverse('courseware_chapter',
kwargs={'course_id': self.toy.id, 'chapter': 'Overview'}))
# Now we directly navigate to a section in a different chapter
self.check_for_get_code(200, reverse('courseware_section',
kwargs={'course_id': self.toy.id,
'chapter':'secret:magic', 'section':'toyvideo'}))
# And now hitting the courseware tab should redirect to 'secret:magic'
resp = self.client.get(reverse('courseware', kwargs={'course_id': self.toy.id}))
self.assertRedirectsNoFollow(resp, reverse('courseware_chapter',
kwargs={'course_id': self.toy.id, 'chapter': 'secret:magic'}))
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
class TestViewAuth(PageLoader):
"""Check that view authentication works properly"""
......@@ -256,12 +345,12 @@ class TestViewAuth(PageLoader):
xmodule.modulestore.django._MODULESTORES = {}
courses = modulestore().get_courses()
def find_course(name):
def find_course(course_id):
"""Assumes the course is present"""
return [c for c in courses if c.location.course==name][0]
return [c for c in courses if c.id==course_id][0]
self.full = find_course("full")
self.toy = find_course("toy")
self.full = find_course("edX/full/6.002_Spring_2012")
self.toy = find_course("edX/toy/2012_Fall")
# Create two accounts
self.student = 'view@test.com'
......@@ -272,19 +361,6 @@ class TestViewAuth(PageLoader):
self.activate_user(self.student)
self.activate_user(self.instructor)
def check_for_get_code(self, code, url):
resp = self.client.get(url)
# HACK: workaround the bug that returns 200 instead of 404.
# TODO (vshnayder): once we're returning 404s, get rid of this if.
if code != 404:
self.assertEqual(resp.status_code, code)
# And 'page not found' shouldn't be in the returned page
self.assertTrue(resp.content.lower().find('page not found') == -1)
else:
# look for "page not found" instead of the status code
#print resp.content
self.assertTrue(resp.content.lower().find('page not found') != -1)
def test_instructor_pages(self):
"""Make sure only instructors for the course or staff can load the instructor
dashboard, the grade views, and student profile pages"""
......@@ -292,11 +368,15 @@ class TestViewAuth(PageLoader):
# First, try with an enrolled student
self.login(self.student, self.password)
# shouldn't work before enroll
self.check_for_get_code(302, reverse('courseware', kwargs={'course_id': self.toy.id}))
response = self.client.get(reverse('courseware', kwargs={'course_id': self.toy.id}))
self.assertRedirectsNoFollow(response, reverse('about_course', args=[self.toy.id]))
self.enroll(self.toy)
self.enroll(self.full)
# should work now
self.check_for_get_code(200, reverse('courseware', kwargs={'course_id': self.toy.id}))
# should work now -- redirect to first page
response = self.client.get(reverse('courseware', kwargs={'course_id': self.toy.id}))
self.assertRedirectsNoFollow(response, reverse('courseware_section', kwargs={'course_id': self.toy.id,
'chapter': 'Overview',
'section': 'Toy_Videos'}))
def instructor_urls(course):
"list of urls that only instructors/staff should be able to see"
......@@ -389,7 +469,7 @@ class TestViewAuth(PageLoader):
list of urls that students should be able to see only
after launch, but staff should see before
"""
urls = reverse_urls(['info', 'courseware', 'progress'], course)
urls = reverse_urls(['info', 'progress'], course)
urls.extend([
reverse('book', kwargs={'course_id': course.id, 'book_index': book.title})
for book in course.textbooks
......@@ -417,7 +497,7 @@ class TestViewAuth(PageLoader):
def check_non_staff(course):
"""Check that access is right for non-staff in course"""
print '=== Checking non-staff access for {0}'.format(course.id)
for url in instructor_urls(course) + dark_student_urls(course):
for url in instructor_urls(course) + dark_student_urls(course) + reverse_urls(['courseware'], course):
print 'checking for 404 on {0}'.format(url)
self.check_for_get_code(404, url)
......@@ -444,6 +524,10 @@ class TestViewAuth(PageLoader):
print 'checking for 404 on view-as-student: {0}'.format(url)
self.check_for_get_code(404, url)
# The courseware url should redirect, not 200
url = reverse_urls(['courseware'], course)[0]
self.check_for_get_code(302, url)
# First, try with an enrolled student
print '=== Testing student access....'
......
......@@ -9,7 +9,9 @@ from django.utils import simplejson
from django.db import connection
from django.conf import settings
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django_comment_client.permissions import check_permissions_by_view
from django_comment_client.models import Role
from mitxmako import middleware
import logging
......@@ -226,11 +228,15 @@ def permalink(content):
args=[content['course_id'], content['commentable_id'], content['thread_id']]) + '#' + content['id']
def extend_content(content):
user = User.objects.get(pk=content['user_id'])
roles = dict(('name', role.name.lower()) for role in user.roles.filter(course_id=content['course_id']))
content_info = {
'displayed_title': content.get('highlighted_title') or content.get('title', ''),
'displayed_body': content.get('highlighted_body') or content.get('body', ''),
'raw_tags': ','.join(content.get('tags', [])),
'permalink': permalink(content),
'roles': roles,
'updated': content['created_at']!=content['updated_at'],
}
return merge_dict(content, content_info)
......
......@@ -194,6 +194,7 @@ def instructor_dashboard(request, course_id):
'instructor_access': instructor_access,
'datatable': datatable,
'msg': msg,
'course_errors': modulestore().get_item_errors(course.location),
}
return render_to_response('courseware/instructor_dashboard.html', context)
......
......@@ -6,6 +6,7 @@
from mitxmako.shortcuts import render_to_response, render_to_string
from django.shortcuts import redirect
from django.conf import settings
from django.http import HttpResponseNotFound, HttpResponseServerError
from django_future.csrf import ensure_csrf_cookie
from util.cache import cache_if_anonymous
......@@ -40,9 +41,9 @@ def render(request, template):
def render_404(request):
return render_to_response('static_templates/404.html', {})
return HttpResponseNotFound(render_to_string('static_templates/404.html', {}))
def render_500(request):
return render_to_response('static_templates/server-error.html', {})
return HttpResponseServerError(render_to_string('static_templates/server-error.html', {}))
......@@ -267,12 +267,22 @@ STATICFILES_DIRS = [
PROJECT_ROOT / "askbot" / "skins",
]
if os.path.isdir(DATA_DIR):
# Add the full course repo if there is no static directory
STATICFILES_DIRS += [
# TODO (cpennington): When courses are stored in a database, this
# should no longer be added to STATICFILES
(course_dir, DATA_DIR / course_dir)
for course_dir in os.listdir(DATA_DIR)
if os.path.isdir(DATA_DIR / course_dir)
if (os.path.isdir(DATA_DIR / course_dir) and
not os.path.isdir(DATA_DIR / course_dir / 'static'))
]
# Otherwise, add only the static directory from the course dir
STATICFILES_DIRS += [
# TODO (cpennington): When courses are stored in a database, this
# should no longer be added to STATICFILES
(course_dir, DATA_DIR / course_dir / 'static')
for course_dir in os.listdir(DATA_DIR)
if (os.path.isdir(DATA_DIR / course_dir / 'static'))
]
# Locale/Internationalization
......
......@@ -16,6 +16,9 @@ from path import path
# can test everything else :)
MITX_FEATURES['DISABLE_START_DATES'] = True
# Until we have discussion actually working in test mode, just turn it off
MITX_FEATURES['ENABLE_DISCUSSION_SERVICE'] = False
# Need wiki for courseware views to work. TODO (vshnayder): shouldn't need it.
WIKI_ENABLED = True
......@@ -43,6 +46,7 @@ DATA_DIR = COURSES_ROOT
LOGGING = get_logger_config(TEST_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
dev_env=True,
debug=True)
COMMON_TEST_DATA_ROOT = COMMON_ROOT / "test" / "data"
......
......@@ -374,6 +374,9 @@ if Backbone?
MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")]
initTimeago: ->
@$("span.timeago").each (index, element) ->
elem = $(element)
elem.html("posted on #{$.timeago.parse(elem.html()).toLocaleString()}")
@$("span.timeago").timeago()
renderPartial: ->
......
......@@ -29,3 +29,11 @@ $ ->
window.postJSON = (url, data, callback) ->
$.postWithPrefix url, data, callback
$('#login').click ->
$('#login_form input[name="email"]').focus()
false
$('#signup').click ->
$('#signup-modal input[name="email"]').focus()
false
......@@ -390,6 +390,14 @@ $tag-text-color: #5b614f;
color: #dea03e;
}
}
.author-moderator:after{
content: " (moderator)"
}
.author-administrator:after{
content: " (instructor)"
}
}
.discussion-content {
......@@ -415,6 +423,13 @@ $tag-text-color: #5b614f;
}
}
// Role based styles
.role-moderator{
background-color: #eafcfc;
}
.role-administrator{
background-color: #eafcea;
}
//COMMENT STYLES
.comments {
overflow: hidden;
......
......@@ -56,19 +56,6 @@
<section class="course-content">
${content}
% if course_errors is not UNDEFINED:
<h2>Course errors</h2>
<div id="course-errors">
<ul>
% for (msg, err) in course_errors:
<li>${msg | h}
<ul><li><pre>${err | h}</pre></li></ul>
</li>
% endfor
</ul>
</div>
% endif
</section>
</div>
</section>
......
......@@ -64,17 +64,17 @@ table.stat_table td {
%if instructor_access:
<hr width="40%" style="align:left">
<p>
<input type="submit" name="action" value="List course staff members">
<input type="submit" name="action" value="List course staff members">
<p>
<input type="text" name="staffuser"> <input type="submit" name="action" value="Remove course staff">
<input type="submit" name="action" value="Add course staff">
<input type="text" name="staffuser"> <input type="submit" name="action" value="Remove course staff">
<input type="submit" name="action" value="Add course staff">
<hr width="40%" style="align:left">
%endif
%if settings.MITX_FEATURES['ENABLE_MANUAL_GIT_RELOAD'] and admin_access:
<p>
<input type="submit" name="action" value="Reload course from XML files">
<input type="submit" name="action" value="GIT pull and Reload course">
<input type="submit" name="action" value="Reload course from XML files">
<input type="submit" name="action" value="GIT pull and Reload course">
%endif
</form>
......@@ -101,9 +101,26 @@ table.stat_table td {
</p>
%if msg:
<p>${msg}</p>
<p>${msg}</p>
%endif
% if course_errors is not UNDEFINED:
<h2>Course errors</h2>
<div id="course-errors">
<ul>
% for (summary, err) in course_errors:
<li>${summary | h}
% if err:
<ul><li><pre>${err | h}</pre></li></ul>
% else:
<p>&nbsp;</p>
% endif
</li>
% endfor
</ul>
</div>
% endif
</section>
</div>
</section>
<h2>${chapter_module.display_name}</h2>
<p>You were most recently in <a href="${prev_section_url}">${prev_section.display_name}</a>. If you're done with that, choose another section on the left.</p>
<div class="discussion-content local">
<div class="discussion-content local{{#content.roles}} role-{{name}}{{/content.roles}}">
<div class="discussion-content-wrapper">
<div class="discussion-votes">
<a class="discussion-vote discussion-vote-up" href="javascript:void(0)" value="up">&#9650;</a>
......@@ -34,12 +34,15 @@
</div>
<div class="info">
<div class="comment-time">
<span class="timeago" title="{{content.updated_at}}">sometime</span> by
{{#content.updated}}
updated
{{/content.updated}}
<span class="timeago" title="{{content.updated_at}}">{{content.created_at}}</span> by
{{#content.anonymous}}
anonymous
{{/content.anonymous}}
{{^content.anonymous}}
<a href="{{##url_for_user}}{{content.user_id}}{{/url_for_user}}">{{content.username}}</a>
<a href="{{##url_for_user}}{{content.user_id}}{{/url_for_user}}" class="{{#content.roles}}author-{{name}} {{/content.roles}}">{{content.username}}</a>
{{/content.anonymous}}
</div>
<div class="show-comments-wrapper">
......
......@@ -8,12 +8,20 @@
<title>EdX Blog</title>
<updated>2012-07-16T14:08:12-07:00</updated>
<entry>
<id>tag:www.edx.org,2012:Post/4</id>
<published>2012-09-06T14:00:00-07:00</published>
<updated>2012-09-06T14:00:00-07:00</updated>
<link type="text/html" rel="alternate" href="${reverse('press/edX-announces-proctored-exam-testing')}"/>
<title>EdX to offer learners option of taking proctored final exam</title>
<content type="html">&lt;img src=&quot;${static.url('images/press/diploma_109x65.jpg')}&quot; /&gt;</content>
</entry>
<entry>
<id>tag:www.edx.org,2012:Post/3</id>
<published>2012-07-16T14:08:12-07:00</published>
<updated>2012-07-16T14:08:12-07:00</updated>
<link type="text/html" rel="alternate" href="${reverse('press/uc-berkeley-joins-edx')}"/>
<title>UC Berkeley joins edX</title>
<content type="html">&lt;img src=&quot;${static.url('images/edx.png')}&quot; /&gt;
<content type="html">&lt;img src=&quot;${static.url('images/edx.png')}&quot; /&gt;
&lt;p&gt;edX broadens course offerings&lt;/p&gt;</content>
</entry>
<entry>
......
......@@ -227,3 +227,13 @@ namespace :cms do
end
end
end
desc "Build a properties file used to trigger autodeploy builds"
task :autodeploy_properties do
File.open("autodeploy.properties", "w") do |file|
file.puts("UPSTREAM_NOOP=false")
file.puts("UPSTREAM_BRANCH=#{BRANCH}")
file.puts("UPSTREAM_JOB=#{PACKAGE_NAME}")
file.puts("UPSTREAM_REVISION=#{COMMIT}")
end
end
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