Update Hour of Code student flow and tracking, remove signup requirement

This commit is contained in:
Nick Winter 2015-10-13 08:11:55 -07:00
parent c969768800
commit d776809e1c
8 changed files with 49 additions and 15 deletions

View file

@ -38,7 +38,7 @@ block content
- i++
mixin hoc-landing
h1.center Welcome Hour of Code!
h1.center Welcome to CodeCombat's Hour of Code!
br
.container-fluid
.row
@ -53,7 +53,7 @@ mixin student-main
mixin teacher-hoc
button.btn.btn-warning.btn-student(data-i18n="courses.students_click")
h1.center Welcome Hour of Code!
h1.center Welcome to CodeCombat's Hour of Code!
p
strong How to use CodeCombat with your students:
ol

View file

@ -36,11 +36,11 @@ block outer_content
block extra_footer_content
if explainHourOfCode
if explainsHourOfCode
// 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.
| to introduce millions of students to one hour of computer science and computer programming.

View file

@ -46,6 +46,8 @@ module.exports = class HomeView extends RootView
@$el.addClass 'hour-of-code' if @explainsHourOfCode
setUpHourOfCode: ->
# All this HoC stuff is for the 2014-2015 year. 2015-2016 year lands at /hoc instead (the courses view).
# TODO: get rid of all this sometime in November 2015 when code.org/learn updates to the new version for Hour of Code tutorials.
elapsed = (new Date() - new Date(me.get('dateCreated')))
if elapsed < 5 * 60 * 1000
me.set 'hourOfCode', true

View file

@ -9,6 +9,8 @@ User = require 'models/User'
utils = require 'core/utils'
Prepaid = require 'models/Prepaid'
autoplayedOnce = false
module.exports = class CourseDetailsView extends RootView
id: 'course-details-view'
template: template
@ -64,7 +66,7 @@ module.exports = class CourseDetailsView extends RootView
onCourseSync: ->
# console.log 'onCourseSync'
if me.isAnonymous()
if me.isAnonymous() and not me.get('hourOfCode')
@noCourseInstance = true
@render?()
return
@ -117,7 +119,7 @@ module.exports = class CourseDetailsView extends RootView
onCourseInstanceSync: ->
# console.log 'onCourseInstanceSync'
@adminMode = true if @courseInstance.get('ownerID') is me.id
@adminMode = true if @courseInstance.get('ownerID') is me.id and @courseInstance.get('name') isnt 'Single Player'
@levelSessions = new CocoCollection([], { url: "/db/course_instance/#{@courseInstance.id}/level_sessions", model: LevelSession, comparator:'_id' })
@listenToOnce @levelSessions, 'sync', @onLevelSessionsSync
@supermodel.loadCollection @levelSessions, 'level_sessions', cache: false
@ -180,6 +182,11 @@ module.exports = class CourseDetailsView extends RootView
@conceptsCompleted[concept]++
@render?()
# If we just joined a single-player course for Hour of Code, we automatically play.
if @instanceStats.totalLevelsCompleted is 0 and @instanceStats.totalPlayTime is 0 and @courseInstance.get('members').length is 1 and me.get('hourOfCode') and not @adminMode and not autoplayedOnce
autoplayedOnce = true
@$el.find('button.btn-play-level').click()
onMembersSync: ->
# console.log 'onMembersSync'
@memberUserMap = {}

View file

@ -17,15 +17,14 @@ module.exports = class CoursesView extends RootView
'click .btn-buy': 'onClickBuy'
'click .btn-enroll': 'onClickEnroll'
'click .btn-enter': 'onClickEnter'
'click .btn-hoc-student-continue': 'onClickHocStudentContinue'
'click .btn-hoc-student-continue': 'onClickHOCStudentContinue'
'click .btn-student': 'onClickStudent'
'click .btn-teacher': 'onClickTeacher'
constructor: (options) ->
super(options)
@setUpHourOfCode()
@praise = utils.getCoursePraise()
@hocLandingPage = Backbone.history.getFragment()?.indexOf('hoc') >= 0
@hocMode = utils.getQueryVariable('hoc', false)
@studentMode = Backbone.history.getFragment()?.indexOf('courses/students') >= 0
@courses = new CocoCollection([], { url: "/db/course", model: Course})
@supermodel.loadCollection(@courses, 'courses')
@ -39,6 +38,21 @@ module.exports = class CoursesView extends RootView
@studentMode = true
@courseEnroll(prepaidCode)
setUpHourOfCode: ->
# If we are coming in at /hoc, then we show the landing page.
# If we have ?hoc=true (for the step after the landing page), then we show any HoC-specific instructions.
# If we haven't tracked this player as an hourOfCode player yet, and it's a new account, we do that now.
@hocLandingPage = Backbone.history.getFragment()?.indexOf('hoc') >= 0
@hocMode = utils.getQueryVariable('hoc', false)
elapsed = new Date() - new Date(me.get('dateCreated'))
if not me.get('hourOfCode') and (@hocLandingPage or @hocMode) and elapsed < 5 * 60 * 1000
me.set('hourOfCode', true)
me.patch()
$('body').append($('<img src="https://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
application.tracker?.trackEvent 'Hour of Code Begin'
if me.get('hourOfCode') and elapsed < 24 * 60 * 60 * 1000
@hocMode = true # If they really just arrived, make sure we're still in hocMode even if they lost ?hoc=true.
getRenderData: ->
context = super()
context.courses = @courses.models ? []
@ -102,10 +116,12 @@ module.exports = class CoursesView extends RootView
navigationEvent = route: route, viewClass: viewClass, viewArgs: viewArgs
Backbone.Mediator.publish 'router:navigate', navigationEvent
onClickHocStudentContinue: (e) ->
onClickHOCStudentContinue: (e) ->
$('.continue-dialog').modal('hide')
return @openModalView new AuthModal() if me.isAnonymous()
courseID = $(e.target).data('course-id')
if e
courseID = $(e.target).data('course-id')
else
courseID = '560f1a9f22961295f9427742'
@state = 'enrolling'
@stateMessage = undefined
@ -117,6 +133,7 @@ module.exports = class CoursesView extends RootView
name: 'Single Player'
seats: 9999
courseID: courseID
hourOfCode: true
jqxhr = $.post('/db/course_instance/-/create', data)
jqxhr.done (data, textStatus, jqXHR) =>
application.tracker?.trackEvent 'Finished HoC student course creation', {courseID: courseID}
@ -145,6 +162,10 @@ module.exports = class CoursesView extends RootView
@render?()
onClickStudent: (e) ->
if @supermodel.finished() and @hocLandingPage
# Automatically enroll in first course
@onClickHOCStudentContinue()
return
route = "/courses/students"
route += "?hoc=true" if @hocLandingPage or @hocMode
viewClass = require 'views/courses/CoursesView'

View file

@ -106,6 +106,7 @@ module.exports = class CampaignView extends RootView
window.tracker?.trackEvent 'Loaded World Map', category: 'World Map', label: @terrain
# 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.
# TODO: get rid of all this sometime in November 2015 when code.org/learn updates to the new version for Hour of Code tutorials.
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="https://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))

View file

@ -189,11 +189,13 @@ module.exports = class HeroVictoryModal extends ModalView
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.
# TODO: get rid of said assumption sometime in November 2015 when code.org/learn updates to the new version for Hour of Code tutorials.
if isHourOfCode
# Show the Hour of Code "I'm Done" tracking pixel after they played for 20 minutes
enough = elapsed >= 20 * 60 * 1000
lastLevel = @level.get('slug') is 'course-kithgard-gates'
enough = elapsed >= 20 * 60 * 1000 or lastLevel
tooMuch = elapsed > 120 * 60 * 1000
showDone = elapsed >= 30 * 60 * 1000 and not tooMuch
showDone = (elapsed >= 30 * 60 * 1000 and not tooMuch) or lastLevel
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.

View file

@ -38,7 +38,8 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler
super arguments...
createAPI: (req, res) ->
return @sendUnauthorizedError(res) if not req.user? or req.user?.isAnonymous()
return @sendUnauthorizedError(res) if not req.user?
return @sendUnauthorizedError(res) if req.user.isAnonymous() and not (req.body.hourOfCode and req.body.courseID is '560f1a9f22961295f9427742')
# Required Input
seats = req.body.seats