From 7cc502c8860b003ab212aa8097a54b7f90bacde6 Mon Sep 17 00:00:00 2001 From: kimth Date: Mon, 6 Aug 2012 15:44:27 -0400 Subject: [PATCH 1/8] Refactor list of inputs in ajax --- common/lib/capa/capa/responsetypes.py | 2 +- common/lib/xmodule/xmodule/js/src/capa/display.coffee | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 12f619a881..ded47b7ea0 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -891,7 +891,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.') return cmap diff --git a/common/lib/xmodule/xmodule/js/src/capa/display.coffee b/common/lib/xmodule/xmodule/js/src/capa/display.coffee index 0e760e98ff..18bec8a7d1 100644 --- a/common/lib/xmodule/xmodule/js/src/capa/display.coffee +++ b/common/lib/xmodule/xmodule/js/src/capa/display.coffee @@ -12,6 +12,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 @@ -66,8 +67,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]) @@ -155,4 +156,4 @@ 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() From 41e7d72ea8b8bb8445011db42e2e8d933f0bd42b Mon Sep 17 00:00:00 2001 From: kimth Date: Mon, 6 Aug 2012 16:13:35 -0400 Subject: [PATCH 2/8] Xqueue interface is embedded in ModuleSystem --- common/lib/capa/capa/responsetypes.py | 8 ++++---- common/lib/capa/capa/xqueue_interface.py | 2 ++ common/lib/xmodule/xmodule/x_module.py | 5 ++--- lms/djangoapps/courseware/module_render.py | 11 +++++++++-- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index ded47b7ea0..6de9ec8523 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -29,7 +29,6 @@ import xqueue_interface log = logging.getLogger('mitx.' + __name__) -qinterface = xqueue_interface.XqueueInterface() #----------------------------------------------------------------------------- # Exceptions @@ -811,7 +810,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: @@ -859,10 +858,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) diff --git a/common/lib/capa/capa/xqueue_interface.py b/common/lib/capa/capa/xqueue_interface.py index deb068adf6..f965b01723 100644 --- a/common/lib/capa/capa/xqueue_interface.py +++ b/common/lib/capa/capa/xqueue_interface.py @@ -112,3 +112,5 @@ class XqueueInterface: return (1, 'cannot connect to server') return parse_xreply(r.text) + +qinterface = XqueueInterface() diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 7bb98dcdc5..0eaca8c426 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -587,7 +587,7 @@ class ModuleSystem(object): def __init__(self, ajax_url, track_function, get_module, render_template, replace_urls, user=None, filestore=None, debug=False, - xqueue_callback_url=None, xqueue_default_queuename="null"): + xqueue=None): ''' Create a closure around the system environment. @@ -615,8 +615,7 @@ class ModuleSystem(object): ajax results. ''' 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 9ce4008597..b54e71df6d 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 @@ -151,6 +152,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) @@ -162,8 +167,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, @@ -214,6 +218,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: From c2e351523f9663778da6a534e267b14ed8e51c5f Mon Sep 17 00:00:00 2001 From: kimth Date: Mon, 6 Aug 2012 16:28:15 -0400 Subject: [PATCH 3/8] Update ModuleSystem --- common/lib/xmodule/xmodule/x_module.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 From 00486819c2e52516b8be84c24d0f1311ad7953fe Mon Sep 17 00:00:00 2001 From: kimth Date: Mon, 6 Aug 2012 16:47:24 -0400 Subject: [PATCH 4/8] Make CodeMirror more pleasant for 6.00 --- common/lib/capa/capa/templates/textbox.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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; } From ae11f91880a59cf654467aee8981df3301ef62e3 Mon Sep 17 00:00:00 2001 From: kimth Date: Mon, 6 Aug 2012 16:56:44 -0400 Subject: [PATCH 5/8] Queue submission reports queue length --- common/lib/capa/capa/responsetypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 6de9ec8523..9420560c02 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -891,7 +891,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 From b6e9e7a4bb29e883523bd2d32a8513b30773d2d4 Mon Sep 17 00:00:00 2001 From: kimth Date: Mon, 6 Aug 2012 17:36:38 -0400 Subject: [PATCH 6/8] Handle network errors more gracefully --- common/lib/capa/capa/xqueue_interface.py | 27 ++++++++++++------------ 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/common/lib/capa/capa/xqueue_interface.py b/common/lib/capa/capa/xqueue_interface.py index f965b01723..f69c9142e8 100644 --- a/common/lib/capa/capa/xqueue_interface.py +++ b/common/lib/capa/capa/xqueue_interface.py @@ -61,7 +61,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,30 +86,31 @@ 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) From 18a7f587354558bb144efb76e5342ef8ca107365 Mon Sep 17 00:00:00 2001 From: kimth Date: Mon, 6 Aug 2012 17:44:07 -0400 Subject: [PATCH 7/8] Handle xreply parsing errors more gracefully --- common/lib/capa/capa/xqueue_interface.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/lib/capa/capa/xqueue_interface.py b/common/lib/capa/capa/xqueue_interface.py index f69c9142e8..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) From f47ab30c3f930244fa44efad0e90fb22c86f90c7 Mon Sep 17 00:00:00 2001 From: ichuang Date: Mon, 6 Aug 2012 18:34:54 -0400 Subject: [PATCH 8/8] small fix to xmodule_modifiers to fix case when filename missing --- common/djangoapps/xmodule_modifiers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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)