Updated Hour of Code visitor counting.

This commit is contained in:
Nick Winter 2014-11-27 09:44:08 -08:00
parent b5c7a4e0f2
commit 2d2a9ad681
12 changed files with 98 additions and 63 deletions

View file

@ -15,8 +15,7 @@ module.exports = class CocoRouter extends Backbone.Router
@initializeSocialMediaServices = _.once @initializeSocialMediaServices
routes:
'': go('HomeView') # This will go somewhere deprecated when FrontView is done.
'front': go('FrontView') # This will become '' when it is done.
'': go('HomeView')
'about': go('AboutView')
@ -85,7 +84,6 @@ module.exports = class CocoRouter extends Backbone.Router
'play-old': go('play/MainPlayView') # This used to be 'play'.
'play': go('play/WorldMapView')
'play-hero': go('play/WorldMapView') # Legacy URL for /play; leave up until start of 2015, I guess.
'play/ladder/:levelID': go('play/ladder/LadderView')
'play/ladder': go('play/ladder/MainLadderView')
'play/level/:levelID': go('play/level/PlayLevelView')

View file

@ -70,3 +70,15 @@
.alert
top: 213px
border: 5px solid darkred
&.hour-of-code
#site-footer
background-color: rgb(70, 58, 44)
height: 185px
.hour-of-code-explanation
color: #9e8777
text-align: center
a
color: lighten(#0b63bc, 10%)

View file

@ -231,6 +231,23 @@
width: calc(33.333333% - 10px)
margin: 5px
.hour-of-code-done
clear: both
padding-top: 10px
strong
color: white
display: block
margin-bottom: 10px
font-weight: normal
.image-link
float: right
margin-left: 10px
.text-link
color: lighten(#0b63bc, 10%)
html.no-borderimage
#hero-victory-modal

View file

@ -86,3 +86,5 @@ block footer
a(href="http://www.fullyillustrated.com/") Fully Illustrated
a.firebase-bade(href="https://www.firebase.com/")
img(src="/images/pages/base/firebase.png", alt="Powered by Firebase")
block extra_footer_content

View file

@ -22,4 +22,13 @@ block outer_content
strong(data-i18n="home.old_browser") Uh oh, your browser is too old to run CodeCombat. Sorry!
br
span(data-i18n="home.old_browser_suffix") You can try anyway, but it probably won't work.
block extra_footer_content
if true || explainHourOfCode
// Does not show up unless lang is en-US.
div.hour-of-code-explanation
| The 'Hour of Code' is a nationwide initiative by
a(href="http://csedweek.org") Computer Science Education Week
| and
a(href="http://code.org") Code.org
| to introduce millions of students to one hour of computer science and computer programming.

View file

@ -37,11 +37,3 @@
#play-footer
p(class='footer-link-text')
a(title='Send CodeCombat a message', tabindex=-1, data-toggle="coco-modal", data-target="modal/ContactModal", data-i18n="nav.contact") Contact
if explainHourOfCode
// Does not show up unless lang is en-US.
div.hour-of-code-explanation
| The 'Hour of Code' is a nationwide initiative by
a(href="http://csedweek.org") Computer Science Education Week
| and
a(href="http://code.org") Code.org
| to introduce millions of students to one hour of computer science and computer programming.

View file

@ -69,3 +69,12 @@ block modal-footer-content
button.btn.btn-primary.return-to-ladder-button(data-href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_return_to_ladder") Return to Ladder
else
button.btn.btn-success.world-map-button.next-level-button.hide#continue-button(data-i18n="play_level.victory_play_continue") Continue
if showHourOfCodeDoneButton
.hour-of-code-done
hr
a.image-link(href="http://code.org/api/hour/finish")
img(src="/images/level/csedweek-logo-final-small.jpg", alt="CS Ed Week Hour of Code", title="I'm finished with my Hour of Code", width=80)
strong(data-i18n="play_level.victory_hour_of_code_done") Are You Done?
a.text-link(href="http://code.org/api/hour/finish")
span(data-i18n="play_level.victory_hour_of_code_done_yes") Yes, I'm finished with my Hour of Code!

View file

@ -1,34 +0,0 @@
RootView = require 'views/kinds/RootView'
template = require 'templates/front-view'
{me} = require '/lib/auth'
ModalView = require 'views/kinds/ModalView'
module.exports = class FrontView extends RootView
id: 'front-view'
template: template
events:
'click .platform-ios a': 'onIOSClicked'
getRenderData: ->
c = super()
if $.browser
majorVersion = $.browser.versionNumber
c.isOldBrowser = true if $.browser.mozilla && majorVersion < 21
c.isOldBrowser = true if $.browser.chrome && majorVersion < 17
c.isOldBrowser = true if $.browser.safari && majorVersion < 6
else
console.warn 'no more jquery browser version...'
c
afterRender: ->
super()
onIOSClicked: (e) ->
header = 'Sorry, the iPad app isn\'t ready yet'
body = '''
<p class="lead">We are working on it!</p>
<p>For now, try playing on the web, and totally sign up (with emails enabled) so you can be the first to hear when it is ready.</p>
'''
notImplementedModal = new ModalView headerContent: header, bodyContent: body
@openModalView notImplementedModal

View file

@ -16,6 +16,12 @@ module.exports = class HomeView extends RootView
constructor: ->
super()
window.tracker?.trackEvent 'Homepage', Action: 'Loaded'
if not me.get('hourOfCode') and @getQueryVariable 'hour_of_code'
@setUpHourOfCode()
elapsed = (new Date() - new Date(me.get('dateCreated')))
if me.get('hourOfCode') and elapsed < 86400 * 1000 and me.get('preferredLanguage', true) is 'en-US'
# Show the Hour of Code footer explanation in English until it's been more than a day
@explainsHourOfCode = true
getRenderData: ->
c = super()
@ -28,6 +34,7 @@ module.exports = class HomeView extends RootView
console.warn 'no more jquery browser version...'
c.isEnglish = (me.get('preferredLanguage') or 'en').startsWith 'en'
c.languageName = me.get('preferredLanguage')
c.explainsHourOfCode = @explainsHourOfCode
c
onClickBeginnerCampaign: (e) ->
@ -39,7 +46,17 @@ module.exports = class HomeView extends RootView
afterInsert: ->
super(arguments...)
@$el.addClass 'hour-of-code' if @explainsHourOfCode
if me.isAdmin() and me.get('slug') is 'nick'
LevelSetupManager = require 'lib/LevelSetupManager'
setupManager = new LevelSetupManager levelID: 'dungeons-of-kithgard', hadEverChosenHero: true, parent: @
setupManager.open()
setUpHourOfCode: ->
elapsed = (new Date() - new Date(me.get('dateCreated')))
if elapsed < 5 * 60 * 1000
me.set 'hourOfCode', true
me.patch()
# We may also insert the tracking pixel for everyone on the WorldMapView so as to count directly-linked visitors.
$('body').append($('<img src="http://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
application.tracker?.trackEvent 'Hour of Code Begin', {}

View file

@ -10,6 +10,8 @@ MusicPlayer = require 'lib/surface/MusicPlayer'
storage = require 'lib/storage'
AuthModal = require 'views/modal/AuthModal'
trackedHourOfCode = false
class LevelSessionsCollection extends CocoCollection
url: ''
model: LevelSession
@ -70,6 +72,12 @@ module.exports = class WorldMapView extends RootView
@listenTo me, 'change:spent', -> @renderSelectors('#gems-count')
window.tracker?.trackEvent 'World Map', Action: 'Loaded', ['Google Analytics']
# If it's a new player who didn't appear to come from Hour of Code, we register her here without setting the hourOfCode property.
elapsed = (new Date() - new Date(me.get('dateCreated')))
if not trackedHourOfCode and not me.get('hourOfCode') and elapsed < 5 * 60 * 1000
$('body').append($('<img src="http://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
trackedHourOfCode = true
destroy: ->
@setupManager?.destroy()
$(window).off 'resize', @onWindowResize

View file

@ -93,8 +93,6 @@ module.exports = class PlayLevelView extends RootView
constructor: (options, @levelID) ->
console.profile?() if PROFILE_ME
super options
if not me.get('hourOfCode') and @getQueryVariable 'hour_of_code'
@setUpHourOfCode()
@isEditorPreview = @getQueryVariable 'dev'
@sessionID = @getQueryVariable 'session'
@ -114,12 +112,6 @@ module.exports = class PlayLevelView extends RootView
@load()
application.tracker?.trackEvent 'Started Level Load', level: @levelID, label: @levelID, ['Google Analytics']
setUpHourOfCode: ->
me.set 'hourOfCode', true
me.patch()
$('body').append($('<img src="http://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
application.tracker?.trackEvent 'Hour of Code Begin', {}
setLevel: (@level, givenSupermodel) ->
@supermodel.models = givenSupermodel.models
@supermodel.collections = givenSupermodel.collections
@ -152,10 +144,6 @@ module.exports = class PlayLevelView extends RootView
getRenderData: ->
c = super()
c.world = @world
if me.get('hourOfCode') and me.get('preferredLanguage', true) is 'en-US'
# Show the Hour of Code footer explanation until it's been more than a day
elapsed = (new Date() - new Date(me.get('dateCreated')))
c.explainHourOfCode = elapsed < 86400 * 1000
c
afterRender: ->
@ -606,7 +594,7 @@ module.exports = class PlayLevelView extends RootView
# Current real-time multiplayer session
# Internal multiplayer create/joined/left events
#
# Real-time state variables.
# Real-time state variables.
# Each Ref is Firebase reference, and may have a matching Data suffixed variable with the latest data received.
# @realTimePlayerRef - User's real-time multiplayer player for this level
# @realTimePlayerGameRef - User's current real-time multiplayer player game session
@ -727,7 +715,7 @@ module.exports = class PlayLevelView extends RootView
@realTimeSessionRef = new Firebase "#{@multiplayerFireHost}multiplayer_level_sessions/#{@levelID}/#{e.realTimeSessionID}"
@realTimePlayersRef = @realTimeSessionRef.child 'players'
# Look for opponent
@realTimeSessionRef.once 'value', (multiplayerSessionSnapshot) =>
if @realTimeSessionData = multiplayerSessionSnapshot.val()
@ -751,7 +739,7 @@ module.exports = class PlayLevelView extends RootView
console.error 'Could not lookup multiplayer session data.'
@realTimeSessionRef.on 'value', @onRealTimeSessionChanged
@realTimePlayerGameRef = @realTimeSessionRef.child "players/#{me.id}"
@realTimePlayerGameRef = @realTimeSessionRef.child "players/#{me.id}"
# TODO: Follow up in MultiplayerView to see if double joins can be avoided
# else
@ -817,7 +805,7 @@ module.exports = class PlayLevelView extends RootView
unless @realTimeSessionRef?
console.error 'Real-time multiplayer cast without multiplayer session.'
return
unless @realTimeSessionData?
unless @realTimeSessionData?
console.error 'Real-time multiplayer cast without multiplayer data.'
return
unless @realTimePlayersData?

View file

@ -80,7 +80,7 @@ module.exports = class HeroVictoryModal extends ModalView
@readyToContinue = true
@updateSavingProgressStatus()
me.fetch() unless me.loading
@readyToContinue = true if not @achievements.models.length
getRenderData: ->
@ -117,6 +117,23 @@ module.exports = class HeroVictoryModal extends ModalView
{key: 'continue', link: @continueLevelLink, 'choice-explicit': 'next_level', 'choice-implicit': 'just_right'}
{key: 'more_practice', link: @morePracticeLevelLink, 'choice-explicit': 'more_practice', 'choice-implicit': 'too_hard'}
]
elapsed = (new Date() - new Date(me.get('dateCreated')))
isHourOfCode = me.get('hourOfCode') or elapsed < 120 * 60 * 1000
# Later we should only check me.get('hourOfCode'), but for now so much traffic comes in that we just assume it.
if isHourOfCode
# Show the Hour of Code "I'm Done" tracking pixel after they played for 20 minutes
enough = elapsed >= 20 * 60 * 1000
tooMuch = elapsed > 120 * 60 * 1000
showDone = elapsed >= 30 * 60 * 1000 and not tooMuch
if enough and not tooMuch and not me.get('hourOfCodeComplete')
$('body').append($('<img src="http://code.org/api/hour/finish_codecombat.png" style="visibility: hidden;">'))
me.set 'hourOfCodeComplete', true # Note that this will track even for players who don't have hourOfCode set.
me.patch()
window.tracker?.trackEvent 'Hour of Code Finish', {}
# Show the "I'm done" button between 30 - 120 minutes if they definitely came from Hour of Code
c.showHourOfCodeDoneButton = me.get('hourOfCode') and showDone
return c
afterRender: ->