From d2cb6458348f581eba9dccfcf7ea573c8a139fee Mon Sep 17 00:00:00 2001 From: kimth <kimt@mit.edu> Date: Sun, 19 Aug 2012 09:26:03 -0400 Subject: [PATCH] Multiple file submissions --- common/lib/capa/capa/responsetypes.py | 16 +++++++------- common/lib/capa/capa/util.py | 18 ++++++++++++++-- common/lib/capa/capa/xqueue_interface.py | 15 ++++++------- .../xmodule/js/src/capa/display.coffee | 21 ++++++++++++------- lms/djangoapps/courseware/module_render.py | 17 ++++++++------- 5 files changed, 54 insertions(+), 33 deletions(-) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 3a2e40f896c..cc67389da9f 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -1119,11 +1119,6 @@ class CodeResponse(LoncapaResponse): (err, self.answer_id, convert_files_to_filenames(student_answers))) raise Exception(err) - if is_file(submission): - self.context.update({'submission': submission.name}) - else: - self.context.update({'submission': submission}) - # Prepare xqueue request #------------------------------------------------------------ qinterface = self.system.xqueue['interface'] @@ -1135,14 +1130,19 @@ class CodeResponse(LoncapaResponse): queue_name=self.queue_name) # Generate body + if is_list_of_files(submission): + self.context.update({'submission': queuekey}) # For tracking. TODO: May want to record something else here + else: + self.context.update({'submission': submission}) + contents = self.payload.copy() # Submit request. When successful, 'msg' is the prior length of the queue - if is_file(submission): - contents.update({'student_response': submission.name}) + if is_list_of_files(submission): + contents.update({'student_response': ''}) # TODO: Is there any information we want to send here? (error, msg) = qinterface.send_to_queue(header=xheader, body=json.dumps(contents), - file_to_upload=submission) + files_to_upload=submission) else: contents.update({'student_response': submission}) (error, msg) = qinterface.send_to_queue(header=xheader, diff --git a/common/lib/capa/capa/util.py b/common/lib/capa/capa/util.py index 005494e8c0a..01f5fe4c82a 100644 --- a/common/lib/capa/capa/util.py +++ b/common/lib/capa/capa/util.py @@ -39,12 +39,26 @@ def convert_files_to_filenames(answers): ''' new_answers = dict() for answer_id in answers.keys(): - if is_file(answers[answer_id]): - new_answers[answer_id] = answers[answer_id].name + answer = answers[answer_id] + if is_list_of_files(answer): # Files are stored as a list, even if one file + list_of_filenames = [] + for inputfile in answer: + list_of_filenames.append(inputfile.name) + new_answers[answer_id] = list_of_filenames else: new_answers[answer_id] = answers[answer_id] return new_answers +def is_list_of_files(list_of_files_to_test): + if not isinstance(list_of_files_to_test, list): + return False + + for li in list_of_files_to_test: + if not is_file(li): + return False + + return True + def is_file(file_to_test): ''' Duck typing to check if 'file_to_test' is a File object diff --git a/common/lib/capa/capa/xqueue_interface.py b/common/lib/capa/capa/xqueue_interface.py index 2847968a89e..81dac229368 100644 --- a/common/lib/capa/capa/xqueue_interface.py +++ b/common/lib/capa/capa/xqueue_interface.py @@ -65,7 +65,7 @@ class XQueueInterface(object): self.auth = django_auth self.session = requests.session(auth=requests_auth) - def send_to_queue(self, header, body, file_to_upload=None): + def send_to_queue(self, header, body, files_to_upload=[]): ''' Submit a request to xqueue. @@ -74,16 +74,16 @@ class XQueueInterface(object): body: Serialized data for the receipient behind the queueing service. The operation of xqueue is agnostic to the contents of 'body' - file_to_upload: File object to be uploaded to xqueue along with queue request + files_to_upload: List of file objects to be uploaded to xqueue along with queue request Returns (error_code, msg) where error_code != 0 indicates an error ''' # Attempt to send to queue - (error, msg) = self._send_to_queue(header, body, file_to_upload) + (error, msg) = self._send_to_queue(header, body, files_to_upload) if error and (msg == 'login_required'): # Log in, then try again self._login() - (error, msg) = self._send_to_queue(header, body, file_to_upload) + (error, msg) = self._send_to_queue(header, body, files_to_upload) return (error, msg) @@ -94,12 +94,13 @@ class XQueueInterface(object): return self._http_post(self.url+'/xqueue/login/', payload) - def _send_to_queue(self, header, body, file_to_upload=None): + def _send_to_queue(self, header, body, files_to_upload): payload = {'xqueue_header': header, 'xqueue_body' : body} files = None - if file_to_upload is not None: - files = { file_to_upload.name: file_to_upload } + for f in files_to_upload: + files = { f.name: f } + return self._http_post(self.url+'/xqueue/submit/', payload, files) diff --git a/common/lib/xmodule/xmodule/js/src/capa/display.coffee b/common/lib/xmodule/xmodule/js/src/capa/display.coffee index 0db000188e7..a242757357f 100644 --- a/common/lib/xmodule/xmodule/js/src/capa/display.coffee +++ b/common/lib/xmodule/xmodule/js/src/capa/display.coffee @@ -151,28 +151,33 @@ class @Problem return if not window.FormData - alert "Sorry, your browser does not support file uploads. Your submit request could not be fulfilled. If you can, please use Chrome or Safari which have been verified to support file uploads." + alert "Submission aborted! Sorry, your browser does not support file uploads. If you can, please use Chrome or Safari which have been verified to support file uploads." return fd = new FormData() - # Sanity check of file size - abort_submission = false + # Sanity checks on submission max_filesize = 4*1000*1000 # 4 MB + file_too_large = false + file_not_selected = false @inputs.each (index, element) -> if element.type is 'file' for file in element.files if file.size > max_filesize - abort_submission = true + file_too_large = true alert 'Submission aborted! Your file "' + file.name '" is too large (max size: ' + max_filesize/(1000*1000) + ' MB)' fd.append(element.id, file) if element.files.length == 0 - abort_submission = true - alert 'Submission aborted! You did not select any files to submit' - fd.append(element.id, '') + file_not_selected = true + fd.append(element.id, '') # In case we want to allow submissions with no file else fd.append(element.id, element.value) + + if file_not_selected + alert 'Submission aborted! You did not select any files to submit' + + abort_submission = file_too_large or file_not_selected settings = type: "POST" @@ -186,7 +191,7 @@ class @Problem @updateProgress response else alert(response.success) - + if not abort_submission $.ajaxWithPrefix("#{@url}/problem_check", settings) diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 558f6deeb2a..53c7e453ddf 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -375,15 +375,16 @@ def modx_dispatch(request, dispatch=None, id=None, course_id=None): # ''' (fix emacs broken parsing) # Check for submitted files and basic file size checks - p = request.POST.copy() + p = request.POST.dict() if request.FILES: - for inputfile_id in request.FILES.keys(): - inputfile = request.FILES[inputfile_id] - if inputfile.size > settings.STUDENT_FILEUPLOAD_MAX_SIZE: # Bytes - file_too_big_msg = 'Submission aborted! Your file "%s" is too large (max size: %d MB)' %\ - (inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE/(1000**2)) - return HttpResponse(json.dumps({'success': file_too_big_msg})) - p[inputfile_id] = inputfile + for fileinput_id in request.FILES.keys(): + inputfiles = request.FILES.getlist(fileinput_id) + for inputfile in inputfiles: + if inputfile.size > settings.STUDENT_FILEUPLOAD_MAX_SIZE: # Bytes + file_too_big_msg = 'Submission aborted! Your file "%s" is too large (max size: %d MB)' %\ + (inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE/(1000**2)) + return HttpResponse(json.dumps({'success': file_too_big_msg})) + p[fileinput_id] = inputfiles student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(request.user, modulestore().get_item(id)) instance = get_module(request.user, request, id, student_module_cache, course_id=course_id) -- GitLab