Commit 77f928d1 by kimth

Queuestate records both secret key and time of queue request

parent f9616c6e
...@@ -202,11 +202,7 @@ class LoncapaProblem(object): ...@@ -202,11 +202,7 @@ class LoncapaProblem(object):
''' '''
Returns True if any part of the problem has been submitted to an external queue Returns True if any part of the problem has been submitted to an external queue
''' '''
queued = False return any([self.correct_map.is_queued(answer_id) for answer_id in self.correct_map])
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):
''' '''
......
...@@ -15,7 +15,8 @@ class CorrectMap(object): ...@@ -15,7 +15,8 @@ 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 - queuestate : Tuple (key, time) where key is a secret string, and time is a string dump
of a DateTime object in the format '%Y%m%d%H%M%S'. Is None when not queued
Behaves as a dict. Behaves as a dict.
''' '''
...@@ -31,14 +32,14 @@ class CorrectMap(object): ...@@ -31,14 +32,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, queuekey=None): def set(self, answer_id=None, correctness=None, npoints=None, msg='', hint='', hintmode=None, queuestate=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, 'queuestate': queuestate,
} }
def __repr__(self): def __repr__(self):
...@@ -67,10 +68,10 @@ class CorrectMap(object): ...@@ -67,10 +68,10 @@ class CorrectMap(object):
return None return None
def is_queued(self, answer_id): def is_queued(self, answer_id):
return answer_id in self.cmap and self.cmap[answer_id]['queuekey'] is not None return answer_id in self.cmap and self.cmap[answer_id]['queuestate'] is not None
def is_right_queuekey(self, answer_id, test_key): def is_right_queuekey(self, answer_id, test_key):
return answer_id in self.cmap and self.cmap[answer_id]['queuekey'] == test_key return self.is_queued(answer_id) and self.cmap[answer_id]['queuestate'][0] == test_key
def get_npoints(self, answer_id): def get_npoints(self, answer_id):
npoints = self.get_property(answer_id, 'npoints') npoints = self.get_property(answer_id, 'npoints')
......
...@@ -26,6 +26,7 @@ import xml.sax.saxutils as saxutils ...@@ -26,6 +26,7 @@ import xml.sax.saxutils as saxutils
# specific library imports # specific library imports
from calc import evaluator, UndefinedVariable from calc import evaluator, UndefinedVariable
from correctmap import CorrectMap from correctmap import CorrectMap
from datetime import datetime
from util import * from util import *
from lxml import etree from lxml import etree
from lxml.html.soupparser import fromstring as fromstring_bs # uses Beautiful Soup!!! FIXME? from lxml.html.soupparser import fromstring as fromstring_bs # uses Beautiful Soup!!! FIXME?
...@@ -1026,7 +1027,7 @@ class CodeResponse(LoncapaResponse): ...@@ -1026,7 +1027,7 @@ class CodeResponse(LoncapaResponse):
TODO: Determines whether in synchronous or asynchronous (queued) mode TODO: Determines whether in synchronous or asynchronous (queued) mode
''' '''
xml = self.xml xml = self.xml
self.url = xml.get('url', None) # XML can override external resource (grader/queue) URL self.url = xml.get('url', None) # TODO: XML can override external resource (grader/queue) URL
self.queue_name = xml.get('queuename', self.system.xqueue['default_queuename']) self.queue_name = xml.get('queuename', self.system.xqueue['default_queuename'])
# VS[compat]: # VS[compat]:
...@@ -1128,7 +1129,7 @@ class CodeResponse(LoncapaResponse): ...@@ -1128,7 +1129,7 @@ class CodeResponse(LoncapaResponse):
xheader = xqueue_interface.make_xheader(lms_callback_url=self.system.xqueue['callback_url'], xheader = xqueue_interface.make_xheader(lms_callback_url=self.system.xqueue['callback_url'],
lms_key=queuekey, lms_key=queuekey,
queue_name=self.queue_name) queue_name=self.queue_name)
# Generate body # Generate body
if is_list_of_files(submission): if is_list_of_files(submission):
self.context.update({'submission': queuekey}) # For tracking. TODO: May want to record something else here self.context.update({'submission': queuekey}) # For tracking. TODO: May want to record something else here
...@@ -1148,16 +1149,17 @@ class CodeResponse(LoncapaResponse): ...@@ -1148,16 +1149,17 @@ class CodeResponse(LoncapaResponse):
(error, msg) = qinterface.send_to_queue(header=xheader, (error, msg) = qinterface.send_to_queue(header=xheader,
body=json.dumps(contents)) body=json.dumps(contents))
queuestate = (queuekey,'')
cmap = CorrectMap() cmap = CorrectMap()
if error: if error:
cmap.set(self.answer_id, queuekey=None, cmap.set(self.answer_id, queuestate=None,
msg='Unable to deliver your submission to grader. (Reason: %s.) Please try again later.' % msg) msg='Unable to deliver your submission to grader. (Reason: %s.) Please try again later.' % msg)
else: else:
# Queueing mechanism flags: # Queueing mechanism flags:
# 1) Backend: Non-null CorrectMap['queuekey'] indicates that the problem has been queued # 1) Backend: Non-null CorrectMap['queuestate'] indicates that the problem has been queued
# 2) Frontend: correctness='incomplete' eventually trickles down through inputtypes.textbox # 2) Frontend: correctness='incomplete' eventually trickles down through inputtypes.textbox
# and .filesubmission to inform the browser to poll the LMS # and .filesubmission to inform the browser to poll the LMS
cmap.set(self.answer_id, queuekey=queuekey, correctness='incomplete', msg=msg) cmap.set(self.answer_id, queuestate=queuestate, correctness='incomplete', msg=msg)
return cmap return cmap
...@@ -1180,7 +1182,7 @@ class CodeResponse(LoncapaResponse): ...@@ -1180,7 +1182,7 @@ class CodeResponse(LoncapaResponse):
points = 0 points = 0
elif points > self.maxpoints[self.answer_id]: elif points > self.maxpoints[self.answer_id]:
points = self.maxpoints[self.answer_id] points = self.maxpoints[self.answer_id]
oldcmap.set(self.answer_id, npoints=points, correctness=correctness, msg=msg.replace(' ', ' '), queuekey=None) # Queuekey is consumed oldcmap.set(self.answer_id, npoints=points, correctness=correctness, msg=msg.replace(' ', ' '), queuestate=None) # Queuestate is consumed
else: else:
log.debug('CodeResponse: queuekey %s does not match for answer_id=%s.' % (queuekey, self.answer_id)) log.debug('CodeResponse: queuekey %s does not match for answer_id=%s.' % (queuekey, self.answer_id))
......
...@@ -462,6 +462,12 @@ class CapaModule(XModule): ...@@ -462,6 +462,12 @@ class CapaModule(XModule):
self.system.track_function('save_problem_check_fail', event_info) self.system.track_function('save_problem_check_fail', event_info)
raise NotFoundError('Problem must be reset before it can be checked again') raise NotFoundError('Problem must be reset before it can be checked again')
# Problem queued. Student should not be able to submit
'''
if self.lcp.is_queued():
return {'success': False, 'html': 'Already queued'}
'''
try: try:
old_state = self.lcp.get_state() old_state = self.lcp.get_state()
lcp_id = self.lcp.problem_id lcp_id = self.lcp.problem_id
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment