From 46ef93190ae0ae72315175efa33d5f5a6c745021 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Wed, 4 Feb 2015 11:31:43 -0800 Subject: [PATCH 1/7] Show old difficulty instead of new difficulty when observing a replayable success. This will likely need revisiting when we are using observing for more than just leaderboards. --- app/lib/LevelLoader.coffee | 3 +++ app/views/play/level/ControlBarView.coffee | 2 ++ app/views/play/level/PlayLevelView.coffee | 2 +- app/views/play/level/tome/TomeView.coffee | 5 ++++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index e57efd220..048487ec1 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -32,6 +32,7 @@ module.exports = class LevelLoader extends CocoClass @team = options.team @headless = options.headless @spectateMode = options.spectateMode ? false + @observing = options.observing @worldNecessities = [] @listenTo @supermodel, 'resource-loaded', @onWorldNecessityLoaded @@ -389,6 +390,8 @@ module.exports = class LevelLoader extends CocoClass @world.submissionCount = @session?.get('state')?.submissionCount ? 0 @world.flagHistory = @session?.get('state')?.flagHistory ? [] @world.difficulty = @session?.get('state')?.difficulty ? 0 + if @observing + @world.difficulty = Math.max 0, @world.difficulty - 1 # Show the difficulty they won, not the next one. serializedLevel = @level.serialize(@supermodel, @session, @opponentSession) @world.loadFromLevel serializedLevel, false console.log 'World has been initialized from level loader.' diff --git a/app/views/play/level/ControlBarView.coffee b/app/views/play/level/ControlBarView.coffee index b493fa064..5d9576ec3 100644 --- a/app/views/play/level/ControlBarView.coffee +++ b/app/views/play/level/ControlBarView.coffee @@ -64,6 +64,8 @@ module.exports = class ControlBarView extends CocoView c.multiplayerStatus = @multiplayerStatusManager?.status if @level.get 'replayable' c.levelDifficulty = @session.get('state')?.difficulty ? 0 + if @observing + c.levelDifficulty = Math.max 0, c.levelDifficulty - 1 # Show the difficulty they won, not the next one. c.difficultyTitle = "#{$.i18n.t 'play.level_difficulty'}#{c.levelDifficulty}" @lastDifficulty = c.levelDifficulty c.spectateGame = @spectateGame diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index d2afbc082..48b302763 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -127,7 +127,7 @@ module.exports = class PlayLevelView extends RootView load: -> @loadStartTime = new Date() @god = new God debugWorker: true - @levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, sessionID: @sessionID, opponentSessionID: @opponentSessionID, team: @getQueryVariable('team') + @levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, sessionID: @sessionID, opponentSessionID: @opponentSessionID, team: @getQueryVariable('team'), observing: @observing @listenToOnce @levelLoader, 'world-necessities-loaded', @onWorldNecessitiesLoaded trackLevelLoadEnd: -> diff --git a/app/views/play/level/tome/TomeView.coffee b/app/views/play/level/tome/TomeView.coffee index e65285f77..41510424f 100644 --- a/app/views/play/level/tome/TomeView.coffee +++ b/app/views/play/level/tome/TomeView.coffee @@ -166,7 +166,10 @@ module.exports = class TomeView extends CocoView sessionState.flagHistory = _.filter sessionState.flagHistory ? [], (event) => event.team isnt (@options.session.get('team') ? 'humans') sessionState.lastUnsuccessfulSubmissionTime = new Date() if @options.level.get 'replayable' @options.session.set 'state', sessionState - Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: preload, realTime: realTime, submissionCount: sessionState.submissionCount ? 0, flagHistory: sessionState.flagHistory ? [], difficulty: sessionState.difficulty ? 0 + difficulty = sessionState.difficulty ? 0 + if @options.observing + difficulty = Math.max 0, difficulty - 1 # Show the difficulty they won, not the next one. + Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: preload, realTime: realTime, submissionCount: sessionState.submissionCount ? 0, flagHistory: sessionState.flagHistory ? [], difficulty: difficulty onToggleSpellList: (e) -> @spellList.rerenderEntries() From ac44acb86790e1560528292ac02fc55dad1b1f8e Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Wed, 4 Feb 2015 13:00:07 -0800 Subject: [PATCH 2/7] Campaign status gets out of the way after four seconds on non-dungeon campaigns. --- app/views/play/CampaignView.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/play/CampaignView.coffee b/app/views/play/CampaignView.coffee index 8eb306f91..72e1b5273 100644 --- a/app/views/play/CampaignView.coffee +++ b/app/views/play/CampaignView.coffee @@ -124,6 +124,7 @@ module.exports = class CampaignView extends RootView @fullyRendered = true @render() @preloadTopHeroes() unless me.get('heroConfig')?.thangType + @$el.find('#campaign-status').delay(4000).animate({top: "-=58"}, 1000) unless @terrain is 'dungeon' setCampaign: (@campaign) -> @render() From 11a3316deca4dfab93a8f188a9dac0c9f60dced6 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Wed, 4 Feb 2015 13:24:15 -0800 Subject: [PATCH 3/7] Preloading levels and sessions when clicking on a level, before clicking start. --- app/views/play/CampaignView.coffee | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/views/play/CampaignView.coffee b/app/views/play/CampaignView.coffee index 72e1b5273..5057cc7e6 100644 --- a/app/views/play/CampaignView.coffee +++ b/app/views/play/CampaignView.coffee @@ -91,7 +91,7 @@ module.exports = class CampaignView extends RootView destroy: -> @setupManager?.destroy() - @$el.find('.ui-draggable').draggable 'destroy' + @$el.find('.ui-draggable').off().draggable 'destroy' $(window).off 'resize', @onWindowResize if ambientSound = @ambientSound # Doesn't seem to work; stops immediately. @@ -190,7 +190,7 @@ module.exports = class CampaignView extends RootView _.defer => @$el?.find('.game-controls .btn').addClass('has-tooltip').tooltip() # Have to defer or i18n doesn't take effect. view = @ @$el.find('.level, .campaign-switch').addClass('has-tooltip').tooltip().each -> - return unless me.isAdmin() + return unless me.isAdmin() and view.editorMode $(@).draggable().on 'dragstop', -> bg = $('.map-background') x = ($(@).offset().left - bg.offset().left + $(@).outerWidth() / 2) / bg.width() @@ -288,10 +288,15 @@ module.exports = class CampaignView extends RootView onClickMap: (e) -> @$levelInfo?.hide() - # Easy-ish way of figuring out coordinates for placing level dots. - x = e.offsetX / @$el.find('.map-background').width() - y = (1 - e.offsetY / @$el.find('.map-background').height()) - console.log " x: #{(100 * x).toFixed(2)}\n y: #{(100 * y).toFixed(2)}\n" + + preloadLevel: (levelSlug) -> + levelURL = "/db/level/#{levelSlug}" + level = new Level().setURL levelURL + level = @supermodel.loadModel(level, 'level', null, 0).model + sessionURL = "/db/level/#{levelSlug}/session" + @preloadedSession = new LevelSession().setURL sessionURL + @preloadedSession.levelSlug = levelSlug + @preloadedSession.fetch() onClickLevel: (e) -> e.preventDefault() @@ -305,6 +310,7 @@ module.exports = class CampaignView extends RootView @$levelInfo = @$el.find(".level-info-container[data-level-slug=#{levelSlug}]").show() @adjustLevelInfoPosition e @endHighlight() + @preloadLevel levelSlug onDoubleClickLevel: (e) -> return unless @editorMode @@ -327,7 +333,9 @@ module.exports = class CampaignView extends RootView startLevel: (levelElement) -> @setupManager?.destroy() - @setupManager = new LevelSetupManager supermodel: @supermodel, levelID: levelElement.data('level-slug'), levelPath: levelElement.data('level-path'), levelName: levelElement.data('level-name'), hadEverChosenHero: @hadEverChosenHero, parent: @ + levelSlug = levelElement.data 'level-slug' + session = @preloadedSession if @preloadedSession?.loaded and @preloadedSession.levelSlug is levelSlug + @setupManager = new LevelSetupManager supermodel: @supermodel, levelID: levelSlug, levelPath: levelElement.data('level-path'), levelName: levelElement.data('level-name'), hadEverChosenHero: @hadEverChosenHero, parent: @, session: session @setupManager.open() @$levelInfo?.hide() From 35c04974dd1d3180d9562a1d73ddcad5e8ee1170 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Wed, 4 Feb 2015 13:42:49 -0800 Subject: [PATCH 4/7] Added a difficulty badge when going to play a replayable level. --- app/styles/play/campaign-view.sass | 15 +++++++++++++++ app/views/play/CampaignView.coffee | 15 ++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/app/styles/play/campaign-view.sass b/app/styles/play/campaign-view.sass index e14d5ba5f..444248910 100644 --- a/app/styles/play/campaign-view.sass +++ b/app/styles/play/campaign-view.sass @@ -243,6 +243,21 @@ $gameControlMargin: 30px min-width: 200px display: block margin: 10px auto 0 auto + position: relative + + .badge + position: absolute + top: initial + left: initial + right: -25px + bottom: -25px + font-size: 20px + color: black + border: 1px solid black + background-color: rgb(232, 217, 87) + border-radius: 50% + opacity: 1 + padding: 3px 9px &.complete .start-level, .view-solutions diff --git a/app/views/play/CampaignView.coffee b/app/views/play/CampaignView.coffee index 5057cc7e6..cc8108b44 100644 --- a/app/views/play/CampaignView.coffee +++ b/app/views/play/CampaignView.coffee @@ -286,9 +286,6 @@ module.exports = class CampaignView extends RootView @levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started' @render() - onClickMap: (e) -> - @$levelInfo?.hide() - preloadLevel: (levelSlug) -> levelURL = "/db/level/#{levelSlug}" level = new Level().setURL levelURL @@ -297,6 +294,18 @@ module.exports = class CampaignView extends RootView @preloadedSession = new LevelSession().setURL sessionURL @preloadedSession.levelSlug = levelSlug @preloadedSession.fetch() + @listenToOnce @preloadedSession, 'sync', @onSessionPreloaded + + onSessionPreloaded: (session) -> + levelElement = @$el.find('.level-info-container:visible') + return unless session.levelSlug is levelElement.data 'level-slug' + return unless difficulty = session.get('state')?.difficulty + badge = $("#{difficulty}") + levelElement.find('.start-level .badge').remove() + levelElement.find('.start-level').append badge + + onClickMap: (e) -> + @$levelInfo?.hide() onClickLevel: (e) -> e.preventDefault() From c842f45786f5515706f8508a974184fbfd104067 Mon Sep 17 00:00:00 2001 From: Matt Lott Date: Wed, 4 Feb 2015 13:54:35 -0800 Subject: [PATCH 5/7] Update subscribe modal with parent help button --- .../pages/play/modal/subscribe-heroes.png | Bin 0 -> 59145 bytes app/locale/en.coffee | 19 ++- app/schemas/models/user.coffee | 8 +- app/styles/modal/subscribe-modal.sass | 109 +++++++++++++----- app/templates/core/subscribe-modal.jade | 66 ++++++++--- app/views/core/SubscribeModal.coffee | 57 ++++++++- server/analytics/AnalyticsLogEvent.coffee | 100 ++++++++++++++++ .../analytics_log_event_handler.coffee | 101 +--------------- server/sendwithus.coffee | 1 + server/users/user_handler.coffee | 32 +++++ 10 files changed, 350 insertions(+), 143 deletions(-) create mode 100644 app/assets/images/pages/play/modal/subscribe-heroes.png diff --git a/app/assets/images/pages/play/modal/subscribe-heroes.png b/app/assets/images/pages/play/modal/subscribe-heroes.png new file mode 100644 index 0000000000000000000000000000000000000000..bd55cf02bf9380ffe58662b9e24bf8b7a284c0a3 GIT binary patch literal 59145 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 O0000new heroes with unique skills!" + feature3: "30+ bonus levels" + feature4: "3500 bonus gems every month!" + feature5: " Video tutorials" + feature6: "Premium email support" + free: "Free" + month: "month" subscribe_title: "Subscribe" unsubscribe: "Unsubscribe" confirm_unsubscribe: "Confirm Unsubscribe" @@ -394,12 +403,20 @@ heroes: "More powerful heroes!" gems: "3500 bonus gems every month!" items: "Over 250 bonus items!" + parent_button: "Ask your parent" + parent_email_description: "We'll email them so they can buy you a CodeCombat subscription." + parent_email_input_invalid: "Email address invalid." + parent_email_input_label: "Parent email address" + parent_email_input_placeholder: "Enter parent email" + parent_email_send: "Send Email" + parent_email_sent: "Email sent!" + parent_email_title: "What's your parent's email?" parents: "For Parents" parents_title: "Your child will learn to code." parents_blurb1: "With CodeCombat, your child learns by writing real code. They start by learning simple commands, and progress to more advanced topics." parents_blurb2: "For $9.99 USD/mo, they get new challenges every week and personal email support from professional programmers." parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." - subscribe_button: "Subscribe Now" + subscribe_button: "Subscribe" stripe_description: "Monthly Subscription" subscription_required_to_play: "You'll need a subscription to play this level." unlock_help_videos: "Subscribe to unlock all video tutorials." diff --git a/app/schemas/models/user.coffee b/app/schemas/models/user.coffee index 1d6396ac0..a499decdd 100644 --- a/app/schemas/models/user.coffee +++ b/app/schemas/models/user.coffee @@ -85,6 +85,12 @@ _.extend UserSchema.properties, recruitNotes: {$ref: '#/definitions/emailSubscription'} employerNotes: {$ref: '#/definitions/emailSubscription'} + oneTimes: c.array {title: 'One-time emails'}, + c.object {title: 'One-time email', required: ['type', 'targetEmail']}, + type: c.shortString() # E.g 'subscribe modal parent' + targetEmail: c.shortString() + sent: c.date() # Set when sent + # server controlled permissions: c.array {}, c.shortString() dateCreated: c.date({title: 'Date Joined'}) @@ -272,7 +278,7 @@ _.extend UserSchema.properties, purchased: c.RewardSchema 'purchased with gems or money' spent: {type: 'number'} stripeCustomerID: { type: 'string' } # TODO: Migrate away from this property - + stripe: c.object {}, { customerID: { type: 'string' } planID: { enum: ['basic'] } diff --git a/app/styles/modal/subscribe-modal.sass b/app/styles/modal/subscribe-modal.sass index 96a89f967..8a56a0895 100644 --- a/app/styles/modal/subscribe-modal.sass +++ b/app/styles/modal/subscribe-modal.sass @@ -51,36 +51,11 @@ &:hover color: yellow - - //- Selling points - - #selling-points - position: absolute - left: 65px - top: 335px - width: 650px - font-weight: bold - line-height: 18px - color: black - font-family: $headings-font-family - font-size: 18px - - .point - width: 150px - overflow: none - float: left - text-align: center - margin-right: 10px - - #parents-info - position: absolute - right: 7px - top: 56px - text-decoration: underline - cursor: pointer + //- Popovers .popover z-index: 1050 + min-width: 400px h3 background: transparent @@ -88,13 +63,67 @@ font-size: 30px color: black + //- Sales image + + .subscribe-image + position: absolute + top: 114px + right: 65px + + //- Feature comparison table + + .comparison-blurb + position: absolute + left: 10% + top: 132px + width: 450px + background: rgba(0, 0, 0, 0.0) + font-weight: normal + line-height: 18px + color: black + font-family: $headings-font-family + font-size: 18px + + .comparison-table + position: absolute + left: 10% + top: 160px + width: 450px + background: rgba(0, 0, 0, 0.0) + thead + tr + th + font-size: 24px + font-variant: small-caps + font-family: "Open Sans Condensed", "Helvetica Neue", Helvetica, Arial, sans-serif + font-weight: 700 + line-height: 1.1 + color: #317EAC + tbody + font-size: 14px + .center-ok + text-align: center + + //- Parent info popover link + + #parents-info + position: absolute + left: 38px + top: 389px + text-decoration: underline + cursor: pointer + font-weight: bold + line-height: 18px + color: black + font-family: $headings-font-family + font-size: 18px //- Purchase button .purchase-button position: absolute - left: 73px - width: 600px + right: 24px + width: 400px height: 70px top: 430px font-size: 32px @@ -116,6 +145,28 @@ padding: 2px 0 0 2px color: white + //- Parent button + //- TODO: Add hover and active effects + + .parent-button + position: absolute + left: 24px + width: 250px + height: 70px + top: 430px + font-size: 28px + line-height: 38px + border-style: solid + border-image: url(/images/common/button-background-warning-disabled.png) 14 20 20 20 fill round + border-width: 14px 20px 20px 20px + color: darken(white, 5%) + + #email-parent-form + .email_invalid + color: red + display: none + #email-parent-complete + display: none //- Errors diff --git a/app/templates/core/subscribe-modal.jade b/app/templates/core/subscribe-modal.jade index ecf13ba2d..3071b2c5e 100644 --- a/app/templates/core/subscribe-modal.jade +++ b/app/templates/core/subscribe-modal.jade @@ -7,27 +7,67 @@ #retrying-alert.alert.alert-danger(data-i18n="buy_gems.retrying") else - img(src="/images/pages/play/modal/subscribe-background.png")#subscribe-background + img#subscribe-background(src="/images/pages/play/modal/subscribe-background-blank.png") + img.subscribe-image(src="/images/pages/play/modal/subscribe-heroes.png") h1(data-i18n="subscribe.subscribe_title") Subscribe div#close-modal span.glyphicon.glyphicon-remove - #selling-points - #point-levels.point - .blurb(data-i18n="subscribe.levels") - #point-heroes.point - .blurb(data-i18n="subscribe.heroes") - #point-gems.point - .blurb(data-i18n="subscribe.gems") - #point-items.point - .blurb(data-i18n="subscribe.items") - - #parents-info(data-i18n="subscribe.parents") + div.comparison-blurb(data-i18n="subscribe.comparison_blurb") + table.table.table-condensed.table-bordered.comparison-table + thead + tr + th + th(data-i18n="subscribe.free") + th + //- TODO: find a better way to localize '$9.99/month' + span $#{price}/ + span(data-i18n="subscribe.month") + tbody + tr + td.feature-description + span(data-i18n="subscribe.feature1") + td.center-ok + span.glyphicon.glyphicon-ok + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="[html]subscribe.feature2") + td + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="subscribe.feature3") + td + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="[html]subscribe.feature4") + td + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="subscribe.feature5") + td + td.center-ok + span.glyphicon.glyphicon-ok + tr + td.feature-description + span(data-i18n="subscribe.feature6") + td + td.center-ok + span.glyphicon.glyphicon-ok + #parents-info(data-i18n="subscribe.parents") button.btn.btn-lg.btn-illustrated.purchase-button(data-i18n="subscribe.subscribe_button") - + button.btn.btn-lg.btn-illustrated.parent-button(data-i18n="subscribe.parent_button") + if state === 'declined' #declined-alert.alert.alert-danger.alert-dismissible span(data-i18n="buy_gems.declined") diff --git a/app/views/core/SubscribeModal.coffee b/app/views/core/SubscribeModal.coffee index 01dfe153c..bf729b381 100644 --- a/app/views/core/SubscribeModal.coffee +++ b/app/views/core/SubscribeModal.coffee @@ -17,8 +17,9 @@ module.exports = class SubscribeModal extends ModalView 'stripe:received-token': 'onStripeReceivedToken' events: - 'click .purchase-button': 'onClickPurchaseButton' 'click #close-modal': 'hide' + 'click #parent-send': 'onClickParentSendButton' + 'click .purchase-button': 'onClickPurchaseButton' constructor: (options) -> super(options) @@ -34,6 +35,40 @@ module.exports = class SubscribeModal extends ModalView afterRender: -> super() + @setupParentButtonPopover() + @setupParentInfoPopover() + + setupParentButtonPopover: -> + popoverTitle = $.i18n.t 'subscribe.parent_email_title' + popoverTitle += '' + popoverContent = "

" + popoverContent += "

#{$.i18n.t('subscribe.parent_email_description')}

" + popoverContent += "
" + popoverContent += "
" + popoverContent += " " + popoverContent += " " + popoverContent += " " + popoverContent += "
" + popoverContent += " " + popoverContent += "
" + popoverContent += "
" + popoverContent += "
" + popoverContent += "

#{$.i18n.t('subscribe.parent_email_sent')}

" + popoverContent += " " + popoverContent += "
" + + @$el.find('.parent-button').popover( + animation: true + html: true + placement: 'top' + trigger: 'click' + title: popoverTitle + content: popoverContent + container: @$el + ).on 'shown.bs.popover', => + application.tracker?.trackEvent 'Subscription ask parent button click', {} + + setupParentInfoPopover: -> popoverTitle = $.i18n.t 'subscribe.parents_title' popoverContent = "

" + $.i18n.t('subscribe.parents_blurb1') + "

" popoverContent += "

" + $.i18n.t('subscribe.parents_blurb2') + "

" @@ -50,6 +85,26 @@ module.exports = class SubscribeModal extends ModalView ).on 'shown.bs.popover', => application.tracker?.trackEvent 'Subscription parent hover', {} + onClickParentSendButton: (e) -> + # TODO: Popover sometimes dismisses immediately after send + + email = $('#parent-input').val() + unless /[\w\.]+@\w+\.\w+/.test email + $('#parent-input').parent().addClass('has-error') + $('#parent-email-validator').show() + return false + + request = @supermodel.addRequestResource 'send_one_time_email', { + url: '/db/user/-/send_one_time_email' + data: {email: email, type: 'subscribe modal parent'} + method: 'POST' + }, 0 + request.load() + + $('#email-parent-form').hide() + $('#email-parent-complete').show() + false + onClickPurchaseButton: (e) -> @playSound 'menu-button-click' return @openModalView new AuthModal() if me.get('anonymous') diff --git a/server/analytics/AnalyticsLogEvent.coffee b/server/analytics/AnalyticsLogEvent.coffee index be9a5b461..f5a5b3791 100644 --- a/server/analytics/AnalyticsLogEvent.coffee +++ b/server/analytics/AnalyticsLogEvent.coffee @@ -1,5 +1,7 @@ +log = require 'winston' mongoose = require 'mongoose' plugins = require '../plugins/plugins' +utils = require '../lib/utils' AnalyticsLogEventSchema = new mongoose.Schema({ u: mongoose.Schema.Types.ObjectId @@ -14,4 +16,102 @@ AnalyticsLogEventSchema = new mongoose.Schema({ AnalyticsLogEventSchema.index({event: 1, _id: 1}) +AnalyticsLogEventSchema.statics.logEvent = (user, event, properties) -> + unless user? + log.warn 'No user given to analytics logEvent.' + return + + saveDoc = (eventID, slimProperties) -> + doc = new AnalyticsLogEvent + u: user + e: eventID + p: slimProperties + # TODO: Remove these legacy properties after we stop querying for them (probably 30 days, ~2/16/15) + user: user + event: event + properties: properties + doc.save() + + utils.getAnalyticsStringID event, (eventID) -> + if eventID > 0 + # TODO: properties slimming is pretty ugly + slimProperties = _.cloneDeep properties + if event in ['Clicked Level', 'Show problem alert', 'Started Level', 'Saw Victory', 'Problem alert help clicked', 'Spell palette help clicked'] + delete slimProperties.level if event is 'Saw Victory' + properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls + slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls + if slimProperties.levelID? + # levelID: string => l: string ID + utils.getAnalyticsStringID slimProperties.levelID, (levelStringID) -> + if levelStringID > 0 + delete slimProperties.levelID + slimProperties.l = levelStringID + saveDoc eventID, slimProperties + return + else if event in ['Script Started', 'Script Ended'] + properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls + slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls + if slimProperties.levelID? and slimProperties.label? + # levelID: string => l: string ID + # label: string => lb: string ID + utils.getAnalyticsStringID slimProperties.levelID, (levelStringID) -> + if levelStringID > 0 + delete slimProperties.levelID + slimProperties.l = levelStringID + utils.getAnalyticsStringID slimProperties.label, (labelStringID) -> + if labelStringID > 0 + delete slimProperties.label + slimProperties.lb = labelStringID + saveDoc eventID, slimProperties + return + else if event is 'Heard Sprite' + properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls + slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls + if slimProperties.message? + # message: string => m: string ID + utils.getAnalyticsStringID slimProperties.message, (messageStringID) -> + if messageStringID > 0 + delete slimProperties.message + slimProperties.m = messageStringID + saveDoc eventID, slimProperties + return + else if event in ['Start help video', 'Finish help video'] + properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls + slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls + if slimProperties.level and slimProperties.style? + # level: string => l: string ID + # style: string => s: string ID + utils.getAnalyticsStringID slimProperties.level, (levelStringID) -> + if levelStringID > 0 + delete slimProperties.level + slimProperties.l = levelStringID + utils.getAnalyticsStringID slimProperties.style, (styleStringID) -> + if styleStringID > 0 + delete slimProperties.style + slimProperties.s = styleStringID + saveDoc eventID, slimProperties + return + else if event is 'Show subscription modal' + delete properties.category + delete slimProperties.category + if slimProperties.label? + # label: string => lb: string ID + utils.getAnalyticsStringID slimProperties.label, (labelStringID) -> + if labelStringID > 0 + delete slimProperties.label + slimProperties.lb = labelStringID + if slimProperties.level? + # level: string => l: string ID + utils.getAnalyticsStringID slimProperties.level, (levelStringID) -> + if levelStringID > 0 + delete slimProperties.level + slimProperties.l = levelStringID + saveDoc eventID, slimProperties + return + saveDoc eventID, slimProperties + return + saveDoc eventID, slimProperties + else + log.warn "Unable to get analytics string ID for " + event + module.exports = AnalyticsLogEvent = mongoose.model('analytics.log.event', AnalyticsLogEventSchema) diff --git a/server/analytics/analytics_log_event_handler.coffee b/server/analytics/analytics_log_event_handler.coffee index 91039b731..f31a17234 100644 --- a/server/analytics/analytics_log_event_handler.coffee +++ b/server/analytics/analytics_log_event_handler.coffee @@ -39,102 +39,7 @@ class AnalyticsLogEventHandler extends Handler event = req.query.event or req.body.event properties = req.query.properties or req.body.properties @sendSuccess res # Return request immediately - unless user? - log.warn 'No user given to analytics logEvent.' - return - - saveDoc = (eventID, slimProperties) -> - doc = new AnalyticsLogEvent - u: user - e: eventID - p: slimProperties - # TODO: Remove these legacy properties after we stop querying for them (probably 30 days, ~2/16/15) - user: user - event: event - properties: properties - doc.save() - - utils.getAnalyticsStringID event, (eventID) -> - if eventID > 0 - # TODO: properties slimming is pretty ugly - slimProperties = _.cloneDeep properties - if event in ['Clicked Level', 'Show problem alert', 'Started Level', 'Saw Victory', 'Problem alert help clicked', 'Spell palette help clicked'] - delete slimProperties.level if event is 'Saw Victory' - properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls - slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls - if slimProperties.levelID? - # levelID: string => l: string ID - utils.getAnalyticsStringID slimProperties.levelID, (levelStringID) -> - if levelStringID > 0 - delete slimProperties.levelID - slimProperties.l = levelStringID - saveDoc eventID, slimProperties - return - else if event in ['Script Started', 'Script Ended'] - properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls - slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls - if slimProperties.levelID? and slimProperties.label? - # levelID: string => l: string ID - # label: string => lb: string ID - utils.getAnalyticsStringID slimProperties.levelID, (levelStringID) -> - if levelStringID > 0 - delete slimProperties.levelID - slimProperties.l = levelStringID - utils.getAnalyticsStringID slimProperties.label, (labelStringID) -> - if labelStringID > 0 - delete slimProperties.label - slimProperties.lb = labelStringID - saveDoc eventID, slimProperties - return - else if event is 'Heard Sprite' - properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls - slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls - if slimProperties.message? - # message: string => m: string ID - utils.getAnalyticsStringID slimProperties.message, (messageStringID) -> - if messageStringID > 0 - delete slimProperties.message - slimProperties.m = messageStringID - saveDoc eventID, slimProperties - return - else if event in ['Start help video', 'Finish help video'] - properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls - slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls - if slimProperties.level and slimProperties.style? - # level: string => l: string ID - # style: string => s: string ID - utils.getAnalyticsStringID slimProperties.level, (levelStringID) -> - if levelStringID > 0 - delete slimProperties.level - slimProperties.l = levelStringID - utils.getAnalyticsStringID slimProperties.style, (styleStringID) -> - if styleStringID > 0 - delete slimProperties.style - slimProperties.s = styleStringID - saveDoc eventID, slimProperties - return - else if event is 'Show subscription modal' - delete properties.category - delete slimProperties.category - if slimProperties.label? - # label: string => lb: string ID - utils.getAnalyticsStringID slimProperties.label, (labelStringID) -> - if labelStringID > 0 - delete slimProperties.label - slimProperties.lb = labelStringID - if slimProperties.level? - # level: string => l: string ID - utils.getAnalyticsStringID slimProperties.level, (levelStringID) -> - if levelStringID > 0 - delete slimProperties.level - slimProperties.l = levelStringID - saveDoc eventID, slimProperties - return - saveDoc eventID, slimProperties - return - saveDoc eventID, slimProperties - else - log.warn "Unable to get analytics string ID for " + event + AnalyticsLogEvent.logEvent user, event, properties getLevelCompletionsBySlug: (req, res) -> # Returns an array of per-day level starts and finishes @@ -204,7 +109,7 @@ class AnalyticsLogEventHandler extends Handler for level of levelDateMap completions[level] = [] for created, item of levelDateMap[level] - completions[level].push + completions[level].push level: level created: created started: Object.keys(item.started).length @@ -382,7 +287,7 @@ class AnalyticsLogEventHandler extends Handler getUserEventData campaigns getCampaignData = () => - # Get campaign data + # Get campaign data # Output: # campaigns - per-campaign dictionary of ordered levelIDs # campaignLevelIDs - dictionary of all campaign levelIDs diff --git a/server/sendwithus.coffee b/server/sendwithus.coffee index ff9cdf8b4..d8411b622 100644 --- a/server/sendwithus.coffee +++ b/server/sendwithus.coffee @@ -10,6 +10,7 @@ module.exports.api = new sendwithusAPI swuAPIKey, debug if config.unittest module.exports.api.send = -> module.exports.templates = + parent_subscribe_email: 'tem_2APERafogvwKhmcnouigud' welcome_email: 'utnGaBHuSU4Hmsi7qrAypU' ladder_update_email: 'JzaZxf39A4cKMxpPZUfWy4' patch_created: 'tem_xhxuNosLALsizTNojBjNcL' diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index 2dd8d2fd2..c070c395e 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -9,6 +9,7 @@ errors = require '../commons/errors' async = require 'async' log = require 'winston' moment = require 'moment' +AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent' LevelSession = require '../levels/sessions/LevelSession' LevelSessionHandler = require '../levels/sessions/level_session_handler' SubscriptionHandler = require '../payments/subscription_handler' @@ -17,6 +18,7 @@ EarnedAchievement = require '../achievements/EarnedAchievement' UserRemark = require './remarks/UserRemark' {isID} = require '../lib/utils' hipchat = require '../hipchat' +sendwithus = require '../sendwithus' serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset', 'lastIP'] candidateProperties = [ @@ -232,6 +234,7 @@ UserHandler = class UserHandler extends Handler return @getRemark(req, res, args[0]) if args[1] is 'remark' return @searchForUser(req, res) if args[1] is 'admin_search' return @getStripeInfo(req, res, args[0]) if args[1] is 'stripe' + return @sendOneTimeEmail(req, res, args[0]) if args[1] is 'send_one_time_email' return @sendNotFoundError(res) super(arguments...) @@ -244,6 +247,35 @@ UserHandler = class UserHandler extends Handler return @sendDatabaseError(res, err) if err @sendSuccess(res, JSON.stringify(customer, null, '\t')) + sendOneTimeEmail: (req, res) -> + # TODO: should this API be somewhere else? + # TODO: hipchat tower success message shows up as a misleading PaperTrail error message + return @sendForbiddenError(res) unless req.user + email = req.query.email or req.body.email + type = req.query.type or req.body.type + return @sendBadInputError res, 'No email given.' unless email? + return @sendBadInputError res, 'No type given.' unless type? + + return @sendBadInputError res, "Unknown one-time email type #{type}" unless type is 'subscribe modal parent' + + emailParams = + email_id: sendwithus.templates.parent_subscribe_email + recipient: + address: email + email_data: + name: req.user.get('name') or '' + sendwithus.api.send emailParams, (err, result) => + if err + log.error "sendwithus one-time email error: #{err}, result: #{result}" + return @sendError res, 500, 'send mail failed.' + req.user.update {$push: {"emails.oneTimes": {type: type, email: email, sent: new Date()}}}, (err) => + return @sendDatabaseError(res, err) if err + req.user.save (err) => + return @sendDatabaseError(res, err) if err + @sendSuccess(res, {result: 'success'}) + hipchat.sendTowerHipChatMessage "#{req.user.get('name') or req.user.get('email')} just submitted subscribe modal parent email #{email}." + AnalyticsLogEvent.logEvent req.user, 'Sent one time email', email: email, type: type + agreeToCLA: (req, res) -> return @sendForbiddenError(res) unless req.user doc = From b42061e0eef0e281772cb9670580e975f2741b64 Mon Sep 17 00:00:00 2001 From: Matt Lott Date: Wed, 4 Feb 2015 14:29:16 -0800 Subject: [PATCH 6/7] Include code language in parent email --- server/users/user_handler.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index c070c395e..2e22a00cb 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -264,6 +264,9 @@ UserHandler = class UserHandler extends Handler address: email email_data: name: req.user.get('name') or '' + if codeLanguage = req.user.get('aceConfig.language') + codeLanguage = codeLanguage[0].toUpperCase() + codeLanguage.slice(1) + emailParams['email_data']['codeLanguage'] = codeLanguage sendwithus.api.send emailParams, (err, result) => if err log.error "sendwithus one-time email error: #{err}, result: #{result}" From 8f996a44b0f6c13b367e454dc7eacd17feaa57e7 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Wed, 4 Feb 2015 14:48:25 -0800 Subject: [PATCH 7/7] Added replays to the campaign editor level analytics drilldowns for particular sessions. --- app/templates/editor/campaign/campaign-level-view.jade | 4 ++++ app/views/editor/campaign/CampaignLevelView.coffee | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/app/templates/editor/campaign/campaign-level-view.jade b/app/templates/editor/campaign/campaign-level-view.jade index 21195bc60..a638db24e 100644 --- a/app/templates/editor/campaign/campaign-level-view.jade +++ b/app/templates/editor/campaign/campaign-level-view.jade @@ -59,6 +59,7 @@ td Playtime td Complete td Changed + td Replay tbody - for (var i = 0; i < analytics.recentSessions.data.length; i++) tr.recent-session(data-player-id=analytics.recentSessions.data[i].creator, data-session-id=analytics.recentSessions.data[i]._id) @@ -71,6 +72,9 @@ else td false td= analytics.recentSessions.data[i].changed + td + button.btn.replay-button.btn-xs + .glyphicon.glyphicon-eye-open h4 Completion Rates if analytics.levelCompletions.loading diff --git a/app/views/editor/campaign/CampaignLevelView.coffee b/app/views/editor/campaign/CampaignLevelView.coffee index 96358c032..3864a7686 100644 --- a/app/views/editor/campaign/CampaignLevelView.coffee +++ b/app/views/editor/campaign/CampaignLevelView.coffee @@ -16,6 +16,7 @@ module.exports = class CampaignLevelView extends CocoView 'dblclick .recent-session': 'onDblClickRecentSession' 'mouseenter .graph-point': 'onMouseEnterPoint' 'mouseleave .graph-point': 'onMouseLeavePoint' + 'click .replay-button': 'onClickReplay' constructor: (options, @level) -> super(options) @@ -77,6 +78,11 @@ module.exports = class CampaignLevelView extends CocoView pointID = $(e.target).data('pointid') @$el.find(".graph-point-info-container[data-pointid=#{pointID}]").hide() + onClickReplay: (e) -> + sessionID = $(e.target).closest('tr').data 'session-id' + url = "/play/level/#{@level.get('slug')}?session=#{sessionID}&observing=true" + window.open url, '_blank' + updateAnalyticsGraphData: -> # console.log 'updateAnalyticsGraphData' # Build graphs based on available @analytics data