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
093ac9d1
Commit
093ac9d1
authored
Jul 16, 2012
by
kimth
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
CorrectMap in LMS keeps track of problems as being queued
parent
838a8624
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
65 additions
and
32 deletions
+65
-32
common/lib/capa/capa/capa_problem.py
+14
-3
common/lib/capa/capa/correctmap.py
+11
-1
common/lib/capa/capa/responsetypes.py
+26
-19
common/lib/xmodule/xmodule/capa_module.py
+7
-5
lms/djangoapps/courseware/module_render.py
+7
-4
No files found.
common/lib/capa/capa/capa_problem.py
View file @
093ac9d1
...
...
@@ -179,21 +179,32 @@ class LoncapaProblem(object):
return
{
'score'
:
correct
,
'total'
:
self
.
get_max_score
()}
def
update_score
(
self
,
score_msg
):
def
update_score
(
self
,
score_msg
,
queuekey
):
'''
Deliver grading response (e.g. from async code checking) to
the specific ResponseType
the specific ResponseType
that requested grading
Returns an updated CorrectMap
'''
oldcmap
=
self
.
correct_map
newcmap
=
CorrectMap
()
for
responder
in
self
.
responders
.
values
():
if
hasattr
(
responder
,
'update_score'
):
# TODO: Is this the best way to target 'update_score' of CodeResponse?
results
=
responder
.
update_score
(
score_msg
)
results
=
responder
.
update_score
(
score_msg
,
oldcmap
,
queuekey
)
newcmap
.
update
(
results
)
self
.
correct_map
=
newcmap
return
newcmap
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
):
'''
Grade student responses. Called by capa_module.check_problem.
...
...
common/lib/capa/capa/correctmap.py
View file @
093ac9d1
...
...
@@ -14,6 +14,7 @@ class CorrectMap(object):
- 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)
- hintmode : one of (None,'on_request','always') criteria for displaying hint
- queuekey : a random integer for xqueue_callback verification
Behaves as a dict.
'''
...
...
@@ -29,13 +30,14 @@ class CorrectMap(object):
def
__iter__
(
self
):
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
:
self
.
cmap
[
answer_id
]
=
{
'correctness'
:
correctness
,
'npoints'
:
npoints
,
'msg'
:
msg
,
'hint'
:
hint
,
'hintmode'
:
hintmode
,
'queuekey'
:
queuekey
,
}
def
__repr__
(
self
):
...
...
@@ -63,6 +65,14 @@ class CorrectMap(object):
if
answer_id
in
self
.
cmap
:
return
self
.
cmap
[
answer_id
][
'correctness'
]
==
'correct'
return
None
def
is_queued
(
self
,
answer_id
):
if
answer_id
in
self
.
cmap
:
return
self
.
cmap
[
answer_id
][
'queuekey'
]
is
not
None
return
None
def
is_right_queuekey
(
self
,
answer_id
,
test_key
):
if
answer_id
in
self
.
cmap
:
return
self
.
cmap
[
answer_id
][
'queuekey'
]
==
test_key
return
None
def
get_npoints
(
self
,
answer_id
):
if
self
.
is_correct
(
answer_id
):
npoints
=
self
.
cmap
[
answer_id
]
.
get
(
'npoints'
,
1
)
# default to 1 point if correct
...
...
common/lib/capa/capa/responsetypes.py
View file @
093ac9d1
...
...
@@ -18,7 +18,6 @@ import re
import
requests
import
traceback
import
abc
import
time
# specific library imports
from
calc
import
evaluator
,
UndefinedVariable
...
...
@@ -709,7 +708,6 @@ class CodeResponse(LoncapaResponse):
self
.
url
=
xml
.
get
(
'url'
,
"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
:
...
...
@@ -727,7 +725,7 @@ class CodeResponse(LoncapaResponse):
def
get_score
(
self
,
student_answers
):
idset
=
sorted
(
self
.
answer_ids
)
try
:
submission
=
[
student_answers
[
k
]
for
k
in
idset
]
except
Exception
as
err
:
...
...
@@ -737,12 +735,16 @@ class CodeResponse(LoncapaResponse):
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
()
r
,
queuekey
=
self
.
_send_to_queue
(
extra_payload
)
# TODO: Perform checks on the xqueue response
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
)
return
cmap
def
update_score
(
self
,
score_msg
,
oldcmap
,
queuekey
):
# Parse 'score_msg' as XML
try
:
rxml
=
etree
.
fromstring
(
score_msg
)
...
...
@@ -752,7 +754,6 @@ class CodeResponse(LoncapaResponse):
# 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'
,
...
...
@@ -761,13 +762,17 @@ class CodeResponse(LoncapaResponse):
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
)
# Replace 'oldcmap' with new grading results if queuekey matches
# If queuekey does not match, we keep waiting for the score_msg that will match
for
answer_id
in
idset
:
if
oldcmap
.
is_right_queuekey
(
answer_id
,
queuekey
):
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
)
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
# does NOT require a queue submission, and the answer is computed (extracted from problem XML) locally.
...
...
@@ -794,10 +799,12 @@ class CodeResponse(LoncapaResponse):
# Prepare payload
xmlstr
=
etree
.
tostring
(
self
.
xml
,
pretty_print
=
True
)
header
=
{
'return_url'
:
self
.
system
.
xqueue_callback_url
}
# header.update({'timestamp': time.time()})
random
.
seed
()
header
.
update
({
'key'
:
random
.
randint
(
0
,
2
**
32
-
1
)})
payload
=
{
'xqueue_header'
:
json
.
dumps
(
header
),
# 'xqueue_header' should eventually be derived from xqueue.queue_common.HEADER_TAG or something similar
queuekey
=
random
.
randint
(
0
,
2
**
32
-
1
)
header
.
update
({
'queuekey'
:
queuekey
})
payload
=
{
'xqueue_header'
:
json
.
dumps
(
header
),
# TODO: 'xqueue_header' should eventually be derived from config file
'xml'
:
xmlstr
,
'edX_cmd'
:
'get_score'
,
'edX_tests'
:
self
.
tests
,
...
...
@@ -813,7 +820,7 @@ class CodeResponse(LoncapaResponse):
log
.
error
(
msg
)
raise
Exception
(
msg
)
return
r
return
r
,
queuekey
#-----------------------------------------------------------------------------
...
...
common/lib/xmodule/xmodule/capa_module.py
View file @
093ac9d1
...
...
@@ -332,8 +332,9 @@ class CapaModule(XModule):
No ajax return is needed. Return empty dict.
"""
queuekey
=
get
[
'queuekey'
]
score_msg
=
get
[
'response'
]
self
.
lcp
.
update_score
(
score_msg
)
self
.
lcp
.
update_score
(
score_msg
,
queuekey
)
return
dict
()
# No AJAX return is needed
...
...
@@ -433,10 +434,11 @@ class CapaModule(XModule):
if
not
correct_map
.
is_correct
(
answer_id
):
success
=
'incorrect'
# log this in the track_function
event_info
[
'correct_map'
]
=
correct_map
.
get_dict
()
event_info
[
'success'
]
=
success
self
.
system
.
track_function
(
'save_problem_check'
,
event_info
)
# log this in the track_function, ONLY if a full grading has been performed (e.g. not queueing)
if
not
self
.
lcp
.
is_queued
():
event_info
[
'correct_map'
]
=
correct_map
.
get_dict
()
event_info
[
'success'
]
=
success
self
.
system
.
track_function
(
'save_problem_check'
,
event_info
)
# render problem into HTML
html
=
self
.
get_problem_html
(
encapsulate
=
False
)
...
...
lms/djangoapps/courseware/module_render.py
View file @
093ac9d1
...
...
@@ -33,6 +33,8 @@ class I4xSystem(object):
Create a closure around the system environment.
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
or otherwise tracking the event.
TODO: Not used, and has inconsistent args in different
...
...
@@ -324,7 +326,7 @@ def add_histogram(module):
module
.
get_html
=
get_html
return
module
# T
HK
: TEMPORARY BYPASS OF AUTH!
# T
ODO
: TEMPORARY BYPASS OF AUTH!
from
django.views.decorators.csrf
import
csrf_exempt
from
django.contrib.auth.models
import
User
@csrf_exempt
...
...
@@ -336,9 +338,6 @@ def xqueue_callback(request, username, id, dispatch):
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
)
...
...
@@ -354,6 +353,10 @@ def xqueue_callback(request, username, id, dispatch):
oldgrade
=
instance_module
.
grade
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
# So far, the only dispatch from xqueue will be 'score_update'
try
:
...
...
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