diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index 27801ad871..5cf45adce2 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -1202,7 +1202,7 @@ class CodeResponse(LoncapaResponse):
'''
Grader reply is a JSON-dump of the following dict
{ 'correct': True/False,
- 'score': # TODO -- Partial grading
+ 'score': Numeric value (floating point is okay) to assign to answer
'msg': grader_msg }
Returns (valid_score_msg, correct, score, msg):
diff --git a/common/lib/xmodule/xmodule/tests/__init__.py b/common/lib/xmodule/xmodule/tests/__init__.py
index 5d70056d2a..bd5d35c75f 100644
--- a/common/lib/xmodule/xmodule/tests/__init__.py
+++ b/common/lib/xmodule/xmodule/tests/__init__.py
@@ -19,6 +19,7 @@ import capa.calc as calc
import capa.capa_problem as lcp
from capa.correctmap import CorrectMap
from capa.util import convert_files_to_filenames
+from datetime import datetime
from xmodule import graders, x_module
from xmodule.x_module import ModuleSystem
from xmodule.graders import Score, aggregate_scores
@@ -283,30 +284,61 @@ class CodeResponseTest(unittest.TestCase):
'''
Test CodeResponse
'''
+ @staticmethod
+ def make_queuestate(key, time):
+ timestr = datetime.strftime(time,'%Y%m%d%H%M%S')
+ return (key, timestr)
+
+ def test_is_queued(self):
+ '''
+ Simple test of whether LoncapaProblem knows when it's been queued
+ '''
+ problem_file = os.path.dirname(__file__) + "/test_files/coderesponse.xml"
+ test_lcp = lcp.LoncapaProblem(open(problem_file).read(), '1', system=i4xs)
+
+ answer_ids = sorted(test_lcp.get_question_answers().keys())
+ num_answers = len(answer_ids)
+
+ # CodeResponse requires internal CorrectMap state. Build it now in the unqueued state
+ cmap = CorrectMap()
+ for i in range(num_answers):
+ cmap.update(CorrectMap(answer_id=answer_ids[i], queuestate=None))
+ test_lcp.correct_map.update(cmap)
+
+ self.assertEquals(test_lcp.is_queued(), False)
+
+ # Now we queue the LCP
+ cmap = CorrectMap()
+ for i in range(num_answers):
+ queuestate = CodeResponseTest.make_queuestate(i, datetime.now())
+ cmap.update(CorrectMap(answer_id=answer_ids[i], queuestate=queuestate))
+ test_lcp.correct_map.update(cmap)
+
+ self.assertEquals(test_lcp.is_queued(), True)
+
def test_update_score(self):
+ '''
+ Test whether LoncapaProblem.update_score can deliver queued result to the right subproblem
+ '''
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):
+ num_answers = len(answer_ids)
+
+ # CodeResponse requires internal CorrectMap state. Build it now in the queued state
+ old_cmap = CorrectMap()
+ for i in range(num_answers):
queuekey = 1000 + i
- queuestate = (queuekey, '')
+ queuestate = CodeResponseTest.make_queuestate(1000+i, datetime.now())
old_cmap.update(CorrectMap(answer_id=answer_ids[i], queuestate=queuestate))
- # TODO: Message format inherited from ExternalResponse
- #correct_score_msg = "EXACT_ANSMESSAGE"
- #incorrect_score_msg = "WRONG_FORMATMESSAGE"
-
- # New message format common to external graders
+ # Message format common to external graders
correct_score_msg = json.dumps({'correct':True, 'score':1, 'msg':'MESSAGE'})
incorrect_score_msg = json.dumps({'correct':False, 'score':0, 'msg':'MESSAGE'})
xserver_msgs = {'correct': correct_score_msg,
- 'incorrect': incorrect_score_msg,
- }
+ 'incorrect': incorrect_score_msg,}
# Incorrect queuekey, state should not be updated
for correctness in ['correct', 'incorrect']:
@@ -316,12 +348,12 @@ class CodeResponseTest(unittest.TestCase):
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):
+ for i in range(num_answers):
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
+ for i in range(num_answers): # Target specific answer_id's
test_lcp.correct_map = CorrectMap()
test_lcp.correct_map.update(old_cmap)
@@ -333,13 +365,51 @@ class CodeResponseTest(unittest.TestCase):
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):
+ for j in range(num_answers):
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
+
+
+ def test_recentmost_queuetime(self):
+ '''
+ Test whether the LoncapaProblem knows about the time of queue requests
+ '''
+ problem_file = os.path.dirname(__file__) + "/test_files/coderesponse.xml"
+ test_lcp = lcp.LoncapaProblem(open(problem_file).read(), '1', system=i4xs)
+
+ answer_ids = sorted(test_lcp.get_question_answers().keys())
+ num_answers = len(answer_ids)
+
+ # CodeResponse requires internal CorrectMap state. Build it now in the unqueued state
+ cmap = CorrectMap()
+ for i in range(num_answers):
+ cmap.update(CorrectMap(answer_id=answer_ids[i], queuestate=None))
+ test_lcp.correct_map.update(cmap)
+
+ self.assertEquals(test_lcp.get_recentmost_queuetime(), None)
+
+ # CodeResponse requires internal CorrectMap state. Build it now in the queued state
+ cmap = CorrectMap()
+ answer_ids = sorted(test_lcp.get_question_answers().keys())
+ num_answers = len(answer_ids)
+ for i in range(num_answers):
+ queuekey = 1000 + i
+ latest_timestamp = datetime.now()
+ queuestate = CodeResponseTest.make_queuestate(1000+i, latest_timestamp)
+ cmap.update(CorrectMap(answer_id=answer_ids[i], queuestate=queuestate))
+ test_lcp.correct_map.update(cmap)
+
+ # Queue state only tracks up to second
+ latest_timestamp = datetime.strptime(datetime.strftime(latest_timestamp,'%Y%m%d%H%M%S'),'%Y%m%d%H%M%S')
+
+ self.assertEquals(test_lcp.get_recentmost_queuetime(), latest_timestamp)
def test_convert_files_to_filenames(self):
+ '''
+ Test whether file objects are converted to filenames without altering other structures
+ '''
problem_file = os.path.dirname(__file__) + "/test_files/coderesponse.xml"
fp = open(problem_file)
answers_with_file = {'1_2_1': 'String-based answer',
diff --git a/common/lib/xmodule/xmodule/tests/test_files/coderesponse.xml b/common/lib/xmodule/xmodule/tests/test_files/coderesponse.xml
index 42b6e0a54a..1c0bf8d4e6 100644
--- a/common/lib/xmodule/xmodule/tests/test_files/coderesponse.xml
+++ b/common/lib/xmodule/xmodule/tests/test_files/coderesponse.xml
@@ -9,91 +9,23 @@
Write a program to compute the square of a number
-
-
+
+ def square(x):
+ answer
+ grader stuff
+
-Write a program to compute the cube of a number
+Write a program to compute the square of a number
-
-
+
+ def square(x):
+ answer
+ grader stuff
+
diff --git a/common/lib/xmodule/xmodule/tests/test_files/coderesponse_externalresponseformat.xml b/common/lib/xmodule/xmodule/tests/test_files/coderesponse_externalresponseformat.xml
new file mode 100644
index 0000000000..42b6e0a54a
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/test_files/coderesponse_externalresponseformat.xml
@@ -0,0 +1,101 @@
+
+
+Code response
+
+
+
+
+
+Write a program to compute the square of a number
+
+
+
+
+
+
+
+
+Write a program to compute the cube of a number
+
+
+
+
+
+
+
+
+
diff --git a/lms/envs/test.py b/lms/envs/test.py
index 1ab3f550b8..c164889d79 100644
--- a/lms/envs/test.py
+++ b/lms/envs/test.py
@@ -58,7 +58,7 @@ XQUEUE_INTERFACE = {
},
"basic_auth": ('anant', 'agarwal'),
}
-
+XQUEUE_WAITTIME_BETWEEN_REQUESTS = 5 # seconds
# TODO (cpennington): We need to figure out how envs/test.py can inject things
# into common.py so that we don't have to repeat this sort of thing