Commit deacfebd by Diana Huang

Merge pull request #9607 from edx/diana/teams-event-tracking

Add teams eventing and tests.
parents d567e5e3 a743f4a6
......@@ -11,7 +11,7 @@ from flaky import flaky
from nose.plugins.attrib import attr
from uuid import uuid4
from ..helpers import UniqueCourseTest
from ..helpers import UniqueCourseTest, EventsTestMixin
from ...fixtures import LMS_BASE_URL
from ...fixtures.course import CourseFixture
from ...fixtures.discussion import (
......@@ -28,7 +28,7 @@ from ...pages.lms.teams import TeamsPage, MyTeamsPage, BrowseTopicsPage, BrowseT
TOPICS_PER_PAGE = 12
class TeamsTabBase(UniqueCourseTest):
class TeamsTabBase(EventsTestMixin, UniqueCourseTest):
"""Base class for Teams Tab tests"""
def setUp(self):
super(TeamsTabBase, self).setUp()
......@@ -123,6 +123,10 @@ class TeamsTabBase(UniqueCourseTest):
# We are doing these operations on this top-level page object to avoid reloading the page.
self.teams_page.verify_my_team_count(expected_number_of_teams)
def only_team_events(self, event):
"""Filter out all non-team events."""
return event['event_type'].startswith('edx.team.')
@ddt.ddt
@attr('shard_5')
......@@ -899,7 +903,8 @@ class CreateTeamTest(TeamFormActions):
Then I should see the Create Team header and form
When I fill all the fields present with appropriate data
And I click Create button
Then I should see the page for my team
Then I expect analytics events to be emitted
And I should see the page for my team
And I should see the message that says "You are member of this team"
And the new team should be added to the list of teams within the topic
And the number of teams should be updated on the topic card
......@@ -911,7 +916,24 @@ class CreateTeamTest(TeamFormActions):
self.verify_and_navigate_to_create_team_page()
self.fill_create_or_edit_form()
self.create_or_edit_team_page.submit_form()
expected_events = [
{
'event_type': 'edx.team.created',
'event': {
'course_id': self.course_id,
}
},
{
'event_type': 'edx.team.learner_added',
'event': {
'course_id': self.course_id,
'add_method': 'added_on_create',
}
}
]
with self.assert_events_match_during(event_filter=self.only_team_events, expected_events=expected_events):
self.create_or_edit_team_page.submit_form()
# Verify that the page is shown for the new team
team_page = TeamPage(self.browser, self.course_id)
......@@ -1330,6 +1352,7 @@ class TeamPageTest(TeamsTabBase):
And I should not see New Post button
When I click on Join Team button
Then there should be no Join Team button and no message
And an analytics event should be emitted
And I should see the updated information under Team Details
And I should see New Post button
And if I switch to "My Team", the team I have joined is displayed
......@@ -1337,7 +1360,17 @@ class TeamPageTest(TeamsTabBase):
self._set_team_configuration_and_membership(create_membership=False)
self.team_page.visit()
self.assertTrue(self.team_page.join_team_button_present)
self.team_page.click_join_team_button()
expected_events = [
{
'event_type': 'edx.team.learner_added',
'event': {
'course_id': self.course_id,
'add_method': 'joined_from_team_view'
}
}
]
with self.assert_events_match_during(event_filter=self.only_team_events, expected_events=expected_events):
self.team_page.click_join_team_button()
self.assertFalse(self.team_page.join_team_button_present)
self.assertFalse(self.team_page.join_team_message_present)
self.assert_team_details(num_members=1, is_member=True)
......@@ -1397,6 +1430,7 @@ class TeamPageTest(TeamsTabBase):
Then I should see Leave Team link
When I click on Leave Team link
Then user should be removed from team
And an analytics event should be emitted
And I should see Join Team button
And I should not see New Post button
And if I switch to "My Team", the team I have left is not displayed
......@@ -1405,7 +1439,17 @@ class TeamPageTest(TeamsTabBase):
self.team_page.visit()
self.assertFalse(self.team_page.join_team_button_present)
self.assert_team_details(num_members=1)
self.team_page.click_leave_team_link()
expected_events = [
{
'event_type': 'edx.team.learner_removed',
'event': {
'course_id': self.course_id,
'remove_method': 'self_removal'
}
}
]
with self.assert_events_match_during(event_filter=self.only_team_events, expected_events=expected_events):
self.team_page.click_leave_team_link()
self.assert_team_details(num_members=0, is_member=False)
self.assertTrue(self.team_page.join_team_button_present)
......
......@@ -14,6 +14,7 @@ from rest_framework.test import APITestCase, APIClient
from courseware.tests.factories import StaffFactory
from common.test.utils import skip_signal
from util.testing import EventTestMixin
from student.tests.factories import UserFactory, AdminFactory, CourseEnrollmentFactory
from student.models import CourseEnrollment
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
......@@ -529,9 +530,12 @@ class TestListTeamsAPI(TeamAPITestCase):
@ddt.ddt
class TestCreateTeamAPI(TeamAPITestCase):
class TestCreateTeamAPI(EventTestMixin, TeamAPITestCase):
"""Test cases for the team creation endpoint."""
def setUp(self): # pylint: disable=arguments-differ
super(TestCreateTeamAPI, self).setUp('teams.views.tracker')
@ddt.data(
(None, 401),
('student_inactive', 401),
......@@ -549,11 +553,15 @@ class TestCreateTeamAPI(TeamAPITestCase):
teams = self.get_teams_list(user=user)
self.assertIn("New Team", [team['name'] for team in teams['results']])
def _expected_team_id(self, team, expected_prefix):
""" Return the team id that we'd expect given this team data and this prefix. """
return expected_prefix + '-' + team['discussion_topic_id']
def verify_expected_team_id(self, team, expected_prefix):
""" Verifies that the team id starts with the specified prefix and ends with the discussion_topic_id """
self.assertIn('id', team)
self.assertIn('discussion_topic_id', team)
self.assertEqual(team['id'], expected_prefix + '-' + team['discussion_topic_id'])
self.assertEqual(team['id'], self._expected_team_id(team, expected_prefix))
def test_naming(self):
new_teams = [
......@@ -640,6 +648,19 @@ class TestCreateTeamAPI(TeamAPITestCase):
self.verify_expected_team_id(team, 'fully-specified-team')
del team['id']
self.assert_event_emitted(
'edx.team.created',
team_id=self._expected_team_id(team, 'fully-specified-team'),
course_id=unicode(self.test_course_1.id),
)
self.assert_event_emitted(
'edx.team.learner_added',
team_id=self._expected_team_id(team, 'fully-specified-team'),
course_id=unicode(self.test_course_1.id),
user_id=self.users[creator].id,
add_method='added_on_create'
)
# Remove date_created and discussion_topic_id because they change between test runs
del team['date_created']
del team['discussion_topic_id']
......@@ -1026,9 +1047,12 @@ class TestListMembershipAPI(TeamAPITestCase):
@ddt.ddt
class TestCreateMembershipAPI(TeamAPITestCase):
class TestCreateMembershipAPI(EventTestMixin, TeamAPITestCase):
"""Test cases for the membership creation endpoint."""
def setUp(self): # pylint: disable=arguments-differ
super(TestCreateMembershipAPI, self).setUp('teams.views.tracker')
@ddt.data(
(None, 401),
('student_inactive', 401),
......@@ -1053,6 +1077,18 @@ class TestCreateMembershipAPI(TeamAPITestCase):
memberships = self.get_membership_list(200, {'team_id': self.solar_team.team_id})
self.assertEqual(memberships['count'], 2)
add_method = 'joined_from_team_view' if user == 'student_enrolled_not_on_team' else 'added_by_another_user'
self.assert_event_emitted(
'edx.team.learner_added',
team_id=self.solar_team.team_id,
user_id=self.users['student_enrolled_not_on_team'].id,
course_id=unicode(self.solar_team.course_id),
add_method=add_method
)
else:
self.assert_no_events_were_emitted()
def test_no_username(self):
response = self.post_create_membership(400, {'team_id': self.solar_team.team_id})
self.assertIn('username', json.loads(response.content)['field_errors'])
......@@ -1176,9 +1212,12 @@ class TestDetailMembershipAPI(TeamAPITestCase):
@ddt.ddt
class TestDeleteMembershipAPI(TeamAPITestCase):
class TestDeleteMembershipAPI(EventTestMixin, TeamAPITestCase):
"""Test cases for the membership deletion endpoint."""
def setUp(self): # pylint: disable=arguments-differ
super(TestDeleteMembershipAPI, self).setUp('teams.views.tracker')
@ddt.data(
(None, 401),
('student_inactive', 401),
......@@ -1198,6 +1237,18 @@ class TestDeleteMembershipAPI(TeamAPITestCase):
user=user
)
if status == 204:
remove_method = 'self_removal' if user == 'student_enrolled' else 'removed_by_admin'
self.assert_event_emitted(
'edx.team.learner_removed',
team_id=self.solar_team.team_id,
course_id=unicode(self.solar_team.course_id),
user_id=self.users['student_enrolled'].id,
remove_method=remove_method
)
else:
self.assert_no_events_were_emitted()
def test_bad_team(self):
self.delete_membership('no_such_team', self.users['student_enrolled'].username, 404)
......
......@@ -34,6 +34,7 @@ from xmodule.modulestore.django import modulestore
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from eventtracking import tracker
from courseware.courses import get_course_with_access, has_access
from student.models import CourseEnrollment, CourseAccessRole
from student.roles import CourseStaffRole
......@@ -417,9 +418,22 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
}, status=status.HTTP_400_BAD_REQUEST)
else:
team = serializer.save()
tracker.emit('edx.team.created', {
'team_id': team.team_id,
'course_id': unicode(course_id)
})
if not team_administrator:
# Add the creating user to the team.
team.add_user(request.user)
tracker.emit(
'edx.team.learner_added',
{
'team_id': team.team_id,
'user_id': request.user.id,
'course_id': unicode(team.course_id),
'add_method': 'added_on_create'
}
)
return Response(CourseTeamSerializer(team).data)
def get_page(self):
......@@ -974,6 +988,15 @@ class MembershipListView(ExpandableFieldViewMixin, GenericAPIView):
try:
membership = team.add_user(user)
tracker.emit(
'edx.team.learner_added',
{
'team_id': team.team_id,
'user_id': user.id,
'course_id': unicode(team.course_id),
'add_method': 'joined_from_team_view' if user == request.user else 'added_by_another_user'
}
)
except AlreadyOnTeamInCourse:
return Response(
build_api_error(
......@@ -1100,6 +1123,15 @@ class MembershipDetailView(ExpandableFieldViewMixin, GenericAPIView):
if has_team_api_access(request.user, team.course_id, access_username=username):
membership = self.get_membership(username, team)
membership.delete()
tracker.emit(
'edx.team.learner_removed',
{
'team_id': team.team_id,
'course_id': unicode(team.course_id),
'user_id': membership.user.id,
'remove_method': 'self_removal' if membership.user == request.user else 'removed_by_admin'
}
)
return Response(status=status.HTTP_204_NO_CONTENT)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment