Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions backend/ohq/invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ def parse_and_send_invites(course, emails, kind):
# Map of pennkey to invite email (which may be different from the user's email)
email_map = {email.split("@")[0]: email for email in emails}

# Remove invitees already in class
# Remove invitees already in class with same kind
existing = Membership.objects.filter(
course=course, user__username__in=email_map.keys()
course=course, user__username__in=email_map.keys(), kind=kind
).values_list("user__username", flat=True)
existing = [email_map[pennkey] for pennkey in existing]

emails = list(set(emails) - set(existing))

# Remove users already invited
existing = MembershipInvite.objects.filter(course=course, email__in=emails).values_list(
"email", flat=True
)
# Remove users already invited with same kind
existing = MembershipInvite.objects.filter(
course=course, email__in=emails, kind=kind
).values_list("email", flat=True)
emails = list(set(emails) - set(existing))

# Generate final map of pennkey to email of users that need to be invited
Expand All @@ -42,13 +42,17 @@ def parse_and_send_invites(course, emails, kind):
# Directly add invitees with existing accounts
users = User.objects.filter(Q(email__in=emails) | Q(username__in=email_map.keys())).distinct()
for user in users:
membership = Membership.objects.create(course=course, user=user, kind=kind)
membership, _ = Membership.objects.update_or_create(
course=course, user=user, defaults={"kind": kind}
)
membership.send_email()
del email_map[user.username]

# Create membership invites for invitees without an account
for email in email_map.values():
invite = MembershipInvite.objects.create(email=email, course=course, kind=kind)
invite, _ = MembershipInvite.objects.update_or_create(
email=email, course=course, defaults={"kind": kind}
)
invite.send_email()

return (users.count(), len(email_map))
69 changes: 48 additions & 21 deletions backend/tests/ohq/test_invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,26 @@

class ParseAndSendInvitesTestCase(TestCase):
def setUp(self):
self.professor = User.objects.create(username="professor", email="professor@seas.upenn.edu")
self.semester = Semester.objects.create(year=2020, term=Semester.TERM_SUMMER)
self.course = Course.objects.create(
course_code="000", department="TEST", course_title="Title", semester=self.semester
)
Membership.objects.create(
course=self.course, user=self.professor, kind=Membership.KIND_PROFESSOR

self.user1 = User.objects.create(username="user1", email="user1@seas.upenn.edu")
Membership.objects.create(course=self.course, user=self.user1, kind=Membership.KIND_STUDENT)

self.user2 = User.objects.create(username="user2", email="user2@seas.upenn.edu")
Membership.objects.create(course=self.course, user=self.user2, kind=Membership.KIND_TA)

self.user3 = User.objects.create(username="user3", email="user3@seas.upenn.edu")

MembershipInvite.objects.create(
course=self.course, email="user4@wharton.upenn.edu", kind=Membership.KIND_STUDENT
)
self.user2 = User.objects.create(username="user2", email="user2@sas.upenn.edu")

MembershipInvite.objects.create(course=self.course, email="user3@wharton.upenn.edu")
MembershipInvite.objects.create(
course=self.course, email="user5@wharton.upenn.edu", kind=Membership.KIND_TA
)

def test_invalid_email(self):
with self.assertRaises(ValidationError):
Expand All @@ -30,43 +39,61 @@ def test_invalid_email(self):
def test_valid_emails(self):
"""
Test situations where
* the user is already a member under a different email
* the user is not a member of the course and has different email
* the email has already been sent an invite
* the user is already a member but is different type - user1 (expected: added, not invited)
* the user is already a member and is same type - user2 (expected: not added, not invited)
* the user is not a member of the course and has different email - user3 (expected: added, not invited)
* the email has already been sent an invite but is different type - user4 (expected: not added, invited)
* the email has already been sent an invite and is same type - user5 (expected: not added, not invited)
* the user is not a member of any course - user6 (expected: not added, invited)
"""
members_added, invites_sent = parse_and_send_invites(
self.course,
[
"professor@sas.upenn.edu",
"user1@seas.upenn.edu",
"user2@seas.upenn.edu",
"user3@wharton.upenn.edu",
"user4@nursing.upenn.edu",
"user3@sas.upenn.edu",
"user4@wharton.upenn.edu",
"user5@wharton.upenn.edu",
"user6@nursing.upenn.edu",
],
Membership.KIND_TA,
)

# # Correct number of invites and memberships created
self.assertEqual(1, members_added)
self.assertEqual(1, invites_sent)
self.assertEqual(2, members_added)
self.assertEqual(2, invites_sent)

# Membership is created for user1
self.assertEqual(
1,
Membership.objects.filter(
user=self.user1, course=self.course, kind=Membership.KIND_TA
).count(),
)

# Membership is created for user2
# Membership is created for user3
self.assertEqual(
1,
Membership.objects.filter(
user=self.user2, course=self.course, kind=Membership.KIND_TA
user=self.user3, course=self.course, kind=Membership.KIND_TA
).count(),
)

# Duplicate membership for user 1 isn't created
self.assertEqual(2, Membership.objects.all().count())
# Duplicate membership for user2 isn't created
self.assertEqual(3, Membership.objects.all().count())

# Invite is sent to 4@nursing.upenn.edu
# Invite is sent to user4@wharton.upenn.edu
self.assertEqual(
1,
MembershipInvite.objects.filter(
email="user4@nursing.upenn.edu", course=self.course, kind=Membership.KIND_TA
email="user4@wharton.upenn.edu", course=self.course, kind=Membership.KIND_TA
).count(),
)

# Duplicate membership invite for 3@example.com isn't created
self.assertEqual(2, MembershipInvite.objects.all().count())
# Invite is sent to user6@nursing.upenn.edu
self.assertEqual(
1,
MembershipInvite.objects.filter(
email="user6@nursing.upenn.edu", course=self.course, kind=Membership.KIND_TA
).count(),
)
26 changes: 20 additions & 6 deletions backend/tests/ohq/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,29 +60,43 @@ def test_resend_success(self):
class MassInviteTestCase(TestCase):
def setUp(self):
self.client = APIClient()
self.professor = User.objects.create(username="professor", email="professor@example.com")
self.semester = Semester.objects.create(year=2020, term=Semester.TERM_SUMMER)
self.course = Course.objects.create(
course_code="000", department="TEST", course_title="Title", semester=self.semester
)

self.professor = User.objects.create(username="professor", email="professor@example.com")
Membership.objects.create(
course=self.course, user=self.professor, kind=Membership.KIND_PROFESSOR
)

self.user1 = User.objects.create(username="user1", email="user1@example.com")
Membership.objects.create(course=self.course, user=self.user1, kind=Membership.KIND_STUDENT)

self.user2 = User.objects.create(username="user2", email="user2@example.com")
Membership.objects.create(course=self.course, user=self.user2, kind=Membership.KIND_TA)

MembershipInvite.objects.create(course=self.course, email="user3@example.com")
self.user3 = User.objects.create(username="user3", email="user3@example.com")

MembershipInvite.objects.create(
course=self.course, email="user4@example.com", kind=Membership.KIND_STUDENT
)

MembershipInvite.objects.create(
course=self.course, email="user5@example.com", kind=Membership.KIND_TA
)

def test_invalid_email(self):
self.client.force_authenticate(user=self.professor)
response = self.client.post(
reverse("ohq:mass-invite", args=[self.course.id]),
data={"emails": "invalidemail", "role": "TA"},
data={"emails": "invalidemail", "kind": "TA"},
)
self.assertEqual(400, response.status_code)

def test_valid_emails(self):
self.client.force_authenticate(user=self.professor)
emails = "professor@example.com,user2@example.com,user3@example.com,user4@example.com"
emails = "user1@example.com,user2@example.com,user3@example.com,user4@example.com,user5@example.com,user6@example.com"
response = self.client.post(
reverse("ohq:mass-invite", args=[self.course.id]),
data={"emails": emails, "kind": "TA"},
Expand All @@ -92,8 +106,8 @@ def test_valid_emails(self):

# Correct number of invites and memberships created
content = json.loads(response.content)
self.assertEqual(1, content["membersAdded"])
self.assertEqual(1, content["invitesSent"])
self.assertEqual(2, content["membersAdded"])
self.assertEqual(2, content["invitesSent"])


class QuestionViewTestCase(TestCase):
Expand Down