Commit aba17a54 by chrisndodge

Merge pull request #1802 from MITx/feature/markchang/studio-analytics

Instrument studio for analytics
parents 262050e4 32bc4620
...@@ -46,6 +46,9 @@ SESSION_COOKIE_DOMAIN = ENV_TOKENS.get('SESSION_COOKIE_DOMAIN') ...@@ -46,6 +46,9 @@ SESSION_COOKIE_DOMAIN = ENV_TOKENS.get('SESSION_COOKIE_DOMAIN')
for feature, value in ENV_TOKENS.get('MITX_FEATURES', {}).items(): for feature, value in ENV_TOKENS.get('MITX_FEATURES', {}).items():
MITX_FEATURES[feature] = value MITX_FEATURES[feature] = value
# load segment.io key, provide a dummy if it does not exist
SEGMENT_IO_KEY = ENV_TOKENS.get('SEGMENT_IO_KEY', '***REMOVED***')
LOGGING = get_logger_config(LOG_DIR, LOGGING = get_logger_config(LOG_DIR,
logging_env=ENV_TOKENS['LOGGING_ENV'], logging_env=ENV_TOKENS['LOGGING_ENV'],
syslog_addr=(ENV_TOKENS['SYSLOG_SERVER'], 514), syslog_addr=(ENV_TOKENS['SYSLOG_SERVER'], 514),
......
...@@ -36,6 +36,7 @@ MITX_FEATURES = { ...@@ -36,6 +36,7 @@ MITX_FEATURES = {
'STUB_VIDEO_FOR_TESTING': False, # do not display video when running automated acceptance tests 'STUB_VIDEO_FOR_TESTING': False, # do not display video when running automated acceptance tests
'STAFF_EMAIL': '', # email address for staff (eg to request course creation) 'STAFF_EMAIL': '', # email address for staff (eg to request course creation)
'STUDIO_NPS_SURVEY': True, 'STUDIO_NPS_SURVEY': True,
'SEGMENT_IO': True,
} }
ENABLE_JASMINE = False ENABLE_JASMINE = False
......
...@@ -150,3 +150,6 @@ DEBUG_TOOLBAR_MONGO_STACKTRACES = True ...@@ -150,3 +150,6 @@ DEBUG_TOOLBAR_MONGO_STACKTRACES = True
# disable NPS survey in dev mode # disable NPS survey in dev mode
MITX_FEATURES['STUDIO_NPS_SURVEY'] = False MITX_FEATURES['STUDIO_NPS_SURVEY'] = False
# segment-io key for dev
SEGMENT_IO_KEY = 'mty8edrrsg'
...@@ -118,3 +118,6 @@ PASSWORD_HASHERS = ( ...@@ -118,3 +118,6 @@ PASSWORD_HASHERS = (
'django.contrib.auth.hashers.SHA1PasswordHasher', 'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher',
) )
# dummy segment-io key
SEGMENT_IO_KEY = '***REMOVED***'
...@@ -15,7 +15,7 @@ class CMS.Views.ModuleEdit extends Backbone.View ...@@ -15,7 +15,7 @@ class CMS.Views.ModuleEdit extends Backbone.View
$component_editor: => @$el.find('.component-editor') $component_editor: => @$el.find('.component-editor')
loadDisplay: -> loadDisplay: ->
XModule.loadModule(@$el.find('.xmodule_display')) XModule.loadModule(@$el.find('.xmodule_display'))
loadEdit: -> loadEdit: ->
if not @module if not @module
...@@ -55,6 +55,11 @@ class CMS.Views.ModuleEdit extends Backbone.View ...@@ -55,6 +55,11 @@ class CMS.Views.ModuleEdit extends Backbone.View
clickSaveButton: (event) => clickSaveButton: (event) =>
event.preventDefault() event.preventDefault()
data = @module.save() data = @module.save()
analytics.track "Saved Module",
course: course_location_analytics
id: _this.model.id
data.metadata = _.extend(data.metadata || {}, @metadata()) data.metadata = _.extend(data.metadata || {}, @metadata())
@hideModal() @hideModal()
@model.save(data).done( => @model.save(data).done( =>
......
...@@ -28,6 +28,10 @@ class CMS.Views.TabsEdit extends Backbone.View ...@@ -28,6 +28,10 @@ class CMS.Views.TabsEdit extends Backbone.View
@$('.component').each((idx, element) => @$('.component').each((idx, element) =>
tabs.push($(element).data('id')) tabs.push($(element).data('id'))
) )
analytics.track "Reordered Static Pages",
course: course_location_analytics
$.ajax({ $.ajax({
type:'POST', type:'POST',
url: '/reorder_static_tabs', url: '/reorder_static_tabs',
...@@ -56,10 +60,18 @@ class CMS.Views.TabsEdit extends Backbone.View ...@@ -56,10 +60,18 @@ class CMS.Views.TabsEdit extends Backbone.View
'i4x://edx/templates/static_tab/Empty' 'i4x://edx/templates/static_tab/Empty'
) )
analytics.track "Added Static Page",
course: course_location_analytics
deleteTab: (event) => deleteTab: (event) =>
if not confirm 'Are you sure you want to delete this component? This action cannot be undone.' if not confirm 'Are you sure you want to delete this component? This action cannot be undone.'
return return
$component = $(event.currentTarget).parents('.component') $component = $(event.currentTarget).parents('.component')
analytics.track "Deleted Static Page",
course: course_location_analytics
id: $component.data('id')
$.post('/delete_item', { $.post('/delete_item', {
id: $component.data('id') id: $component.data('id')
}, => }, =>
......
...@@ -35,6 +35,10 @@ class CMS.Views.UnitEdit extends Backbone.View ...@@ -35,6 +35,10 @@ class CMS.Views.UnitEdit extends Backbone.View
@$('.components').sortable( @$('.components').sortable(
handle: '.drag-handle' handle: '.drag-handle'
update: (event, ui) => update: (event, ui) =>
analytics.track "Reordered Components",
course: course_location_analytics
id: unit_location_analytics
payload = children : @components() payload = children : @components()
options = success : => @model.unset('children') options = success : => @model.unset('children')
@model.save(payload, options) @model.save(payload, options)
...@@ -89,6 +93,11 @@ class CMS.Views.UnitEdit extends Backbone.View ...@@ -89,6 +93,11 @@ class CMS.Views.UnitEdit extends Backbone.View
$(event.currentTarget).data('location') $(event.currentTarget).data('location')
) )
analytics.track "Added a Component",
course: course_location_analytics
unit_id: unit_location_analytics
type: $(event.currentTarget).data('location')
@closeNewComponent(event) @closeNewComponent(event)
components: => @$('.component').map((idx, el) -> $(el).data('id')).get() components: => @$('.component').map((idx, el) -> $(el).data('id')).get()
...@@ -111,6 +120,11 @@ class CMS.Views.UnitEdit extends Backbone.View ...@@ -111,6 +120,11 @@ class CMS.Views.UnitEdit extends Backbone.View
$.post('/delete_item', { $.post('/delete_item', {
id: $component.data('id') id: $component.data('id')
}, => }, =>
analytics.track "Deleted a Component",
course: course_location_analytics
unit_id: unit_location_analytics
id: $component.data('id')
$component.remove() $component.remove()
# b/c we don't vigilantly keep children up to date # b/c we don't vigilantly keep children up to date
# get rid of it before it hurts someone # get rid of it before it hurts someone
...@@ -129,6 +143,10 @@ class CMS.Views.UnitEdit extends Backbone.View ...@@ -129,6 +143,10 @@ class CMS.Views.UnitEdit extends Backbone.View
id: @$el.data('id') id: @$el.data('id')
delete_children: true delete_children: true
}, => }, =>
analytics.track "Deleted Draft",
course: course_location_analytics
unit_id: unit_location_analytics
window.location.reload() window.location.reload()
) )
...@@ -138,6 +156,10 @@ class CMS.Views.UnitEdit extends Backbone.View ...@@ -138,6 +156,10 @@ class CMS.Views.UnitEdit extends Backbone.View
$.post('/create_draft', { $.post('/create_draft', {
id: @$el.data('id') id: @$el.data('id')
}, => }, =>
analytics.track "Created Draft",
course: course_location_analytics
unit_id: unit_location_analytics
@model.set('state', 'draft') @model.set('state', 'draft')
) )
...@@ -148,20 +170,31 @@ class CMS.Views.UnitEdit extends Backbone.View ...@@ -148,20 +170,31 @@ class CMS.Views.UnitEdit extends Backbone.View
$.post('/publish_draft', { $.post('/publish_draft', {
id: @$el.data('id') id: @$el.data('id')
}, => }, =>
analytics.track "Published Draft",
course: course_location_analytics
unit_id: unit_location_analytics
@model.set('state', 'public') @model.set('state', 'public')
) )
setVisibility: (event) -> setVisibility: (event) ->
if @$('.visibility-select').val() == 'private' if @$('.visibility-select').val() == 'private'
target_url = '/unpublish_unit' target_url = '/unpublish_unit'
visibility = "private"
else else
target_url = '/publish_draft' target_url = '/publish_draft'
visibility = "public"
@wait(true) @wait(true)
$.post(target_url, { $.post(target_url, {
id: @$el.data('id') id: @$el.data('id')
}, => }, =>
analytics.track "Set Unit Visibility",
course: course_location_analytics
unit_id: unit_location_analytics
visibility: visibility
@model.set('state', @$('.visibility-select').val()) @model.set('state', @$('.visibility-select').val())
) )
...@@ -193,6 +226,11 @@ class CMS.Views.UnitEdit.NameEdit extends Backbone.View ...@@ -193,6 +226,11 @@ class CMS.Views.UnitEdit.NameEdit extends Backbone.View
@model.save(metadata: metadata) @model.save(metadata: metadata)
# Update name shown in the right-hand side location summary. # Update name shown in the right-hand side location summary.
$('.unit-location .editing .unit-name').html(metadata.display_name) $('.unit-location .editing .unit-name').html(metadata.display_name)
analytics.track "Edited Unit Name",
course: course_location_analytics
unit_id: unit_location_analytics
display_name: metadata.display_name
class CMS.Views.UnitEdit.LocationState extends Backbone.View class CMS.Views.UnitEdit.LocationState extends Backbone.View
initialize: => initialize: =>
......
...@@ -331,6 +331,12 @@ function createNewUnit(e) { ...@@ -331,6 +331,12 @@ function createNewUnit(e) {
var parent = $(this).data('parent'); var parent = $(this).data('parent');
var template = $(this).data('template'); var template = $(this).data('template');
analytics.track('Created a Unit', {
'course': course_location_analytics,
'parent_location': parent
});
$.post('/clone_item', $.post('/clone_item',
{'parent_location': parent, {'parent_location': parent,
'template': template, 'template': template,
...@@ -363,6 +369,12 @@ function _deleteItem($el) { ...@@ -363,6 +369,12 @@ function _deleteItem($el) {
var id = $el.data('id'); var id = $el.data('id');
analytics.track('Deleted an Item', {
'course': course_location_analytics,
'id': id
});
$.post('/delete_item', $.post('/delete_item',
{'id': id, 'delete_children': true, 'delete_all_versions': true}, {'id': id, 'delete_children': true, 'delete_all_versions': true},
function (data) { function (data) {
...@@ -426,6 +438,11 @@ function displayFinishedUpload(xhr) { ...@@ -426,6 +438,11 @@ function displayFinishedUpload(xhr) {
var html = Mustache.to_html(template, resp); var html = Mustache.to_html(template, resp);
$('table > tbody').prepend(html); $('table > tbody').prepend(html);
analytics.track('Uploaded a File', {
'course': course_location_analytics,
'asset_url': resp.url
});
} }
function markAsLoaded() { function markAsLoaded() {
...@@ -555,6 +572,11 @@ function saveNewSection(e) { ...@@ -555,6 +572,11 @@ function saveNewSection(e) {
var template = $saveButton.data('template'); var template = $saveButton.data('template');
var display_name = $(this).find('.new-section-name').val(); var display_name = $(this).find('.new-section-name').val();
analytics.track('Created a Section', {
'course': course_location_analytics,
'display_name': display_name
});
$.post('/clone_item', { $.post('/clone_item', {
'parent_location': parent, 'parent_location': parent,
'template': template, 'template': template,
...@@ -600,6 +622,12 @@ function saveNewCourse(e) { ...@@ -600,6 +622,12 @@ function saveNewCourse(e) {
return; return;
} }
analytics.track('Created a Course', {
'org': org,
'number': number,
'display_name': display_name
});
$.post('/create_new_course', { $.post('/create_new_course', {
'template': template, 'template': template,
'org': org, 'org': org,
...@@ -646,9 +674,14 @@ function saveNewSubsection(e) { ...@@ -646,9 +674,14 @@ function saveNewSubsection(e) {
var parent = $(this).find('.new-subsection-name-save').data('parent'); var parent = $(this).find('.new-subsection-name-save').data('parent');
var template = $(this).find('.new-subsection-name-save').data('template'); var template = $(this).find('.new-subsection-name-save').data('template');
var display_name = $(this).find('.new-subsection-name-input').val(); var display_name = $(this).find('.new-subsection-name-input').val();
analytics.track('Created a Subsection', {
'course': course_location_analytics,
'display_name': display_name
});
$.post('/clone_item', { $.post('/clone_item', {
'parent_location': parent, 'parent_location': parent,
'template': template, 'template': template,
...@@ -702,6 +735,13 @@ function saveEditSectionName(e) { ...@@ -702,6 +735,13 @@ function saveEditSectionName(e) {
return; return;
} }
analytics.track('Edited Section Name', {
'course': course_location_analytics,
'display_name': display_name,
'id': id
});
var $_this = $(this); var $_this = $(this);
// call into server to commit the new order // call into server to commit the new order
$.ajax({ $.ajax({
...@@ -741,6 +781,12 @@ function saveSetSectionScheduleDate(e) { ...@@ -741,6 +781,12 @@ function saveSetSectionScheduleDate(e) {
var id = $modal.attr('data-id'); var id = $modal.attr('data-id');
analytics.track('Edited Section Release Date', {
'course': course_location_analytics,
'id': id,
'start': start
});
// call into server to commit the new order // call into server to commit the new order
$.ajax({ $.ajax({
url: "/save_item", url: "/save_item",
......
...@@ -77,11 +77,18 @@ CMS.Views.Checklists = Backbone.View.extend({ ...@@ -77,11 +77,18 @@ CMS.Views.Checklists = Backbone.View.extend({
var task_index = $checkbox.data('task'); var task_index = $checkbox.data('task');
var model = this.collection.at(checklist_index); var model = this.collection.at(checklist_index);
model.attributes.items[task_index].is_checked = $task.hasClass(completed); model.attributes.items[task_index].is_checked = $task.hasClass(completed);
model.save({}, model.save({},
{ {
success : function() { success : function() {
var updatedTemplate = self.renderTemplate(model, checklist_index); var updatedTemplate = self.renderTemplate(model, checklist_index);
self.$el.find('#course-checklist'+checklist_index).first().replaceWith(updatedTemplate); self.$el.find('#course-checklist'+checklist_index).first().replaceWith(updatedTemplate);
analytics.track('Toggled a Checklist Task', {
'course': course_location_analytics,
'task': model.attributes.items[task_index].short_description,
'state': model.attributes.items[task_index].is_checked
});
}, },
error : CMS.ServerError error : CMS.ServerError
}); });
......
...@@ -107,6 +107,11 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({ ...@@ -107,6 +107,11 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
// push change to display, hide the editor, submit the change // push change to display, hide the editor, submit the change
targetModel.save({}, {error : CMS.ServerError}); targetModel.save({}, {error : CMS.ServerError});
this.closeEditor(this); this.closeEditor(this);
analytics.track('Saved Course Update', {
'course': course_location_analytics,
'date': this.dateEntry(event).val()
});
}, },
onCancel: function(event) { onCancel: function(event) {
...@@ -147,6 +152,11 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({ ...@@ -147,6 +152,11 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
return; return;
} }
analytics.track('Deleted Course Update', {
'course': course_location_analytics,
'date': this.dateEntry(event).val()
});
var targetModel = this.eventModel(event); var targetModel = this.eventModel(event);
this.modelDom(event).remove(); this.modelDom(event).remove();
var cacheThis = this; var cacheThis = this;
...@@ -284,6 +294,11 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({ ...@@ -284,6 +294,11 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({
this.model.save({}, {error: CMS.ServerError}); this.model.save({}, {error: CMS.ServerError});
this.$form.hide(); this.$form.hide();
this.closeEditor(this); this.closeEditor(this);
analytics.track('Saved Course Handouts', {
'course': course_location_analytics
});
}, },
onCancel: function(event) { onCancel: function(event) {
......
...@@ -137,6 +137,10 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -137,6 +137,10 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
success : function() { success : function() {
self.render(); self.render();
self.showMessage(self.successful_changes); self.showMessage(self.successful_changes);
analytics.track('Saved Advanced Settings', {
'course': course_location_analytics
});
}, },
error : CMS.ServerError error : CMS.ServerError
}); });
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
<link rel="stylesheet" type="text/css" href="${static.url('css/vendor/symbolset.ss-symbolicons-block.css')}" /> <link rel="stylesheet" type="text/css" href="${static.url('css/vendor/symbolset.ss-symbolicons-block.css')}" />
<link rel="stylesheet" type="text/css" href="${static.url('css/vendor/symbolset.ss-standard.css')}" /> <link rel="stylesheet" type="text/css" href="${static.url('css/vendor/symbolset.ss-standard.css')}" />
<%include file="widgets/segment-io.html" />
<%block name="header_extras"></%block> <%block name="header_extras"></%block>
</head> </head>
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
}); });
}); });
var unit_location_analytics = '${unit_location}';
</script> </script>
</%block> </%block>
......
% if settings.MITX_FEATURES.get('SEGMENT_IO'):
<!-- begin Segment.io -->
<script type="text/javascript">
// if inside course, inject the course location into the JS namespace
%if context_course:
var course_location_analytics = "${context_course.location}";
%endif
var analytics=analytics||[];analytics.load=function(e){var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src=("https:"===document.location.protocol?"https://":"http://")+"d2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/"+e+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n);var r=function(e){return function(){analytics.push([e].concat(Array.prototype.slice.call(arguments,0)))}},i=["identify","track","trackLink","trackForm","trackClick","trackSubmit","pageview","ab","alias","ready"];for(var s=0;s<i.length;s++)analytics[i[s]]=r(i[s])};
analytics.load("${ settings.SEGMENT_IO_KEY }");
% if user.is_authenticated():
analytics.identify("${ user.id }", {
email : "${ user.email }",
username : "${ user.username }"
});
% endif
</script>
<!-- end Segment.io -->
% else:
<!-- dummy segment.io -->
<script type="text/javascript">
%if context_course:
var course_location_analytics = "${context_course.location}";
%endif
var analytics = {
track: function() { return; }
};
</script>
<!-- end dummy segment.io -->
% 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