Commit 725e4908 by Sarina Canelake

Remove more pep8 violations

parent 09c1f524
# pylint: disable=C0111 # pylint: disable=missing-docstring
from lettuce import world, step from lettuce import world, step
from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.keys import Keys
...@@ -20,9 +20,10 @@ SELECTORS = { ...@@ -20,9 +20,10 @@ SELECTORS = {
# We should wait 300 ms for event handler invocation + 200ms for safety. # We should wait 300 ms for event handler invocation + 200ms for safety.
DELAY = 0.5 DELAY = 0.5
@step('youtube stub server (.*) YouTube API') @step('youtube stub server (.*) YouTube API')
def configure_youtube_api(_step, action): def configure_youtube_api(_step, action):
action=action.strip() action = action.strip()
if action == 'proxies': if action == 'proxies':
world.youtube.config['youtube_api_blocked'] = False world.youtube.config['youtube_api_blocked'] = False
elif action == 'blocks': elif action == 'blocks':
...@@ -30,6 +31,7 @@ def configure_youtube_api(_step, action): ...@@ -30,6 +31,7 @@ def configure_youtube_api(_step, action):
else: else:
raise ValueError('Parameter `action` should be one of "proxies" or "blocks".') raise ValueError('Parameter `action` should be one of "proxies" or "blocks".')
@step('I have created a Video component$') @step('I have created a Video component$')
def i_created_a_video_component(step): def i_created_a_video_component(step):
step.given('I am in Studio editing a new unit') step.given('I am in Studio editing a new unit')
...@@ -47,6 +49,7 @@ def i_created_a_video_component(step): ...@@ -47,6 +49,7 @@ def i_created_a_video_component(step):
if not world.youtube.config.get('youtube_api_blocked'): if not world.youtube.config.get('youtube_api_blocked'):
world.wait_for_visible(SELECTORS['controls']) world.wait_for_visible(SELECTORS['controls'])
@step('I have created a Video component with subtitles$') @step('I have created a Video component with subtitles$')
def i_created_a_video_with_subs(_step): def i_created_a_video_with_subs(_step):
_step.given('I have created a Video component with subtitles "OEoXaMPEzfM"') _step.given('I have created a Video component with subtitles "OEoXaMPEzfM"')
...@@ -221,7 +224,7 @@ def see_a_range_slider_with_proper_range(_step): ...@@ -221,7 +224,7 @@ def see_a_range_slider_with_proper_range(_step):
def do_not_see_or_not_button_video(_step, action, button_type): def do_not_see_or_not_button_video(_step, action, button_type):
world.wait(DELAY) world.wait(DELAY)
world.wait_for_ajax_complete() world.wait_for_ajax_complete()
action=action.strip() action = action.strip()
button = button_type.strip() button = button_type.strip()
if action == 'do not': if action == 'do not':
assert not world.is_css_present(VIDEO_BUTTONS[button]) assert not world.is_css_present(VIDEO_BUTTONS[button])
......
...@@ -607,11 +607,13 @@ class CourseMetadataEditingTest(CourseTestCase): ...@@ -607,11 +607,13 @@ class CourseMetadataEditingTest(CourseTestCase):
def test_correct_http_status(self): def test_correct_http_status(self):
json_data = json.dumps({ json_data = json.dumps({
"advertised_start": {"value": 1, "display_name": "Course Advertised Start Date", }, "advertised_start": {"value": 1, "display_name": "Course Advertised Start Date", },
"days_early_for_beta": {"value": "supposed to be an integer", "days_early_for_beta": {
"display_name": "Days Early for Beta Users", }, "value": "supposed to be an integer",
"advanced_modules": {"value": 1, "display_name": "Advanced Module List", }, "display_name": "Days Early for Beta Users",
}) },
"advanced_modules": {"value": 1, "display_name": "Advanced Module List", },
})
response = self.client.ajax_post(self.course_setting_url, json_data) response = self.client.ajax_post(self.course_setting_url, json_data)
self.assertEqual(400, response.status_code) self.assertEqual(400, response.status_code)
...@@ -623,7 +625,7 @@ class CourseMetadataEditingTest(CourseTestCase): ...@@ -623,7 +625,7 @@ class CourseMetadataEditingTest(CourseTestCase):
"days_early_for_beta": {"value": 2}, "days_early_for_beta": {"value": 2},
}, },
user=self.user user=self.user
) )
self.update_check(test_model) self.update_check(test_model)
# try fresh fetch to ensure persistence # try fresh fetch to ensure persistence
fresh = modulestore().get_course(self.course.id) fresh = modulestore().get_course(self.course.id)
......
...@@ -66,8 +66,10 @@ class TemplateTests(unittest.TestCase): ...@@ -66,8 +66,10 @@ class TemplateTests(unittest.TestCase):
self.assertEqual(index_info['course'], 'course') self.assertEqual(index_info['course'], 'course')
self.assertEqual(index_info['run'], '2014') self.assertEqual(index_info['run'], '2014')
test_chapter = persistent_factories.ItemFactory.create(display_name='chapter 1', test_chapter = persistent_factories.ItemFactory.create(
parent_location=test_course.location) display_name='chapter 1',
parent_location=test_course.location
)
self.assertIsInstance(test_chapter, SequenceDescriptor) self.assertIsInstance(test_chapter, SequenceDescriptor)
# refetch parent which should now point to child # refetch parent which should now point to child
test_course = self.split_store.get_course(test_course.id.version_agnostic()) test_course = self.split_store.get_course(test_course.id.version_agnostic())
...@@ -156,8 +158,10 @@ class TemplateTests(unittest.TestCase): ...@@ -156,8 +158,10 @@ class TemplateTests(unittest.TestCase):
course='history', run='doomed', org='edu.harvard', course='history', run='doomed', org='edu.harvard',
display_name='doomed test course', display_name='doomed test course',
user_id='testbot') user_id='testbot')
persistent_factories.ItemFactory.create(display_name='chapter 1', persistent_factories.ItemFactory.create(
parent_location=test_course.location) display_name='chapter 1',
parent_location=test_course.location
)
id_locator = test_course.id.for_branch(ModuleStoreEnum.BranchName.draft) id_locator = test_course.id.for_branch(ModuleStoreEnum.BranchName.draft)
guid_locator = test_course.location.course_agnostic() guid_locator = test_course.location.course_agnostic()
...@@ -180,10 +184,17 @@ class TemplateTests(unittest.TestCase): ...@@ -180,10 +184,17 @@ class TemplateTests(unittest.TestCase):
display_name='history test course', display_name='history test course',
user_id='testbot' user_id='testbot'
) )
chapter = persistent_factories.ItemFactory.create(display_name='chapter 1', chapter = persistent_factories.ItemFactory.create(
parent_location=test_course.location, user_id='testbot') display_name='chapter 1',
sub = persistent_factories.ItemFactory.create(display_name='subsection 1', parent_location=test_course.location,
parent_location=chapter.location, user_id='testbot', category='vertical') user_id='testbot'
)
sub = persistent_factories.ItemFactory.create(
display_name='subsection 1',
parent_location=chapter.location,
user_id='testbot',
category='vertical'
)
first_problem = persistent_factories.ItemFactory.create( first_problem = persistent_factories.ItemFactory.create(
display_name='problem 1', parent_location=sub.location, user_id='testbot', category='problem', display_name='problem 1', parent_location=sub.location, user_id='testbot', category='problem',
data="<problem></problem>" data="<problem></problem>"
......
...@@ -16,6 +16,7 @@ from django.conf import settings ...@@ -16,6 +16,7 @@ from django.conf import settings
from mako.template import Template from mako.template import Template
import textwrap import textwrap
class Command(NoArgsCommand): class Command(NoArgsCommand):
""" """
Basic management command to preprocess asset template files. Basic management command to preprocess asset template files.
...@@ -45,7 +46,6 @@ class Command(NoArgsCommand): ...@@ -45,7 +46,6 @@ class Command(NoArgsCommand):
self.__preprocess(os.path.join(root, filename), self.__preprocess(os.path.join(root, filename),
os.path.join(root, outfile)) os.path.join(root, outfile))
def __context(self): def __context(self):
""" """
Return a dict that contains all of the available context Return a dict that contains all of the available context
...@@ -55,10 +55,9 @@ class Command(NoArgsCommand): ...@@ -55,10 +55,9 @@ class Command(NoArgsCommand):
# TODO: do this with the django-settings-context-processor # TODO: do this with the django-settings-context-processor
return { return {
"FEATURES": settings.FEATURES, "FEATURES": settings.FEATURES,
"THEME_NAME" : getattr(settings, "THEME_NAME", None), "THEME_NAME": getattr(settings, "THEME_NAME", None),
} }
def __preprocess(self, infile, outfile): def __preprocess(self, infile, outfile):
""" """
Run `infile` through the Mako template engine, storing the Run `infile` through the Mako template engine, storing the
...@@ -73,4 +72,3 @@ class Command(NoArgsCommand): ...@@ -73,4 +72,3 @@ class Command(NoArgsCommand):
*/ */
""" % infile)) """ % infile))
_outfile.write(Template(filename=str(infile)).render(env=self.__context())) _outfile.write(Template(filename=str(infile)).render(env=self.__context()))
...@@ -168,8 +168,8 @@ class TestSafeExecCaching(unittest.TestCase): ...@@ -168,8 +168,8 @@ class TestSafeExecCaching(unittest.TestCase):
def test_unicode_submission(self): def test_unicode_submission(self):
# Check that using non-ASCII unicode does not raise an encoding error. # Check that using non-ASCII unicode does not raise an encoding error.
# Try several non-ASCII unicode characters # Try several non-ASCII unicode characters.
for code in [129, 500, 2**8 - 1, 2**16 - 1]: for code in [129, 500, 2 ** 8 - 1, 2 ** 16 - 1]:
code_with_unichr = unicode("# ") + unichr(code) code_with_unichr = unicode("# ") + unichr(code)
try: try:
safe_exec(code_with_unichr, {}, cache=DictCache({})) safe_exec(code_with_unichr, {}, cache=DictCache({}))
...@@ -194,7 +194,7 @@ class TestUpdateHash(unittest.TestCase): ...@@ -194,7 +194,7 @@ class TestUpdateHash(unittest.TestCase):
make them different. make them different.
""" """
d1 = {k:1 for k in "abcdefghijklmnopqrstuvwxyz"} d1 = {k: 1 for k in "abcdefghijklmnopqrstuvwxyz"}
d2 = dict(d1) d2 = dict(d1)
for i in xrange(10000): for i in xrange(10000):
d2[i] = 1 d2[i] = 1
...@@ -216,8 +216,8 @@ class TestUpdateHash(unittest.TestCase): ...@@ -216,8 +216,8 @@ class TestUpdateHash(unittest.TestCase):
self.assertNotEqual(h1, hs1) self.assertNotEqual(h1, hs1)
def test_list_ordering(self): def test_list_ordering(self):
h1 = self.hash_obj({'a': [1,2,3]}) h1 = self.hash_obj({'a': [1, 2, 3]})
h2 = self.hash_obj({'a': [3,2,1]}) h2 = self.hash_obj({'a': [3, 2, 1]})
self.assertNotEqual(h1, h2) self.assertNotEqual(h1, h2)
def test_dict_ordering(self): def test_dict_ordering(self):
...@@ -228,8 +228,8 @@ class TestUpdateHash(unittest.TestCase): ...@@ -228,8 +228,8 @@ class TestUpdateHash(unittest.TestCase):
def test_deep_ordering(self): def test_deep_ordering(self):
d1, d2 = self.equal_but_different_dicts() d1, d2 = self.equal_but_different_dicts()
o1 = {'a':[1, 2, [d1], 3, 4]} o1 = {'a': [1, 2, [d1], 3, 4]}
o2 = {'a':[1, 2, [d2], 3, 4]} o2 = {'a': [1, 2, [d2], 3, 4]}
h1 = self.hash_obj(o1) h1 = self.hash_obj(o1)
h2 = self.hash_obj(o2) h2 = self.hash_obj(o2)
self.assertEqual(h1, h2) self.assertEqual(h1, h2)
......
...@@ -134,9 +134,11 @@ class ResponseXMLFactory(object): ...@@ -134,9 +134,11 @@ class ResponseXMLFactory(object):
len(choice_names) == len(choices) len(choice_names) == len(choices)
""" """
# Names of group elements # Names of group elements
group_element_names = {'checkbox': 'checkboxgroup', group_element_names = {
'radio': 'radiogroup', 'checkbox': 'checkboxgroup',
'multiple': 'choicegroup'} 'radio': 'radiogroup',
'multiple': 'choicegroup'
}
# Retrieve **kwargs # Retrieve **kwargs
choices = kwargs.get('choices', [True]) choices = kwargs.get('choices', [True])
...@@ -441,7 +443,6 @@ class FormulaResponseXMLFactory(ResponseXMLFactory): ...@@ -441,7 +443,6 @@ class FormulaResponseXMLFactory(ResponseXMLFactory):
sample_str = self._sample_str(sample_dict, num_samples, tolerance) sample_str = self._sample_str(sample_dict, num_samples, tolerance)
response_element.set("samples", sample_str) response_element.set("samples", sample_str)
# Set the tolerance # Set the tolerance
responseparam_element = etree.SubElement(response_element, "responseparam") responseparam_element = etree.SubElement(response_element, "responseparam")
responseparam_element.set("type", "tolerance") responseparam_element.set("type", "tolerance")
...@@ -487,10 +488,12 @@ class FormulaResponseXMLFactory(ResponseXMLFactory): ...@@ -487,10 +488,12 @@ class FormulaResponseXMLFactory(ResponseXMLFactory):
variables = [str(v) for v in sample_dict.keys()] variables = [str(v) for v in sample_dict.keys()]
low_range_vals = [str(f[0]) for f in sample_dict.values()] low_range_vals = [str(f[0]) for f in sample_dict.values()]
high_range_vals = [str(f[1]) for f in sample_dict.values()] high_range_vals = [str(f[1]) for f in sample_dict.values()]
sample_str = (",".join(sample_dict.keys()) + "@" + sample_str = (
",".join(low_range_vals) + ":" + ",".join(sample_dict.keys()) + "@" +
",".join(high_range_vals) + ",".join(low_range_vals) + ":" +
"#" + str(num_samples)) ",".join(high_range_vals) +
"#" + str(num_samples)
)
return sample_str return sample_str
...@@ -501,7 +504,6 @@ class ImageResponseXMLFactory(ResponseXMLFactory): ...@@ -501,7 +504,6 @@ class ImageResponseXMLFactory(ResponseXMLFactory):
""" Create the <imageresponse> element.""" """ Create the <imageresponse> element."""
return etree.Element("imageresponse") return etree.Element("imageresponse")
def create_input_element(self, **kwargs): def create_input_element(self, **kwargs):
""" Create the <imageinput> element. """ Create the <imageinput> element.
...@@ -578,7 +580,7 @@ class JavascriptResponseXMLFactory(ResponseXMLFactory): ...@@ -578,7 +580,7 @@ class JavascriptResponseXMLFactory(ResponseXMLFactory):
# Both display_src and display_class given, # Both display_src and display_class given,
# or neither given # or neither given
assert((display_src and display_class) or assert((display_src and display_class) or
(not display_src and not display_class)) (not display_src and not display_class))
# Create the <javascriptresponse> element # Create the <javascriptresponse> element
response_element = etree.Element("javascriptresponse") response_element = etree.Element("javascriptresponse")
...@@ -764,7 +766,7 @@ class AnnotationResponseXMLFactory(ResponseXMLFactory): ...@@ -764,7 +766,7 @@ class AnnotationResponseXMLFactory(ResponseXMLFactory):
text_children = [ text_children = [
{'tag': 'title', 'text': kwargs.get('title', 'super cool annotation')}, {'tag': 'title', 'text': kwargs.get('title', 'super cool annotation')},
{'tag': 'text', 'text': kwargs.get('text', 'texty text')}, {'tag': 'text', 'text': kwargs.get('text', 'texty text')},
{'tag': 'comment', 'text':kwargs.get('comment', 'blah blah erudite comment blah blah')}, {'tag': 'comment', 'text': kwargs.get('comment', 'blah blah erudite comment blah blah')},
{'tag': 'comment_prompt', 'text': kwargs.get('comment_prompt', 'type a commentary below')}, {'tag': 'comment_prompt', 'text': kwargs.get('comment_prompt', 'type a commentary below')},
{'tag': 'tag_prompt', 'text': kwargs.get('tag_prompt', 'select one tag')} {'tag': 'tag_prompt', 'text': kwargs.get('tag_prompt', 'select one tag')}
] ]
...@@ -772,7 +774,7 @@ class AnnotationResponseXMLFactory(ResponseXMLFactory): ...@@ -772,7 +774,7 @@ class AnnotationResponseXMLFactory(ResponseXMLFactory):
for child in text_children: for child in text_children:
etree.SubElement(input_element, child['tag']).text = child['text'] etree.SubElement(input_element, child['tag']).text = child['text']
default_options = [('green', 'correct'),('eggs', 'incorrect'), ('ham', 'partially-correct')] default_options = [('green', 'correct'), ('eggs', 'incorrect'), ('ham', 'partially-correct')]
options = kwargs.get('options', default_options) options = kwargs.get('options', default_options)
options_element = etree.SubElement(input_element, 'options') options_element = etree.SubElement(input_element, 'options')
......
...@@ -96,9 +96,11 @@ def sub_miller(segments): ...@@ -96,9 +96,11 @@ def sub_miller(segments):
''' '''
fracts = [segment_to_fraction(segment) for segment in segments] fracts = [segment_to_fraction(segment) for segment in segments]
common_denominator = reduce(lcm, [fract.denominator for fract in fracts]) common_denominator = reduce(lcm, [fract.denominator for fract in fracts])
miller = ([fract.numerator * math.fabs(common_denominator) / miller_indices = ([
fract.denominator for fract in fracts]) fract.numerator * math.fabs(common_denominator) / fract.denominator
return'(' + ','.join(map(str, map(decimal.Decimal, miller))) + ')' for fract in fracts
])
return'(' + ','.join(map(str, map(decimal.Decimal, miller_indices))) + ')'
def miller(points): def miller(points):
...@@ -145,19 +147,22 @@ def miller(points): ...@@ -145,19 +147,22 @@ def miller(points):
O = np.array([0, 0, 0]) O = np.array([0, 0, 0])
P = points[0] # point of plane P = points[0] # point of plane
Ccs = map(np.array, [[1.0, 0, 0], [0, 1.0, 0], [0, 0, 1.0]]) Ccs = map(np.array, [[1.0, 0, 0], [0, 1.0, 0], [0, 0, 1.0]])
segments = ([np.dot(P - O, N) / np.dot(ort, N) if np.dot(ort, N) != 0 else segments = ([
np.nan for ort in Ccs]) np.dot(P - O, N) / np.dot(ort, N) if np.dot(ort, N) != 0
else np.nan for ort in Ccs
])
if any(x == 0 for x in segments): # Plane goes through origin. if any(x == 0 for x in segments): # Plane goes through origin.
vertices = [ # top: vertices = [
np.array([1.0, 1.0, 1.0]), # top:
np.array([0.0, 0.0, 1.0]), np.array([1.0, 1.0, 1.0]),
np.array([1.0, 0.0, 1.0]), np.array([0.0, 0.0, 1.0]),
np.array([0.0, 1.0, 1.0]), np.array([1.0, 0.0, 1.0]),
# bottom, except 0,0,0: np.array([0.0, 1.0, 1.0]),
np.array([1.0, 0.0, 0.0]), # bottom, except 0,0,0:
np.array([0.0, 1.0, 0.0]), np.array([1.0, 0.0, 0.0]),
np.array([1.0, 1.0, 1.0]), np.array([0.0, 1.0, 0.0]),
] np.array([1.0, 1.0, 1.0]),
]
for vertex in vertices: for vertex in vertices:
if np.dot(vertex - O, N) != 0: # vertex not in plane if np.dot(vertex - O, N) != 0: # vertex not in plane
new_origin = vertex new_origin = vertex
......
...@@ -9,7 +9,8 @@ from os.path import abspath, realpath, dirname, join as joinpath ...@@ -9,7 +9,8 @@ from os.path import abspath, realpath, dirname, join as joinpath
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
import logging import logging
log = logging.getLogger(__name__) # pylint: disable=C0103 log = logging.getLogger(__name__)
def resolved(rpath): def resolved(rpath):
""" """
...@@ -17,12 +18,14 @@ def resolved(rpath): ...@@ -17,12 +18,14 @@ def resolved(rpath):
""" """
return realpath(abspath(rpath)) return realpath(abspath(rpath))
def _is_bad_path(path, base): def _is_bad_path(path, base):
""" """
Is (the canonical absolute path of) `path` outside `base`? Is (the canonical absolute path of) `path` outside `base`?
""" """
return not resolved(joinpath(base, path)).startswith(base) return not resolved(joinpath(base, path)).startswith(base)
def _is_bad_link(info, base): def _is_bad_link(info, base):
""" """
Does the file sym- ord hard-link to files outside `base`? Does the file sym- ord hard-link to files outside `base`?
...@@ -31,6 +34,7 @@ def _is_bad_link(info, base): ...@@ -31,6 +34,7 @@ def _is_bad_link(info, base):
tip = resolved(joinpath(base, dirname(info.name))) tip = resolved(joinpath(base, dirname(info.name)))
return _is_bad_path(info.linkname, base=tip) return _is_bad_path(info.linkname, base=tip)
def safemembers(members): def safemembers(members):
""" """
Check that all elements of a tar file are safe. Check that all elements of a tar file are safe.
...@@ -43,19 +47,20 @@ def safemembers(members): ...@@ -43,19 +47,20 @@ def safemembers(members):
log.debug("File %r is blocked (illegal path)", finfo.name) log.debug("File %r is blocked (illegal path)", finfo.name)
raise SuspiciousOperation("Illegal path") raise SuspiciousOperation("Illegal path")
elif finfo.issym() and _is_bad_link(finfo, base): elif finfo.issym() and _is_bad_link(finfo, base):
log.debug( "File %r is blocked: Hard link to %r", finfo.name, finfo.linkname) log.debug("File %r is blocked: Hard link to %r", finfo.name, finfo.linkname)
raise SuspiciousOperation("Hard link") raise SuspiciousOperation("Hard link")
elif finfo.islnk() and _is_bad_link(finfo, base): elif finfo.islnk() and _is_bad_link(finfo, base):
log.debug("File %r is blocked: Symlink to %r", finfo.name, log.debug("File %r is blocked: Symlink to %r", finfo.name,
finfo.linkname) finfo.linkname)
raise SuspiciousOperation("Symlink") raise SuspiciousOperation("Symlink")
elif finfo.isdev(): elif finfo.isdev():
log.debug("File %r is blocked: FIFO, device or character file", log.debug("File %r is blocked: FIFO, device or character file",
finfo.name) finfo.name)
raise SuspiciousOperation("Dev file") raise SuspiciousOperation("Dev file")
return members return members
def safetar_extractall(tarf, *args, **kwargs): def safetar_extractall(tarf, *args, **kwargs):
""" """
Safe version of `tarf.extractall()`. Safe version of `tarf.extractall()`.
......
...@@ -21,13 +21,14 @@ log = logging.getLogger(__name__) ...@@ -21,13 +21,14 @@ log = logging.getLogger(__name__)
def symmath_check_simple(expect, ans, adict={}, symtab=None, extra_options=None): def symmath_check_simple(expect, ans, adict={}, symtab=None, extra_options=None):
''' """
Check a symbolic mathematical expression using sympy. Check a symbolic mathematical expression using sympy.
The input is an ascii string (not MathML) converted to math using sympy.sympify. The input is an ascii string (not MathML) converted to math using sympy.sympify.
''' """
options = {'__MATRIX__': False, '__ABC__': False, '__LOWER__': False} options = {'__MATRIX__': False, '__ABC__': False, '__LOWER__': False}
if extra_options: options.update(extra_options) if extra_options:
options.update(extra_options)
for op in options: # find options in expect string for op in options: # find options in expect string
if op in expect: if op in expect:
expect = expect.replace(op, '') expect = expect.replace(op, '')
...@@ -145,6 +146,7 @@ def make_error_message(msg): ...@@ -145,6 +146,7 @@ def make_error_message(msg):
msg = '<div class="capa_alert">%s</div>' % msg msg = '<div class="capa_alert">%s</div>' % msg
return msg return msg
def is_within_tolerance(expected, actual, tolerance): def is_within_tolerance(expected, actual, tolerance):
if expected == 0: if expected == 0:
return (abs(actual) < tolerance) return (abs(actual) < tolerance)
...@@ -158,7 +160,7 @@ def is_within_tolerance(expected, actual, tolerance): ...@@ -158,7 +160,7 @@ def is_within_tolerance(expected, actual, tolerance):
def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None): def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None):
''' """
Check a symbolic mathematical expression using sympy. Check a symbolic mathematical expression using sympy.
The input may be presentation MathML. Uses formula. The input may be presentation MathML. Uses formula.
...@@ -181,7 +183,7 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None ...@@ -181,7 +183,7 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None
-qubit - passed to my_sympify -qubit - passed to my_sympify
-imaginary - used in formla, presumably to signal to use i as sqrt(-1)? -imaginary - used in formla, presumably to signal to use i as sqrt(-1)?
-numerical - force numerical comparison. -numerical - force numerical comparison.
''' """
msg = '' msg = ''
# msg += '<p/>abname=%s' % abname # msg += '<p/>abname=%s' % abname
...@@ -208,7 +210,6 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None ...@@ -208,7 +210,6 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None
msg += '<p>Error %s in parsing OUR expected answer "%s"</p>' % (err, expect) msg += '<p>Error %s in parsing OUR expected answer "%s"</p>' % (err, expect)
return {'ok': False, 'msg': make_error_message(msg)} return {'ok': False, 'msg': make_error_message(msg)}
###### Sympy input ####### ###### Sympy input #######
# if expected answer is a number, try parsing provided answer as a number also # if expected answer is a number, try parsing provided answer as a number also
try: try:
...@@ -217,8 +218,8 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None ...@@ -217,8 +218,8 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None
fans = None fans = None
# do a numerical comparison if both expected and answer are numbers # do a numerical comparison if both expected and answer are numbers
if (hasattr(fexpect, 'is_number') and fexpect.is_number if hasattr(fexpect, 'is_number') and fexpect.is_number \
and hasattr(fans, 'is_number') and fans.is_number): and hasattr(fans, 'is_number') and fans.is_number:
if is_within_tolerance(fexpect, fans, threshold): if is_within_tolerance(fexpect, fans, threshold):
return {'ok': True, 'msg': msg} return {'ok': True, 'msg': msg}
else: else:
...@@ -236,7 +237,6 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None ...@@ -236,7 +237,6 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None
msg += '<p>You entered: %s</p>' % to_latex(fans) msg += '<p>You entered: %s</p>' % to_latex(fans)
return {'ok': True, 'msg': msg} return {'ok': True, 'msg': msg}
###### PMathML input ###### ###### PMathML input ######
# convert mathml answer to formula # convert mathml answer to formula
try: try:
...@@ -298,7 +298,8 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None ...@@ -298,7 +298,8 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None
return {'ok': False, 'msg': make_error_message(msg)} return {'ok': False, 'msg': make_error_message(msg)}
except Exception, err: except Exception, err:
msg += "<p>Error %s in comparing expected (a list) and your answer</p>" % str(err).replace('<', '&lt;') msg += "<p>Error %s in comparing expected (a list) and your answer</p>" % str(err).replace('<', '&lt;')
if DEBUG: msg += "<p/><pre>%s</pre>" % traceback.format_exc() if DEBUG:
msg += "<p/><pre>%s</pre>" % traceback.format_exc()
return {'ok': False, 'msg': make_error_message(msg)} return {'ok': False, 'msg': make_error_message(msg)}
#diff = (fexpect-fsym).simplify() #diff = (fexpect-fsym).simplify()
......
""" """
Tests of symbolic math Tests of symbolic math
""" """
import unittest import unittest
import formula import formula
import re import re
from lxml import etree from lxml import etree
def stripXML(xml): def stripXML(xml):
xml = xml.replace('\n', '') xml = xml.replace('\n', '')
xml = re.sub(r'\> +\<', '><', xml) xml = re.sub(r'\> +\<', '><', xml)
return xml return xml
class FormulaTest(unittest.TestCase): class FormulaTest(unittest.TestCase):
# for readability later # for readability later
mathml_start = '<math xmlns="http://www.w3.org/1998/Math/MathML"><mstyle displaystyle="true">' mathml_start = '<math xmlns="http://www.w3.org/1998/Math/MathML"><mstyle displaystyle="true">'
...@@ -41,7 +41,6 @@ class FormulaTest(unittest.TestCase): ...@@ -41,7 +41,6 @@ class FormulaTest(unittest.TestCase):
# success? # success?
self.assertEqual(test, expected) self.assertEqual(test, expected)
def test_fix_simple_superscripts(self): def test_fix_simple_superscripts(self):
expr = ''' expr = '''
<msup> <msup>
...@@ -91,7 +90,6 @@ class FormulaTest(unittest.TestCase): ...@@ -91,7 +90,6 @@ class FormulaTest(unittest.TestCase):
# success? # success?
self.assertEqual(test, expected) self.assertEqual(test, expected)
def test_fix_msubsup(self): def test_fix_msubsup(self):
expr = ''' expr = '''
<msubsup> <msubsup>
...@@ -100,7 +98,7 @@ class FormulaTest(unittest.TestCase): ...@@ -100,7 +98,7 @@ class FormulaTest(unittest.TestCase):
<mi>c</mi> <mi>c</mi>
</msubsup>''' </msubsup>'''
expected = '<msup><mi>a_b</mi><mi>c</mi></msup>' # which is (a_b)^c expected = '<msup><mi>a_b</mi><mi>c</mi></msup>' # which is (a_b)^c
# wrap # wrap
expr = stripXML(self.mathml_start + expr + self.mathml_end) expr = stripXML(self.mathml_start + expr + self.mathml_end)
......
...@@ -257,7 +257,6 @@ class CombinedOpenEndedV1Module(): ...@@ -257,7 +257,6 @@ class CombinedOpenEndedV1Module():
""" """
return all(self.is_initial_child_state(child) for child in task_state) return all(self.is_initial_child_state(child) for child in task_state)
def states_sort_key(self, idx_task_states): def states_sort_key(self, idx_task_states):
""" """
Return a key for sorting a list of indexed task_states, by how far the student got Return a key for sorting a list of indexed task_states, by how far the student got
...@@ -544,8 +543,8 @@ class CombinedOpenEndedV1Module(): ...@@ -544,8 +543,8 @@ class CombinedOpenEndedV1Module():
last_response_data = self.get_last_response(self.current_task_number - 1) last_response_data = self.get_last_response(self.current_task_number - 1)
current_response_data = self.get_current_attributes(self.current_task_number) current_response_data = self.get_current_attributes(self.current_task_number)
if (current_response_data['min_score_to_attempt'] > last_response_data['score'] if current_response_data['min_score_to_attempt'] > last_response_data['score'] or\
or current_response_data['max_score_to_attempt'] < last_response_data['score']): current_response_data['max_score_to_attempt'] < last_response_data['score']:
self.state = self.DONE self.state = self.DONE
self.ready_to_reset = True self.ready_to_reset = True
...@@ -818,7 +817,7 @@ class CombinedOpenEndedV1Module(): ...@@ -818,7 +817,7 @@ class CombinedOpenEndedV1Module():
log.error("Invalid response from grading server for location {0} and student {1}".format(self.location, student_id)) log.error("Invalid response from grading server for location {0} and student {1}".format(self.location, student_id))
error_message = "Received invalid response from the graders. Please notify course staff." error_message = "Received invalid response from the graders. Please notify course staff."
return success, allowed_to_submit, error_message return success, allowed_to_submit, error_message
if count_graded >= count_required or count_available==0: if count_graded >= count_required or count_available == 0:
error_message = "" error_message = ""
return success, allowed_to_submit, error_message return success, allowed_to_submit, error_message
else: else:
...@@ -853,7 +852,7 @@ class CombinedOpenEndedV1Module(): ...@@ -853,7 +852,7 @@ class CombinedOpenEndedV1Module():
contexts = [] contexts = []
rubric_number = self.current_task_number rubric_number = self.current_task_number
if self.ready_to_reset: if self.ready_to_reset:
rubric_number+=1 rubric_number += 1
response = self.get_last_response(rubric_number) response = self.get_last_response(rubric_number)
score_length = len(response['grader_types']) score_length = len(response['grader_types'])
for z in xrange(score_length): for z in xrange(score_length):
...@@ -861,7 +860,7 @@ class CombinedOpenEndedV1Module(): ...@@ -861,7 +860,7 @@ class CombinedOpenEndedV1Module():
try: try:
feedback = response['feedback_dicts'][z].get('feedback', '') feedback = response['feedback_dicts'][z].get('feedback', '')
except TypeError: except TypeError:
return {'success' : False} return {'success': False}
rubric_scores = [[response['rubric_scores'][z]]] rubric_scores = [[response['rubric_scores'][z]]]
grader_types = [[response['grader_types'][z]]] grader_types = [[response['grader_types'][z]]]
feedback_items = [[response['feedback_items'][z]]] feedback_items = [[response['feedback_items'][z]]]
...@@ -879,14 +878,14 @@ class CombinedOpenEndedV1Module(): ...@@ -879,14 +878,14 @@ class CombinedOpenEndedV1Module():
# That longer string appears when a user is viewing a graded rubric # That longer string appears when a user is viewing a graded rubric
# returned from one of the graders of their openended response problem. # returned from one of the graders of their openended response problem.
'task_name': ugettext('Scored rubric'), 'task_name': ugettext('Scored rubric'),
'feedback' : feedback 'feedback': feedback
}) })
context = { context = {
'results': contexts, 'results': contexts,
} }
html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context) html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True, 'hide_reset' : False} return {'html': html, 'success': True, 'hide_reset': False}
def get_legend(self, _data): def get_legend(self, _data):
""" """
...@@ -978,7 +977,7 @@ class CombinedOpenEndedV1Module(): ...@@ -978,7 +977,7 @@ class CombinedOpenEndedV1Module():
max_number_of_attempts=self.max_attempts max_number_of_attempts=self.max_attempts
) )
} }
self.student_attempts +=1 self.student_attempts += 1
self.state = self.INITIAL self.state = self.INITIAL
self.ready_to_reset = False self.ready_to_reset = False
for i in xrange(len(self.task_xml)): for i in xrange(len(self.task_xml)):
......
...@@ -245,7 +245,7 @@ class OpenEndedChild(object): ...@@ -245,7 +245,7 @@ class OpenEndedChild(object):
Replaces "\n" newlines with <br/> Replaces "\n" newlines with <br/>
""" """
retv = re.sub(r'</p>$', '', re.sub(r'^<p>', '', html)) retv = re.sub(r'</p>$', '', re.sub(r'^<p>', '', html))
return re.sub("\n","<br/>", retv) return re.sub("\n", "<br/>", retv)
def new_history_entry(self, answer): def new_history_entry(self, answer):
""" """
...@@ -293,7 +293,7 @@ class OpenEndedChild(object): ...@@ -293,7 +293,7 @@ class OpenEndedChild(object):
'child_attempts': self.child_attempts, 'child_attempts': self.child_attempts,
'child_created': self.child_created, 'child_created': self.child_created,
'stored_answer': self.stored_answer, 'stored_answer': self.stored_answer,
} }
return json.dumps(state) return json.dumps(state)
def _allow_reset(self): def _allow_reset(self):
...@@ -333,13 +333,13 @@ class OpenEndedChild(object): ...@@ -333,13 +333,13 @@ class OpenEndedChild(object):
previous_answer = latest previous_answer = latest
else: else:
previous_answer = "" previous_answer = ""
previous_answer = previous_answer.replace("<br/>","\n").replace("<br>", "\n") previous_answer = previous_answer.replace("<br/>", "\n").replace("<br>", "\n")
else: else:
if latest is not None and len(latest) > 0: if latest is not None and len(latest) > 0:
previous_answer = latest previous_answer = latest
else: else:
previous_answer = "" previous_answer = ""
previous_answer = previous_answer.replace("\n","<br/>") previous_answer = previous_answer.replace("\n", "<br/>")
return previous_answer return previous_answer
...@@ -439,16 +439,16 @@ class OpenEndedChild(object): ...@@ -439,16 +439,16 @@ class OpenEndedChild(object):
image_tag = "" image_tag = ""
# Ensure that a valid file was uploaded. # Ensure that a valid file was uploaded.
if ('valid_files_attached' in data if 'valid_files_attached' in data and \
and data['valid_files_attached'] in ['true', '1', True] data['valid_files_attached'] in ['true', '1', True] and \
and data['student_file'] is not None data['student_file'] is not None and \
and len(data['student_file']) > 0): len(data['student_file']) > 0:
has_file_to_upload = True has_file_to_upload = True
student_file = data['student_file'][0] student_file = data['student_file'][0]
# Upload the file to S3 and generate html to embed a link. # Upload the file to S3 and generate html to embed a link.
s3_public_url = self.upload_file_to_s3(student_file) s3_public_url = self.upload_file_to_s3(student_file)
image_tag = self.generate_file_link_html_from_url(s3_public_url, student_file.name) image_tag = self.generate_file_link_html_from_url(s3_public_url, student_file.name)
return has_file_to_upload, image_tag return has_file_to_upload, image_tag
...@@ -521,7 +521,7 @@ class OpenEndedChild(object): ...@@ -521,7 +521,7 @@ class OpenEndedChild(object):
# Find all links in the string. # Find all links in the string.
links = re.findall(r'(https?://\S+)', string) links = re.findall(r'(https?://\S+)', string)
if len(links)>0: if len(links) > 0:
has_link = True has_link = True
# Autolink by wrapping links in anchor tags. # Autolink by wrapping links in anchor tags.
......
...@@ -41,10 +41,12 @@ class PollFields(object): ...@@ -41,10 +41,12 @@ class PollFields(object):
class PollModule(PollFields, XModule): class PollModule(PollFields, XModule):
"""Poll Module""" """Poll Module"""
js = { js = {
'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee')], 'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee')],
'js': [resource_string(__name__, 'js/src/poll/poll.js'), 'js': [
resource_string(__name__, 'js/src/poll/poll_main.js')] resource_string(__name__, 'js/src/poll/poll.js'),
} resource_string(__name__, 'js/src/poll/poll_main.js')
]
}
css = {'scss': [resource_string(__name__, 'css/poll/display.scss')]} css = {'scss': [resource_string(__name__, 'css/poll/display.scss')]}
js_module_name = "Poll" js_module_name = "Poll"
...@@ -94,11 +96,11 @@ class PollModule(PollFields, XModule): ...@@ -94,11 +96,11 @@ class PollModule(PollFields, XModule):
def get_html(self): def get_html(self):
"""Renders parameters to template.""" """Renders parameters to template."""
params = { params = {
'element_id': self.location.html_id(), 'element_id': self.location.html_id(),
'element_class': self.location.category, 'element_class': self.location.category,
'ajax_url': self.system.ajax_url, 'ajax_url': self.system.ajax_url,
'configuration_json': self.dump_poll(), 'configuration_json': self.dump_poll(),
} }
self.content = self.system.render_template('poll.html', params) self.content = self.system.render_template('poll.html', params)
return self.content return self.content
...@@ -127,13 +129,15 @@ class PollModule(PollFields, XModule): ...@@ -127,13 +129,15 @@ class PollModule(PollFields, XModule):
answers_to_json[answer['id']] = cgi.escape(answer['text']) answers_to_json[answer['id']] = cgi.escape(answer['text'])
self.poll_answers = temp_poll_answers self.poll_answers = temp_poll_answers
return json.dumps({'answers': answers_to_json, return json.dumps({
'answers': answers_to_json,
'question': cgi.escape(self.question), 'question': cgi.escape(self.question),
# to show answered poll after reload: # to show answered poll after reload:
'poll_answer': self.poll_answer, 'poll_answer': self.poll_answer,
'poll_answers': self.poll_answers if self.voted else {}, 'poll_answers': self.poll_answers if self.voted else {},
'total': sum(self.poll_answers.values()) if self.voted else 0, 'total': sum(self.poll_answers.values()) if self.voted else 0,
'reset': str(self.descriptor.xml_attributes.get('reset', 'true')).lower()}) 'reset': str(self.descriptor.xml_attributes.get('reset', 'true')).lower()
})
class PollDescriptor(PollFields, MakoModuleDescriptor, XmlDescriptor): class PollDescriptor(PollFields, MakoModuleDescriptor, XmlDescriptor):
......
...@@ -12,6 +12,7 @@ from opaque_keys.edx.locations import Location ...@@ -12,6 +12,7 @@ from opaque_keys.edx.locations import Location
from . import get_test_system from . import get_test_system
class AnnotatableModuleTestCase(unittest.TestCase): class AnnotatableModuleTestCase(unittest.TestCase):
sample_xml = ''' sample_xml = '''
<annotatable display_name="Iliad"> <annotatable display_name="Iliad">
...@@ -42,7 +43,7 @@ class AnnotatableModuleTestCase(unittest.TestCase): ...@@ -42,7 +43,7 @@ class AnnotatableModuleTestCase(unittest.TestCase):
el = etree.fromstring('<annotation title="bar" body="foo" problem="0">test</annotation>') el = etree.fromstring('<annotation title="bar" body="foo" problem="0">test</annotation>')
expected_attr = { expected_attr = {
'data-comment-body': {'value': 'foo', '_delete': 'body' }, 'data-comment-body': {'value': 'foo', '_delete': 'body'},
'data-comment-title': {'value': 'bar', '_delete': 'title'}, 'data-comment-title': {'value': 'bar', '_delete': 'title'},
'data-problem-id': {'value': '0', '_delete': 'problem'} 'data-problem-id': {'value': '0', '_delete': 'problem'}
} }
...@@ -56,7 +57,7 @@ class AnnotatableModuleTestCase(unittest.TestCase): ...@@ -56,7 +57,7 @@ class AnnotatableModuleTestCase(unittest.TestCase):
xml = '<annotation title="x" body="y" problem="0">test</annotation>' xml = '<annotation title="x" body="y" problem="0">test</annotation>'
el = etree.fromstring(xml) el = etree.fromstring(xml)
expected_attr = { 'class': { 'value': 'annotatable-span highlight' } } expected_attr = {'class': {'value': 'annotatable-span highlight'}}
actual_attr = self.annotatable._get_annotation_class_attr(0, el) actual_attr = self.annotatable._get_annotation_class_attr(0, el)
self.assertIsInstance(actual_attr, dict) self.assertIsInstance(actual_attr, dict)
...@@ -69,9 +70,11 @@ class AnnotatableModuleTestCase(unittest.TestCase): ...@@ -69,9 +70,11 @@ class AnnotatableModuleTestCase(unittest.TestCase):
el = etree.fromstring(xml.format(highlight=color)) el = etree.fromstring(xml.format(highlight=color))
value = 'annotatable-span highlight highlight-{highlight}'.format(highlight=color) value = 'annotatable-span highlight highlight-{highlight}'.format(highlight=color)
expected_attr = { 'class': { expected_attr = {
'value': value, 'class': {
'_delete': 'highlight' } 'value': value,
'_delete': 'highlight'
}
} }
actual_attr = self.annotatable._get_annotation_class_attr(0, el) actual_attr = self.annotatable._get_annotation_class_attr(0, el)
...@@ -83,9 +86,11 @@ class AnnotatableModuleTestCase(unittest.TestCase): ...@@ -83,9 +86,11 @@ class AnnotatableModuleTestCase(unittest.TestCase):
for invalid_color in ['rainbow', 'blink', 'invisible', '', None]: for invalid_color in ['rainbow', 'blink', 'invisible', '', None]:
el = etree.fromstring(xml.format(highlight=invalid_color)) el = etree.fromstring(xml.format(highlight=invalid_color))
expected_attr = { 'class': { expected_attr = {
'value': 'annotatable-span highlight', 'class': {
'_delete': 'highlight' } 'value': 'annotatable-span highlight',
'_delete': 'highlight'
}
} }
actual_attr = self.annotatable._get_annotation_class_attr(0, el) actual_attr = self.annotatable._get_annotation_class_attr(0, el)
......
...@@ -11,46 +11,60 @@ class DateTest(unittest.TestCase): ...@@ -11,46 +11,60 @@ class DateTest(unittest.TestCase):
date = Date() date = Date()
def compare_dates(self, dt1, dt2, expected_delta): def compare_dates(self, dt1, dt2, expected_delta):
self.assertEqual(dt1 - dt2, expected_delta, str(dt1) + "-" self.assertEqual(
+ str(dt2) + "!=" + str(expected_delta)) dt1 - dt2,
expected_delta,
str(dt1) + "-" + str(dt2) + "!=" + str(expected_delta)
)
def test_from_json(self): def test_from_json(self):
'''Test conversion from iso compatible date strings to struct_time''' """Test conversion from iso compatible date strings to struct_time"""
self.compare_dates( self.compare_dates(
DateTest.date.from_json("2013-01-01"), DateTest.date.from_json("2013-01-01"),
DateTest.date.from_json("2012-12-31"), DateTest.date.from_json("2012-12-31"),
datetime.timedelta(days=1)) datetime.timedelta(days=1)
)
self.compare_dates( self.compare_dates(
DateTest.date.from_json("2013-01-01T00"), DateTest.date.from_json("2013-01-01T00"),
DateTest.date.from_json("2012-12-31T23"), DateTest.date.from_json("2012-12-31T23"),
datetime.timedelta(hours=1)) datetime.timedelta(hours=1)
)
self.compare_dates( self.compare_dates(
DateTest.date.from_json("2013-01-01T00:00"), DateTest.date.from_json("2013-01-01T00:00"),
DateTest.date.from_json("2012-12-31T23:59"), DateTest.date.from_json("2012-12-31T23:59"),
datetime.timedelta(minutes=1)) datetime.timedelta(minutes=1)
)
self.compare_dates( self.compare_dates(
DateTest.date.from_json("2013-01-01T00:00:00"), DateTest.date.from_json("2013-01-01T00:00:00"),
DateTest.date.from_json("2012-12-31T23:59:59"), DateTest.date.from_json("2012-12-31T23:59:59"),
datetime.timedelta(seconds=1)) datetime.timedelta(seconds=1)
)
self.compare_dates( self.compare_dates(
DateTest.date.from_json("2013-01-01T00:00:00Z"), DateTest.date.from_json("2013-01-01T00:00:00Z"),
DateTest.date.from_json("2012-12-31T23:59:59Z"), DateTest.date.from_json("2012-12-31T23:59:59Z"),
datetime.timedelta(seconds=1)) datetime.timedelta(seconds=1)
)
self.compare_dates( self.compare_dates(
DateTest.date.from_json("2012-12-31T23:00:01-01:00"), DateTest.date.from_json("2012-12-31T23:00:01-01:00"),
DateTest.date.from_json("2013-01-01T00:00:00+01:00"), DateTest.date.from_json("2013-01-01T00:00:00+01:00"),
datetime.timedelta(hours=1, seconds=1)) datetime.timedelta(hours=1, seconds=1)
)
def test_enforce_type(self): def test_enforce_type(self):
self.assertEqual(DateTest.date.enforce_type(None), None) self.assertEqual(DateTest.date.enforce_type(None), None)
self.assertEqual(DateTest.date.enforce_type(""), None) self.assertEqual(DateTest.date.enforce_type(""), None)
self.assertEqual(DateTest.date.enforce_type("2012-12-31T23:00:01"), self.assertEqual(
datetime.datetime(2012, 12, 31, 23, 0, 1, tzinfo=UTC())) DateTest.date.enforce_type("2012-12-31T23:00:01"),
self.assertEqual(DateTest.date.enforce_type(1234567890000), datetime.datetime(2012, 12, 31, 23, 0, 1, tzinfo=UTC())
datetime.datetime(2009, 2, 13, 23, 31, 30, tzinfo=UTC())) )
self.assertEqual(DateTest.date.enforce_type( self.assertEqual(
datetime.datetime(2014, 5, 9, 21, 1, 27, tzinfo=UTC())), DateTest.date.enforce_type(1234567890000),
datetime.datetime(2014, 5, 9, 21, 1, 27, tzinfo=UTC())) datetime.datetime(2009, 2, 13, 23, 31, 30, tzinfo=UTC())
)
self.assertEqual(
DateTest.date.enforce_type(datetime.datetime(2014, 5, 9, 21, 1, 27, tzinfo=UTC())),
datetime.datetime(2014, 5, 9, 21, 1, 27, tzinfo=UTC())
)
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
DateTest.date.enforce_type([1]) DateTest.date.enforce_type([1])
...@@ -64,10 +78,12 @@ class DateTest(unittest.TestCase): ...@@ -64,10 +78,12 @@ class DateTest(unittest.TestCase):
current = datetime.datetime.today() current = datetime.datetime.today()
self.assertEqual( self.assertEqual(
datetime.datetime(current.year, 3, 12, 12, tzinfo=UTC()), datetime.datetime(current.year, 3, 12, 12, tzinfo=UTC()),
DateTest.date.from_json("March 12 12:00")) DateTest.date.from_json("March 12 12:00")
)
self.assertEqual( self.assertEqual(
datetime.datetime(current.year, 12, 4, 16, 30, tzinfo=UTC()), datetime.datetime(current.year, 12, 4, 16, 30, tzinfo=UTC()),
DateTest.date.from_json("December 4 16:30")) DateTest.date.from_json("December 4 16:30")
)
self.assertIsNone(DateTest.date.from_json("12 12:00")) self.assertIsNone(DateTest.date.from_json("12 12:00"))
def test_non_std_from_json(self): def test_non_std_from_json(self):
...@@ -76,27 +92,29 @@ class DateTest(unittest.TestCase): ...@@ -76,27 +92,29 @@ class DateTest(unittest.TestCase):
""" """
now = datetime.datetime.now(UTC()) now = datetime.datetime.now(UTC())
delta = now - datetime.datetime.fromtimestamp(0, UTC()) delta = now - datetime.datetime.fromtimestamp(0, UTC())
self.assertEqual(DateTest.date.from_json(delta.total_seconds() * 1000), self.assertEqual(
now) DateTest.date.from_json(delta.total_seconds() * 1000), # pylint: disable=maybe-no-member
now
)
yesterday = datetime.datetime.now(UTC()) - datetime.timedelta(days=-1) yesterday = datetime.datetime.now(UTC()) - datetime.timedelta(days=-1)
self.assertEqual(DateTest.date.from_json(yesterday), yesterday) self.assertEqual(DateTest.date.from_json(yesterday), yesterday)
def test_to_json(self): def test_to_json(self):
''' """
Test converting time reprs to iso dates Test converting time reprs to iso dates
''' """
self.assertEqual( self.assertEqual(
DateTest.date.to_json( DateTest.date.to_json(datetime.datetime.strptime("2012-12-31T23:59:59Z", "%Y-%m-%dT%H:%M:%SZ")),
datetime.datetime.strptime("2012-12-31T23:59:59Z", "%Y-%m-%dT%H:%M:%SZ")), "2012-12-31T23:59:59Z"
"2012-12-31T23:59:59Z") )
self.assertEqual( self.assertEqual(
DateTest.date.to_json( DateTest.date.to_json(DateTest.date.from_json("2012-12-31T23:59:59Z")),
DateTest.date.from_json("2012-12-31T23:59:59Z")), "2012-12-31T23:59:59Z"
"2012-12-31T23:59:59Z") )
self.assertEqual( self.assertEqual(
DateTest.date.to_json( DateTest.date.to_json(DateTest.date.from_json("2012-12-31T23:00:01-01:00")),
DateTest.date.from_json("2012-12-31T23:00:01-01:00")), "2012-12-31T23:00:01-01:00"
"2012-12-31T23:00:01-01:00") )
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
DateTest.date.to_json('2012-12-31T23:00:01-01:00') DateTest.date.to_json('2012-12-31T23:00:01-01:00')
...@@ -117,11 +135,14 @@ class TimedeltaTest(unittest.TestCase): ...@@ -117,11 +135,14 @@ class TimedeltaTest(unittest.TestCase):
def test_enforce_type(self): def test_enforce_type(self):
self.assertEqual(TimedeltaTest.delta.enforce_type(None), None) self.assertEqual(TimedeltaTest.delta.enforce_type(None), None)
self.assertEqual(TimedeltaTest.delta.enforce_type( self.assertEqual(
datetime.timedelta(days=1, seconds=46799)), TimedeltaTest.delta.enforce_type(datetime.timedelta(days=1, seconds=46799)),
datetime.timedelta(days=1, seconds=46799)) datetime.timedelta(days=1, seconds=46799)
self.assertEqual(TimedeltaTest.delta.enforce_type('1 day 46799 seconds'), )
datetime.timedelta(days=1, seconds=46799)) self.assertEqual(
TimedeltaTest.delta.enforce_type('1 day 46799 seconds'),
datetime.timedelta(days=1, seconds=46799)
)
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
TimedeltaTest.delta.enforce_type([1]) TimedeltaTest.delta.enforce_type([1])
...@@ -137,8 +158,10 @@ class TimeInfoTest(unittest.TestCase): ...@@ -137,8 +158,10 @@ class TimeInfoTest(unittest.TestCase):
due_date = datetime.datetime(2000, 4, 14, 10, tzinfo=UTC()) due_date = datetime.datetime(2000, 4, 14, 10, tzinfo=UTC())
grace_pd_string = '1 day 12 hours 59 minutes 59 seconds' grace_pd_string = '1 day 12 hours 59 minutes 59 seconds'
timeinfo = TimeInfo(due_date, grace_pd_string) timeinfo = TimeInfo(due_date, grace_pd_string)
self.assertEqual(timeinfo.close_date, self.assertEqual(
due_date + Timedelta().from_json(grace_pd_string)) timeinfo.close_date,
due_date + Timedelta().from_json(grace_pd_string)
)
class RelativeTimeTest(unittest.TestCase): class RelativeTimeTest(unittest.TestCase):
...@@ -168,11 +191,14 @@ class RelativeTimeTest(unittest.TestCase): ...@@ -168,11 +191,14 @@ class RelativeTimeTest(unittest.TestCase):
def test_enforce_type(self): def test_enforce_type(self):
self.assertEqual(RelativeTimeTest.delta.enforce_type(None), None) self.assertEqual(RelativeTimeTest.delta.enforce_type(None), None)
self.assertEqual(RelativeTimeTest.delta.enforce_type( self.assertEqual(
datetime.timedelta(days=1, seconds=46799)), RelativeTimeTest.delta.enforce_type(datetime.timedelta(days=1, seconds=46799)),
datetime.timedelta(days=1, seconds=46799)) datetime.timedelta(days=1, seconds=46799)
self.assertEqual(RelativeTimeTest.delta.enforce_type('0:05:07'), )
datetime.timedelta(seconds=307)) self.assertEqual(
RelativeTimeTest.delta.enforce_type('0:05:07'),
datetime.timedelta(seconds=307)
)
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
RelativeTimeTest.delta.enforce_type([1]) RelativeTimeTest.delta.enforce_type([1])
......
...@@ -7,6 +7,7 @@ from xmodule.html_module import HtmlModule ...@@ -7,6 +7,7 @@ from xmodule.html_module import HtmlModule
from . import get_test_system from . import get_test_system
class HtmlModuleSubstitutionTestCase(unittest.TestCase): class HtmlModuleSubstitutionTestCase(unittest.TestCase):
descriptor = Mock() descriptor = Mock()
...@@ -17,7 +18,6 @@ class HtmlModuleSubstitutionTestCase(unittest.TestCase): ...@@ -17,7 +18,6 @@ class HtmlModuleSubstitutionTestCase(unittest.TestCase):
module = HtmlModule(self.descriptor, module_system, field_data, Mock()) module = HtmlModule(self.descriptor, module_system, field_data, Mock())
self.assertEqual(module.get_html(), str(module_system.anonymous_student_id)) self.assertEqual(module.get_html(), str(module_system.anonymous_student_id))
def test_substitution_without_magic_string(self): def test_substitution_without_magic_string(self):
sample_xml = ''' sample_xml = '''
<html> <html>
...@@ -29,7 +29,6 @@ class HtmlModuleSubstitutionTestCase(unittest.TestCase): ...@@ -29,7 +29,6 @@ class HtmlModuleSubstitutionTestCase(unittest.TestCase):
module = HtmlModule(self.descriptor, module_system, field_data, Mock()) module = HtmlModule(self.descriptor, module_system, field_data, Mock())
self.assertEqual(module.get_html(), sample_xml) self.assertEqual(module.get_html(), sample_xml)
def test_substitution_without_anonymous_student_id(self): def test_substitution_without_anonymous_student_id(self):
sample_xml = '''%%USER_ID%%''' sample_xml = '''%%USER_ID%%'''
field_data = DictFieldData({'data': sample_xml}) field_data = DictFieldData({'data': sample_xml})
...@@ -37,4 +36,3 @@ class HtmlModuleSubstitutionTestCase(unittest.TestCase): ...@@ -37,4 +36,3 @@ class HtmlModuleSubstitutionTestCase(unittest.TestCase):
module_system.anonymous_student_id = None module_system.anonymous_student_id = None
module = HtmlModule(self.descriptor, module_system, field_data, Mock()) module = HtmlModule(self.descriptor, module_system, field_data, Mock())
self.assertEqual(module.get_html(), sample_xml) self.assertEqual(module.get_html(), sample_xml)
...@@ -20,6 +20,7 @@ S3_INTERFACE = { ...@@ -20,6 +20,7 @@ S3_INTERFACE = {
"storage_bucket_name": "", "storage_bucket_name": "",
} }
class MockS3Key(object): class MockS3Key(object):
""" """
Mock an S3 Key object from boto. Used for file upload testing. Mock an S3 Key object from boto. Used for file upload testing.
...@@ -96,6 +97,7 @@ class DummyModulestore(object): ...@@ -96,6 +97,7 @@ class DummyModulestore(object):
descriptor.xmodule_runtime = self.get_module_system(descriptor) descriptor.xmodule_runtime = self.get_module_system(descriptor)
return descriptor return descriptor
def serialize_child_history(task_state): def serialize_child_history(task_state):
""" """
To json serialize feedback and post_assessment in child_history of task state. To json serialize feedback and post_assessment in child_history of task state.
...@@ -107,6 +109,7 @@ def serialize_child_history(task_state): ...@@ -107,6 +109,7 @@ def serialize_child_history(task_state):
attempt["post_assessment"]["feedback"] = json.dumps(attempt["post_assessment"].get("feedback")) attempt["post_assessment"]["feedback"] = json.dumps(attempt["post_assessment"].get("feedback"))
task_state["child_history"][i]["post_assessment"] = json.dumps(attempt["post_assessment"]) task_state["child_history"][i]["post_assessment"] = json.dumps(attempt["post_assessment"])
def serialize_open_ended_instance_state(json_str): def serialize_open_ended_instance_state(json_str):
""" """
To json serialize task_states and old_task_states in instance state. To json serialize task_states and old_task_states in instance state.
...@@ -933,4 +936,4 @@ TEST_STATE_AI2_INVALID = ["{\"child_created\": false, \"child_attempts\": 0, \"v ...@@ -933,4 +936,4 @@ TEST_STATE_AI2_INVALID = ["{\"child_created\": false, \"child_attempts\": 0, \"v
TEST_STATE_SINGLE = ["{\"child_created\": false, \"child_attempts\": 1, \"version\": 1, \"child_history\": [{\"answer\": \"'All of us can think of a book that we hope none of our children or any other children have taken off the shelf. But if I have the right to remove that book from the shelf -- that work I abhor -- then you also have exactly the same right and so does everyone else. And then we have no books left on the shelf for any of us.' --Katherine Paterson, Author\\r<br><br>Write a persuasive essay to a newspaper reflecting your views on censorship in libraries. Do you believe that certain materials, such as books, music, movies, magazines, etc., should be removed from the shelves if they are found offensive? Support your position with convincing arguments from your own experience, observations, and/or reading. \", \"post_assessment\": \"[3, 3, 2, 2, 2]\", \"score\": 12}], \"max_score\": 12, \"child_state\": \"done\"}"] TEST_STATE_SINGLE = ["{\"child_created\": false, \"child_attempts\": 1, \"version\": 1, \"child_history\": [{\"answer\": \"'All of us can think of a book that we hope none of our children or any other children have taken off the shelf. But if I have the right to remove that book from the shelf -- that work I abhor -- then you also have exactly the same right and so does everyone else. And then we have no books left on the shelf for any of us.' --Katherine Paterson, Author\\r<br><br>Write a persuasive essay to a newspaper reflecting your views on censorship in libraries. Do you believe that certain materials, such as books, music, movies, magazines, etc., should be removed from the shelves if they are found offensive? Support your position with convincing arguments from your own experience, observations, and/or reading. \", \"post_assessment\": \"[3, 3, 2, 2, 2]\", \"score\": 12}], \"max_score\": 12, \"child_state\": \"done\"}"]
# Peer grading state. # Peer grading state.
TEST_STATE_PE_SINGLE = ["{\"child_created\": false, \"child_attempts\": 0, \"version\": 1, \"child_history\": [{\"answer\": \"Passage its ten led hearted removal cordial. Preference any astonished unreserved mrs. Prosperous understood middletons in conviction an uncommonly do. Supposing so be resolving breakfast am or perfectly. Is drew am hill from mr. Valley by oh twenty direct me so. Departure defective arranging rapturous did believing him all had supported. Family months lasted simple set nature vulgar him. Picture for attempt joy excited ten carried manners talking how. Suspicion neglected he resolving agreement perceived at an. \\r<br><br>Ye on properly handsome returned throwing am no whatever. In without wishing he of picture no exposed talking minutes. Curiosity continual belonging offending so explained it exquisite. Do remember to followed yourself material mr recurred carriage. High drew west we no or at john. About or given on witty event. Or sociable up material bachelor bringing landlord confined. Busy so many in hung easy find well up. So of exquisite my an explained remainder. Dashwood denoting securing be on perceive my laughing so. \\r<br><br>Ought these are balls place mrs their times add she. Taken no great widow spoke of it small. Genius use except son esteem merely her limits. Sons park by do make on. It do oh cottage offered cottage in written. Especially of dissimilar up attachment themselves by interested boisterous. Linen mrs seems men table. Jennings dashwood to quitting marriage bachelor in. On as conviction in of appearance apartments boisterous. \", \"post_assessment\": \"{\\\"submission_id\\\": 1439, \\\"score\\\": [0], \\\"feedback\\\": [\\\"{\\\\\\\"feedback\\\\\\\": \\\\\\\"\\\\\\\"}\\\"], \\\"success\\\": true, \\\"grader_id\\\": [5337], \\\"grader_type\\\": \\\"PE\\\", \\\"rubric_scores_complete\\\": [true], \\\"rubric_xml\\\": [\\\"<rubric><category><description>\\\\nIdeas\\\\n</description><score>0</score><option points='0'>\\\\nDifficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.\\\\n</option><option points='1'>\\\\nAttempts a main idea. Sometimes loses focus or ineffectively displays focus.\\\\n</option><option points='2'>\\\\nPresents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.\\\\n</option><option points='3'>\\\\nPresents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.\\\\n</option></category><category><description>\\\\nContent\\\\n</description><score>0</score><option points='0'>\\\\nIncludes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.\\\\n</option><option points='1'>\\\\nIncludes little information and few or no details. Explores only one or two facets of the topic.\\\\n</option><option points='2'>\\\\nIncludes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.\\\\n</option><option points='3'>\\\\nIncludes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.\\\\n</option></category><category><description>\\\\nOrganization\\\\n</description><score>0</score><option points='0'>\\\\nIdeas organized illogically, transitions weak, and response difficult to follow.\\\\n</option><option points='1'>\\\\nAttempts to logically organize ideas. Attempts to progress in an order that enhances meaning, and demonstrates use of transitions.\\\\n</option><option points='2'>\\\\nIdeas organized logically. Progresses in an order that enhances meaning. Includes smooth transitions.\\\\n</option></category><category><description>\\\\nStyle\\\\n</description><score>0</score><option points='0'>\\\\nContains limited vocabulary, with many words used incorrectly. Demonstrates problems with sentence patterns.\\\\n</option><option points='1'>\\\\nContains basic vocabulary, with words that are predictable and common. Contains mostly simple sentences (although there may be an attempt at more varied sentence patterns).\\\\n</option><option points='2'>\\\\nIncludes vocabulary to make explanations detailed and precise. Includes varied sentence patterns, including complex sentences.\\\\n</option></category><category><description>\\\\nVoice\\\\n</description><score>0</score><option points='0'>\\\\nDemonstrates language and tone that may be inappropriate to task and reader.\\\\n</option><option points='1'>\\\\nDemonstrates an attempt to adjust language and tone to task and reader.\\\\n</option><option points='2'>\\\\nDemonstrates effective adjustment of language and tone to task and reader.\\\\n</option></category></rubric>\\\"]}\", \"score\": 0}], \"max_score\": 12, \"child_state\": \"done\"}"] TEST_STATE_PE_SINGLE = ["{\"child_created\": false, \"child_attempts\": 0, \"version\": 1, \"child_history\": [{\"answer\": \"Passage its ten led hearted removal cordial. Preference any astonished unreserved mrs. Prosperous understood middletons in conviction an uncommonly do. Supposing so be resolving breakfast am or perfectly. Is drew am hill from mr. Valley by oh twenty direct me so. Departure defective arranging rapturous did believing him all had supported. Family months lasted simple set nature vulgar him. Picture for attempt joy excited ten carried manners talking how. Suspicion neglected he resolving agreement perceived at an. \\r<br><br>Ye on properly handsome returned throwing am no whatever. In without wishing he of picture no exposed talking minutes. Curiosity continual belonging offending so explained it exquisite. Do remember to followed yourself material mr recurred carriage. High drew west we no or at john. About or given on witty event. Or sociable up material bachelor bringing landlord confined. Busy so many in hung easy find well up. So of exquisite my an explained remainder. Dashwood denoting securing be on perceive my laughing so. \\r<br><br>Ought these are balls place mrs their times add she. Taken no great widow spoke of it small. Genius use except son esteem merely her limits. Sons park by do make on. It do oh cottage offered cottage in written. Especially of dissimilar up attachment themselves by interested boisterous. Linen mrs seems men table. Jennings dashwood to quitting marriage bachelor in. On as conviction in of appearance apartments boisterous. \", \"post_assessment\": \"{\\\"submission_id\\\": 1439, \\\"score\\\": [0], \\\"feedback\\\": [\\\"{\\\\\\\"feedback\\\\\\\": \\\\\\\"\\\\\\\"}\\\"], \\\"success\\\": true, \\\"grader_id\\\": [5337], \\\"grader_type\\\": \\\"PE\\\", \\\"rubric_scores_complete\\\": [true], \\\"rubric_xml\\\": [\\\"<rubric><category><description>\\\\nIdeas\\\\n</description><score>0</score><option points='0'>\\\\nDifficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.\\\\n</option><option points='1'>\\\\nAttempts a main idea. Sometimes loses focus or ineffectively displays focus.\\\\n</option><option points='2'>\\\\nPresents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.\\\\n</option><option points='3'>\\\\nPresents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.\\\\n</option></category><category><description>\\\\nContent\\\\n</description><score>0</score><option points='0'>\\\\nIncludes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.\\\\n</option><option points='1'>\\\\nIncludes little information and few or no details. Explores only one or two facets of the topic.\\\\n</option><option points='2'>\\\\nIncludes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.\\\\n</option><option points='3'>\\\\nIncludes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.\\\\n</option></category><category><description>\\\\nOrganization\\\\n</description><score>0</score><option points='0'>\\\\nIdeas organized illogically, transitions weak, and response difficult to follow.\\\\n</option><option points='1'>\\\\nAttempts to logically organize ideas. Attempts to progress in an order that enhances meaning, and demonstrates use of transitions.\\\\n</option><option points='2'>\\\\nIdeas organized logically. Progresses in an order that enhances meaning. Includes smooth transitions.\\\\n</option></category><category><description>\\\\nStyle\\\\n</description><score>0</score><option points='0'>\\\\nContains limited vocabulary, with many words used incorrectly. Demonstrates problems with sentence patterns.\\\\n</option><option points='1'>\\\\nContains basic vocabulary, with words that are predictable and common. Contains mostly simple sentences (although there may be an attempt at more varied sentence patterns).\\\\n</option><option points='2'>\\\\nIncludes vocabulary to make explanations detailed and precise. Includes varied sentence patterns, including complex sentences.\\\\n</option></category><category><description>\\\\nVoice\\\\n</description><score>0</score><option points='0'>\\\\nDemonstrates language and tone that may be inappropriate to task and reader.\\\\n</option><option points='1'>\\\\nDemonstrates an attempt to adjust language and tone to task and reader.\\\\n</option><option points='2'>\\\\nDemonstrates effective adjustment of language and tone to task and reader.\\\\n</option></category></rubric>\\\"]}\", \"score\": 0}], \"max_score\": 12, \"child_state\": \"done\"}"]
\ No newline at end of file
...@@ -132,7 +132,6 @@ class InheritingFieldDataTest(unittest.TestCase): ...@@ -132,7 +132,6 @@ class InheritingFieldDataTest(unittest.TestCase):
self.assertEqual(child.not_inherited, "nothing") self.assertEqual(child.not_inherited, "nothing")
class EditableMetadataFieldsTest(unittest.TestCase): class EditableMetadataFieldsTest(unittest.TestCase):
def test_display_name_field(self): def test_display_name_field(self):
editable_fields = self.get_xml_editable_fields(DictFieldData({})) editable_fields = self.get_xml_editable_fields(DictFieldData({}))
...@@ -331,7 +330,6 @@ class TestDeserializeInteger(TestDeserialize): ...@@ -331,7 +330,6 @@ class TestDeserializeInteger(TestDeserialize):
# 2.78 can be converted to int, so the string will be deserialized # 2.78 can be converted to int, so the string will be deserialized
self.assertDeserializeEqual(-2.78, '-2.78') self.assertDeserializeEqual(-2.78, '-2.78')
def test_deserialize_unsupported_types(self): def test_deserialize_unsupported_types(self):
self.assertDeserializeEqual('[3]', '[3]') self.assertDeserializeEqual('[3]', '[3]')
# '2.78' cannot be converted to int, so input value is returned # '2.78' cannot be converted to int, so input value is returned
...@@ -415,7 +413,7 @@ class TestDeserializeAny(TestDeserialize): ...@@ -415,7 +413,7 @@ class TestDeserializeAny(TestDeserialize):
def test_deserialize(self): def test_deserialize(self):
self.assertDeserializeEqual('hAlf', '"hAlf"') self.assertDeserializeEqual('hAlf', '"hAlf"')
self.assertDeserializeEqual('false', '"false"') self.assertDeserializeEqual('false', '"false"')
self.assertDeserializeEqual({'bar': 'hat', 'frog' : 'green'}, '{"bar": "hat", "frog": "green"}') self.assertDeserializeEqual({'bar': 'hat', 'frog': 'green'}, '{"bar": "hat", "frog": "green"}')
self.assertDeserializeEqual([3.5, 5.6], '[3.5, 5.6]') self.assertDeserializeEqual([3.5, 5.6], '[3.5, 5.6]')
self.assertDeserializeEqual('[', '[') self.assertDeserializeEqual('[', '[')
self.assertDeserializeEqual(False, 'false') self.assertDeserializeEqual(False, 'false')
...@@ -457,10 +455,14 @@ class TestDeserializeTimedelta(TestDeserialize): ...@@ -457,10 +455,14 @@ class TestDeserializeTimedelta(TestDeserialize):
test_field = Timedelta test_field = Timedelta
def test_deserialize(self): def test_deserialize(self):
self.assertDeserializeEqual('1 day 12 hours 59 minutes 59 seconds', self.assertDeserializeEqual(
'1 day 12 hours 59 minutes 59 seconds') '1 day 12 hours 59 minutes 59 seconds',
self.assertDeserializeEqual('1 day 12 hours 59 minutes 59 seconds', '1 day 12 hours 59 minutes 59 seconds'
'"1 day 12 hours 59 minutes 59 seconds"') )
self.assertDeserializeEqual(
'1 day 12 hours 59 minutes 59 seconds',
'"1 day 12 hours 59 minutes 59 seconds"'
)
self.assertDeserializeNonString() self.assertDeserializeNonString()
......
...@@ -46,5 +46,3 @@ class ProblemPage(PageObject): ...@@ -46,5 +46,3 @@ class ProblemPage(PageObject):
Is there a "correct" status showing? Is there a "correct" status showing?
""" """
return self.q(css="div.problem div.capa_inputtype.textline div.correct p.status").is_present() return self.q(css="div.problem div.capa_inputtype.textline div.correct p.status").is_present()
...@@ -5,6 +5,7 @@ from django.conf import settings ...@@ -5,6 +5,7 @@ from django.conf import settings
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from microsite_configuration import microsite from microsite_configuration import microsite
def get_visible_courses(): def get_visible_courses():
""" """
Return the set of CourseDescriptors that should be visible in this branded instance Return the set of CourseDescriptors that should be visible in this branded instance
......
...@@ -33,7 +33,7 @@ class AnonymousIndexPageTest(ModuleStoreTestCase): ...@@ -33,7 +33,7 @@ class AnonymousIndexPageTest(ModuleStoreTestCase):
self.factory = RequestFactory() self.factory = RequestFactory()
self.course = CourseFactory.create( self.course = CourseFactory.create(
days_early_for_beta=5, days_early_for_beta=5,
enrollment_start=datetime.datetime.now(UTC)+datetime.timedelta(days=3), enrollment_start=datetime.datetime.now(UTC) + datetime.timedelta(days=3),
user_id=self.user.id, user_id=self.user.id,
) )
......
...@@ -44,9 +44,10 @@ def index(request): ...@@ -44,9 +44,10 @@ def index(request):
# courses in his/her dashboard. Otherwise UX is a bit cryptic. # courses in his/her dashboard. Otherwise UX is a bit cryptic.
# In this case, we want to have the user stay on a course catalog # In this case, we want to have the user stay on a course catalog
# page to make it easier to browse for courses (and register) # page to make it easier to browse for courses (and register)
if microsite.get_value('ALWAYS_REDIRECT_HOMEPAGE_TO_DASHBOARD_FOR_AUTHENTICATED_USER', \ if microsite.get_value(
settings.FEATURES.get('ALWAYS_REDIRECT_HOMEPAGE_TO_DASHBOARD_FOR_AUTHENTICATED_USER', True)) or \ 'ALWAYS_REDIRECT_HOMEPAGE_TO_DASHBOARD_FOR_AUTHENTICATED_USER',
get_course_enrollments(request.user): settings.FEATURES.get('ALWAYS_REDIRECT_HOMEPAGE_TO_DASHBOARD_FOR_AUTHENTICATED_USER', True)
) or get_course_enrollments(request.user):
return redirect(reverse('dashboard')) return redirect(reverse('dashboard'))
......
...@@ -21,6 +21,7 @@ class CourseEmailTemplateForm(forms.ModelForm): # pylint: disable=R0924 ...@@ -21,6 +21,7 @@ class CourseEmailTemplateForm(forms.ModelForm): # pylint: disable=R0924
"""Form providing validation of CourseEmail templates.""" """Form providing validation of CourseEmail templates."""
name = forms.CharField(required=False) name = forms.CharField(required=False)
class Meta: # pylint: disable=C0111 class Meta: # pylint: disable=C0111
model = CourseEmailTemplate model = CourseEmailTemplate
fields = ('html_template', 'plain_template', 'name') fields = ('html_template', 'plain_template', 'name')
......
...@@ -130,6 +130,7 @@ class CourseEmail(Email): ...@@ -130,6 +130,7 @@ class CourseEmail(Email):
""" """
return CourseEmailTemplate.get_template(name=self.template_name) return CourseEmailTemplate.get_template(name=self.template_name)
class Optout(models.Model): class Optout(models.Model):
""" """
Stores users that have opted out of receiving emails from a course. Stores users that have opted out of receiving emails from a course.
......
...@@ -13,25 +13,29 @@ class Command(BaseCommand): ...@@ -13,25 +13,29 @@ class Command(BaseCommand):
""" """
option_list = BaseCommand.option_list + ( option_list = BaseCommand.option_list + (
make_option('-n', '--noop', make_option(
'-n',
'--noop',
action='store_true', action='store_true',
dest='noop', dest='noop',
default=False, default=False,
help="Print but do not update the GeneratedCertificate table"), help="Print but do not update the GeneratedCertificate table"
make_option('-c', '--course', ),
make_option(
'-c',
'--course',
metavar='COURSE_ID', metavar='COURSE_ID',
dest='course', dest='course',
default=False, default=False,
help='Grade ungraded users for this course'), help='Grade ungraded users for this course'
),
) )
def handle(self, *args, **options): def handle(self, *args, **options):
course_id = options['course'] course_id = options['course']
print "Fetching ungraded students for {0}".format(course_id) print "Fetching ungraded students for {0}".format(course_id)
ungraded = GeneratedCertificate.objects.filter( ungraded = GeneratedCertificate.objects.filter(
course_id__exact=course_id).filter(grade__exact='') course_id__exact=course_id).filter(grade__exact='')
course = courses.get_course_by_id(course_id) course = courses.get_course_by_id(course_id)
factory = RequestFactory() factory = RequestFactory()
request = factory.get('/') request = factory.get('/')
......
...@@ -49,20 +49,20 @@ Eligibility: ...@@ -49,20 +49,20 @@ Eligibility:
If the user and course is present in the certificate whitelist table If the user and course is present in the certificate whitelist table
then the student will be issued a certificate regardless of his grade, then the student will be issued a certificate regardless of his grade,
unless he has allow_certificate set to False. unless he has allow_certificate set to False.
""" """
class CertificateStatuses(object): class CertificateStatuses(object):
deleted = 'deleted' deleted = 'deleted'
deleting = 'deleting' deleting = 'deleting'
downloadable = 'downloadable' downloadable = 'downloadable'
error = 'error' error = 'error'
generating = 'generating' generating = 'generating'
notpassing = 'notpassing' notpassing = 'notpassing'
regenerating = 'regenerating' regenerating = 'regenerating'
restricted = 'restricted' restricted = 'restricted'
unavailable = 'unavailable' unavailable = 'unavailable'
class CertificateWhitelist(models.Model): class CertificateWhitelist(models.Model):
""" """
...@@ -88,7 +88,7 @@ class GeneratedCertificate(models.Model): ...@@ -88,7 +88,7 @@ class GeneratedCertificate(models.Model):
course_id = CourseKeyField(max_length=255, blank=True, default=None) course_id = CourseKeyField(max_length=255, blank=True, default=None)
verify_uuid = models.CharField(max_length=32, blank=True, default='') verify_uuid = models.CharField(max_length=32, blank=True, default='')
download_uuid = models.CharField(max_length=32, blank=True, default='') download_uuid = models.CharField(max_length=32, blank=True, default='')
download_url = models.CharField(max_length=128, blank=True, default='') download_url = models.CharField(max_length=128, blank=True, default='')
grade = models.CharField(max_length=5, blank=True, default='') grade = models.CharField(max_length=5, blank=True, default='')
key = models.CharField(max_length=32, blank=True, default='') key = models.CharField(max_length=32, blank=True, default='')
distinction = models.BooleanField(default=False) distinction = models.BooleanField(default=False)
...@@ -117,6 +117,7 @@ class GeneratedCertificate(models.Model): ...@@ -117,6 +117,7 @@ class GeneratedCertificate(models.Model):
return None return None
def certificate_status_for_student(student, course_id): def certificate_status_for_student(student, course_id):
''' '''
This returns a dictionary with a key for status, and other information. This returns a dictionary with a key for status, and other information.
......
...@@ -4,6 +4,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey ...@@ -4,6 +4,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from certificates.models import GeneratedCertificate, CertificateStatuses from certificates.models import GeneratedCertificate, CertificateStatuses
# Factories don't have __init__ methods, and are self documenting # Factories don't have __init__ methods, and are self documenting
# pylint: disable=W0232 # pylint: disable=W0232
class GeneratedCertificateFactory(DjangoModelFactory): class GeneratedCertificateFactory(DjangoModelFactory):
......
...@@ -92,7 +92,6 @@ def update_certificate(request): ...@@ -92,7 +92,6 @@ def update_certificate(request):
# HTTP error (reason=error(32, 'Broken pipe'), filename=None) : # HTTP error (reason=error(32, 'Broken pipe'), filename=None) :
# certificate_agent.py:175 # certificate_agent.py:175
cert.error_reason = xqueue_body['error_reason'] cert.error_reason = xqueue_body['error_reason']
else: else:
if cert.status in [status.generating, status.regenerating]: if cert.status in [status.generating, status.regenerating]:
...@@ -105,10 +104,13 @@ def update_certificate(request): ...@@ -105,10 +104,13 @@ def update_certificate(request):
else: else:
logger.critical('Invalid state for cert update: {0}'.format( logger.critical('Invalid state for cert update: {0}'.format(
cert.status)) cert.status))
return HttpResponse(json.dumps({ return HttpResponse(
'return_code': 1, json.dumps({
'content': 'invalid cert status'}), 'return_code': 1,
mimetype='application/json') 'content': 'invalid cert status'
}),
mimetype='application/json'
)
dog_stats_api.increment(XQUEUE_METRIC_NAME, tags=[ dog_stats_api.increment(XQUEUE_METRIC_NAME, tags=[
u'action:update_certificate', u'action:update_certificate',
......
...@@ -17,6 +17,7 @@ from opaque_keys.edx.locations import Location ...@@ -17,6 +17,7 @@ from opaque_keys.edx.locations import Location
# Used to limit the length of list displayed to the screen. # Used to limit the length of list displayed to the screen.
MAX_SCREEN_LIST_LENGTH = 250 MAX_SCREEN_LIST_LENGTH = 250
def get_problem_grade_distribution(course_id): def get_problem_grade_distribution(course_id):
""" """
Returns the grade distribution per problem for the course Returns the grade distribution per problem for the course
......
...@@ -105,6 +105,7 @@ class RequestHandlerWithSessionId(object): ...@@ -105,6 +105,7 @@ class RequestHandlerWithSessionId(object):
return True return True
return False return False
def get_metadata(parent_location, player_mode, data, display_name='Video'): def get_metadata(parent_location, player_mode, data, display_name='Video'):
kwargs = { kwargs = {
'parent_location': parent_location, 'parent_location': parent_location,
...@@ -316,7 +317,7 @@ def reload_the_page_with_video(_step): ...@@ -316,7 +317,7 @@ def reload_the_page_with_video(_step):
@step('youtube stub server (.*) YouTube API') @step('youtube stub server (.*) YouTube API')
def configure_youtube_api(_step, action): def configure_youtube_api(_step, action):
action=action.strip() action = action.strip()
if action == 'proxies': if action == 'proxies':
world.youtube.config['youtube_api_blocked'] = False world.youtube.config['youtube_api_blocked'] = False
elif action == 'blocks': elif action == 'blocks':
...@@ -569,7 +570,7 @@ def video_alignment(_step, transcript_visibility): ...@@ -569,7 +570,7 @@ def video_alignment(_step, transcript_visibility):
set_window_dimensions(300, 600) set_window_dimensions(300, 600)
real, expected = get_all_dimensions() real, expected = get_all_dimensions()
width = round(100 * real['width']/expected['width']) == wrapper_width width = round(100 * real['width'] / expected['width']) == wrapper_width
set_window_dimensions(600, 300) set_window_dimensions(600, 300)
real, expected = get_all_dimensions() real, expected = get_all_dimensions()
...@@ -659,7 +660,6 @@ def is_hidden_button(_step, button, state): ...@@ -659,7 +660,6 @@ def is_hidden_button(_step, button, state):
def i_see_active_button(_step, button, state): def i_see_active_button(_step, button, state):
selector = VIDEO_BUTTONS[button] selector = VIDEO_BUTTONS[button]
if state == 'active': if state == 'active':
assert world.css_has_class(selector, 'active') assert world.css_has_class(selector, 'active')
else: else:
assert not world.css_has_class(selector, 'active') assert not world.css_has_class(selector, 'active')
...@@ -160,11 +160,11 @@ class StudentModuleHistoryCleaner(object): ...@@ -160,11 +160,11 @@ class StudentModuleHistoryCleaner(object):
""" """
start = self.next_student_module_id start = self.next_student_module_id
for smid in range(start, start+batch_size): for smid in range(start, start + batch_size):
if smid > self.last_student_module_id: if smid > self.last_student_module_id:
break break
yield smid yield smid
self.next_student_module_id = smid+1 self.next_student_module_id = smid + 1
def get_history_for_student_modules(self, student_module_id): def get_history_for_student_modules(self, student_module_id):
""" """
...@@ -177,7 +177,8 @@ class StudentModuleHistoryCleaner(object): ...@@ -177,7 +177,8 @@ class StudentModuleHistoryCleaner(object):
""" """
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute(""" cursor.execute(
"""
SELECT id, created FROM courseware_studentmodulehistory SELECT id, created FROM courseware_studentmodulehistory
WHERE student_module_id = %s WHERE student_module_id = %s
ORDER BY created, id ORDER BY created, id
...@@ -196,7 +197,8 @@ class StudentModuleHistoryCleaner(object): ...@@ -196,7 +197,8 @@ class StudentModuleHistoryCleaner(object):
""" """
assert ids_to_delete assert ids_to_delete
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute(""" cursor.execute(
"""
DELETE FROM courseware_studentmodulehistory DELETE FROM courseware_studentmodulehistory
WHERE id IN ({ids}) WHERE id IN ({ids})
""".format(ids=",".join(str(i) for i in ids_to_delete)) """.format(ids=",".join(str(i) for i in ids_to_delete))
......
...@@ -12,7 +12,7 @@ from xmodule.modulestore.xml import XMLModuleStore ...@@ -12,7 +12,7 @@ from xmodule.modulestore.xml import XMLModuleStore
def traverse_tree(course): def traverse_tree(course):
'''Load every descriptor in course. Return bool success value.''' """Load every descriptor in course. Return bool success value."""
queue = [course] queue = [course]
while len(queue) > 0: while len(queue) > 0:
node = queue.pop() node = queue.pop()
...@@ -56,9 +56,11 @@ def import_with_checks(course_dir, verbose=True): ...@@ -56,9 +56,11 @@ def import_with_checks(course_dir, verbose=True):
# No default class--want to complain if it doesn't find plugins for any # No default class--want to complain if it doesn't find plugins for any
# module. # module.
modulestore = XMLModuleStore(data_dir, modulestore = XMLModuleStore(
default_class=None, data_dir,
course_dirs=course_dirs) default_class=None,
course_dirs=course_dirs
)
def str_of_err(tpl): def str_of_err(tpl):
(msg, exc_str) = tpl (msg, exc_str) = tpl
...@@ -83,11 +85,10 @@ def import_with_checks(course_dir, verbose=True): ...@@ -83,11 +85,10 @@ def import_with_checks(course_dir, verbose=True):
print "=" * 40 print "=" * 40
print '\n' print '\n'
# print course
#print course
validators = ( validators = (
traverse_tree, traverse_tree,
) )
print "=" * 40 print "=" * 40
print "Running validators..." print "Running validators..."
...@@ -96,7 +97,6 @@ def import_with_checks(course_dir, verbose=True): ...@@ -96,7 +97,6 @@ def import_with_checks(course_dir, verbose=True):
print 'Running {0}'.format(validate.__name__) print 'Running {0}'.format(validate.__name__)
all_ok = validate(course) and all_ok all_ok = validate(course) and all_ok
if all_ok: if all_ok:
print 'Course passes all checks!' print 'Course passes all checks!'
else: else:
...@@ -105,7 +105,7 @@ def import_with_checks(course_dir, verbose=True): ...@@ -105,7 +105,7 @@ def import_with_checks(course_dir, verbose=True):
def check_roundtrip(course_dir): def check_roundtrip(course_dir):
'''Check that import->export leaves the course the same''' """Check that import->export leaves the course the same"""
print "====== Roundtrip import =======" print "====== Roundtrip import ======="
(ok, course) = import_with_checks(course_dir) (ok, course) = import_with_checks(course_dir)
...@@ -135,7 +135,6 @@ def clean_xml(course_dir, export_dir, force): ...@@ -135,7 +135,6 @@ def clean_xml(course_dir, export_dir, force):
print "Did NOT export" print "Did NOT export"
class Command(BaseCommand): class Command(BaseCommand):
help = """Imports specified course.xml, validate it, then exports in help = """Imports specified course.xml, validate it, then exports in
a canonical format. a canonical format.
...@@ -145,6 +144,7 @@ Usage: clean_xml PATH-TO-COURSE-DIR PATH-TO-OUTPUT-DIR [force] ...@@ -145,6 +144,7 @@ Usage: clean_xml PATH-TO-COURSE-DIR PATH-TO-OUTPUT-DIR [force]
If 'force' is specified as the last argument, exports even if there If 'force' is specified as the last argument, exports even if there
were import errors. were import errors.
""" """
def handle(self, *args, **options): def handle(self, *args, **options):
n = len(args) n = len(args)
if n < 2 or n > 3: if n < 2 or n > 3:
......
...@@ -21,9 +21,11 @@ def import_course(course_dir, verbose=True): ...@@ -21,9 +21,11 @@ def import_course(course_dir, verbose=True):
# No default class--want to complain if it doesn't find plugins for any # No default class--want to complain if it doesn't find plugins for any
# module. # module.
modulestore = XMLModuleStore(data_dir, modulestore = XMLModuleStore(
default_class=None, data_dir,
course_dirs=course_dirs) default_class=None,
course_dirs=course_dirs
)
def str_of_err(tpl): def str_of_err(tpl):
(msg, exc_str) = tpl (msg, exc_str) = tpl
...@@ -89,6 +91,7 @@ Usage: metadata_to_json PATH-TO-COURSE-DIR OUTPUT-PATH ...@@ -89,6 +91,7 @@ Usage: metadata_to_json PATH-TO-COURSE-DIR OUTPUT-PATH
if OUTPUT-PATH isn't given, print to stdout. if OUTPUT-PATH isn't given, print to stdout.
""" """
def handle(self, *args, **options): def handle(self, *args, **options):
n = len(args) n = len(args)
if n < 1 or n > 2: if n < 1 or n > 2:
......
...@@ -46,7 +46,7 @@ class AboutTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -46,7 +46,7 @@ class AboutTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
# Check that registration button is present # Check that registration button is present
self.assertIn(REG_STR, resp.content) self.assertIn(REG_STR, resp.content)
@patch.dict(settings.FEATURES, {'ENABLE_MKTG_SITE': True}) @patch.dict(settings.FEATURES, {'ENABLE_MKTG_SITE': True})
def test_logged_in_marketing(self): def test_logged_in_marketing(self):
self.setup_user() self.setup_user()
url = reverse('about_course', args=[self.course.id.to_deprecated_string()]) url = reverse('about_course', args=[self.course.id.to_deprecated_string()])
......
...@@ -21,6 +21,7 @@ from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE ...@@ -21,6 +21,7 @@ from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from courseware.views import get_course_lti_endpoints from courseware.views import get_course_lti_endpoints
from lms.lib.xblock.runtime import quote_slashes from lms.lib.xblock.runtime import quote_slashes
class TestLTI(BaseTestXmodule): class TestLTI(BaseTestXmodule):
""" """
Integration test for lti xmodule. Integration test for lti xmodule.
...@@ -107,7 +108,7 @@ class TestLTI(BaseTestXmodule): ...@@ -107,7 +108,7 @@ class TestLTI(BaseTestXmodule):
old_parsed[u'OAuth oauth_nonce'] = mocked_nonce old_parsed[u'OAuth oauth_nonce'] = mocked_nonce
old_parsed[u'oauth_timestamp'] = mocked_timestamp old_parsed[u'oauth_timestamp'] = mocked_timestamp
old_parsed[u'oauth_signature'] = mocked_signature_after_sign old_parsed[u'oauth_signature'] = mocked_signature_after_sign
headers[u'Authorization'] = ', '.join([k+'="'+v+'"' for k, v in old_parsed.items()]) headers[u'Authorization'] = ', '.join([k + '="' + v + '"' for k, v in old_parsed.items()])
return None, headers, None return None, headers, None
patcher = mock.patch.object(oauthlib.oauth1.Client, "sign", mocked_sign) patcher = mock.patch.object(oauthlib.oauth1.Client, "sign", mocked_sign)
......
...@@ -10,6 +10,7 @@ from mock import patch ...@@ -10,6 +10,7 @@ from mock import patch
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from django.utils import translation from django.utils import translation
class TestSortedCountryList(TestCase): class TestSortedCountryList(TestCase):
""" """
Test that country list is always sorted alphabetically Test that country list is always sorted alphabetically
...@@ -48,7 +49,7 @@ class TestSortedCountryList(TestCase): ...@@ -48,7 +49,7 @@ class TestSortedCountryList(TestCase):
self.assertLess(options[1].text, options[10].text) self.assertLess(options[1].text, options[10].text)
@patch.dict(settings.REGISTRATION_EXTRA_FIELDS, {'country': 'required'}) @patch.dict(settings.REGISTRATION_EXTRA_FIELDS, {'country': 'required'})
def test_country_sorting_french (self): def test_country_sorting_french(self):
""" """
Test that country list is always sorted alphabetically in French Test that country list is always sorted alphabetically in French
""" """
...@@ -73,7 +74,8 @@ class TestSortedCountryList(TestCase): ...@@ -73,7 +74,8 @@ class TestSortedCountryList(TestCase):
self.assertLess(af_index, us_index) self.assertLess(af_index, us_index)
# testing two option elements to be in alphabetical order # testing two option elements to be in alphabetical order
self.assertLess(options[1].text, options[10].text) self.assertLess(options[1].text, options[10].text)
class TestExtraRegistrationVariables(TestCase): class TestExtraRegistrationVariables(TestCase):
""" """
Test that extra registration variables are properly checked according to settings Test that extra registration variables are properly checked according to settings
......
...@@ -58,6 +58,7 @@ def _check_asset(location, asset_name): ...@@ -58,6 +58,7 @@ def _check_asset(location, asset_name):
else: else:
return True return True
def _clear_assets(location): def _clear_assets(location):
""" """
Clear all assets for location. Clear all assets for location.
...@@ -154,6 +155,7 @@ class TestVideo(BaseTestXmodule): ...@@ -154,6 +155,7 @@ class TestVideo(BaseTestXmodule):
def tearDown(self): def tearDown(self):
_clear_assets(self.item_descriptor.location) _clear_assets(self.item_descriptor.location)
class TestTranscriptAvailableTranslationsDispatch(TestVideo): class TestTranscriptAvailableTranslationsDispatch(TestVideo):
""" """
Test video handler that provide available translations info. Test video handler that provide available translations info.
...@@ -336,8 +338,9 @@ class TestTranscriptTranslationGetDispatch(TestVideo): ...@@ -336,8 +338,9 @@ class TestTranscriptTranslationGetDispatch(TestVideo):
u'end': [100], u'end': [100],
u'start': [12], u'start': [12],
u'text': [ u'text': [
u'\u041f\u0440\u0438\u0432\u0456\u0442, edX \u0432\u0456\u0442\u0430\u0454 \u0432\u0430\u0441.' u'\u041f\u0440\u0438\u0432\u0456\u0442, edX \u0432\u0456\u0442\u0430\u0454 \u0432\u0430\u0441.'
]} ]
}
self.non_en_file.seek(0) self.non_en_file.seek(0)
_upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1]) _upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1])
subs_id = _get_subs_id(self.non_en_file.name) subs_id = _get_subs_id(self.non_en_file.name)
...@@ -580,7 +583,7 @@ class TestStudioTranscriptTranslationPostDispatch(TestVideo): ...@@ -580,7 +583,7 @@ class TestStudioTranscriptTranslationPostDispatch(TestVideo):
# No language is passed. # No language is passed.
request = Request.blank('/translation', POST={'file': ('filename', SRT_content)}) request = Request.blank('/translation', POST={'file': ('filename', SRT_content)})
response = self.item_descriptor.studio_transcript(request=request, dispatch='translation') response = self.item_descriptor.studio_transcript(request=request, dispatch='translation')
self.assertEqual(response.status, '400 Bad Request') self.assertEqual(response.status, '400 Bad Request')
# Language, good filename and good content. # Language, good filename and good content.
request = Request.blank('/translation/uk', POST={'file': ('filename.srt', SRT_content)}) request = Request.blank('/translation/uk', POST={'file': ('filename.srt', SRT_content)})
......
...@@ -372,7 +372,6 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -372,7 +372,6 @@ class TestGetHtmlMethod(BaseTestXmodule):
self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context) self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context)
) )
def test_get_html_with_non_existant_edx_video_id(self): def test_get_html_with_non_existant_edx_video_id(self):
""" """
Tests the VideoModule get_html where a edx_video_id is given but a video is not found Tests the VideoModule get_html where a edx_video_id is given but a video is not found
...@@ -395,7 +394,7 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -395,7 +394,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
<source src="example.mp4"/> <source src="example.mp4"/>
<source src="example.webm"/> <source src="example.webm"/>
""", """,
'edx_video_id':"meow", 'edx_video_id': "meow",
'result': { 'result': {
'download_video_link': u'example_source.mp4', 'download_video_link': u'example_source.mp4',
'sources': json.dumps([u'example.mp4', u'example.webm']), 'sources': json.dumps([u'example.mp4', u'example.webm']),
...@@ -416,7 +415,7 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -416,7 +415,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
@mock.patch('edxval.api.get_video_info') @mock.patch('edxval.api.get_video_info')
def test_get_html_with_mocked_edx_video_id(self, mock_get_video_info): def test_get_html_with_mocked_edx_video_id(self, mock_get_video_info):
mock_get_video_info.return_value = { mock_get_video_info.return_value = {
'url' : '/edxval/video/example', 'url': '/edxval/video/example',
'edx_video_id': u'example', 'edx_video_id': u'example',
'duration': 111.0, 'duration': 111.0,
'client_video_id': u'The example video', 'client_video_id': u'The example video',
...@@ -556,7 +555,7 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -556,7 +555,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
<source src="example.mp4"/> <source src="example.mp4"/>
<source src="example.webm"/> <source src="example.webm"/>
""", """,
'edx_video_id':"thundercats", 'edx_video_id': "thundercats",
'result': { 'result': {
'download_video_link': u'http://fake-video.edx.org/thundercats.mp4', 'download_video_link': u'http://fake-video.edx.org/thundercats.mp4',
'sources': json.dumps([u'example.mp4', u'example.webm']), 'sources': json.dumps([u'example.mp4', u'example.webm']),
...@@ -624,8 +623,8 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -624,8 +623,8 @@ class TestGetHtmlMethod(BaseTestXmodule):
""" """
def side_effect(*args, **kwargs): def side_effect(*args, **kwargs):
cdn = { cdn = {
'http://example.com/example.mp4': 'http://cdn_example.com/example.mp4', 'http://example.com/example.mp4': 'http://cdn_example.com/example.mp4',
'http://example.com/example.webm': 'http://cdn_example.com/example.webm', 'http://example.com/example.webm': 'http://cdn_example.com/example.webm',
} }
return cdn.get(args[1]) return cdn.get(args[1])
...@@ -717,7 +716,6 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -717,7 +716,6 @@ class TestGetHtmlMethod(BaseTestXmodule):
self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context) self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context)
) )
@patch('xmodule.video_module.video_module.get_video_from_cdn') @patch('xmodule.video_module.video_module.get_video_from_cdn')
def test_get_html_cdn_source(self, mocked_get_video): def test_get_html_cdn_source(self, mocked_get_video):
""" """
...@@ -725,8 +723,8 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -725,8 +723,8 @@ class TestGetHtmlMethod(BaseTestXmodule):
""" """
def side_effect(*args, **kwargs): def side_effect(*args, **kwargs):
cdn = { cdn = {
'http://example.com/example.mp4': 'http://cdn_example.com/example.mp4', 'http://example.com/example.mp4': 'http://cdn_example.com/example.mp4',
'http://example.com/example.webm': 'http://cdn_example.com/example.webm', 'http://example.com/example.webm': 'http://cdn_example.com/example.webm',
} }
return cdn.get(args[1]) return cdn.get(args[1])
......
...@@ -39,6 +39,7 @@ def _attr_safe_json(obj): ...@@ -39,6 +39,7 @@ def _attr_safe_json(obj):
""" """
return saxutils.escape(json.dumps(obj), {'"': '&quot;'}) return saxutils.escape(json.dumps(obj), {'"': '&quot;'})
@newrelic.agent.function_trace() @newrelic.agent.function_trace()
def make_course_settings(course): def make_course_settings(course):
""" """
...@@ -56,6 +57,7 @@ def make_course_settings(course): ...@@ -56,6 +57,7 @@ def make_course_settings(course):
return obj return obj
@newrelic.agent.function_trace() @newrelic.agent.function_trace()
def get_threads(request, course_key, discussion_id=None, per_page=THREADS_PER_PAGE): def get_threads(request, course_key, discussion_id=None, per_page=THREADS_PER_PAGE):
""" """
...@@ -156,6 +158,7 @@ def inline_discussion(request, course_id, discussion_id): ...@@ -156,6 +158,7 @@ def inline_discussion(request, course_id, discussion_id):
'course_settings': make_course_settings(course) 'course_settings': make_course_settings(course)
}) })
@login_required @login_required
def forum_form_discussion(request, course_id): def forum_form_discussion(request, course_id):
""" """
...@@ -212,7 +215,7 @@ def forum_form_discussion(request, course_id): ...@@ -212,7 +215,7 @@ def forum_form_discussion(request, course_id):
'roles': _attr_safe_json(utils.get_role_ids(course_key)), 'roles': _attr_safe_json(utils.get_role_ids(course_key)),
'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_key), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_key),
'cohorts': course_settings["cohorts"], # still needed to render _thread_list_template 'cohorts': course_settings["cohorts"], # still needed to render _thread_list_template
'user_cohort': user_cohort_id, # read from container in NewPostView 'user_cohort': user_cohort_id, # read from container in NewPostView
'is_course_cohorted': is_course_cohorted(course_key), # still needed to render _thread_list_template 'is_course_cohorted': is_course_cohorted(course_key), # still needed to render _thread_list_template
'sort_preference': user.default_sort_key, 'sort_preference': user.default_sort_key,
'category_map': course_settings["category_map"], 'category_map': course_settings["category_map"],
...@@ -314,6 +317,7 @@ def single_thread(request, course_id, discussion_id, thread_id): ...@@ -314,6 +317,7 @@ def single_thread(request, course_id, discussion_id, thread_id):
} }
return render_to_response('discussion/index.html', context) return render_to_response('discussion/index.html', context)
@require_GET @require_GET
@login_required @login_required
def user_profile(request, course_id, user_id): def user_profile(request, course_id, user_id):
......
...@@ -9,12 +9,16 @@ def include_mustache_templates(): ...@@ -9,12 +9,16 @@ def include_mustache_templates():
def is_valid_file_name(file_name): def is_valid_file_name(file_name):
return file_name.endswith('.mustache') return file_name.endswith('.mustache')
def read_file(file_name): def read_file(file_name):
return open(mustache_dir / file_name, "r").read().decode('utf-8') return open(mustache_dir / file_name, "r").read().decode('utf-8')
def template_id_from_file_name(file_name): def template_id_from_file_name(file_name):
return file_name.rpartition('.')[0] return file_name.rpartition('.')[0]
def process_mako(template_content): def process_mako(template_content):
return Template(template_content).render_unicode() return Template(template_content).render_unicode()
def make_script_tag(id, content): def make_script_tag(id, content):
return u"<script type='text/template' id='{0}'>{1}</script>".format(id, content) return u"<script type='text/template' id='{0}'>{1}</script>".format(id, content)
......
...@@ -121,7 +121,6 @@ class PuzzleComplete(models.Model): ...@@ -121,7 +121,6 @@ class PuzzleComplete(models.Model):
'subset': c.puzzle_subset, 'subset': c.puzzle_subset,
'created': c.created} for c in complete] 'created': c.created} for c in complete]
@staticmethod @staticmethod
def is_level_complete(anonymous_user_id, level, sub_level, due=None): def is_level_complete(anonymous_user_id, level, sub_level, due=None):
""" """
...@@ -141,4 +140,3 @@ class PuzzleComplete(models.Model): ...@@ -141,4 +140,3 @@ class PuzzleComplete(models.Model):
complete = complete.filter(created__lte=due) complete = complete.filter(created__lte=due)
return complete.exists() return complete.exists()
...@@ -28,19 +28,25 @@ class HintManagerTest(ModuleStoreTestCase): ...@@ -28,19 +28,25 @@ class HintManagerTest(ModuleStoreTestCase):
self.c.login(username='robot', password='test') self.c.login(username='robot', password='test')
self.course_id = self.course.id self.course_id = self.course.id
self.problem_id = self.course_id.make_usage_key('crowdsource_hinter', 'crowdsource_hinter_001') self.problem_id = self.course_id.make_usage_key('crowdsource_hinter', 'crowdsource_hinter_001')
UserStateSummaryFactory.create(field_name='hints', UserStateSummaryFactory.create(
usage_id=self.problem_id, field_name='hints',
value=json.dumps({'1.0': {'1': ['Hint 1', 2], usage_id=self.problem_id,
'3': ['Hint 3', 12]}, value=json.dumps({
'2.0': {'4': ['Hint 4', 3]} '1.0': {'1': ['Hint 1', 2], '3': ['Hint 3', 12]},
})) '2.0': {'4': ['Hint 4', 3]}
UserStateSummaryFactory.create(field_name='mod_queue', })
usage_id=self.problem_id, )
value=json.dumps({'2.0': {'2': ['Hint 2', 1]}})) UserStateSummaryFactory.create(
field_name='mod_queue',
UserStateSummaryFactory.create(field_name='hint_pk', usage_id=self.problem_id,
usage_id=self.problem_id, value=json.dumps({'2.0': {'2': ['Hint 2', 1]}})
value=5) )
UserStateSummaryFactory.create(
field_name='hint_pk',
usage_id=self.problem_id,
value=5
)
# Mock out location_to_problem_name, which ordinarily accesses the modulestore. # Mock out location_to_problem_name, which ordinarily accesses the modulestore.
# (I can't figure out how to get fake structures into the modulestore.) # (I can't figure out how to get fake structures into the modulestore.)
view.location_to_problem_name = lambda course_id, loc: "Test problem" view.location_to_problem_name = lambda course_id, loc: "Test problem"
......
...@@ -226,13 +226,13 @@ def _section_course_info(course, access): ...@@ -226,13 +226,13 @@ def _section_course_info(course, access):
try: try:
advance = lambda memo, (letter, score): "{}: {}, ".format(letter, score) + memo advance = lambda memo, (letter, score): "{}: {}, ".format(letter, score) + memo
section_data['grade_cutoffs'] = reduce(advance, course.grade_cutoffs.items(), "")[:-2] section_data['grade_cutoffs'] = reduce(advance, course.grade_cutoffs.items(), "")[:-2]
except Exception: except Exception: # pylint: disable=broad-except
section_data['grade_cutoffs'] = "Not Available" section_data['grade_cutoffs'] = "Not Available"
# section_data['offline_grades'] = offline_grades_available(course_key) # section_data['offline_grades'] = offline_grades_available(course_key)
try: try:
section_data['course_errors'] = [(escape(a), '') for (a, _unused) in modulestore().get_course_errors(course.id)] section_data['course_errors'] = [(escape(a), '') for (a, _unused) in modulestore().get_course_errors(course.id)]
except Exception: except Exception: # pylint: disable=broad-except
section_data['course_errors'] = [('Error fetching errors', '')] section_data['course_errors'] = [('Error fetching errors', '')]
return section_data return section_data
......
...@@ -78,12 +78,11 @@ BULK_EMAIL_FAILURE_ERRORS = ( ...@@ -78,12 +78,11 @@ BULK_EMAIL_FAILURE_ERRORS = (
SMTPException, SMTPException,
) )
MAX_ATTEMPTS = 10 MAX_ATTEMPTS = 10
log = logging.getLogger("linkedin") log = logging.getLogger("linkedin")
class Command(BaseCommand): class Command(BaseCommand):
""" """
Django command for inviting users to add their course certificates to their Django command for inviting users to add their course certificates to their
...@@ -152,7 +151,6 @@ class Command(BaseCommand): ...@@ -152,7 +151,6 @@ class Command(BaseCommand):
transaction.commit() transaction.commit()
def certificate_url(self, certificate): def certificate_url(self, certificate):
""" """
Generates a certificate URL based on LinkedIn's documentation. The Generates a certificate URL based on LinkedIn's documentation. The
...@@ -230,7 +228,7 @@ class Command(BaseCommand): ...@@ -230,7 +228,7 @@ class Command(BaseCommand):
while i <= num_attempts: while i <= num_attempts:
try: try:
msg.send() msg.send()
return True # Happy path! return True # Happy path!
except SINGLE_EMAIL_FAILURE_ERRORS: except SINGLE_EMAIL_FAILURE_ERRORS:
# Something unrecoverable is wrong about the email acct we're sending to # Something unrecoverable is wrong about the email acct we're sending to
log.exception( log.exception(
......
...@@ -79,7 +79,7 @@ class UserCourseEnrollmentsList(generics.ListAPIView): ...@@ -79,7 +79,7 @@ class UserCourseEnrollmentsList(generics.ListAPIView):
certified. certified.
* is_active: Whether the course is currently active; true or false. * is_active: Whether the course is currently active; true or false.
* course: A collection of data about the course: * course: A collection of data about the course:
* course_about: The URI to get the data for the course About page. * course_about: The URI to get the data for the course About page.
* course_updates: The URI to get data for course updates. * course_updates: The URI to get data for course updates.
* number: The course number. * number: The course number.
......
...@@ -104,7 +104,7 @@ class VideoTranscripts(generics.RetrieveAPIView): ...@@ -104,7 +104,7 @@ class VideoTranscripts(generics.RetrieveAPIView):
**Example request**: **Example request**:
GET /api/mobile/v0.5/video_outlines/transcripts/{organization}/{course_number}/{course_run}/{video ID}/{language code} GET /api/mobile/v0.5/video_outlines/transcripts/{organization}/{course_number}/{course_run}/{video ID}/{language code}
**Response Values** **Response Values**
An HttpResponse with an SRT file download. An HttpResponse with an SRT file download.
......
...@@ -68,35 +68,35 @@ class StudentProblemListMockQuery(object): ...@@ -68,35 +68,35 @@ class StudentProblemListMockQuery(object):
@returns: grading status message dictionary. @returns: grading status message dictionary.
""" """
return { return {
"version": 1, "version": 1,
"problem_list": [ "problem_list": [
{ {
"problem_name": "Test1", "problem_name": "Test1",
"grader_type": "IN", "grader_type": "IN",
"eta_available": True, "eta_available": True,
"state": "Finished", "state": "Finished",
"eta": 259200, "eta": 259200,
"location": "i4x://edX/open_ended/combinedopenended/SampleQuestion1Attempt" "location": "i4x://edX/open_ended/combinedopenended/SampleQuestion1Attempt"
}, },
{ {
"problem_name": "Test2", "problem_name": "Test2",
"grader_type": "NA", "grader_type": "NA",
"eta_available": True, "eta_available": True,
"state": "Waiting to be Graded", "state": "Waiting to be Graded",
"eta": 259200, "eta": 259200,
"location": "i4x://edX/open_ended/combinedopenended/SampleQuestion" "location": "i4x://edX/open_ended/combinedopenended/SampleQuestion"
}, },
{ {
"problem_name": "Test3", "problem_name": "Test3",
"grader_type": "PE", "grader_type": "PE",
"eta_available": True, "eta_available": True,
"state": "Waiting to be Graded", "state": "Waiting to be Graded",
"eta": 259200, "eta": 259200,
"location": "i4x://edX/open_ended/combinedopenended/SampleQuestion454" "location": "i4x://edX/open_ended/combinedopenended/SampleQuestion454"
}, },
], ],
"success": True "success": True
} }
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
...@@ -273,7 +273,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -273,7 +273,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.location_string = self.course_id.make_usage_key('html', 'TestLocation').to_deprecated_string() self.location_string = self.course_id.make_usage_key('html', 'TestLocation').to_deprecated_string()
self.toy = modulestore().get_course(self.course_id) self.toy = modulestore().get_course(self.course_id)
location = "i4x://edX/toy/peergrading/init" location = "i4x://edX/toy/peergrading/init"
field_data = DictFieldData({'data': "<peergrading/>", 'location': location, 'category':'peergrading'}) field_data = DictFieldData({'data': "<peergrading/>", 'location': location, 'category': 'peergrading'})
self.mock_service = peer_grading_service.MockPeerGradingService() self.mock_service = peer_grading_service.MockPeerGradingService()
self.system = LmsModuleSystem( self.system = LmsModuleSystem(
static_url=settings.STATIC_URL, static_url=settings.STATIC_URL,
...@@ -321,7 +321,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -321,7 +321,7 @@ class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
'feedback': 'feedback', 'feedback': 'feedback',
'submission_flagged': 'false', 'submission_flagged': 'false',
'answer_unknown': 'false', 'answer_unknown': 'false',
'rubric_scores_complete' : 'true' 'rubric_scores_complete': 'true'
} }
qdict = MagicMock() qdict = MagicMock()
......
...@@ -61,6 +61,7 @@ ALERT_DICT = { ...@@ -61,6 +61,7 @@ ALERT_DICT = {
'Flagged Submissions': _("Submissions have been flagged for review"), 'Flagged Submissions': _("Submissions have been flagged for review"),
} }
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
def staff_grading(request, course_id): def staff_grading(request, course_id):
""" """
...@@ -133,6 +134,7 @@ def peer_grading(request, course_id): ...@@ -133,6 +134,7 @@ def peer_grading(request, course_id):
return HttpResponseRedirect(problem_url) return HttpResponseRedirect(problem_url)
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
def student_problem_list(request, course_id): def student_problem_list(request, course_id):
""" """
...@@ -175,10 +177,11 @@ def student_problem_list(request, course_id): ...@@ -175,10 +177,11 @@ def student_problem_list(request, course_id):
'error_text': error_text, 'error_text': error_text,
# Checked above # Checked above
'staff_access': False, 'staff_access': False,
} }
return render_to_response('open_ended_problems/open_ended_problems.html', context) return render_to_response('open_ended_problems/open_ended_problems.html', context)
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
def flagged_problem_list(request, course_id): def flagged_problem_list(request, course_id):
''' '''
...@@ -336,4 +339,4 @@ def take_action_on_flags(request, course_id): ...@@ -336,4 +339,4 @@ def take_action_on_flags(request, course_id):
'success': False, 'success': False,
'error': STAFF_ERROR_MESSAGE 'error': STAFF_ERROR_MESSAGE
} }
return HttpResponse(json.dumps(response),mimetype="application/json") return HttpResponse(json.dumps(response), mimetype="application/json")
...@@ -242,15 +242,16 @@ def get_processor_decline_html(params): ...@@ -242,15 +242,16 @@ def get_processor_decline_html(params):
payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL) payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)
msg = dedent(_( msg = dedent(_(
""" """
<p class="error_msg"> <p class="error_msg">
Sorry! Our payment processor did not accept your payment. Sorry! Our payment processor did not accept your payment.
The decision they returned was <span class="decision">{decision}</span>, The decision they returned was <span class="decision">{decision}</span>,
and the reason was <span class="reason">{reason_code}:{reason_msg}</span>. and the reason was <span class="reason">{reason_code}:{reason_msg}</span>.
You were not charged. Please try a different form of payment. You were not charged. Please try a different form of payment.
Contact us with payment-related questions at {email}. Contact us with payment-related questions at {email}.
</p> </p>
""")) """
))
return msg.format( return msg.format(
decision=params['decision'], decision=params['decision'],
...@@ -266,36 +267,39 @@ def get_processor_exception_html(exception): ...@@ -266,36 +267,39 @@ def get_processor_exception_html(exception):
payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL) payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)
if isinstance(exception, CCProcessorDataException): if isinstance(exception, CCProcessorDataException):
msg = dedent(_( msg = dedent(_(
""" """
<p class="error_msg"> <p class="error_msg">
Sorry! Our payment processor sent us back a payment confirmation that had inconsistent data! Sorry! Our payment processor sent us back a payment confirmation that had inconsistent data!
We apologize that we cannot verify whether the charge went through and take further action on your order. We apologize that we cannot verify whether the charge went through and take further action on your order.
The specific error message is: <span class="exception_msg">{msg}</span>. The specific error message is: <span class="exception_msg">{msg}</span>.
Your credit card may possibly have been charged. Contact us with payment-specific questions at {email}. Your credit card may possibly have been charged. Contact us with payment-specific questions at {email}.
</p> </p>
""".format(msg=exception.message, email=payment_support_email))) """.format(msg=exception.message, email=payment_support_email)
))
return msg return msg
elif isinstance(exception, CCProcessorWrongAmountException): elif isinstance(exception, CCProcessorWrongAmountException):
msg = dedent(_( msg = dedent(_(
""" """
<p class="error_msg"> <p class="error_msg">
Sorry! Due to an error your purchase was charged for a different amount than the order total! Sorry! Due to an error your purchase was charged for a different amount than the order total!
The specific error message is: <span class="exception_msg">{msg}</span>. The specific error message is: <span class="exception_msg">{msg}</span>.
Your credit card has probably been charged. Contact us with payment-specific questions at {email}. Your credit card has probably been charged. Contact us with payment-specific questions at {email}.
</p> </p>
""".format(msg=exception.message, email=payment_support_email))) """.format(msg=exception.message, email=payment_support_email)
))
return msg return msg
elif isinstance(exception, CCProcessorSignatureException): elif isinstance(exception, CCProcessorSignatureException):
msg = dedent(_( msg = dedent(_(
""" """
<p class="error_msg"> <p class="error_msg">
Sorry! Our payment processor sent us back a corrupted message regarding your charge, so we are Sorry! Our payment processor sent us back a corrupted message regarding your charge, so we are
unable to validate that the message actually came from the payment processor. unable to validate that the message actually came from the payment processor.
The specific error message is: <span class="exception_msg">{msg}</span>. The specific error message is: <span class="exception_msg">{msg}</span>.
We apologize that we cannot verify whether the charge went through and take further action on your order. We apologize that we cannot verify whether the charge went through and take further action on your order.
Your credit card may possibly have been charged. Contact us with payment-specific questions at {email}. Your credit card may possibly have been charged. Contact us with payment-specific questions at {email}.
</p> </p>
""".format(msg=exception.message, email=payment_support_email))) """.format(msg=exception.message, email=payment_support_email)
))
return msg return msg
# fallthrough case, which basically never happens # fallthrough case, which basically never happens
......
...@@ -49,6 +49,7 @@ def mock_render_purchase_form_html(*args, **kwargs): ...@@ -49,6 +49,7 @@ def mock_render_purchase_form_html(*args, **kwargs):
form_mock = Mock(side_effect=mock_render_purchase_form_html) form_mock = Mock(side_effect=mock_render_purchase_form_html)
def mock_render_to_response(*args, **kwargs): def mock_render_to_response(*args, **kwargs):
return render_to_response(*args, **kwargs) return render_to_response(*args, **kwargs)
...@@ -633,7 +634,6 @@ class ShoppingCartViewsTests(ModuleStoreTestCase): ...@@ -633,7 +634,6 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertTrue(PaidCourseRegistration.contained_in_order(self.cart, self.course_key)) self.assertTrue(PaidCourseRegistration.contained_in_order(self.cart, self.course_key))
@patch('shoppingcart.views.render_purchase_form_html', form_mock) @patch('shoppingcart.views.render_purchase_form_html', form_mock)
@patch('shoppingcart.views.render_to_response', render_mock) @patch('shoppingcart.views.render_to_response', render_mock)
def test_show_cart(self): def test_show_cart(self):
...@@ -911,12 +911,10 @@ class ShoppingCartViewsTests(ModuleStoreTestCase): ...@@ -911,12 +911,10 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.assertIn('FirstNameTesting123', resp.content) self.assertIn('FirstNameTesting123', resp.content)
self.assertIn('80.00', resp.content) self.assertIn('80.00', resp.content)
((template, context), _) = render_mock.call_args ((template, context), _) = render_mock.call_args
# When we come from the upgrade flow, we get these context variables # When we come from the upgrade flow, we get these context variables
self.assertEqual(template, 'shoppingcart/receipt.html') self.assertEqual(template, 'shoppingcart/receipt.html')
self.assertEqual(context['order'], self.cart) self.assertEqual(context['order'], self.cart)
self.assertIn(reg_item, context['shoppingcart_items'][0]) self.assertIn(reg_item, context['shoppingcart_items'][0])
...@@ -1167,7 +1165,6 @@ class DonationViewTest(ModuleStoreTestCase): ...@@ -1167,7 +1165,6 @@ class DonationViewTest(ModuleStoreTestCase):
) )
self.assertEqual(response.status_code, 405) self.assertEqual(response.status_code, 405)
def test_donations_disabled(self): def test_donations_disabled(self):
config = DonationConfiguration.current() config = DonationConfiguration.current()
config.enabled = False config.enabled = False
......
...@@ -253,7 +253,6 @@ class StudentAccountViewTest(UrlResetMixin, TestCase): ...@@ -253,7 +253,6 @@ class StudentAccountViewTest(UrlResetMixin, TestCase):
result = self.client.login(username=self.USERNAME, password=self.NEW_PASSWORD) result = self.client.login(username=self.USERNAME, password=self.NEW_PASSWORD)
self.assertTrue(result) self.assertTrue(result)
@ddt.data(True, False) @ddt.data(True, False)
def test_password_change_logged_out(self, send_email): def test_password_change_logged_out(self, send_email):
# Log the user out # Log the user out
...@@ -277,7 +276,7 @@ class StudentAccountViewTest(UrlResetMixin, TestCase): ...@@ -277,7 +276,7 @@ class StudentAccountViewTest(UrlResetMixin, TestCase):
# Create a second user, but do not activate it # Create a second user, but do not activate it
account_api.create_account(self.ALTERNATE_USERNAME, self.OLD_PASSWORD, self.NEW_EMAIL) account_api.create_account(self.ALTERNATE_USERNAME, self.OLD_PASSWORD, self.NEW_EMAIL)
# Send the view the email address tied to the inactive user # Send the view the email address tied to the inactive user
response = self._change_password(email=self.NEW_EMAIL) response = self._change_password(email=self.NEW_EMAIL)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
...@@ -285,7 +284,7 @@ class StudentAccountViewTest(UrlResetMixin, TestCase): ...@@ -285,7 +284,7 @@ class StudentAccountViewTest(UrlResetMixin, TestCase):
def test_password_change_no_user(self): def test_password_change_no_user(self):
# Log out the user created during test setup # Log out the user created during test setup
self.client.logout() self.client.logout()
# Send the view an email address not tied to any user # Send the view an email address not tied to any user
response = self._change_password(email=self.NEW_EMAIL) response = self._change_password(email=self.NEW_EMAIL)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
...@@ -299,7 +298,7 @@ class StudentAccountViewTest(UrlResetMixin, TestCase): ...@@ -299,7 +298,7 @@ class StudentAccountViewTest(UrlResetMixin, TestCase):
# Make many consecutive bad requests in an attempt to trigger the rate limiter # Make many consecutive bad requests in an attempt to trigger the rate limiter
for attempt in xrange(self.INVALID_ATTEMPTS): for attempt in xrange(self.INVALID_ATTEMPTS):
self._change_password(email=self.NEW_EMAIL) self._change_password(email=self.NEW_EMAIL)
response = self._change_password(email=self.NEW_EMAIL) response = self._change_password(email=self.NEW_EMAIL)
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
......
...@@ -205,4 +205,3 @@ def body_string(body_dict, prefix=""): ...@@ -205,4 +205,3 @@ def body_string(body_dict, prefix=""):
body_list.append(u"{}{}:{}\n".format(prefix, key, value).encode('utf-8')) body_list.append(u"{}{}:{}\n".format(prefix, key, value).encode('utf-8'))
return "".join(body_list) # Note that trailing \n's are important return "".join(body_list) # Note that trailing \n's are important
...@@ -75,6 +75,7 @@ class MockS3Connection(object): ...@@ -75,6 +75,7 @@ class MockS3Connection(object):
def get_bucket(self, bucket_name): def get_bucket(self, bucket_name):
return MockBucket(bucket_name) return MockBucket(bucket_name)
def mock_software_secure_post(url, headers=None, data=None, **kwargs): def mock_software_secure_post(url, headers=None, data=None, **kwargs):
""" """
Mocks our interface when we post to Software Secure. Does basic assertions Mocks our interface when we post to Software Secure. Does basic assertions
...@@ -103,6 +104,7 @@ def mock_software_secure_post(url, headers=None, data=None, **kwargs): ...@@ -103,6 +104,7 @@ def mock_software_secure_post(url, headers=None, data=None, **kwargs):
return response return response
def mock_software_secure_post_error(url, headers=None, data=None, **kwargs): def mock_software_secure_post_error(url, headers=None, data=None, **kwargs):
""" """
Simulates what happens if our post to Software Secure is rejected, for Simulates what happens if our post to Software Secure is rejected, for
...@@ -112,6 +114,7 @@ def mock_software_secure_post_error(url, headers=None, data=None, **kwargs): ...@@ -112,6 +114,7 @@ def mock_software_secure_post_error(url, headers=None, data=None, **kwargs):
response.status_code = 400 response.status_code = 400
return response return response
def mock_software_secure_post_unavailable(url, headers=None, data=None, **kwargs): def mock_software_secure_post_unavailable(url, headers=None, data=None, **kwargs):
"""Simulates a connection failure when we try to submit to Software Secure.""" """Simulates a connection failure when we try to submit to Software Secure."""
raise requests.exceptions.ConnectionError raise requests.exceptions.ConnectionError
...@@ -188,7 +191,7 @@ class TestPhotoVerification(TestCase): ...@@ -188,7 +191,7 @@ class TestPhotoVerification(TestCase):
was when you submitted it. was when you submitted it.
""" """
user = UserFactory.create() user = UserFactory.create()
user.profile.name = u"Jack \u01B4" # gratuious non-ASCII char to test encodings user.profile.name = u"Jack \u01B4" # gratuious non-ASCII char to test encodings
attempt = SoftwareSecurePhotoVerification(user=user) attempt = SoftwareSecurePhotoVerification(user=user)
user.profile.name = u"Clyde \u01B4" user.profile.name = u"Clyde \u01B4"
......
...@@ -46,6 +46,7 @@ EVENT_NAME_USER_ENTERED_MIDCOURSE_REVERIFY_VIEW = 'edx.course.enrollment.reverif ...@@ -46,6 +46,7 @@ EVENT_NAME_USER_ENTERED_MIDCOURSE_REVERIFY_VIEW = 'edx.course.enrollment.reverif
EVENT_NAME_USER_SUBMITTED_MIDCOURSE_REVERIFY = 'edx.course.enrollment.reverify.submitted' EVENT_NAME_USER_SUBMITTED_MIDCOURSE_REVERIFY = 'edx.course.enrollment.reverify.submitted'
EVENT_NAME_USER_REVERIFICATION_REVIEWED_BY_SOFTWARESECURE = 'edx.course.enrollment.reverify.reviewed' EVENT_NAME_USER_REVERIFICATION_REVIEWED_BY_SOFTWARESECURE = 'edx.course.enrollment.reverify.reviewed'
class VerifyView(View): class VerifyView(View):
@method_decorator(login_required) @method_decorator(login_required)
...@@ -134,7 +135,6 @@ class VerifiedView(View): ...@@ -134,7 +135,6 @@ class VerifiedView(View):
if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True): if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True):
return redirect(reverse('dashboard')) return redirect(reverse('dashboard'))
modes_dict = CourseMode.modes_for_course_dict(course_id) modes_dict = CourseMode.modes_for_course_dict(course_id)
# we prefer professional over verify # we prefer professional over verify
...@@ -335,8 +335,10 @@ def show_requirements(request, course_id): ...@@ -335,8 +335,10 @@ def show_requirements(request, course_id):
return redirect(reverse('dashboard')) return redirect(reverse('dashboard'))
if SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user): if SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user):
return redirect( return redirect(
reverse('verify_student_verified', reverse(
kwargs={'course_id': course_id.to_deprecated_string()}) + "?upgrade={}".format(upgrade) 'verify_student_verified',
kwargs={'course_id': course_id.to_deprecated_string()}
) + "?upgrade={}".format(upgrade)
) )
upgrade = request.GET.get('upgrade', False) upgrade = request.GET.get('upgrade', False)
...@@ -523,7 +525,6 @@ def toggle_failed_banner_off(request): ...@@ -523,7 +525,6 @@ def toggle_failed_banner_off(request):
return HttpResponse('Success') return HttpResponse('Success')
@login_required @login_required
def reverification_submission_confirmation(_request): def reverification_submission_confirmation(_request):
""" """
......
...@@ -6,7 +6,7 @@ Toolbar. I it suitable to run against acceptance tests. ...@@ -6,7 +6,7 @@ Toolbar. I it suitable to run against acceptance tests.
# We intentionally define lots of variables that aren't used, and # We intentionally define lots of variables that aren't used, and
# want to import all variables from base settings files # want to import all variables from base settings files
# pylint: disable=W0401, W0614 # pylint: disable=wildcard-import, unused-wildcard-import, undefined-variable
from .dev import * from .dev import *
...@@ -15,7 +15,7 @@ from .dev import * ...@@ -15,7 +15,7 @@ from .dev import *
INSTALLED_APPS = tuple(e for e in INSTALLED_APPS if e != 'debug_toolbar') INSTALLED_APPS = tuple(e for e in INSTALLED_APPS if e != 'debug_toolbar')
INSTALLED_APPS = tuple(e for e in INSTALLED_APPS if e != 'debug_toolbar_mongo') INSTALLED_APPS = tuple(e for e in INSTALLED_APPS if e != 'debug_toolbar_mongo')
MIDDLEWARE_CLASSES = tuple(e for e in MIDDLEWARE_CLASSES \ MIDDLEWARE_CLASSES = tuple(e for e in MIDDLEWARE_CLASSES # pylint: disable=used-before-assignment
if e != 'debug_toolbar.middleware.DebugToolbarMiddleware') if e != 'debug_toolbar.middleware.DebugToolbarMiddleware')
......
...@@ -33,12 +33,12 @@ CONTENTSTORE = { ...@@ -33,12 +33,12 @@ CONTENTSTORE = {
INSTALLED_APPS += ( INSTALLED_APPS += (
# Mongo perf stats # Mongo perf stats
'debug_toolbar_mongo', 'debug_toolbar_mongo',
) )
DEBUG_TOOLBAR_PANELS += ( DEBUG_TOOLBAR_PANELS += (
'debug_toolbar_mongo.panel.MongoDebugPanel', 'debug_toolbar_mongo.panel.MongoDebugPanel',
) )
# HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS defines, as dictionary of regex's, a set of mappings of HTTP request hostnames to # HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS defines, as dictionary of regex's, a set of mappings of HTTP request hostnames to
# what the 'default' modulestore to use while processing the request # what the 'default' modulestore to use while processing the request
......
...@@ -1237,7 +1237,7 @@ STATICFILES_IGNORE_PATTERNS = ( ...@@ -1237,7 +1237,7 @@ STATICFILES_IGNORE_PATTERNS = (
"common_static", "common_static",
) )
PIPELINE_UGLIFYJS_BINARY='node_modules/.bin/uglifyjs' PIPELINE_UGLIFYJS_BINARY = 'node_modules/.bin/uglifyjs'
# Setting that will only affect the edX version of django-pipeline until our changes are merged upstream # Setting that will only affect the edX version of django-pipeline until our changes are merged upstream
PIPELINE_COMPILE_INPLACE = True PIPELINE_COMPILE_INPLACE = True
......
...@@ -19,18 +19,18 @@ INSTALLED_APPS += ('debug_toolbar',) ...@@ -19,18 +19,18 @@ INSTALLED_APPS += ('debug_toolbar',)
MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',) MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
DEBUG_TOOLBAR_PANELS = ( DEBUG_TOOLBAR_PANELS = (
'debug_toolbar.panels.version.VersionDebugPanel', 'debug_toolbar.panels.version.VersionDebugPanel',
'debug_toolbar.panels.timer.TimerDebugPanel', 'debug_toolbar.panels.timer.TimerDebugPanel',
'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
'debug_toolbar.panels.headers.HeaderDebugPanel', 'debug_toolbar.panels.headers.HeaderDebugPanel',
'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
'debug_toolbar.panels.sql.SQLDebugPanel', 'debug_toolbar.panels.sql.SQLDebugPanel',
'debug_toolbar.panels.signals.SignalDebugPanel', 'debug_toolbar.panels.signals.SignalDebugPanel',
'debug_toolbar.panels.logger.LoggingPanel', 'debug_toolbar.panels.logger.LoggingPanel',
# Enabling the profiler has a weird bug as of django-debug-toolbar==0.9.4 and # Enabling the profiler has a weird bug as of django-debug-toolbar==0.9.4 and
# Django=1.3.1/1.4 where requests to views get duplicated (your method gets # Django=1.3.1/1.4 where requests to views get duplicated (your method gets
# hit twice). So you can uncomment when you need to diagnose performance # hit twice). So you can uncomment when you need to diagnose performance
# problems, but you shouldn't leave it on. # problems, but you shouldn't leave it on.
# 'debug_toolbar.panels.profiling.ProfilingDebugPanel', # 'debug_toolbar.panels.profiling.ProfilingDebugPanel',
) )
...@@ -171,12 +171,12 @@ EDX_PLATFORM_VERSION_STRING = os.popen('cd %s; git describe' % REPO_ROOT).read() ...@@ -171,12 +171,12 @@ EDX_PLATFORM_VERSION_STRING = os.popen('cd %s; git describe' % REPO_ROOT).read()
############################ Open ended grading config ##################### ############################ Open ended grading config #####################
OPEN_ENDED_GRADING_INTERFACE = { OPEN_ENDED_GRADING_INTERFACE = {
'url' : 'http://127.0.0.1:3033/', 'url': 'http://127.0.0.1:3033/',
'username' : 'lms', 'username': 'lms',
'password' : 'abcd', 'password': 'abcd',
'staff_grading' : 'staff_grading', 'staff_grading': 'staff_grading',
'peer_grading' : 'peer_grading', 'peer_grading': 'peer_grading',
'grading_controller' : 'grading_controller' 'grading_controller': 'grading_controller'
} }
############################## LMS Migration ################################## ############################## LMS Migration ##################################
...@@ -230,20 +230,20 @@ MIDDLEWARE_CLASSES += ( ...@@ -230,20 +230,20 @@ MIDDLEWARE_CLASSES += (
INTERNAL_IPS = ('127.0.0.1',) INTERNAL_IPS = ('127.0.0.1',)
DEBUG_TOOLBAR_PANELS = ( DEBUG_TOOLBAR_PANELS = (
'debug_toolbar.panels.version.VersionDebugPanel', 'debug_toolbar.panels.version.VersionDebugPanel',
'debug_toolbar.panels.timer.TimerDebugPanel', 'debug_toolbar.panels.timer.TimerDebugPanel',
'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
'debug_toolbar.panels.headers.HeaderDebugPanel', 'debug_toolbar.panels.headers.HeaderDebugPanel',
'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
'debug_toolbar.panels.sql.SQLDebugPanel', 'debug_toolbar.panels.sql.SQLDebugPanel',
'debug_toolbar.panels.signals.SignalDebugPanel', 'debug_toolbar.panels.signals.SignalDebugPanel',
'debug_toolbar.panels.logger.LoggingPanel', 'debug_toolbar.panels.logger.LoggingPanel',
# Enabling the profiler has a weird bug as of django-debug-toolbar==0.9.4 and # Enabling the profiler has a weird bug as of django-debug-toolbar==0.9.4 and
# Django=1.3.1/1.4 where requests to views get duplicated (your method gets # Django=1.3.1/1.4 where requests to views get duplicated (your method gets
# hit twice). So you can uncomment when you need to diagnose performance # hit twice). So you can uncomment when you need to diagnose performance
# problems, but you shouldn't leave it on. # problems, but you shouldn't leave it on.
# 'debug_toolbar.panels.profiling.ProfilingDebugPanel', # 'debug_toolbar.panels.profiling.ProfilingDebugPanel',
) )
DEBUG_TOOLBAR_CONFIG = { DEBUG_TOOLBAR_CONFIG = {
......
...@@ -20,12 +20,12 @@ FEATURES['SUBDOMAIN_COURSE_LISTINGS'] = True ...@@ -20,12 +20,12 @@ FEATURES['SUBDOMAIN_COURSE_LISTINGS'] = True
COURSE_LISTINGS = { COURSE_LISTINGS = {
'default': ['BerkeleyX/CS169.1x/2012_Fall', 'default': ['BerkeleyX/CS169.1x/2012_Fall',
'BerkeleyX/CS188.1x/2012_Fall', 'BerkeleyX/CS188.1x/2012_Fall',
'HarvardX/CS50x/2012', 'HarvardX/CS50x/2012',
'HarvardX/PH207x/2012_Fall', 'HarvardX/PH207x/2012_Fall',
'MITx/3.091x/2012_Fall', 'MITx/3.091x/2012_Fall',
'MITx/6.002x/2012_Fall', 'MITx/6.002x/2012_Fall',
'MITx/6.00x/2012_Fall'], 'MITx/6.00x/2012_Fall'],
'berkeley': ['BerkeleyX/CS169.1x/2012_Fall', 'berkeley': ['BerkeleyX/CS169.1x/2012_Fall',
'BerkeleyX/CS188.1x/2012_Fall'], 'BerkeleyX/CS188.1x/2012_Fall'],
...@@ -33,5 +33,5 @@ COURSE_LISTINGS = { ...@@ -33,5 +33,5 @@ COURSE_LISTINGS = {
'harvard': ['HarvardX/CS50x/2012'], 'harvard': ['HarvardX/CS50x/2012'],
'mit': ['MITx/3.091x/2012_Fall', 'mit': ['MITx/3.091x/2012_Fall',
'MITx/6.00x/2012_Fall'] 'MITx/6.00x/2012_Fall']
} }
...@@ -17,18 +17,18 @@ CLASSES_TO_DBS = { ...@@ -17,18 +17,18 @@ CLASSES_TO_DBS = {
CACHES = { CACHES = {
'default': { 'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211', 'LOCATION': '127.0.0.1:11211',
'KEY_FUNCTION': 'util.memcache.safe_key', 'KEY_FUNCTION': 'util.memcache.safe_key',
}, },
'general': { 'general': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211', 'LOCATION': '127.0.0.1:11211',
'KEY_PREFIX': 'general', 'KEY_PREFIX': 'general',
'VERSION': 5, 'VERSION': 5,
'KEY_FUNCTION': 'util.memcache.safe_key', 'KEY_FUNCTION': 'util.memcache.safe_key',
} }
} }
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
...@@ -41,8 +41,8 @@ def path_for_db(db_name): ...@@ -41,8 +41,8 @@ def path_for_db(db_name):
def course_db_for(course_id): def course_db_for(course_id):
db_name = CLASSES_TO_DBS[course_id] db_name = CLASSES_TO_DBS[course_id]
return { return {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': path_for_db(db_name) 'NAME': path_for_db(db_name)
} }
} }
...@@ -16,7 +16,7 @@ Dir structure: ...@@ -16,7 +16,7 @@ Dir structure:
# We intentionally define lots of variables that aren't used, and # We intentionally define lots of variables that aren't used, and
# want to import all variables from base settings files # want to import all variables from base settings files
# pylint: disable=W0401, W0614 # pylint: disable=wildcard-import, unused-wildcard-import
from .dev import * from .dev import *
...@@ -34,18 +34,18 @@ DATABASES = { ...@@ -34,18 +34,18 @@ DATABASES = {
} }
CACHES = { CACHES = {
'default': { 'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211', 'LOCATION': '127.0.0.1:11211',
'KEY_FUNCTION': 'util.memcache.safe_key', 'KEY_FUNCTION': 'util.memcache.safe_key',
}, },
'general': { 'general': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211', 'LOCATION': '127.0.0.1:11211',
'KEY_PREFIX': 'general', 'KEY_PREFIX': 'general',
'VERSION': 5, 'VERSION': 5,
'KEY_FUNCTION': 'util.memcache.safe_key', 'KEY_FUNCTION': 'util.memcache.safe_key',
} }
} }
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
...@@ -57,20 +57,20 @@ MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',) ...@@ -57,20 +57,20 @@ MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
INTERNAL_IPS = ('127.0.0.1',) INTERNAL_IPS = ('127.0.0.1',)
DEBUG_TOOLBAR_PANELS = ( DEBUG_TOOLBAR_PANELS = (
'debug_toolbar.panels.version.VersionDebugPanel', 'debug_toolbar.panels.version.VersionDebugPanel',
'debug_toolbar.panels.timer.TimerDebugPanel', 'debug_toolbar.panels.timer.TimerDebugPanel',
'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
'debug_toolbar.panels.headers.HeaderDebugPanel', 'debug_toolbar.panels.headers.HeaderDebugPanel',
'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
'debug_toolbar.panels.sql.SQLDebugPanel', 'debug_toolbar.panels.sql.SQLDebugPanel',
'debug_toolbar.panels.signals.SignalDebugPanel', 'debug_toolbar.panels.signals.SignalDebugPanel',
'debug_toolbar.panels.logger.LoggingPanel', 'debug_toolbar.panels.logger.LoggingPanel',
# Enabling the profiler has a weird bug as of django-debug-toolbar==0.9.4 and # Enabling the profiler has a weird bug as of django-debug-toolbar==0.9.4 and
# Django=1.3.1/1.4 where requests to views get duplicated (your method gets # Django=1.3.1/1.4 where requests to views get duplicated (your method gets
# hit twice). So you can uncomment when you need to diagnose performance # hit twice). So you can uncomment when you need to diagnose performance
# problems, but you shouldn't leave it on. # problems, but you shouldn't leave it on.
'debug_toolbar.panels.profiling.ProfilingDebugPanel', 'debug_toolbar.panels.profiling.ProfilingDebugPanel',
) )
#PIPELINE = True #PIPELINE = True
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Specific overrides to the base prod settings to make development easier. Specific overrides to the base prod settings to make development easier.
""" """
from .aws import * # pylint: disable=wildcard-import, unused-wildcard-import from .aws import * # pylint: disable=wildcard-import, unused-wildcard-import
# Don't use S3 in devstack, fall back to filesystem # Don't use S3 in devstack, fall back to filesystem
del DEFAULT_FILE_STORAGE del DEFAULT_FILE_STORAGE
......
...@@ -16,15 +16,17 @@ PIPELINE_CSS_COMPRESSOR = None ...@@ -16,15 +16,17 @@ PIPELINE_CSS_COMPRESSOR = None
PIPELINE_JS_COMPRESSOR = None PIPELINE_JS_COMPRESSOR = None
COURSE_DEFAULT = 'edx4edx' COURSE_DEFAULT = 'edx4edx'
COURSE_SETTINGS = {'edx4edx': {'number' : 'edX.01', COURSE_SETTINGS = {
'title': 'edx4edx: edX Author Course', 'edx4edx': {
'xmlpath': '/edx4edx/', 'number': 'edX.01',
'github_url': 'https://github.com/MITx/edx4edx', 'title': 'edx4edx: edX Author Course',
'active': True, 'xmlpath': '/edx4edx/',
'default_chapter': 'Introduction', 'github_url': 'https://github.com/MITx/edx4edx',
'default_section': 'edx4edx_Course', 'active': True,
}, 'default_chapter': 'Introduction',
} 'default_section': 'edx4edx_Course',
},
}
STATICFILES_DIRS = [ STATICFILES_DIRS = [
PROJECT_ROOT / "static", PROJECT_ROOT / "static",
......
...@@ -9,8 +9,9 @@ Settings for load testing. ...@@ -9,8 +9,9 @@ Settings for load testing.
from .aws import * from .aws import *
# Disable CSRF for load testing # Disable CSRF for load testing
exclude_csrf = lambda elem: not elem in \ EXCLUDE_CSRF = lambda elem: not elem in [
['django.core.context_processors.csrf', 'django.core.context_processors.csrf',
'django.middleware.csrf.CsrfViewMiddleware'] 'django.middleware.csrf.CsrfViewMiddleware'
TEMPLATE_CONTEXT_PROCESSORS = filter(exclude_csrf, TEMPLATE_CONTEXT_PROCESSORS) ]
MIDDLEWARE_CLASSES = filter(exclude_csrf, MIDDLEWARE_CLASSES) TEMPLATE_CONTEXT_PROCESSORS = filter(EXCLUDE_CSRF, TEMPLATE_CONTEXT_PROCESSORS)
MIDDLEWARE_CLASSES = filter(EXCLUDE_CSRF, MIDDLEWARE_CLASSES)
...@@ -10,11 +10,13 @@ so that we can run the lettuce acceptance tests on SauceLabs. ...@@ -10,11 +10,13 @@ so that we can run the lettuce acceptance tests on SauceLabs.
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
import os import os
PORTS = [2000, 2001, 2020, 2109, 2222, 2310, 3000, 3001, PORTS = [
3030, 3210, 3333, 4000, 4001, 4040, 4321, 4502, 4503, 2000, 2001, 2020, 2109, 2222, 2310, 3000, 3001,
5050, 5555, 5432, 6060, 6666, 6543, 7000, 7070, 7774, 3030, 3210, 3333, 4000, 4001, 4040, 4321, 4502, 4503,
7777, 8003, 8031, 8080, 8081, 8765, 8888, 5050, 5555, 5432, 6060, 6666, 6543, 7000, 7070, 7774,
9080, 9090, 9876, 9999, 49221, 55001] 7777, 8003, 8031, 8080, 8081, 8765, 8888,
9080, 9090, 9876, 9999, 49221, 55001
]
DESIRED_CAPABILITIES = { DESIRED_CAPABILITIES = {
'chrome': DesiredCapabilities.CHROME, 'chrome': DesiredCapabilities.CHROME,
......
...@@ -119,8 +119,8 @@ STATICFILES_DIRS += [ ...@@ -119,8 +119,8 @@ STATICFILES_DIRS += [
# If we don't add these settings, then Django templates that can't # If we don't add these settings, then Django templates that can't
# find pipelined assets will raise a ValueError. # find pipelined assets will raise a ValueError.
# http://stackoverflow.com/questions/12816941/unit-testing-with-django-pipeline # http://stackoverflow.com/questions/12816941/unit-testing-with-django-pipeline
STATICFILES_STORAGE='pipeline.storage.NonPackagingPipelineStorage' STATICFILES_STORAGE = 'pipeline.storage.NonPackagingPipelineStorage'
PIPELINE_ENABLED=False PIPELINE_ENABLED = False
update_module_store_settings( update_module_store_settings(
MODULESTORE, MODULESTORE,
...@@ -372,8 +372,8 @@ LINKEDIN_API['COMPANY_ID'] = '0000000' ...@@ -372,8 +372,8 @@ LINKEDIN_API['COMPANY_ID'] = '0000000'
# Setting for the testing of Software Secure Result Callback # Setting for the testing of Software Secure Result Callback
VERIFY_STUDENT["SOFTWARE_SECURE"] = { VERIFY_STUDENT["SOFTWARE_SECURE"] = {
"API_ACCESS_KEY": "BBBBBBBBBBBBBBBBBBBB", "API_ACCESS_KEY": "BBBBBBBBBBBBBBBBBBBB",
"API_SECRET_KEY": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "API_SECRET_KEY": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
} }
VIDEO_CDN_URL = { VIDEO_CDN_URL = {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import models import models
import settings import settings
class Commentable(models.Model): class Commentable(models.Model):
base_url = "{prefix}/commentables".format(prefix=settings.PREFIX) base_url = "{prefix}/commentables".format(prefix=settings.PREFIX)
......
...@@ -8,6 +8,7 @@ import settings ...@@ -8,6 +8,7 @@ import settings
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Thread(models.Model): class Thread(models.Model):
accessible_fields = [ accessible_fields = [
......
...@@ -6,12 +6,13 @@ import settings ...@@ -6,12 +6,13 @@ import settings
class User(models.Model): class User(models.Model):
accessible_fields = ['username', 'follower_ids', 'upvoted_ids', 'downvoted_ids', accessible_fields = [
'id', 'external_id', 'subscribed_user_ids', 'children', 'course_id', 'username', 'follower_ids', 'upvoted_ids', 'downvoted_ids',
'group_id', 'subscribed_thread_ids', 'subscribed_commentable_ids', 'id', 'external_id', 'subscribed_user_ids', 'children', 'course_id',
'subscribed_course_ids', 'threads_count', 'comments_count', 'group_id', 'subscribed_thread_ids', 'subscribed_commentable_ids',
'default_sort_key' 'subscribed_course_ids', 'threads_count', 'comments_count',
] 'default_sort_key'
]
updatable_fields = ['username', 'external_id', 'default_sort_key'] updatable_fields = ['username', 'external_id', 'default_sort_key']
initializable_fields = updatable_fields initializable_fields = updatable_fields
...@@ -165,5 +166,3 @@ def _url_for_user_active_threads(user_id): ...@@ -165,5 +166,3 @@ def _url_for_user_active_threads(user_id):
def _url_for_user_subscribed_threads(user_id): def _url_for_user_subscribed_threads(user_id):
return "{prefix}/users/{user_id}/subscribed_threads".format(prefix=settings.PREFIX, user_id=user_id) return "{prefix}/users/{user_id}/subscribed_threads".format(prefix=settings.PREFIX, user_id=user_id)
...@@ -24,10 +24,10 @@ class LmsBlockMixin(XBlockMixin): ...@@ -24,10 +24,10 @@ class LmsBlockMixin(XBlockMixin):
chrome = String( chrome = String(
display_name=_("Courseware Chrome"), display_name=_("Courseware Chrome"),
help=_("Enter the chrome, or navigation tools, to use for the XBlock in the LMS. Valid values are: \n" help=_("Enter the chrome, or navigation tools, to use for the XBlock in the LMS. Valid values are: \n"
"\"chromeless\" -- to not use tabs or the accordion; \n" "\"chromeless\" -- to not use tabs or the accordion; \n"
"\"tabs\" -- to use tabs only; \n" "\"tabs\" -- to use tabs only; \n"
"\"accordion\" -- to use the accordion only; or \n" "\"accordion\" -- to use the accordion only; or \n"
"\"tabs,accordion\" -- to use tabs and the accordion."), "\"tabs,accordion\" -- to use tabs and the accordion."),
scope=Scope.settings, scope=Scope.settings,
default=None, default=None,
) )
......
...@@ -35,7 +35,7 @@ def run(): ...@@ -35,7 +35,7 @@ def run():
if settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH', False): if settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH', False):
enable_third_party_auth() enable_third_party_auth()
# Initialize Segment.io analytics module. Flushes first time a message is received and # Initialize Segment.io analytics module. Flushes first time a message is received and
# every 50 messages thereafter, or if 10 seconds have passed since last flush # every 50 messages thereafter, or if 10 seconds have passed since last flush
if settings.FEATURES.get('SEGMENT_IO_LMS') and hasattr(settings, 'SEGMENT_IO_LMS_KEY'): if settings.FEATURES.get('SEGMENT_IO_LMS') and hasattr(settings, 'SEGMENT_IO_LMS_KEY'):
analytics.init(settings.SEGMENT_IO_LMS_KEY, flush_at=50) analytics.init(settings.SEGMENT_IO_LMS_KEY, flush_at=50)
......
...@@ -4,7 +4,6 @@ import mimetypes ...@@ -4,7 +4,6 @@ import mimetypes
from mock import patch from mock import patch
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from edxmako import add_lookup, LOOKUP from edxmako import add_lookup, LOOKUP
......
...@@ -122,7 +122,7 @@ favicon_path = microsite.get_value('favicon_path', settings.FAVICON_PATH) ...@@ -122,7 +122,7 @@ favicon_path = microsite.get_value('favicon_path', settings.FAVICON_PATH)
urlpatterns += (( urlpatterns += ((
r'^favicon\.ico$', r'^favicon\.ico$',
'django.views.generic.simple.redirect_to', 'django.views.generic.simple.redirect_to',
{'url': settings.STATIC_URL + favicon_path} {'url': settings.STATIC_URL + favicon_path}
),) ),)
# Semi-static views only used by edX, not by themes # Semi-static views only used by edX, not by themes
...@@ -153,7 +153,7 @@ if not settings.FEATURES["USE_CUSTOM_THEME"]: ...@@ -153,7 +153,7 @@ if not settings.FEATURES["USE_CUSTOM_THEME"]:
# Press releases # Press releases
url(r'^press/([_a-zA-Z0-9-]+)$', 'static_template_view.views.render_press_release', name='press_release'), url(r'^press/([_a-zA-Z0-9-]+)$', 'static_template_view.views.render_press_release', name='press_release'),
) )
# Only enable URLs for those marketing links actually enabled in the # Only enable URLs for those marketing links actually enabled in the
# settings. Disable URLs by marking them as None. # settings. Disable URLs by marking them as None.
......
...@@ -25,4 +25,3 @@ modulestore() ...@@ -25,4 +25,3 @@ modulestore()
# as well as any WSGI server configured to use this file. # as well as any WSGI server configured to use this file.
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
application = get_wsgi_application() application = get_wsgi_application()
...@@ -56,8 +56,8 @@ set -e ...@@ -56,8 +56,8 @@ set -e
############################################################################### ###############################################################################
# Violations thresholds for failing the build # Violations thresholds for failing the build
PYLINT_THRESHOLD=4800 PYLINT_THRESHOLD=4725
PEP8_THRESHOLD=675 PEP8_THRESHOLD=400
source $HOME/jenkins_env source $HOME/jenkins_env
......
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