diff --git a/common/djangoapps/xmodule_modifiers.py b/common/djangoapps/xmodule_modifiers.py index 843d2eaa38..597d74ce6f 100644 --- a/common/djangoapps/xmodule_modifiers.py +++ b/common/djangoapps/xmodule_modifiers.py @@ -92,7 +92,7 @@ def add_histogram(get_html, module): if settings.MITX_FEATURES.get('ENABLE_LMS_MIGRATION'): [filepath, filename] = module.definition.get('filename','') osfs = module.system.filestore - if osfs.exists(filename): + if filename is not None and osfs.exists(filename): filepath = filename # if original, unmangled filename exists then use it (github doesn't like symlinks) data_dir = osfs.root_path.rsplit('/')[-1] edit_link = "https://github.com/MITx/%s/tree/master/%s" % (data_dir,filepath) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 903c697459..0e4d23f572 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -31,7 +31,6 @@ import xqueue_interface log = logging.getLogger('mitx.' + __name__) -qinterface = xqueue_interface.XqueueInterface() #----------------------------------------------------------------------------- # Exceptions @@ -981,7 +980,7 @@ class CodeResponse(LoncapaResponse): def setup_response(self): xml = self.xml - self.queue_name = xml.get('queuename', self.system.xqueue_default_queuename) + self.queue_name = xml.get('queuename', self.system.xqueue['default_queuename']) answer = xml.find('answer') if answer is not None: @@ -1029,10 +1028,11 @@ class CodeResponse(LoncapaResponse): # Prepare xqueue request #------------------------------------------------------------ + qinterface = self.system.xqueue['interface'] # Generate header - queuekey = xqueue_interface.make_hashkey(self.system.seed) - xheader = xqueue_interface.make_xheader(lms_callback_url=self.system.xqueue_callback_url, + queuekey = xqueue_interface.make_hashkey(str(self.system.seed)+self.answer_id) + xheader = xqueue_interface.make_xheader(lms_callback_url=self.system.xqueue['callback_url'], lms_key=queuekey, queue_name=self.queue_name) @@ -1061,7 +1061,7 @@ class CodeResponse(LoncapaResponse): msg='Unable to deliver your submission to grader. (Reason: %s.) Please try again later.' % msg) else: # Non-null CorrectMap['queuekey'] indicates that the problem has been queued - cmap.set(self.answer_id, queuekey=queuekey, msg='Submitted to grader') + cmap.set(self.answer_id, queuekey=queuekey, msg='Submitted to grader. (Queue length: %s)' % msg) return cmap diff --git a/common/lib/capa/capa/templates/textbox.html b/common/lib/capa/capa/templates/textbox.html index d37eda7284..f201bd6947 100644 --- a/common/lib/capa/capa/templates/textbox.html +++ b/common/lib/capa/capa/templates/textbox.html @@ -39,6 +39,7 @@ lineWrapping: true, indentUnit: "${tabsize}", tabSize: "${tabsize}", + indentWithTabs: true, smartIndent: false }); }); @@ -48,7 +49,7 @@ border: 1px solid black; font-size: 14px; line-height: 18px; - resize: vertical; + resize: both; } diff --git a/common/lib/capa/capa/xqueue_interface.py b/common/lib/capa/capa/xqueue_interface.py index deb068adf6..82e4fba5cc 100644 --- a/common/lib/capa/capa/xqueue_interface.py +++ b/common/lib/capa/capa/xqueue_interface.py @@ -47,7 +47,12 @@ def parse_xreply(xreply): 'content': Message from xqueue (string) } ''' - xreply = json.loads(xreply) + try: + xreply = json.loads(xreply) + except ValueError, err: + log.error(err) + return (1, 'unexpected reply from server') + return_code = xreply['return_code'] content = xreply['content'] return (return_code, content) @@ -61,7 +66,7 @@ class XqueueInterface: def __init__(self, url=XQUEUE_URL, auth=XQUEUE_LMS_AUTH): self.url = url self.auth = auth - self.s = requests.session() + self.session = requests.session() self._login() def send_to_queue(self, header, body, file_to_upload=None): @@ -86,29 +91,32 @@ class XqueueInterface: return (error, msg) - def _login(self): - try: - r = self.s.post(self.url+'/xqueue/login/', data={ 'username': self.auth['username'], - 'password': self.auth['password'] }) - except requests.exceptions.ConnectionError, err: - log.error(err) - return (1, 'cannot connect to server') - return parse_xreply(r.text) + def _login(self): + payload = { 'username': self.auth['username'], + 'password': self.auth['password'] } + return self._http_post(self.url+'/xqueue/login/', payload) + def _send_to_queue(self, header, body, file_to_upload=None): - payload = {'xqueue_header': header, 'xqueue_body' : body} - files = None if file_to_upload is not None: files = { file_to_upload.name: file_to_upload } + return self._http_post(self.url+'/xqueue/submit/', payload, files) + + def _http_post(self, url, data, files=None): try: - r = self.s.post(self.url+'/xqueue/submit/', data=payload, files=files) + r = self.session.post(url, data=data, files=files) except requests.exceptions.ConnectionError, err: log.error(err) - return (1, 'cannot connect to server') + return (1, 'cannot connect to server') + + if r.status_code not in [200]: + return (1, 'unexpected HTTP status code [%d]' % r.status_code) return parse_xreply(r.text) + +qinterface = XqueueInterface() diff --git a/common/lib/xmodule/xmodule/js/src/capa/display.coffee b/common/lib/xmodule/xmodule/js/src/capa/display.coffee index 92900aa851..b30a52c37b 100644 --- a/common/lib/xmodule/xmodule/js/src/capa/display.coffee +++ b/common/lib/xmodule/xmodule/js/src/capa/display.coffee @@ -13,6 +13,7 @@ class @Problem bind: => MathJax.Hub.Queue ["Typeset", MathJax.Hub] window.update_schematics() + @inputs = @$("[id^=input_#{@element_id.replace(/problem_/, '')}_]") @$('section.action input:button').click @refreshAnswers @$('section.action input.check').click @check_fd #@$('section.action input.check').click @check @@ -114,8 +115,8 @@ class @Problem return fd = new FormData() - - @$("[id^=input_#{@element_id.replace(/problem_/, '')}_]").each (index, element) -> + + @inputs.each (index, element) -> if element.type is 'file' if element.files[0] instanceof File fd.append(element.id, element.files[0]) @@ -206,7 +207,7 @@ class @Problem element.schematic.update_value() @$(".CodeMirror").each (index, element) -> element.CodeMirror.save() if element.CodeMirror.save - @answers = @$("[id^=input_#{@element_id.replace(/problem_/, '')}_]").serialize() + @answers = @inputs.serialize() inputtypeSetupMethods: javascriptinput: (element) => diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 60670767f7..f013aac785 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -643,8 +643,7 @@ class ModuleSystem(object): user=None, filestore=None, debug=False, - xqueue_callback_url=None, - xqueue_default_queuename="null", + xqueue = None, is_staff=False): ''' Create a closure around the system environment. @@ -668,6 +667,9 @@ class ModuleSystem(object): filestore - A filestore ojbect. Defaults to an instance of OSFS based at settings.DATA_DIR. + xqueue - Dict containing XqueueInterface object, as well as parameters + for the specific StudentModule + replace_urls - TEMPORARY - A function like static_replace.replace_urls that capa_module can use to fix up the static urls in ajax results. @@ -676,8 +678,7 @@ class ModuleSystem(object): TODO (vshnayder): this will need to change once we have real user roles. ''' self.ajax_url = ajax_url - self.xqueue_callback_url = xqueue_callback_url - self.xqueue_default_queuename = xqueue_default_queuename + self.xqueue = xqueue self.track_function = track_function self.filestore = filestore self.get_module = get_module diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index abab4df149..5b4dc602e2 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -8,6 +8,7 @@ from django.views.decorators.csrf import csrf_exempt from django.contrib.auth.models import User from xmodule.modulestore.django import modulestore +from capa.xqueue_interface import qinterface from mitxmako.shortcuts import render_to_string from models import StudentModule, StudentModuleCache from static_replace import replace_urls @@ -157,6 +158,10 @@ def get_module(user, request, location, student_module_cache, position=None): # TODO: Queuename should be derived from 'course_settings.json' of each course xqueue_default_queuename = descriptor.location.org + '-' + descriptor.location.course + xqueue = { 'interface': qinterface, + 'callback_url': xqueue_callback_url, + 'default_queuename': xqueue_default_queuename.replace(' ','_') } + def _get_module(location): (module, _, _, _) = get_module(user, request, location, student_module_cache, position) @@ -168,8 +173,7 @@ def get_module(user, request, location, student_module_cache, position=None): system = ModuleSystem(track_function=make_track_function(request), render_template=render_to_string, ajax_url=ajax_url, - xqueue_callback_url=xqueue_callback_url, - xqueue_default_queuename=xqueue_default_queuename.replace(' ','_'), + xqueue=xqueue, # TODO (cpennington): Figure out how to share info between systems filestore=descriptor.system.resources_fs, get_module=_get_module, @@ -222,6 +226,9 @@ def get_module(user, request, location, student_module_cache, position=None): @csrf_exempt def xqueue_callback(request, userid, id, dispatch): + ''' + Entry point for graded results from the queueing system. + ''' # Parse xqueue response get = request.POST.copy() try: