Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
81ff20c0
Commit
81ff20c0
authored
Dec 01, 2016
by
Christina Roberts
Committed by
GitHub
Dec 01, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #14080 from edx/christina/delete-javascriptinput
Delete javascriptinput and javascriptresponse.
parents
6ed02f3b
a949efc6
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
0 additions
and
433 deletions
+0
-433
common/lib/capa/capa/inputtypes.py
+0
-37
common/lib/capa/capa/responsetypes.py
+0
-205
common/lib/capa/capa/templates/javascriptinput.html
+0
-11
common/lib/capa/capa/tests/response_xml_factory.py
+0
-54
common/lib/capa/capa/tests/test_inputtypes.py
+0
-45
common/lib/capa/capa/tests/test_responsetypes.py
+0
-41
common/lib/xmodule/xmodule/js/src/capa/display.js
+0
-40
No files found.
common/lib/capa/capa/inputtypes.py
View file @
81ff20c0
...
...
@@ -9,7 +9,6 @@ Module containing the problem elements which render into input objects
- textbox (aka codeinput)
- schematic
- choicegroup (aka radiogroup, checkboxgroup)
- javascriptinput
- imageinput (for clickable image)
- optioninput (for option list)
- filesubmission (upload a file)
...
...
@@ -544,42 +543,6 @@ class ChoiceGroup(InputTypeBase):
return
[
self
.
_choices_map
[
i
]
for
i
in
internal_answer
]
#-----------------------------------------------------------------------------
@registry.register
class
JavascriptInput
(
InputTypeBase
):
"""
Hidden field for javascript to communicate via; also loads the required
scripts for rendering the problem and passes data to the problem.
TODO (arjun?): document this in detail. Initial notes:
- display_class is a subclass of XProblemClassDisplay (see
xmodule/xmodule/js/src/capa/display.js),
- display_file is the js script to be in /static/js/ where display_class is defined.
"""
template
=
"javascriptinput.html"
tags
=
[
'javascriptinput'
]
@classmethod
def
get_attributes
(
cls
):
"""
Register the attributes.
"""
return
[
Attribute
(
'params'
,
None
),
Attribute
(
'problem_state'
,
None
),
Attribute
(
'display_class'
,
None
),
Attribute
(
'display_file'
,
None
),
]
def
setup
(
self
):
# Need to provide a value that JSON can parse if there is no
# student-supplied value yet.
if
self
.
value
==
""
:
self
.
value
=
'null'
#-----------------------------------------------------------------------------
...
...
common/lib/capa/capa/responsetypes.py
View file @
81ff20c0
...
...
@@ -610,210 +610,6 @@ class LoncapaResponse(object):
"""True if the response has an answer-pool transformation."""
return
hasattr
(
self
,
'_has_answerpool'
)
#-----------------------------------------------------------------------------
@registry.register
class
JavascriptResponse
(
LoncapaResponse
):
"""
This response type is used when the student's answer is graded via
Javascript using Node.js.
"""
human_name
=
_
(
'JavaScript Input'
)
tags
=
[
'javascriptresponse'
]
max_inputfields
=
1
allowed_inputfields
=
[
'javascriptinput'
]
def
setup_response
(
self
):
# Sets up generator, grader, display, and their dependencies.
self
.
parse_xml
()
self
.
compile_display_javascript
()
self
.
params
=
self
.
extract_params
()
if
self
.
generator
:
self
.
problem_state
=
self
.
generate_problem_state
()
else
:
self
.
problem_state
=
None
self
.
solution
=
None
self
.
prepare_inputfield
()
def
compile_display_javascript
(
self
):
# TODO FIXME
# arjun: removing this behavior for now (and likely forever). Keeping
# until we decide on exactly how to solve this issue. For now, files are
# manually being compiled to DATA_DIR/js/compiled.
# latestTimestamp = 0
# basepath = self.capa_system.filestore.root_path + '/js/'
# for filename in (self.display_dependencies + [self.display]):
# filepath = basepath + filename
# timestamp = os.stat(filepath).st_mtime
# if timestamp > latestTimestamp:
# latestTimestamp = timestamp
#
# h = hashlib.md5()
# h.update(self.answer_id + str(self.display_dependencies))
# compiled_filename = 'compiled/' + h.hexdigest() + '.js'
# compiled_filepath = basepath + compiled_filename
# if not os.path.exists(compiled_filepath) or os.stat(compiled_filepath).st_mtime < latestTimestamp:
# outfile = open(compiled_filepath, 'w')
# for filename in (self.display_dependencies + [self.display]):
# filepath = basepath + filename
# infile = open(filepath, 'r')
# outfile.write(infile.read())
# outfile.write(';\n')
# infile.close()
# outfile.close()
# TODO this should also be fixed when the above is fixed.
filename
=
self
.
capa_system
.
ajax_url
.
split
(
'/'
)[
-
1
]
+
'.js'
self
.
display_filename
=
'compiled/'
+
filename
def
parse_xml
(
self
):
self
.
generator_xml
=
self
.
xml
.
xpath
(
'//*[@id=$id]//generator'
,
id
=
self
.
xml
.
get
(
'id'
))[
0
]
self
.
grader_xml
=
self
.
xml
.
xpath
(
'//*[@id=$id]//grader'
,
id
=
self
.
xml
.
get
(
'id'
))[
0
]
self
.
display_xml
=
self
.
xml
.
xpath
(
'//*[@id=$id]//display'
,
id
=
self
.
xml
.
get
(
'id'
))[
0
]
self
.
xml
.
remove
(
self
.
generator_xml
)
self
.
xml
.
remove
(
self
.
grader_xml
)
self
.
xml
.
remove
(
self
.
display_xml
)
self
.
generator
=
self
.
generator_xml
.
get
(
"src"
)
self
.
grader
=
self
.
grader_xml
.
get
(
"src"
)
self
.
display
=
self
.
display_xml
.
get
(
"src"
)
if
self
.
generator_xml
.
get
(
"dependencies"
):
self
.
generator_dependencies
=
self
.
generator_xml
.
get
(
"dependencies"
)
.
split
()
else
:
self
.
generator_dependencies
=
[]
if
self
.
grader_xml
.
get
(
"dependencies"
):
self
.
grader_dependencies
=
self
.
grader_xml
.
get
(
"dependencies"
)
.
split
()
else
:
self
.
grader_dependencies
=
[]
if
self
.
display_xml
.
get
(
"dependencies"
):
self
.
display_dependencies
=
self
.
display_xml
.
get
(
"dependencies"
)
.
split
()
else
:
self
.
display_dependencies
=
[]
self
.
display_class
=
self
.
display_xml
.
get
(
"class"
)
def
get_node_env
(
self
):
js_dir
=
os
.
path
.
join
(
self
.
capa_system
.
filestore
.
root_path
,
'js'
)
tmp_env
=
os
.
environ
.
copy
()
node_path
=
self
.
capa_system
.
node_path
+
":"
+
os
.
path
.
normpath
(
js_dir
)
tmp_env
[
"NODE_PATH"
]
=
node_path
return
tmp_env
def
call_node
(
self
,
args
):
# Node.js code is un-sandboxed. If the LoncapaSystem says we aren't
# allowed to run unsafe code, then stop now.
if
not
self
.
capa_system
.
can_execute_unsafe_code
():
_
=
self
.
capa_system
.
i18n
.
ugettext
msg
=
_
(
"Execution of unsafe Javascript code is not allowed."
)
raise
LoncapaProblemError
(
msg
)
subprocess_args
=
[
"node"
]
subprocess_args
.
extend
(
args
)
return
subprocess
.
check_output
(
subprocess_args
,
env
=
self
.
get_node_env
())
def
generate_problem_state
(
self
):
generator_file
=
os
.
path
.
dirname
(
os
.
path
.
normpath
(
__file__
))
+
'/javascript_problem_generator.js'
output
=
self
.
call_node
([
generator_file
,
self
.
generator
,
json
.
dumps
(
self
.
generator_dependencies
),
json
.
dumps
(
str
(
self
.
context
[
'seed'
])),
json
.
dumps
(
self
.
params
)])
.
strip
()
return
json
.
loads
(
output
)
def
extract_params
(
self
):
params
=
{}
for
param
in
self
.
xml
.
xpath
(
'//*[@id=$id]//responseparam'
,
id
=
self
.
xml
.
get
(
'id'
)):
raw_param
=
param
.
get
(
"value"
)
params
[
param
.
get
(
"name"
)]
=
json
.
loads
(
contextualize_text
(
raw_param
,
self
.
context
))
return
params
def
prepare_inputfield
(
self
):
for
inputfield
in
self
.
xml
.
xpath
(
'//*[@id=$id]//javascriptinput'
,
id
=
self
.
xml
.
get
(
'id'
)):
escapedict
=
{
'"'
:
'"'
}
encoded_params
=
json
.
dumps
(
self
.
params
)
encoded_params
=
saxutils
.
escape
(
encoded_params
,
escapedict
)
inputfield
.
set
(
"params"
,
encoded_params
)
encoded_problem_state
=
json
.
dumps
(
self
.
problem_state
)
encoded_problem_state
=
saxutils
.
escape
(
encoded_problem_state
,
escapedict
)
inputfield
.
set
(
"problem_state"
,
encoded_problem_state
)
inputfield
.
set
(
"display_file"
,
self
.
display_filename
)
inputfield
.
set
(
"display_class"
,
self
.
display_class
)
def
get_score
(
self
,
student_answers
):
json_submission
=
student_answers
[
self
.
answer_id
]
(
all_correct
,
evaluation
,
solution
)
=
self
.
run_grader
(
json_submission
)
self
.
solution
=
solution
correctness
=
'correct'
if
all_correct
else
'incorrect'
if
all_correct
:
points
=
self
.
get_max_score
()
else
:
points
=
0
return
CorrectMap
(
self
.
answer_id
,
correctness
,
npoints
=
points
,
msg
=
evaluation
)
def
run_grader
(
self
,
submission
):
if
submission
is
None
or
submission
==
''
:
submission
=
json
.
dumps
(
None
)
grader_file
=
os
.
path
.
dirname
(
os
.
path
.
normpath
(
__file__
))
+
'/javascript_problem_grader.js'
outputs
=
self
.
call_node
([
grader_file
,
self
.
grader
,
json
.
dumps
(
self
.
grader_dependencies
),
submission
,
json
.
dumps
(
self
.
problem_state
),
json
.
dumps
(
self
.
params
)])
.
split
(
'
\n
'
)
all_correct
=
json
.
loads
(
outputs
[
0
]
.
strip
())
evaluation
=
outputs
[
1
]
.
strip
()
solution
=
outputs
[
2
]
.
strip
()
return
(
all_correct
,
evaluation
,
solution
)
def
get_answers
(
self
):
if
self
.
solution
is
None
:
(
_
,
_
,
self
.
solution
)
=
self
.
run_grader
(
None
)
return
{
self
.
answer_id
:
self
.
solution
}
#-----------------------------------------------------------------------------
@registry.register
...
...
@@ -4119,7 +3915,6 @@ __all__ = [
ChoiceResponse
,
MultipleChoiceResponse
,
TrueFalseResponse
,
JavascriptResponse
,
AnnotationResponse
,
ChoiceTextResponse
,
]
...
...
common/lib/capa/capa/templates/javascriptinput.html
deleted
100644 → 0
View file @
6ed02f3b
<
%
page
expression_filter=
"h"
/>
<div
class=
"javascriptinput capa_inputtype"
id=
"inputtype_${id}"
>
<input
type=
"hidden"
name=
"input_${id}"
id=
"input_${id}"
class=
"javascriptinput_input"
/>
<div
class=
"javascriptinput_data"
data-display_class=
"${display_class}"
data-problem_state=
"${problem_state}"
data-params=
"${params}"
data-submission=
"${value}"
data-evaluation=
"${msg}"
>
</div>
<div
class=
"script_placeholder"
data-src=
"/static/js/${display_file}"
></div>
<div
class=
"javascriptinput_container"
></div>
</div>
common/lib/capa/capa/tests/response_xml_factory.py
View file @
81ff20c0
...
...
@@ -608,60 +608,6 @@ class ImageResponseXMLFactory(ResponseXMLFactory):
return
input_element
class
JavascriptResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for producing <javascriptresponse> XML """
def
create_response_element
(
self
,
**
kwargs
):
""" Create the <javascriptresponse> element.
Uses **kwargs:
*generator_src*: Name of the JS file to generate the problem.
*grader_src*: Name of the JS file to grade the problem.
*display_class*: Name of the class used to display the problem
*display_src*: Name of the JS file used to display the problem
*param_dict*: Dictionary of parameters to pass to the JS
"""
# Get **kwargs
generator_src
=
kwargs
.
get
(
"generator_src"
,
None
)
grader_src
=
kwargs
.
get
(
"grader_src"
,
None
)
display_class
=
kwargs
.
get
(
"display_class"
,
None
)
display_src
=
kwargs
.
get
(
"display_src"
,
None
)
param_dict
=
kwargs
.
get
(
"param_dict"
,
{})
# Both display_src and display_class given,
# or neither given
assert
((
display_src
and
display_class
)
or
(
not
display_src
and
not
display_class
))
# Create the <javascriptresponse> element
response_element
=
etree
.
Element
(
"javascriptresponse"
)
if
generator_src
:
generator_element
=
etree
.
SubElement
(
response_element
,
"generator"
)
generator_element
.
set
(
"src"
,
str
(
generator_src
))
if
grader_src
:
grader_element
=
etree
.
SubElement
(
response_element
,
"grader"
)
grader_element
.
set
(
"src"
,
str
(
grader_src
))
if
display_class
and
display_src
:
display_element
=
etree
.
SubElement
(
response_element
,
"display"
)
display_element
.
set
(
"class"
,
str
(
display_class
))
display_element
.
set
(
"src"
,
str
(
display_src
))
for
(
param_name
,
param_val
)
in
param_dict
.
items
():
responseparam_element
=
etree
.
SubElement
(
response_element
,
"responseparam"
)
responseparam_element
.
set
(
"name"
,
str
(
param_name
))
responseparam_element
.
set
(
"value"
,
str
(
param_val
))
return
response_element
def
create_input_element
(
self
,
**
kwargs
):
""" Create the <javascriptinput> element """
return
etree
.
Element
(
"javascriptinput"
)
class
MultipleChoiceResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for producing <multiplechoiceresponse> XML """
...
...
common/lib/capa/capa/tests/test_inputtypes.py
View file @
81ff20c0
...
...
@@ -162,51 +162,6 @@ class ChoiceGroupTest(unittest.TestCase):
self
.
check_group
(
'checkboxgroup'
,
'checkbox'
,
'[]'
)
class
JavascriptInputTest
(
unittest
.
TestCase
):
'''
The javascript input is a pretty straightforward pass-thru, but test it anyway
'''
def
test_rendering
(
self
):
params
=
"(1,2,3)"
problem_state
=
"abc12',12&hi<there>"
display_class
=
"a_class"
display_file
=
"my_files/hi.js"
xml_str
=
"""<javascriptinput id="prob_1_2" params="{params}" problem_state="{ps}"
display_class="{dc}" display_file="{df}"/>"""
.
format
(
params
=
params
,
ps
=
quote_attr
(
problem_state
),
dc
=
display_class
,
df
=
display_file
)
element
=
etree
.
fromstring
(
xml_str
)
state
=
{
'value'
:
'3'
,
'response_data'
:
RESPONSE_DATA
}
the_input
=
lookup_tag
(
'javascriptinput'
)(
test_capa_system
(),
element
,
state
)
context
=
the_input
.
_get_render_context
()
# pylint: disable=protected-access
prob_id
=
'prob_1_2'
expected
=
{
'STATIC_URL'
:
'/dummy-static/'
,
'id'
:
prob_id
,
'status'
:
inputtypes
.
Status
(
'unanswered'
),
'msg'
:
''
,
'value'
:
'3'
,
'params'
:
params
,
'display_file'
:
display_file
,
'display_class'
:
display_class
,
'problem_state'
:
problem_state
,
'response_data'
:
RESPONSE_DATA
,
'describedby_html'
:
DESCRIBEDBY
.
format
(
status_id
=
prob_id
)
}
self
.
assertEqual
(
context
,
expected
)
class
TextLineTest
(
unittest
.
TestCase
):
'''
Check that textline inputs work, with and without math.
...
...
common/lib/capa/capa/tests/test_responsetypes.py
View file @
81ff20c0
...
...
@@ -31,7 +31,6 @@ from capa.tests.response_xml_factory import (
CustomResponseXMLFactory
,
FormulaResponseXMLFactory
,
ImageResponseXMLFactory
,
JavascriptResponseXMLFactory
,
MultipleChoiceResponseXMLFactory
,
NumericalResponseXMLFactory
,
OptionResponseXMLFactory
,
...
...
@@ -1342,46 +1341,6 @@ class ChoiceResponseTest(ResponseTest): # pylint: disable=missing-docstring
self
.
assert_grade
(
problem
,
[
'choice_1'
,
'choice_3'
],
'incorrect'
)
class
JavascriptResponseTest
(
ResponseTest
):
# pylint: disable=missing-docstring
xml_factory_class
=
JavascriptResponseXMLFactory
def
test_grade
(
self
):
# Compile coffee files into javascript used by the response
coffee_file_path
=
os
.
path
.
dirname
(
__file__
)
+
"/test_files/js/*.coffee"
os
.
system
(
"node_modules/.bin/coffee -c
%
s"
%
(
coffee_file_path
))
capa_system
=
test_capa_system
()
capa_system
.
can_execute_unsafe_code
=
lambda
:
True
problem
=
self
.
build_problem
(
capa_system
=
capa_system
,
generator_src
=
"test_problem_generator.js"
,
grader_src
=
"test_problem_grader.js"
,
display_class
=
"TestProblemDisplay"
,
display_src
=
"test_problem_display.js"
,
param_dict
=
{
'value'
:
'4'
},
)
# Test that we get graded correctly
self
.
assert_grade
(
problem
,
json
.
dumps
({
0
:
4
}),
"correct"
)
self
.
assert_grade
(
problem
,
json
.
dumps
({
0
:
5
}),
"incorrect"
)
def
test_cant_execute_javascript
(
self
):
# If the system says to disallow unsafe code execution, then making
# this problem will raise an exception.
capa_system
=
test_capa_system
()
capa_system
.
can_execute_unsafe_code
=
lambda
:
False
with
self
.
assertRaises
(
LoncapaProblemError
):
self
.
build_problem
(
capa_system
=
capa_system
,
generator_src
=
"test_problem_generator.js"
,
grader_src
=
"test_problem_grader.js"
,
display_class
=
"TestProblemDisplay"
,
display_src
=
"test_problem_display.js"
,
param_dict
=
{
'value'
:
'4'
},
)
class
NumericalResponseTest
(
ResponseTest
):
# pylint: disable=missing-docstring
xml_factory_class
=
NumericalResponseXMLFactory
...
...
common/lib/xmodule/xmodule/js/src/capa/display.js
View file @
81ff20c0
...
...
@@ -997,24 +997,6 @@
return
preprocessor
.
fn
;
}
},
javascriptinput
:
function
(
element
)
{
var
container
,
data
,
display
,
displayClass
,
evaluation
,
params
,
problemState
,
submission
,
submissionField
;
data
=
$
(
element
).
find
(
'.javascriptinput_data'
);
params
=
data
.
data
(
'params'
);
submission
=
data
.
data
(
'submission'
);
evaluation
=
data
.
data
(
'evaluation'
);
problemState
=
data
.
data
(
'problem_state'
);
displayClass
=
window
[
data
.
data
(
'display_class'
)];
if
(
evaluation
===
''
)
{
evaluation
=
null
;
}
container
=
$
(
element
).
find
(
'.javascriptinput_container'
);
submissionField
=
$
(
element
).
find
(
'.javascriptinput_input'
);
display
=
new
displayClass
(
problemState
,
submission
,
evaluation
,
container
,
submissionField
,
params
);
display
.
render
();
return
display
;
},
cminput
:
function
(
container
)
{
var
CodeMirrorEditor
,
CodeMirrorTextArea
,
element
,
id
,
linenumbers
,
mode
,
spaces
,
tabsize
;
element
=
$
(
container
).
find
(
'textarea'
);
...
...
@@ -1064,14 +1046,6 @@
}
return
results
;
},
javascriptinput
:
function
(
element
,
display
,
answers
)
{
var
answer
,
answerId
;
answerId
=
$
(
element
).
attr
(
'id'
).
split
(
'_'
)
.
slice
(
1
)
.
join
(
'_'
);
answer
=
JSON
.
parse
(
answers
[
answerId
]);
return
display
.
showAnswer
(
answer
);
},
choicetextgroup
:
function
(
element
,
display
,
answers
)
{
var
answer
,
choice
,
inputId
,
i
,
len
,
results
,
$element
;
$element
=
$
(
element
);
...
...
@@ -1172,20 +1146,6 @@
}
};
Problem
.
prototype
.
inputtypeHideAnswerMethods
=
{
choicegroup
:
function
(
element
)
{
var
$element
=
$
(
element
);
return
$element
.
find
(
'label'
).
removeClass
(
'choicegroup_correct'
);
},
javascriptinput
:
function
(
element
,
display
)
{
return
display
.
hideAnswer
();
},
choicetextgroup
:
function
(
element
)
{
var
$element
=
$
(
element
);
return
$element
.
find
(
'section[id^="forinput"]'
).
removeClass
(
'choicetextgroup_show_correct'
);
}
};
/**
* Used to keep the buttons disabled while operationCallback is running.
*
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment