Commit fa2154a6 by Renzo Lucioni

Merge pull request #4893 from edx/web-analytics/pass-ga-cookie

Re-use GA cookie when sending server-side events to Segment.io
parents 3efc384f 79461722
......@@ -756,6 +756,7 @@ class CourseEnrollment(models.Model):
tracker.emit(event_name, data)
if settings.FEATURES.get('SEGMENT_IO_LMS') and settings.SEGMENT_IO_LMS_KEY:
tracking_context = tracker.get_tracker().resolve_context()
analytics.track(self.user_id, event_name, {
'category': 'conversion',
'label': self.course_id.to_deprecated_string(),
......@@ -763,7 +764,12 @@ class CourseEnrollment(models.Model):
'course': self.course_id.course,
'run': self.course_id.run,
'mode': self.mode,
}, context={
'Google Analytics': {
'clientId': tracking_context.get('client_id')
}
})
except: # pylint: disable=bare-except
if event_name and self.course_id:
log.exception('Unable to emit event %s for user %s and course %s', event_name, self.user.username, self.course_id)
......
......@@ -87,7 +87,7 @@ class TrackMiddleware(object):
Extract information from the request and add it to the tracking
context.
The following fields are injected in to the context:
The following fields are injected into the context:
* session - The Django session key that identifies the user's session.
* user_id - The numeric ID for the logged in user.
......@@ -96,6 +96,7 @@ class TrackMiddleware(object):
* host - The "SERVER_NAME" header, which should be the name of the server running this code.
* agent - The client browser identification string.
* path - The path part of the requested URL.
* client_id - The unique key used by Google Analytics to identify a user
"""
context = {
'session': self.get_session_key(request),
......@@ -105,6 +106,14 @@ class TrackMiddleware(object):
for header_name, context_key in META_KEY_TO_CONTEXT_KEY.iteritems():
context[context_key] = request.META.get(header_name, '')
# Google Analytics uses the clientId to keep track of unique visitors. A GA cookie looks like
# this: _ga=GA1.2.1033501218.1368477899. The clientId is this part: 1033501218.1368477899.
google_analytics_cookie = request.COOKIES.get('_ga')
if google_analytics_cookie is None:
context['client_id'] = None
else:
context['client_id'] = '.'.join(google_analytics_cookie.split('.')[2:])
context.update(contexts.course_context_from_url(request.build_absolute_uri()))
tracker.get_tracker().enter_context(
......
......@@ -45,6 +45,10 @@ class LegacyFieldMappingProcessor(object):
def remove_shim_context(event):
if 'context' in event:
context = event['context']
for field in CONTEXT_FIELDS_TO_INCLUDE:
# These fields are present elsewhere in the event at this point
context_fields_to_remove = set(CONTEXT_FIELDS_TO_INCLUDE)
# This field is only used for Segment.io web analytics and does not concern researchers
context_fields_to_remove.add('client_id')
for field in context_fields_to_remove:
if field in context:
del context[field]
......@@ -64,6 +64,7 @@ class TrackMiddlewareTestCase(TestCase):
'path': '/courses/',
'org_id': '',
'course_id': '',
'client_id': None,
})
def get_context_for_path(self, path):
......
......@@ -50,6 +50,7 @@ class LegacyFieldMappingProcessorTestCase(TestCase):
'course_id': sentinel.course_id,
'org_id': sentinel.org_id,
'event_type': sentinel.event_type,
'client_id': sentinel.client_id,
}
with django_tracker.context('test', context):
django_tracker.emit(sentinel.name, data)
......
......@@ -145,6 +145,39 @@ class TestTrackViews(TestCase):
self.mock_tracker.send.assert_called_once_with(expected_event)
@freeze_time(expected_time)
def test_server_track_with_middleware_and_google_analytics_cookie(self):
middleware = TrackMiddleware()
request = self.request_factory.get(self.path_with_course)
request.COOKIES['_ga'] = 'GA1.2.1033501218.1368477899'
middleware.process_request(request)
# The middleware emits an event, reset the mock to ignore it since we aren't testing that feature.
self.mock_tracker.reset_mock()
try:
views.server_track(request, str(sentinel.event_type), '{}')
expected_event = {
'username': 'anonymous',
'ip': '127.0.0.1',
'event_source': 'server',
'event_type': str(sentinel.event_type),
'event': '{}',
'agent': '',
'page': None,
'time': expected_time,
'host': 'testserver',
'context': {
'user_id': '',
'course_id': u'foo/bar/baz',
'org_id': 'foo',
'path': u'/courses/foo/bar/baz/xmod/'
},
}
finally:
middleware.process_response(request, None)
self.mock_tracker.send.assert_called_once_with(expected_event)
@freeze_time(expected_time)
def test_server_track_with_no_request(self):
request = None
views.server_track(request, str(sentinel.event_type), '{}')
......
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