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
6d3516a7
Commit
6d3516a7
authored
Jul 13, 2012
by
kimth
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
CodeResponse for queued code checking
parent
28007b28
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
192 additions
and
3 deletions
+192
-3
common/lib/capa/capa/capa_problem.py
+9
-0
common/lib/capa/capa/responsetypes.py
+120
-1
common/lib/xmodule/xmodule/capa_module.py
+10
-0
lms/djangoapps/courseware/module_render.py
+52
-2
lms/urls.py
+1
-0
No files found.
common/lib/capa/capa/capa_problem.py
View file @
6d3516a7
...
...
@@ -179,6 +179,15 @@ class LoncapaProblem(object):
return
{
'score'
:
correct
,
'total'
:
self
.
get_max_score
()}
def
update_score
(
self
,
score_msg
):
newcmap
=
CorrectMap
()
for
responder
in
self
.
responders
.
values
():
if
hasattr
(
responder
,
'update_score'
):
# Is this the best way to implement 'update_score' for CodeResponse?
results
=
responder
.
update_score
(
score_msg
)
newcmap
.
update
(
results
)
self
.
correct_map
=
newcmap
return
newcmap
def
grade_answers
(
self
,
answers
):
'''
Grade student responses. Called by capa_module.check_problem.
...
...
common/lib/capa/capa/responsetypes.py
View file @
6d3516a7
...
...
@@ -18,6 +18,7 @@ import re
import
requests
import
traceback
import
abc
import
time
# specific library imports
from
calc
import
evaluator
,
UndefinedVariable
...
...
@@ -693,6 +694,124 @@ class SymbolicResponse(CustomResponse):
#-----------------------------------------------------------------------------
class
CodeResponse
(
LoncapaResponse
):
'''
Grade student code using an external server
'''
response_tag
=
'coderesponse'
allowed_inputfields
=
[
'textline'
,
'textbox'
]
def
setup_response
(
self
):
xml
=
self
.
xml
self
.
url
=
xml
.
get
(
'url'
)
or
"http://ec2-50-16-59-149.compute-1.amazonaws.com/xqueue/submit/"
# FIXME -- hardcoded url
answer
=
xml
.
find
(
'answer'
)
if
answer
is
not
None
:
answer_src
=
answer
.
get
(
'src'
)
if
answer_src
is
not
None
:
self
.
code
=
self
.
system
.
filesystem
.
open
(
'src/'
+
answer_src
)
.
read
()
else
:
self
.
code
=
answer
.
text
else
:
# no <answer> stanza; get code from <script>
self
.
code
=
self
.
context
[
'script_code'
]
if
not
self
.
code
:
msg
=
'
%
s: Missing answer script code for externalresponse'
%
unicode
(
self
)
msg
+=
"
\n
See XML source line
%
s"
%
getattr
(
self
.
xml
,
'sourceline'
,
'<unavailable>'
)
raise
LoncapaProblemError
(
msg
)
self
.
tests
=
xml
.
get
(
'tests'
)
def
get_score
(
self
,
student_answers
):
idset
=
sorted
(
self
.
answer_ids
)
try
:
submission
=
[
student_answers
[
k
]
for
k
in
idset
]
except
Exception
as
err
:
log
.
error
(
'Error in CodeResponse
%
s: cannot get student answer for
%
s; student_answers=
%
s'
%
(
err
,
self
.
answer_ids
,
student_answers
))
raise
Exception
(
err
)
self
.
context
.
update
({
'submission'
:
submission
})
extra_payload
=
{
'edX_student_response'
:
json
.
dumps
(
submission
)}
# Should do something -- like update the problem state -- based on the queue response
r
=
self
.
_send_to_queue
(
extra_payload
)
return
CorrectMap
()
def
update_score
(
self
,
score_msg
):
# Parse 'score_msg' as XML
try
:
rxml
=
etree
.
fromstring
(
score_msg
)
except
Exception
as
err
:
msg
=
'Error in CodeResponse
%
s: cannot parse response from xworker r.text=
%
s'
%
(
err
,
score_msg
)
raise
Exception
(
err
)
# The following process is lifted directly from ExternalResponse
idset
=
sorted
(
self
.
answer_ids
)
cmap
=
CorrectMap
()
ad
=
rxml
.
find
(
'awarddetail'
)
.
text
admap
=
{
'EXACT_ANS'
:
'correct'
,
# TODO: handle other loncapa responses
'WRONG_FORMAT'
:
'incorrect'
,
}
self
.
context
[
'correct'
]
=
[
'correct'
]
if
ad
in
admap
:
self
.
context
[
'correct'
][
0
]
=
admap
[
ad
]
# create CorrectMap
for
key
in
idset
:
idx
=
idset
.
index
(
key
)
msg
=
rxml
.
find
(
'message'
)
.
text
.
replace
(
' '
,
' '
)
if
idx
==
0
else
None
cmap
.
set
(
key
,
self
.
context
[
'correct'
][
idx
],
msg
=
msg
)
return
cmap
# 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.
def
get_answers
(
self
):
# Extract the CodeResponse answer from XML
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
]))
# CodeResponse._send_to_queue implements the same interface as defined for ExternalResponse's 'get_score'
def
_send_to_queue
(
self
,
extra_payload
):
# Prepare payload
xmlstr
=
etree
.
tostring
(
self
.
xml
,
pretty_print
=
True
)
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
'xml'
:
xmlstr
,
'edX_cmd'
:
'get_score'
,
'edX_tests'
:
self
.
tests
,
'processor'
:
self
.
code
,
}
payload
.
update
(
extra_payload
)
# Contact queue server
try
:
r
=
requests
.
post
(
self
.
url
,
data
=
payload
)
except
Exception
as
err
:
msg
=
"Error in CodeResponse
%
s: cannot connect to queue server url=
%
s"
%
(
err
,
self
.
url
)
log
.
error
(
msg
)
raise
Exception
(
msg
)
return
r
#-----------------------------------------------------------------------------
class
ExternalResponse
(
LoncapaResponse
):
'''
Grade the students input using an external server.
...
...
@@ -1072,5 +1191,5 @@ class ImageResponse(LoncapaResponse):
# TEMPORARY: List of all response subclasses
# FIXME: To be replaced by auto-registration
__all__
=
[
NumericalResponse
,
FormulaResponse
,
CustomResponse
,
SchematicResponse
,
MultipleChoiceResponse
,
TrueFalseResponse
,
ExternalResponse
,
ImageResponse
,
OptionResponse
,
SymbolicResponse
,
StringResponse
]
__all__
=
[
CodeResponse
,
NumericalResponse
,
FormulaResponse
,
CustomResponse
,
SchematicResponse
,
MultipleChoiceResponse
,
TrueFalseResponse
,
ExternalResponse
,
ImageResponse
,
OptionResponse
,
SymbolicResponse
,
StringResponse
]
common/lib/xmodule/xmodule/capa_module.py
View file @
6d3516a7
...
...
@@ -231,6 +231,9 @@ class CapaModule(XModule):
# Only show the reset button if pressing it will show different values
if
self
.
rerandomize
!=
'always'
:
reset_button
=
False
# THK: for debugging
reset_button
=
True
# User hasn't submitted an answer yet -- we don't want resets
if
not
self
.
lcp
.
done
:
...
...
@@ -275,6 +278,7 @@ class CapaModule(XModule):
'problem_reset'
:
self
.
reset_problem
,
'problem_save'
:
self
.
save_problem
,
'problem_show'
:
self
.
get_answer
,
'score_update'
:
self
.
update_score
,
}
if
dispatch
not
in
handlers
:
...
...
@@ -321,6 +325,12 @@ class CapaModule(XModule):
#TODO: Not 404
raise
self
.
system
.
exception404
def
update_score
(
self
,
get
):
score_msg
=
get
[
'response'
]
self
.
lcp
.
update_score
(
score_msg
)
return
dict
()
# No AJAX return is needed
def
get_answer
(
self
,
get
):
'''
For the "show answer" button.
...
...
lms/djangoapps/courseware/module_render.py
View file @
6d3516a7
...
...
@@ -28,7 +28,7 @@ class I4xSystem(object):
'''
def
__init__
(
self
,
ajax_url
,
track_function
,
get_module
,
render_template
,
replace_urls
,
user
=
None
,
filestore
=
None
):
user
=
None
,
filestore
=
None
,
xqueue_callback_url
=
None
):
'''
Create a closure around the system environment.
...
...
@@ -48,6 +48,7 @@ class I4xSystem(object):
that capa_module can use to fix up the static urls in ajax results.
'''
self
.
ajax_url
=
ajax_url
self
.
xqueue_callback_url
=
xqueue_callback_url
self
.
track_function
=
track_function
self
.
filestore
=
filestore
self
.
get_module
=
get_module
...
...
@@ -207,6 +208,7 @@ def get_module(user, request, location, student_module_cache, position=None):
# Setup system context for module instance
ajax_url
=
settings
.
MITX_ROOT_URL
+
'/modx/'
+
descriptor
.
location
.
url
()
+
'/'
xqueue_callback_url
=
settings
.
MITX_ROOT_URL
+
'/xqueue/'
+
user
.
username
+
'/'
+
descriptor
.
location
.
url
()
+
'/'
def
_get_module
(
location
):
(
module
,
_
,
_
,
_
)
=
get_module
(
user
,
request
,
location
,
student_module_cache
,
position
)
...
...
@@ -218,6 +220,7 @@ def get_module(user, request, location, student_module_cache, position=None):
system
=
I4xSystem
(
track_function
=
make_track_function
(
request
),
render_template
=
render_to_string
,
ajax_url
=
ajax_url
,
xqueue_callback_url
=
xqueue_callback_url
,
# TODO (cpennington): Figure out how to share info between systems
filestore
=
descriptor
.
system
.
resources_fs
,
get_module
=
_get_module
,
...
...
@@ -321,6 +324,53 @@ def add_histogram(module):
module
.
get_html
=
get_html
return
module
# THK: TEMPORARY BYPASS OF AUTH!
from
django.views.decorators.csrf
import
csrf_exempt
from
django.contrib.auth.models
import
User
@csrf_exempt
def
xqueue_callback
(
request
,
username
,
id
,
dispatch
):
# Parse xqueue response
get
=
request
.
POST
.
copy
()
try
:
header
=
json
.
loads
(
get
.
pop
(
'xqueue_header'
)[
0
])
# 'dict'
except
Exception
as
err
:
msg
=
"Error in xqueue_callback
%
s: Invalid return format"
%
err
raise
Exception
(
msg
)
# Should proceed only when the request timestamp is more recent than problem timestamp
timestamp
=
header
[
'timestamp'
]
# Retrieve target StudentModule
user
=
User
.
objects
.
get
(
username
=
username
)
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
)
if
instance_module
is
None
:
log
.
debug
(
"Couldn't find module '
%
s' for user '
%
s'"
,
id
,
request
.
user
)
raise
Http404
oldgrade
=
instance_module
.
grade
old_instance_state
=
instance_module
.
state
# We go through the "AJAX" path
# So far, the only dispatch from xqueue will be 'score_update'
try
:
ajax_return
=
instance
.
handle_ajax
(
dispatch
,
get
)
# Can ignore the "ajax" return in 'xqueue_callback'
except
:
log
.
exception
(
"error processing ajax call"
)
raise
# Save state back to database
instance_module
.
state
=
instance
.
get_instance_state
()
if
instance
.
get_score
():
instance_module
.
grade
=
instance
.
get_score
()[
'score'
]
if
instance_module
.
grade
!=
oldgrade
or
instance_module
.
state
!=
old_instance_state
:
instance_module
.
save
()
return
HttpResponse
(
""
)
def
modx_dispatch
(
request
,
dispatch
=
None
,
id
=
None
):
''' Generic view for extensions. This is where AJAX calls go.
...
...
@@ -339,7 +389,7 @@ def modx_dispatch(request, dispatch=None, id=None):
student_module_cache
=
StudentModuleCache
(
request
.
user
,
modulestore
()
.
get_item
(
id
))
instance
,
instance_module
,
shared_module
,
module_type
=
get_module
(
request
.
user
,
request
,
id
,
student_module_cache
)
if
instance_module
is
None
:
log
.
debug
(
"Couldn't find module '
%
s' for user '
%
s'"
,
id
,
request
.
user
)
...
...
lms/urls.py
View file @
6d3516a7
...
...
@@ -55,6 +55,7 @@ if settings.COURSEWARE_ENABLED:
url
(
r'^masquerade/'
,
include
(
'masquerade.urls'
)),
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'^xqueue/(?P<username>[^/]*)/(?P<id>.*?)/(?P<dispatch>[^/]*)$'
,
'courseware.module_render.xqueue_callback'
),
url
(
r'^change_setting$'
,
'student.views.change_setting'
),
url
(
r'^s/(?P<template>[^/]*)$'
,
'static_template_view.views.auth_index'
),
url
(
r'^book/(?P<page>[^/]*)$'
,
'staticbook.views.index'
),
...
...
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