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
49ee1296
Commit
49ee1296
authored
Jul 19, 2012
by
Calen Pennington
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #185 from MITx/kimth/lms-coderesponse
Kimth/lms coderesponse
parents
2eaa6dbc
cc9f9a9d
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
285 additions
and
55 deletions
+285
-55
common/lib/capa/capa/capa_problem.py
+36
-8
common/lib/capa/capa/correctmap.py
+9
-1
common/lib/capa/capa/responsetypes.py
+59
-34
common/lib/xmodule/tests/__init__.py
+54
-0
common/lib/xmodule/tests/test_files/coderesponse.xml
+101
-0
common/lib/xmodule/xmodule/capa_module.py
+13
-2
lms/djangoapps/courseware/module_render.py
+12
-9
lms/urls.py
+1
-1
No files found.
common/lib/capa/capa/capa_problem.py
View file @
49ee1296
...
@@ -118,6 +118,9 @@ class LoncapaProblem(object):
...
@@ -118,6 +118,9 @@ class LoncapaProblem(object):
# the dict has keys = xml subtree of Response, values = Response instance
# the dict has keys = xml subtree of Response, values = Response instance
self
.
_preprocess_problem
(
self
.
tree
)
self
.
_preprocess_problem
(
self
.
tree
)
if
not
self
.
student_answers
:
# True when student_answers is an empty dict
self
.
set_initial_display
()
def
do_reset
(
self
):
def
do_reset
(
self
):
'''
'''
Reset internal state to unfinished, with no answers
Reset internal state to unfinished, with no answers
...
@@ -126,6 +129,14 @@ class LoncapaProblem(object):
...
@@ -126,6 +129,14 @@ class LoncapaProblem(object):
self
.
correct_map
=
CorrectMap
()
self
.
correct_map
=
CorrectMap
()
self
.
done
=
False
self
.
done
=
False
def
set_initial_display
(
self
):
initial_answers
=
dict
()
for
responder
in
self
.
responders
.
values
():
if
hasattr
(
responder
,
'get_initial_display'
):
initial_answers
.
update
(
responder
.
get_initial_display
())
self
.
student_answers
=
initial_answers
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
u"LoncapaProblem ({0})"
.
format
(
self
.
problem_id
)
return
u"LoncapaProblem ({0})"
.
format
(
self
.
problem_id
)
...
@@ -180,14 +191,31 @@ class LoncapaProblem(object):
...
@@ -180,14 +191,31 @@ class LoncapaProblem(object):
return
{
'score'
:
correct
,
return
{
'score'
:
correct
,
'total'
:
self
.
get_max_score
()}
'total'
:
self
.
get_max_score
()}
def
update_score
(
self
,
score_msg
):
def
update_score
(
self
,
score_msg
,
queuekey
):
newcmap
=
CorrectMap
()
'''
Deliver grading response (e.g. from async code checking) to
the specific ResponseType that requested grading
Returns an updated CorrectMap
'''
cmap
=
CorrectMap
()
cmap
.
update
(
self
.
correct_map
)
for
responder
in
self
.
responders
.
values
():
for
responder
in
self
.
responders
.
values
():
if
hasattr
(
responder
,
'update_score'
):
# Is this the best way to implement 'update_score' for CodeResponse?
if
hasattr
(
responder
,
'update_score'
):
results
=
responder
.
update_score
(
score_msg
)
# Each LoncapaResponse will update the specific entries of 'cmap' that it's responsible for
newcmap
.
update
(
results
)
cmap
=
responder
.
update_score
(
score_msg
,
cmap
,
queuekey
)
self
.
correct_map
=
newcmap
self
.
correct_map
.
set_dict
(
cmap
.
get_dict
())
return
newcmap
return
cmap
def
is_queued
(
self
):
'''
Returns True if any part of the problem has been submitted to an external queue
'''
queued
=
False
for
answer_id
in
self
.
correct_map
:
if
self
.
correct_map
.
is_queued
(
answer_id
):
queued
=
True
return
queued
def
grade_answers
(
self
,
answers
):
def
grade_answers
(
self
,
answers
):
'''
'''
...
@@ -457,7 +485,7 @@ class LoncapaProblem(object):
...
@@ -457,7 +485,7 @@ class LoncapaProblem(object):
self
.
responder_answers
=
{}
self
.
responder_answers
=
{}
for
response
in
self
.
responders
.
keys
():
for
response
in
self
.
responders
.
keys
():
try
:
try
:
self
.
responder_answers
[
response
]
=
responder
.
get_answers
()
self
.
responder_answers
[
response
]
=
self
.
responders
[
response
]
.
get_answers
()
except
:
except
:
log
.
debug
(
'responder
%
s failed to properly return get_answers()'
%
self
.
responders
[
response
])
# FIXME
log
.
debug
(
'responder
%
s failed to properly return get_answers()'
%
self
.
responders
[
response
])
# FIXME
raise
raise
...
...
common/lib/capa/capa/correctmap.py
View file @
49ee1296
...
@@ -14,6 +14,7 @@ class CorrectMap(object):
...
@@ -14,6 +14,7 @@ class CorrectMap(object):
- msg : string (may have HTML) giving extra message response (displayed below textline or textbox)
- msg : string (may have HTML) giving extra message response (displayed below textline or textbox)
- hint : string (may have HTML) giving optional hint (displayed below textline or textbox, above msg)
- hint : string (may have HTML) giving optional hint (displayed below textline or textbox, above msg)
- hintmode : one of (None,'on_request','always') criteria for displaying hint
- hintmode : one of (None,'on_request','always') criteria for displaying hint
- queuekey : a random integer for xqueue_callback verification
Behaves as a dict.
Behaves as a dict.
'''
'''
...
@@ -29,13 +30,14 @@ class CorrectMap(object):
...
@@ -29,13 +30,14 @@ class CorrectMap(object):
def
__iter__
(
self
):
def
__iter__
(
self
):
return
self
.
cmap
.
__iter__
()
return
self
.
cmap
.
__iter__
()
def
set
(
self
,
answer_id
=
None
,
correctness
=
None
,
npoints
=
None
,
msg
=
''
,
hint
=
''
,
hintmode
=
None
):
def
set
(
self
,
answer_id
=
None
,
correctness
=
None
,
npoints
=
None
,
msg
=
''
,
hint
=
''
,
hintmode
=
None
,
queuekey
=
None
):
if
answer_id
is
not
None
:
if
answer_id
is
not
None
:
self
.
cmap
[
answer_id
]
=
{
'correctness'
:
correctness
,
self
.
cmap
[
answer_id
]
=
{
'correctness'
:
correctness
,
'npoints'
:
npoints
,
'npoints'
:
npoints
,
'msg'
:
msg
,
'msg'
:
msg
,
'hint'
:
hint
,
'hint'
:
hint
,
'hintmode'
:
hintmode
,
'hintmode'
:
hintmode
,
'queuekey'
:
queuekey
,
}
}
def
__repr__
(
self
):
def
__repr__
(
self
):
...
@@ -63,6 +65,12 @@ class CorrectMap(object):
...
@@ -63,6 +65,12 @@ class CorrectMap(object):
if
answer_id
in
self
.
cmap
:
return
self
.
cmap
[
answer_id
][
'correctness'
]
==
'correct'
if
answer_id
in
self
.
cmap
:
return
self
.
cmap
[
answer_id
][
'correctness'
]
==
'correct'
return
None
return
None
def
is_queued
(
self
,
answer_id
):
return
answer_id
in
self
.
cmap
and
self
.
cmap
[
answer_id
][
'queuekey'
]
is
not
None
def
is_right_queuekey
(
self
,
answer_id
,
test_key
):
return
answer_id
in
self
.
cmap
and
self
.
cmap
[
answer_id
][
'queuekey'
]
==
test_key
def
get_npoints
(
self
,
answer_id
):
def
get_npoints
(
self
,
answer_id
):
if
self
.
is_correct
(
answer_id
):
if
self
.
is_correct
(
answer_id
):
npoints
=
self
.
cmap
[
answer_id
]
.
get
(
'npoints'
,
1
)
# default to 1 point if correct
npoints
=
self
.
cmap
[
answer_id
]
.
get
(
'npoints'
,
1
)
# default to 1 point if correct
...
...
common/lib/capa/capa/responsetypes.py
View file @
49ee1296
...
@@ -8,6 +8,7 @@ Used by capa_problem.py
...
@@ -8,6 +8,7 @@ Used by capa_problem.py
'''
'''
# standard library imports
# standard library imports
import
hashlib
import
inspect
import
inspect
import
json
import
json
import
logging
import
logging
...
@@ -16,9 +17,9 @@ import numpy
...
@@ -16,9 +17,9 @@ import numpy
import
random
import
random
import
re
import
re
import
requests
import
requests
import
time
import
traceback
import
traceback
import
abc
import
abc
import
time
# specific library imports
# specific library imports
from
calc
import
evaluator
,
UndefinedVariable
from
calc
import
evaluator
,
UndefinedVariable
...
@@ -696,7 +697,10 @@ class SymbolicResponse(CustomResponse):
...
@@ -696,7 +697,10 @@ class SymbolicResponse(CustomResponse):
class
CodeResponse
(
LoncapaResponse
):
class
CodeResponse
(
LoncapaResponse
):
'''
'''
Grade student code using an external server
Grade student code using an external server, called 'xqueue'
In contrast to ExternalResponse, CodeResponse has following behavior:
1) Goes through a queueing system
2) Does not do external request for 'get_answers'
'''
'''
response_tag
=
'coderesponse'
response_tag
=
'coderesponse'
...
@@ -704,7 +708,7 @@ class CodeResponse(LoncapaResponse):
...
@@ -704,7 +708,7 @@ class CodeResponse(LoncapaResponse):
def
setup_response
(
self
):
def
setup_response
(
self
):
xml
=
self
.
xml
xml
=
self
.
xml
self
.
url
=
xml
.
get
(
'url'
)
or
"http://ec2-50-16-59-149.compute-1.amazonaws.com/xqueue/submit/"
# FIXME -- hardcoded url
self
.
url
=
xml
.
get
(
'url'
,
"http://ec2-50-16-59-149.compute-1.amazonaws.com/xqueue/submit/"
)
# FIXME -- hardcoded url
answer
=
xml
.
find
(
'answer'
)
answer
=
xml
.
find
(
'answer'
)
if
answer
is
not
None
:
if
answer
is
not
None
:
...
@@ -722,9 +726,27 @@ class CodeResponse(LoncapaResponse):
...
@@ -722,9 +726,27 @@ class CodeResponse(LoncapaResponse):
self
.
tests
=
xml
.
get
(
'tests'
)
self
.
tests
=
xml
.
get
(
'tests'
)
# Extract 'answer' and 'initial_display' from XML. Note that the code to be exec'ed here is:
# (1) Internal edX code, i.e. NOT student submissions, and
# (2) The code should only define the strings 'initial_display', 'answer', 'preamble', 'test_program'
# following the 6.01 problem definition convention
penv
=
{}
penv
[
'__builtins__'
]
=
globals
()[
'__builtins__'
]
try
:
exec
(
self
.
code
,
penv
,
penv
)
except
Exception
as
err
:
log
.
error
(
'Error in CodeResponse
%
s: Error in problem reference code'
%
err
)
raise
Exception
(
err
)
try
:
self
.
answer
=
penv
[
'answer'
]
self
.
initial_display
=
penv
[
'initial_display'
]
except
Exception
as
err
:
log
.
error
(
"Error in CodeResponse
%
s: Problem reference code does not define 'answer' and/or 'initial_display' in <answer>...</answer>"
%
err
)
raise
Exception
(
err
)
def
get_score
(
self
,
student_answers
):
def
get_score
(
self
,
student_answers
):
idset
=
sorted
(
self
.
answer_ids
)
idset
=
sorted
(
self
.
answer_ids
)
try
:
try
:
submission
=
[
student_answers
[
k
]
for
k
in
idset
]
submission
=
[
student_answers
[
k
]
for
k
in
idset
]
except
Exception
as
err
:
except
Exception
as
err
:
...
@@ -734,12 +756,16 @@ class CodeResponse(LoncapaResponse):
...
@@ -734,12 +756,16 @@ class CodeResponse(LoncapaResponse):
self
.
context
.
update
({
'submission'
:
submission
})
self
.
context
.
update
({
'submission'
:
submission
})
extra_payload
=
{
'edX_student_response'
:
json
.
dumps
(
submission
)}
extra_payload
=
{
'edX_student_response'
:
json
.
dumps
(
submission
)}
# Should do something -- like update the problem state -- based on the queue response
r
,
queuekey
=
self
.
_send_to_queue
(
extra_payload
)
# TODO: Perform checks on the xqueue response
r
=
self
.
_send_to_queue
(
extra_payload
)
return
CorrectMap
()
def
update_score
(
self
,
score_msg
):
# Non-null CorrectMap['queuekey'] indicates that the problem has been submitted
cmap
=
CorrectMap
()
for
answer_id
in
idset
:
cmap
.
set
(
answer_id
,
queuekey
=
queuekey
,
msg
=
'Submitted to queue'
)
return
cmap
def
update_score
(
self
,
score_msg
,
oldcmap
,
queuekey
):
# Parse 'score_msg' as XML
# Parse 'score_msg' as XML
try
:
try
:
rxml
=
etree
.
fromstring
(
score_msg
)
rxml
=
etree
.
fromstring
(
score_msg
)
...
@@ -749,7 +775,6 @@ class CodeResponse(LoncapaResponse):
...
@@ -749,7 +775,6 @@ class CodeResponse(LoncapaResponse):
# The following process is lifted directly from ExternalResponse
# The following process is lifted directly from ExternalResponse
idset
=
sorted
(
self
.
answer_ids
)
idset
=
sorted
(
self
.
answer_ids
)
cmap
=
CorrectMap
()
ad
=
rxml
.
find
(
'awarddetail'
)
.
text
ad
=
rxml
.
find
(
'awarddetail'
)
.
text
admap
=
{
'EXACT_ANS'
:
'correct'
,
# TODO: handle other loncapa responses
admap
=
{
'EXACT_ANS'
:
'correct'
,
# TODO: handle other loncapa responses
'WRONG_FORMAT'
:
'incorrect'
,
'WRONG_FORMAT'
:
'incorrect'
,
...
@@ -758,41 +783,41 @@ class CodeResponse(LoncapaResponse):
...
@@ -758,41 +783,41 @@ class CodeResponse(LoncapaResponse):
if
ad
in
admap
:
if
ad
in
admap
:
self
.
context
[
'correct'
][
0
]
=
admap
[
ad
]
self
.
context
[
'correct'
][
0
]
=
admap
[
ad
]
# create CorrectMap
# Replace 'oldcmap' with new grading results if queuekey matches
for
key
in
idset
:
# If queuekey does not match, we keep waiting for the score_msg that will match
idx
=
idset
.
index
(
key
)
for
answer_id
in
idset
:
msg
=
rxml
.
find
(
'message'
)
.
text
.
replace
(
' '
,
' '
)
if
idx
==
0
else
None
if
oldcmap
.
is_right_queuekey
(
answer_id
,
queuekey
):
# If answer_id is not queued, will return False
cmap
.
set
(
key
,
self
.
context
[
'correct'
][
idx
],
msg
=
msg
)
idx
=
idset
.
index
(
answer_id
)
msg
=
rxml
.
find
(
'message'
)
.
text
.
replace
(
' '
,
' '
)
if
idx
==
0
else
None
oldcmap
.
set
(
answer_id
,
self
.
context
[
'correct'
][
idx
],
msg
=
msg
,
queuekey
=
None
)
# Queuekey is consumed
else
:
# Queuekey does not match
log
.
debug
(
'CodeResponse: queuekey
%
d does not match for answer_id=
%
s.'
%
(
queuekey
,
answer_id
))
return
cmap
return
oldcmap
# CodeResponse differentiates from ExternalResponse in the behavior of 'get_answers'. CodeResponse.get_answers
# CodeResponse differentiates from ExternalResponse in the behavior of 'get_answers'. CodeResponse.get_answers
# does NOT require a queue submission, and the answer is computed (extracted from problem XML) locally.
# does NOT require a queue submission, and the answer is computed (extracted from problem XML) locally.
def
get_answers
(
self
):
def
get_answers
(
self
):
# Extract the CodeResponse answer from XML
anshtml
=
'<font color="blue"><span class="code-answer"><br/><pre>
%
s</pre><br/></span></font>'
%
self
.
answer
penv
=
{}
penv
[
'__builtins__'
]
=
globals
()[
'__builtins__'
]
try
:
exec
(
self
.
code
,
penv
,
penv
)
except
Exception
as
err
:
log
.
error
(
'Error in CodeResponse
%
s: Error in problem reference code'
%
err
)
raise
Exception
(
err
)
try
:
ans
=
penv
[
'answer'
]
except
Exception
as
err
:
log
.
error
(
'Error in CodeResponse
%
s: Problem reference code does not define answer in <answer>...</answer>'
%
err
)
raise
Exception
(
err
)
anshtml
=
'<font color="blue"><span class="code-answer"><br/><pre>
%
s</pre><br/></span></font>'
%
ans
return
dict
(
zip
(
self
.
answer_ids
,[
anshtml
]))
return
dict
(
zip
(
self
.
answer_ids
,[
anshtml
]))
def
get_initial_display
(
self
):
return
dict
(
zip
(
self
.
answer_ids
,[
self
.
initial_display
]))
# CodeResponse._send_to_queue implements the same interface as defined for ExternalResponse's 'get_score'
# CodeResponse._send_to_queue implements the same interface as defined for ExternalResponse's 'get_score'
def
_send_to_queue
(
self
,
extra_payload
):
def
_send_to_queue
(
self
,
extra_payload
):
# Prepare payload
# Prepare payload
xmlstr
=
etree
.
tostring
(
self
.
xml
,
pretty_print
=
True
)
xmlstr
=
etree
.
tostring
(
self
.
xml
,
pretty_print
=
True
)
header
=
{
'return_url'
:
self
.
system
.
xqueue_callback_url
}
header
=
{
'return_url'
:
self
.
system
.
xqueue_callback_url
}
header
.
update
({
'timestamp'
:
time
.
time
()})
payload
=
{
'xqueue_header'
:
json
.
dumps
(
header
),
# 'xqueue_header' should eventually be derived from xqueue.queue_common.HEADER_TAG or something similar
h
=
hashlib
.
md5
()
if
self
.
system
is
not
None
:
h
.
update
(
str
(
self
.
system
.
get
(
'seed'
)))
h
.
update
(
str
(
time
.
time
()))
queuekey
=
int
(
h
.
hexdigest
(),
16
)
header
.
update
({
'queuekey'
:
queuekey
})
payload
=
{
'xqueue_header'
:
json
.
dumps
(
header
),
# TODO: 'xqueue_header' should eventually be derived from a config file
'xml'
:
xmlstr
,
'xml'
:
xmlstr
,
'edX_cmd'
:
'get_score'
,
'edX_cmd'
:
'get_score'
,
'edX_tests'
:
self
.
tests
,
'edX_tests'
:
self
.
tests
,
...
@@ -808,7 +833,7 @@ class CodeResponse(LoncapaResponse):
...
@@ -808,7 +833,7 @@ class CodeResponse(LoncapaResponse):
log
.
error
(
msg
)
log
.
error
(
msg
)
raise
Exception
(
msg
)
raise
Exception
(
msg
)
return
r
return
r
,
queuekey
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
...
...
common/lib/xmodule/tests/__init__.py
View file @
49ee1296
...
@@ -13,6 +13,7 @@ import numpy
...
@@ -13,6 +13,7 @@ import numpy
import
xmodule
import
xmodule
import
capa.calc
as
calc
import
capa.calc
as
calc
import
capa.capa_problem
as
lcp
import
capa.capa_problem
as
lcp
from
capa.correctmap
import
CorrectMap
from
xmodule
import
graders
,
x_module
from
xmodule
import
graders
,
x_module
from
xmodule.graders
import
Score
,
aggregate_scores
from
xmodule.graders
import
Score
,
aggregate_scores
from
xmodule.progress
import
Progress
from
xmodule.progress
import
Progress
...
@@ -271,6 +272,59 @@ class StringResponseWithHintTest(unittest.TestCase):
...
@@ -271,6 +272,59 @@ class StringResponseWithHintTest(unittest.TestCase):
self
.
assertEquals
(
cmap
.
get_correctness
(
'1_2_1'
),
'incorrect'
)
self
.
assertEquals
(
cmap
.
get_correctness
(
'1_2_1'
),
'incorrect'
)
self
.
assertTrue
(
'St. Paul'
in
cmap
.
get_hint
(
'1_2_1'
))
self
.
assertTrue
(
'St. Paul'
in
cmap
.
get_hint
(
'1_2_1'
))
class
CodeResponseTest
(
unittest
.
TestCase
):
'''
Test CodeResponse
'''
def
test_update_score
(
self
):
problem_file
=
os
.
path
.
dirname
(
__file__
)
+
"/test_files/coderesponse.xml"
test_lcp
=
lcp
.
LoncapaProblem
(
open
(
problem_file
)
.
read
(),
'1'
,
system
=
i4xs
)
# CodeResponse requires internal CorrectMap state. Build it now in the 'queued' state
old_cmap
=
CorrectMap
()
answer_ids
=
sorted
(
test_lcp
.
get_question_answers
()
.
keys
())
numAnswers
=
len
(
answer_ids
)
for
i
in
range
(
numAnswers
):
old_cmap
.
update
(
CorrectMap
(
answer_id
=
answer_ids
[
i
],
queuekey
=
1000
+
i
))
# Message format inherited from ExternalResponse
correct_score_msg
=
"<edxgrade><awarddetail>EXACT_ANS</awarddetail><message>MESSAGE</message></edxgrade>"
incorrect_score_msg
=
"<edxgrade><awarddetail>WRONG_FORMAT</awarddetail><message>MESSAGE</message></edxgrade>"
xserver_msgs
=
{
'correct'
:
correct_score_msg
,
'incorrect'
:
incorrect_score_msg
,
}
# Incorrect queuekey, state should not be updated
for
correctness
in
[
'correct'
,
'incorrect'
]:
test_lcp
.
correct_map
=
CorrectMap
()
test_lcp
.
correct_map
.
update
(
old_cmap
)
# Deep copy
test_lcp
.
update_score
(
xserver_msgs
[
correctness
],
queuekey
=
0
)
self
.
assertEquals
(
test_lcp
.
correct_map
.
get_dict
(),
old_cmap
.
get_dict
())
# Deep comparison
for
i
in
range
(
numAnswers
):
self
.
assertTrue
(
test_lcp
.
correct_map
.
is_queued
(
answer_ids
[
i
]))
# Should be still queued, since message undelivered
# Correct queuekey, state should be updated
for
correctness
in
[
'correct'
,
'incorrect'
]:
for
i
in
range
(
numAnswers
):
# Target specific answer_id's
test_lcp
.
correct_map
=
CorrectMap
()
test_lcp
.
correct_map
.
update
(
old_cmap
)
new_cmap
=
CorrectMap
()
new_cmap
.
update
(
old_cmap
)
new_cmap
.
set
(
answer_id
=
answer_ids
[
i
],
correctness
=
correctness
,
msg
=
'MESSAGE'
,
queuekey
=
None
)
test_lcp
.
update_score
(
xserver_msgs
[
correctness
],
queuekey
=
1000
+
i
)
self
.
assertEquals
(
test_lcp
.
correct_map
.
get_dict
(),
new_cmap
.
get_dict
())
for
j
in
range
(
numAnswers
):
if
j
==
i
:
self
.
assertFalse
(
test_lcp
.
correct_map
.
is_queued
(
answer_ids
[
j
]))
# Should be dequeued, message delivered
else
:
self
.
assertTrue
(
test_lcp
.
correct_map
.
is_queued
(
answer_ids
[
j
]))
# Should be queued, message undelivered
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Grading tests
# Grading tests
...
...
common/lib/xmodule/tests/test_files/coderesponse.xml
0 → 100644
View file @
49ee1296
<problem>
<text>
<h2>
Code response
</h2>
<p>
</p>
<text>
Write a program to compute the square of a number
<coderesponse
tests=
"repeat:2,generate"
>
<textbox
rows=
"10"
cols=
"70"
mode=
"python"
/>
<answer>
<![CDATA[
initial_display = """
def square(n):
"""
answer = """
def square(n):
return n**2
"""
preamble = """
import sys, time
"""
test_program = """
import random
import operator
def testSquare(n = None):
if n is None:
n = random.randint(2, 20)
print 'Test is: square(%d)'%n
return str(square(n))
def main():
f = os.fdopen(3,'w')
test = int(sys.argv[1])
rndlist = map(int,os.getenv('rndlist').split(','))
random.seed(rndlist[0])
if test == 1: f.write(testSquare(0))
elif test == 2: f.write(testSquare(1))
else: f.write(testSquare())
f.close()
main()
sys.exit(0)
"""
]]>
</answer>
</coderesponse>
</text>
<text>
Write a program to compute the cube of a number
<coderesponse
tests=
"repeat:2,generate"
>
<textbox
rows=
"10"
cols=
"70"
mode=
"python"
/>
<answer>
<![CDATA[
initial_display = """
def cube(n):
"""
answer = """
def cube(n):
return n**3
"""
preamble = """
import sys, time
"""
test_program = """
import random
import operator
def testCube(n = None):
if n is None:
n = random.randint(2, 20)
print 'Test is: cube(%d)'%n
return str(cube(n))
def main():
f = os.fdopen(3,'w')
test = int(sys.argv[1])
rndlist = map(int,os.getenv('rndlist').split(','))
random.seed(rndlist[0])
if test == 1: f.write(testCube(0))
elif test == 2: f.write(testCube(1))
else: f.write(testCube())
f.close()
main()
sys.exit(0)
"""
]]>
</answer>
</coderesponse>
</text>
</text>
</problem>
common/lib/xmodule/xmodule/capa_module.py
View file @
49ee1296
...
@@ -323,8 +323,18 @@ class CapaModule(XModule):
...
@@ -323,8 +323,18 @@ class CapaModule(XModule):
raise
self
.
system
.
exception404
raise
self
.
system
.
exception404
def
update_score
(
self
,
get
):
def
update_score
(
self
,
get
):
"""
Delivers grading response (e.g. from asynchronous code checking) to
the capa problem, so its score can be updated
'get' must have a field 'response' which is a string that contains the
grader's response
No ajax return is needed. Return empty dict.
"""
queuekey
=
get
[
'queuekey'
]
score_msg
=
get
[
'response'
]
score_msg
=
get
[
'response'
]
self
.
lcp
.
update_score
(
score_msg
)
self
.
lcp
.
update_score
(
score_msg
,
queuekey
)
return
dict
()
# No AJAX return is needed
return
dict
()
# No AJAX return is needed
...
@@ -424,7 +434,8 @@ class CapaModule(XModule):
...
@@ -424,7 +434,8 @@ class CapaModule(XModule):
if
not
correct_map
.
is_correct
(
answer_id
):
if
not
correct_map
.
is_correct
(
answer_id
):
success
=
'incorrect'
success
=
'incorrect'
# log this in the track_function
# NOTE: We are logging both full grading and queued-grading submissions. In the latter,
# 'success' will always be incorrect
event_info
[
'correct_map'
]
=
correct_map
.
get_dict
()
event_info
[
'correct_map'
]
=
correct_map
.
get_dict
()
event_info
[
'success'
]
=
success
event_info
[
'success'
]
=
success
self
.
system
.
track_function
(
'save_problem_check'
,
event_info
)
self
.
system
.
track_function
(
'save_problem_check'
,
event_info
)
...
...
lms/djangoapps/courseware/module_render.py
View file @
49ee1296
...
@@ -4,8 +4,10 @@ import logging
...
@@ -4,8 +4,10 @@ import logging
from
django.conf
import
settings
from
django.conf
import
settings
from
django.http
import
Http404
from
django.http
import
Http404
from
django.http
import
HttpResponse
from
django.http
import
HttpResponse
from
django.views.decorators.csrf
import
csrf_exempt
from
functools
import
wraps
from
functools
import
wraps
from
django.contrib.auth.models
import
User
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
mitxmako.shortcuts
import
render_to_string
from
mitxmako.shortcuts
import
render_to_string
from
models
import
StudentModule
,
StudentModuleCache
from
models
import
StudentModule
,
StudentModuleCache
...
@@ -33,6 +35,8 @@ class I4xSystem(object):
...
@@ -33,6 +35,8 @@ class I4xSystem(object):
Create a closure around the system environment.
Create a closure around the system environment.
ajax_url - the url where ajax calls to the encapsulating module go.
ajax_url - the url where ajax calls to the encapsulating module go.
xqueue_callback_url - the url where external queueing system (e.g. for grading)
returns its response
track_function - function of (event_type, event), intended for logging
track_function - function of (event_type, event), intended for logging
or otherwise tracking the event.
or otherwise tracking the event.
TODO: Not used, and has inconsistent args in different
TODO: Not used, and has inconsistent args in different
...
@@ -208,7 +212,7 @@ def get_module(user, request, location, student_module_cache, position=None):
...
@@ -208,7 +212,7 @@ def get_module(user, request, location, student_module_cache, position=None):
# Setup system context for module instance
# Setup system context for module instance
ajax_url
=
settings
.
MITX_ROOT_URL
+
'/modx/'
+
descriptor
.
location
.
url
()
+
'/'
ajax_url
=
settings
.
MITX_ROOT_URL
+
'/modx/'
+
descriptor
.
location
.
url
()
+
'/'
xqueue_callback_url
=
settings
.
MITX_ROOT_URL
+
'/xqueue/'
+
user
.
username
+
'/'
+
descriptor
.
location
.
url
()
+
'/'
xqueue_callback_url
=
settings
.
MITX_ROOT_URL
+
'/xqueue/'
+
str
(
user
.
id
)
+
'/'
+
descriptor
.
location
.
url
()
+
'/'
def
_get_module
(
location
):
def
_get_module
(
location
):
(
module
,
_
,
_
,
_
)
=
get_module
(
user
,
request
,
location
,
student_module_cache
,
position
)
(
module
,
_
,
_
,
_
)
=
get_module
(
user
,
request
,
location
,
student_module_cache
,
position
)
...
@@ -324,11 +328,9 @@ def add_histogram(module):
...
@@ -324,11 +328,9 @@ def add_histogram(module):
module
.
get_html
=
get_html
module
.
get_html
=
get_html
return
module
return
module
# THK: TEMPORARY BYPASS OF AUTH!
# TODO: TEMPORARY BYPASS OF AUTH!
from
django.views.decorators.csrf
import
csrf_exempt
from
django.contrib.auth.models
import
User
@csrf_exempt
@csrf_exempt
def
xqueue_callback
(
request
,
user
name
,
id
,
dispatch
):
def
xqueue_callback
(
request
,
user
id
,
id
,
dispatch
):
# Parse xqueue response
# Parse xqueue response
get
=
request
.
POST
.
copy
()
get
=
request
.
POST
.
copy
()
try
:
try
:
...
@@ -336,12 +338,9 @@ def xqueue_callback(request, username, id, dispatch):
...
@@ -336,12 +338,9 @@ def xqueue_callback(request, username, id, dispatch):
except
Exception
as
err
:
except
Exception
as
err
:
msg
=
"Error in xqueue_callback
%
s: Invalid return format"
%
err
msg
=
"Error in xqueue_callback
%
s: Invalid return format"
%
err
raise
Exception
(
msg
)
raise
Exception
(
msg
)
# Should proceed only when the request timestamp is more recent than problem timestamp
timestamp
=
header
[
'timestamp'
]
# Retrieve target StudentModule
# Retrieve target StudentModule
user
=
User
.
objects
.
get
(
username
=
username
)
user
=
User
.
objects
.
get
(
id
=
userid
)
student_module_cache
=
StudentModuleCache
(
user
,
modulestore
()
.
get_item
(
id
))
student_module_cache
=
StudentModuleCache
(
user
,
modulestore
()
.
get_item
(
id
))
instance
,
instance_module
,
shared_module
,
module_type
=
get_module
(
request
.
user
,
request
,
id
,
student_module_cache
)
instance
,
instance_module
,
shared_module
,
module_type
=
get_module
(
request
.
user
,
request
,
id
,
student_module_cache
)
...
@@ -354,6 +353,10 @@ def xqueue_callback(request, username, id, dispatch):
...
@@ -354,6 +353,10 @@ def xqueue_callback(request, username, id, dispatch):
oldgrade
=
instance_module
.
grade
oldgrade
=
instance_module
.
grade
old_instance_state
=
instance_module
.
state
old_instance_state
=
instance_module
.
state
# Transfer 'queuekey' from xqueue response header to 'get'. This is required to
# use the interface defined by 'handle_ajax'
get
.
update
({
'queuekey'
:
header
[
'queuekey'
]})
# We go through the "AJAX" path
# We go through the "AJAX" path
# So far, the only dispatch from xqueue will be 'score_update'
# So far, the only dispatch from xqueue will be 'score_update'
try
:
try
:
...
...
lms/urls.py
View file @
49ee1296
...
@@ -95,7 +95,7 @@ if settings.COURSEWARE_ENABLED:
...
@@ -95,7 +95,7 @@ if settings.COURSEWARE_ENABLED:
url
(
r'^masquerade/'
,
include
(
'masquerade.urls'
)),
url
(
r'^masquerade/'
,
include
(
'masquerade.urls'
)),
url
(
r'^jumpto/(?P<probname>[^/]+)/$'
,
'courseware.views.jump_to'
),
url
(
r'^jumpto/(?P<probname>[^/]+)/$'
,
'courseware.views.jump_to'
),
url
(
r'^modx/(?P<id>.*?)/(?P<dispatch>[^/]*)$'
,
'courseware.module_render.modx_dispatch'
),
#reset_problem'),
url
(
r'^modx/(?P<id>.*?)/(?P<dispatch>[^/]*)$'
,
'courseware.module_render.modx_dispatch'
),
#reset_problem'),
url
(
r'^xqueue/(?P<user
name
>[^/]*)/(?P<id>.*?)/(?P<dispatch>[^/]*)$'
,
'courseware.module_render.xqueue_callback'
),
url
(
r'^xqueue/(?P<user
id
>[^/]*)/(?P<id>.*?)/(?P<dispatch>[^/]*)$'
,
'courseware.module_render.xqueue_callback'
),
url
(
r'^change_setting$'
,
'student.views.change_setting'
),
url
(
r'^change_setting$'
,
'student.views.change_setting'
),
url
(
r'^s/(?P<template>[^/]*)$'
,
'static_template_view.views.auth_index'
),
url
(
r'^s/(?P<template>[^/]*)$'
,
'static_template_view.views.auth_index'
),
# url(r'^course_info/$', 'student.views.courseinfo'),
# url(r'^course_info/$', 'student.views.courseinfo'),
...
...
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