Commit e968f063 by Tyler Hallada Committed by GitHub

Merge pull request #542 from edx/thallada/new-bi-tracking-events

Add 3 new business intelligence tracking events
parents e0201bc6 1302d22f
......@@ -18,7 +18,9 @@
{% captureas chart_tip_text %}{% block chart_tip_text %}{% endblock %}{% endcaptureas %}
{% include "chart_tooltip.html" with tip_text=chart_tip_text track_category="bar" %}
{% endif %}
<div id="chart-view" class="analytics-chart {% if not js_data.course.hasData%}message-only-chart{% endif %}">
<div id="chart-view" class="analytics-chart {% if not js_data.course.hasData%}message-only-chart{% endif %}"
data-track-type="hover" data-track-event="edx.bi.graph.interacted"
data-track-category="{% block hover_category %}{% endblock %}">
{% if js_data.course.hasData %}
{% include "loading.html" %}
{% else %}
......
......@@ -62,7 +62,9 @@
{% include "chart_tooltip.html" with tip_text=tip_text track_category="bar" %}
<div id="performance-chart-view" class="analytics-chart
{% if not js_data.course.answerDistribution or js_data.course.isRandom %}message-only-chart{% endif %}">
{% if not js_data.course.answerDistribution or js_data.course.isRandom %}message-only-chart{% endif %}"
data-track-type="hover" data-track-event="edx.bi.graph.interacted"
data-track-category="performance_answer_distribution">
{% if js_data.course.isRandom %}
<!-- sets the message on a new row; otherwise, it won't be centered properly. -->
<div class="clearfix"></div>
......@@ -104,7 +106,7 @@
class="btn btn-default"
data-role="performance-csv" data-track-type="click"
data-track-event="edx.bi.csv.downloaded"
data-track-category="answer-distribution">
data-track-category="performance_answer_distribution">
<span class="ico fa fa-download" aria-hidden="true"></span> {% trans "Download CSV" %}
<span class="sr-only">{% trans "Student Submission Counts" %}</span>
</a>
......
......@@ -31,7 +31,8 @@ Individual course-centric engagement content view.
<div class="chart-info">{% trans "Students" %}</div>
{% trans "The number of active students, and the number of students who engaged in specific activities, over time." as tip_text %}
{% include "chart_tooltip.html" with tip_text=tip_text track_category="trend"%}
<div id="engagement-trend-view" class="analytics-chart">
<div id="engagement-trend-view" class="analytics-chart" data-track-type="hover"
data-track-event="edx.bi.graph.interacted" data-track-category="engagement_content">
{% include "loading.html" %}
</div>
</div>
......
......@@ -15,6 +15,8 @@
{% trans "Video Views" %}
{% endblock table_title %}
{% block hover_category %}engagement_video_content{% endblock hover_category %}
{% block chart_info %}
{% include "courses/views_chart_info.html" %}
{% endblock chart_info %}
......@@ -9,6 +9,8 @@
<script src="{% static_rjs 'js/engagement-video-timeline-main.js' %}"></script>
{% endblock javascript %}
{% block hover_category %}engagement_video_timeline{% endblock hover_category %}
{% block table_title %}
{% trans "Total Video Views" %}
{% endblock table_title %}
......@@ -79,7 +81,7 @@
<div class="section-actions">
<a href="{% url 'courses:csv:engagement_video_timeline' course_id=course_id pipeline_video_id=summary_metrics.pipeline_video_id %}"
class="btn btn-default pull-right" data-role="engagement-csv" data-track-type="click"
data-track-event="edx.bi.csv.downloaded" data-track-category="video-timeline">
data-track-event="edx.bi.csv.downloaded" data-track-category="video_timeline">
<span class="ico fa fa-download" aria-hidden="true"></span> {% trans "Download CSV" %}
<span class="sr-only">{% trans "Video Viewing Over Time" %}</span>
</a>
......
......@@ -32,7 +32,8 @@ Individual course-centric enrollment activity view.
<div class="chart-info">{% trans "Enrollments" %}</div>
{% trans "This graph displays the current enrollment for the course as of the end of each day. This includes new enrollments as well as unenrollments." as tip_text %}
{% include "chart_tooltip.html" with tip_text=tip_text track_category="trend" %}
<div id="enrollment-trend-view" class="analytics-chart">
<div id="enrollment-trend-view" class="analytics-chart" data-track-type="hover"
data-track-event="edx.bi.graph.interacted" data-track-category="enrollment_activity">
{% include "loading.html" %}
</div>
</div>
......
......@@ -38,7 +38,8 @@
{% blocktrans with value=chart_tooltip_value %}This age histogram presents data computed for the {{ value }}% of enrolled students who provided a year of birth.{% endblocktrans %}
{% endcaptureas %}
{% include "chart_tooltip.html" with tip_text=tip_text track_category="histogram"%}
<div id="enrollment-chart-view" class="analytics-chart">
<div id="enrollment-chart-view" class="analytics-chart" data-track-type="hover"
data-track-event="edx.bi.graph.interacted" data-track-category="enrollment_demographics_age">
{% include "loading.html" %}
</div>
</div>
......
......@@ -38,7 +38,9 @@
{% blocktrans with value=chart_tooltip_value %}This graph presents data for the {{ value }}% of enrolled students who provided a highest level of education completed.{% endblocktrans %}
{% endcaptureas %}
{% include "chart_tooltip.html" with tip_text=tip_text track_category="bar"%}
<div id="enrollment-chart-view" class="analytics-chart">
<div id="enrollment-chart-view" class="analytics-chart" data-track-type="hover"
data-track-event="edx.bi.graph.interacted"
data-track-category="enrollment_demographics_education">
{% include "loading.html" %}
</div>
</div>
......
......@@ -38,7 +38,9 @@
{% blocktrans with value=chart_tooltip_value %}This graph presents data for the {{ value }}% of enrolled students who specified their gender.{% endblocktrans %}
{% endcaptureas %}
{% include "chart_tooltip.html" with tip_text=tip_text track_category="bar"%}
<div id="enrollment-chart-view" class="analytics-chart">
<div id="enrollment-chart-view" class="analytics-chart" data-track-type="hover"
data-track-event="edx.bi.graph.interacted"
data-track-category="enrollment_demographics_gender">
{% include "loading.html" %}
</div>
</div>
......
......@@ -25,7 +25,8 @@ Individual course-centric enrollment geography view.
{% if js_data.course.enrollmentByCountry %}
<div class="section-content section-data-viz">
<div class="world-map" data-view="world-map" data-title="{% trans "Enrollment by Country" %}"
data-series-name="{% trans "Enrollment" %}">
data-series-name="{% trans "Enrollment" %}" data-track-type="hover"
data-track-event="edx.bi.graph.interacted" data-track-category="enrollment_geography">
{% comment %}
The map is loaded via ajax, so display a loading message. Everything inside of this div will be
cleared when the map data loads.
......
......@@ -43,7 +43,10 @@
{% for item in column.items %}
<div class="item">
<div class="title"><a href="{% url item.view course_id=course_id %}{{ item.fragment }}">{{ item.title }}</a></div>
<div class="title"><a href="{% url item.view course_id=course_id %}{{ item.fragment }}"
data-track-type="click" data-track-event="edx.bi.course.question_clicked"
data-track-category="{{ column.name|lower }}" data-track-question="{{ item.title }}"
data-track-url="{% url item.view course_id=course_id %}{{ item.fragment }}">{{ item.title }}</a></div>
<div class="breadcrumbs">
<span class="ico fa {{ column.icon }}" aria-hidden="true"></span>
<span class="ico fa fa-caret-right" aria-hidden="true"></span>
......
......@@ -10,6 +10,8 @@
{% endblock performance_javascript %}
{% endblock javascript %}
{% block hover_category %}performance_content{% endblock hover_category %}
{% block chart_info %}
{% include "courses/submissions_chart_info.html" %}
{% endblock chart_info %}
......@@ -128,7 +128,8 @@ define(function(require) {
detailView = new LearnerDetailView({
learnerModel: learnerModel,
engagementTimelineModel: engagementTimelineModel
engagementTimelineModel: engagementTimelineModel,
trackingModel: this.options.trackingModel
});
this.options.rootView.showChildView('main', detailView);
......
......@@ -29,8 +29,6 @@ define(function(require) {
.toContainText('Learner Details');
expect(controller.options.rootView.$('.learners-header-region').html())
.not.toContainText(date.toLocaleDateString('en-us', {year: 'numeric', month: 'long', day: 'numeric'}));
expect(controller.options.trackingModel.get('page')).toBe('learner_details');
expect(controller.options.trackingModel.trigger).toHaveBeenCalledWith('segment:page');
};
// convenience method for asserting that we are on the roster page
......@@ -39,8 +37,6 @@ define(function(require) {
.not.toContainText('Return to Learners');
expect(controller.options.rootView.$('.learner-roster')).toBeInDOM();
expect(controller.options.rootView.$('.learners-header-region').html()).toContainText('Learners');
expect(controller.options.trackingModel.get('page')).toBe('learner_roster');
expect(controller.options.trackingModel.trigger).toHaveBeenCalledWith('segment:page');
};
beforeEach(function() {
......@@ -80,7 +76,6 @@ define(function(require) {
courseId: courseId,
trackingModel: new TrackingModel()
});
spyOn(this.controller.options.trackingModel, 'trigger');
});
afterEach(function() {
......@@ -179,8 +174,6 @@ define(function(require) {
this.controller.showNotFoundPage();
// eslint-disable-next-line max-len
expect(this.rootView.$el.html()).toContainText("Sorry, we couldn't find the page you're looking for.");
expect(this.controller.options.trackingModel.get('page')).toBe('learner_not_found');
expect(this.controller.options.trackingModel.trigger).toHaveBeenCalledWith('segment:page');
});
});
});
<div class="analytics-chart-container">
<div class="learner-engagement-timeline analytics-chart"></div>
<div class="learner-engagement-timeline analytics-chart" data-track-type="hover"
data-track-event="edx.bi.graph.interacted" data-track-category="engagement_timeline"></div>
</div>
......@@ -85,7 +85,8 @@ define(function(require) {
}
this.showChildView('names', new LearnerNameView({
model: learnerModel
model: learnerModel,
trackingModel: this.options.trackingModel
}));
this.showChildView('enrollment', new LearnerSummaryFieldView({
......
......@@ -6,9 +6,15 @@ define(function(require) {
return Marionette.ItemView.extend({
template: _.template(require('text!learners/detail/templates/learner-names.underscore')),
events: {
'click .learner-email a': 'onEmailClick'
},
modelEvents: {
change: 'render',
'change:email': 'render'
},
onEmailClick: function() {
this.options.trackingModel.trigger('segment:track', 'edx.bi.learner.email_link_clicked', {});
}
});
});
......@@ -4,6 +4,7 @@ define(function(require) {
var EngagementTimelineModel = require('learners/common/models/engagement-timeline'),
LearnerDetailView = require('learners/detail/views/learner-detail'),
LearnerModel = require('learners/common/models/learner'),
TrackingModel = require('models/tracking-model'),
Utils = require('utils/utils'),
_ = require('underscore');
......@@ -167,6 +168,30 @@ define(function(require) {
expect(detailView.triggerMethod)
.toHaveBeenCalledWith('learnerUnavailable', jasmine.any(Object));
});
it('triggers a tracking event on email link click', function() {
var trackingModel = new TrackingModel(),
detailView = new LearnerDetailView({
learnerModel: learnerModel,
engagementTimelineModel: new EngagementTimelineModel(),
el: fixtureClass,
trackingModel: trackingModel
}),
triggerSpy = spyOn(trackingModel, 'trigger');
trackingModel.set({
segmentApplicationId: 'foobar',
page: 'learner_details'
});
learnerModel.set({
email: 'spider@plant.com'
});
detailView.render().onBeforeShow();
detailView.$('.learner-email a').click();
expect(triggerSpy).toHaveBeenCalledWith('segment:track', 'edx.bi.learner.email_link_clicked', {});
});
});
});
});
......@@ -5,12 +5,28 @@
* be defined by the element's data-track-event and all other data-track-*
* attribute values will be returns as properties to be tracked.
*/
define(['jquery', 'underscore', 'views/clickable-view', 'views/tracking-view', 'utils/utils'],
function($, _, ClickableView, TrackingView, Utils) {
define(['jquery', 'underscore', 'views/clickable-view', 'views/hoverable-view', 'views/tracking-view', 'utils/utils'],
function($, _, ClickableView, HoverableView, TrackingView, Utils) {
'use strict';
var instrumentEvents = function(eventType, trackingViewClass, models) {
_($('[data-track-type="' + eventType + '"]')).each(function(track) {
// get the properties that we want to send back for with
// the tracking events
var trackingView,
properties = Utils.getNodeProperties(track.attributes,
'data-track-', ['data-track-event', 'data-track-triggered']);
trackingView = new trackingViewClass({
model: models.trackingModel,
trackEventType: $(track).attr('data-track-event'),
trackProperties: properties,
el: track
});
trackingView.renderIfHasEventType();
});
};
return function(models) {
var trackingView,
clickableView;
var trackingView;
if (models.trackingModel.isTracking()) {
// this is only activated when tracking ID is set
......@@ -23,20 +39,10 @@ define(['jquery', 'underscore', 'views/clickable-view', 'views/tracking-view', '
trackingView.applicationIdSet();
// instrument the click events
_($('[data-track-type="click"]')).each(function(track) {
// get the properties that we want to send back for with
// the tracking events
var properties = Utils.getNodeProperties(track.attributes,
'data-track-', ['data-track-event']);
instrumentEvents('click', ClickableView, models);
clickableView = new ClickableView({
model: models.trackingModel,
trackEventType: $(track).attr('data-track-event'),
trackProperties: properties,
el: track
});
clickableView.renderIfHasEventType();
});
// instrument the hover events
instrumentEvents('hover', HoverableView, models);
}
};
});
define(['views/hoverable-view'], function(HoverableView) {
'use strict';
describe('Hoverable views', function() {
it('should fire event when hovered', function() {
var model = {trigger: jasmine.createSpy('trigger')},
view = new HoverableView({
model: model,
el: document.createElement('a'),
trackEventType: 'my:event'
});
view.render();
// the click event should have in turn fired a segment:track event
view.$el.trigger('mouseenter');
expect(model.trigger)
.toHaveBeenCalledWith('segment:track', 'my:event', undefined);
view = new HoverableView({
model: model,
el: document.createElement('a'),
trackEventType: 'my:event',
trackProperties: {'my-prop': 'a property'}
});
view.render();
view.$el.trigger('mouseenter');
expect(model.trigger)
.toHaveBeenCalledWith('segment:track', 'my:event', {'my-prop': 'a property'});
// should only fire one event per page load
model.trigger.calls.reset();
view.$el.trigger('mouseenter');
expect(model.trigger.calls.any()).toEqual(false);
});
});
});
......@@ -28,6 +28,8 @@ define(['underscore', 'backbone'],
},
renderIfHasEventType: function() {
var self = this;
if (_(self.options).has('trackEventType')) {
self.render();
}
......
define(['underscore', 'backbone'],
function(_, Backbone) {
'use strict';
/**
* Use this for triggering track events when an element is hovered over.
* 'segment:track' and an event are fired when the element is hovered over.
*/
var HoverableView = Backbone.View.extend({
initialize: function(options) {
var self = this;
self.options = options;
},
render: function() {
var self = this;
// track the hover
self.$el.one('mouseenter', function() {
// track this event type along with properties
self.model.trigger('segment:track',
self.options.trackEventType,
self.options.trackProperties);
});
return this;
},
renderIfHasEventType: function() {
var self = this;
if (_(self.options).has('trackEventType')) {
self.render();
}
}
});
return HoverableView;
}
);
......@@ -56,14 +56,18 @@ define(['backbone', 'jquery', 'underscore', 'utils/utils'],
var self = this,
trackedElement = ev.target,
properties = Utils.getNodeProperties(
trackedElement.attributes, 'data-track-', ['data-track-event']),
eventType = $(trackedElement).attr('data-track-event');
trackedElement.attributes, 'data-track-', ['data-track-event', 'data-track-triggered']),
eventType = $(trackedElement).attr('data-track-event'),
trackType = $(trackedElement).attr('data-track-type'),
triggered = $(trackedElement).attr('data-track-triggered');
if (!self.model.isTracking() || _.isEmpty(eventType) || !_.isString(eventType)) {
if ((!self.model.isTracking() || _.isEmpty(eventType) || !_.isString(eventType)) ||
(trackType === 'tooltip' && triggered)) {
return;
}
self.track(eventType, properties);
$(trackedElement).attr('data-track-triggered', 'true');
},
/**
......
......@@ -159,7 +159,7 @@ define(['jquery', 'd3', 'datamaps', 'underscore', 'utils/utils', 'views/attribut
*/
tooltipTemplate: _.template('<span class="sr-only"><%=text%></span>' +
'<span class="ico ico-tooltip fa fa-info-circle chart-tooltip" ' +
'data-toggle="tooltip" data-placement="top" ' +
'data-toggle="tooltip" data-placement="top" data-track-type="tooltip"' +
'data-track-event="edx.bi.tooltip.displayed" data-track-category="map" ' +
'title="<%=text%>" aria-hidden="true"></span>'
),
......
<span class="sr-only">{{tip_text}}</span>
<span class="ico ico-tooltip fa fa-info-circle chart-tooltip has-tooltip" data-toggle="tooltip"
data-placement="top" data-track-event="edx.bi.tooltip.displayed"
data-placement="top" data-track-event="edx.bi.tooltip.displayed" data-track-type="tooltip"
data-track-category="{{track_category}}" title="{{tip_text}}" aria-hidden="true"></span>
......@@ -19,6 +19,7 @@
data-track-event="edx.bi.tooltip.displayed"
data-track-title="{{ label }}"
data-track-category="summary"
data-track-type="tooltip"
class="ico ico-tooltip fa fa-info-circle has-tooltip" aria-hidden="true"></span>
</div>
{% endif %}
......
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