def test_remove_degenerate(self): Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) group.remove(self.user1, self.user1) assert Group.lookup(self.user1, self.assignment) is None assert Group.lookup(self.user2, self.assignment) is None
def test_decline_degenerate(self): Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) group.decline(self.user2) assert Group.lookup(self.user1, self.assignment) is None assert Group.lookup(self.user2, self.assignment) is None
def test_decline(self): Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) group.decline(self.user3) assert group.has_status(self.user1, 'active') assert group.has_status(self.user2, 'active') assert Group.lookup(self.user3, self.assignment) is None assert group.size() == 2
def test_remove_self(self): Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) group.accept(self.user3) group.remove(self.user1, self.user1) assert Group.lookup(self.user1, self.assignment) is None assert group.has_status(self.user2, 'active') assert group.has_status(self.user3, 'active')
def test_accept_not_pending(self): Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) self.assertRaises(BadRequest, group.accept, self.user2) self.assertRaises(BadRequest, group.accept, self.user3)
def assignment(name): assign = get_assignment(name) user_ids = assign.active_user_ids(current_user.id) fs = assign.final_submission(user_ids) revision = assign.revision(user_ids) scores = assign.scores(user_ids) group = Group.lookup(current_user, assign) can_invite = assign.max_group_size > 1 and assign.active can_remove = group and group.has_status(current_user, 'active') if group: can_invite = len(group.members) < assign.max_group_size data = { 'course': assign.course, 'assignment': assign, 'backups': assign.backups(user_ids).limit(5).all(), 'subms': assign.submissions(user_ids).limit(5).all(), 'final_submission': fs, 'flagged': fs and fs.flagged, 'group': group, 'revision': revision, 'scores': scores, 'can_invite': can_invite, 'can_remove': can_remove, 'csrf_form': CSRFForm() } return render_template('student/assignment/index.html', **data)
def setUp(self): super(TestGrading, self).setUp() self.setup_course() message_dict = {'file_contents': {'backup.py': '1'}, 'analytics': {}} self.active_user_ids = [self.user1.id, self.user2.id, self.user3.id] self.active_staff = [self.staff1, self.staff2] self.active_assignments = [self.assignment, self.assignment2] Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) # Creates 5 submissions for each assignment per user, each spaced two minutes apart for assign in self.active_assignments: time = assign.due_date - datetime.timedelta(minutes=30) for num in range(5): for user_id in self.active_user_ids: num += 1 time += datetime.timedelta(minutes=2) backup = Backup(submitter_id=user_id, assignment=assign, submit=True) messages = [Message(kind=k, backup=backup, contents=m) for k, m in message_dict.items()] backup.created = time db.session.add_all(messages) db.session.add(backup) # Debugging print if tests fails # print("User {} | Assignment {} | Submission {} | Time {}".format( # user_id, assign.id, num, time)) db.session.commit()
def setUp(self): """ Add submissions for 3 users. """ super(TestRevision, self).setUp() self.setup_course() message_dict = {'file_contents': {'backup.py': '1'}, 'analytics': {}} self.active_user_ids = [self.user1.id, self.user2.id, self.user3.id] self.assignment.revisions_allowed = True time = self.assignment.due_date # Set to dt.now(), so future subms are late for user_id in self.active_user_ids: time -= datetime.timedelta(minutes=15) backup = Backup(submitter_id=user_id, assignment=self.assignment, submit=True) # Revisions are submitted on time. backup.created = time messages = [Message(kind=k, backup=backup, contents=m) for k, m in message_dict.items()] db.session.add_all(messages) db.session.add(backup) # Put user 3 in a group with user 4 Group.invite(self.user3, self.user4, self.assignment) group = Group.lookup(self.user3, self.assignment) group.accept(self.user4) okversion = Version(name="ok-client", current_version="v1.5.0", download_link="http://localhost/ok") db.session.add(okversion) db.session.commit()
def test_remove_not_in_group(self): Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) self.assertRaises(BadRequest, group.remove, self.user2, self.user3) self.assertRaises(BadRequest, group.remove, self.user3, self.user2)
def test_remove(self): Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) Group.invite(self.user1, self.user4, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) group.remove(self.user1, self.user2) assert group.has_status(self.user1, 'active') assert Group.lookup(self.user2, self.assignment) is None assert group.has_status(self.user3, 'pending') assert group.size() == 3 group.remove(self.user1, self.user3) assert group.has_status(self.user1, 'active') assert Group.lookup(self.user3, self.assignment) is None assert group.size() == 2
def test_accept(self): Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) assert group.has_status(self.user1, 'active') assert group.has_status(self.user2, 'active') assert group.size() == 2
def test_invite_full(self): Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) Group.invite(self.user1, self.user4, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) assert group.size() == 4 self.assertRaises(BadRequest, Group.invite, self.user1, self.user5, self.assignment)
def test_accept_unflag(self): # when a user accepts an invitation, unflag their submissions. submission = self.assignment.submissions([self.user1.id]).all()[3] self.assignment.flag(submission.id, [self.user1.id]) Group.invite(self.user2, self.user1, self.assignment) assert submission.flagged group = Group.lookup(self.user1, self.assignment) group.accept(self.user1) assert not submission.flagged
def test_active_user_ids(self): Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) assert self.assignment.active_user_ids(self.user1.id) == \ {self.user1.id, self.user2.id} assert self.assignment.active_user_ids(self.user2.id) == \ {self.user1.id, self.user2.id} assert self.assignment.active_user_ids(self.user3.id) == \ {self.user3.id}
def submit_assignment(name): assign = get_assignment(name) group = Group.lookup(current_user, assign) user_ids = assign.active_user_ids(current_user.id) fs = assign.final_submission(user_ids) if not assign.uploads_enabled: flash("This assignment cannot be submitted online", 'warning') return redirect(url_for('.assignment', name=assign.name)) if not assign.active: flash("It's too late to submit this assignment", 'warning') return redirect(url_for('.assignment', name=assign.name)) form = UploadSubmissionForm() if form.validate_on_submit(): files = request.files.getlist("upload_files") if files: templates = assign.files messages = {'file_contents': {}} for upload in files: data = upload.read() if len(data) > 2097152: # File is too large (over 2 MB) flash("{} is over the maximum file size limit of 2MB".format(upload.filename), 'danger') return redirect(url_for('.submit_assignment', name=assign.name)) messages['file_contents'][upload.filename] = str(data, 'latin1') if templates: missing = [] for template in templates: if template not in messages['file_contents']: missing.append(template) if missing: flash(("Missing files: {}. The following files are required: {}" .format(', '.join(missing), ', '.join([t for t in templates])) ), 'danger') return redirect(url_for('.submit_assignment', name=assign.name)) backup = make_backup(current_user, assign.id, messages, True) if form.flag_submission.data: assign.flag(backup.id, user_ids) if assign.autograding_key: try: submit_continous(backup) except ValueError as e: logger.warning('Web submission did not autograde', exc_info=True) flash('Did not send to autograder: {}'.format(e), 'warning') flash("Uploaded submission (ID: {})".format(backup.hashid), 'success') return redirect(url_for('.assignment', name=assign.name)) return render_template('student/assignment/submit.html', assignment=assign, group=group, course=assign.course, form=form)
def test_group_api(self): self._test_backup(True) self.logout() student = User.lookup(self.user1.email) Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) base_api = '/api/v3/assignment/{0}/group/{1}' endpoint = base_api.format(self.assignment.name, self.user1.email) response = self.client.get(endpoint) self.assert_401(response) self.login(self.user1.email) response = self.client.get(endpoint) self.assert_200(response) members = response.json['data']['members'] self.assertEquals(len(members), 2) assert 'email' in members[0]['user'] # Make sure user2 can access user1's endpoint self.login(self.user2.email) response = self.client.get(endpoint) self.assert_200(response) members = response.json['data']['members'] self.assertEquals(len(members), 2) assert 'email' in members[1]['user'] self.login(self.staff1.email) response = self.client.get(endpoint) self.assert_200(response) members = response.json['data']['members'] self.assertEquals(len(members), 2) assert 'email' in members[0]['user'] # Login as some random user self.login(self.user3.email) response = self.client.get(endpoint) self.assert_403(response) # Check for existence of email response = self.client.get( base_api.format(self.assignment.name, '*****@*****.**')) self.assert_403(response) self.login(self.admin.email) response = self.client.get( base_api.format(self.assignment.name, '*****@*****.**')) self.assert_404(response)
def test_group_api(self): self._test_backup(True) self.logout() student = User.lookup(self.user1.email) Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) base_api = '/api/v3/assignment/{0}/group/{1}' endpoint = base_api.format(self.assignment.name, self.user1.email) response = self.client.get(endpoint) self.assert_401(response) self.login(self.user1.email) response = self.client.get(endpoint) self.assert_200(response) members = response.json['data']['members'] self.assertEqual(len(members), 2) assert 'email' in members[0]['user'] # Make sure user2 can access user1's endpoint self.login(self.user2.email) response = self.client.get(endpoint) self.assert_200(response) members = response.json['data']['members'] self.assertEqual(len(members), 2) assert 'email' in members[1]['user'] self.login(self.staff1.email) response = self.client.get(endpoint) self.assert_200(response) members = response.json['data']['members'] self.assertEqual(len(members), 2) assert 'email' in members[0]['user'] # Login as some random user self.login(self.user3.email) response = self.client.get(endpoint) self.assert_403(response) # Check for existence of email response = self.client.get(base_api.format(self.assignment.name, '*****@*****.**')) self.assert_403(response) self.login(self.admin.email) response = self.client.get(base_api.format(self.assignment.name, '*****@*****.**')) self.assert_404(response)
def test_invite(self): Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) assert group.has_status(self.user1, 'active') assert group.has_status(self.user2, 'pending') assert group.size() == 2 Group.invite(self.user1, self.user3, self.assignment) assert group.has_status(self.user1, 'active') assert group.has_status(self.user2, 'pending') assert group.has_status(self.user3, 'pending') assert group.size() == 3
def test_locked(self): Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) Group.invite(self.user1, self.user4, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) self.assignment.lock_date = datetime.datetime.now() - datetime.timedelta(days=1) db.session.commit() self.assertRaises(BadRequest, Group.invite, self.user1, self.user2, self.assignment) self.assertRaises(BadRequest, group.accept, self.user3) self.assertRaises(BadRequest, group.decline, self.user3) self.assertRaises(BadRequest, group.remove, self.user1, self.user2)
def test_locked(self): Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) Group.invite(self.user1, self.user4, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) self.assignment.lock_date = datetime.datetime.now( ) - datetime.timedelta(days=1) db.session.commit() self.assertRaises(BadRequest, Group.invite, self.user1, self.user2, self.assignment) self.assertRaises(BadRequest, group.accept, self.user3) self.assertRaises(BadRequest, group.decline, self.user3) self.assertRaises(BadRequest, group.remove, self.user1, self.user2)
def test_group_permission(self): Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) # Only the original creator and staff can accept the files self.assertTrue(ExternalFile.can(self.file2, self.user1, 'download')) self.assertTrue(ExternalFile.can(self.file2, self.staff1, 'download')) self.assertFalse(ExternalFile.can(self.file2, self.user2, 'download')) group.accept(self.user2) # Now all group members can access the files self.assertTrue(ExternalFile.can(self.file2, self.user1, 'download')) self.assertTrue(ExternalFile.can(self.file2, self.staff1, 'download')) self.assertTrue(ExternalFile.can(self.file2, self.user2, 'download')) self.assertFalse(ExternalFile.can(self.file2, self.user3, 'download'))
def test_extension_group(self): self.set_offset(1) # Allow assignment to be active Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) self.set_offset(-1) # Lock assignment ext = self._make_ext(self.assignment, self.user1) self.assertEquals(ext, Extension.get_extension(self.user1, self.assignment)) self.assertEquals(ext, Extension.get_extension(self.user2, self.assignment)) # User 3 has not accepted yet so does not get an extension self.assertFalse(Extension.get_extension(self.user3, self.assignment)) # If user 2 leaves, they no longer have access to the extension group.remove(self.user1, self.user2) self.assertFalse(Extension.get_extension(self.user2, self.assignment))
def submit_assignment(name): assign = get_assignment(name) group = Group.lookup(current_user, assign) user_ids = assign.active_user_ids(current_user.id) fs = assign.final_submission(user_ids) if not assign.uploads_enabled: flash("This assignment cannot be submitted online", 'warning') return redirect(url_for('.assignment', name=assign.name)) if not assign.active: flash("It's too late to submit this assignment", 'warning') return redirect(url_for('.assignment', name=assign.name)) form = UploadSubmissionForm() if form.validate_on_submit(): backup = Backup( submitter=current_user, assignment=assign, submit=True, ) if form.upload_files.upload_backup_files(backup): db.session.add(backup) db.session.commit() if assign.autograding_key: try: submit_continous(backup) except ValueError as e: logger.warning('Web submission did not autograde', exc_info=True) flash('Did not send to autograder: {}'.format(e), 'warning') flash('Uploaded submission', 'success') return redirect( url_for( '.code', name=assign.name, submit=backup.submit, bid=backup.id, )) return render_template('student/assignment/submit.html', assignment=assign, group=group, course=assign.course, form=form)
def test_extension_group(self): self.set_offset(1) # Allow assignment to be active Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) self.set_offset(-2) # Lock assignment ext = self._make_ext(self.assignment, self.user1) self.assertEqual(ext, Extension.get_extension(self.user1, self.assignment)) self.assertEqual(ext, Extension.get_extension(self.user2, self.assignment)) # User 3 has not accepted yet so does not get an extension self.assertFalse(Extension.get_extension(self.user3, self.assignment)) # If user 2 leaves, they no longer have access to the extension self.set_offset(1) # Allow assignment to be active to remove the user. group.remove(self.user1, self.user2) self.assertFalse(Extension.get_extension(self.user2, self.assignment))
def test_backup_owners(self): backup = Backup(submitter_id=self.user1.id, assignment=self.assignment, submit=True) backup2 = Backup(submitter_id=self.user2.id, assignment=self.assignment, submit=True) db.session.add(backup) db.session.add(backup2) db.session.commit() assert backup2.owners() == {self.user2.id} Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) assert backup.owners() == {self.user1.id, self.user2.id} assert backup2.owners() == {self.user1.id, self.user2.id}
def test_backup_owners(self): backup = Backup( submitter_id=self.user1.id, assignment=self.assignment, submit=True) backup2 = Backup( submitter_id=self.user2.id, assignment=self.assignment, submit=True) db.session.add(backup) db.session.add(backup2) db.session.commit() assert backup2.owners() == {self.user2.id} Group.invite(self.user1, self.user2, self.assignment) Group.invite(self.user1, self.user3, self.assignment) group = Group.lookup(self.user1, self.assignment) group.accept(self.user2) assert backup.owners() == {self.user1.id, self.user2.id} assert backup2.owners() == {self.user1.id, self.user2.id}
def group_respond(name): assignment = get_assignment(name) action = request.form.get('action') target = request.form.get('email') if not action or action not in ['accept', 'decline', 'revoke']: abort(400) group = Group.lookup(current_user, assignment) if not group: flash("You are not in a group") else: try: if action == "accept": group.accept(current_user) subject = "{0} has accepted the invitation to join your group".format( current_user.email) body = "Your group for {0} now has {1} members".format( assignment.display_name, len(group.members)) group_action_email(group.members, subject, body) elif action == "decline": members = [m.user.email for m in group.members] group.decline(current_user) subject = "{0} declined an invite to join the group".format( current_user.email) body = "{0} declined to join the group for {1}".format( current_user.email, assignment.display_name) send_email(members, subject, body) elif action == "revoke": members = [m.user.email for m in group.members] group.decline(current_user) subject = "{0} invitation for {1} revoked".format( assignment.display_name, target) body = "{0} has revoked the invitation for {1}".format( current_user.email, target) send_email(members, subject, body) except BadRequest as e: flash(e.description, 'danger') return redirect(url_for('.assignment', name=assignment.name))
def submit_assignment(name): assign = get_assignment(name) group = Group.lookup(current_user, assign) user_ids = assign.active_user_ids(current_user.id) fs = assign.final_submission(user_ids) if not assign.uploads_enabled: flash("This assignment cannot be submitted online", 'warning') return redirect(url_for('.assignment', name=assign.name)) if not assign.active: flash("It's too late to submit this assignment", 'warning') return redirect(url_for('.assignment', name=assign.name)) form = UploadSubmissionForm() if form.validate_on_submit(): backup = Backup( submitter=current_user, assignment=assign, submit=True, ) if form.upload_files.upload_backup_files(backup): db.session.add(backup) db.session.commit() if assign.autograding_key: try: submit_continous(backup) except ValueError as e: logger.warning('Web submission did not autograde', exc_info=True) flash('Did not send to autograder: {}'.format(e), 'warning') flash('Uploaded submission', 'success') return redirect(url_for( '.code', name=assign.name, submit=backup.submit, bid=backup.id, )) return render_template('student/assignment/submit.html', assignment=assign, group=group, course=assign.course, form=form)
def group_remove(name): assignment = get_assignment(name) target = User.lookup(request.form['email']) group = Group.lookup(current_user, assignment) if not target: flash("{0} is not enrolled".format(request.form['email']), 'warning') elif not group: flash("You are not in a group", 'warning') else: try: members = [m.user.email for m in group.members] group.remove(current_user, target) subject = "{0} has been removed from your {1} group".format(target.email, assignment.display_name) if target.email == current_user.email: descriptor = "themselves" else: descriptor = target.email body = "{0} removed {1} from the group.".format(current_user.email, descriptor) send_email(members, subject, body) except BadRequest as e: flash(e.description, 'danger') return redirect(url_for('.assignment', name=assignment.name))
def group_respond(name): assignment = get_assignment(name) action = request.form.get('action') target = request.form.get('email') if not action or action not in ['accept', 'decline', 'revoke']: abort(400) group = Group.lookup(current_user, assignment) if not group: flash("You are not in a group") else: try: if action == "accept": group.accept(current_user) subject = "{0} has accepted the invitation to join your group".format(current_user.email) body = "Your group for {0} now has {1} members".format(assignment.display_name, len(group.members)) group_action_email(group.members, subject, body) elif action == "decline": members = [m.user.email for m in group.members] group.decline(current_user) subject = "{0} declined an invite to join the group".format(current_user.email) body = "{0} declined to join the group for {1}".format(current_user.email, assignment.display_name) send_email(members, subject, body) elif action == "revoke": members = [m.user.email for m in group.members] group.decline(current_user) subject = "{0} invitation for {1} revoked".format(assignment.display_name, target) body = "{0} has revoked the invitation for {1}".format(current_user.email, target) send_email(members, subject, body) except BadRequest as e: flash(e.description, 'danger') return redirect(url_for('.assignment', name=assignment.name))
def setUp(self): """ Add submissions for 3 users. """ super(TestRevision, self).setUp() self.setup_course() message_dict = {'file_contents': {'backup.py': '1'}, 'analytics': {}} self.active_user_ids = [self.user1.id, self.user2.id, self.user3.id] self.assignment.revisions_allowed = True time = self.assignment.due_date # Set to dt.now(), so future subms are late for user_id in self.active_user_ids: time -= datetime.timedelta(minutes=15) backup = Backup(submitter_id=user_id, assignment=self.assignment, submit=True) # Revisions are submitted on time. backup.created = time messages = [ Message(kind=k, backup=backup, contents=m) for k, m in message_dict.items() ] db.session.add_all(messages) db.session.add(backup) # Put user 3 in a group with user 4 Group.invite(self.user3, self.user4, self.assignment) group = Group.lookup(self.user3, self.assignment) group.accept(self.user4) okversion = Version(name="ok-client", current_version="v1.5.0", download_link="http://localhost/ok") db.session.add(okversion) db.session.commit()
def submit_assignment(name): assign = get_assignment(name) group = Group.lookup(current_user, assign) user_ids = assign.active_user_ids(current_user.id) fs = assign.final_submission(user_ids) if not assign.uploads_enabled: flash("This assignment cannot be submitted online", 'warning') return redirect(url_for('.assignment', name=assign.name)) if not assign.active: flash("It's too late to submit this assignment", 'warning') return redirect(url_for('.assignment', name=assign.name)) form = UploadSubmissionForm() if form.validate_on_submit(): files = request.files.getlist("upload_files") if files: templates = assign.files messages = {'file_contents': {}} for upload in files: data = upload.read() if len(data) > 2097152: # File is too large (over 2 MB) flash( "{} is over the maximum file size limit of 2MB".format( upload.filename), 'danger') return redirect( url_for('.submit_assignment', name=assign.name)) messages['file_contents'][upload.filename] = str( data, 'latin1') if templates: missing = [] for template in templates: if template not in messages['file_contents']: missing.append(template) if missing: flash(( "Missing files: {}. The following files are required: {}" .format(', '.join(missing), ', '.join( [t for t in templates]))), 'danger') return redirect( url_for('.submit_assignment', name=assign.name)) backup = make_backup(current_user, assign.id, messages, True) if form.flag_submission.data: assign.flag(backup.id, user_ids) if assign.autograding_key: try: submit_continous(backup) except ValueError as e: logger.warning('Web submission did not autograde', exc_info=True) flash('Did not send to autograder: {}'.format(e), 'warning') flash("Uploaded submission (ID: {})".format(backup.hashid), 'success') return redirect(url_for('.assignment', name=assign.name)) return render_template('student/assignment/submit.html', assignment=assign, group=group, course=assign.course, form=form)
def gen_invite(member, invitee, assignment, accept=False): Group.invite(member, invitee, assignment) group = Group.lookup(invitee, assignment) if accept: group.accept(invitee) return group
def test_decline_not_pending(self): Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) self.assertRaises(BadRequest, group.decline, self.user3)
def test_log(self): def latest_action(): return GroupAction.query.order_by(GroupAction.id.desc()).first() Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) state = { 'id': group.id, 'assignment_id': group.assignment_id, 'members': [] } action = latest_action() assert action.action_type == 'invite' assert action.user_id == self.user1.id assert action.target_id == self.user2.id assert action.group_before == state state['members'].append({'user_id': self.user1.id, 'status': 'active'}) state['members'].append({ 'user_id': self.user2.id, 'status': 'pending' }) assert action.group_after == state group.accept(self.user2) action = latest_action() assert action.action_type == 'accept' assert action.user_id == self.user2.id assert action.target_id == self.user2.id assert action.group_before == state state['members'][1]['status'] = 'active' assert action.group_after == state Group.invite(self.user1, self.user3, self.assignment) action = latest_action() assert action.action_type == 'invite' assert action.user_id == self.user1.id assert action.target_id == self.user3.id assert action.group_before == state state['members'].append({ 'user_id': self.user3.id, 'status': 'pending' }) assert action.group_after == state group.decline(self.user3) action = latest_action() assert action.action_type == 'decline' assert action.user_id == self.user3.id assert action.target_id == self.user3.id assert action.group_before == state state['members'].pop(2) assert action.group_after == state group.remove(self.user2, self.user1) action = latest_action() assert action.action_type == 'remove' assert action.user_id == self.user2.id assert action.target_id == self.user1.id assert action.group_before == state state['members'] = [] assert action.group_after == state
def submit_assignment(name): # TODO: Unify student & staff upload. assign = get_assignment(name) group = Group.lookup(current_user, assign) user_ids = assign.active_user_ids(current_user.id) fs = assign.final_submission(user_ids) if not assign.uploads_enabled: flash("This assignment cannot be submitted online", 'warning') return redirect(url_for('.assignment', name=assign.name)) extension = None # No need for an extension if not assign.active: extension = Extension.get_extension(current_user, assign) if not extension: flash("It's too late to submit this assignment", 'warning') return redirect(url_for('.assignment', name=assign.name)) if request.method == "POST": backup = Backup.create( submitter=current_user, assignment=assign, submit=True, ) assignment = backup.assignment if extension: backup.custom_submission_time = extension.custom_submission_time templates = assignment.files or [] files = {} def extract_file_index(file_ind): """ Get the index of of file objects. Used because request.files.getlist() does not handle uniquely indexed lists. >>> extract_file_index('file[12']) 12 """ brace_loc = file_ind.find('[') index_str = file_ind[brace_loc+1:-1] return int(index_str) # A list of one element lists sorted_uploads = sorted(list(request.files.items()), key=lambda x: extract_file_index(x[0])) uploads = [v[1] for v in sorted_uploads] full_path_names = list(request.form.listvalues())[0] template_files = assign.files or [] file_names = [os.path.split(f)[1] for f in full_path_names] missing = [t for t in template_files if t not in file_names] if missing: return jsonify({ 'error': ('Missing files: {}. The following files are required: {}' .format(', '.join(missing), ', '.join(template_files))) }), 400 backup_folder_postfix = time.time() for full_path, upload in zip(full_path_names, uploads): data = upload.read() if len(data) > MAX_UPLOAD_FILE_SIZE: # file is too large (over 25 MB) return jsonify({ 'error': ('{} is larger than the maximum file size of {} MB' .format(full_path, MAX_UPLOAD_FILE_SIZE/1024/1024)) }), 400 try: files[full_path] = str(data, 'utf-8') except UnicodeDecodeError: upload.stream.seek(0) # We've already read data, so reset before uploading dest_folder = "uploads/{}/{}/{}/".format(assign.name, current_user.id, backup_folder_postfix) bin_file = ExternalFile.upload(upload.stream, current_user.id, full_path, staff_file=False, prefix=dest_folder, course_id=assign.course.id, backup=backup, assignment_id=assign.id) db.session.add(bin_file) message = Message(kind='file_contents', contents=files) backup.messages.append(message) db.session.add(backup) db.session.commit() # Send to continuous autograder if assign.autograding_key and assign.continuous_autograding: try: submit_continuous(backup) except ValueError as e: flash('Did not send to autograder: {}'.format(e), 'warning') return jsonify({ 'backup': backup.hashid, 'url': url_for('.code', name=assign.name, submit=backup.submit, bid=backup.id) }) return render_template('student/assignment/submit.html', assignment=assign, group=group, course=assign.course)
def test_log(self): def latest_action(): return GroupAction.query.order_by(GroupAction.id.desc()).first() Group.invite(self.user1, self.user2, self.assignment) group = Group.lookup(self.user1, self.assignment) state = { 'id': group.id, 'assignment_id': group.assignment_id, 'members': [] } action = latest_action() assert action.action_type == 'invite' assert action.user_id == self.user1.id assert action.target_id == self.user2.id assert action.group_before == state state['members'].append({ 'user_id': self.user1.id, 'status': 'active' }) state['members'].append({ 'user_id': self.user2.id, 'status': 'pending' }) assert action.group_after == state group.accept(self.user2) action = latest_action() assert action.action_type == 'accept' assert action.user_id == self.user2.id assert action.target_id == self.user2.id assert action.group_before == state state['members'][1]['status'] = 'active' assert action.group_after == state Group.invite(self.user1, self.user3, self.assignment) action = latest_action() assert action.action_type == 'invite' assert action.user_id == self.user1.id assert action.target_id == self.user3.id assert action.group_before == state state['members'].append({ 'user_id': self.user3.id, 'status': 'pending' }) assert action.group_after == state group.decline(self.user3) action = latest_action() assert action.action_type == 'decline' assert action.user_id == self.user3.id assert action.target_id == self.user3.id assert action.group_before == state state['members'].pop(2) assert action.group_after == state group.remove(self.user2, self.user1) action = latest_action() assert action.action_type == 'remove' assert action.user_id == self.user2.id assert action.target_id == self.user1.id assert action.group_before == state state['members'] = [] assert action.group_after == state