Exemple #1
0
def shared_team_show(ctx, course, team_id):
    team = course.get_team(team_id)
    if team is None:
        print "Team %s does not exist" % team_id
        ctx.exit(CHISUBMIT_FAIL)

    print "Team name: %s" % team.id
    print
    print "Extensions available: %i" % team.extensions_available
    print

    if len(team.extras) > 0:
        print "Attributes:"
        for k, v in team.extras.items():
            print " - %s: %s" % (k, v)
        print

    if len(team.students) == 0:
        print "No students in this team"
    else:
        print "STUDENTS"
        print "--------"
        for student in team.students:
            if student.status == 0:
                status = "UNCONFIRMED"
            elif student.status == 1:
                status = "CONFIRMED"
            else:
                raise ChisubmitException(
                    "Student '%s' in team '%s' has unknown status %i" %
                    (student.user.id, team.id, student.status))
            print "%s: %s, %s  (%s)" % (student.user.id,
                                        student.user.last_name,
                                        student.user.first_name, status)

    print

    if len(team.assignments) == 0:
        print "This team is not registered for any assignments."
    else:
        print "ASSIGNMENTS"
        print "-----------"
        for ta in team.assignments:
            assignment = course.get_assignment(ta.assignment_id)
            print "ID: %s" % assignment.id
            print "Name: %s" % assignment.name
            print "Deadline: %s" % convert_datetime_to_local(
                assignment.deadline).isoformat(" ")
            if ta.submitted_at is not None:
                print "Last submitted at: %s" % convert_datetime_to_local(
                    ta.submitted_at).isoformat(" ")
                print "Commit SHA: %s" % ta.commit_sha
                print "Extensions used: %i" % ta.extensions_used
            else:
                print "NOT SUBMITTED"
            print
Exemple #2
0
def student_assignment_show_deadline(ctx, course, assignment_id, utc):
    assignment = course.get_assignment(assignment_id)
    if assignment is None:
        print("Assignment %s does not exist" % assignment_id)
        ctx.exit(CHISUBMIT_FAIL)

    now_utc = get_datetime_now_utc()
    now_local = convert_datetime_to_local(now_utc)

    deadline_utc = assignment.deadline
    deadline_local = convert_datetime_to_local(deadline_utc)

    print(assignment.name)
    print()
    if utc:
        print("      Now (Local): %s" % now_local.isoformat(" "))
        print(" Deadline (Local): %s" % deadline_local.isoformat(" "))
        print()
        print("        Now (UTC): %s" % now_utc.isoformat(" "))
        print("   Deadline (UTC): %s" % deadline_utc.isoformat(" "))
    else:
        print("      Now: %s" % now_local.isoformat(" "))
        print(" Deadline: %s" % deadline_local.isoformat(" "))

    print()

    extensions = compute_extensions_needed(now_utc, deadline_utc)

    if extensions == 0:
        diff = deadline_utc - now_utc
    else:
        diff = now_utc - deadline_utc

    days = diff.days
    hours = diff.seconds // 3600
    minutes = (diff.seconds // 60) % 60
    seconds = diff.seconds % 60

    if extensions == 0:
        print("The deadline has not yet passed")
        print("You have %i days, %i hours, %i minutes, %i seconds left" %
              (days, hours, minutes, seconds))
    else:
        print(
            "The deadline passed %i days, %i hours, %i minutes, %i seconds ago"
            % (days, hours, minutes, seconds))
        print(
            "If you submit your assignment now, you will need to use %i extensions"
            % extensions)

    return CHISUBMIT_SUCCESS
Exemple #3
0
def shared_team_show(ctx, course, team_id):
    team = course.get_team(team_id)
    if team is None:
        print "Team %s does not exist" % team_id
        ctx.exit(CHISUBMIT_FAIL)
        
    print "Team name: %s" % team.id
    print
    print "Extensions available: %i" % team.extensions_available 
    print
    
    if len(team.extras) > 0:
        print "Attributes:"
        for k,v in team.extras.items():
            print " - %s: %s" % (k,v)
        print
    
    if len(team.students) == 0:
        print "No students in this team"
    else:
        print "STUDENTS"
        print "--------"
        for student in team.students:
            if student.status == 0:
                status = "UNCONFIRMED"
            elif student.status == 1:
                status = "CONFIRMED"
            else:
                raise ChisubmitException("Student '%s' in team '%s' has unknown status %i" % (student.user.id, team.id, student.status))
            print "%s: %s, %s  (%s)" % (student.user.id, student.user.last_name, student.user.first_name, status)

    print

    if len(team.assignments) == 0:
        print "This team is not registered for any assignments."
    else:
        print "ASSIGNMENTS"
        print "-----------"
        for ta in team.assignments:
            assignment = course.get_assignment(ta.assignment_id)
            print "ID: %s" % assignment.id
            print "Name: %s" % assignment.name
            print "Deadline: %s" % convert_datetime_to_local(assignment.deadline).isoformat(" ")        
            if ta.submitted_at is not None:
                print "Last submitted at: %s" % convert_datetime_to_local(ta.submitted_at).isoformat(" ")
                print "Commit SHA: %s" % ta.commit_sha
                print "Extensions used: %i" % ta.extensions_used
            else:
                print "NOT SUBMITTED"    
            print    
Exemple #4
0
def shared_team_show(ctx, course, team_id):
    team = get_team_or_exit(ctx, course, team_id)
        
    print "Team name: %s" % team.team_id
    print
    if course.extension_policy == "per-team":
        print "Extensions available: %i" % team.extensions 
        print
    
    team_members = team.get_team_members()
    
    if len(team_members) == 0:
        print "No students in this team"
    else:
        print "STUDENTS"
        print "--------"
        for team_member in team_members:
            if team_member.confirmed:
                status = "CONFIRMED"
            else:
                status = "UNCONFIRMED"

            user = team_member.student.user

            print "%s: %s, %s  (%s)" % (user.username, user.last_name, user.first_name, status)

    print

    registrations = team.get_assignment_registrations()

    if len(registrations) == 0:
        print "This team is not registered for any assignments."
    else:
        print "ASSIGNMENTS"
        print "-----------"
        for r in registrations:
            assignment = r.assignment
            print "ID: %s" % assignment.assignment_id
            print "Name: %s" % assignment.name
            print "Deadline: %s" % convert_datetime_to_local(assignment.deadline).isoformat(" ")        
            if r.final_submission is not None:
                print "Last submitted at: %s" % convert_datetime_to_local(r.final_submission.submitted_at).isoformat(" ")
                print "Commit SHA: %s" % r.final_submission.commit_sha
                print "Extensions used: %i" % r.final_submission.extensions_used
            else:
                print "NOT SUBMITTED"    
            print    
Exemple #5
0
def student_assignment_show_deadline(ctx, course, assignment_id, utc):
    assignment = course.get_assignment(assignment_id)
    if assignment is None:
        print("Assignment %s does not exist" % assignment_id)
        ctx.exit(CHISUBMIT_FAIL)

    now_utc = get_datetime_now_utc()
    now_local = convert_datetime_to_local(now_utc)

    deadline_utc = assignment.deadline
    deadline_local = convert_datetime_to_local(deadline_utc)

    print(assignment.name)
    print()
    if utc:
        print("      Now (Local): %s" % now_local.isoformat(" "))
        print(" Deadline (Local): %s" % deadline_local.isoformat(" "))
        print()
        print("        Now (UTC): %s" % now_utc.isoformat(" "))
        print("   Deadline (UTC): %s" % deadline_utc.isoformat(" "))
    else:
        print("      Now: %s" % now_local.isoformat(" "))
        print(" Deadline: %s" % deadline_local.isoformat(" "))

    print()

    extensions = compute_extensions_needed(now_utc, deadline_utc)

    if extensions == 0:
        diff = deadline_utc - now_utc
    else:
        diff = now_utc - deadline_utc

    days = diff.days
    hours = diff.seconds // 3600
    minutes = (diff.seconds//60)%60
    seconds = diff.seconds%60

    if extensions == 0:
        print("The deadline has not yet passed")
        print("You have %i days, %i hours, %i minutes, %i seconds left" % (days, hours, minutes, seconds))
    else:
        print("The deadline passed %i days, %i hours, %i minutes, %i seconds ago" % (days, hours, minutes, seconds))
        print("If you submit your assignment now, you will need to use %i extensions" % extensions)

    return CHISUBMIT_SUCCESS
def shared_assignment_list(ctx, course, ids, utc):
    assignments = course.get_assignments()
    assignments.sort(key=operator.attrgetter("deadline"))

    for assignment in assignments:
        if ids:
            print assignment.assignment_id
        else:
            if utc:
                deadline = assignment.deadline.isoformat(" ")
            else:
                deadline = convert_datetime_to_local(assignment.deadline).isoformat(" ")

            fields = [assignment.assignment_id, deadline, assignment.name]

            print "\t".join(fields)

    return CHISUBMIT_SUCCESS
Exemple #7
0
def student_assignment_submit(ctx, course, team_id, assignment_id, commit_sha, extensions, force, yes):
    assignment = course.get_assignment(assignment_id)
    if assignment is None:
        print "Assignment %s does not exist" % assignment_id
        ctx.exit(CHISUBMIT_FAIL)
    
    team = course.get_team(team_id)
    if team is None:
        print "Team %s does not exist" % team_id
        ctx.exit(CHISUBMIT_FAIL)
    
    ta = team.get_assignment(assignment_id)
    if ta is None:
        print "Team %s is not registered for assignment %s" % (team_id, assignment_id)
        ctx.exit(CHISUBMIT_FAIL)
        
    if team.has_assignment_ready_for_grading(assignment):
        print "You cannot re-submit this assignment."
        print "You made a submission before the deadline, and the deadline has passed."

        ctx.exit(CHISUBMIT_FAIL)
        
    conn = create_connection(course, ctx.obj['config'])
    
    if conn is None:
        print "Could not connect to git server."
        ctx.exit(CHISUBMIT_FAIL)
    
    commit = conn.get_commit(course, team, commit_sha)
    
    if commit is None:
        print "Commit %s does not exist in repository" % commit_sha
        ctx.exit(CHISUBMIT_FAIL)

    response = assignment.submit(team_id, commit_sha, extensions, dry_run=True)

    success = response["success"]
    dry_run = response["dry_run"]

    deadline_utc = parse(response["submission"]["deadline"])
    
    submitted_at_utc = parse(response["submission"]["submitted_at"])
    extensions_needed = response["submission"]["extensions_needed"]
    extensions_requested = response["submission"]["extensions_requested"]    

    extensions_available_before = response["team"]["extensions_available_before"]
    extensions_available = response["team"]["extensions_available"]

    if response["prior_submission"]["submitted_at"] is not None:    
        prior_submitted_at_utc = parse(response["prior_submission"]["submitted_at"])
        prior_submitted_at_local = convert_datetime_to_local(prior_submitted_at_utc)
    prior_commit_sha = response["prior_submission"]["commit_sha"]
    prior_extensions_used = response["prior_submission"]["extensions_used"]

    deadline_local = convert_datetime_to_local(deadline_utc)
    submitted_at_local = convert_datetime_to_local(submitted_at_utc)
        
    if not success:
        if extensions_needed > extensions_available:
            msg1 = "You do not have enough extensions to submit this assignment."
            msg2 = "You would need %i extensions to submit this assignment at this " \
                   "time, but you only have %i left" % (extensions_needed, extensions_available)
        elif extensions_requested < extensions_needed:
            msg1 = "The number of extensions you have requested is insufficient."
            msg2 = "You need to request %s extensions." % extensions_needed
        elif extensions_requested > extensions_needed:
            msg1 = "The number of extensions you have requested is excessive."
            msg2 = "You only need to request %s extensions." % extensions_needed

        print
        print msg1
        print            
        print "     Deadline (UTC): %s" % deadline_utc.isoformat(sep=" ")
        print "          Now (UTC): %s" % submitted_at_utc.isoformat(sep=" ")
        print 
        print "   Deadline (Local): %s" % deadline_local.isoformat(sep=" ")
        print "        Now (Local): %s" % submitted_at_local.isoformat(sep=" ")
        print 
        print msg2 
        print

        ctx.exit(CHISUBMIT_FAIL)
    else:             
        if prior_commit_sha is not None:
            submission_commit = conn.get_commit(course, team, prior_commit_sha)
            
            if prior_commit_sha == commit_sha:
                print "You have already submitted assignment %s" % assignment.id
                print "You submitted the following commit on %s:" % prior_submitted_at_local
                print
                if submission_commit is None:
                    print "WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha
                else:
                    print_commit(submission_commit)
                print
                print "You are trying to submit the same commit again (%s)" % prior_commit_sha
                print "If you want to re-submit, please specify a different commit"
                ctx.exit(CHISUBMIT_FAIL)
                
            if not force:
                print        
                print "You have already submitted assignment %s" % assignment.id
                print "You submitted the following commit on %s:" % prior_submitted_at_local
                print
                if submission_commit is None:
                    print "WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha
                else:
                    print_commit(submission_commit)
                print
                print "If you want to submit again, please use the --force option"
                ctx.exit(CHISUBMIT_FAIL)
            else:
                print
                print "WARNING: You have already submitted assignment %s and you" % assignment.id 
                print "are about to overwrite the previous submission of the following commit:"
                print
                if submission_commit is None:
                    print "WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha
                else:
                    print_commit(submission_commit)
                print
    
        if prior_commit_sha is not None and force:
            msg = "THE ABOVE SUBMISSION FOR %s (%s) WILL BE CANCELLED." % (assignment.id, assignment.name)
            
            print "!"*len(msg)
            print msg
            print "!"*len(msg)
            print
            print "If you continue, your submission for %s (%s)" % (assignment.id, assignment.name)
            print "will now point to the following commit:"                
        else:
            print "You are going to make a submission for %s (%s)." % (assignment.id, assignment.name)
            print "The commit you are submitting is the following:"
        print
        print_commit(commit)
        print
        print "PLEASE VERIFY THIS IS THE EXACT COMMIT YOU WANT TO SUBMIT"
        print
        print "Your team currently has %i extensions" % (extensions_available_before)
        print
        if prior_commit_sha is not None:
            print "You used %i extensions in your previous submission of this assignment." % prior_extensions_used
            print "and you are going to use %i additional extensions now." % (extensions_needed - prior_extensions_used)
        else:
            print "You are going to use %i extensions on this submission." % extensions_needed
        print
        print "You will have %i extensions left after this submission." % extensions_available
        print 
        print "Are you sure you want to continue? (y/n): ", 
        
        if not yes:
            yesno = raw_input()
        else:
            yesno = 'y'
            print 'y'
        
        if yesno in ('y', 'Y', 'yes', 'Yes', 'YES'):
            response = assignment.submit(team_id, commit_sha, extensions, dry_run=False)
              
            # TODO: Can't do this until GitLab supports updating tags
            #    
            # message = "Extensions: %i\n" % extensions_requested
            # if submission_tag is None:
            #     conn.create_submission_tag(course, team, tag_name, message, commit.sha)
            # else:
            #     conn.update_submission_tag(course, team, tag_name, message, commit.sha)
                
            print
            if response["success"]:
                print "Your submission has been completed."
            else:
                print "ERROR: Your submission was not completed."
            
        return CHISUBMIT_SUCCESS
    def test_complete_with_multiple_instructors_multiple_graders(self, runner):
        course_id = u"cmsc40200"
        course_name = u"Foobarmentals of Foobar"

        admin_id = u"admin"
        instructor_ids = [u"instructor1", u"instructor2"]
        grader_ids = [u"grader1", u"grader2"]
        student_ids = [u"student1", u"student2", u"student3", u"student4"]

        all_users = instructor_ids + grader_ids + student_ids

        admin, instructors, graders, students = self.create_clients(
            runner,
            admin_id,
            instructor_ids,
            grader_ids,
            student_ids,
            course_id,
            verbose=True)
        self.create_users(admin, all_users)

        self.create_course(admin, course_id, course_name)

        self.add_users_to_course(admin, course_id, instructors, graders,
                                 students)

        students_team = [[s] for s in students]

        deadline = get_datetime_now_utc() + timedelta(hours=1)
        deadline = convert_datetime_to_local(deadline)
        deadline = deadline.replace(tzinfo=None).isoformat(sep=" ")

        result = instructors[0].run(
            "instructor assignment add",
            ["pa1", "Programming Assignment 1", deadline])
        self.assertEqual(result.exit_code, 0)

        pa1_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 
        
Total Points: 0 / 100
"""

        with open("pa1.rubric.txt", "w") as f:
            f.write(pa1_rubric)

        result = instructors[0].run("instructor assignment add-rubric",
                                    ["pa1", "pa1.rubric.txt"])
        self.assertEqual(result.exit_code, 0)

        result = admin.run(
            "admin course show",
            ["--include-users", "--include-assignments", course_id])
        self.assertEqual(result.exit_code, 0)

        for student_id, student in zip(student_ids, students):
            self.register_team([student], student_id, "pa1", course_id)

        for student_id, student in zip(student_ids, students):
            result = student.run("student team list")
            self.assertEqual(result.exit_code, 0)
            self.assertIn(student_id, result.output)

            result = student.run("student team show", [student_id])
            self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team list")
        self.assertEqual(result.exit_code, 0)

        for student_id in student_ids:
            result = instructors[0].run("instructor team show", [student_id])
            self.assertEqual(result.exit_code, 0)

        student_git_paths, student_git_repos, team_commits = self.create_team_repos(
            admin, course_id, student_ids, students_team)

        for student_id, student in zip(student_ids, students):
            result = student.run("student assignment submit", ["pa1", "--yes"])
            self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

            result = student.run("student team show", [student_id])
            self.assertEqual(result.exit_code, 0)

        # Let the deadline "pass"
        new_now = get_datetime_now_utc() + timedelta(hours=2)
        set_testing_now(new_now)

        print()
        print("~~~ Time has moved 'forward' by two hours ~~~")
        print()

        # The "master instructor" creates the grading repos

        result = instructors[0].run("instructor grading list-submissions",
                                    ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading create-grading-repos",
                                    ["--master", "pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading assign-grader",
                                    ["pa1", "student1", "grader1"])
        self.assertEqual(result.exit_code, 0)
        result = instructors[0].run("instructor grading assign-grader",
                                    ["pa1", "student2", "grader1"])
        self.assertEqual(result.exit_code, 0)
        result = instructors[0].run("instructor grading assign-grader",
                                    ["pa1", "student3", "grader2"])
        self.assertEqual(result.exit_code, 0)
        result = instructors[0].run("instructor grading assign-grader",
                                    ["pa1", "student4", "grader2"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run(
            "instructor grading list-grader-assignments", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading show-grading-status",
                                    ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        # The non-master instructor downloads the grading repos

        result = instructors[1].run("instructor grading create-grading-repos",
                                    ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[1].run("instructor grading show-grading-status",
                                    ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        # Grader 1 pulls their grading
        result = graders[0].run("grader pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        student1_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (
            course_id, "pa1", "student1")
        student2_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (
            course_id, "pa1", "student2")

        student_git_repos[0], student_git_paths[0] = graders[
            0].get_local_git_repository(student1_grading_repo_path)
        student_git_repos[1], student_git_paths[1] = graders[
            0].get_local_git_repository(student2_grading_repo_path)

        student1_rubric_path = "%s/pa1.rubric.txt" % student_git_paths[0]
        student2_rubric_path = "%s/pa1.rubric.txt" % student_git_paths[1]

        # Grader 1 grades student1 and pushes the grading.

        student1_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 45

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 30
        
Penalties:
    Used O(n^156) algorithm: -10
    Submitted code in a Word document: -30

Bonuses:
    Worked alone: 15

Total Points: 50 / 100

Comments: >
    None"""

        with open(student1_rubric_path, "w") as f:
            f.write(student1_rubric)

        result = graders[0].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        student_git_repos[0].index.add(["pa1.rubric.txt"])
        student_git_repos[0].index.commit("Finished grading")

        result = graders[0].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        # The non-master instructor pulls the repos and generates a report

        result = instructors[1].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[1].run("instructor grading show-grading-status",
                                    ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        # Grader 1 grades student2 and pushes the grading.

        student2_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 50

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 45

Total Points: 95 / 100

Comments: >
    Great job!"""

        with open(student2_rubric_path, "w") as f:
            f.write(student2_rubric)

        result = graders[0].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        student_git_repos[1].index.add(["pa1.rubric.txt"])
        student_git_repos[1].index.commit("Finished grading")

        result = graders[0].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        # The non-master instructor pulls the repos and generates a report

        result = instructors[1].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[1].run("instructor grading show-grading-status",
                                    ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        # Grader 2 pulls their grading

        result = graders[1].run("grader pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        student3_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (
            course_id, "pa1", "student3")
        student4_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (
            course_id, "pa1", "student4")

        student_git_repos[2], student_git_paths[2] = graders[
            1].get_local_git_repository(student3_grading_repo_path)
        student_git_repos[3], student_git_paths[3] = graders[
            1].get_local_git_repository(student4_grading_repo_path)

        student3_rubric_path = "%s/pa1.rubric.txt" % student_git_paths[2]
        student4_rubric_path = "%s/pa1.rubric.txt" % student_git_paths[3]

        # Grader 2 adds the empty rubrics (which should be generated by pull-grading)

        result = graders[1].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        student_git_repos[2].index.add(["pa1.rubric.txt"])
        student_git_repos[2].index.commit("Added rubric")
        student_git_repos[3].index.add(["pa1.rubric.txt"])
        student_git_repos[3].index.commit("Added rubric")

        result = graders[1].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        # The non-master instructor pulls the repos and generates a report

        result = instructors[1].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[1].run("instructor grading show-grading-status",
                                    ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        # Grader 2 grades student3 but does only a partial grading of student4

        student3_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 20

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 15

Total Points: 35 / 100

Comments: >
    Needs improvement!"""

        with open(student3_rubric_path, "w") as f:
            f.write(student3_rubric)

        student4_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 35

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 

Total Points: 35 / 100

Comments: >
"""

        with open(student4_rubric_path, "w") as f:
            f.write(student4_rubric)

        result = graders[1].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        student_git_repos[2].index.add(["pa1.rubric.txt"])
        student_git_repos[2].index.commit("Finished grading")

        student_git_repos[3].index.add(["pa1.rubric.txt"])
        student_git_repos[3].index.commit("Grading in progress")

        result = graders[1].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        # The non-master instructor pulls the repos and generates a report

        result = instructors[1].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[1].run("instructor grading show-grading-status",
                                    ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        # Grader 2 finishes grading

        student4_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 35

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 25

Total Points: 60 / 100

Comments: >
"""

        with open(student4_rubric_path, "w") as f:
            f.write(student4_rubric)

        result = graders[1].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        student_git_repos[3].index.add(["pa1.rubric.txt"])
        student_git_repos[3].index.commit("Finished grading")

        result = graders[1].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        # The non-master instructor pulls the repos and generates a report

        result = instructors[1].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[1].run("instructor grading show-grading-status",
                                    ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        # The master instructor pulls the repos and pushes them to the students

        result = instructors[0].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading show-grading-status",
                                    ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading collect-rubrics",
                                    ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run(
            "instructor grading show-grading-status",
            ["--use-stored-grades", "--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading list-grades")
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading push-grading",
                                    ["--to-students", "--yes", "pa1"])
        self.assertEqual(result.exit_code, 0)

        for student_id, student in zip(student_ids, students):
            repo, path = student.get_local_git_repository(student_id)
            repo.remote("origin").pull("pa1-grading:pa1-grading")
            repo.heads["pa1-grading"].checkout()
            self.assertTrue(os.path.exists(path + "/pa1.rubric.txt"))
Exemple #9
0
def student_assignment_submit(ctx, course, team_id, assignment_id, commit_sha,
                              extensions, force, yes):
    assignment = course.get_assignment(assignment_id)
    if assignment is None:
        print "Assignment %s does not exist" % assignment_id
        ctx.exit(CHISUBMIT_FAIL)

    team = course.get_team(team_id)
    if team is None:
        print "Team %s does not exist" % team_id
        ctx.exit(CHISUBMIT_FAIL)

    ta = team.get_assignment(assignment_id)
    if ta is None:
        print "Team %s is not registered for assignment %s" % (team_id,
                                                               assignment_id)
        ctx.exit(CHISUBMIT_FAIL)

    if team.has_assignment_ready_for_grading(assignment):
        print "You cannot re-submit this assignment."
        print "You made a submission before the deadline, and the deadline has passed."

        ctx.exit(CHISUBMIT_FAIL)

    conn = create_connection(course, ctx.obj['config'])

    if conn is None:
        print "Could not connect to git server."
        ctx.exit(CHISUBMIT_FAIL)

    commit = conn.get_commit(course, team, commit_sha)

    if commit is None:
        print "Commit %s does not exist in repository" % commit_sha
        ctx.exit(CHISUBMIT_FAIL)

    response = assignment.submit(team_id, commit_sha, extensions, dry_run=True)

    success = response["success"]
    dry_run = response["dry_run"]

    deadline_utc = parse(response["submission"]["deadline"])

    submitted_at_utc = parse(response["submission"]["submitted_at"])
    extensions_needed = response["submission"]["extensions_needed"]
    extensions_requested = response["submission"]["extensions_requested"]

    extensions_available_before = response["team"][
        "extensions_available_before"]
    extensions_available = response["team"]["extensions_available"]

    if response["prior_submission"]["submitted_at"] is not None:
        prior_submitted_at_utc = parse(
            response["prior_submission"]["submitted_at"])
        prior_submitted_at_local = convert_datetime_to_local(
            prior_submitted_at_utc)
    prior_commit_sha = response["prior_submission"]["commit_sha"]
    prior_extensions_used = response["prior_submission"]["extensions_used"]

    deadline_local = convert_datetime_to_local(deadline_utc)
    submitted_at_local = convert_datetime_to_local(submitted_at_utc)

    if not success:
        if extensions_needed > extensions_available:
            msg1 = "You do not have enough extensions to submit this assignment."
            msg2 = "You would need %i extensions to submit this assignment at this " \
                   "time, but you only have %i left" % (extensions_needed, extensions_available)
        elif extensions_requested < extensions_needed:
            msg1 = "The number of extensions you have requested is insufficient."
            msg2 = "You need to request %s extensions." % extensions_needed
        elif extensions_requested > extensions_needed:
            msg1 = "The number of extensions you have requested is excessive."
            msg2 = "You only need to request %s extensions." % extensions_needed

        print
        print msg1
        print
        print "     Deadline (UTC): %s" % deadline_utc.isoformat(sep=" ")
        print "          Now (UTC): %s" % submitted_at_utc.isoformat(sep=" ")
        print
        print "   Deadline (Local): %s" % deadline_local.isoformat(sep=" ")
        print "        Now (Local): %s" % submitted_at_local.isoformat(sep=" ")
        print
        print msg2
        print

        ctx.exit(CHISUBMIT_FAIL)
    else:
        if prior_commit_sha is not None:
            submission_commit = conn.get_commit(course, team, prior_commit_sha)

            if prior_commit_sha == commit_sha:
                print "You have already submitted assignment %s" % assignment.id
                print "You submitted the following commit on %s:" % prior_submitted_at_local
                print
                if submission_commit is None:
                    print "WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha
                else:
                    print_commit(submission_commit)
                print
                print "You are trying to submit the same commit again (%s)" % prior_commit_sha
                print "If you want to re-submit, please specify a different commit"
                ctx.exit(CHISUBMIT_FAIL)

            if not force:
                print
                print "You have already submitted assignment %s" % assignment.id
                print "You submitted the following commit on %s:" % prior_submitted_at_local
                print
                if submission_commit is None:
                    print "WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha
                else:
                    print_commit(submission_commit)
                print
                print "If you want to submit again, please use the --force option"
                ctx.exit(CHISUBMIT_FAIL)
            else:
                print
                print "WARNING: You have already submitted assignment %s and you" % assignment.id
                print "are about to overwrite the previous submission of the following commit:"
                print
                if submission_commit is None:
                    print "WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha
                else:
                    print_commit(submission_commit)
                print

        if prior_commit_sha is not None and force:
            msg = "THE ABOVE SUBMISSION FOR %s (%s) WILL BE CANCELLED." % (
                assignment.id, assignment.name)

            print "!" * len(msg)
            print msg
            print "!" * len(msg)
            print
            print "If you continue, your submission for %s (%s)" % (
                assignment.id, assignment.name)
            print "will now point to the following commit:"
        else:
            print "You are going to make a submission for %s (%s)." % (
                assignment.id, assignment.name)
            print "The commit you are submitting is the following:"
        print
        print_commit(commit)
        print
        print "PLEASE VERIFY THIS IS THE EXACT COMMIT YOU WANT TO SUBMIT"
        print
        print "Your team currently has %i extensions" % (
            extensions_available_before)
        print
        if prior_commit_sha is not None:
            print "You used %i extensions in your previous submission of this assignment." % prior_extensions_used
            print "and you are going to use %i additional extensions now." % (
                extensions_needed - prior_extensions_used)
        else:
            print "You are going to use %i extensions on this submission." % extensions_needed
        print
        print "You will have %i extensions left after this submission." % extensions_available
        print
        print "Are you sure you want to continue? (y/n): ",

        if not yes:
            yesno = raw_input()
        else:
            yesno = 'y'
            print 'y'

        if yesno in ('y', 'Y', 'yes', 'Yes', 'YES'):
            response = assignment.submit(team_id,
                                         commit_sha,
                                         extensions,
                                         dry_run=False)

            # TODO: Can't do this until GitLab supports updating tags
            #
            # message = "Extensions: %i\n" % extensions_requested
            # if submission_tag is None:
            #     conn.create_submission_tag(course, team, tag_name, message, commit.sha)
            # else:
            #     conn.update_submission_tag(course, team, tag_name, message, commit.sha)

            print
            if response["success"]:
                print "Your submission has been completed."
            else:
                print "ERROR: Your submission was not completed."

        return CHISUBMIT_SUCCESS
Exemple #10
0
def instructor_assignment_submit(ctx, course, team_id, assignment_id,
                                 commit_sha, extensions):
    team = get_team_or_exit(ctx, course, team_id)
    registration = get_assignment_registration_or_exit(ctx, team,
                                                       assignment_id)

    conn = create_connection(course, ctx.obj['config'])

    if conn is None:
        print "Could not connect to git server."
        ctx.exit(CHISUBMIT_FAIL)

    commit = conn.get_commit(course, team, commit_sha)

    if commit is None:
        print "Commit %s does not exist in repository" % commit_sha
        ctx.exit(CHISUBMIT_FAIL)

    if registration.final_submission is not None:
        prior_commit_sha = registration.final_submission.commit_sha
        prior_submitted_at_utc = registration.final_submission.submitted_at
        prior_submitted_at_local = convert_datetime_to_local(
            prior_submitted_at_utc)

        submission_commit = conn.get_commit(course, team, prior_commit_sha)

        if prior_commit_sha == commit_sha:
            print "The team has already submitted assignment %s" % registration.assignment.assignment_id
            print "They submitted the following commit on %s:" % prior_submitted_at_local
            print
            if submission_commit is None:
                print "WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha
            else:
                print_commit(submission_commit)
            print
            print "You are trying to submit the same commit again (%s)" % prior_commit_sha
            print "If you want to re-submit, please specify a different commit"
            ctx.exit(CHISUBMIT_FAIL)

        print
        print "WARNING: This team has already submitted assignment %s and you" % registration.assignment.assignment_id
        print "are about to overwrite the previous submission of the following commit:"
        print
        if submission_commit is None:
            print "WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha
        else:
            print_commit(submission_commit)
        print

    if registration.final_submission is not None:
        msg = "THE ABOVE SUBMISSION FOR %s (%s) WILL BE CANCELLED." % (
            registration.assignment.assignment_id,
            registration.assignment.name)

        print "!" * len(msg)
        print msg
        print "!" * len(msg)
        print
        print "If you continue, their submission for %s (%s)" % (
            registration.assignment.assignment_id,
            registration.assignment.name)
        print "will now point to the following commit:"
    else:
        print "You are going to make a submission for %s (%s)." % (
            registration.assignment.assignment_id,
            registration.assignment.name)
        print "The commit you are submitting is the following:"
    print
    print_commit(commit)
    print
    print "PLEASE VERIFY THIS IS THE EXACT COMMIT YOU WANT TO SUBMIT"
    print
    print "Are you sure you want to continue? (y/n): ",

    yesno = raw_input()

    if yesno in ('y', 'Y', 'yes', 'Yes', 'YES'):
        try:
            submit_response = registration.submit(
                commit_sha, extensions_override=extensions, dry_run=False)

            print "The submission has been completed."
            return CHISUBMIT_SUCCESS

        except BadRequestException, bre:
            print "ERROR: The submission was not completed. The server reported the following errors:"
            bre.print_errors()
            return CHISUBMIT_FAIL
Exemple #11
0
    def test_complete_with_extensions_per_team(self, runner):
        course_id = u"cmsc40200"
        course_name = u"Foobarmentals of Foobar"

        admin_id = u"admin"
        instructor_ids = [u"instructor"]
        grader_ids = [u"grader"]
        student_ids = [u"student1", u"student2", u"student3", u"student4"]

        all_users = instructor_ids + grader_ids + student_ids

        admin, instructors, graders, students = self.create_clients(
            runner, course_id, admin_id, instructor_ids, grader_ids,
            student_ids)
        self.create_users(admin, all_users)

        self.create_course(admin, course_id, course_name)

        result = admin.run("admin course set-option %s default-extensions 2" %
                           (course_id))
        self.assertEquals(result.exit_code, 0)

        result = admin.run(
            "admin course set-option %s extension-policy per_team" %
            (course_id))
        self.assertEquals(result.exit_code, 0)

        self.add_users_to_course(admin, course_id, instructors, graders,
                                 students)

        teams = ["the-flaming-foobars", "the-magnificent-mallocs"]

        students_team = [students[0:2], students[2:4]]

        deadline = get_datetime_now_utc() - timedelta(hours=23)
        deadline = convert_datetime_to_local(deadline)
        deadline = deadline.replace(tzinfo=None).isoformat(sep=" ")

        result = instructors[0].run(
            "instructor assignment add",
            ["pa1", "Programming Assignment 1", deadline])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run(
            "instructor assignment add-grade-component",
            ["pa1", "tests", "The PA1 Tests", "50"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run(
            "instructor assignment add-grade-component",
            ["pa1", "design", "The PA1 Design", "50"])
        self.assertEquals(result.exit_code, 0)

        deadline = get_datetime_now_utc() - timedelta(hours=49)
        deadline = deadline.isoformat(sep=" ")

        result = instructors[0].run(
            "instructor assignment add",
            ["pa2", "Programming Assignment 2", deadline])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run(
            "instructor assignment add-grade-component",
            ["pa2", "tests", "The PA2 Tests", "50"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run(
            "instructor assignment add-grade-component",
            ["pa2", "design", "The PA2 Design", "50"])
        self.assertEquals(result.exit_code, 0)

        result = admin.run(
            "admin course show",
            ["--include-users", "--include-assignments", course_id])
        self.assertEquals(result.exit_code, 0)

        self.register_team(students_team[0], teams[0], "pa1", course_id)
        self.register_team(students_team[1], teams[1], "pa1", course_id)

        self.register_team(students_team[0], teams[0], "pa2", course_id)

        result = students_team[0][0].run("student team list")
        self.assertEquals(result.exit_code, 0)
        self.assertIn(teams[0], result.output)
        self.assertNotIn(teams[1], result.output)

        result = students_team[1][0].run("student team list")
        self.assertEquals(result.exit_code, 0)
        self.assertIn(teams[1], result.output)
        self.assertNotIn(teams[0], result.output)

        result = students_team[0][0].run("student team show", [teams[1]])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        result = students_team[1][0].run("student team show", [teams[0]])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        result = instructors[0].run("instructor team set-attribute",
                                    [teams[0], "alias", "foobar " + teams[0]])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor team set-attribute",
                                    [teams[1], "alias", "foobar " + teams[1]])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor team list")
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor team show", [teams[0]])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor team show", [teams[1]])
        self.assertEquals(result.exit_code, 0)

        team_git_paths, team_git_repos, team_commits = self.create_team_repos(
            admin, course_id, teams, students_team)

        # Try to submit without enough extensions
        result = students_team[0][0].run("student assignment submit", [
            teams[0], "pa1", team_commits[0][0].hexsha, "--extensions", "0",
            "--yes"
        ])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        # Try to submit with too many extensions
        result = students_team[0][0].run("student assignment submit", [
            teams[0], "pa1", team_commits[0][0].hexsha, "--extensions", "2",
            "--yes"
        ])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        # Submit with just the right number
        result = students_team[0][0].run("student assignment submit", [
            teams[0], "pa1", team_commits[0][0].hexsha, "--extensions", "1",
            "--yes"
        ])
        self.assertEquals(result.exit_code, CHISUBMIT_SUCCESS)

        result = students_team[0][0].run("student team show", [teams[0]])
        self.assertEquals(result.exit_code, 0)

        # Try submitting an already-submitted assignment
        result = students_team[0][0].run("student assignment submit", [
            teams[0], "pa1", team_commits[0][1].hexsha, "--extensions", "1",
            "--yes"
        ])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        # Try submitting an already-submitted assignment, with the same
        # commit as before
        result = students_team[0][0].run("student assignment submit", [
            teams[0], "pa1", team_commits[0][0].hexsha, "--extensions", "1",
            "--yes", "--force"
        ])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        # Submit an already-submitted assignment
        result = students_team[0][0].run("student assignment submit", [
            teams[0], "pa1", team_commits[0][1].hexsha, "--extensions", "1",
            "--yes", "--force"
        ])
        self.assertEquals(result.exit_code, CHISUBMIT_SUCCESS)

        result = students_team[0][0].run("student team show", [teams[0]])
        self.assertEquals(result.exit_code, 0)

        # Try requesting more extensions than the team has
        result = students_team[0][0].run("student assignment submit", [
            teams[0], "pa2", team_commits[0][1].hexsha, "--extensions", "3",
            "--yes"
        ])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        # Try submitting for a project the team is not registered for
        result = students_team[1][0].run("student assignment submit", [
            teams[1], "pa2", team_commits[1][1].hexsha, "--extensions", "0",
            "--yes"
        ])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        result = students_team[1][0].run("student assignment submit", [
            teams[1], "pa1", team_commits[1][1].hexsha, "--extensions", "1",
            "--yes"
        ])
        self.assertEquals(result.exit_code, CHISUBMIT_SUCCESS)

        result = instructors[0].run("instructor grading list-submissions",
                                    ["pa1"])
        self.assertEquals(result.exit_code, 0)

        # Let the deadline "pass"
        new_now = get_datetime_now_utc() + timedelta(hours=2)
        set_testing_now(new_now)

        print
        print "~~~ Time has moved 'forward' by two hours ~~~"
        print

        result = instructors[0].run("instructor grading list-submissions",
                                    ["pa1"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading create-grading-repos",
                                    ["pa1"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run(
            "instructor grading create-grading-branches", ["pa1"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading set-grade",
                                    [teams[0], "pa1", "tests", "100"])
        self.assertEquals(result.exit_code, 1)

        result = instructors[0].run("instructor grading set-grade",
                                    [teams[0], "pa1", "tests", "40"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading set-grade",
                                    [teams[1], "pa1", "tests", "45"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading list-grades")
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading set-grade",
                                    [teams[0], "pa1", "tests", "50"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading list-grades")
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading add-rubrics",
                                    ["pa1", "--commit"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading assign-graders",
                                    ["pa1"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run(
            "instructor grading list-grader-assignments", ["pa1"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading push-grading-branches",
                                    ["--to-staging", "pa1"])
        self.assertEquals(result.exit_code, 0)

        result = graders[0].run("grader create-local-grading-repos",
                                [graders[0].user_id, "pa1"])
        self.assertEquals(result.exit_code, 0)

        team1_grading_repo_path = ".chisubmit/repositories/%s/%s/%s" % (
            course_id, "pa1", teams[0])
        team2_grading_repo_path = ".chisubmit/repositories/%s/%s/%s" % (
            course_id, "pa1", teams[1])

        team_git_repos[0], team_git_paths[0] = graders[
            0].get_local_git_repository(team1_grading_repo_path)
        team_git_repos[1], team_git_paths[1] = graders[
            0].get_local_git_repository(team2_grading_repo_path)

        team1_rubric_path = "%s/pa1.rubric.txt" % team_git_paths[0]
        team2_rubric_path = "%s/pa1.rubric.txt" % team_git_paths[1]

        team1_rubric = """Points:
    The PA1 Tests:
        Points Possible: 50
        Points Obtained: 45

    The PA1 Design:
        Points Possible: 50
        Points Obtained: 30
        
Penalties:
    Used O(n^156) algorithm: -10
    Submitted code in a Word document: -30

Total Points: 35 / 100

Comments: >
    None"""

        with open(team1_rubric_path, "w") as f:
            f.write(team1_rubric)

        result = graders[0].run(
            "grader validate-rubrics",
            [graders[0].user_id, "pa1", "--only", teams[0]])
        self.assertEquals(result.exit_code, 0)

        team_git_repos[0].index.add(["pa1.rubric.txt"])
        team_git_repos[0].index.commit("Finished grading")

        with open("%s/bar" % team_git_paths[1], "a") as f:
            f.write("Great job!\n")

        team2_rubric = """Points:
    The PA1 Tests:
        Points Possible: 50
        Points Obtained: 50

    The PA1 Design:
        Points Possible: 50
        Points Obtained: 45

Total Points: 95 / 100

Comments: >
    Great job!"""

        with open(team2_rubric_path, "w") as f:
            f.write(team2_rubric)

        result = graders[0].run(
            "grader validate-rubrics",
            [graders[0].user_id, "pa1", "--only", teams[1]])
        self.assertEquals(result.exit_code, 0)

        team_git_repos[1].index.add(["pa1.rubric.txt"])
        team_git_repos[1].index.add(["bar"])
        team_git_repos[1].index.commit("Finished grading")

        result = graders[0].run("grader validate-rubrics",
                                [graders[0].user_id, "pa1"])
        self.assertEquals(result.exit_code, 0)

        result = graders[0].run("grader push-grading-branches",
                                [graders[0].user_id, "pa1"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading pull-grading-branches",
                                    ["--from-staging", "pa1"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading collect-rubrics",
                                    ["pa1"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading list-grades")
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading push-grading-branches",
                                    ["--to-students", "pa1"])
        self.assertEquals(result.exit_code, 0)

        team_git_repos[0], team_git_paths[0] = students_team[0][
            0].get_local_git_repository(teams[0])
        team_git_repos[0].remote("origin").pull("pa1-grading:pa1-grading")
        team_git_repos[0].heads["pa1-grading"].checkout()
        self.assertTrue(os.path.exists(team_git_paths[0] + "/pa1.rubric.txt"))

        team_git_repos[1], team_git_paths[1] = students_team[1][
            0].get_local_git_repository(teams[1])
        team_git_repos[1].remote("origin").pull("pa1-grading:pa1-grading")
        team_git_repos[1].heads["pa1-grading"].checkout()
        self.assertTrue(os.path.exists(team_git_paths[0] + "/pa1.rubric.txt"))
        self.assertIn("Great job!", open(team_git_paths[1] + "/bar").read())
Exemple #12
0
def student_assignment_submit(ctx, course, assignment_id, commit_sha, yes):
    assignment = get_assignment_or_exit(ctx, course, assignment_id)

    # Determine team for this assignment
    team, registration = get_team_registration_from_user(ctx, course, assignment)
    team_members = team.get_team_members()    
                
    title = "SUBMISSION FOR ASSIGNMENT %s (%s)" % (assignment.assignment_id, assignment.name)
    print title
    print "-" * len(title)
    print
    if len(team_members) == 1:
        student = team_members[0].student
        individual = True
        print "This is an INDIVIDUAL submission for %s %s" % (student.user.first_name, student.user.last_name)
    else:
        students = [tm.student for tm in team_members]
        individual = False
        print "This is a TEAM submission for team %s with the following students:" % team.team_id
        for s in students:
            print " - %s %s" % (s.user.first_name, s.user.last_name)
    print
                
    conn = create_connection(course, ctx.obj['config'])
    
    if conn is None:
        print "Could not connect to git server."
        ctx.exit(CHISUBMIT_FAIL)
    
    if commit_sha is None:
        commit = conn.get_latest_commit(course, team)

        if commit is None:
            print "It seems there are no commits in your repository, so I cannot submit anything"
            ctx.exit(CHISUBMIT_FAIL)
                
        user_specified_commit = False
    else:    
        commit = conn.get_commit(course, team, commit_sha)
        
        if commit is None:
            print "Commit %s does not exist in repository" % commit_sha
            ctx.exit(CHISUBMIT_FAIL)

        user_specified_commit = True
        
    try:
        submit_response = registration.submit(commit.sha, dry_run = True)
    except BadRequestException, bre:
        response_data = bre.json
        
        if "extensions_needed" in response_data and "extensions_available" in response_data:
            extensions_needed = response_data["extensions_needed"]
            extensions_available = response_data["extensions_available"]
            
            deadline_utc = parse(response_data["deadline"])
            submitted_at_utc = parse(response_data["submitted_at"])
            deadline_local = convert_datetime_to_local(deadline_utc)
            submitted_at_local = convert_datetime_to_local(submitted_at_utc)        
            
            if extensions_needed > extensions_available:
                msg1 = "You do not have enough extensions to submit this assignment."
                msg2 = "You would need %i extensions to submit this assignment at this " \
                       "time, but you only have %i left" % (extensions_needed, extensions_available)
    
                print
                print msg1
                print            
                print "     Deadline (UTC): %s" % deadline_utc.isoformat(sep=" ")
                print "          Now (UTC): %s" % submitted_at_utc.isoformat(sep=" ")
                print 
                print "   Deadline (Local): %s" % deadline_local.isoformat(sep=" ")
                print "        Now (Local): %s" % submitted_at_local.isoformat(sep=" ")
                print 
                print msg2 
                print
            else:
                print "ERROR: Your submission cannot be completed. The server reported the following:"
                print
                bre.print_errors()
        else:
            print "ERROR: Your submission cannot be completed. The server reported the following:"
            print
            bre.print_errors()

        ctx.exit(CHISUBMIT_FAIL)
Exemple #13
0
            else:
                print "ERROR: Your submission cannot be completed. The server reported the following:"
                print
                bre.print_errors()
        else:
            print "ERROR: Your submission cannot be completed. The server reported the following:"
            print
            bre.print_errors()

        ctx.exit(CHISUBMIT_FAIL)
    
    if registration.final_submission is not None:
        prior_commit_sha = registration.final_submission.commit_sha
        prior_extensions_used = registration.final_submission.extensions_used             
        prior_submitted_at_utc = registration.final_submission.submitted_at
        prior_submitted_at_local = convert_datetime_to_local(prior_submitted_at_utc)            
        
        submission_commit = conn.get_commit(course, team, prior_commit_sha)            
        
        if prior_commit_sha == commit.sha:
            print "You have already submitted assignment %s" % registration.assignment.assignment_id
            print
            print "You submitted the following commit on %s:" % prior_submitted_at_local
            print
            if submission_commit is None:
                print "WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha
            else:
                print_commit(submission_commit)
            print
            if user_specified_commit:
                print "You are trying to submit the same commit again (%s)" % prior_commit_sha
    def test_complete_with_extensions_per_team(self, runner):
        course_id = u"cmsc40200"
        course_name = u"Foobarmentals of Foobar"

        admin_id = u"admin"
        instructor_ids = [u"instructor"]
        grader_ids = [u"grader"]
        student_ids = [u"student1", u"student2", u"student3", u"student4"]

        all_users = instructor_ids + grader_ids + student_ids

        admin, instructors, graders, students = self.create_clients(
            runner,
            admin_id,
            instructor_ids,
            grader_ids,
            student_ids,
            course_id,
            verbose=True)
        self.create_users(admin, all_users)

        self.create_course(admin, course_id, course_name)

        result = admin.run(
            "admin course set-attribute %s default_extensions 2" % (course_id))
        self.assertEqual(result.exit_code, 0)

        result = admin.run(
            "admin course set-attribute %s extension_policy per-team" %
            (course_id))
        self.assertEqual(result.exit_code, 0)

        self.add_users_to_course(admin, course_id, instructors, graders,
                                 students)

        teams = ["student1-student2", "student3-student4"]

        students_team = [students[0:2], students[2:4]]

        deadline = get_datetime_now_utc() - timedelta(hours=23)
        deadline = convert_datetime_to_local(deadline)
        deadline = deadline.replace(tzinfo=None).isoformat(sep=" ")

        result = instructors[0].run(
            "instructor assignment add",
            ["pa1", "Programming Assignment 1", deadline])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute",
                                    ["pa1", "min_students", "2"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute",
                                    ["pa1", "max_students", "2"])
        self.assertEqual(result.exit_code, 0)

        pa1_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 
        
Total Points: 0 / 100
"""

        with open("pa1.rubric.txt", "w") as f:
            f.write(pa1_rubric)

        result = instructors[0].run("instructor assignment add-rubric",
                                    ["pa1", "pa1.rubric.txt"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment show-rubric",
                                    ["pa1"])
        self.assertEqual(result.exit_code, 0)

        deadline = get_datetime_now_utc() - timedelta(hours=49)
        deadline = deadline.isoformat(sep=" ")

        result = instructors[0].run(
            "instructor assignment add",
            ["pa2", "Programming Assignment 2", deadline])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute",
                                    ["pa2", "min_students", "2"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute",
                                    ["pa2", "max_students", "2"])
        self.assertEqual(result.exit_code, 0)

        pa2_rubric = """Points:
    - The PA2 Tests:
        Points Possible: 50
        Points Obtained: 

    - The PA2 Design:
        Points Possible: 50
        Points Obtained: 
        
Total Points: 0 / 100
"""

        with open("pa2.rubric.txt", "w") as f:
            f.write(pa2_rubric)

        result = instructors[0].run("instructor assignment add-rubric",
                                    ["pa2", "pa2.rubric.txt"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment show-rubric",
                                    ["pa2"])
        self.assertEqual(result.exit_code, 0)

        result = admin.run(
            "admin course show",
            ["--include-users", "--include-assignments", course_id])
        self.assertEqual(result.exit_code, 0)

        self.register_team(students_team[0], teams[0], "pa1", course_id)
        self.register_team(students_team[1], teams[1], "pa1", course_id)

        self.register_team(students_team[0], teams[0], "pa2", course_id)

        result = students_team[0][0].run("student team list")
        self.assertEqual(result.exit_code, 0)
        self.assertIn(teams[0], result.output)
        self.assertNotIn(teams[1], result.output)

        result = students_team[1][0].run("student team list")
        self.assertEqual(result.exit_code, 0)
        self.assertIn(teams[1], result.output)
        self.assertNotIn(teams[0], result.output)

        result = students_team[0][0].run("student team show", [teams[0]])
        self.assertEqual(result.exit_code, 0)

        result = students_team[0][0].run("student team show", [teams[1]])
        self.assertEqual(result.exit_code, CHISUBMIT_FAIL)

        result = students_team[1][0].run("student team show", [teams[1]])
        self.assertEqual(result.exit_code, 0)

        result = students_team[1][0].run("student team show", [teams[0]])
        self.assertEqual(result.exit_code, CHISUBMIT_FAIL)

        result = instructors[0].run("instructor team list")
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team show", [teams[0]])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team show", [teams[1]])
        self.assertEqual(result.exit_code, 0)

        team_git_paths, team_git_repos, team_commits = self.create_team_repos(
            admin, course_id, teams, students_team)

        # Submit with just the right number
        result = students_team[0][0].run("student assignment submit",
                                         ["pa1", "--yes"])
        self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

        result = students_team[0][0].run("student team show", [teams[0]])
        self.assertEqual(result.exit_code, 0)

        # Try submitting an already-submitted assignment, with the same
        # commit as before (without --commit-sha)
        result = students_team[0][0].run("student assignment submit",
                                         ["pa1", "--yes"])
        self.assertEqual(result.exit_code, CHISUBMIT_FAIL)

        # Try submitting an already-submitted assignment, with the same
        # commit as before (with --commit-sha)
        result = students_team[0][0].run(
            "student assignment submit",
            ["pa1", "--yes", "--commit-sha", team_commits[0][1].hexsha])
        self.assertEqual(result.exit_code, CHISUBMIT_FAIL)

        # Submit an already-submitted assignment
        result = students_team[0][0].run(
            "student assignment submit",
            ["pa1", "--yes", "--commit-sha", team_commits[0][0].hexsha])
        self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

        result = students_team[0][0].run("student team show", [teams[0]])
        self.assertEqual(result.exit_code, 0)

        # Try submitting for a project the team is not registered for
        result = students_team[1][0].run("student assignment submit",
                                         ["pa2", "--yes"])
        self.assertEqual(result.exit_code, CHISUBMIT_FAIL)

        result = students_team[1][0].run(
            "student assignment submit",
            ["pa1", "--yes", "--commit-sha", team_commits[1][0].hexsha])
        self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

        result = students_team[1][0].run("student assignment submit",
                                         ["pa1", "--yes"])
        self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

        result = instructors[0].run("instructor grading list-submissions",
                                    ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos",
                                    ["repos/all/"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos",
                                    ["--assignment", "pa1", "repos/pa1/"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos", [
            "--assignment", "pa1", "repos/ready/", "--only-ready-for-grading"
        ])
        self.assertEqual(result.exit_code, 0)

        # Let the deadline "pass"
        new_now = get_datetime_now_utc() + timedelta(hours=2)
        set_testing_now(new_now)

        print()
        print("~~~ Time has moved 'forward' by two hours ~~~")
        print()

        result = instructors[0].run("instructor grading list-submissions",
                                    ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos",
                                    ["repos/all/"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos",
                                    ["--assignment", "pa1", "repos/pa1/"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos", [
            "--assignment", "pa1", "repos/ready/", "--only-ready-for-grading"
        ])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading create-grading-repos",
                                    ["--master", "pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading assign-graders",
                                    ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run(
            "instructor grading list-grader-assignments", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = graders[0].run("grader pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        team1_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (
            course_id, "pa1", teams[0])
        team2_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (
            course_id, "pa1", teams[1])

        team_git_repos[0], team_git_paths[0] = graders[
            0].get_local_git_repository(team1_grading_repo_path)
        team_git_repos[1], team_git_paths[1] = graders[
            0].get_local_git_repository(team2_grading_repo_path)

        team1_rubric_path = "%s/pa1.rubric.txt" % team_git_paths[0]
        team2_rubric_path = "%s/pa1.rubric.txt" % team_git_paths[1]

        team1_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 45

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 30
        
Penalties:
    Used O(n^156) algorithm: -10
    Submitted code in a Word document: -30

Bonuses:
    Worked alone: 15

Total Points: 50 / 100

Comments: >
    None"""

        with open(team1_rubric_path, "w") as f:
            f.write(team1_rubric)

        result = graders[0].run("grader validate-rubrics",
                                ["pa1", "--only", teams[0]])
        self.assertEqual(result.exit_code, 0)

        team_git_repos[0].index.add(["pa1.rubric.txt"])
        team_git_repos[0].index.commit("Finished grading")

        with open("%s/bar" % team_git_paths[1], "a") as f:
            f.write("Great job!\n")

        team2_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 50

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 45

Total Points: 95 / 100

Comments: >
    Great job!"""

        with open(team2_rubric_path, "w") as f:
            f.write(team2_rubric)

        result = graders[0].run("grader validate-rubrics",
                                ["pa1", "--only", teams[1]])
        self.assertEqual(result.exit_code, 0)

        team_git_repos[1].index.add(["pa1.rubric.txt"])
        team_git_repos[1].index.add(["bar"])
        team_git_repos[1].index.commit("Finished grading")

        result = graders[0].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = graders[0].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = graders[0].run("instructor grading validate-rubrics",
                                ["pa1", "--only", teams[0]])
        self.assertEqual(result.exit_code, 0)

        result = graders[0].run("instructor grading validate-rubrics",
                                ["pa1", "--only", teams[1]])
        self.assertEqual(result.exit_code, 0)

        result = graders[0].run("instructor grading validate-rubrics",
                                ["pa1", "--grader", "grader"])
        self.assertEqual(result.exit_code, 0)

        result = graders[0].run("instructor grading validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading show-grading-status",
                                    ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading collect-rubrics",
                                    ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run(
            "instructor grading show-grading-status",
            ["--use-stored-grades", "--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading list-grades")
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading push-grading",
                                    ["--to-students", "--yes", "pa1"])
        self.assertEqual(result.exit_code, 0)

        team_git_repos[0], team_git_paths[0] = students_team[0][
            0].get_local_git_repository(teams[0])
        team_git_repos[0].remote("origin").pull("pa1-grading:pa1-grading")
        team_git_repos[0].heads["pa1-grading"].checkout()
        self.assertTrue(os.path.exists(team_git_paths[0] + "/pa1.rubric.txt"))

        team_git_repos[1], team_git_paths[1] = students_team[1][
            0].get_local_git_repository(teams[1])
        team_git_repos[1].remote("origin").pull("pa1-grading:pa1-grading")
        team_git_repos[1].heads["pa1-grading"].checkout()
        self.assertTrue(os.path.exists(team_git_paths[0] + "/pa1.rubric.txt"))
        self.assertIn("Great job!", open(team_git_paths[1] + "/bar").read())
Exemple #15
0
def student_assignment_submit(ctx, course, assignment_id, commit_sha, yes):
    assignment = get_assignment_or_exit(ctx, course, assignment_id)

    # Determine team for this assignment
    team, registration = get_team_registration_from_user(
        ctx, course, assignment)
    team_members = team.get_team_members()

    title = "SUBMISSION FOR ASSIGNMENT %s (%s)" % (assignment.assignment_id,
                                                   assignment.name)
    print(title)
    print("-" * len(title))
    print()
    if len(team_members) == 1:
        student = team_members[0].student
        individual = True
        print("This is an INDIVIDUAL submission for %s %s" %
              (student.user.first_name, student.user.last_name))
    else:
        students = [tm.student for tm in team_members]
        individual = False
        print(
            "This is a TEAM submission for team %s with the following students:"
            % team.team_id)
        for s in students:
            print(" - %s %s" % (s.user.first_name, s.user.last_name))
    print()

    conn = create_connection(course, ctx.obj['config'])

    if conn is None:
        print("Could not connect to git server.")
        ctx.exit(CHISUBMIT_FAIL)

    if commit_sha is None:
        commit = conn.get_latest_commit(course, team)

        if commit is None:
            print(
                "It seems there are no commits in your repository, so I cannot submit anything"
            )
            ctx.exit(CHISUBMIT_FAIL)

        user_specified_commit = False
    else:
        commit = conn.get_commit(course, team, commit_sha)

        if commit is None:
            print("Commit %s does not exist in repository" % commit_sha)
            ctx.exit(CHISUBMIT_FAIL)

        user_specified_commit = True

    try:
        submit_response = registration.submit(commit.sha, dry_run=True)
    except BadRequestException as bre:
        response_data = bre.json

        if "extensions_needed" in response_data and "extensions_available" in response_data:
            extensions_needed = response_data["extensions_needed"]
            extensions_available = response_data["extensions_available"]

            deadline_utc = parse(response_data["deadline"])
            submitted_at_utc = parse(response_data["submitted_at"])
            deadline_local = convert_datetime_to_local(deadline_utc)
            submitted_at_local = convert_datetime_to_local(submitted_at_utc)

            if extensions_needed > extensions_available:
                msg1 = "You do not have enough extensions to submit this assignment."
                msg2 = "You would need %i extensions to submit this assignment at this " \
                       "time, but you only have %i left" % (extensions_needed, extensions_available)

                print()
                print(msg1)
                print()
                print("     Deadline (UTC): %s" %
                      deadline_utc.isoformat(sep=" "))
                print("          Now (UTC): %s" %
                      submitted_at_utc.isoformat(sep=" "))
                print()
                print("   Deadline (Local): %s" %
                      deadline_local.isoformat(sep=" "))
                print("        Now (Local): %s" %
                      submitted_at_local.isoformat(sep=" "))
                print()
                print(msg2)
                print()
            else:
                print(
                    "ERROR: Your submission cannot be completed. The server reported the following:"
                )
                print()
                bre.print_errors()
        else:
            print(
                "ERROR: Your submission cannot be completed. The server reported the following:"
            )
            print()
            bre.print_errors()

        ctx.exit(CHISUBMIT_FAIL)

    if registration.final_submission is not None:
        prior_commit_sha = registration.final_submission.commit_sha
        prior_extensions_used = registration.final_submission.extensions_used
        prior_submitted_at_utc = registration.final_submission.submitted_at
        prior_submitted_at_local = convert_datetime_to_local(
            prior_submitted_at_utc)

        submission_commit = conn.get_commit(course, team, prior_commit_sha)

        if prior_commit_sha == commit.sha:
            print("You have already submitted assignment %s" %
                  registration.assignment.assignment_id)
            print()
            print("You submitted the following commit on %s:" %
                  prior_submitted_at_local)
            print()
            if submission_commit is None:
                print(
                    "WARNING: Previously submitted commit '%s' is not in the repository!"
                    % prior_commit_sha)
            else:
                print_commit(submission_commit)
            print()
            if user_specified_commit:
                print("You are trying to submit the same commit again (%s)" %
                      prior_commit_sha)
                print(
                    "If you want to re-submit, please specify a different commit."
                )
            else:
                print(
                    "The above commit is the latest commit in your repository."
                )
                print()
                print(
                    "If you were expecting to see a different commit, make sure you've pushed"
                )
                print("your latest code to your repository.")
            ctx.exit(CHISUBMIT_FAIL)

        print("You have already submitted assignment %s" %
              registration.assignment.assignment_id)
        print()
        print("You submitted the following commit on %s:" %
              prior_submitted_at_local)
        print()
        if submission_commit is None:
            print(
                "WARNING: Previously submitted commit '%s' is not in the repository!"
                % prior_commit_sha)
        else:
            print_commit(submission_commit)
        print()

        msg = "IF YOU CONTINUE, THE ABOVE SUBMISSION FOR %s (%s) WILL BE CANCELLED." % (
            registration.assignment.assignment_id,
            registration.assignment.name)

        print("!" * len(msg))
        print(msg)
        print("!" * len(msg))
        print()
        if not user_specified_commit:
            print(
                "If you continue, your submission will instead point to the latest commit in your repository:"
            )
        else:
            print(
                "If you continue, your submission will instead point to the following commit:"
            )
    else:
        if not user_specified_commit:
            print("The latest commit in your repository is the following:")
        else:
            print("The commit you are submitting is the following:")
    print()
    print_commit(commit)
    print()
    print("PLEASE VERIFY THIS IS THE EXACT COMMIT YOU WANT TO SUBMIT")
    print()
    if individual:
        print("You currently have %i extensions" %
              (submit_response.extensions_before))
    else:
        print("Your team currently has %i extensions" %
              (submit_response.extensions_before))
    print()
    if registration.final_submission is not None:
        print(
            "You used %i extensions in your previous submission of this assignment."
            % prior_extensions_used)
        print("and you are going to use %i additional extensions now." %
              (submit_response.extensions_needed - prior_extensions_used))
    else:
        print("You are going to use %i extensions on this submission." %
              submit_response.extensions_needed)
    print()
    print("You will have %i extensions left after this submission." %
          submit_response.extensions_after)
    print()

    if submit_response.in_grace_period:
        print(
            "NOTE: You are submitting after the deadline, but the instructor has"
        )
        print(
            "allowed some extra time after the deadline for students to submit"
        )
        print("without having to consume an extension.")
        print()

    print("Are you sure you want to continue? (y/n): ", end=' ')

    if not yes:
        yesno = input()
    else:
        yesno = 'y'
        print('y')

    if yesno in ('y', 'Y', 'yes', 'Yes', 'YES'):
        try:
            submit_response = registration.submit(commit.sha, dry_run=False)

            # TODO: Can't do this until GitLab supports updating tags
            #
            # message = "Extensions: %i\n" % extensions_requested
            # if submission_tag is None:
            #     conn.create_submission_tag(course, team, tag_name, message, commit.sha)
            # else:
            #     conn.update_submission_tag(course, team, tag_name, message, commit.sha)

            print()
            print("Your submission has been completed.")

            if submit_response.in_grace_period:
                print()
                print(
                    "Your submission was made during the deadline's grace period. This means"
                )
                print(
                    "that, although your submission was technically made *after* the"
                )
                print(
                    "deadline, we are counting it as if it had been made before the deadline."
                )
                print()
                print(
                    "In the future, you should not rely on the presence of this grace period!"
                )
                print(
                    "Your instructor may choose not to use one in future assignments, or may"
                )
                print(
                    "use a shorter grace period. Your instructor is also aware of what"
                )
                print(
                    "submissions are made during the grace period; if you repeatedly submit"
                )
                print(
                    "during the grace period, your instructor may charge you an extension"
                )
                print(
                    "or refuse to accept your assignment if you are out of extensions."
                )

            return CHISUBMIT_SUCCESS

        except BadRequestException as bre:
            print()
            print(
                "ERROR: Your submission was not completed. The server reported the following errors:"
            )
            bre.print_errors()
            ctx.exit(CHISUBMIT_FAIL)
    else:
        print("Your submission has not been completed.")
        print()
        print(
            "If you chose not to proceed because the above commit is not the one you wanted"
        )
        print(
            "to submit, make sure you've pushed your latest code to your repository before"
        )
        print("attempting to submit again.")
        print()
        print(
            "If you want to submit a different commit from your latest commit (e.g., an earlier"
        )
        print(
            "commit), you can use the --commit-sha option to specify a different commit."
        )
        ctx.exit(CHISUBMIT_FAIL)
Exemple #16
0
    def test_complete_with_gradescope(self, runner):
        course_id = u"cmsc40300"
        course_name = u"Foobarmentals of Foobar II"

        admin_id = u"admin"
        instructor_ids = [u"instructor"]
        grader_ids = [u"grader"]
        student_ids = [u"student1", u"student2", u"student3", u"student4"]

        all_users = instructor_ids + grader_ids + student_ids

        admin, instructors, graders, students = self.create_clients(
            runner,
            admin_id,
            instructor_ids,
            grader_ids,
            student_ids,
            course_id,
            gradescope_api_key="gradescope-testing",
            verbose=True)
        self.create_users(admin, all_users)

        self.create_course(admin, course_id, course_name)

        course = Course.get_by_course_id(course_id)
        self.assertIsNotNone(course)
        self.assertEqual(course.name, course_name)

        result = admin.run("admin course set-attribute %s gradescope_id 4242" %
                           (course_id))
        self.assertEqual(result.exit_code, 0)

        self.add_users_to_course(admin, course_id, instructors, graders,
                                 students)

        deadline = get_datetime_now_utc() + timedelta(hours=1)
        deadline = convert_datetime_to_local(deadline)
        deadline = deadline.replace(tzinfo=None).isoformat(sep=" ")

        result = instructors[0].run(
            "instructor assignment add",
            ["pa1", "Programming Assignment 1", deadline])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute",
                                    ["pa1", "max_students", "2"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute",
                                    ["pa1", "gradescope_id", "3737"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute",
                                    ["pa1", "expected_files", "foo,b*,qux"])
        self.assertEqual(result.exit_code, 0)

        teams = [u"student1-student2", u"student3-student4"]

        students_team = [(students[0], students[1]),
                         (students[2], students[3])]

        self.register_team(students_team[0], teams[0], "pa1", course_id)
        self.register_team(students_team[1], teams[1], "pa1", course_id)

        _, _, team_commits = self.create_team_repos(admin, course_id,
                                                    teams[0:2],
                                                    students_team[0:2])

        # Team 0 and 1 submit
        result = students_team[0][0].run("student assignment submit",
                                         ["pa1", "--yes"])
        self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

        result = students_team[1][0].run("student assignment submit",
                                         ["pa1", "--yes"])
        self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

        for team, student_team in zip(teams, students_team):
            result = student_team[0].run("student team show", [team])
            self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

        # Let the deadline "pass"
        new_now = get_datetime_now_utc() + timedelta(hours=2)
        set_testing_now(new_now)

        print()
        print("~~~ Time has moved 'forward' by two hours ~~~")
        print()

        # Instructor uploads to Gradescope. Both repos should be skipped
        # because we haven't created the the grading repos yet
        result = instructors[0].run("instructor grading gradescope-upload",
                                    ["--dry-run", "pa1"])
        self.assertEqual(result.exit_code, 0)

        # Instructor creates grading repos
        result = instructors[0].run("instructor grading create-grading-repos",
                                    ["--master", "pa1"])
        self.assertEqual(result.exit_code, 0)

        # Instructor uploads to Gradescope. Both repos should be skipped
        # because they're missing required file "qux"
        result = instructors[0].run("instructor grading gradescope-upload",
                                    ["--dry-run", "pa1"])
        self.assertEqual(result.exit_code, 0)

        # We change the list of expected files
        result = instructors[0].run("instructor assignment set-attribute",
                                    ["pa1", "expected_files", "foo,b*"])
        self.assertEqual(result.exit_code, 0)

        # Instructor uploads to Gradescope. Both repos should be uploaded
        # (but we're running in dry-run mode, so we don't actually contact
        # Gradescope)
        result = instructors[0].run("instructor grading gradescope-upload",
                                    ["--dry-run", "pa1"])
        self.assertEqual(result.exit_code, 0)
Exemple #17
0
def instructor_assignment_submit(ctx, course, team_id, assignment_id, commit_sha, extensions):
    team = get_team_or_exit(ctx, course, team_id)
    registration = get_assignment_registration_or_exit(ctx, team, assignment_id)
            
    conn = create_connection(course, ctx.obj['config'])
    
    if conn is None:
        print("Could not connect to git server.")
        ctx.exit(CHISUBMIT_FAIL)
    
    commit = conn.get_commit(course, team, commit_sha)
    
    if commit is None:
        print("Commit %s does not exist in repository" % commit_sha)
        ctx.exit(CHISUBMIT_FAIL)
    
    if registration.final_submission is not None:
        prior_commit_sha = registration.final_submission.commit_sha
        prior_submitted_at_utc = registration.final_submission.submitted_at
        prior_submitted_at_local = convert_datetime_to_local(prior_submitted_at_utc)            
        
        submission_commit = conn.get_commit(course, team, prior_commit_sha)            
        
        if prior_commit_sha == commit_sha:
            print("The team has already submitted assignment %s" % registration.assignment.assignment_id)
            print("They submitted the following commit on %s:" % prior_submitted_at_local)
            print()
            if submission_commit is None:
                print("WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha)
            else:
                print_commit(submission_commit)
            print()
            print("You are trying to submit the same commit again (%s)" % prior_commit_sha)
            print("If you want to re-submit, please specify a different commit")
            ctx.exit(CHISUBMIT_FAIL)
            
        print()
        print("WARNING: This team has already submitted assignment %s and you" % registration.assignment.assignment_id) 
        print("are about to overwrite the previous submission of the following commit:")
        print()
        if submission_commit is None:
            print("WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha)
        else:
            print_commit(submission_commit)
        print()

    if registration.final_submission is not None:
        msg = "THE ABOVE SUBMISSION FOR %s (%s) WILL BE CANCELLED." % (registration.assignment.assignment_id, registration.assignment.name)
        
        print("!"*len(msg))
        print(msg)
        print("!"*len(msg))
        print()
        print("If you continue, their submission for %s (%s)" % (registration.assignment.assignment_id, registration.assignment.name))
        print("will now point to the following commit:")                
    else:
        print("You are going to make a submission for %s (%s)." % (registration.assignment.assignment_id, registration.assignment.name))
        print("The commit you are submitting is the following:")
    print()
    print_commit(commit)
    print()
    print("PLEASE VERIFY THIS IS THE EXACT COMMIT YOU WANT TO SUBMIT")
    print()
    print("Are you sure you want to continue? (y/n): ", end=' ') 
    
    yesno = input()
    
    if yesno in ('y', 'Y', 'yes', 'Yes', 'YES'):
        try:
            submit_response = registration.submit(commit_sha, extensions_override = extensions, dry_run=False)
                        
            print("The submission has been completed.")
            return CHISUBMIT_SUCCESS

        except BadRequestException as bre:        
            print("ERROR: The submission was not completed. The server reported the following errors:")
            bre.print_errors()
            ctx.exit(CHISUBMIT_FAIL)
    def test_complete_with_extensions_per_team(self, runner):
        course_id = u"cmsc40200"
        course_name = u"Foobarmentals of Foobar"

        admin_id = u"admin"
        instructor_ids = [u"instructor"]
        grader_ids= [u"grader"]
        student_ids = [u"student1", u"student2", u"student3", u"student4"]
        
        all_users = instructor_ids + grader_ids + student_ids
        
        admin, instructors, graders, students = self.create_clients(runner, admin_id, instructor_ids, grader_ids, student_ids, course_id, verbose = True)
        self.create_users(admin, all_users)
        
        self.create_course(admin, course_id, course_name)

        result = admin.run("admin course set-attribute %s default_extensions 2" % (course_id))
        self.assertEqual(result.exit_code, 0)
        
        result = admin.run("admin course set-attribute %s extension_policy per-team" % (course_id))
        self.assertEqual(result.exit_code, 0)
        
        self.add_users_to_course(admin, course_id, instructors, graders, students)
        
        teams = ["student1-student2", "student3-student4"]        

        students_team = [students[0:2], students[2:4]]

        
        deadline = get_datetime_now_utc() - timedelta(hours=23)
        deadline = convert_datetime_to_local(deadline)
        deadline = deadline.replace(tzinfo=None).isoformat(sep=" ")        

        result = instructors[0].run("instructor assignment add", 
                                    ["pa1", "Programming Assignment 1", deadline])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute", 
                                    ["pa1", "min_students", "2"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute", 
                                    ["pa1", "max_students", "2"])
        self.assertEqual(result.exit_code, 0)


        pa1_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 
        
Total Points: 0 / 100
"""

        with open("pa1.rubric.txt", "w") as f:
            f.write(pa1_rubric)
            
        result = instructors[0].run("instructor assignment add-rubric", 
                                    ["pa1", "pa1.rubric.txt"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment show-rubric", 
                                    ["pa1"])
        self.assertEqual(result.exit_code, 0)

        deadline = get_datetime_now_utc() - timedelta(hours=49)
        deadline = deadline.isoformat(sep=" ")

        result = instructors[0].run("instructor assignment add", 
                                    ["pa2", "Programming Assignment 2", deadline])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute", 
                                    ["pa2", "min_students", "2"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor assignment set-attribute", 
                                    ["pa2", "max_students", "2"])
        self.assertEqual(result.exit_code, 0)

        pa2_rubric = """Points:
    - The PA2 Tests:
        Points Possible: 50
        Points Obtained: 

    - The PA2 Design:
        Points Possible: 50
        Points Obtained: 
        
Total Points: 0 / 100
"""

        with open("pa2.rubric.txt", "w") as f:
            f.write(pa2_rubric)
            
        result = instructors[0].run("instructor assignment add-rubric", 
                                    ["pa2", "pa2.rubric.txt"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor assignment show-rubric", 
                                    ["pa2"])
        self.assertEqual(result.exit_code, 0)

        result = admin.run("admin course show", ["--include-users", "--include-assignments", course_id])
        self.assertEqual(result.exit_code, 0)

        
        self.register_team(students_team[0], teams[0], "pa1", course_id)
        self.register_team(students_team[1], teams[1], "pa1", course_id)

        self.register_team(students_team[0], teams[0], "pa2", course_id)

        
        result = students_team[0][0].run("student team list")
        self.assertEqual(result.exit_code, 0)
        self.assertIn(teams[0], result.output)
        self.assertNotIn(teams[1], result.output)

        result = students_team[1][0].run("student team list")
        self.assertEqual(result.exit_code, 0)
        self.assertIn(teams[1], result.output)
        self.assertNotIn(teams[0], result.output)
        
        result = students_team[0][0].run("student team show", [teams[0]])
        self.assertEqual(result.exit_code, 0)

        result = students_team[0][0].run("student team show", [teams[1]])
        self.assertEqual(result.exit_code, CHISUBMIT_FAIL)

        result = students_team[1][0].run("student team show", [teams[1]])
        self.assertEqual(result.exit_code, 0)

        result = students_team[1][0].run("student team show", [teams[0]])
        self.assertEqual(result.exit_code, CHISUBMIT_FAIL)

        result = instructors[0].run("instructor team list")
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team show", [teams[0]])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team show", [teams[1]])
        self.assertEqual(result.exit_code, 0)

        team_git_paths, team_git_repos, team_commits = self.create_team_repos(admin, course_id, teams, students_team)
        
        # Submit with just the right number
        result = students_team[0][0].run("student assignment submit", 
                                         ["pa1", "--yes"])
        self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

        result = students_team[0][0].run("student team show", [teams[0]])
        self.assertEqual(result.exit_code, 0)

        # Try submitting an already-submitted assignment, with the same
        # commit as before (without --commit-sha)
        result = students_team[0][0].run("student assignment submit", 
                                         ["pa1", "--yes"])
        self.assertEqual(result.exit_code, CHISUBMIT_FAIL)

        # Try submitting an already-submitted assignment, with the same
        # commit as before (with --commit-sha)
        result = students_team[0][0].run("student assignment submit", 
                                         ["pa1", "--yes", "--commit-sha", team_commits[0][1].hexsha])
        self.assertEqual(result.exit_code, CHISUBMIT_FAIL)

        # Submit an already-submitted assignment
        result = students_team[0][0].run("student assignment submit", 
                                         ["pa1", "--yes", "--commit-sha", team_commits[0][0].hexsha])
        self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)
        
        result = students_team[0][0].run("student team show", [teams[0]])
        self.assertEqual(result.exit_code, 0)
        
        # Try submitting for a project the team is not registered for
        result = students_team[1][0].run("student assignment submit", 
                                         ["pa2", "--yes"])        
        self.assertEqual(result.exit_code, CHISUBMIT_FAIL)

        result = students_team[1][0].run("student assignment submit", 
                                         ["pa1", "--yes", "--commit-sha", team_commits[1][0].hexsha])
        self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)
        
        result = students_team[1][0].run("student assignment submit", 
                                         ["pa1", "--yes"])
        self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

        result = instructors[0].run("instructor grading list-submissions", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos", ["repos/all/"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos", ["--assignment", "pa1", "repos/pa1/"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos", ["--assignment", "pa1", "repos/ready/", "--only-ready-for-grading"])
        self.assertEqual(result.exit_code, 0)

        # Let the deadline "pass"
        new_now = get_datetime_now_utc() + timedelta(hours=2)
        set_testing_now(new_now)

        print()
        print("~~~ Time has moved 'forward' by two hours ~~~")
        print()

        result = instructors[0].run("instructor grading list-submissions", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos", ["repos/all/"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos", ["--assignment", "pa1", "repos/pa1/"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team pull-repos", ["--assignment", "pa1", "repos/ready/", "--only-ready-for-grading"])
        self.assertEqual(result.exit_code, 0)
                
        result = instructors[0].run("instructor grading create-grading-repos", ["--master", "pa1"])
        self.assertEqual(result.exit_code, 0)
                        
        result = instructors[0].run("instructor grading assign-graders", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading list-grader-assignments", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = graders[0].run("grader pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
                
        team1_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (course_id, "pa1", teams[0])
        team2_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (course_id, "pa1", teams[1])
            
        team_git_repos[0], team_git_paths[0] = graders[0].get_local_git_repository(team1_grading_repo_path)
        team_git_repos[1], team_git_paths[1] = graders[0].get_local_git_repository(team2_grading_repo_path)

        
        team1_rubric_path = "%s/pa1.rubric.txt" % team_git_paths[0] 
        team2_rubric_path = "%s/pa1.rubric.txt" % team_git_paths[1] 

        team1_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 45

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 30
        
Penalties:
    Used O(n^156) algorithm: -10
    Submitted code in a Word document: -30

Bonuses:
    Worked alone: 15

Total Points: 50 / 100

Comments: >
    None"""

        with open(team1_rubric_path, "w") as f:
            f.write(team1_rubric)

        result = graders[0].run("grader validate-rubrics", ["pa1", "--only", teams[0]])
        self.assertEqual(result.exit_code, 0)
    
        team_git_repos[0].index.add(["pa1.rubric.txt"])
        team_git_repos[0].index.commit("Finished grading")
        
        
        
        with open("%s/bar" % team_git_paths[1], "a") as f:
            f.write("Great job!\n") 
            
        team2_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 50

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 45

Total Points: 95 / 100

Comments: >
    Great job!"""
                
        with open(team2_rubric_path, "w") as f:
            f.write(team2_rubric)

        result = graders[0].run("grader validate-rubrics", ["pa1", "--only", teams[1]])
        self.assertEqual(result.exit_code, 0)

        team_git_repos[1].index.add(["pa1.rubric.txt"])
        team_git_repos[1].index.add(["bar"])
        team_git_repos[1].index.commit("Finished grading")

        result = graders[0].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = graders[0].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = graders[0].run("instructor grading validate-rubrics", ["pa1", "--only", teams[0]])
        self.assertEqual(result.exit_code, 0)

        result = graders[0].run("instructor grading validate-rubrics", ["pa1", "--only", teams[1]])
        self.assertEqual(result.exit_code, 0)

        result = graders[0].run("instructor grading validate-rubrics", ["pa1", "--grader", "grader"])
        self.assertEqual(result.exit_code, 0)

        result = graders[0].run("instructor grading validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading show-grading-status", ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading collect-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading show-grading-status", ["--use-stored-grades", "--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
                
        result = instructors[0].run("instructor grading list-grades")
        self.assertEqual(result.exit_code, 0)
                
        result = instructors[0].run("instructor grading push-grading", ["--to-students", "--yes", "pa1"])
        self.assertEqual(result.exit_code, 0)
        
    
        team_git_repos[0], team_git_paths[0] = students_team[0][0].get_local_git_repository(teams[0])
        team_git_repos[0].remote("origin").pull("pa1-grading:pa1-grading")
        team_git_repos[0].heads["pa1-grading"].checkout()        
        self.assertTrue(os.path.exists(team_git_paths[0] + "/pa1.rubric.txt"))

        team_git_repos[1], team_git_paths[1] = students_team[1][0].get_local_git_repository(teams[1])
        team_git_repos[1].remote("origin").pull("pa1-grading:pa1-grading")
        team_git_repos[1].heads["pa1-grading"].checkout()        
        self.assertTrue(os.path.exists(team_git_paths[0] + "/pa1.rubric.txt"))
        self.assertIn("Great job!", open(team_git_paths[1]+"/bar").read())
    
    
        
    def test_complete_with_multiple_instructors_multiple_graders(self, runner):
        course_id = u"cmsc40200"
        course_name = u"Foobarmentals of Foobar"

        admin_id = u"admin"
        instructor_ids = [u"instructor1", u"instructor2"]
        grader_ids= [u"grader1", u"grader2"]
        student_ids = [u"student1", u"student2", u"student3", u"student4"]
                
        all_users = instructor_ids + grader_ids + student_ids
        
        admin, instructors, graders, students = self.create_clients(runner, admin_id, instructor_ids, grader_ids, student_ids, course_id, verbose = True)
        self.create_users(admin, all_users)
        
        self.create_course(admin, course_id, course_name)
        
        self.add_users_to_course(admin, course_id, instructors, graders, students)
        
        students_team = [[s] for s in students]
                
        deadline = get_datetime_now_utc() + timedelta(hours=1)
        deadline = convert_datetime_to_local(deadline)
        deadline = deadline.replace(tzinfo=None).isoformat(sep=" ")        

        result = instructors[0].run("instructor assignment add", 
                                    ["pa1", "Programming Assignment 1", deadline])
        self.assertEqual(result.exit_code, 0)

        pa1_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 
        
Total Points: 0 / 100
"""

        with open("pa1.rubric.txt", "w") as f:
            f.write(pa1_rubric)
            
        result = instructors[0].run("instructor assignment add-rubric", 
                                    ["pa1", "pa1.rubric.txt"])
        self.assertEqual(result.exit_code, 0)

        
        result = admin.run("admin course show", ["--include-users", "--include-assignments", course_id])
        self.assertEqual(result.exit_code, 0)

        for student_id, student in zip(student_ids, students):
            self.register_team([student], student_id, "pa1", course_id)

        for student_id, student in zip(student_ids, students):                    
            result = student.run("student team list")
            self.assertEqual(result.exit_code, 0)
            self.assertIn(student_id, result.output)
        
            result = student.run("student team show", [student_id])
            self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor team list")
        self.assertEqual(result.exit_code, 0)
        
        for student_id in student_ids:
            result = instructors[0].run("instructor team show", [student_id])
            self.assertEqual(result.exit_code, 0)

        student_git_paths, student_git_repos, team_commits = self.create_team_repos(admin, course_id, student_ids, students_team)
        
        
        for student_id, student in zip(student_ids, students):   
            result = student.run("student assignment submit", 
                                             ["pa1", "--yes"])
            self.assertEqual(result.exit_code, CHISUBMIT_SUCCESS)

            result = student.run("student team show", [student_id])
            self.assertEqual(result.exit_code, 0)

        # Let the deadline "pass"
        new_now = get_datetime_now_utc() + timedelta(hours=2)
        set_testing_now(new_now)

        print()
        print("~~~ Time has moved 'forward' by two hours ~~~")
        print()

        # The "master instructor" creates the grading repos

        result = instructors[0].run("instructor grading list-submissions", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        result = instructors[0].run("instructor grading create-grading-repos", ["--master", "pa1"])
        self.assertEqual(result.exit_code, 0)
                        
        result = instructors[0].run("instructor grading assign-grader", ["pa1", "student1", "grader1"])
        self.assertEqual(result.exit_code, 0)
        result = instructors[0].run("instructor grading assign-grader", ["pa1", "student2", "grader1"])
        self.assertEqual(result.exit_code, 0)
        result = instructors[0].run("instructor grading assign-grader", ["pa1", "student3", "grader2"])
        self.assertEqual(result.exit_code, 0)
        result = instructors[0].run("instructor grading assign-grader", ["pa1", "student4", "grader2"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading list-grader-assignments", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading show-grading-status", ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
               
        result = instructors[0].run("instructor grading push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        

        # The non-master instructor downloads the grading repos
        
        result = instructors[1].run("instructor grading create-grading-repos", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[1].run("instructor grading show-grading-status", ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
        
        # Grader 1 pulls their grading
        result = graders[0].run("grader pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
                    
        student1_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (course_id, "pa1", "student1")
        student2_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (course_id, "pa1", "student2")
            
        student_git_repos[0], student_git_paths[0] = graders[0].get_local_git_repository(student1_grading_repo_path)
        student_git_repos[1], student_git_paths[1] = graders[0].get_local_git_repository(student2_grading_repo_path)
  
        student1_rubric_path = "%s/pa1.rubric.txt" % student_git_paths[0] 
        student2_rubric_path = "%s/pa1.rubric.txt" % student_git_paths[1] 


        # Grader 1 grades student1 and pushes the grading.
        
        student1_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 45

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 30
        
Penalties:
    Used O(n^156) algorithm: -10
    Submitted code in a Word document: -30

Bonuses:
    Worked alone: 15

Total Points: 50 / 100

Comments: >
    None"""

        with open(student1_rubric_path, "w") as f:
            f.write(student1_rubric)

        result = graders[0].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)
    
        student_git_repos[0].index.add(["pa1.rubric.txt"])
        student_git_repos[0].index.commit("Finished grading")
        
        result = graders[0].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        # The non-master instructor pulls the repos and generates a report

        result = instructors[1].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[1].run("instructor grading show-grading-status", ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
        
        # Grader 1 grades student2 and pushes the grading.
        
        student2_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 50

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 45

Total Points: 95 / 100

Comments: >
    Great job!"""

        with open(student2_rubric_path, "w") as f:
            f.write(student2_rubric)

        result = graders[0].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)
    
        student_git_repos[1].index.add(["pa1.rubric.txt"])
        student_git_repos[1].index.commit("Finished grading")
        
        result = graders[0].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        # The non-master instructor pulls the repos and generates a report

        result = instructors[1].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[1].run("instructor grading show-grading-status", ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
        
        # Grader 2 pulls their grading
                
        result = graders[1].run("grader pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
                    
        student3_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (course_id, "pa1", "student3")
        student4_grading_repo_path = "chisubmit-test/repositories/%s/%s/%s" % (course_id, "pa1", "student4")
            
        student_git_repos[2], student_git_paths[2] = graders[1].get_local_git_repository(student3_grading_repo_path)
        student_git_repos[3], student_git_paths[3] = graders[1].get_local_git_repository(student4_grading_repo_path)
  
        student3_rubric_path = "%s/pa1.rubric.txt" % student_git_paths[2] 
        student4_rubric_path = "%s/pa1.rubric.txt" % student_git_paths[3] 
                

        # Grader 2 adds the empty rubrics (which should be generated by pull-grading)
        
        result = graders[1].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)
            
        student_git_repos[2].index.add(["pa1.rubric.txt"])
        student_git_repos[2].index.commit("Added rubric")        
        student_git_repos[3].index.add(["pa1.rubric.txt"])
        student_git_repos[3].index.commit("Added rubric")        

        result = graders[1].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        # The non-master instructor pulls the repos and generates a report

        result = instructors[1].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[1].run("instructor grading show-grading-status", ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
        
        
        # Grader 2 grades student3 but does only a partial grading of student4
            
        student3_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 20

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 15

Total Points: 35 / 100

Comments: >
    Needs improvement!"""
                
        with open(student3_rubric_path, "w") as f:
            f.write(student3_rubric)

        student4_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 35

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 

Total Points: 35 / 100

Comments: >
"""
                
        with open(student4_rubric_path, "w") as f:
            f.write(student4_rubric)

        result = graders[1].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        student_git_repos[2].index.add(["pa1.rubric.txt"])
        student_git_repos[2].index.commit("Finished grading")

        student_git_repos[3].index.add(["pa1.rubric.txt"])
        student_git_repos[3].index.commit("Grading in progress")

        result = graders[1].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        # The non-master instructor pulls the repos and generates a report

        result = instructors[1].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[1].run("instructor grading show-grading-status", ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
        
        # Grader 2 finishes grading
        
        student4_rubric = """Points:
    - The PA1 Tests:
        Points Possible: 50
        Points Obtained: 35

    - The PA1 Design:
        Points Possible: 50
        Points Obtained: 25

Total Points: 60 / 100

Comments: >
"""
                
        with open(student4_rubric_path, "w") as f:
            f.write(student4_rubric)

        result = graders[1].run("grader validate-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)

        student_git_repos[3].index.add(["pa1.rubric.txt"])
        student_git_repos[3].index.commit("Finished grading")        
        
        result = graders[1].run("grader push-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        # The non-master instructor pulls the repos and generates a report

        result = instructors[1].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[1].run("instructor grading show-grading-status", ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
        
                    
        # The master instructor pulls the repos and pushes them to the students

        result = instructors[0].run("instructor grading pull-grading", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading show-grading-status", ["--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading collect-rubrics", ["pa1"])
        self.assertEqual(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading show-grading-status", ["--use-stored-grades", "--by-grader", "pa1"])
        self.assertEqual(result.exit_code, 0)
                
        result = instructors[0].run("instructor grading list-grades")
        self.assertEqual(result.exit_code, 0)
                
        result = instructors[0].run("instructor grading push-grading", ["--to-students", "--yes", "pa1"])
        self.assertEqual(result.exit_code, 0)
    
        for student_id, student in zip(student_ids, students):
            repo, path = student.get_local_git_repository(student_id)
            repo.remote("origin").pull("pa1-grading:pa1-grading")
            repo.heads["pa1-grading"].checkout()        
            self.assertTrue(os.path.exists(path + "/pa1.rubric.txt"))
Exemple #20
0
def student_assignment_submit(ctx, course, assignment_id, commit_sha, yes):
    assignment = get_assignment_or_exit(ctx, course, assignment_id)

    # Determine team for this assignment
    team, registration = get_team_registration_from_user(ctx, course, assignment)
    team_members = team.get_team_members()    
                
    title = "SUBMISSION FOR ASSIGNMENT %s (%s)" % (assignment.assignment_id, assignment.name)
    print(title)
    print("-" * len(title))
    print()
    if len(team_members) == 1:
        student = team_members[0].student
        individual = True
        print("This is an INDIVIDUAL submission for %s %s" % (student.user.first_name, student.user.last_name))
    else:
        students = [tm.student for tm in team_members]
        individual = False
        print("This is a TEAM submission for team %s with the following students:" % team.team_id)
        for s in students:
            print(" - %s %s" % (s.user.first_name, s.user.last_name))
    print()
                
    conn = create_connection(course, ctx.obj['config'])
    
    if conn is None:
        print("Could not connect to git server.")
        ctx.exit(CHISUBMIT_FAIL)
    
    if commit_sha is None:
        commit = conn.get_latest_commit(course, team)

        if commit is None:
            print("It seems there are no commits in your repository, so I cannot submit anything")
            ctx.exit(CHISUBMIT_FAIL)
                
        user_specified_commit = False
    else:    
        commit = conn.get_commit(course, team, commit_sha)
        
        if commit is None:
            print("Commit %s does not exist in repository" % commit_sha)
            ctx.exit(CHISUBMIT_FAIL)

        user_specified_commit = True
        
    try:
        submit_response = registration.submit(commit.sha, dry_run = True)
    except BadRequestException as bre:
        response_data = bre.json
        
        if "extensions_needed" in response_data and "extensions_available" in response_data:
            extensions_needed = response_data["extensions_needed"]
            extensions_available = response_data["extensions_available"]
            
            deadline_utc = parse(response_data["deadline"])
            submitted_at_utc = parse(response_data["submitted_at"])
            deadline_local = convert_datetime_to_local(deadline_utc)
            submitted_at_local = convert_datetime_to_local(submitted_at_utc)        
            
            if extensions_needed > extensions_available:
                msg1 = "You do not have enough extensions to submit this assignment."
                msg2 = "You would need %i extensions to submit this assignment at this " \
                       "time, but you only have %i left" % (extensions_needed, extensions_available)
    
                print()
                print(msg1)
                print()            
                print("     Deadline (UTC): %s" % deadline_utc.isoformat(sep=" "))
                print("          Now (UTC): %s" % submitted_at_utc.isoformat(sep=" "))
                print() 
                print("   Deadline (Local): %s" % deadline_local.isoformat(sep=" "))
                print("        Now (Local): %s" % submitted_at_local.isoformat(sep=" "))
                print() 
                print(msg2) 
                print()
            else:
                print("ERROR: Your submission cannot be completed. The server reported the following:")
                print()
                bre.print_errors()
        else:
            print("ERROR: Your submission cannot be completed. The server reported the following:")
            print()
            bre.print_errors()

        ctx.exit(CHISUBMIT_FAIL)
    
    if registration.final_submission is not None:
        prior_commit_sha = registration.final_submission.commit_sha
        prior_extensions_used = registration.final_submission.extensions_used             
        prior_submitted_at_utc = registration.final_submission.submitted_at
        prior_submitted_at_local = convert_datetime_to_local(prior_submitted_at_utc)            
        
        submission_commit = conn.get_commit(course, team, prior_commit_sha)            
        
        if prior_commit_sha == commit.sha:
            print("You have already submitted assignment %s" % registration.assignment.assignment_id)
            print()
            print("You submitted the following commit on %s:" % prior_submitted_at_local)
            print()
            if submission_commit is None:
                print("WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha)
            else:
                print_commit(submission_commit)
            print()
            if user_specified_commit:
                print("You are trying to submit the same commit again (%s)" % prior_commit_sha)
                print("If you want to re-submit, please specify a different commit.")
            else:
                print("The above commit is the latest commit in your repository.")
                print()
                print("If you were expecting to see a different commit, make sure you've pushed")
                print("your latest code to your repository.")
            ctx.exit(CHISUBMIT_FAIL)
            
        print("You have already submitted assignment %s" % registration.assignment.assignment_id)
        print()
        print("You submitted the following commit on %s:" % prior_submitted_at_local)
        print()
        if submission_commit is None:
            print("WARNING: Previously submitted commit '%s' is not in the repository!" % prior_commit_sha)
        else:
            print_commit(submission_commit)
        print()

        msg = "IF YOU CONTINUE, THE ABOVE SUBMISSION FOR %s (%s) WILL BE CANCELLED." % (registration.assignment.assignment_id, registration.assignment.name)
        
        print("!"*len(msg))
        print(msg)
        print("!"*len(msg))
        print()        
        if not user_specified_commit:
            print("If you continue, your submission will instead point to the latest commit in your repository:")
        else:
            print("If you continue, your submission will instead point to the following commit:")                
    else:
        if not user_specified_commit:
            print("The latest commit in your repository is the following:")
        else:
            print("The commit you are submitting is the following:")
    print()
    print_commit(commit)
    print()
    print("PLEASE VERIFY THIS IS THE EXACT COMMIT YOU WANT TO SUBMIT")
    print()
    if individual:
        print("You currently have %i extensions" % (submit_response.extensions_before))
    else:
        print("Your team currently has %i extensions" % (submit_response.extensions_before))
    print()
    if registration.final_submission is not None:
        print("You used %i extensions in your previous submission of this assignment." % prior_extensions_used)
        print("and you are going to use %i additional extensions now." % (submit_response.extensions_needed - prior_extensions_used))
    else:
        print("You are going to use %i extensions on this submission." % submit_response.extensions_needed)
    print()
    print("You will have %i extensions left after this submission." % submit_response.extensions_after)
    print()
    
    if submit_response.in_grace_period:
        print("NOTE: You are submitting after the deadline, but the instructor has")
        print("allowed some extra time after the deadline for students to submit")
        print("without having to consume an extension.")
        print()
     
    print("Are you sure you want to continue? (y/n): ", end=' ') 
    
    if not yes:
        yesno = input()
    else:
        yesno = 'y'
        print('y')
    
    if yesno in ('y', 'Y', 'yes', 'Yes', 'YES'):
        try:
            submit_response = registration.submit(commit.sha, dry_run=False)
            
            # TODO: Can't do this until GitLab supports updating tags
            #    
            # message = "Extensions: %i\n" % extensions_requested
            # if submission_tag is None:
            #     conn.create_submission_tag(course, team, tag_name, message, commit.sha)
            # else:
            #     conn.update_submission_tag(course, team, tag_name, message, commit.sha)
            
            print()
            print("Your submission has been completed.")

            if submit_response.in_grace_period:
                print()
                print("Your submission was made during the deadline's grace period. This means")
                print("that, although your submission was technically made *after* the")
                print("deadline, we are counting it as if it had been made before the deadline.")
                print()
                print("In the future, you should not rely on the presence of this grace period!")
                print("Your instructor may choose not to use one in future assignments, or may")
                print("use a shorter grace period. Your instructor is also aware of what")
                print("submissions are made during the grace period; if you repeatedly submit")
                print("during the grace period, your instructor may charge you an extension")
                print("or refuse to accept your assignment if you are out of extensions.")
            
            return CHISUBMIT_SUCCESS

        except BadRequestException as bre:
            print()        
            print("ERROR: Your submission was not completed. The server reported the following errors:")
            bre.print_errors()
            ctx.exit(CHISUBMIT_FAIL)
    else:
        print("Your submission has not been completed.")
        print()
        print("If you chose not to proceed because the above commit is not the one you wanted")
        print("to submit, make sure you've pushed your latest code to your repository before")
        print("attempting to submit again.")
        print()
        print("If you want to submit a different commit from your latest commit (e.g., an earlier")
        print("commit), you can use the --commit-sha option to specify a different commit.")
        ctx.exit(CHISUBMIT_FAIL)
Exemple #21
0
    def test_complete_with_extensions_per_team(self, runner):
        course_id = u"cmsc40200"
        course_name = u"Foobarmentals of Foobar"

        admin_id = u"admin"
        instructor_ids = [u"instructor"]
        grader_ids= [u"grader"]
        student_ids = [u"student1", u"student2", u"student3", u"student4"]
        
        all_users = instructor_ids + grader_ids + student_ids
        
        admin, instructors, graders, students = self.create_clients(runner, course_id, admin_id, instructor_ids, grader_ids, student_ids)
        self.create_users(admin, all_users)
        
        self.create_course(admin, course_id, course_name)

        result = admin.run("admin course set-option %s default-extensions 2" % (course_id))
        self.assertEquals(result.exit_code, 0)
        
        result = admin.run("admin course set-option %s extension-policy per_team" % (course_id))
        self.assertEquals(result.exit_code, 0)
        
        self.add_users_to_course(admin, course_id, instructors, graders, students)
        
        teams = ["the-flaming-foobars", "the-magnificent-mallocs"]        

        students_team = [students[0:2], students[2:4]]

        
        deadline = get_datetime_now_utc() - timedelta(hours=23)
        deadline = convert_datetime_to_local(deadline)
        deadline = deadline.replace(tzinfo=None).isoformat(sep=" ")        

        result = instructors[0].run("instructor assignment add", 
                                    ["pa1", "Programming Assignment 1", deadline])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor assignment add-grade-component", 
                                    ["pa1", "tests", "The PA1 Tests", "50"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor assignment add-grade-component", 
                                    ["pa1", "design", "The PA1 Design", "50"])
        self.assertEquals(result.exit_code, 0)

        deadline = get_datetime_now_utc() - timedelta(hours=49)
        deadline = deadline.isoformat(sep=" ")

        result = instructors[0].run("instructor assignment add", 
                                    ["pa2", "Programming Assignment 2", deadline])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor assignment add-grade-component", 
                                    ["pa2", "tests", "The PA2 Tests", "50"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor assignment add-grade-component", 
                                    ["pa2", "design", "The PA2 Design", "50"])
        self.assertEquals(result.exit_code, 0)
        
        
        result = admin.run("admin course show", ["--include-users", "--include-assignments", course_id])
        self.assertEquals(result.exit_code, 0)

        
        self.register_team(students_team[0], teams[0], "pa1", course_id)
        self.register_team(students_team[1], teams[1], "pa1", course_id)

        self.register_team(students_team[0], teams[0], "pa2", course_id)

        
        result = students_team[0][0].run("student team list")
        self.assertEquals(result.exit_code, 0)
        self.assertIn(teams[0], result.output)
        self.assertNotIn(teams[1], result.output)

        result = students_team[1][0].run("student team list")
        self.assertEquals(result.exit_code, 0)
        self.assertIn(teams[1], result.output)
        self.assertNotIn(teams[0], result.output)
        
        result = students_team[0][0].run("student team show", [teams[1]])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)        

        result = students_team[1][0].run("student team show", [teams[0]])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)        


        result = instructors[0].run("instructor team set-attribute", [teams[0], "alias", "foobar "+teams[0]])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor team set-attribute", [teams[1], "alias", "foobar "+teams[1]])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor team list")
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor team show", [teams[0]])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor team show", [teams[1]])
        self.assertEquals(result.exit_code, 0)

        
        team_git_paths, team_git_repos, team_commits = self.create_team_repos(admin, course_id, teams, students_team)
        
        # Try to submit without enough extensions
        result = students_team[0][0].run("student assignment submit", 
                                         [teams[0], "pa1", team_commits[0][0].hexsha, 
                                          "--extensions", "0",
                                          "--yes"])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)
        
        # Try to submit with too many extensions
        result = students_team[0][0].run("student assignment submit", 
                                         [teams[0], "pa1", team_commits[0][0].hexsha, 
                                          "--extensions", "2",
                                          "--yes"])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        # Submit with just the right number
        result = students_team[0][0].run("student assignment submit", 
                                         [teams[0], "pa1", team_commits[0][0].hexsha, 
                                          "--extensions", "1",
                                          "--yes"])
        self.assertEquals(result.exit_code, CHISUBMIT_SUCCESS)

        result = students_team[0][0].run("student team show", [teams[0]])
        self.assertEquals(result.exit_code, 0)

        # Try submitting an already-submitted assignment
        result = students_team[0][0].run("student assignment submit", 
                                         [teams[0], "pa1", team_commits[0][1].hexsha, 
                                          "--extensions", "1",
                                          "--yes"])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        # Try submitting an already-submitted assignment, with the same
        # commit as before 
        result = students_team[0][0].run("student assignment submit", 
                                         [teams[0], "pa1", team_commits[0][0].hexsha, 
                                          "--extensions", "1",
                                          "--yes", "--force"])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)


        # Submit an already-submitted assignment
        result = students_team[0][0].run("student assignment submit", 
                                         [teams[0], "pa1", team_commits[0][1].hexsha, 
                                          "--extensions", "1",
                                          "--yes", "--force"])
        self.assertEquals(result.exit_code, CHISUBMIT_SUCCESS)        
        
        result = students_team[0][0].run("student team show", [teams[0]])
        self.assertEquals(result.exit_code, 0)
        
        # Try requesting more extensions than the team has
        result = students_team[0][0].run("student assignment submit", 
                                         [teams[0], "pa2", team_commits[0][1].hexsha, 
                                          "--extensions", "3",
                                          "--yes"])
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)
        
        # Try submitting for a project the team is not registered for
        result = students_team[1][0].run("student assignment submit", 
                                         [teams[1], "pa2", team_commits[1][1].hexsha, 
                                          "--extensions", "0",
                                          "--yes"])        
        self.assertEquals(result.exit_code, CHISUBMIT_FAIL)

        result = students_team[1][0].run("student assignment submit", 
                                         [teams[1], "pa1", team_commits[1][1].hexsha, 
                                         "--extensions", "1",
                                         "--yes"])
        self.assertEquals(result.exit_code, CHISUBMIT_SUCCESS)

        result = instructors[0].run("instructor grading list-submissions", ["pa1"])
        self.assertEquals(result.exit_code, 0)

        # Let the deadline "pass"
        new_now = get_datetime_now_utc() + timedelta(hours=2)
        set_testing_now(new_now)

        print
        print "~~~ Time has moved 'forward' by two hours ~~~"
        print

        result = instructors[0].run("instructor grading list-submissions", ["pa1"])
        self.assertEquals(result.exit_code, 0)
                
        result = instructors[0].run("instructor grading create-grading-repos", ["pa1"])
        self.assertEquals(result.exit_code, 0)        
        
        result = instructors[0].run("instructor grading create-grading-branches", ["pa1"])
        self.assertEquals(result.exit_code, 0)        
                
        result = instructors[0].run("instructor grading set-grade", 
                                [teams[0], "pa1", "tests", "100"])
        self.assertEquals(result.exit_code, 1)

        result = instructors[0].run("instructor grading set-grade", 
                                [teams[0], "pa1", "tests", "40"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading set-grade", 
                                [teams[1], "pa1",  "tests", "45"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading list-grades")
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading set-grade", 
                                [teams[0], "pa1", "tests", "50"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading list-grades")
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading add-rubrics", ["pa1", "--commit"])
        self.assertEquals(result.exit_code, 0)

        result = instructors[0].run("instructor grading assign-graders", ["pa1"])
        self.assertEquals(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading list-grader-assignments", ["pa1"])
        self.assertEquals(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading push-grading-branches", ["--to-staging", "pa1"])
        self.assertEquals(result.exit_code, 0)
        
        result = graders[0].run("grader create-local-grading-repos", [graders[0].user_id, "pa1"])
        self.assertEquals(result.exit_code, 0)        
                
        team1_grading_repo_path = ".chisubmit/repositories/%s/%s/%s" % (course_id, "pa1", teams[0])
        team2_grading_repo_path = ".chisubmit/repositories/%s/%s/%s" % (course_id, "pa1", teams[1])
            
        team_git_repos[0], team_git_paths[0] = graders[0].get_local_git_repository(team1_grading_repo_path)
        team_git_repos[1], team_git_paths[1] = graders[0].get_local_git_repository(team2_grading_repo_path)

        
        team1_rubric_path = "%s/pa1.rubric.txt" % team_git_paths[0] 
        team2_rubric_path = "%s/pa1.rubric.txt" % team_git_paths[1] 

        team1_rubric = """Points:
    The PA1 Tests:
        Points Possible: 50
        Points Obtained: 45

    The PA1 Design:
        Points Possible: 50
        Points Obtained: 30
        
Penalties:
    Used O(n^156) algorithm: -10
    Submitted code in a Word document: -30

Total Points: 35 / 100

Comments: >
    None"""

        with open(team1_rubric_path, "w") as f:
            f.write(team1_rubric)

        result = graders[0].run("grader validate-rubrics", [graders[0].user_id, "pa1", "--only", teams[0]])
        self.assertEquals(result.exit_code, 0)        
    
        team_git_repos[0].index.add(["pa1.rubric.txt"])
        team_git_repos[0].index.commit("Finished grading")
        
        
        
        with open("%s/bar" % team_git_paths[1], "a") as f:
            f.write("Great job!\n") 
            
        team2_rubric = """Points:
    The PA1 Tests:
        Points Possible: 50
        Points Obtained: 50

    The PA1 Design:
        Points Possible: 50
        Points Obtained: 45

Total Points: 95 / 100

Comments: >
    Great job!"""
                
        with open(team2_rubric_path, "w") as f:
            f.write(team2_rubric)

        result = graders[0].run("grader validate-rubrics", [graders[0].user_id, "pa1", "--only", teams[1]])
        self.assertEquals(result.exit_code, 0)        

        team_git_repos[1].index.add(["pa1.rubric.txt"])
        team_git_repos[1].index.add(["bar"])
        team_git_repos[1].index.commit("Finished grading")

        result = graders[0].run("grader validate-rubrics", [graders[0].user_id, "pa1"])
        self.assertEquals(result.exit_code, 0)                

        result = graders[0].run("grader push-grading-branches", [graders[0].user_id, "pa1"])
        self.assertEquals(result.exit_code, 0)                

        result = instructors[0].run("instructor grading pull-grading-branches", ["--from-staging", "pa1"])
        self.assertEquals(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading collect-rubrics", ["pa1"])
        self.assertEquals(result.exit_code, 0)
        
        result = instructors[0].run("instructor grading list-grades")
        self.assertEquals(result.exit_code, 0)
                
        result = instructors[0].run("instructor grading push-grading-branches", ["--to-students", "pa1"])
        self.assertEquals(result.exit_code, 0)
        
    
        team_git_repos[0], team_git_paths[0] = students_team[0][0].get_local_git_repository(teams[0])
        team_git_repos[0].remote("origin").pull("pa1-grading:pa1-grading")
        team_git_repos[0].heads["pa1-grading"].checkout()        
        self.assertTrue(os.path.exists(team_git_paths[0] + "/pa1.rubric.txt"))

        team_git_repos[1], team_git_paths[1] = students_team[1][0].get_local_git_repository(teams[1])
        team_git_repos[1].remote("origin").pull("pa1-grading:pa1-grading")
        team_git_repos[1].heads["pa1-grading"].checkout()        
        self.assertTrue(os.path.exists(team_git_paths[0] + "/pa1.rubric.txt"))
        self.assertIn("Great job!", open(team_git_paths[1]+"/bar").read())