diff --git a/cms/envs/aws.py b/cms/envs/aws.py index be7816d21f..edf67badfe 100644 --- a/cms/envs/aws.py +++ b/cms/envs/aws.py @@ -46,6 +46,9 @@ SESSION_COOKIE_DOMAIN = ENV_TOKENS.get('SESSION_COOKIE_DOMAIN') for feature, value in ENV_TOKENS.get('MITX_FEATURES', {}).items(): 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_env=ENV_TOKENS['LOGGING_ENV'], syslog_addr=(ENV_TOKENS['SYSLOG_SERVER'], 514), diff --git a/cms/envs/common.py b/cms/envs/common.py index a56e56025f..37cfeea7a1 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -36,6 +36,7 @@ MITX_FEATURES = { '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) 'STUDIO_NPS_SURVEY': True, + 'SEGMENT_IO': True, } ENABLE_JASMINE = False diff --git a/cms/envs/dev.py b/cms/envs/dev.py index ae78b93f06..dbf9c5574c 100644 --- a/cms/envs/dev.py +++ b/cms/envs/dev.py @@ -150,3 +150,6 @@ DEBUG_TOOLBAR_MONGO_STACKTRACES = True # disable NPS survey in dev mode MITX_FEATURES['STUDIO_NPS_SURVEY'] = False + +# segment-io key for dev +SEGMENT_IO_KEY = 'mty8edrrsg' diff --git a/cms/envs/test.py b/cms/envs/test.py index 59664bfd40..820b2cbe23 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -118,3 +118,6 @@ PASSWORD_HASHERS = ( 'django.contrib.auth.hashers.SHA1PasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher', ) + +# dummy segment-io key +SEGMENT_IO_KEY = '***REMOVED***' diff --git a/cms/static/coffee/src/views/module_edit.coffee b/cms/static/coffee/src/views/module_edit.coffee index 9f7e3a5e60..3cb3b1703f 100644 --- a/cms/static/coffee/src/views/module_edit.coffee +++ b/cms/static/coffee/src/views/module_edit.coffee @@ -15,7 +15,7 @@ class CMS.Views.ModuleEdit extends Backbone.View $component_editor: => @$el.find('.component-editor') loadDisplay: -> - XModule.loadModule(@$el.find('.xmodule_display')) + XModule.loadModule(@$el.find('.xmodule_display')) loadEdit: -> if not @module @@ -55,6 +55,11 @@ class CMS.Views.ModuleEdit extends Backbone.View clickSaveButton: (event) => event.preventDefault() data = @module.save() + + analytics.track "Saved Module", + course: course_location_analytics + id: _this.model.id + data.metadata = _.extend(data.metadata || {}, @metadata()) @hideModal() @model.save(data).done( => diff --git a/cms/static/coffee/src/views/tabs.coffee b/cms/static/coffee/src/views/tabs.coffee index 9fbe4e5789..1034fc988e 100644 --- a/cms/static/coffee/src/views/tabs.coffee +++ b/cms/static/coffee/src/views/tabs.coffee @@ -28,6 +28,10 @@ class CMS.Views.TabsEdit extends Backbone.View @$('.component').each((idx, element) => tabs.push($(element).data('id')) ) + + analytics.track "Reordered Static Pages", + course: course_location_analytics + $.ajax({ type:'POST', url: '/reorder_static_tabs', @@ -56,10 +60,18 @@ class CMS.Views.TabsEdit extends Backbone.View 'i4x://edx/templates/static_tab/Empty' ) + analytics.track "Added Static Page", + course: course_location_analytics + deleteTab: (event) => if not confirm 'Are you sure you want to delete this component? This action cannot be undone.' return $component = $(event.currentTarget).parents('.component') + + analytics.track "Deleted Static Page", + course: course_location_analytics + id: $component.data('id') + $.post('/delete_item', { id: $component.data('id') }, => diff --git a/cms/static/coffee/src/views/unit.coffee b/cms/static/coffee/src/views/unit.coffee index 42127b2800..e23477ccfa 100644 --- a/cms/static/coffee/src/views/unit.coffee +++ b/cms/static/coffee/src/views/unit.coffee @@ -35,6 +35,10 @@ class CMS.Views.UnitEdit extends Backbone.View @$('.components').sortable( handle: '.drag-handle' update: (event, ui) => + analytics.track "Reordered Components", + course: course_location_analytics + id: unit_location_analytics + payload = children : @components() options = success : => @model.unset('children') @model.save(payload, options) @@ -89,6 +93,11 @@ class CMS.Views.UnitEdit extends Backbone.View $(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) components: => @$('.component').map((idx, el) -> $(el).data('id')).get() @@ -111,6 +120,11 @@ class CMS.Views.UnitEdit extends Backbone.View $.post('/delete_item', { id: $component.data('id') }, => + analytics.track "Deleted a Component", + course: course_location_analytics + unit_id: unit_location_analytics + id: $component.data('id') + $component.remove() # b/c we don't vigilantly keep children up to date # get rid of it before it hurts someone @@ -129,6 +143,10 @@ class CMS.Views.UnitEdit extends Backbone.View id: @$el.data('id') delete_children: true }, => + analytics.track "Deleted Draft", + course: course_location_analytics + unit_id: unit_location_analytics + window.location.reload() ) @@ -138,6 +156,10 @@ class CMS.Views.UnitEdit extends Backbone.View $.post('/create_draft', { id: @$el.data('id') }, => + analytics.track "Created Draft", + course: course_location_analytics + unit_id: unit_location_analytics + @model.set('state', 'draft') ) @@ -148,20 +170,31 @@ class CMS.Views.UnitEdit extends Backbone.View $.post('/publish_draft', { id: @$el.data('id') }, => + analytics.track "Published Draft", + course: course_location_analytics + unit_id: unit_location_analytics + @model.set('state', 'public') ) setVisibility: (event) -> if @$('.visibility-select').val() == 'private' target_url = '/unpublish_unit' + visibility = "private" else target_url = '/publish_draft' + visibility = "public" @wait(true) $.post(target_url, { 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()) ) @@ -193,6 +226,11 @@ class CMS.Views.UnitEdit.NameEdit extends Backbone.View @model.save(metadata: metadata) # Update name shown in the right-hand side location summary. $('.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 initialize: => diff --git a/cms/static/js/base.js b/cms/static/js/base.js index 6ea918cc36..7b930a176b 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -331,6 +331,12 @@ function createNewUnit(e) { var parent = $(this).data('parent'); var template = $(this).data('template'); + analytics.track('Created a Unit', { + 'course': course_location_analytics, + 'parent_location': parent + }); + + $.post('/clone_item', {'parent_location': parent, 'template': template, @@ -363,6 +369,12 @@ function _deleteItem($el) { var id = $el.data('id'); + analytics.track('Deleted an Item', { + 'course': course_location_analytics, + 'id': id + }); + + $.post('/delete_item', {'id': id, 'delete_children': true, 'delete_all_versions': true}, function (data) { @@ -426,6 +438,11 @@ function displayFinishedUpload(xhr) { var html = Mustache.to_html(template, resp); $('table > tbody').prepend(html); + analytics.track('Uploaded a File', { + 'course': course_location_analytics, + 'asset_url': resp.url + }); + } function markAsLoaded() { @@ -555,6 +572,11 @@ function saveNewSection(e) { var template = $saveButton.data('template'); 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', { 'parent_location': parent, 'template': template, @@ -600,6 +622,12 @@ function saveNewCourse(e) { return; } + analytics.track('Created a Course', { + 'org': org, + 'number': number, + 'display_name': display_name + }); + $.post('/create_new_course', { 'template': template, 'org': org, @@ -646,9 +674,14 @@ function saveNewSubsection(e) { var parent = $(this).find('.new-subsection-name-save').data('parent'); var template = $(this).find('.new-subsection-name-save').data('template'); - 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', { 'parent_location': parent, 'template': template, @@ -702,6 +735,13 @@ function saveEditSectionName(e) { return; } + analytics.track('Edited Section Name', { + 'course': course_location_analytics, + 'display_name': display_name, + 'id': id + }); + + var $_this = $(this); // call into server to commit the new order $.ajax({ @@ -741,6 +781,12 @@ function saveSetSectionScheduleDate(e) { 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 $.ajax({ url: "/save_item", diff --git a/cms/static/js/views/checklists_view.js b/cms/static/js/views/checklists_view.js index e553c7a2ca..85c0f5242b 100644 --- a/cms/static/js/views/checklists_view.js +++ b/cms/static/js/views/checklists_view.js @@ -77,11 +77,18 @@ CMS.Views.Checklists = Backbone.View.extend({ var task_index = $checkbox.data('task'); var model = this.collection.at(checklist_index); model.attributes.items[task_index].is_checked = $task.hasClass(completed); + model.save({}, { success : function() { var updatedTemplate = self.renderTemplate(model, checklist_index); 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 }); diff --git a/cms/static/js/views/course_info_edit.js b/cms/static/js/views/course_info_edit.js index ce959fd443..a6d42e1927 100644 --- a/cms/static/js/views/course_info_edit.js +++ b/cms/static/js/views/course_info_edit.js @@ -107,6 +107,11 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({ // push change to display, hide the editor, submit the change targetModel.save({}, {error : CMS.ServerError}); this.closeEditor(this); + + analytics.track('Saved Course Update', { + 'course': course_location_analytics, + 'date': this.dateEntry(event).val() + }); }, onCancel: function(event) { @@ -147,6 +152,11 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({ return; } + analytics.track('Deleted Course Update', { + 'course': course_location_analytics, + 'date': this.dateEntry(event).val() + }); + var targetModel = this.eventModel(event); this.modelDom(event).remove(); var cacheThis = this; @@ -284,6 +294,11 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({ this.model.save({}, {error: CMS.ServerError}); this.$form.hide(); this.closeEditor(this); + + analytics.track('Saved Course Handouts', { + 'course': course_location_analytics + }); + }, onCancel: function(event) { diff --git a/cms/static/js/views/settings/advanced_view.js b/cms/static/js/views/settings/advanced_view.js index e3ff098efb..52c5ed78d0 100644 --- a/cms/static/js/views/settings/advanced_view.js +++ b/cms/static/js/views/settings/advanced_view.js @@ -137,6 +137,10 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ success : function() { self.render(); self.showMessage(self.successful_changes); + analytics.track('Saved Advanced Settings', { + 'course': course_location_analytics + }); + }, error : CMS.ServerError }); diff --git a/cms/templates/base.html b/cms/templates/base.html index 15f4c556bb..e4f5befd63 100644 --- a/cms/templates/base.html +++ b/cms/templates/base.html @@ -23,6 +23,8 @@ + <%include file="widgets/segment-io.html" /> + <%block name="header_extras"> diff --git a/cms/templates/unit.html b/cms/templates/unit.html index e1a020dfca..1b9e49620b 100644 --- a/cms/templates/unit.html +++ b/cms/templates/unit.html @@ -28,6 +28,7 @@ }); }); + var unit_location_analytics = '${unit_location}'; diff --git a/cms/templates/widgets/segment-io.html b/cms/templates/widgets/segment-io.html new file mode 100644 index 0000000000..f623c47542 --- /dev/null +++ b/cms/templates/widgets/segment-io.html @@ -0,0 +1,32 @@ +% if settings.MITX_FEATURES.get('SEGMENT_IO'): + + + +% else: + + + +% endif