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
559bf887
Commit
559bf887
authored
Jan 03, 2013
by
David Ormsbee
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1132 from MITx/vik/modify_open_ended
Vik/modify open ended
parents
ecd2304c
90d902d1
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
238 additions
and
13 deletions
+238
-13
common/lib/capa/capa/capa_problem.py
+18
-0
common/lib/capa/capa/inputtypes.py
+1
-1
common/lib/capa/capa/responsetypes.py
+100
-11
common/lib/capa/capa/templates/openendedinput.html
+25
-1
common/lib/xmodule/xmodule/capa_module.py
+15
-0
common/lib/xmodule/xmodule/css/capa/display.scss
+49
-0
common/lib/xmodule/xmodule/js/src/capa/display.coffee
+30
-0
No files found.
common/lib/capa/capa/capa_problem.py
View file @
559bf887
...
@@ -186,6 +186,24 @@ class LoncapaProblem(object):
...
@@ -186,6 +186,24 @@ class LoncapaProblem(object):
maxscore
+=
responder
.
get_max_score
()
maxscore
+=
responder
.
get_max_score
()
return
maxscore
return
maxscore
def
message_post
(
self
,
event_info
):
"""
Handle an ajax post that contains feedback on feedback
Returns a boolean success variable
Note: This only allows for feedback to be posted back to the grading controller for the first
open ended response problem on each page. Multiple problems will cause some sync issues.
TODO: Handle multiple problems on one page sync issues.
"""
success
=
False
message
=
"Could not find a valid responder."
log
.
debug
(
"in lcp"
)
for
responder
in
self
.
responders
.
values
():
if
hasattr
(
responder
,
'handle_message_post'
):
success
,
message
=
responder
.
handle_message_post
(
event_info
)
if
success
:
break
return
success
,
message
def
get_score
(
self
):
def
get_score
(
self
):
"""
"""
Compute score for this problem. The score is the number of points awarded.
Compute score for this problem. The score is the number of points awarded.
...
...
common/lib/capa/capa/inputtypes.py
View file @
559bf887
...
@@ -748,7 +748,7 @@ class OpenEndedInput(InputTypeBase):
...
@@ -748,7 +748,7 @@ class OpenEndedInput(InputTypeBase):
# pulled out for testing
# pulled out for testing
submitted_msg
=
(
"Feedback not yet available. Reload to check again. "
submitted_msg
=
(
"Feedback not yet available. Reload to check again. "
"Once the problem is graded, this message will be "
"Once the problem is graded, this message will be "
"replaced with the grader's feedback"
)
"replaced with the grader's feedback
.
"
)
@classmethod
@classmethod
def
get_attributes
(
cls
):
def
get_attributes
(
cls
):
...
...
common/lib/capa/capa/responsetypes.py
View file @
559bf887
...
@@ -1836,6 +1836,7 @@ class OpenEndedResponse(LoncapaResponse):
...
@@ -1836,6 +1836,7 @@ class OpenEndedResponse(LoncapaResponse):
"""
"""
DEFAULT_QUEUE
=
'open-ended'
DEFAULT_QUEUE
=
'open-ended'
DEFAULT_MESSAGE_QUEUE
=
'open-ended-message'
response_tag
=
'openendedresponse'
response_tag
=
'openendedresponse'
allowed_inputfields
=
[
'openendedinput'
]
allowed_inputfields
=
[
'openendedinput'
]
max_inputfields
=
1
max_inputfields
=
1
...
@@ -1847,12 +1848,17 @@ class OpenEndedResponse(LoncapaResponse):
...
@@ -1847,12 +1848,17 @@ class OpenEndedResponse(LoncapaResponse):
xml
=
self
.
xml
xml
=
self
.
xml
self
.
url
=
xml
.
get
(
'url'
,
None
)
self
.
url
=
xml
.
get
(
'url'
,
None
)
self
.
queue_name
=
xml
.
get
(
'queuename'
,
self
.
DEFAULT_QUEUE
)
self
.
queue_name
=
xml
.
get
(
'queuename'
,
self
.
DEFAULT_QUEUE
)
self
.
message_queue_name
=
xml
.
get
(
'message-queuename'
,
self
.
DEFAULT_MESSAGE_QUEUE
)
# The openendedparam tag encapsulates all grader settings
# The openendedparam tag encapsulates all grader settings
oeparam
=
self
.
xml
.
find
(
'openendedparam'
)
oeparam
=
self
.
xml
.
find
(
'openendedparam'
)
prompt
=
self
.
xml
.
find
(
'prompt'
)
prompt
=
self
.
xml
.
find
(
'prompt'
)
rubric
=
self
.
xml
.
find
(
'openendedrubric'
)
rubric
=
self
.
xml
.
find
(
'openendedrubric'
)
#This is needed to attach feedback to specific responses later
self
.
submission_id
=
None
self
.
grader_id
=
None
if
oeparam
is
None
:
if
oeparam
is
None
:
raise
ValueError
(
"No oeparam found in problem xml."
)
raise
ValueError
(
"No oeparam found in problem xml."
)
if
prompt
is
None
:
if
prompt
is
None
:
...
@@ -1899,23 +1905,81 @@ class OpenEndedResponse(LoncapaResponse):
...
@@ -1899,23 +1905,81 @@ class OpenEndedResponse(LoncapaResponse):
# response types)
# response types)
except
TypeError
,
ValueError
:
except
TypeError
,
ValueError
:
log
.
exception
(
"Grader payload
%
r is not a json object!"
,
grader_payload
)
log
.
exception
(
"Grader payload
%
r is not a json object!"
,
grader_payload
)
self
.
initial_display
=
find_with_default
(
oeparam
,
'initial_display'
,
''
)
self
.
answer
=
find_with_default
(
oeparam
,
'answer_display'
,
'No answer given.'
)
parsed_grader_payload
.
update
({
parsed_grader_payload
.
update
({
'location'
:
self
.
system
.
location
,
'location'
:
self
.
system
.
location
,
'course_id'
:
self
.
system
.
course_id
,
'course_id'
:
self
.
system
.
course_id
,
'prompt'
:
prompt_string
,
'prompt'
:
prompt_string
,
'rubric'
:
rubric_string
,
'rubric'
:
rubric_string
,
})
'initial_display'
:
self
.
initial_display
,
'answer'
:
self
.
answer
,
})
updated_grader_payload
=
json
.
dumps
(
parsed_grader_payload
)
updated_grader_payload
=
json
.
dumps
(
parsed_grader_payload
)
self
.
payload
=
{
'grader_payload'
:
updated_grader_payload
}
self
.
payload
=
{
'grader_payload'
:
updated_grader_payload
}
self
.
initial_display
=
find_with_default
(
oeparam
,
'initial_display'
,
''
)
self
.
answer
=
find_with_default
(
oeparam
,
'answer_display'
,
'No answer given.'
)
try
:
try
:
self
.
max_score
=
int
(
find_with_default
(
oeparam
,
'max_score'
,
1
))
self
.
max_score
=
int
(
find_with_default
(
oeparam
,
'max_score'
,
1
))
except
ValueError
:
except
ValueError
:
self
.
max_score
=
1
self
.
max_score
=
1
def
handle_message_post
(
self
,
event_info
):
"""
Handles a student message post (a reaction to the grade they received from an open ended grader type)
Returns a boolean success/fail and an error message
"""
survey_responses
=
event_info
[
'survey_responses'
]
for
tag
in
[
'feedback'
,
'submission_id'
,
'grader_id'
,
'score'
]:
if
tag
not
in
survey_responses
:
return
False
,
"Could not find needed tag {0}"
.
format
(
tag
)
try
:
submission_id
=
int
(
survey_responses
[
'submission_id'
])
grader_id
=
int
(
survey_responses
[
'grader_id'
])
feedback
=
str
(
survey_responses
[
'feedback'
]
.
encode
(
'ascii'
,
'ignore'
))
score
=
int
(
survey_responses
[
'score'
])
except
:
error_message
=
(
"Could not parse submission id, grader id, "
"or feedback from message_post ajax call. Here is the message data: {0}"
.
format
(
survey_responses
))
log
.
exception
(
error_message
)
return
False
,
"There was an error saving your feedback. Please contact course staff."
qinterface
=
self
.
system
.
xqueue
[
'interface'
]
qtime
=
datetime
.
strftime
(
datetime
.
now
(),
xqueue_interface
.
dateformat
)
anonymous_student_id
=
self
.
system
.
anonymous_student_id
queuekey
=
xqueue_interface
.
make_hashkey
(
str
(
self
.
system
.
seed
)
+
qtime
+
anonymous_student_id
+
self
.
answer_id
)
xheader
=
xqueue_interface
.
make_xheader
(
lms_callback_url
=
self
.
system
.
xqueue
[
'callback_url'
],
lms_key
=
queuekey
,
queue_name
=
self
.
message_queue_name
)
student_info
=
{
'anonymous_student_id'
:
anonymous_student_id
,
'submission_time'
:
qtime
,
}
contents
=
{
'feedback'
:
feedback
,
'submission_id'
:
submission_id
,
'grader_id'
:
grader_id
,
'score'
:
score
,
'student_info'
:
json
.
dumps
(
student_info
),
}
(
error
,
msg
)
=
qinterface
.
send_to_queue
(
header
=
xheader
,
body
=
json
.
dumps
(
contents
))
#Convert error to a success value
success
=
True
if
error
:
success
=
False
return
success
,
"Successfully submitted your feedback."
def
get_score
(
self
,
student_answers
):
def
get_score
(
self
,
student_answers
):
try
:
try
:
...
@@ -1956,7 +2020,7 @@ class OpenEndedResponse(LoncapaResponse):
...
@@ -1956,7 +2020,7 @@ class OpenEndedResponse(LoncapaResponse):
contents
.
update
({
contents
.
update
({
'student_info'
:
json
.
dumps
(
student_info
),
'student_info'
:
json
.
dumps
(
student_info
),
'student_response'
:
submission
,
'student_response'
:
submission
,
'max_score'
:
self
.
max_score
'max_score'
:
self
.
max_score
,
})
})
# Submit request. When successful, 'msg' is the prior length of the queue
# Submit request. When successful, 'msg' is the prior length of the queue
...
@@ -2056,18 +2120,36 @@ class OpenEndedResponse(LoncapaResponse):
...
@@ -2056,18 +2120,36 @@ class OpenEndedResponse(LoncapaResponse):
"""
"""
return
priorities
.
get
(
elt
[
0
],
default_priority
)
return
priorities
.
get
(
elt
[
0
],
default_priority
)
def
encode_values
(
feedback_type
,
value
):
feedback_type
=
str
(
feedback_type
)
.
encode
(
'ascii'
,
'ignore'
)
if
not
isinstance
(
value
,
basestring
):
value
=
str
(
value
)
value
=
value
.
encode
(
'ascii'
,
'ignore'
)
return
feedback_type
,
value
def
format_feedback
(
feedback_type
,
value
):
def
format_feedback
(
feedback_type
,
value
):
return
"""
feedback_type
,
value
=
encode_values
(
feedback_type
,
value
)
feedback
=
"""
<div class="{feedback_type}">
<div class="{feedback_type}">
{value}
{value}
</div>
</div>
"""
.
format
(
feedback_type
=
feedback_type
,
value
=
value
)
"""
.
format
(
feedback_type
=
feedback_type
,
value
=
value
)
return
feedback
def
format_feedback_hidden
(
feedback_type
,
value
):
feedback_type
,
value
=
encode_values
(
feedback_type
,
value
)
feedback
=
"""
<div class="{feedback_type}" style="display: none;">
{value}
</div>
"""
.
format
(
feedback_type
=
feedback_type
,
value
=
value
)
return
feedback
# TODO (vshnayder): design and document the details of this format so
# TODO (vshnayder): design and document the details of this format so
# that we can do proper escaping here (e.g. are the graders allowed to
# that we can do proper escaping here (e.g. are the graders allowed to
# include HTML?)
# include HTML?)
for
tag
in
[
'success'
,
'feedback'
]:
for
tag
in
[
'success'
,
'feedback'
,
'submission_id'
,
'grader_id'
]:
if
tag
not
in
response_items
:
if
tag
not
in
response_items
:
return
format_feedback
(
'errors'
,
'Error getting feedback'
)
return
format_feedback
(
'errors'
,
'Error getting feedback'
)
...
@@ -2083,10 +2165,15 @@ class OpenEndedResponse(LoncapaResponse):
...
@@ -2083,10 +2165,15 @@ class OpenEndedResponse(LoncapaResponse):
return
format_feedback
(
'errors'
,
'No feedback available'
)
return
format_feedback
(
'errors'
,
'No feedback available'
)
feedback_lst
=
sorted
(
feedback
.
items
(),
key
=
get_priority
)
feedback_lst
=
sorted
(
feedback
.
items
(),
key
=
get_priority
)
return
u"
\n
"
.
join
(
format_feedback
(
k
,
v
)
for
k
,
v
in
feedback_lst
)
feedback_list_part1
=
u"
\n
"
.
join
(
format_feedback
(
k
,
v
)
for
k
,
v
in
feedback_lst
)
else
:
else
:
return
format_feedback
(
'errors'
,
response_items
[
'feedback'
])
feedback_list_part1
=
format_feedback
(
'errors'
,
response_items
[
'feedback'
])
feedback_list_part2
=
(
u"
\n
"
.
join
([
format_feedback_hidden
(
feedback_type
,
value
)
for
feedback_type
,
value
in
response_items
.
items
()
if
feedback_type
in
[
'submission_id'
,
'grader_id'
]]))
return
u"
\n
"
.
join
([
feedback_list_part1
,
feedback_list_part2
])
def
_format_feedback
(
self
,
response_items
):
def
_format_feedback
(
self
,
response_items
):
"""
"""
...
@@ -2104,7 +2191,7 @@ class OpenEndedResponse(LoncapaResponse):
...
@@ -2104,7 +2191,7 @@ class OpenEndedResponse(LoncapaResponse):
feedback_template
=
self
.
system
.
render_template
(
"open_ended_feedback.html"
,
{
feedback_template
=
self
.
system
.
render_template
(
"open_ended_feedback.html"
,
{
'grader_type'
:
response_items
[
'grader_type'
],
'grader_type'
:
response_items
[
'grader_type'
],
'score'
:
response_items
[
'score'
]
,
'score'
:
"{0} / {1}"
.
format
(
response_items
[
'score'
],
self
.
max_score
)
,
'feedback'
:
feedback
,
'feedback'
:
feedback
,
})
})
...
@@ -2138,17 +2225,19 @@ class OpenEndedResponse(LoncapaResponse):
...
@@ -2138,17 +2225,19 @@ class OpenEndedResponse(LoncapaResponse):
" Received score_result = {0}"
.
format
(
score_result
))
" Received score_result = {0}"
.
format
(
score_result
))
return
fail
return
fail
for
tag
in
[
'score'
,
'feedback'
,
'grader_type'
,
'success'
]:
for
tag
in
[
'score'
,
'feedback'
,
'grader_type'
,
'success'
,
'grader_id'
,
'submission_id'
]:
if
tag
not
in
score_result
:
if
tag
not
in
score_result
:
log
.
error
(
"External grader message is missing required tag: {0}"
log
.
error
(
"External grader message is missing required tag: {0}"
.
format
(
tag
))
.
format
(
tag
))
return
fail
return
fail
feedback
=
self
.
_format_feedback
(
score_result
)
feedback
=
self
.
_format_feedback
(
score_result
)
self
.
submission_id
=
score_result
[
'submission_id'
]
self
.
grader_id
=
score_result
[
'grader_id'
]
# HACK: for now, just assume it's correct if you got more than 2/3.
# HACK: for now, just assume it's correct if you got more than 2/3.
# Also assumes that score_result['score'] is an integer.
# Also assumes that score_result['score'] is an integer.
score_ratio
=
int
(
score_result
[
'score'
])
/
self
.
max_score
score_ratio
=
int
(
score_result
[
'score'
])
/
float
(
self
.
max_score
)
correct
=
(
score_ratio
>=
0.66
)
correct
=
(
score_ratio
>=
0.66
)
#Currently ignore msg and only return feedback (which takes the place of msg)
#Currently ignore msg and only return feedback (which takes the place of msg)
...
...
common/lib/capa/capa/templates/openendedinput.html
View file @
559bf887
...
@@ -27,6 +27,30 @@
...
@@ -27,6 +27,30 @@
<input
name=
"reload"
class=
"reload"
type=
"button"
value=
"Recheck for Feedback"
onclick=
"document.location.reload(true);"
/>
<input
name=
"reload"
class=
"reload"
type=
"button"
value=
"Recheck for Feedback"
onclick=
"document.location.reload(true);"
/>
% endif
% endif
<div
class=
"external-grader-message"
>
<div
class=
"external-grader-message"
>
${msg|n}
${msg|n}
% if status in ['correct','incorrect']:
<div
class=
"collapsible evaluation-response"
>
<header>
<a
href=
"#"
>
Respond to Feedback
</a>
</header>
<section
id=
"evaluation_${id}"
class=
"evaluation"
>
<p>
How accurate do you find this feedback?
</p>
<div
class=
"evaluation-scoring"
>
<ul
class=
"scoring-list"
>
<li><input
type=
"radio"
name=
"evaluation-score"
id=
"evaluation-score-5"
value=
"5"
/>
<label
for=
"evaluation-score-5"
>
Correct
</label></li>
<li><input
type=
"radio"
name=
"evaluation-score"
id=
"evaluation-score-4"
value=
"4"
/>
<label
for=
"evaluation-score-4"
>
Partially Correct
</label></li>
<li><input
type=
"radio"
name=
"evaluation-score"
id=
"evaluation-score-3"
value=
"3"
/>
<label
for=
"evaluation-score-3"
>
No Opinion
</label></li>
<li><input
type=
"radio"
name=
"evaluation-score"
id=
"evaluation-score-2"
value=
"2"
/>
<label
for=
"evaluation-score-2"
>
Partially Incorrect
</label></li>
<li><input
type=
"radio"
name=
"evaluation-score"
id=
"evaluation-score-1"
value=
"1"
/>
<label
for=
"evaluation-score-1"
>
Incorrect
</label></li>
</ul>
</div>
<p>
Additional comments:
</p>
<textarea
rows=
"${rows}"
cols=
"${cols}"
name=
"feedback_${id}"
class=
"feedback-on-feedback"
id=
"feedback_${id}"
></textarea>
<div
class=
"submit-message-container"
>
<input
name=
"submit-message"
class=
"submit-message"
type=
"button"
value=
"Submit your message"
/>
</div>
</section>
</div>
% endif
</div>
</div>
</section>
</section>
common/lib/xmodule/xmodule/capa_module.py
View file @
559bf887
...
@@ -380,6 +380,7 @@ class CapaModule(XModule):
...
@@ -380,6 +380,7 @@ class CapaModule(XModule):
'problem_save'
:
self
.
save_problem
,
'problem_save'
:
self
.
save_problem
,
'problem_show'
:
self
.
get_answer
,
'problem_show'
:
self
.
get_answer
,
'score_update'
:
self
.
update_score
,
'score_update'
:
self
.
update_score
,
'message_post'
:
self
.
message_post
,
}
}
if
dispatch
not
in
handlers
:
if
dispatch
not
in
handlers
:
...
@@ -394,6 +395,20 @@ class CapaModule(XModule):
...
@@ -394,6 +395,20 @@ class CapaModule(XModule):
})
})
return
json
.
dumps
(
d
,
cls
=
ComplexEncoder
)
return
json
.
dumps
(
d
,
cls
=
ComplexEncoder
)
def
message_post
(
self
,
get
):
"""
Posts a message from a form to an appropriate location
"""
event_info
=
dict
()
event_info
[
'state'
]
=
self
.
lcp
.
get_state
()
event_info
[
'problem_id'
]
=
self
.
location
.
url
()
event_info
[
'student_id'
]
=
self
.
system
.
anonymous_student_id
event_info
[
'survey_responses'
]
=
get
success
,
message
=
self
.
lcp
.
message_post
(
event_info
)
return
{
'success'
:
success
,
'message'
:
message
}
def
closed
(
self
):
def
closed
(
self
):
''' Is the student still allowed to submit answers? '''
''' Is the student still allowed to submit answers? '''
if
self
.
attempts
==
self
.
max_attempts
:
if
self
.
attempts
==
self
.
max_attempts
:
...
...
common/lib/xmodule/xmodule/css/capa/display.scss
View file @
559bf887
...
@@ -297,6 +297,51 @@ section.problem {
...
@@ -297,6 +297,51 @@ section.problem {
float
:
left
;
float
:
left
;
}
}
}
}
}
.evaluation
{
p
{
margin-bottom
:
4px
;
}
}
.feedback-on-feedback
{
height
:
100px
;
margin-right
:
20px
;
}
.evaluation-response
{
header
{
text-align
:
right
;
a
{
font-size
:
.85em
;
}
}
}
.evaluation-scoring
{
.scoring-list
{
list-style-type
:
none
;
margin-left
:
3px
;
li
{
&
:first-child
{
margin-left
:
0px
;
}
display
:inline
;
margin-left
:
50px
;
label
{
font-size
:
.9em
;
}
}
}
}
.submit-message-container
{
margin
:
10px
0px
;
}
}
}
}
...
@@ -634,6 +679,10 @@ section.problem {
...
@@ -634,6 +679,10 @@ section.problem {
color
:
#2C2C2C
;
color
:
#2C2C2C
;
font-family
:
monospace
;
font-family
:
monospace
;
font-size
:
1em
;
font-size
:
1em
;
padding-top
:
10px
;
header
{
font-size
:
1
.4em
;
}
.shortform
{
.shortform
{
font-weight
:
bold
;
font-weight
:
bold
;
...
...
common/lib/xmodule/xmodule/js/src/capa/display.coffee
View file @
559bf887
...
@@ -25,6 +25,7 @@ class @Problem
...
@@ -25,6 +25,7 @@ class @Problem
@
$
(
'section.action input.reset'
).
click
@
reset
@
$
(
'section.action input.reset'
).
click
@
reset
@
$
(
'section.action input.show'
).
click
@
show
@
$
(
'section.action input.show'
).
click
@
show
@
$
(
'section.action input.save'
).
click
@
save
@
$
(
'section.action input.save'
).
click
@
save
@
$
(
'section.evaluation input.submit-message'
).
click
@
message_post
# Collapsibles
# Collapsibles
Collapsible
.
setCollapsibles
(
@
el
)
Collapsible
.
setCollapsibles
(
@
el
)
...
@@ -197,6 +198,35 @@ class @Problem
...
@@ -197,6 +198,35 @@ class @Problem
else
else
@
gentle_alert
response
.
success
@
gentle_alert
response
.
success
message_post
:
=>
Logger
.
log
'message_post'
,
@
answers
fd
=
new
FormData
()
feedback
=
@
$
(
'section.evaluation textarea.feedback-on-feedback'
)[
0
].
value
submission_id
=
$
(
'div.external-grader-message div.submission_id'
)[
0
].
innerHTML
grader_id
=
$
(
'div.external-grader-message div.grader_id'
)[
0
].
innerHTML
score
=
$
(
".evaluation-scoring input:radio[name='evaluation-score']:checked"
).
val
()
fd
.
append
(
'feedback'
,
feedback
)
fd
.
append
(
'submission_id'
,
submission_id
)
fd
.
append
(
'grader_id'
,
grader_id
)
if
(
!
score
)
@
gentle_alert
"You need to pick a rating before you can submit."
return
else
fd
.
append
(
'score'
,
score
)
settings
=
type
:
"POST"
data
:
fd
processData
:
false
contentType
:
false
success
:
(
response
)
=>
@
gentle_alert
response
.
message
@
$
(
'section.evaluation'
).
slideToggle
()
$
.
ajaxWithPrefix
(
"
#{
@
url
}
/message_post"
,
settings
)
reset
:
=>
reset
:
=>
Logger
.
log
'problem_reset'
,
@
answers
Logger
.
log
'problem_reset'
,
@
answers
$
.
postWithPrefix
"
#{
@
url
}
/problem_reset"
,
id
:
@
id
,
(
response
)
=>
$
.
postWithPrefix
"
#{
@
url
}
/problem_reset"
,
id
:
@
id
,
(
response
)
=>
...
...
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