def testLateOffset(self): """ Makes sure offsets from A01's deadline are correct. """ # vs: A01-20380119-0314 tests = { '20380119-0314': '-0d 0h 00m', '20380119-0315': '+0d 0h 01m', '20380118-0211': '-1d 1h 03m', '20380119-0414': '+0d 1h 00m', } for stamp, offset in tests.items(): a = Assignment('A01') self.assertEqual(a.getLateOffset(stamp), offset)
def validateUploadedFile(form): """ Processes a file upload. Assumes the passed form is a valid query submission. On an error, reports what went wrong to the user. Otherwise confirms that the user wants to submit the uploaded file for grading. """ printHeader("File Upload Results") try: print('<h2>File Upload Results</h2>') print('<p>') # get filename and check that file was properly uploaded assert 'file' in form and 'pass' in form, "Invalid form submission" filename = form['file'].filename if not filename: raise TamarinError('NO_FILE_UPLOADED') else: print('<!--Original filename: ' + filename + '-->') filename = stripFilename(filename) #defined below print('<b>Filename:</b> ' + filename + '<br>') filecontents = form.getfirst('file') if not filecontents: raise TamarinError('EMPTY_FILE') # validate filename checkFilename(filename) # authenticate user match = re.match(tamarin.UPLOADED_RE, filename) assert match #because filename is valid from above username = match.group(1) username = username.lower() #for any initial uppercase letter assignmentName = match.group(2) extension = match.group(3) password = form.getfirst('pass') print('<b>Username:</b> ' + username + '<br>') print('<b>Password:</b> ', end='') if password: print('*' * len(password) + '<br>') else: print('[missing]<br>') tamarin.authenticate(username, password) # validate that assignment exists print('<b>Assignment:</b> ' + assignmentName) #... no <br> yet assignment = Assignment(assignmentName) #may throw TamarinError # confirm any type-specific requirements... # right extension? if extension != assignment.type.fileExt: raise TamarinError('WRONG_EXTENSION', 'Your file is .' + extension + ' but this assignment requires .' + assignment.type.fileExt) # initial cap? if assignment.type.initialCap and not re.match(r"^[A-Z]", filename): raise TamarinError('NO_INITIAL_CAP', filename) # check that file contents are plain text (if necessary) if assignment.type.encoding: try: # convert bytes to str filecontents = filecontents.decode( encoding=assignment.type.encoding) # files often uploaded in binary form from various OSs, # so adjust line-endings # XXX: Still need this in Python3? filecontents = filecontents.replace('\r\n', '\r') # \n comes out properly as 0D0A on Windows filecontents = filecontents.replace('\r', '\n') except UnicodeError as err: # this problem is often due to a copy-and-paste thing from a # different encoding where a couple odd characters (like quotes # and such) come from a word processor. So this will help # students track down where their bad chars are in this most # common non-ASCII case lineCount = 1 charCount = 1 for char in filecontents: if char > 128: print("<p><b>First non-ASCII character:</b>: ") print("line " + str(lineCount) + ", char " + str(charCount) + "<br>") break elif char == '\n': # NOTE: line count only correct for Unix and Windows; # not old Mac, etc lineCount += 1 charCount = 1 else: charCount += 1 raise TamarinError('BINARY_FILE', "This assignment requires " "plain text files using the " + assignment.type.encoding + " (or compatible) encoding, but: " + str(err)) # Ready to accept based on submission itself. # Now see if late or already submitted (for warnings) or verified # Reuse filename (without extension) from above wildFilename = filename.replace('.', '*.', 1) alreadySubmitted = glob.glob(os.path.join(tamarin.SUBMITTED_ROOT, wildFilename)) alreadyGraded = glob.glob(os.path.join(assignment.path, wildFilename)) # but not all files in graded == a graded submission file if submitted # ext is same as grader output file ext, so remove grader output files for ag in alreadyGraded[:]: if re.match(tamarin.GRADED_RE, os.path.basename(ag)): alreadyGraded.remove(ag) # determine lateness, so can print it here if assignment.isLate(): print(' (<i>Late: </i>' + assignment.getLateOffset() + ')') if assignment.isTooLate(): raise TamarinError('SUBMISSION_TOO_LATE') if not tamarin.MAY_RESUBMIT_LATE and \ (alreadySubmitted or alreadyGraded): raise 'RESUBMISSION_LATE' # has a human already verified a previous submission? if not tamarin.MAY_RESUMBIT_AFTER_HUMAN and alreadyGraded: # check if a graded file has also been verified for f in alreadyGraded: gradedF = GradedFile(os.path.basename(f)) if gradedF.humanVerified: # oops for student: already verified raise TamarinError('PREVIOUS_SUBMISSION_VERIFIED') # display file contents (if appropriate) print('<p><b>Uploaded Code:</b></p>') print('<pre ' if assignment.type.preformatted else '<div ') print('class="code">') if not assignment.type.encoding: print('[ <i>binary file format: ' 'cannot display contents here</i> ]') else: # prevent disappearing code due to code test looking like html tags if assignment.type.preformatted: print(html.escape(filecontents)) else: print(html.escape(filecontents).replace('\n', '<br>\n')) print('</pre>' if assignment.type.preformatted else '</div>') # Done! # write file (could result in an io error, handed as UNHANDLED below) uploadedFilename = os.path.join(tamarin.UPLOADED_ROOT, filename) overwrite = glob.glob(uploadedFilename) #see if file already exists outfile = open(uploadedFilename, 'w' if assignment.type.encoding else 'wb') outfile.write(filecontents) outfile.close() # give success message print('<p>') print('Your code (as shown above) has been uploaded to Tamarin. ') if overwrite: print('(Doing so overwrote a previously uploaded ' 'but unsubmitted file of the same name.) ') print('<i>It has not yet been submitted.</i> ') # warn if already submitted (and/or graded) this assignment if alreadySubmitted or alreadyGraded: print('<p><b><i>Warning:</i></b> You already have ') if alreadySubmitted: print(str(len(alreadySubmitted)) + ' file(s) submitted ' '(but not yet graded) ') if alreadyGraded: print('and ') if alreadyGraded: print(str(len(alreadyGraded)) + ' file(s) graded ') print('for this ' + str(assignment) + ' assignment. ') submitCount = len(alreadySubmitted) + len(alreadyGraded) if tamarin.RESUBMISSION_PENALTY: print('If you submit this uploaded file, it will be a ' + '(resubmission * ' + str(submitCount) + '), ') print('and so its final score will be reduced by ' + str(round(submitCount * tamarin.RESUBMISSION_PENALTY, tamarin.GRADE_PRECISION)) + '.</p>') # provide submit button print("<p>If you are ready to officially submit this uploaded file " "for grading, click the button below. </p>") print() print('<form action="' + tamarin.CGI_URL + 'submit.py" ' 'method="post" class="html">') print('<input type="hidden" name="uploaded" value="' + filename + '">') print('<input type="submit" value="Submit this file" ' 'class="centered">') print('</form>') except TamarinError as err: printError(err) print('<p>') print('Due to the above error, your uploaded file was not saved. ' 'Please <a href="' + tamarin.CGI_URL + 'upload.py' + '">return to the upload page</a> and try again.') print('</p>') except: printError('UNHANDLED_ERROR') finally: printFooter()