Commit 2abd023a by Will Daly

Merge pull request #5895 from edx/will/logistration-include-course-in-analytics

Logistration: Include course ID in analytics events for login/registration
parents bb257b6f 95a435ab
...@@ -343,6 +343,20 @@ def shim_student_view(view_func, check_logged_in=False): ...@@ -343,6 +343,20 @@ def shim_student_view(view_func, check_logged_in=False):
if "course_id" in request.POST: if "course_id" in request.POST:
del request.POST["course_id"] del request.POST["course_id"]
# Include the course ID if it's specified in the analytics info
# so it can be included in analytics events.
if "analytics" in request.POST:
try:
analytics = json.loads(request.POST["analytics"])
if "enroll_course_id" in analytics:
request.POST["course_id"] = analytics.get("enroll_course_id")
except (ValueError, TypeError):
LOGGER.error(
u"Could not parse analytics object sent to user API: {analytics}".format(
analytics=analytics
)
)
# Backwards compatibility: the student view expects both # Backwards compatibility: the student view expects both
# terms of service and honor code values. Since we're combining # terms of service and honor code values. Since we're combining
# these into a single checkbox, the only value we may get # these into a single checkbox, the only value we may get
......
...@@ -150,6 +150,17 @@ class StudentViewShimTest(TestCase): ...@@ -150,6 +150,17 @@ class StudentViewShimTest(TestCase):
self.assertNotIn("enrollment_action", self.captured_request.POST) self.assertNotIn("enrollment_action", self.captured_request.POST)
self.assertNotIn("course_id", self.captured_request.POST) self.assertNotIn("course_id", self.captured_request.POST)
def test_include_analytics_info(self):
view = self._shimmed_view(HttpResponse())
request = HttpRequest()
request.POST["analytics"] = json.dumps({
"enroll_course_id": "edX/DemoX/Fall"
})
view(request)
# Expect that the analytics course ID was passed to the view
self.assertEqual(self.captured_request.POST.get("course_id"), "edX/DemoX/Fall")
def test_third_party_auth_login_failure(self): def test_third_party_auth_login_failure(self):
view = self._shimmed_view( view = self._shimmed_view(
HttpResponse(status=403), HttpResponse(status=403),
......
...@@ -133,6 +133,13 @@ class LoginSessionView(APIView): ...@@ -133,6 +133,13 @@ class LoginSessionView(APIView):
def post(self, request): def post(self, request):
"""Log in a user. """Log in a user.
You must send all required form fields with the request.
You can optionally send an `analytics` param with a JSON-encoded
object with additional info to include in the login analytics event.
Currently, the only supported field is "enroll_course_id" to indicate
that the user logged in while enrolling in a particular course.
Arguments: Arguments:
request (HttpRequest) request (HttpRequest)
...@@ -148,7 +155,7 @@ class LoginSessionView(APIView): ...@@ -148,7 +155,7 @@ class LoginSessionView(APIView):
Example Usage: Example Usage:
POST /user_api/v1/login_session POST /user_api/v1/login_session
with POST params `email` and `password` with POST params `email`, `password`, and `remember`.
200 OK 200 OK
...@@ -246,6 +253,13 @@ class RegistrationView(APIView): ...@@ -246,6 +253,13 @@ class RegistrationView(APIView):
def post(self, request): def post(self, request):
"""Create the user's account. """Create the user's account.
You must send all required form fields with the request.
You can optionally send an `analytics` param with a JSON-encoded
object with additional info to include in the registration analytics event.
Currently, the only supported field is "enroll_course_id" to indicate
that the user registered while enrolling in a particular course.
Arguments: Arguments:
request (HTTPRequest) request (HTTPRequest)
......
...@@ -6,8 +6,8 @@ define([ ...@@ -6,8 +6,8 @@ define([
'js/student_account/models/LoginModel', 'js/student_account/models/LoginModel',
'js/student_account/views/LoginView' 'js/student_account/views/LoginView'
], function($, _, TemplateHelpers, AjaxHelpers, LoginModel, LoginView) { ], function($, _, TemplateHelpers, AjaxHelpers, LoginModel, LoginView) {
describe('edx.student.account.LoginView', function() {
'use strict'; 'use strict';
describe('edx.student.account.LoginView', function() {
var model = null, var model = null,
view = null, view = null,
...@@ -69,7 +69,8 @@ define([ ...@@ -69,7 +69,8 @@ define([
restrictions: {} restrictions: {}
} }
] ]
}; },
COURSE_ID = "edX/demoX/Fall";
var createLoginView = function(test) { var createLoginView = function(test) {
// Initialize the login model // Initialize the login model
...@@ -147,6 +148,35 @@ define([ ...@@ -147,6 +148,35 @@ define([
expect(authComplete).toBe(true); expect(authComplete).toBe(true);
}); });
it('sends analytics info containing the enrolled course ID', function() {
createLoginView( this );
// Simulate that the user is attempting to enroll in a course
// by setting the course_id query string param.
spyOn($, 'url').andCallFake(function( param ) {
if (param === "?course_id") {
return encodeURIComponent( COURSE_ID );
}
});
// Attempt to login
submitForm( true );
// Verify that the client sent the course ID for analytics
var expectedData = {};
$.extend(expectedData, USER_DATA, {
analytics: JSON.stringify({
enroll_course_id: COURSE_ID
})
});
AjaxHelpers.expectRequest(
requests, 'POST',
FORM_DESCRIPTION.submit_url,
$.param( expectedData )
);
});
it('displays third-party auth login buttons', function() { it('displays third-party auth login buttons', function() {
createLoginView(this); createLoginView(this);
...@@ -209,5 +239,4 @@ define([ ...@@ -209,5 +239,4 @@ define([
expect(authComplete).toBe(true); expect(authComplete).toBe(true);
}); });
}); });
} });
);
...@@ -6,14 +6,16 @@ define([ ...@@ -6,14 +6,16 @@ define([
'js/student_account/models/RegisterModel', 'js/student_account/models/RegisterModel',
'js/student_account/views/RegisterView' 'js/student_account/views/RegisterView'
], function($, _, TemplateHelpers, AjaxHelpers, RegisterModel, RegisterView) { ], function($, _, TemplateHelpers, AjaxHelpers, RegisterModel, RegisterView) {
describe('edx.student.account.RegisterView', function() {
'use strict'; 'use strict';
describe('edx.student.account.RegisterView', function() {
var model = null, var model = null,
view = null, view = null,
requests = null, requests = null,
authComplete = false, authComplete = false,
PLATFORM_NAME = 'edX', PLATFORM_NAME = 'edX',
COURSE_ID = "edX/DemoX/Fall",
USER_DATA = { USER_DATA = {
email: 'xsy@edx.org', email: 'xsy@edx.org',
name: 'Xsy M. Education', name: 'Xsy M. Education',
...@@ -226,7 +228,7 @@ define([ ...@@ -226,7 +228,7 @@ define([
createRegisterView(this); createRegisterView(this);
// Submit the form, with successful validation // Submit the form, with successful validation
submitForm(true); submitForm( true );
// Verify that the client contacts the server with the expected data // Verify that the client contacts the server with the expected data
AjaxHelpers.expectRequest( AjaxHelpers.expectRequest(
...@@ -242,6 +244,35 @@ define([ ...@@ -242,6 +244,35 @@ define([
expect(authComplete).toBe(true); expect(authComplete).toBe(true);
}); });
it('sends analytics info containing the enrolled course ID', function() {
createRegisterView( this );
// Simulate that the user is attempting to enroll in a course
// by setting the course_id query string param.
spyOn($, 'url').andCallFake(function( param ) {
if (param === "?course_id") {
return encodeURIComponent( COURSE_ID );
}
});
// Attempt to register
submitForm( true );
// Verify that the client sent the course ID for analytics
var expectedData = {};
$.extend(expectedData, USER_DATA, {
analytics: JSON.stringify({
enroll_course_id: COURSE_ID
})
});
AjaxHelpers.expectRequest(
requests, 'POST',
FORM_DESCRIPTION.submit_url,
$.param( expectedData )
);
});
it('displays third-party auth registration buttons', function() { it('displays third-party auth registration buttons', function() {
createRegisterView(this); createRegisterView(this);
...@@ -303,5 +334,4 @@ define([ ...@@ -303,5 +334,4 @@ define([
expect(authComplete).toBe(true); expect(authComplete).toBe(true);
}); });
}); });
} });
);
...@@ -24,14 +24,27 @@ var edx = edx || {}; ...@@ -24,14 +24,27 @@ var edx = edx || {};
}, },
sync: function(method, model) { sync: function(method, model) {
var headers = { var headers = { 'X-CSRFToken': $.cookie('csrftoken') },
'X-CSRFToken': $.cookie('csrftoken') data = {},
}; analytics,
courseId = $.url( '?course_id' );
// If there is a course ID in the query string param,
// send that to the server as well so it can be included
// in analytics events.
if ( courseId ) {
analytics = JSON.stringify({
enroll_course_id: decodeURIComponent( courseId )
});
}
// Include all form fields and analytics info in the data sent to the server
$.extend( data, model.attributes, { analytics: analytics });
$.ajax({ $.ajax({
url: model.urlRoot, url: model.urlRoot,
type: model.ajaxType, type: model.ajaxType,
data: model.attributes, data: data,
headers: headers, headers: headers,
success: function() { success: function() {
model.trigger('sync'); model.trigger('sync');
......
...@@ -30,14 +30,27 @@ var edx = edx || {}; ...@@ -30,14 +30,27 @@ var edx = edx || {};
}, },
sync: function(method, model) { sync: function(method, model) {
var headers = { var headers = { 'X-CSRFToken': $.cookie('csrftoken') },
'X-CSRFToken': $.cookie('csrftoken') data = {},
}; analytics,
courseId = $.url( '?course_id' );
// If there is a course ID in the query string param,
// send that to the server as well so it can be included
// in analytics events.
if ( courseId ) {
analytics = JSON.stringify({
enroll_course_id: decodeURIComponent( courseId )
});
}
// Include all form fields and analytics info in the data sent to the server
$.extend( data, model.attributes, { analytics: analytics });
$.ajax({ $.ajax({
url: model.urlRoot, url: model.urlRoot,
type: model.ajaxType, type: model.ajaxType,
data: model.attributes, data: data,
headers: headers, headers: headers,
success: function() { success: function() {
model.trigger('sync'); model.trigger('sync');
......
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