Commit 0a63ad8f by kimth

Multiple file submissions

parent cc6d4757
...@@ -1119,11 +1119,6 @@ class CodeResponse(LoncapaResponse): ...@@ -1119,11 +1119,6 @@ class CodeResponse(LoncapaResponse):
(err, self.answer_id, convert_files_to_filenames(student_answers))) (err, self.answer_id, convert_files_to_filenames(student_answers)))
raise Exception(err) raise Exception(err)
if is_file(submission):
self.context.update({'submission': submission.name})
else:
self.context.update({'submission': submission})
# Prepare xqueue request # Prepare xqueue request
#------------------------------------------------------------ #------------------------------------------------------------
qinterface = self.system.xqueue['interface'] qinterface = self.system.xqueue['interface']
...@@ -1135,14 +1130,19 @@ class CodeResponse(LoncapaResponse): ...@@ -1135,14 +1130,19 @@ class CodeResponse(LoncapaResponse):
queue_name=self.queue_name) queue_name=self.queue_name)
# Generate body # 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() contents = self.payload.copy()
# Submit request. When successful, 'msg' is the prior length of the queue # Submit request. When successful, 'msg' is the prior length of the queue
if is_file(submission): if is_list_of_files(submission):
contents.update({'student_response': submission.name}) contents.update({'student_response': ''}) # TODO: Is there any information we want to send here?
(error, msg) = qinterface.send_to_queue(header=xheader, (error, msg) = qinterface.send_to_queue(header=xheader,
body=json.dumps(contents), body=json.dumps(contents),
file_to_upload=submission) files_to_upload=submission)
else: else:
contents.update({'student_response': submission}) contents.update({'student_response': submission})
(error, msg) = qinterface.send_to_queue(header=xheader, (error, msg) = qinterface.send_to_queue(header=xheader,
......
...@@ -39,12 +39,26 @@ def convert_files_to_filenames(answers): ...@@ -39,12 +39,26 @@ def convert_files_to_filenames(answers):
''' '''
new_answers = dict() new_answers = dict()
for answer_id in answers.keys(): for answer_id in answers.keys():
if is_file(answers[answer_id]): answer = answers[answer_id]
new_answers[answer_id] = answers[answer_id].name 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: else:
new_answers[answer_id] = answers[answer_id] new_answers[answer_id] = answers[answer_id]
return new_answers 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): def is_file(file_to_test):
''' '''
Duck typing to check if 'file_to_test' is a File object Duck typing to check if 'file_to_test' is a File object
......
...@@ -65,7 +65,7 @@ class XQueueInterface(object): ...@@ -65,7 +65,7 @@ class XQueueInterface(object):
self.auth = django_auth self.auth = django_auth
self.session = requests.session(auth=requests_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. Submit a request to xqueue.
...@@ -74,16 +74,16 @@ class XQueueInterface(object): ...@@ -74,16 +74,16 @@ class XQueueInterface(object):
body: Serialized data for the receipient behind the queueing service. The operation of body: Serialized data for the receipient behind the queueing service. The operation of
xqueue is agnostic to the contents of 'body' 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 Returns (error_code, msg) where error_code != 0 indicates an error
''' '''
# Attempt to send to queue # 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 if error and (msg == 'login_required'): # Log in, then try again
self._login() 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) return (error, msg)
...@@ -94,12 +94,13 @@ class XQueueInterface(object): ...@@ -94,12 +94,13 @@ class XQueueInterface(object):
return self._http_post(self.url+'/xqueue/login/', payload) 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, payload = {'xqueue_header': header,
'xqueue_body' : body} 'xqueue_body' : body}
files = None files = None
if file_to_upload is not None: for f in files_to_upload:
files = { file_to_upload.name: file_to_upload } files = { f.name: f }
return self._http_post(self.url+'/xqueue/submit/', payload, files) return self._http_post(self.url+'/xqueue/submit/', payload, files)
......
...@@ -151,28 +151,33 @@ class @Problem ...@@ -151,28 +151,33 @@ class @Problem
return return
if not window.FormData 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 return
fd = new FormData() fd = new FormData()
# Sanity check of file size # Sanity checks on submission
abort_submission = false
max_filesize = 4*1000*1000 # 4 MB max_filesize = 4*1000*1000 # 4 MB
file_too_large = false
file_not_selected = false
@inputs.each (index, element) -> @inputs.each (index, element) ->
if element.type is 'file' if element.type is 'file'
for file in element.files for file in element.files
if file.size > max_filesize 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)' alert 'Submission aborted! Your file "' + file.name '" is too large (max size: ' + max_filesize/(1000*1000) + ' MB)'
fd.append(element.id, file) fd.append(element.id, file)
if element.files.length == 0 if element.files.length == 0
abort_submission = true file_not_selected = true
alert 'Submission aborted! You did not select any files to submit' fd.append(element.id, '') # In case we want to allow submissions with no file
fd.append(element.id, '')
else else
fd.append(element.id, element.value) 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 = settings =
type: "POST" type: "POST"
...@@ -186,7 +191,7 @@ class @Problem ...@@ -186,7 +191,7 @@ class @Problem
@updateProgress response @updateProgress response
else else
alert(response.success) alert(response.success)
if not abort_submission if not abort_submission
$.ajaxWithPrefix("#{@url}/problem_check", settings) $.ajaxWithPrefix("#{@url}/problem_check", settings)
......
...@@ -375,15 +375,16 @@ def modx_dispatch(request, dispatch=None, id=None, course_id=None): ...@@ -375,15 +375,16 @@ def modx_dispatch(request, dispatch=None, id=None, course_id=None):
# ''' (fix emacs broken parsing) # ''' (fix emacs broken parsing)
# Check for submitted files and basic file size checks # Check for submitted files and basic file size checks
p = request.POST.copy() p = request.POST.dict()
if request.FILES: if request.FILES:
for inputfile_id in request.FILES.keys(): for fileinput_id in request.FILES.keys():
inputfile = request.FILES[inputfile_id] inputfiles = request.FILES.getlist(fileinput_id)
if inputfile.size > settings.STUDENT_FILEUPLOAD_MAX_SIZE: # Bytes for inputfile in inputfiles:
file_too_big_msg = 'Submission aborted! Your file "%s" is too large (max size: %d MB)' %\ if inputfile.size > settings.STUDENT_FILEUPLOAD_MAX_SIZE: # Bytes
(inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE/(1000**2)) file_too_big_msg = 'Submission aborted! Your file "%s" is too large (max size: %d MB)' %\
return HttpResponse(json.dumps({'success': file_too_big_msg})) (inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE/(1000**2))
p[inputfile_id] = inputfile 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)) 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) instance = get_module(request.user, request, id, student_module_cache, course_id=course_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