def main(form=None): if not form: form = cgi.FieldStorage() tamarin.printHeader("Submission and Grading Results") print("<h2>Submission and Grading Results</h2>") try: print("<p>") # check that submitted form was correct with all needed details if "uploaded" not in form: raise TamarinError("BAD_SUBMITTED_FORM", "Missing uploaded field.") # validate filename format, printing username and assignment filename = form.getfirst("uploaded") match = re.match(tamarin.UPLOADED_RE, filename) if not match: raise TamarinError("BAD_SUBMITTED_FORM", "Wrong filename format.") print("<b>Username:</b> " + match.group(1) + "<br>") print("<b>Assignment:</b> " + match.group(2) + "<br>") # check that uploaded file really exists uploadedFilename = os.path.join(tamarin.UPLOADED_ROOT, filename) if not os.path.exists(uploadedFilename): raise TamarinError("NO_UPLOADED_FILE") # create submitted filename and move uploaded file to submitted folder currentStamp = tamarin.convertTimeToTimestamp() submittedFilename = filename.replace(".", "-" + currentStamp + ".", 1) submittedFilename = os.path.join(tamarin.SUBMITTED_ROOT, submittedFilename) shutil.move(uploadedFilename, submittedFilename) print("<b>File submitted at:</b> " + currentStamp + "<br>") # Yay! Submission is successful, and user is DONE print("<i>--Submission completed successfully--</i>") print("</p>") # now start grading... # (In future, might have option to grade in real time. For now, # just start the grade pipe and print results.) startGradePipe() except TamarinError as err: tamarin.printError(err) print("<p>") print( "Due to the above error, your file was <i>not</i> successfully " "submitted. You may want to try again or else contact your " "Tamarin administrator " ) if tamarin.ADMIN_EMAIL: print("(" + tamarin.ADMIN_EMAIL + ")") print(" with the details of the error you encountered.") print("</p>") except: tamarin.printError("UNHANDLED_ERROR") finally: tamarin.printFooter()
def isLate(self, submittedTimestamp=None): """ Determines whether the submittedTimestamp (in 'YYYYMMDD-HHMM' format) occurs before this assignment's deadline. If submittedTimestamp is None, uses the current time. """ import tamarin if not submittedTimestamp: # use current time submittedTimestamp = tamarin.convertTimeToTimestamp() # nice thing about Tamarin timestamps is they also compare properly return submittedTimestamp > self.due
def main(): """ Displays current time, grading method, gradepipe status, number of uploaded (but unsubmitted files), and the submitted queue. """ tamarin.printHeader("Tamarin Status") try: print('<h2>Tamarin Status</h2>') # current time # (Printed in two different formats as a way for users # to learn how to make sense of Tamarin timestamp format) print('<p><b>Current System Time:</b> ') now = datetime.datetime.now() print(tamarin.convertTimeToTimestamp(now)) print(' (<i>' + now.isoformat(' ') + '</i>)') print('</p>') # uploaded file count print('<p><b>Uploaded (but not submitted) file count:</b> ') uploaded = glob.glob(os.path.join(tamarin.UPLOADED_ROOT, '*')) print(len(uploaded)) # grade pipe status print('<p><b>Gradepipe:</b> ') if os.path.exists(tamarin.GRADEPIPE_ACTIVE): print('<small>RUNNING</small>') else: print('<small>OFF</small>') if os.path.exists(tamarin.GRADEPIPE_DISABLED): print(' <small>(DISABLED)</small>') print('</p>') # grading queue contents print('<p><b>Submitted (but not yet graded) queue:</b> ') submitted = tamarin.getSubmittedFilenames() if len(submitted) == 0: print('[empty]') else: print('<br>') for s in submitted: print('<span class="gradequeue">' + os.path.basename(s) + '</span><br>') print('</p>') except: tamarin.printError('UNHANDLED_ERROR') finally: tamarin.printFooter()
def isTooLate(self, submittedTimestamp=None): """ Determines whether the submittedTimestamp (in 'YYYYMMDD-HHMM' format) occurs after this assignment's deadline AND after all late policies. If submittedTimestamp is None, uses the current time. """ import tamarin if not submittedTimestamp: # use current time submittedTimestamp = tamarin.convertTimeToTimestamp() if self.policies: # just look at last one (already sorted) return submittedTimestamp > self.policies[-1].end else: return self.isLate()
def getPolicy(self, submittedTimestamp=None): """ Returns the appropriate policy associated with this assignment based on the given timestamp. If no timestamp is given, uses the current time. May return None if this assignment has no late policies, or if the given timestamp is before the deadline but there is no early submission policy. """ import tamarin if not submittedTimestamp: # use current time submittedTimestamp = tamarin.convertTimeToTimestamp() timestamp = submittedTimestamp if not self.policies: return None elif self.isLate(timestamp): if self.policies[-1].isEarlyPolicy(): # only have early policies, though, so none apply return None else: # find last late policy that applies (even if past last one) for policy in self.policies: if not policy.isEarlyPolicy() and timestamp <= policy.end: break return policy else: if not self.policies[0].isEarlyPolicy(): # submission is early, but only have late policies return None else: #find closest-to-deadline early policy that applies (or first) for policy in reversed(self.policies): if policy.isEarlyPolicy() and timestamp >= policy.end: break return policy
def __init__(self, policy, deadline): """ Constructs a LatePolicy according to the given policy string. The deadline should be in Tamarin timestamp format. Each policy should be a string in the form of: 'range:rule' A range has the following format: [+|-][[#d][#h][#m]|timestamp] That is, the range may optionally begin with either + or -. If the sign is omitted, + is assumed. The sign may then be followed by a span specified in days, hours, and minutes. This span is relative to the assignment deadline, either earlier (if sign is -) or later (if sign is +). A particular unit may be omitted if unused; it will be assumed to be 0. If more than 23 hours or 59 minutes are specified, the corresponding overflow into days and hours will be calculated. At least one unit must be given though: d for day, h for hours, or m for minutes. If more than one is used, they must be in that order. As an alternative to specifying a relative span, a specific timestamp may be given. The sign is ignored in these cases. (Since LatePolicies usually apply across multiple assignments, specific timestamps are rare. However, they can be handy to specify a final cutoff date for all assignments--such as the last day of the course.) It is an error to have a 0 length span, either because the relative span is of size 0 or if the timestamp given is equal to the deadline. If neither span nor timestamp is given, the end of the span is treated as year 0 for a - sign or year 9999 for a positive sign. The range is then separated from the associated rule by a colon (:). The rule is of the form: [+|-|=][#[%|$]][/[#d][#h][#m]] That is, the rule may begin with a sign: +, -, or =. If omitted, = is assumed. The sign specifies whether the grade should be increased by (+), decreased (-) by, or set equal to (=) the following value. The sign is followed by an integer value, which may be appended by either % or $. Without a modifier, the value is treated as a raw point modifier. If appended with a %, this is a percentage of the assignment's total possible score. If appended with a $, it is a percentage of the submission's actual score. (If the actual score to be modified is not numeric, will default to % instead.) Optionally, the modifier may be followed by a / and relative span. At least one value--whether day, hour, or minutes--must be given after a /. When such a span is given, the modifier is applied for each span that the assignment is late. For example, /1d means apply the modifier for each day. Such as span does nothing if applied to an = modifier. If this rule is associated with a -timespan ("bonus policy"), it is applied for each full span. Otherwise, it is applied for each partial span. The rule may be omitted. In this case, the submission is marked late (or early), but no grade change is applied. Examples: +5d:-1/1d - For up to 5 days after the deadline, submissions suffer a penalty of 1 point off for each (partial) day. +48h:-10% - For 2 days after the deadline, any submission loses 10% of the assignment total. (So, for a 5 point assignment, this would be -1 point.) Equivalent to '2d:-10%' (if relying on the assumed + sign on the timespan). -3d:+5$/1d - An example of an "early bonus" policy, this grants a cumulative bonus of 5 percent of the submission's score for each full day early. 20121221-1200:40$ - Using a timestamp to define the span, anything submitted after the deadline but before noon on 21 Dec 2012 will receive 40% of the score it would otherwise have received. (:=40$ or :-60$ would also have been equivalent rules, since = is assumed on rules without a sign.) 1d: - Anything submitted for up to 1 day past the deadline is marked as late but its grade is unaffected. Given only the policies above, any submissions later than the deadline plus the given late period timespan would be refused by Tamarin. +:10% - Anything submitted after the deadline, no matter how late or how bad, is given 10% of the total possible assignment value. : - Anything submitted after the deadline is late but still accepted. """ import tamarin self.raw = policy self.deadline = deadline assert re.match(r'\d{8}-\d{4}', self.deadline) span_re = r'(\+|-)?((\d{8}-\d{4})|((\d+d)?(\d+h)?(\d+m)?))' rule_re = r'(\+|-|=)?(\d*)([%$])?(/(\d+d)?(\d+h)?(\d+m)?)?' parsed = re.match(span_re + ':' + rule_re, policy) if not parsed: raise TamarinError('INVALID_LATE_POLICY', policy) (self.span_sign, unused_offset, self.span_timestamp, relative, self.span_day, self.span_hour, self.span_minute, self.rule_sign, self.rule_value, self.rule_unit, self.rule_repeater, self.rule_day, self.rule_hour, self.rule_minute) = parsed.groups() #apply defaults and constrains if not self.span_sign: self.span_sign = '+' if self.span_timestamp: self.end = self.span_timestamp elif relative: day = hour = minute = 0 sign = -1 if self.span_sign == '-' else 1 if self.span_day: day = int(self.span_day[:-1]) * sign if self.span_hour: hour = int(self.span_hour[:-1]) * sign if self.span_minute: minute = int(self.span_minute[:-1]) * sign endDate = tamarin.convertTimestampToTime(deadline) shift = datetime.timedelta(days=day, hours=hour, minutes=minute) self.end = tamarin.convertTimeToTimestamp(endDate + shift) else: if self.span_sign == '-': self.end = '00000101-0000' else: self.end = '99991231-2359' if self.end == self.deadline: raise TamarinError('INVALID_LATE_POLICY', "Parsable, but deadline == endpoint.") if self.rule_sign or self.rule_value or self.rule_unit or \ self.rule_repeater: #some part of a rule given, so lets see if it's all valid if not self.rule_sign: self.rule_sign = '=' if not self.rule_value: raise TamarinError('INVALID_LATE_POLICY', "No grade change value given.") self.rule_value = int(self.rule_value) if self.rule_repeater and not self.rule_day and \ not self.rule_hour and not self.rule_minute: raise TamarinError('INVALID_LATE_POLICY', "Empty /modifier given: " + self.raw)