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
cd42c917
Commit
cd42c917
authored
Mar 05, 2014
by
Han Su Kim
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2818 from edx/alex/allow_html_in_grader_msg
Allow HTML in grader messages.
parents
5e14d045
c7785d8f
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
116 additions
and
6 deletions
+116
-6
common/lib/capa/capa/inputtypes.py
+13
-1
common/lib/capa/capa/responsetypes.py
+11
-5
common/lib/capa/capa/tests/test_inputtypes.py
+39
-0
common/lib/capa/capa/tests/test_responsetypes.py
+53
-0
No files found.
common/lib/capa/capa/inputtypes.py
View file @
cd42c917
...
...
@@ -46,6 +46,7 @@ import re
import
shlex
# for splitting quoted strings
import
sys
import
pyparsing
import
html5lib
from
.registry
import
TagRegistry
from
chem
import
chemcalc
...
...
@@ -286,7 +287,18 @@ class InputTypeBase(object):
context
=
self
.
_get_render_context
()
html
=
self
.
capa_system
.
render_template
(
self
.
template
,
context
)
return
etree
.
XML
(
html
)
try
:
output
=
etree
.
XML
(
html
)
except
etree
.
XMLSyntaxError
as
ex
:
# If `html` contains attrs with no values, like `controls` in <audio controls src='smth'/>,
# XML parser will raise exception, so wee fallback to html5parser, which will set empty "" values for such attrs.
try
:
output
=
html5lib
.
parseFragment
(
html
,
treebuilder
=
'lxml'
,
namespaceHTMLElements
=
False
)[
0
]
except
IndexError
:
raise
ex
return
output
def
get_user_visible_answer
(
self
,
internal_answer
):
"""
...
...
common/lib/capa/capa/responsetypes.py
View file @
cd42c917
...
...
@@ -14,6 +14,7 @@ import cgi
import
inspect
import
json
import
logging
import
html5lib
import
numbers
import
numpy
import
os
...
...
@@ -1761,17 +1762,22 @@ class CodeResponse(LoncapaResponse):
" tags: 'correct', 'score', 'msg'"
)
return
fail
# Next, we need to check that the contents of the external grader message
# is safe for the LMS.
# Next, we need to check that the contents of the external grader message is safe for the LMS.
# 1) Make sure that the message is valid XML (proper opening/closing tags)
# 2) TODO: Is the message actually HTML?
# 2) If it is not valid XML, make sure it is valid HTML. Note: html5lib parser will try to repair any broken HTML
# For example: <aaa></bbb> will become <aaa/>.
msg
=
score_result
[
'msg'
]
try
:
etree
.
fromstring
(
msg
)
except
etree
.
XMLSyntaxError
as
_err
:
log
.
error
(
"Unable to parse external grader message as valid"
# If `html` contains attrs with no values, like `controls` in <audio controls src='smth'/>,
# XML parser will raise exception, so wee fallback to html5parser, which will set empty "" values for such attrs.
parsed
=
html5lib
.
parseFragment
(
msg
,
treebuilder
=
'lxml'
,
namespaceHTMLElements
=
False
)
if
not
parsed
:
log
.
error
(
"Unable to parse external grader message as valid"
" XML: score_msg['msg']=
%
s"
,
msg
)
return
fail
return
fail
return
(
True
,
score_result
[
'correct'
],
score_result
[
'score'
],
msg
)
...
...
common/lib/capa/capa/tests/test_inputtypes.py
View file @
cd42c917
...
...
@@ -20,6 +20,7 @@ TODO:
import
json
from
lxml
import
etree
import
unittest
import
textwrap
import
xml.sax.saxutils
as
saxutils
from
.
import
test_capa_system
...
...
@@ -583,6 +584,44 @@ class MatlabTest(unittest.TestCase):
self
.
assertEqual
(
input_state
[
'queuestate'
],
'queued'
)
self
.
assertFalse
(
'queue_msg'
in
input_state
)
def
test_get_html
(
self
):
# usual output
output
=
self
.
the_input
.
get_html
()
self
.
assertEqual
(
etree
.
tostring
(
output
),
"""<div>{
\'
status
\'
:
\'
queued
\'
,
\'
button_enabled
\'
: True,
\'
rows
\'
:
\'
10
\'
,
\'
queue_len
\'
:
\'
3
\'\
,
\'
mode
\'
:
\'\'
,
\'
cols
\'
:
\'
80
\'
,
\'
STATIC_URL
\'
:
\'
/dummy-static/
\'
,
\'
linenumbers
\'
:
\'
true
\'\
,
\'
queue_msg
\'
:
\'\'
,
\'
value
\'
:
\'
print "good evening"
\'
,
\'
msg
\'
: u
\'
Submitted
\
. As soon as a response is returned, this message will be replaced by that feedback.
\'
,
\'
hidden
\'
:
\'\'\
,
\'
id
\'
:
\'
prob_1_2
\'
,
\'
tabsize
\'
: 4}</div>"""
)
# test html, that is correct HTML5 html, but is not parsable by XML parser.
old_render_template
=
self
.
the_input
.
capa_system
.
render_template
self
.
the_input
.
capa_system
.
render_template
=
lambda
*
args
:
textwrap
.
dedent
(
"""
<div class='matlabResponse'><div id='mwAudioPlaceHolder'>
<audio controls autobuffer autoplay src='data:audio/wav;base64='>Audio is not supported on this browser.</audio>
<div>Right click <a href=https://endpoint.mss-mathworks.com/media/filename.wav>here</a> and click
\"
Save As
\"
to download the file</div></div>
<div style='white-space:pre' class='commandWindowOutput'></div><ul></ul></div>
"""
)
.
replace
(
'
\n
'
,
''
)
output
=
self
.
the_input
.
get_html
()
self
.
assertEqual
(
etree
.
tostring
(
output
),
textwrap
.
dedent
(
"""
<div class='matlabResponse'><div id='mwAudioPlaceHolder'>
<audio src='data:audio/wav;base64=' autobuffer="" controls="" autoplay="">Audio is not supported on this browser.</audio>
<div>Right click <a href="https://endpoint.mss-mathworks.com/media/filename.wav">here</a> and click
\"
Save As
\"
to download the file</div></div>
<div style='white-space:pre' class='commandWindowOutput'/><ul/></div>
"""
)
.
replace
(
'
\n
'
,
''
)
.
replace
(
'
\'
'
,
'
\"
'
)
)
# check that exception is raised during parsing for html.
self
.
the_input
.
capa_system
.
render_template
=
lambda
*
args
:
"<aaa"
with
self
.
assertRaises
(
etree
.
XMLSyntaxError
):
self
.
the_input
.
get_html
()
self
.
the_input
.
capa_system
.
render_template
=
old_render_template
class
SchematicTest
(
unittest
.
TestCase
):
'''
...
...
common/lib/capa/capa/tests/test_responsetypes.py
View file @
cd42c917
...
...
@@ -998,6 +998,59 @@ class CodeResponseTest(ResponseTest):
self
.
assertEquals
(
answers_converted
[
'1_3_1'
],
[
'answer1'
,
'answer2'
,
'answer3'
])
self
.
assertEquals
(
answers_converted
[
'1_4_1'
],
[
fp
.
name
,
fp
.
name
])
def
test_parse_score_msg_of_responder
(
self
):
"""
Test whether LoncapaProblem._parse_score_msg correcly parses valid HTML5 html.
"""
valid_grader_msgs
=
[
u'<span>MESSAGE</span>'
,
# Valid XML
textwrap
.
dedent
(
"""
<div class='matlabResponse'><div id='mwAudioPlaceHolder'>
<audio controls autobuffer autoplay src='data:audio/wav;base64='>Audio is not supported on this browser.</audio>
<div>Right click <a href=https://endpoint.mss-mathworks.com/media/filename.wav>here</a> and click
\"
Save As
\"
to download the file</div></div>
<div style='white-space:pre' class='commandWindowOutput'></div><ul></ul></div>
"""
)
.
replace
(
'
\n
'
,
''
),
# Valid HTML5 real case Matlab response, invalid XML
'<aaa></bbb>'
# Invalid XML, but will be parsed by html5lib to <aaa/>
]
invalid_grader_msgs
=
[
'<audio'
,
# invalid XML and HTML5
]
answer_ids
=
sorted
(
self
.
problem
.
get_question_answers
())
# CodeResponse requires internal CorrectMap state. Build it now in the queued state
old_cmap
=
CorrectMap
()
for
i
,
answer_id
in
enumerate
(
answer_ids
):
queuekey
=
1000
+
i
queuestate
=
CodeResponseTest
.
make_queuestate
(
queuekey
,
datetime
.
now
(
UTC
))
old_cmap
.
update
(
CorrectMap
(
answer_id
=
answer_ids
[
i
],
queuestate
=
queuestate
))
for
grader_msg
in
valid_grader_msgs
:
correct_score_msg
=
json
.
dumps
({
'correct'
:
True
,
'score'
:
1
,
'msg'
:
grader_msg
})
incorrect_score_msg
=
json
.
dumps
({
'correct'
:
False
,
'score'
:
0
,
'msg'
:
grader_msg
})
xserver_msgs
=
{
'correct'
:
correct_score_msg
,
'incorrect'
:
incorrect_score_msg
,
}
for
i
,
answer_id
in
enumerate
(
answer_ids
):
self
.
problem
.
correct_map
=
CorrectMap
()
self
.
problem
.
correct_map
.
update
(
old_cmap
)
output
=
self
.
problem
.
update_score
(
xserver_msgs
[
'correct'
],
queuekey
=
1000
+
i
)
self
.
assertEquals
(
output
[
answer_id
][
'msg'
],
grader_msg
)
for
grader_msg
in
invalid_grader_msgs
:
correct_score_msg
=
json
.
dumps
({
'correct'
:
True
,
'score'
:
1
,
'msg'
:
grader_msg
})
incorrect_score_msg
=
json
.
dumps
({
'correct'
:
False
,
'score'
:
0
,
'msg'
:
grader_msg
})
xserver_msgs
=
{
'correct'
:
correct_score_msg
,
'incorrect'
:
incorrect_score_msg
,
}
for
i
,
answer_id
in
enumerate
(
answer_ids
):
self
.
problem
.
correct_map
=
CorrectMap
()
self
.
problem
.
correct_map
.
update
(
old_cmap
)
output
=
self
.
problem
.
update_score
(
xserver_msgs
[
'correct'
],
queuekey
=
1000
+
i
)
self
.
assertEquals
(
output
[
answer_id
][
'msg'
],
u'Invalid grader reply. Please contact the course staff.'
)
class
ChoiceResponseTest
(
ResponseTest
):
from
capa.tests.response_xml_factory
import
ChoiceResponseXMLFactory
...
...
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