Commit 290d049c by marco

Merge branch 'feature/christina/metadata-ui' of…

Merge branch 'feature/christina/metadata-ui' of https://github.com/edx/edx-platform into feature/christina/metadata-ui
parents 72e1fab1 57f8bbf6
...@@ -35,29 +35,29 @@ Feature: Problem Editor ...@@ -35,29 +35,29 @@ Feature: Problem Editor
Scenario: User can modify float input values Scenario: User can modify float input values
Given I have created a Blank Common Problem Given I have created a Blank Common Problem
And I edit and select Settings And I edit and select Settings
Then I can set the weight to 3.5 Then I can set the weight to "3.5"
And my change to weight is persisted And my change to weight is persisted
And I can revert to the default value of unset for weight And I can revert to the default value of unset for weight
Scenario: User cannot type letters in float number field Scenario: User cannot type letters in float number field
Given I have created a Blank Common Problem Given I have created a Blank Common Problem
And I edit and select Settings And I edit and select Settings
Then if I set the weight to abc, it remains unset Then if I set the weight to "abc", it remains unset
Scenario: User cannot type decimal values integer number field Scenario: User cannot type decimal values integer number field
Given I have created a Blank Common Problem Given I have created a Blank Common Problem
And I edit and select Settings And I edit and select Settings
Then if I set the max attempts to 2.34, the max attempts are persisted as 234 Then if I set the max attempts to "2.34", it displays initially as "234", and is persisted as "234"
Scenario: User cannot type out of range values in an integer number field Scenario: User cannot type out of range values in an integer number field
Given I have created a Blank Common Problem Given I have created a Blank Common Problem
And I edit and select Settings And I edit and select Settings
Then if I set the max attempts to -3, the max attempts are persisted as 1 Then if I set the max attempts to "-3", it displays initially as "-3", and is persisted as "1"
Scenario: Settings changes are not saved on Cancel Scenario: Settings changes are not saved on Cancel
Given I have created a Blank Common Problem Given I have created a Blank Common Problem
And I edit and select Settings And I edit and select Settings
Then I can set the weight to 3.5 Then I can set the weight to "3.5"
And I can modify the display name And I can modify the display name
Then If I press Cancel my changes are not persisted Then If I press Cancel my changes are not persisted
......
...@@ -94,9 +94,9 @@ def i_can_revert_to_default_for_randomization(step): ...@@ -94,9 +94,9 @@ def i_can_revert_to_default_for_randomization(step):
world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Always", False) world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Always", False)
@step('I can set the weight to 3.5') @step('I can set the weight to "(.*)"?')
def i_can_set_weight_to_3_5(step): def i_can_set_weight(step, weight):
world.get_setting_entry(PROBLEM_WEIGHT).find_by_css('.setting-input')[0].fill('3.5') set_weight(weight)
verify_modified_weight() verify_modified_weight()
...@@ -113,9 +113,9 @@ def i_can_revert_to_default_for_unset_weight(step): ...@@ -113,9 +113,9 @@ def i_can_revert_to_default_for_unset_weight(step):
world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False) world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False)
@step('if I set the weight to abc, it remains unset') @step('if I set the weight to "(.*)", it remains unset')
def set_the_weight_to_abc(step): def set_the_weight_to_abc(step, bad_weight):
world.get_setting_entry(PROBLEM_WEIGHT).find_by_css('.setting-input')[0].fill('abc') set_weight(bad_weight)
# We show the clear button immediately on type, hence the "True" here. # We show the clear button immediately on type, hence the "True" here.
world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", True) world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", True)
world.save_component_and_reopen(step) world.save_component_and_reopen(step)
...@@ -123,20 +123,12 @@ def set_the_weight_to_abc(step): ...@@ -123,20 +123,12 @@ def set_the_weight_to_abc(step):
world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False) world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False)
@step('if I set the max attempts to 2.34, the max attempts are persisted as 234') @step('if I set the max attempts to "(.*)", it displays initially as "(.*)", and is persisted as "(.*)"')
def set_the_max_attempts_234(step): def set_the_max_attempts(step, max_attempts_set, max_attempts_displayed, max_attempts_persisted):
world.get_setting_entry(MAXIMUM_ATTEMPTS).find_by_css('.setting-input')[0].fill('2.34') world.get_setting_entry(MAXIMUM_ATTEMPTS).find_by_css('.setting-input')[0].fill(max_attempts_set)
world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "234", True) world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, max_attempts_displayed, True)
world.save_component_and_reopen(step) world.save_component_and_reopen(step)
world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "234", True) world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, max_attempts_persisted, True)
@step('I set the max attempts to -3, the max attempts are persisted as 1')
def set_max_attempts_to_neg_3(step):
world.get_setting_entry(MAXIMUM_ATTEMPTS).find_by_css('.setting-input')[0].fill('-3')
world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "-3", True)
world.save_component_and_reopen(step)
world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "1", True)
@step('Edit High Level Source is not visible') @step('Edit High Level Source is not visible')
...@@ -189,3 +181,7 @@ def verify_modified_display_name_with_special_chars(): ...@@ -189,3 +181,7 @@ def verify_modified_display_name_with_special_chars():
def verify_unset_display_name(): def verify_unset_display_name():
world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, '', False) world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, '', False)
def set_weight(weight):
world.get_setting_entry(PROBLEM_WEIGHT).find_by_css('.setting-input')[0].fill(weight)
...@@ -61,6 +61,8 @@ ...@@ -61,6 +61,8 @@
<div class="wrapper wrapper-view"> <div class="wrapper wrapper-view">
<%include file="widgets/header.html" /> <%include file="widgets/header.html" />
## remove this block after advanced settings notification is rewritten
<%block name="view_alerts"></%block>
<div id="page-alert"></div> <div id="page-alert"></div>
<%block name="content"></%block> <%block name="content"></%block>
...@@ -72,9 +74,13 @@ ...@@ -72,9 +74,13 @@
<%include file="widgets/footer.html" /> <%include file="widgets/footer.html" />
<%include file="widgets/tender.html" /> <%include file="widgets/tender.html" />
## remove this block after advanced settings notification is rewritten
<%block name="view_notifications"></%block>
<div id="page-notification"></div> <div id="page-notification"></div>
</div> </div>
## remove this block after advanced settings notification is rewritten
<%block name="view_prompts"></%block>
<div id="page-prompt"></div> <div id="page-prompt"></div>
<%block name="jsextra"></%block> <%block name="jsextra"></%block>
</body> </body>
......
# .coveragerc for common/lib/calc
[run]
data_file = reports/common/lib/calc/.coverage
source = common/lib/calc
branch = true
[report]
ignore_errors = True
[html]
title = Calc Python Test Coverage Report
directory = reports/common/lib/calc/cover
[xml]
output = reports/common/lib/calc/coverage.xml
...@@ -144,6 +144,8 @@ def evaluator(variables, functions, string, cs=False): ...@@ -144,6 +144,8 @@ def evaluator(variables, functions, string, cs=False):
return x return x
def parallel(x): # Parallel resistors [ 1 2 ] => 2/3 def parallel(x): # Parallel resistors [ 1 2 ] => 2/3
# convert from pyparsing.ParseResults, which doesn't support '0 in x'
x = list(x)
if len(x) == 1: if len(x) == 1:
return x[0] return x[0]
if 0 in x: if 0 in x:
...@@ -230,27 +232,3 @@ def evaluator(variables, functions, string, cs=False): ...@@ -230,27 +232,3 @@ def evaluator(variables, functions, string, cs=False):
expr << Optional((plus | minus)) + term + ZeroOrMore((plus | minus) + term) # -5 + 4 - 3 expr << Optional((plus | minus)) + term + ZeroOrMore((plus | minus) + term) # -5 + 4 - 3
expr = expr.setParseAction(sum_parse_action) expr = expr.setParseAction(sum_parse_action)
return (expr + stringEnd).parseString(string)[0] return (expr + stringEnd).parseString(string)[0]
if __name__ == '__main__':
variables = {'R1': 2.0, 'R3': 4.0}
functions = {'sin': numpy.sin, 'cos': numpy.cos}
print "X", evaluator(variables, functions, "10000||sin(7+5)-6k")
print "X", evaluator(variables, functions, "13")
print evaluator({'R1': 2.0, 'R3': 4.0}, {}, "13")
print evaluator({'e1': 1, 'e2': 1.0, 'R3': 7, 'V0': 5, 'R5': 15, 'I1': 1, 'R4': 6}, {}, "e2")
print evaluator({'a': 2.2997471478310274, 'k': 9, 'm': 8, 'x': 0.66009498411213041}, {}, "5")
print evaluator({}, {}, "-1")
print evaluator({}, {}, "-(7+5)")
print evaluator({}, {}, "-0.33")
print evaluator({}, {}, "-.33")
print evaluator({}, {}, "5+1*j")
print evaluator({}, {}, "j||1")
print evaluator({}, {}, "e^(j*pi)")
print evaluator({}, {}, "fact(5)")
print evaluator({}, {}, "factorial(5)")
try:
print evaluator({}, {}, "5+7 QWSEKO")
except UndefinedVariable:
print "Successfully caught undefined variable"
...@@ -10,7 +10,6 @@ import random ...@@ -10,7 +10,6 @@ import random
import unittest import unittest
import textwrap import textwrap
import mock import mock
import textwrap
from . import new_loncapa_problem, test_system from . import new_loncapa_problem, test_system
...@@ -190,7 +189,7 @@ class SymbolicResponseTest(ResponseTest): ...@@ -190,7 +189,7 @@ class SymbolicResponseTest(ResponseTest):
def test_grade_single_input(self): def test_grade_single_input(self):
problem = self.build_problem(math_display=True, problem = self.build_problem(math_display=True,
expect="2*x+3*y") expect="2*x+3*y")
# Correct answers # Correct answers
correct_inputs = [ correct_inputs = [
...@@ -223,7 +222,6 @@ class SymbolicResponseTest(ResponseTest): ...@@ -223,7 +222,6 @@ class SymbolicResponseTest(ResponseTest):
for (input_str, input_mathml) in incorrect_inputs: for (input_str, input_mathml) in incorrect_inputs:
self._assert_symbolic_grade(problem, input_str, input_mathml, 'incorrect') self._assert_symbolic_grade(problem, input_str, input_mathml, 'incorrect')
def test_complex_number_grade(self): def test_complex_number_grade(self):
problem = self.build_problem(math_display=True, problem = self.build_problem(math_display=True,
expect="[[cos(theta),i*sin(theta)],[i*sin(theta),cos(theta)]]", expect="[[cos(theta),i*sin(theta)],[i*sin(theta),cos(theta)]]",
...@@ -241,7 +239,7 @@ class SymbolicResponseTest(ResponseTest): ...@@ -241,7 +239,7 @@ class SymbolicResponseTest(ResponseTest):
# Correct answer # Correct answer
with mock.patch.object(requests, 'post') as mock_post: with mock.patch.object(requests, 'post') as mock_post:
# Simulate what the LaTeX-to-MathML server would # Simulate what the LaTeX-to-MathML server would
# send for the correct response input # send for the correct response input
mock_post.return_value.text = correct_snuggletex_response mock_post.return_value.text = correct_snuggletex_response
...@@ -323,7 +321,7 @@ class SymbolicResponseTest(ResponseTest): ...@@ -323,7 +321,7 @@ class SymbolicResponseTest(ResponseTest):
dynamath_input, dynamath_input,
expected_correctness): expected_correctness):
input_dict = {'1_2_1': str(student_input), input_dict = {'1_2_1': str(student_input),
'1_2_1_dynamath': str(dynamath_input) } '1_2_1_dynamath': str(dynamath_input)}
correct_map = problem.grade_answers(input_dict) correct_map = problem.grade_answers(input_dict)
...@@ -349,10 +347,18 @@ class OptionResponseTest(ResponseTest): ...@@ -349,10 +347,18 @@ class OptionResponseTest(ResponseTest):
class FormulaResponseTest(ResponseTest): class FormulaResponseTest(ResponseTest):
"""
Test the FormulaResponse class
"""
from response_xml_factory import FormulaResponseXMLFactory from response_xml_factory import FormulaResponseXMLFactory
xml_factory_class = FormulaResponseXMLFactory xml_factory_class = FormulaResponseXMLFactory
def test_grade(self): def test_grade(self):
"""
Test basic functionality of FormulaResponse
Specifically, if it can understand equivalence of formulae
"""
# Sample variables x and y in the range [-10, 10] # Sample variables x and y in the range [-10, 10]
sample_dict = {'x': (-10, 10), 'y': (-10, 10)} sample_dict = {'x': (-10, 10), 'y': (-10, 10)}
...@@ -373,6 +379,9 @@ class FormulaResponseTest(ResponseTest): ...@@ -373,6 +379,9 @@ class FormulaResponseTest(ResponseTest):
self.assert_grade(problem, input_formula, "incorrect") self.assert_grade(problem, input_formula, "incorrect")
def test_hint(self): def test_hint(self):
"""
Test the hint-giving functionality of FormulaResponse
"""
# Sample variables x and y in the range [-10, 10] # Sample variables x and y in the range [-10, 10]
sample_dict = {'x': (-10, 10), 'y': (-10, 10)} sample_dict = {'x': (-10, 10), 'y': (-10, 10)}
...@@ -401,6 +410,10 @@ class FormulaResponseTest(ResponseTest): ...@@ -401,6 +410,10 @@ class FormulaResponseTest(ResponseTest):
'Try including the variable x') 'Try including the variable x')
def test_script(self): def test_script(self):
"""
Test if python script can be used to generate answers
"""
# Calculate the answer using a script # Calculate the answer using a script
script = "calculated_ans = 'x+x'" script = "calculated_ans = 'x+x'"
...@@ -419,7 +432,9 @@ class FormulaResponseTest(ResponseTest): ...@@ -419,7 +432,9 @@ class FormulaResponseTest(ResponseTest):
self.assert_grade(problem, '3*x', 'incorrect') self.assert_grade(problem, '3*x', 'incorrect')
def test_parallel_resistors(self): def test_parallel_resistors(self):
"""Test parallel resistors""" """
Test parallel resistors
"""
sample_dict = {'R1': (10, 10), 'R2': (2, 2), 'R3': (5, 5), 'R4': (1, 1)} sample_dict = {'R1': (10, 10), 'R2': (2, 2), 'R3': (5, 5), 'R4': (1, 1)}
# Test problem # Test problem
...@@ -440,8 +455,11 @@ class FormulaResponseTest(ResponseTest): ...@@ -440,8 +455,11 @@ class FormulaResponseTest(ResponseTest):
self.assert_grade(problem, input_formula, "incorrect") self.assert_grade(problem, input_formula, "incorrect")
def test_default_variables(self): def test_default_variables(self):
"""Test the default variables provided in common/lib/capa/capa/calc.py""" """
# which are: j (complex number), e, pi, k, c, T, q Test the default variables provided in calc.py
which are: j (complex number), e, pi, k, c, T, q
"""
# Sample x in the range [-10,10] # Sample x in the range [-10,10]
sample_dict = {'x': (-10, 10)} sample_dict = {'x': (-10, 10)}
...@@ -464,11 +482,14 @@ class FormulaResponseTest(ResponseTest): ...@@ -464,11 +482,14 @@ class FormulaResponseTest(ResponseTest):
msg="Failed on variable {0}; the given, incorrect answer was {1} but graded 'correct'".format(var, incorrect)) msg="Failed on variable {0}; the given, incorrect answer was {1} but graded 'correct'".format(var, incorrect))
def test_default_functions(self): def test_default_functions(self):
"""Test the default functions provided in common/lib/capa/capa/calc.py""" """
# which are: sin, cos, tan, sqrt, log10, log2, ln, Test the default functions provided in common/lib/capa/capa/calc.py
# arccos, arcsin, arctan, abs,
# fact, factorial
which are:
sin, cos, tan, sqrt, log10, log2, ln,
arccos, arcsin, arctan, abs,
fact, factorial
"""
w = random.randint(3, 10) w = random.randint(3, 10)
sample_dict = {'x': (-10, 10), # Sample x in the range [-10,10] sample_dict = {'x': (-10, 10), # Sample x in the range [-10,10]
'y': (1, 10), # Sample y in the range [1,10] - logs, arccos need positive inputs 'y': (1, 10), # Sample y in the range [1,10] - logs, arccos need positive inputs
...@@ -496,8 +517,10 @@ class FormulaResponseTest(ResponseTest): ...@@ -496,8 +517,10 @@ class FormulaResponseTest(ResponseTest):
msg="Failed on function {0}; the given, incorrect answer was {1} but graded 'correct'".format(func, incorrect)) msg="Failed on function {0}; the given, incorrect answer was {1} but graded 'correct'".format(func, incorrect))
def test_grade_infinity(self): def test_grade_infinity(self):
# This resolves a bug where a problem with relative tolerance would """
# pass with any arbitrarily large student answer. Test that a large input on a problem with relative tolerance isn't
erroneously marked as correct.
"""
sample_dict = {'x': (1, 2)} sample_dict = {'x': (1, 2)}
...@@ -514,8 +537,9 @@ class FormulaResponseTest(ResponseTest): ...@@ -514,8 +537,9 @@ class FormulaResponseTest(ResponseTest):
self.assert_grade(problem, input_formula, "incorrect") self.assert_grade(problem, input_formula, "incorrect")
def test_grade_nan(self): def test_grade_nan(self):
# Attempt to produce a value which causes the student's answer to be """
# evaluated to nan. See if this is resolved correctly. Test that expressions that evaluate to NaN are not marked as correct.
"""
sample_dict = {'x': (1, 2)} sample_dict = {'x': (1, 2)}
...@@ -532,6 +556,18 @@ class FormulaResponseTest(ResponseTest): ...@@ -532,6 +556,18 @@ class FormulaResponseTest(ResponseTest):
input_formula = "x + 0*1e999" input_formula = "x + 0*1e999"
self.assert_grade(problem, input_formula, "incorrect") self.assert_grade(problem, input_formula, "incorrect")
def test_raises_zero_division_err(self):
"""
See if division by zero raises an error.
"""
sample_dict = {'x': (1, 2)}
problem = self.build_problem(sample_dict=sample_dict,
num_samples=10,
tolerance="1%",
answer="x") # Answer doesn't matter
input_dict = {'1_2_1': '1/0'}
self.assertRaises(StudentInputError, problem.grade_answers, input_dict)
class StringResponseTest(ResponseTest): class StringResponseTest(ResponseTest):
from response_xml_factory import StringResponseXMLFactory from response_xml_factory import StringResponseXMLFactory
...@@ -592,7 +628,7 @@ class StringResponseTest(ResponseTest): ...@@ -592,7 +628,7 @@ class StringResponseTest(ResponseTest):
problem = self.build_problem( problem = self.build_problem(
answer="Michigan", answer="Michigan",
hintfn="gimme_a_hint", hintfn="gimme_a_hint",
script = textwrap.dedent(""" script=textwrap.dedent("""
def gimme_a_hint(answer_ids, student_answers, new_cmap, old_cmap): def gimme_a_hint(answer_ids, student_answers, new_cmap, old_cmap):
aid = answer_ids[0] aid = answer_ids[0]
answer = student_answers[aid] answer = student_answers[aid]
...@@ -898,6 +934,14 @@ class NumericalResponseTest(ResponseTest): ...@@ -898,6 +934,14 @@ class NumericalResponseTest(ResponseTest):
incorrect_responses = ["", "3.9", "4.1", "0", "5.01e1"] incorrect_responses = ["", "3.9", "4.1", "0", "5.01e1"]
self.assert_multiple_grade(problem, correct_responses, incorrect_responses) self.assert_multiple_grade(problem, correct_responses, incorrect_responses)
def test_raises_zero_division_err(self):
"""See if division by zero is handled correctly"""
problem = self.build_problem(question_text="What 5 * 10?",
explanation="The answer is 50",
answer="5e+1") # Answer doesn't matter
input_dict = {'1_2_1': '1/0'}
self.assertRaises(StudentInputError, problem.grade_answers, input_dict)
class CustomResponseTest(ResponseTest): class CustomResponseTest(ResponseTest):
from response_xml_factory import CustomResponseXMLFactory from response_xml_factory import CustomResponseXMLFactory
...@@ -947,8 +991,8 @@ class CustomResponseTest(ResponseTest): ...@@ -947,8 +991,8 @@ class CustomResponseTest(ResponseTest):
# #
# 'answer_given' is the answer the student gave (if there is just one input) # 'answer_given' is the answer the student gave (if there is just one input)
# or an ordered list of answers (if there are multiple inputs) # or an ordered list of answers (if there are multiple inputs)
# #
# The function should return a dict of the form # The function should return a dict of the form
# { 'ok': BOOL, 'msg': STRING } # { 'ok': BOOL, 'msg': STRING }
# #
script = textwrap.dedent(""" script = textwrap.dedent("""
......
...@@ -69,7 +69,7 @@ class CapaFields(object): ...@@ -69,7 +69,7 @@ class CapaFields(object):
max_attempts = StringyInteger( max_attempts = StringyInteger(
display_name="Maximum Attempts", display_name="Maximum Attempts",
help="Defines the number of times a student can try to answer this problem. If the value is not set, infinite attempts are allowed.", help="Defines the number of times a student can try to answer this problem. If the value is not set, infinite attempts are allowed.",
values = {"min" : 1 }, scope=Scope.settings values={"min": 1}, scope=Scope.settings
) )
due = Date(help="Date that this problem is due by", scope=Scope.settings) due = Date(help="Date that this problem is due by", scope=Scope.settings)
graceperiod = Timedelta(help="Amount of time after the due date that submissions will be accepted", scope=Scope.settings) graceperiod = Timedelta(help="Amount of time after the due date that submissions will be accepted", scope=Scope.settings)
...@@ -103,7 +103,7 @@ class CapaFields(object): ...@@ -103,7 +103,7 @@ class CapaFields(object):
weight = StringyFloat( weight = StringyFloat(
display_name="Problem Weight", display_name="Problem Weight",
help="Defines the number of points each problem is worth. If the value is not set, each response field in the problem is worth one point.", help="Defines the number of points each problem is worth. If the value is not set, each response field in the problem is worth one point.",
values = {"min" : 0 , "step": .1}, values={"min": 0, "step": .1},
scope=Scope.settings scope=Scope.settings
) )
markdown = String(help="Markdown source of this module", scope=Scope.settings) markdown = String(help="Markdown source of this module", scope=Scope.settings)
......
...@@ -37,14 +37,14 @@ class WordCloudFields(object): ...@@ -37,14 +37,14 @@ class WordCloudFields(object):
help="Number of text boxes available for students to input words/sentences.", help="Number of text boxes available for students to input words/sentences.",
scope=Scope.settings, scope=Scope.settings,
default=5, default=5,
values = {"min" : 1 } values={"min": 1}
) )
num_top_words = StringyInteger( num_top_words = StringyInteger(
display_name="Maximum Words", display_name="Maximum Words",
help="Maximum number of words to be displayed in generated word cloud.", help="Maximum number of words to be displayed in generated word cloud.",
scope=Scope.settings, scope=Scope.settings,
default=250, default=250,
values = {"min" : 1 } values={"min": 1}
) )
display_student_percents = StringyBoolean( display_student_percents = StringyBoolean(
display_name="Show Percents", display_name="Show Percents",
......
...@@ -76,7 +76,7 @@ class HTMLSnippet(object): ...@@ -76,7 +76,7 @@ class HTMLSnippet(object):
""" """
raise NotImplementedError( raise NotImplementedError(
"get_html() must be provided by specific modules - not present in {0}" "get_html() must be provided by specific modules - not present in {0}"
.format(self.__class__)) .format(self.__class__))
class XModuleFields(object): class XModuleFields(object):
...@@ -358,7 +358,7 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): ...@@ -358,7 +358,7 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
metadata_translations = { metadata_translations = {
'slug': 'url_name', 'slug': 'url_name',
'name': 'display_name', 'name': 'display_name',
} }
# ============================= STRUCTURAL MANIPULATION =================== # ============================= STRUCTURAL MANIPULATION ===================
def __init__(self, def __init__(self,
...@@ -460,7 +460,6 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): ...@@ -460,7 +460,6 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
""" """
return False return False
# ================================= JSON PARSING =========================== # ================================= JSON PARSING ===========================
@staticmethod @staticmethod
def load_from_json(json_data, system, default_class=None): def load_from_json(json_data, system, default_class=None):
...@@ -525,10 +524,10 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): ...@@ -525,10 +524,10 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
# ================================= XML PARSING ============================ # ================================= XML PARSING ============================
@staticmethod @staticmethod
def load_from_xml(xml_data, def load_from_xml(xml_data,
system, system,
org=None, org=None,
course=None, course=None,
default_class=None): default_class=None):
""" """
This method instantiates the correct subclass of XModuleDescriptor based This method instantiates the correct subclass of XModuleDescriptor based
on the contents of xml_data. on the contents of xml_data.
...@@ -543,7 +542,7 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): ...@@ -543,7 +542,7 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
class_ = XModuleDescriptor.load_class( class_ = XModuleDescriptor.load_class(
etree.fromstring(xml_data).tag, etree.fromstring(xml_data).tag,
default_class default_class
) )
# leave next line, commented out - useful for low-level debugging # leave next line, commented out - useful for low-level debugging
# log.debug('[XModuleDescriptor.load_from_xml] tag=%s, class_=%s' % ( # log.debug('[XModuleDescriptor.load_from_xml] tag=%s, class_=%s' % (
# etree.fromstring(xml_data).tag,class_)) # etree.fromstring(xml_data).tag,class_))
...@@ -665,11 +664,11 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): ...@@ -665,11 +664,11 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
type = "Integer" type = "Integer"
elif isinstance(field, Float): elif isinstance(field, Float):
type = "Float" type = "Float"
metadata_fields[field.name] = {'field_name' : field.name, metadata_fields[field.name] = {'field_name': field.name,
'type' : type, 'type': type,
'display_name' : field.display_name, 'display_name': field.display_name,
'value': field.to_json(value), 'value': field.to_json(value),
'options' : values, 'options': values,
'default_value': field.to_json(default_value), 'default_value': field.to_json(default_value),
'inheritable': inheritable, 'inheritable': inheritable,
'explicitly_set': explicitly_set, 'explicitly_set': explicitly_set,
...@@ -768,7 +767,7 @@ class ModuleSystem(object): ...@@ -768,7 +767,7 @@ class ModuleSystem(object):
s3_interface=None, s3_interface=None,
cache=None, cache=None,
can_execute_unsafe_code=None, can_execute_unsafe_code=None,
): ):
''' '''
Create a closure around the system environment. Create a closure around the system environment.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
34d1996e44f78168a73297217b3a0973c2ae90e1
\ No newline at end of file
...@@ -115,6 +115,11 @@ xmodule can be tested independently, with this: ...@@ -115,6 +115,11 @@ xmodule can be tested independently, with this:
rake test_common/lib/xmodule rake test_common/lib/xmodule
other module level tests include
* `rake test_common/lib/capa`
* `rake test_common/lib/calc`
To run a single django test class: To run a single django test class:
rake test_lms[courseware.tests.tests:testViewAuth] rake test_lms[courseware.tests.tests:testViewAuth]
......
b154ce99fb5c8d413ba769e8cc0df94ed674c3f4
\ No newline at end of file
2b8c58b098bdb17f9ddcbb2098f94c50fdcedf60
\ No newline at end of file
7d8b9879f7e5b859910edba7249661eedd3fcf37
\ No newline at end of file
caf8b43337faa75cef5da5cd090010215a67b1bd
\ No newline at end of file
b4d043bb1ca4a8815d4a388a2c9d96038211417b
\ No newline at end of file
6718f0c6e851376b5478baff94e1f1f4449bd938
\ No newline at end of file
...@@ -10,6 +10,9 @@ end ...@@ -10,6 +10,9 @@ end
# the ENV_TOKENS to the templating context. # the ENV_TOKENS to the templating context.
def preprocess_with_mako(filename) def preprocess_with_mako(filename)
# simple command-line invocation of Mako engine # simple command-line invocation of Mako engine
# cdodge: the .gsub() are used to translate true->True and false->False to make the generated
# python actually valid python. This is just a short term hack to unblock the release train
# until a real fix can be made by people who know this better
mako = "from mako.template import Template;" + mako = "from mako.template import Template;" +
"print Template(filename=\"#{filename}\")" + "print Template(filename=\"#{filename}\")" +
# Total hack. It works because a Python dict literal has # Total hack. It works because a Python dict literal has
......
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