def markAllAsVerified(assignName, silent=False): """ Marks all graded files in the given assignment directory as human-verified. If silent is True, won't print any feedback. """ files = tamarin.getSubmissions(assignment=assignName, submitted=False) files.sort() if not silent: print('<p class="strip"><b>Marking these files as human ' 'verified:</b><br>') marked = 0 for f in files: # convert to GradedFile objects gf = GradedFile(os.path.basename(f)) if not gf.humanVerified: if not silent: print(gf.graderOutputFilename + ' ==> ', end='') gf.humanVerified = True gf.update() marked += 1 if not silent: print(gf.graderOutputFilename + '<br>') if not silent: print('<p class="strip">Marked ' + str(marked) + ' of ' + str(len(files)) + ' submissions for ' + assignName + ' as human-verified.</p>') print('<p class="strip"><b>Done.</b></p>')
def getResubmissionGradeAdjustment(self, submissionCount=None): """ Returns the penalty due to additional submissions for this assignment. Penalty can be more than can be covered by the grade itself. Returns the penalty even for non-numeric grades. The submissionCount is a performance boost. If the number of submissions (including this one) is given here, will be used instead of polling the file system to (re)collect the same data. """ import tamarin if not submissionCount: # need to count the files here files = tamarin.getSubmissions(user=self.username, assignment=self.assignment) submissionCount = len(files) if submissionCount <= 1: return 0 else: adj = (submissionCount - 1) * tamarin.RESUBMISSION_PENALTY return round(adj, tamarin.GRADE_PRECISION)
def displayAssignmentSubmissions(user, assignmentName, brief=False, master=False): """ Displays all the submissions the user made for this assignment. In brief mode, this will just be a list of files, including the grade for each one. Each listing will be a button (or link, if in master mode) to the appropriate submission view. If in full mode (that is, brief=False), will show each submission expanded within this view. At the top off all submissions, will include a header listing the assignment and the final grade based on last submission and late policy adjustments. """ assignment = Assignment(assignmentName) files = tamarin.getSubmissions(user=user, assignment=assignmentName) # calculate final grade and status for this assignment # (Assuming that even ungraded and grader-error submissions count as # submissions, we only need the grade of the last submission to have an # accurate final grade.) grade = '<i>Not yet submitted.</i>' reason = None if files: lastSubmit = files[-1] if tamarin.SUBMITTED_ROOT in lastSubmit: grade = '<i>Not yet graded.</i>' lastFile = SubmittedFile(os.path.basename(lastSubmit)) else: lastFile = GradedFile(os.path.basename(lastSubmit)) grade = lastFile.getAdjustedGrade(len(files)) lateness = lastFile.getLateGradeAdjustment() resubmits = lastFile.getResubmissionGradeAdjustment(len(files)) if lateness or resubmits: # explain grade calcs reason = '[= ' + str(lastFile.grade) if lateness: reason += ' ' + str(lateness) offset = lastFile.getLateOffset() label = 'early' if offset[0] == '-' else 'late' reason += ' <i>(' + label + ' ' + offset + ')</i>' if resubmits: reason += ' ' + str(resubmits) reason += ' <i>(' + str(len(files) - 1) + \ ' resubmits)</i>' reason += ']' elif lastFile.isLate(): # just a little informational timestamping reason = '<small>' + lastFile.getLateOffset() + '</small>' # mark grade if not verified yet if not lastFile.humanVerified: grade = '<span class="unverified">' + str(grade) + \ tamarin.UNVERIFIED_GRADE_LABEL + '</span>' else: #no files submitted at all yet if assignment.isTooLate(): #can't submit, so grade goes to 0 grade = 0 reason = '<i>Too late to submit.</i>' # print submission list header, starting with assignment grade print('<div class="submissionList">') print('<div class="assignment">') print('<table class="assignment"><tr><td class="assignment">') print('<b>' + assignment.name + '</b> ') print('<small>(Due: ' + assignment.due + '. ') print('Total: ' + str(assignment.maxScore) + ' points.)</small></td>') print('<td class="grade"><b>Grade:</b> ' + str(grade) + '</td>') if reason: print('<td class="reason">' + reason + '</td>') print('</tr></table>') #list contents if files and brief: print('<ul>') for f in files: if brief: if master: print('<li><a href="masterview.py?submission=' + os.path.basename(f) + '"', end='') if tamarin.MASTER_LINKS_OPEN_NEW_WINDOW: print(' target="_blank"', end='') print('>' + os.path.basename(f) + '</a>', end=' ') else: print('<li><input type="submit" name="submission" value="' + os.path.basename(f) + '">', end=' ') if tamarin.SUBMITTED_ROOT in f: print(' [<i>Not yet graded.</i>]') else: graded = GradedFile(os.path.basename(f)) shortGrade = str(graded.grade) if not graded.humanVerified: shortGrade += tamarin.SHORT_UNVERIFIED_GRADE_LABEL if graded.humanComment: shortGrade += tamarin.HUMAN_COMMENT_LABEL print(' [' + shortGrade + ']') else: displaySubmission(os.path.basename(f), master) #list footer if files and brief: print('</ul>') print('</div></div>')
def displayGradeSheet(comma=False): """ Produces a grade sheet view for all users and assignments. Rows correspond to users, sorted by username. Rows contain the Tamarin adjusted scores for each assignment followed by the total adjusted score. By default, uses tabs as delimiters, with the user column padded to 14 characters to maintain a readable format even when faced with slightly longer usernames. If comma is True, just uses commas and no padding. Unverified grades (and any totals depending on them) are marked with an appended ?. Missing/unsubmitted grades are left blank. Submitted but ungraded submissions are ignored (so blank if no other submissions for that assignment). The total score is the sum of all numeric values, ignoring any OK, X, or ERR grades. If there are no numeric values, the total is ERR, X, or OK (in that order) if such a value is a component grade. Otherwise, the total is blank. If at least one score is tentative (unverified), the total is also tentative. """ users = tamarin.getUsers() delim = ',' if comma else '\t' # build data structure sheet = dict() for user in users: sheet[user] = {} # scores keyed by assignment name # now process each assignment assignments = tamarin.getAssignments() # already sorted for assign in assignments: subs = tamarin.getSubmissions(assignment=assign, submitted=False) # overwrite until last sub for each user, keyed by lower username submissions = {} for sub in subs: sub = os.path.basename(sub) usr = re.match(r"(\w+)" + assign, sub).group(1).lower() submissions[usr] = sub for user in users: if user in submissions: sub = GradedFile(submissions[user]) grd = sub.getAdjustedGrade() # add to total grade list: (grade, verified?) if 'Total' not in sheet[user]: sheet[user]['Total'] = [grd, True] elif isinstance(grd, float) or isinstance(grd, int): # grd is a number, so overwrite/adds to whatever is there if isinstance(sheet[user]['Total'][0], str): sheet[user]['Total'][0] = grd else: sheet[user]['Total'][0] += grd else: # grd is a string, so maybe replace any str or else ignore if isinstance(sheet[user]['Total'][0], str): if sheet[user]['Total'][0] == 'OK': sheet[user]['Total'][0] = grd elif sheet[user]['Total'][0] == 'X' and grd != 'OK': sheet[user]['Total'][0] = grd else: pass # grade is already ERR else: pass # grade is a number, so ignore this string grd grd = str(grd) if not sub.humanVerified: grd += '?' sheet[user]['Total'][1] = False sheet[user][assign] = grd else: sheet[user][assign] = '' # blank # print details # header print(',' if comma else '{:14}\t'.format(' '), end='') for assign in assignments: print(assign + delim, end='') print('Total') for user in users: print(user + ',' if comma else '{:14}\t'.format(user), end='') for assign in assignments: print(sheet[user][assign] + delim, end='') # print final total if 'Total' in sheet[user]: if isinstance(sheet[user]['Total'][0], float): sheet[user]['Total'][0] = round(sheet[user]['Total'][0], tamarin.GRADE_PRECISION) print(sheet[user]['Total'][0], end='') if not sheet[user]['Total'][1]: print('?', end='') print('')