Skip to content

Commit

Permalink
Merge 0db4649 into 26faa58
Browse files Browse the repository at this point in the history
  • Loading branch information
knrafto committed Jan 28, 2016
2 parents 26faa58 + 0db4649 commit 133a9ba
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 25 deletions.
18 changes: 18 additions & 0 deletions server/controllers/student.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,21 @@ def code(cid, aid, bid):
flash("That assignment does not exist", "danger")

abort(404)

@student.route("/course/<int:cid>/assignment/<int:aid>/<int:bid>/flag/", methods=['POST'])
@login_required
@is_enrolled
def flag(cid, aid, bid):
assgn = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
if assgn:
course = assgn.course
user_ids = assgn.active_user_ids(current_user.id)
flag = 'flag' in request.form
next_url = request.form['next']
if flag:
assgn.flag(bid, user_ids)
else:
assgn.unflag(bid, user_ids)
return redirect(next_url)
else:
abort(404)
44 changes: 42 additions & 2 deletions server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,43 @@ def final_submission(self, user_ids):
Backup.submit == True
).order_by(Backup.flagged.desc(), Backup.created.desc()).first()

@transaction
def flag(self, backup_id, member_ids):
"""Flag a submission. First unflags any submissions by one of
MEMBER_IDS, which is a list of group member user IDs.
"""
self._unflag_all(member_ids)
backup = Backup.query.filter(
Backup.id == backup_id,
Backup.submitter_id.in_(member_ids),
Backup.flagged == False
).one_or_none()
if not backup:
raise BadRequest('Could not find backup')
backup.flagged = True

@transaction
def unflag(self, backup_id, member_ids):
"""Unflag a submission."""
backup = Backup.query.filter(
Backup.id == backup_id,
Backup.submitter_id.in_(member_ids),
Backup.flagged == True
).one_or_none()
if not backup:
raise BadRequest('Could not find backup')
backup.flagged = False

def _unflag_all(self, member_ids):
"""Unflag all submissions by members of MEMBER_IDS."""
# There should only ever be one flagged submission
backup = Backup.query.filter(
Backup.submitter_id.in_(member_ids),
Backup.flagged == True
).one_or_none()
if backup:
backup.flagged = False

class Enrollment(db.Model, TimestampMixin):
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.ForeignKey("user.id"), index=True, nullable=False)
Expand Down Expand Up @@ -239,10 +276,12 @@ def create(cid, usr_ids=[], role=STUDENT_ROLE):

class Message(db.Model, TimestampMixin):
id = db.Column(db.Integer(), primary_key=True)
backup = db.Column(db.ForeignKey("backup.id"), index=True)
backup_id = db.Column(db.ForeignKey("backup.id"), index=True)
raw_contents = db.Column(db.String())
kind = db.Column(db.String(), index=True)

backup = db.relationship("Backup")

@hybrid_property
def contents(self):
return json.dumps(str(self.raw_contents))
Expand Down Expand Up @@ -383,7 +422,7 @@ class GroupMember(db.Model, TimestampMixin):
pending - The user has been invited to the group.
active - The user accepted the invite and is part of the group.
"""
__tablename__ = 'GroupMember'
__tablename__ = 'group_member'
__table_args__ = (
PrimaryKeyConstraint('user_id', 'assignment_id', name='pk_GroupMember'),
)
Expand Down Expand Up @@ -478,6 +517,7 @@ def accept(self, user):
if not member:
raise BadRequest('{0} is not invited to this group'.format(user.email))
member.status = 'active'
self.assignment._unflag_all(self.assignment.active_user_ids(user.id))

@transaction
def decline(self, user):
Expand Down
32 changes: 14 additions & 18 deletions server/templates/student/assignment/_tablehelper.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,23 @@ <h2>{{ tname }}</h2>
</tr>
{% for subm in subms %}
<tr>

<td class="tip-trigger">
{% if subm.flagged %}
<span class="final-yes"><i class="fa fa-flag"></i>
<div class="tip">
<span>Marked for grading</span>
</div>
</span>
{% elif not flagged and loop.index == 1 %}
<span class="grey"><i class="fa fa-flag"></i>
<div class="tip">
<span> This is the most recent submission. <br> Unless you flag another submission, this one will be graded</span>
<form class="form-inline" action="{{ url_for('.flag', cid=cid, aid=subm.assignment_id, bid=subm.id) }}" method="post">
{% if not subm.flagged %}
<input type="hidden" name="flag">
{% endif %}
<input type="hidden" name="next" value="{{ request.full_path }}">
<div class="final-{{ 'yes' if subm.flagged else 'no' }}">
<button type="submit"><i class="fa fa-flag"></i></button>
</div>
</span>
{% else %}
<span class="final-no"><i class="fa fa-flag-o"></i>
<div class="tip grey">
<span>Not flagged for grading.
</div>
</span>
</form>
<div class="tip">
{% if subm.flagged %}
<span>Flagged for grading (click to unflag)</span>
{% else %}
<span>Not flagged for grading (click to flag)</span>
{% endif %}
</div>
</td>
<td>
<a href="{{ url_for('.code', cid=cid, aid=subm.assignment_id, bid=subm.id)}}">
Expand Down
86 changes: 81 additions & 5 deletions tests/test_submission.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
from .helpers import OkTestCase
import datetime
import json
from werkzeug.exceptions import BadRequest

from server.models import db, Backup, Group, Message

from server.models import db, Group
from .helpers import OkTestCase

class TestSubmission(OkTestCase):
"""Tests querying for submissions and backups, flagging submissions, and
final submissions.
"""
"""Tests flagging submissions and final submissions."""
def setUp(self):
super(TestSubmission, 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]

# create a submission every 15 minutes
time = self.assignment.due_date
for _ in range(20):
for user_id in self.active_user_ids:
time -= datetime.timedelta(minutes=15)
backup = Backup(client_time=time,
submitter_id=user_id,
assignment=self.assignment, submit=True)
messages = [Message(kind=k, backup=backup,
raw_contents=json.dumps(m)) for k, m in message_dict.items()]
db.session.add_all(messages)
db.session.add(backup)
db.session.commit()

def test_active_user_ids(self):
Group.invite(self.user1, self.user2, self.assignment)
Group.invite(self.user1, self.user3, self.assignment)
Expand All @@ -22,3 +42,59 @@ def test_active_user_ids(self):
{self.user1.id, self.user2.id}
assert self.assignment.active_user_ids(self.user3.id) == \
{self.user3.id}

def test_no_flags(self):
final = self.assignment.final_submission(self.active_user_ids)
most_recent = self.assignment.submissions(self.active_user_ids).first()
assert final == most_recent

def test_flag(self):
submission = self.assignment.submissions(self.active_user_ids).all()[3]
self.assignment.flag(submission.id, self.active_user_ids)

final = self.assignment.final_submission(self.active_user_ids)
assert final == submission

def test_two_flags(self):
submission1 = self.assignment.submissions(self.active_user_ids).all()[3]
submission2 = self.assignment.submissions(self.active_user_ids).all()[7]
self.assignment.flag(submission1.id, self.active_user_ids)
self.assignment.flag(submission2.id, self.active_user_ids)

final = self.assignment.final_submission(self.active_user_ids)
assert final == submission2
assert not submission1.flagged

def test_unflag(self):
submission = self.assignment.submissions(self.active_user_ids).all()[3]
self.assignment.flag(submission.id, self.active_user_ids)
self.assignment.unflag(submission.id, self.active_user_ids)

final = self.assignment.final_submission(self.active_user_ids)
most_recent = self.assignment.submissions(self.active_user_ids).first()
assert final == most_recent
assert not submission.flagged

def test_unflag_not_flagged(self):
submission = self.assignment.submissions(self.active_user_ids).all()[3]
self.assertRaises(BadRequest, self.assignment.unflag, submission.id, self.active_user_ids)

def test_sabotage(self):
submission = self.assignment.submissions([self.user1.id]).all()[3]
self.assertRaises(BadRequest, self.assignment.flag, submission.id, [self.user2.id])

self.assignment.flag(submission.id, [self.user1.id])
self.assertRaises(BadRequest, self.assignment.unflag, submission.id, [self.user2.id])

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

0 comments on commit 133a9ba

Please sign in to comment.