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
c7785d8f
Commit
c7785d8f
authored
Feb 24, 2014
by
Alexander Kryklia
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow HTML in grader messages.
parent
8e4b924b
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 @
c7785d8f
...
...
@@ -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 @
c7785d8f
...
...
@@ -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 @
c7785d8f
...
...
@@ -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 @
c7785d8f
...
...
@@ -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