codecombat/app/core/Tracker.coffee
Matt Lott bce9862be2 Update request demo Ux
Renaming request quote to request demo
Changing create class wording to set up class
Showing different UI if teacher or not
Adding gameplay screenshots to homepage
Update request demo email

Closes #3449
2016-03-08 09:30:50 -08:00

199 lines
8.1 KiB
CoffeeScript

{me} = require 'core/auth'
SuperModel = require 'models/SuperModel'
utils = require 'core/utils'
CocoClass = require 'core/CocoClass'
debugAnalytics = false
targetInspectJSLevelSlugs = ['cupboards-of-kithgard']
module.exports = class Tracker extends CocoClass
subscriptions:
'application:service-loaded': 'onServiceLoaded'
constructor: ->
super()
if window.tracker
console.error 'Overwrote our Tracker!', window.tracker
window.tracker = @
@isProduction = document.location.href.search('codecombat.com') isnt -1
@trackReferrers()
@identify()
@supermodel = new SuperModel()
@updateRole() if me.get 'role'
enableInspectletJS: (levelSlug) ->
# InspectletJS loading is delayed and targeting specific levels for more focused investigations
return @disableInspectletJS() unless levelSlug in targetInspectJSLevelSlugs
scriptLoaded = =>
# Identify and track pageview here, because inspectlet is loaded too late for standard Tracker calls
@identify()
# http://www.inspectlet.com/docs#virtual_pageviews
window.__insp?.push(['virtualPage'])
window.__insp = [['wid', 2102699786]]
insp = document.createElement('script')
insp.type = 'text/javascript'
insp.async = true
insp.id = 'inspsync'
insp.src = (if 'https:' == document.location.protocol then 'https' else 'http') + '://cdn.inspectlet.com/inspectlet.js'
insp.onreadystatechange = -> scriptLoaded() if insp.readyState is 'complete'
insp.onload = scriptLoaded
x = document.getElementsByTagName('script')[0]
@inspectletScriptNode = x.parentNode.insertBefore insp, x
disableInspectletJS: ->
if @inspectletScriptNode
x = document.getElementsByTagName('script')[0]
x.parentNode.removeChild(@inspectletScriptNode)
@inspectletScriptNode = null
delete window.__insp
trackReferrers: ->
elapsed = new Date() - new Date(me.get('dateCreated'))
return unless elapsed < 5 * 60 * 1000
return if me.get('siteref') or me.get('referrer')
changed = false
if siteref = utils.getQueryVariable '_r'
me.set 'siteref', siteref
changed = true
if referrer = document.referrer
me.set 'referrer', referrer
changed = true
me.patch() if changed
identify: (traits={}) ->
return unless me
# Save explicit traits for internal tracking
@explicitTraits ?= {}
@explicitTraits[key] = value for key, value of traits
for userTrait in ['email', 'anonymous', 'dateCreated', 'name', 'testGroupNumber', 'gender', 'lastLevel', 'siteref', 'ageRange', 'schoolName', 'coursePrepaidID', 'role']
traits[userTrait] ?= me.get(userTrait)
if me.isTeacher()
traits.teacher = true
console.log 'Would identify', me.id, traits if debugAnalytics
return unless @isProduction and not me.isAdmin()
# Errorception
# https://errorception.com/docs/meta
_errs?.meta = traits
# Inspectlet
# https://www.inspectlet.com/docs#identifying_users
__insp?.push ['identify', me.id]
__insp?.push ['tagSession', traits]
# Mixpanel
# https://mixpanel.com/help/reference/javascript
mixpanel.identify(me.id)
mixpanel.register(traits)
if me.isTeacher() and @segmentLoaded
traits.createdAt = me.get 'dateCreated' # Intercom, at least, wants this
analytics.identify me.id, traits
trackPageView: (includeIntegrations=[]) ->
name = Backbone.history.getFragment()
url = "/#{name}"
console.log "Would track analytics pageview: #{url}" if debugAnalytics
@trackEventInternal 'Pageview', url: name unless me?.isAdmin() and @isProduction
return unless @isProduction and not me.isAdmin()
# Google Analytics
# https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
ga? 'send', 'pageview', url
# Mixpanel
mixpanelIncludes = ['', 'courses', 'courses/purchase', 'courses/teachers', 'courses/students', 'schools', 'teachers', 'teachers/freetrial', 'teachers/quote', 'play', 'play/level/dungeons-of-kithgard']
mixpanel.track('page viewed', 'page name' : name, url : url) if name in mixpanelIncludes
if me.isTeacher() and @segmentLoaded
options = {}
if includeIntegrations?.length
options.integrations = All: false
for integration in includeIntegrations
options.integrations[integration] = true
analytics.page url, {}, options
trackEvent: (action, properties={}, includeIntegrations=[]) =>
@trackEventInternal action, _.cloneDeep properties unless me?.isAdmin() and @isProduction
console.log 'Tracking external analytics event:', action, properties, includeIntegrations if debugAnalytics
return unless me and @isProduction and not me.isAdmin()
# Google Analytics
# https://developers.google.com/analytics/devguides/collection/analyticsjs/events
gaFieldObject =
hitType: 'event'
eventCategory: properties.category ? 'All'
eventAction: action
gaFieldObject.eventLabel = properties.label if properties.label?
gaFieldObject.eventValue = properties.value if properties.value?
ga? 'send', gaFieldObject
# Inspectlet
# http://www.inspectlet.com/docs#tagging
__insp?.push ['tagSession', action: action, properies: properties]
# Mixpanel
# Only log explicit events for now
mixpanel.track(action, properties) if 'Mixpanel' in includeIntegrations
if me.isTeacher() and @segmentLoaded
options = {}
if includeIntegrations
# https://segment.com/docs/libraries/analytics.js/#selecting-integrations
options.integrations = All: false
for integration in includeIntegrations
options.integrations[integration] = true
analytics?.track action, {}, options
trackEventInternal: (event, properties) =>
# Skipping heavily logged actions we don't use internally
return if event in ['Simulator Result', 'Started Level Load', 'Finished Level Load']
# Trimming properties we don't use internally
# TODO: delete properites.level for 'Saw Victory' after 2/8/15. Should be using levelID instead.
if event in ['Clicked Start Level', 'Inventory Play', 'Heard Sprite', 'Started Level', 'Saw Victory', 'Click Play', 'Choose Inventory', 'Homepage Loaded', 'Change Hero']
delete properties.category
delete properties.label
else if event in ['Loaded World Map', 'Started Signup', 'Finished Signup', 'Login', 'Facebook Login', 'Google Login', 'Show subscription modal']
delete properties.category
properties[key] = value for key, value of @explicitTraits if @explicitTraits?
console.log 'Tracking internal analytics event:', event, properties if debugAnalytics
if @isProduction
eventObject = {}
eventObject["event"] = event
eventObject["properties"] = properties unless _.isEmpty properties
eventObject["user"] = me.id
dataToSend = JSON.stringify eventObject
# console.log dataToSend if debugAnalytics
$.post("#{window.location.protocol or 'http:'}//analytics.codecombat.com/analytics", dataToSend).fail ->
console.error "Analytics post failed!"
else
request = @supermodel.addRequestResource {
url: '/db/analytics.log.event/-/log_event'
data: {event: event, properties: properties}
method: 'POST'
}, 0
request.load()
trackTiming: (duration, category, variable, label) ->
# https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings
return console.warn "Duration #{duration} invalid for trackTiming call." unless duration >= 0 and duration < 60 * 60 * 1000
console.log 'Would track timing event:', arguments if debugAnalytics
return unless me and @isProduction and not me.isAdmin()
ga? 'send', 'timing', category, variable, duration, label
updateRole: ->
return unless me.isTeacher()
return require('core/services/segment')() unless @segmentLoaded
@identify()
#analytics.page() # It looks like we don't want to call this here because it somehow already gets called once in addition to this.
# TODO: record any events and pageviews that have built up before we knew we were a teacher.
onServiceLoaded: (e) ->
return unless e.service is 'segment'
@segmentLoaded = true
@updateRole()