def viewEurobook(cid, instr): ''' Function Type: View Function Template: common/eurobook.html Purpose: Display all of the grades for this course. Allow for creation of arbitrary submission entries. Inputs: cid: The object ID of the course to display Template Parameters: TODO Forms Handled: TODO ''' try: c = Course.objects.get(id=cid) if instr and not c in current_user.courseInstructor: abort(403) elif not instr and not c in current_user.gradingCourses(): abort(403) #Get the users for this course s = User.objects.filter(courseStudent=c) s = list(s) s.sort(key=lambda x: x.username) uids = [str(u.id) for u in s] return render_template('common/eurobook.html', course=c, uids=uids,\ instructor=instr) except Course.DoesNotExist: abort(404)
def viewEurobook(cid, instr): ''' Function Type: View Function Template: common/eurobook.html Purpose: Display all of the grades for this course. Allow for creation of arbitrary submission entries. Inputs: cid: The object ID of the course to display Template Parameters: TODO Forms Handled: TODO ''' try: c = Course.objects.get(id=cid) if instr and not c in current_user.courseInstructor: abort(403) elif not instr and not c in current_user.gradingCourses(): abort(403) #Get the users for this course s = User.objects.filter(courseStudent=c) s = list(s) s.sort(key=lambda x:x.username) uids = [str(u.id) for u in s] return render_template('common/eurobook.html', course=c, uids=uids,\ instructor=instr) except Course.DoesNotExist: abort(404)
def editAuxillaryGrades(cid, instr, col): ''' Function Type: View Function Template: instructor/editcolumn.html Purpose: Allows the grutor to edit one column of the gradebook manually Inputs: cid: The object ID of the course to authenticate the grader col: The object ID of the column to be edited Template Parameters: TODO ''' try: course = Course.objects.get(id=cid) column = GBColumn.objects.get(id=col) if instr and not course in current_user.courseInstructor: abort(403) elif not instr and not course in current_user.gradingCourses(): abort(403) users = User.objects.filter(courseStudent=course) for u in users: if not u.keyOfUsername() in column.scores: grade = GBGrade() grade.scores['score'] = 0 grade.save() column.scores[u.keyOfUsername()] = grade column.save() return render_template("common/auxillaryGrades.html", course = course, col=column, users=users, instructor=instr) except (Course.DoesNotExist, GBColumn.DoesNotExist): abort(404)
def grutorGradelistProblem(pid): ''' Function Type: View Function Template: grutor/problems.html Purpose: Display all of the student submissions for the problem specified by <pid>. Inputs: pid: A problem object ID Template Parameters: course: The course which contains the problem specified by <pid> assignment: The assignment group containing the problem specified by <pid> problem: The problem specified by <pid> users: A list of the students who are enrolled in <course> Forms handled: None ''' try: p = Problem.objects.get(id=pid) c, a = p.getParents() #For security purposes we send anyone who isnt in this class to the index if not (c in current_user.gradingCourses()): abort(403) #Get the students for this course students = User.objects.filter(courseStudent=c) return render_template("grutor/problems.html", \ course=c, problem=p, assignment=a, users=students) except Course.DoesNotExist as e: abort(404)
def grutorGradelistProblem(pid): ''' Function Type: View Function Template: grutor/problems.html Purpose: Display all of the student submissions for the problem specified by <pid>. Inputs: pid: A problem object ID Template Parameters: course: The course which contains the problem specified by <pid> assignment: The assignment group containing the problem specified by <pid> problem: The problem specified by <pid> users: A list of the students who are enrolled in <course> Forms handled: None ''' try: p = Problem.objects.get(id=pid) c,a = p.getParents() #For security purposes we send anyone who isnt in this class to the index if not ( c in current_user.gradingCourses()): abort(403) #Get the students for this course students = User.objects.filter(courseStudent=c) return render_template("grutor/problems.html", \ course=c, problem=p, assignment=a, users=students) except Course.DoesNotExist as e: abort(404)
def studentGetFiles(pid, uid, subnum): try: problem = Problem.objects.get(id=pid) c, a = problem.getParents() if not (c in current_user.courseStudent or c in current_user.gradingCourses()): abort(403) u = User.objects.get(id=uid) s = problem.getSubmission(u, subnum) content = request.get_json() filepath = getSubmissionPath(c, a, problem, u, subnum) filepath = os.path.join(filepath, content['filename']) import magic fileType = magic.from_file(filepath, mime=True) fileType = fileType.split('/') if fileType[0] == 'text': try: f = codecs.open(filepath, encoding='utf-8', errors='ignore') content = f.read() return jsonify(majorType=fileType[0], minorType=fileType[1], content=content) except Exception as e: return jsonify(majorType=fileType[0], minorType=fileType[1], content=str(e)) pass finally: f.close() else: return jsonify(majorType=fileType[0], minorType=fileType[1],\ url=url_for('serveFiles', pid=pid, uid=uid, subnum=subnum, filename=content['filename'])) except Problem.DoesNotExist: abort(404)
def serveFiles(pid, uid, subnum, filename): ''' Function Type: Callback-Download Purpose: Downloads the file specified for the user. Inputs: pid: The object ID of the problem that the file belongs to uid: The object ID of the user the file belongs to subnum: The submission number that the file belongs to filename: The filename from the submission to download ''' try: p = Problem.objects.get(id=pid) c,a = p.getParents() #For security purposes we send anyone who isnt in this class to the index if not ( c in current_user.courseStudent or c in current_user.gradingCourses()): abort(403) u = User.objects.get(id=uid) s = p.getSubmission(u, subnum) filepath = getSubmissionPath(c, a, p, u, subnum) return send_file(os.path.join(filepath, filename)) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): #If either p can't be found or we can't get its parents then 404 abort(404)
def grutorSaveGrades(pid, uid, subnum): ''' Function Type: Callback-AJAX Function Called By: grutor/gradesubmission.html:saveGrades() Purpose: Recieves a list of grades from the page and puts them into the grade for this submission. Inputs: pid: The problem that this grade is for uid: The user whose grade this is subnum: The submission number that is currently being graded POST Values: A dictionary mapping names of rubric sections to numbers. Outputs: res: The result. True if it succeeded, False if it failed, and a string if there was an exception. ''' try: p = Problem.objects.get(id=pid) c, a = p.getParents() #For security purposes we send anyone who isnt in this class to the index if not (c in current_user.gradingCourses()): return jsonify(res=False) #Try to get the contents content = request.get_json() #make sure we got the contents if content == None: return jsonify(res=False) #Define function for applying scores to a submission def score(sub): for section in content: sub.grade.scores[section] = content[section] sub.grade.save() sub.save() #End definition #Before we change anything make sure all changes can go through checkMounted() user = User.objects.get(id=uid) sub = p.getSubmission(user, subnum) score(sub) grutorSubArchive(p, c, a, user, sub) if sub.partner != None: score(sub.partnerSubmission) grutorSubArchive(p, c, a, sub.partner, sub.partnerSubmission) return jsonify(res=True, type="grades") except Exception as e: return jsonify(res=False, error=str(e))
def grutorSaveGrades(pid, uid, subnum): ''' Function Type: Callback-AJAX Function Called By: grutor/gradesubmission.html:saveGrades() Purpose: Recieves a list of grades from the page and puts them into the grade for this submission. Inputs: pid: The problem that this grade is for uid: The user whose grade this is subnum: The submission number that is currently being graded POST Values: A dictionary mapping names of rubric sections to numbers. Outputs: res: The result. True if it succeeded, False if it failed, and a string if there was an exception. ''' try: p = Problem.objects.get(id=pid) c,a = p.getParents() #For security purposes we send anyone who isnt in this class to the index if not ( c in current_user.gradingCourses()): return jsonify(res=False) #Try to get the contents content = request.get_json() #make sure we got the contents if content == None: return jsonify(res=False) #Define function for applying scores to a submission def score(sub): for section in content: sub.grade.scores[section] = content[section] sub.grade.save() sub.save() #End definition #Before we change anything make sure all changes can go through checkMounted() user = User.objects.get(id=uid) sub = p.getSubmission(user, subnum) score(sub) grutorSubArchive(p, c, a, user, sub) if sub.partner != None: score(sub.partnerSubmission) grutorSubArchive(p, c, a, sub.partner, sub.partnerSubmission) return jsonify(res=True, type="grades") except Exception as e: return jsonify(res=False, error=str(e))
def grutorGetStatus_Problems(pid): try: p = Problem.objects.get(id=pid) c,a = p.getParents() if not (c in current_user.gradingCourses()): abort(403) u, i, d = p.getStatusCount() return jsonify(u=u, i=i, d=d) except Problem.DoesNotExist: abort(404)
def grutorGetStatus_Problems(pid): try: p = Problem.objects.get(id=pid) c, a = p.getParents() if not (c in current_user.gradingCourses()): abort(403) u, i, d = p.getStatusCount() return jsonify(u=u, i=i, d=d) except Problem.DoesNotExist: abort(404)
def studentViewFiles(pid, uid, subnum): try: problem = Problem.objects.get(id=pid) c, a = problem.getParents() if not (c in current_user.courseStudent or c in current_user.gradingCourses()): abort(403) u = User.objects.get(id=uid) return render_template('student/reviewfiles.html', problem=problem, subnum=subnum,\ user=u, course=c, assignment=a) except Problem.DoesNotExist: abort(404)
def grutorGetStatus_Assignments(pid): try: p = Problem.objects.get(id=pid) c,a = p.getParents() if not (c in current_user.gradingCourses()): abort(403) if c.name != "CS5G": u, i, d = p.getStatusCount() return jsonify(u=u, i=i, d=d) # Special case to avoid calculations for CS5G else: return jsonify(u=0, i=0, d=0) except Problem.DoesNotExist: abort(404)
def grutorGetStatus_Assignments(pid): try: p = Problem.objects.get(id=pid) c, a = p.getParents() if not (c in current_user.gradingCourses()): abort(403) if c.name != "CS5G": u, i, d = p.getStatusCount() return jsonify(u=u, i=i, d=d) # Special case to avoid calculations for CS5G else: return jsonify(u=0, i=0, d=0) except Problem.DoesNotExist: abort(404)
def commonRenderEuros(cid, instr): try: content = request.get_json() c = Course.objects.get(id=cid) u = User.objects.get(id=content['uid']) #If we are accessing the instructor version check that they are an instr if instr and not c in current_user.courseInstructor: abort(403) if not c in current_user.gradingCourses(): abort(403) euroCount, lateAssignments = c.getEuroCountAndLateAssignmentsList(u) # Username outString = "<tr>" outString += "<td>" if instr: outString += u.username if c.anonymousGrading: outString += " (" + c.getIdentifier(u.keyOfUsername()) + ")" else: if c.anonymousGrading: outString += c.getIdentifier(u.keyOfUsername()) else: outString += u.username outString += "</td>" # Total euros used outString += "<td>%d</td>" % (euroCount) # Summary by assignment for a in c.assignments: if a in lateAssignments: outString += "<td style='text-align:center' class='danger'><span class='octicon octicon-check'></span></td>" else: outString += "<td></td>" # Close the row outString += "</tr>" return jsonify(res=outString) except (Course.DoesNotExist, User.DoesNotExist): abort(404)
def commonRenderEuros(cid, instr): try: content = request.get_json() c = Course.objects.get(id=cid) u = User.objects.get(id=content['uid']) #If we are accessing the instructor version check that they are an instr if instr and not c in current_user.courseInstructor: abort(403) if not c in current_user.gradingCourses(): abort(403) euroCount, lateAssignments = c.getEuroCountAndLateAssignmentsList(u) # Username outString = "<tr>" outString += "<td>" if instr: outString += u.username if c.anonymousGrading: outString += " (" + c.getIdentifier(u.keyOfUsername()) + ")" else: if c.anonymousGrading: outString += c.getIdentifier(u.keyOfUsername()) else: outString += u.username outString += "</td>" # Total euros used outString += "<td>%d</td>" % (euroCount) # Summary by assignment for a in c.assignments: if a in lateAssignments: outString += "<td style='text-align:center' class='danger'><span class='octicon octicon-check'></span></td>" else: outString += "<td></td>" # Close the row outString += "</tr>" return jsonify(res=outString) except (Course.DoesNotExist,User.DoesNotExist): abort(404)
def grutorReleaseSubmission(pid, uid, subnum): ''' Function Type: Callback-Redirect Function Purpose: Put the submission back so that it may be chosen by another grader. Inputs: pid: The object ID of the problem this submission belongs to uid: The object ID of the user this submission belongs to subnum: The submission number for this submission Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c, a = p.getParents() user = User.objects.get(id=uid) #For security purposes we send anyone who isnt in this class to the index if not (c in current_user.gradingCourses()): abort(403) #Define function for releasing submissions def release(sub): sub.status = SUBMISSION_TESTED sub.gradedBy = None sub.grade.save() sub.save() #End definition submission = p.getSubmission(user, subnum) #if not submission.status == 4: release(submission) if submission.partner != None: release(submission.partnerSubmission) p.save() return redirect(url_for('grutorGradelistProblem', pid=pid)) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): #If either p can't be found or we can't get its parents then 404 abort(404)
def grutorFinishSubmission(pid, uid, subnum): ''' Function Type: Callback-Redirect Function Purpose: Save all the changes to a given submission and return to the list of problems. Inputs: pid: The object ID of the problem this submission belongs to uid: The object ID of the user this submission belongs to subnum: The submission number for this submission Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c, a = p.getParents() user = User.objects.get(id=uid) #For security purposes we send anyone who isnt in this class to the index if not (c in current_user.gradingCourses()): abort(403) #Define a function for performing closing operations def finish(sub): sub.status = SUBMISSION_GRADED sub.gradedBy = User.objects.get(id=g.user.id) sub.save() #End definition submission = p.getSubmission(user, subnum) finish(submission) #Handle the partners submission as well if submission.partner != None: finish(submission.partnerSubmission) p.save() return redirect(url_for('grutorGradelistProblem', pid=pid)) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): #If either p can't be found or we can't get its parents then 404 abort(404)
def viewGradebook(cid, instr): ''' Function Type: View Function Template: instructor/gradebook.html Purpose: Display all of the grades for this course. Allow for creation of arbitrary submission entries. Inputs: cid: The object ID of the course to display Template Parameters: TODO Forms Handled: TODO ''' try: c = Course.objects.get(id=cid) if instr and not c in current_user.courseInstructor: abort(403) elif not instr and not c in current_user.gradingCourses(): abort(403) #Get the users for this course s = User.objects.filter(courseStudent=c) disableColForm = False colForm = CreateGradeColumnForm() colForm.group.choices = [(x.id,x.name) for x in c.gradeBook.auxillaryGrades] if len(colForm.group.choices) == 0: colForm.group.choices = [("N/A", "N/A")] disableColForm = True s = list(s) s.sort(key=lambda x:x.username) uids = [str(u.id) for u in s] return render_template('common/gradebook.html', course=c, uids=uids,\ groupForm=CreateGradebookGroupForm(),\ colForm=colForm, disableColForm=disableColForm,\ instructor=instr) except Course.DoesNotExist: abort(404)
def grutorReleaseSubmission(pid, uid, subnum): ''' Function Type: Callback-Redirect Function Purpose: Put the submission back so that it may be chosen by another grader. Inputs: pid: The object ID of the problem this submission belongs to uid: The object ID of the user this submission belongs to subnum: The submission number for this submission Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c,a = p.getParents() user = User.objects.get(id=uid) #For security purposes we send anyone who isnt in this class to the index if not ( c in current_user.gradingCourses()): abort(403) #Define function for releasing submissions def release(sub): sub.status = SUBMISSION_TESTED sub.gradedBy = None sub.grade.save() sub.save() #End definition submission = p.getSubmission(user, subnum) #if not submission.status == 4: release(submission) if submission.partner != None: release(submission.partnerSubmission) p.save() return redirect(url_for('grutorGradelistProblem', pid=pid)) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): #If either p can't be found or we can't get its parents then 404 abort(404)
def grutorToggleLate(pid, uid, subnum): ''' Function Type: Callback-Redirect Function Purpose: Toggle the isLate flag for an assignment Inputs: pid: The object ID of the problem this submission belongs to uid: The object ID of the user this submission belongs to subnum: The submission number for this submission Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c, a = p.getParents() user = User.objects.get(id=uid) #For security purposes we send anyone who isnt in this class to the index if not (c in current_user.gradingCourses()): return jsonify(res=False) #Define function for releasing submissions def toggle(sub): sub.isLate = not sub.isLate sub.save() #End definition submission = p.getSubmission(user, subnum) #if not submission.status == 4: toggle(submission) if submission.partner != None: toggle(submission.partnerSubmission) p.save() return jsonify(res=True) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): pass return jsonify(res=False)
def grutorFinishSubmission(pid, uid, subnum): ''' Function Type: Callback-Redirect Function Purpose: Save all the changes to a given submission and return to the list of problems. Inputs: pid: The object ID of the problem this submission belongs to uid: The object ID of the user this submission belongs to subnum: The submission number for this submission Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c,a = p.getParents() user = User.objects.get(id=uid) #For security purposes we send anyone who isnt in this class to the index if not ( c in current_user.gradingCourses()): abort(403) #Define a function for performing closing operations def finish(sub): sub.status = SUBMISSION_GRADED sub.gradedBy = User.objects.get(id=g.user.id) sub.save() #End definition submission = p.getSubmission(user, subnum) finish(submission) #Handle the partners submission as well if submission.partner != None: finish(submission.partnerSubmission) p.save() return redirect(url_for('grutorGradelistProblem', pid=pid)) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): #If either p can't be found or we can't get its parents then 404 abort(404)
def grutorToggleLate(pid, uid, subnum): ''' Function Type: Callback-Redirect Function Purpose: Toggle the isLate flag for an assignment Inputs: pid: The object ID of the problem this submission belongs to uid: The object ID of the user this submission belongs to subnum: The submission number for this submission Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c,a = p.getParents() user = User.objects.get(id=uid) #For security purposes we send anyone who isnt in this class to the index if not ( c in current_user.gradingCourses()): return jsonify(res=False) #Define function for releasing submissions def toggle(sub): sub.isLate = not sub.isLate sub.save() #End definition submission = p.getSubmission(user, subnum) #if not submission.status == 4: toggle(submission) if submission.partner != None: toggle(submission.partnerSubmission) p.save() return jsonify(res=True) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): pass return jsonify(res=False)
def saveGradeColumn(cid,col): try: course = Course.objects.get(id=cid) column = GBColumn.objects.get(id=col) if not (course in current_user.gradingCourses() or current_user.isAdmin): return jsonify(res=False) content = request.get_json() column.maxScore = content['maxScore'] for id in content['scores']: u = User.objects.get(id=id) column.scores[u.keyOfUsername()].scores['score'] = content['scores'][id] column.scores[u.keyOfUsername()].save() column.save() return jsonify(res=True) except Exception as e: return jsonify(res=False, exeption=str(e))
def grutorAssignments(cid): ''' Function Type: View Function Template: grutor/assignments.html Purpose: Display all of the assignment groups and problems in those groups for the course specified by <cid>. Inputs: cid: A course object ID Template Parameters: course: The course object specified by <cid> Forms Handled: None ''' try: c = Course.objects.get(id=cid) #For security purposes we send anyone who isnt grading this class to the index if not ( c in current_user.gradingCourses()): abort(403) return render_template("grutor/assignments.html", course=c) except Course.DoesNotExist as e: abort(404)
def grutorSaveComment(pid, uid, subnum): ''' Function Type: Callback-AJAX Function Called By: grutor/gradesubmission.html:saveComments() Purpose: Recieves a markdown formatted string and saves it as a grader comment for a specified submission Inputs: pid: The problem that this grade is for uid: The user whose grade this is subnum: The submission number that is currently being graded POST Values: A json object containing one field called "text" which contains the markdown formatted string Outputs: res: The result. True if it succeeded, False if it failed, and a string if there was an exception. ''' try: p = Problem.objects.get(id=pid) c, a = p.getParents() #For security purposes we send anyone who isnt in this class to the index if not (c in current_user.gradingCourses()): return jsonify(res=False) #Try to get the contents content = request.get_json() #make sure we got the contents if content == None: return jsonify(res=False) #Define function for saving comments def comment(sub): sub.comments = content['text'] sub.autoGraderComments = content['autotext'] sub.save() user = User.objects.get(id=uid) sub = p.getSubmission(user, subnum) #Before we change anything make sure all changes can go through checkMounted() comment(sub) grutorSubArchive(p, c, a, user, sub) if sub.partner != None: comment(sub.partnerSubmission) grutorSubArchive(p, c, a, sub.partner, sub.partnerSubmission) #Save changes to the problem p.save(cascade=True) return jsonify(res=True, type="comments") except Exception as e: import traceback tb = traceback.format_exc() return jsonify(res=False, error=str(tb))
def grutorGradeRandom(pid): ''' Function Type: Callback-Redirect Function Purpose: Select an ungraded student submission and claim it for the current grader. If it selects a student without a submission one is created. Inputs: pid: The object ID of the problem that is being graded Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c, a = p.getParents() #For security we redirect anyone who shouldn't be here to the index if not (c in current_user.gradingCourses()): abort(403) #Shuffle the users in the course so we can get a random one courseUsers = list(User.objects.filter(courseStudent=c)) random.shuffle(courseUsers) #For each user try to get a submission for them for key in p.studentSubmissions.keys(): username = userFromKey(key) user = User.objects.get(username=username) #Get the pymongo collection for some atomic actions not provided by #the mongoengine wrapper subCol = Submission._get_collection() sub = p.getLatestSubmission(user) if sub == None: flash( "Bad state for user %s. Please notify the administrator." % (username), "error") continue if sub.partner == None: res = subCol.find_and_modify(query={'_id': sub.id, 'status':SUBMISSION_TESTED, 'isLatest':True}, \ update={'$set': {'status':SUBMISSION_GRADING, 'gradedBy': g.user.id}}) else: otherSub = sub.partnerSubmission #We use total lock oerdering to prevent deadlock subList = sorted([sub, otherSub], key=lambda x: x.id) res = subCol.find_and_modify(query={'_id': subList[0].id, 'status':SUBMISSION_TESTED, 'isLatest':True}, \ update={'$set': {'status':SUBMISSION_GRADING, 'gradedBy': g.user.id}}) #res = Submission.objects.exec_js(LOCK_QUERY, id=subList[0].id, uid=g.user.id) if res == None: continue res = subCol.find_and_modify(query={'_id': subList[1].id, 'status':SUBMISSION_TESTED, 'isLatest':True}, \ update={'$set': {'status':SUBMISSION_GRADING, 'gradedBy': g.user.id}}) #res = Submission.objects.exec_js(LOCK_QUERY, id=subList[1].id, uid=g.user.id) if not res == None: return redirect( url_for("grutorGradeSubmission", pid=pid, uid=user.id, subnum=p.getSubmissionNumber(user))) flash("All submissions have been claimed", "warning") flash( "Untested submissions or submissions that crashed the auto-grader are not considered by this button. Please look below for such submissions." ) return redirect(url_for('grutorGradelistProblem', pid=pid)) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): #If either p can't be found or we can't get its parents then 404 abort(404) except User.DoesNotExist: #If the user doesn't exist we have a problem flash( """Successfully locked a submission but the user for that submission couldn't be found in the database. Please contact a system administrator to have them resolve this issue.""", "error") abort(404)
def grutorMakeBlank(pid, uid): ''' Function Type: Callback-Redirect Function Purpose: When a student does not have a submission for a given assignment this function is called. It creates a blank submission with no files and then redirects the grader to the grade submission page. Inputs: pid: The object ID for the problem that is being graded. uid: The object ID of the user who is being graded. Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c, a = p.getParents() user = User.objects.get(id=uid) #For security purposes we send anyone who isnt in this class to the index if not (c in current_user.gradingCourses()): abort(403) #Check that the user we are trying to create a submission for is in the class if not (c in user.courseStudent): flash( "The user you were trying to make a submission for is not in the course." ) abort(404) #Create a blank submission #Create the grade grade = GBGrade() grade.save() p.gradeColumn.scores[user.keyOfUsername()] = grade p.studentSubmissions[user.keyOfUsername()] = StudentSubmissionList() #create a filepath filepath = getSubmissionPath(c, a, p, user, 1) sub = Submission() sub.problem = p #Initial fields for submission sub.filePath = filepath sub.grade = p.gradeColumn.scores[user.keyOfUsername()] sub.submissionTime = datetime.datetime.utcnow() sub.status = SUBMISSION_GRADING sub.gradedBy = User.objects.get(id=g.user.id) sub.save() p.studentSubmissions[user.keyOfUsername()].addSubmission(sub) #The grader is making this so it isn't late sub.isLate = False #Create the needed folders os.makedirs(filepath) p.save(cascade=True) return redirect( url_for('grutorGradeSubmission', uid=uid, pid=pid, subnum=1)) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): #If either p can't be found or we can't get its parents then 404 abort(404)
def grutorGradeRandom(pid): ''' Function Type: Callback-Redirect Function Purpose: Select an ungraded student submission and claim it for the current grader. If it selects a student without a submission one is created. Inputs: pid: The object ID of the problem that is being graded Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c,a = p.getParents() #For security we redirect anyone who shouldn't be here to the index if not (c in current_user.gradingCourses()): abort(403) #Shuffle the users in the course so we can get a random one courseUsers = list(User.objects.filter(courseStudent=c)) random.shuffle(courseUsers) #For each user try to get a submission for them for key in p.studentSubmissions.keys(): username = userFromKey(key) user = User.objects.get(username=username) #Get the pymongo collection for some atomic actions not provided by #the mongoengine wrapper subCol = Submission._get_collection() sub = p.getLatestSubmission(user) if sub == None: flash("Bad state for user %s. Please notify the administrator."%(username), "error") continue if sub.partner == None: res = subCol.find_and_modify(query={'_id': sub.id, 'status':SUBMISSION_TESTED, 'isLatest':True}, \ update={'$set': {'status':SUBMISSION_GRADING, 'gradedBy': g.user.id}}) else: otherSub = sub.partnerSubmission #We use total lock oerdering to prevent deadlock subList = sorted([sub, otherSub], key=lambda x: x.id) res = subCol.find_and_modify(query={'_id': subList[0].id, 'status':SUBMISSION_TESTED, 'isLatest':True}, \ update={'$set': {'status':SUBMISSION_GRADING, 'gradedBy': g.user.id}}) #res = Submission.objects.exec_js(LOCK_QUERY, id=subList[0].id, uid=g.user.id) if res == None: continue res = subCol.find_and_modify(query={'_id': subList[1].id, 'status':SUBMISSION_TESTED, 'isLatest':True}, \ update={'$set': {'status':SUBMISSION_GRADING, 'gradedBy': g.user.id}}) #res = Submission.objects.exec_js(LOCK_QUERY, id=subList[1].id, uid=g.user.id) if not res == None: return redirect(url_for("grutorGradeSubmission", pid=pid, uid=user.id, subnum=p.getSubmissionNumber(user))) flash("All submissions have been claimed", "warning") flash("Untested submissions or submissions that crashed the auto-grader are not considered by this button. Please look below for such submissions.") return redirect(url_for('grutorGradelistProblem', pid=pid)) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): #If either p can't be found or we can't get its parents then 404 abort(404) except User.DoesNotExist: #If the user doesn't exist we have a problem flash("""Successfully locked a submission but the user for that submission couldn't be found in the database. Please contact a system administrator to have them resolve this issue.""", "error") abort(404)
def commonRenderGrade(cid, instr): try: content = request.get_json() c = Course.objects.get(id=cid) u = User.objects.get(id=content['uid']) #If we are accessing the instructor version check that they are an instr if instr and not c in current_user.courseInstructor: abort(403) if not c in current_user.gradingCourses(): abort(403) assignmentScores = getStudentAssignmentScores(c, u) userCourseScore = 0 outString = "<tr>" # <td>{{username/identifier}}</td> outString += "<td>" if instr: outString += u.username if c.anonymousGrading: outString += " (" + c.getIdentifier(u.keyOfUsername()) + ")" else: if c.anonymousGrading: outString += c.getIdentifier(u.keyOfUsername()) else: outString += u.username outString += "</td>" # <td>{{link to problem grading}}</td> for assignment, a in zip(assignmentScores, c.assignments): #If this assignment doesn't have any problems we put a blank column in if len(assignment) == 0: outString += "<td class='active'></td>" continue for problem, p in zip(assignment, a.problems): if problem == None: #If there was no submission link to the make blank page outString += "<td class='active'><a href='" outString += "#'" #TODO Replace this with an actual link outString += ">0.00" outString += "</a></td>" else: highlight = createHighlight(problem) url = url_for('grutorGradeSubmission', pid=p.id, uid=u.id, subnum=p.getSubmissionNumber(u)) if 'finalTotalScore' in problem: points = problem['finalTotalScore'] userCourseScore += problem['finalTotalScore'] else: points = problem['rawTotalScore'] userCourseScore += problem['rawTotalScore'] maxPoints = p.gradeColumn.maxScore cellTemplate = "<td %s><a href='%s'>%.2f</a></td>" % (highlight, url, points) outString += cellTemplate for group in c.gradeBook.auxillaryGrades: if len(group.columns) == 0: outString += "<td class='active'></td>" continue for col in group.columns: score = col.scores.setdefault(u.keyOfUsername(), None) if score: outString += "<td>%.2f</td>" % (score.totalScore()) userCourseScore += score.totalScore() else: outString += "<td>%.2f</td>" % (0) outString += "<td>%.2f</td></tr>" % (userCourseScore) return jsonify(res=outString) except (Course.DoesNotExist,User.DoesNotExist): abort(404)
def grutorSaveComment(pid, uid, subnum): ''' Function Type: Callback-AJAX Function Called By: grutor/gradesubmission.html:saveComments() Purpose: Recieves a markdown formatted string and saves it as a grader comment for a specified submission Inputs: pid: The problem that this grade is for uid: The user whose grade this is subnum: The submission number that is currently being graded POST Values: A json object containing one field called "text" which contains the markdown formatted string Outputs: res: The result. True if it succeeded, False if it failed, and a string if there was an exception. ''' try: p = Problem.objects.get(id=pid) c,a = p.getParents() #For security purposes we send anyone who isnt in this class to the index if not ( c in current_user.gradingCourses()): return jsonify(res=False) #Try to get the contents content = request.get_json() #make sure we got the contents if content == None: return jsonify(res=False) #Define function for saving comments def comment(sub): sub.comments = content['text'] sub.autoGraderComments = content['autotext'] sub.save() user = User.objects.get(id=uid) sub = p.getSubmission(user, subnum) #Before we change anything make sure all changes can go through checkMounted() comment(sub) grutorSubArchive(p, c, a, user, sub) if sub.partner != None: comment(sub.partnerSubmission) grutorSubArchive(p, c, a, sub.partner, sub.partnerSubmission) #Save changes to the problem p.save(cascade=True) return jsonify(res=True, type="comments") except Exception as e: import traceback tb = traceback.format_exc() return jsonify(res=False, error=str(tb))
def grutorGradeSubmission(pid, uid, subnum): ''' Function Type: View Function Template: grutor/gradesubmission.html Purpose: Display to the grader forms that will allow the grader to assign grades and give comments on a submission. Additionally allows the grader to download the files for the submission. Inputs: pid: The object ID of the problem being graded uid: The object ID of the user whose submission is being graded subnum: Which submission of the user is being graded Template Parameters: course: The course this problem is contained in assignment: The assignment group this problem is contained in problem: The problem with ID <pid> subnum: The submission number that is being graded user: The user object specified by <uid> submission: The submission object specified by the user, problem, and subnum Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c, a = p.getParents() user = User.objects.get(id=uid) #For security purposes we send anyone who isnt in this class to the index if not (c in current_user.gradingCourses()): abort(403) #p = Problem.objects.get(id=pid) #a = AssignmentGroup.objects.get(id=aid) submission = p.getSubmission(user, subnum) u = User.objects.get(id=g.user.id) #If this is not in progress by anyone try to claim it atomically if submission.status < SUBMISSION_GRADING: subCol = Submission._get_collection() if submission.partner == None: res = subCol.find_and_modify(query={ '_id': submission.id, 'status': { "$lt": SUBMISSION_GRADING } }, update={ "$set": { "status": SUBMISSION_GRADING, "gradedBy": g.user.id } }) submission.reload() else: otherSub = submission.partnerSubmission subList = sorted([submission, otherSub], key=lambda x: x.id) res = subCol.find_and_modify(query={'_id': subList[0].id, 'status':{"$lt": SUBMISSION_GRADING}}, \ update={'$set': {'status':SUBMISSION_GRADING, 'gradedBy': g.user.id}}) #res = Submission.objects.exec_js(LOCK_QUERY, id=subList[0].id, uid=g.user.id) if res != None: res = subCol.find_and_modify(query={'_id': subList[1].id, 'status':{"$lt": SUBMISSION_GRADING}}, \ update={'$set': {'status':SUBMISSION_GRADING, 'gradedBy': g.user.id}}) if res == None: flash( "It appears another grader has already claimed this assignment are you sure you want to grade it?", "warning") elif submission.gradedBy != u and not submission.status == 4: flash( "It appears another grader has already claimed this assignment are you sure you want to grade it?", "warning") submission.reload() submission.save() p.save() return render_template("grutor/gradesubmission.html", \ course=c, problem=p, assignment=a, subnum=subnum, user=user, submission=submission) except Course.DoesNotExist as e: abort(404)
def grutorGradeSubmission(pid, uid, subnum): ''' Function Type: View Function Template: grutor/gradesubmission.html Purpose: Display to the grader forms that will allow the grader to assign grades and give comments on a submission. Additionally allows the grader to download the files for the submission. Inputs: pid: The object ID of the problem being graded uid: The object ID of the user whose submission is being graded subnum: Which submission of the user is being graded Template Parameters: course: The course this problem is contained in assignment: The assignment group this problem is contained in problem: The problem with ID <pid> subnum: The submission number that is being graded user: The user object specified by <uid> submission: The submission object specified by the user, problem, and subnum Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c,a = p.getParents() user = User.objects.get(id=uid) #For security purposes we send anyone who isnt in this class to the index if not ( c in current_user.gradingCourses()): abort(403) #p = Problem.objects.get(id=pid) #a = AssignmentGroup.objects.get(id=aid) submission = p.getSubmission(user, subnum) u = User.objects.get(id=g.user.id) #If this is not in progress by anyone try to claim it atomically if submission.status < SUBMISSION_GRADING: subCol = Submission._get_collection() if submission.partner == None: res = subCol.find_and_modify(query={'_id':submission.id, 'status': {"$lt": SUBMISSION_GRADING}}, update={"$set": {"status":SUBMISSION_GRADING, "gradedBy": g.user.id}}) submission.reload() else: otherSub = submission.partnerSubmission subList = sorted([submission, otherSub], key=lambda x: x.id) res = subCol.find_and_modify(query={'_id': subList[0].id, 'status':{"$lt": SUBMISSION_GRADING}}, \ update={'$set': {'status':SUBMISSION_GRADING, 'gradedBy': g.user.id}}) #res = Submission.objects.exec_js(LOCK_QUERY, id=subList[0].id, uid=g.user.id) if res != None: res = subCol.find_and_modify(query={'_id': subList[1].id, 'status':{"$lt": SUBMISSION_GRADING}}, \ update={'$set': {'status':SUBMISSION_GRADING, 'gradedBy': g.user.id}}) if res == None: flash("It appears another grader has already claimed this assignment are you sure you want to grade it?", "warning") elif submission.gradedBy != u and not submission.status == 4: flash("It appears another grader has already claimed this assignment are you sure you want to grade it?", "warning") submission.reload() submission.save() p.save() return render_template("grutor/gradesubmission.html", \ course=c, problem=p, assignment=a, subnum=subnum, user=user, submission=submission) except Course.DoesNotExist as e: abort(404)
def grutorMakeBlank(pid, uid): ''' Function Type: Callback-Redirect Function Purpose: When a student does not have a submission for a given assignment this function is called. It creates a blank submission with no files and then redirects the grader to the grade submission page. Inputs: pid: The object ID for the problem that is being graded. uid: The object ID of the user who is being graded. Forms Handled: None ''' try: p = Problem.objects.get(id=pid) c,a = p.getParents() user = User.objects.get(id=uid) #For security purposes we send anyone who isnt in this class to the index if not (c in current_user.gradingCourses()): abort(403) #Check that the user we are trying to create a submission for is in the class if not (c in user.courseStudent): flash("The user you were trying to make a submission for is not in the course.") abort(404) #Create a blank submission #Create the grade grade = GBGrade() grade.save() p.gradeColumn.scores[user.keyOfUsername()] = grade p.studentSubmissions[user.keyOfUsername()] = StudentSubmissionList() #create a filepath filepath = getSubmissionPath(c, a, p, user, 1) sub = Submission() sub.problem = p #Initial fields for submission sub.filePath = filepath sub.grade = p.gradeColumn.scores[user.keyOfUsername()] sub.submissionTime = datetime.datetime.utcnow() sub.status = SUBMISSION_GRADING sub.gradedBy = User.objects.get(id=g.user.id) sub.save() p.studentSubmissions[user.keyOfUsername()].addSubmission(sub) #The grader is making this so it isn't late sub.isLate = False #Create the needed folders os.makedirs(filepath) p.save(cascade=True) return redirect(url_for('grutorGradeSubmission', uid=uid, pid=pid, subnum=1)) except (Problem.DoesNotExist, Course.DoesNotExist, AssignmentGroup.DoesNotExist): #If either p can't be found or we can't get its parents then 404 abort(404)