codecombat/app/views/courses/CourseDetailsView.coffee
Matt Lott d72e4eb750 Practice levels Ux and next level algorithm
Update classroom and gameplay Ux to surface practice levels as 3a, 3b,
etc.
Update next level logic to leverage practice levels based on per level
completion playtime thresholds.
Patrol buster and patrol buster A are live for testing.
Fix a few classroom Ux progress hover bubble info bugs.

Closes #3767
2016-06-27 14:05:42 -07:00

159 lines
6.8 KiB
CoffeeScript

Course = require 'models/Course'
Courses = require 'collections/Courses'
LevelSessions = require 'collections/LevelSessions'
CourseInstance = require 'models/CourseInstance'
CourseInstances = require 'collections/CourseInstances'
Classroom = require 'models/Classroom'
Classrooms = require 'collections/Classrooms'
Levels = require 'collections/Levels'
RootView = require 'views/core/RootView'
template = require 'templates/courses/course-details'
User = require 'models/User'
storage = require 'core/storage'
module.exports = class CourseDetailsView extends RootView
id: 'course-details-view'
template: template
teacherMode: false
memberSort: 'nameAsc'
events:
'click .btn-play-level': 'onClickPlayLevel'
'click .btn-select-instance': 'onClickSelectInstance'
'submit #school-form': 'onSubmitSchoolForm'
constructor: (options, @courseID, @courseInstanceID) ->
super options
@ownedClassrooms = new Classrooms()
@courses = new Courses()
@course = new Course()
@levelSessions = new LevelSessions()
@courseInstance = new CourseInstance({_id: @courseInstanceID})
@owner = new User()
@classroom = new Classroom()
@levels = new Levels()
@courseInstances = new CourseInstances()
@supermodel.trackRequest @ownedClassrooms.fetchMine({data: {project: '_id'}})
@supermodel.trackRequest(@courses.fetch().then(=>
@course = @courses.get(@courseID)
))
sessionsLoaded = @supermodel.trackRequest(@levelSessions.fetchForCourseInstance(@courseInstanceID, {cache: false}))
@supermodel.trackRequest(@courseInstance.fetch().then(=>
return if @destroyed
@teacherMode = @courseInstance.get('ownerID') is me.id
@owner = new User({_id: @courseInstance.get('ownerID')})
@supermodel.trackRequest(@owner.fetch())
classroomID = @courseInstance.get('classroomID')
@classroom = new Classroom({ _id: classroomID })
@supermodel.trackRequest(@classroom.fetch())
levelsLoaded = @supermodel.trackRequest(@levels.fetchForClassroomAndCourse(classroomID, @courseID, {
data: { project: 'concepts,practice,type,slug,name,original,description' }
}))
@supermodel.trackRequest($.when(levelsLoaded, sessionsLoaded).then(=>
@buildSessionStats()
return if @destroyed
if @memberStats[me.id]?.totalLevelsCompleted >= @levels.size() - 1 # Don't need to complete arena
# need to figure out the next course instance
@courseComplete = true
@courseInstances.comparator = 'courseID'
# TODO: make this logic use locked course content to figure out the next course, then fetch the
# course instance for that
@supermodel.trackRequest(@courseInstances.fetchForClassroom(classroomID).then(=>
@nextCourseInstance = _.find @courseInstances.models, (ci) => ci.get('courseID') > @courseID
if @nextCourseInstance
nextCourseID = @nextCourseInstance.get('courseID')
@nextCourse = @courses.get(nextCourseID)
))
@promptForSchool = @courseComplete and not me.isAnonymous() and not me.get('schoolName') and not storage.load('no-school')
))
))
initialize: (options) ->
window.tracker?.trackEvent 'Students Class Course Loaded', category: 'Students', ['Mixpanel']
super(options)
buildSessionStats: ->
return if @destroyed
@levelConceptMap = {}
for level in @levels.models
@levelConceptMap[level.get('original')] ?= {}
for concept in level.get('concepts')
@levelConceptMap[level.get('original')][concept] = true
if level.get('type') is 'course-ladder'
@arenaLevel = level
# console.log 'onLevelSessionsSync'
@memberStats = {}
@userConceptStateMap = {}
@userLevelStateMap = {}
for levelSession in @levelSessions.models
continue if levelSession.skipMe # Don't track second arena session as another completed level
userID = levelSession.get('creator')
levelID = levelSession.get('level').original
state = if levelSession.get('state')?.complete then 'complete' else 'started'
playtime = parseInt(levelSession.get('playtime') ? 0, 10)
do (userID, levelID) =>
secondSessionForLevel = _.find(@levelSessions.models, ((otherSession) ->
otherSession.get('creator') is userID and otherSession.get('level').original is levelID and otherSession.id isnt levelSession.id
))
if secondSessionForLevel
state = 'complete' if secondSessionForLevel.get('state')?.complete
playtime = playtime + parseInt(secondSessionForLevel.get('playtime') ? 0, 10)
secondSessionForLevel.skipMe = true
@memberStats[userID] ?= totalLevelsCompleted: 0, totalPlayTime: 0
@memberStats[userID].totalLevelsCompleted++ if state is 'complete'
@memberStats[userID].totalPlayTime += playtime
@userConceptStateMap[userID] ?= {}
for concept of @levelConceptMap[levelID]
@userConceptStateMap[userID][concept] = state
@userLevelStateMap[userID] ?= {}
@userLevelStateMap[userID][levelID] = state
@conceptsCompleted = {}
for userID, conceptStateMap of @userConceptStateMap
for concept, state of conceptStateMap
@conceptsCompleted[concept] ?= 0
@conceptsCompleted[concept]++
onClickPlayLevel: (e) ->
levelSlug = $(e.target).closest('.btn-play-level').data('level-slug')
levelID = $(e.target).closest('.btn-play-level').data('level-id')
level = @levels.findWhere({original: levelID})
window.tracker?.trackEvent 'Students Class Course Play Level', category: 'Students', courseID: @courseID, courseInstanceID: @courseInstanceID, levelSlug: levelSlug, ['Mixpanel']
if level.get('type') is 'course-ladder'
viewClass = 'views/ladder/LadderView'
viewArgs = [{supermodel: @supermodel}, levelSlug]
route = '/play/ladder/' + levelSlug
route += '/course/' + @courseInstance.id
viewArgs = viewArgs.concat ['course', @courseInstance.id]
else
route = @getLevelURL levelSlug
viewClass = 'views/play/level/PlayLevelView'
viewArgs = [{courseID: @courseID, courseInstanceID: @courseInstanceID, supermodel: @supermodel}, levelSlug]
Backbone.Mediator.publish 'router:navigate', route: route, viewClass: viewClass, viewArgs: viewArgs
getLevelURL: (levelSlug) ->
"/play/level/#{levelSlug}?course=#{@courseID}&course-instance=#{@courseInstanceID}"
getOwnerName: ->
return if @owner.isNew()
if @owner.get('firstName') and @owner.get('lastName')
return "#{@owner.get('firstName')} #{@owner.get('lastName')}"
@owner.get('name') or @owner.get('email')
getLastLevelCompleted: ->
lastLevelCompleted = null
for levelID in @levels.pluck('original')
if @userLevelStateMap?[me.id]?[levelID] is 'complete'
lastLevelCompleted = levelID
return lastLevelCompleted