mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-13 09:11:22 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
152e468865
26 changed files with 508 additions and 151 deletions
|
@ -325,3 +325,48 @@ module.exports.capitalLanguages = capitalLanguages =
|
|||
'python': 'Python'
|
||||
'java': 'Java'
|
||||
'lua': 'Lua'
|
||||
|
||||
module.exports.createLevelNumberMap = (levels) ->
|
||||
levelNumberMap = {}
|
||||
practiceLevelTotalCount = 0
|
||||
practiceLevelCurrentCount = 0
|
||||
for level, i in levels
|
||||
levelNumber = i - practiceLevelTotalCount + 1
|
||||
if level.practice
|
||||
levelNumber = i - practiceLevelTotalCount + String.fromCharCode('a'.charCodeAt(0) + practiceLevelCurrentCount)
|
||||
practiceLevelTotalCount++
|
||||
practiceLevelCurrentCount++
|
||||
else
|
||||
practiceLevelCurrentCount = 0
|
||||
levelNumberMap[level.key] = levelNumber
|
||||
levelNumberMap
|
||||
|
||||
module.exports.findNextLevel = (levels, currentIndex, needsPractice) ->
|
||||
# levels = [{practice: true/false, complete: true/false}]
|
||||
index = currentIndex
|
||||
index++
|
||||
if needsPractice
|
||||
if levels[currentIndex].practice or index < levels.length and levels[index].practice
|
||||
# Needs practice, on practice or next practice, choose next incomplete level
|
||||
# May leave earlier practice levels incomplete and reach end of course
|
||||
index++ while index < levels.length and levels[index].complete
|
||||
else
|
||||
# Needs practice, on required, next required, choose first incomplete level of previous practice chain
|
||||
index--
|
||||
index-- while index >= 0 and not levels[index].practice
|
||||
if index >= 0
|
||||
index-- while index >= 0 and levels[index].practice
|
||||
if index >= 0
|
||||
index++
|
||||
index++ while index < levels.length and levels[index].practice and levels[index].complete
|
||||
if levels[index].practice and not levels[index].complete
|
||||
return index
|
||||
index = currentIndex + 1
|
||||
index++ while index < levels.length and levels[index].complete
|
||||
else
|
||||
# No practice needed, next required incomplete level
|
||||
index++ while index < levels.length and (levels[index].practice or levels[index].complete)
|
||||
index
|
||||
|
||||
module.exports.needsPractice = (playtime=0, threshold=2) ->
|
||||
playtime / 60 > threshold
|
||||
|
|
|
@ -295,6 +295,7 @@
|
|||
saving: "Saving..."
|
||||
sending: "Sending..."
|
||||
send: "Send"
|
||||
type: "Type"
|
||||
cancel: "Cancel"
|
||||
save: "Save"
|
||||
publish: "Publish"
|
||||
|
|
|
@ -3,6 +3,7 @@ schema = require 'schemas/models/campaign.schema'
|
|||
Level = require 'models/Level'
|
||||
Levels = require 'collections/Levels'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
utils = require '../core/utils'
|
||||
|
||||
module.exports = class Campaign extends CocoModel
|
||||
@className: 'Campaign'
|
||||
|
@ -23,3 +24,11 @@ module.exports = class Campaign extends CocoModel
|
|||
levels.comparator = 'campaignIndex'
|
||||
levels.sort()
|
||||
return levels
|
||||
|
||||
getLevelNumber: (levelID, defaultNumber) ->
|
||||
unless @levelNumberMap
|
||||
levels = []
|
||||
for level in @getLevels().models when level.get('original')
|
||||
levels.push({key: level.get('original'), practice: level.get('practice') ? false})
|
||||
@levelNumberMap = utils.createLevelNumberMap(levels)
|
||||
@levelNumberMap[levelID] ? defaultNumber
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
CocoModel = require './CocoModel'
|
||||
schema = require 'schemas/models/classroom.schema'
|
||||
utils = require 'core/utils'
|
||||
utils = require '../core/utils'
|
||||
User = require 'models/User'
|
||||
|
||||
module.exports = class Classroom extends CocoModel
|
||||
|
@ -44,6 +44,16 @@ module.exports = class Classroom extends CocoModel
|
|||
_.extend options, opts
|
||||
@fetch(options)
|
||||
|
||||
getLevelNumber: (levelID, defaultNumber) ->
|
||||
unless @levelNumberMap
|
||||
@levelNumberMap = {}
|
||||
for course in @get('courses') ? []
|
||||
levels = []
|
||||
for level in course.levels when level.original
|
||||
levels.push({key: level.original, practice: level.practice ? false})
|
||||
_.assign(@levelNumberMap, utils.createLevelNumberMap(levels))
|
||||
@levelNumberMap[levelID] ? defaultNumber
|
||||
|
||||
removeMember: (userID, opts) ->
|
||||
options = {
|
||||
url: _.result(@, 'url') + '/members'
|
||||
|
@ -88,30 +98,59 @@ module.exports = class Classroom extends CocoModel
|
|||
|
||||
statsForSessions: (sessions, courseID) ->
|
||||
return null unless sessions
|
||||
stats = {}
|
||||
sessions = sessions.models or sessions
|
||||
arena = @getLadderLevel(courseID)
|
||||
levels = @getLevels({courseID: courseID, withoutLadderLevels: true})
|
||||
levelOriginals = levels.pluck('original')
|
||||
completeSessionOriginals = (session.get('level').original for session in sessions when session.get('state').complete)
|
||||
incompleteSessionOriginals = (session.get('level').original for session in sessions when not session.get('state').complete)
|
||||
levelsLeft = _.size(_.difference(levelOriginals, completeSessionOriginals))
|
||||
next = _.find levels.models, (level) -> level.get('original') not in completeSessionOriginals
|
||||
lastPlayed = _.find levels.models, (level) -> level.get('original') in incompleteSessionOriginals
|
||||
stats.levels = {
|
||||
size: levels.size()
|
||||
left: levelsLeft
|
||||
done: levelsLeft is 0
|
||||
numDone: levels.size() - levelsLeft
|
||||
pctDone: (100 * (levels.size() - levelsLeft) / levels.size()).toFixed(1) + '%'
|
||||
lastPlayed: lastPlayed
|
||||
next: next
|
||||
first: levels.first()
|
||||
arena: arena
|
||||
}
|
||||
sum = (nums) -> _.reduce(nums, (s, num) -> s + num) or 0
|
||||
stats.playtime = sum((session.get('playtime') or 0 for session in sessions))
|
||||
return stats
|
||||
courseLevels = @getLevels({courseID: courseID, withoutLadderLevels: true})
|
||||
levelSessionMap = {}
|
||||
levelSessionMap[session.get('level').original] = session for session in sessions
|
||||
currentIndex = -1
|
||||
lastStarted = null
|
||||
levelsTotal = 0
|
||||
levelsLeft = 0
|
||||
lastPlayed = null
|
||||
playtime = 0
|
||||
levels = []
|
||||
for level, index in courseLevels.models
|
||||
levelsTotal++ unless level.get('practice')
|
||||
complete = false
|
||||
if session = levelSessionMap[level.get('original')]
|
||||
complete = session.get('state').complete ? false
|
||||
playtime += session.get('playtime') ? 0
|
||||
lastPlayed = level
|
||||
if complete
|
||||
currentIndex = index
|
||||
else
|
||||
lastStarted = level
|
||||
levelsLeft++ unless level.get('practice')
|
||||
else if not level.get('practice')
|
||||
levelsLeft++
|
||||
levels.push
|
||||
practice: level.get('practice') ? false
|
||||
complete: complete
|
||||
lastPlayed = lastStarted ? lastPlayed
|
||||
needsPractice = false
|
||||
nextIndex = 0
|
||||
if currentIndex >= 0
|
||||
currentLevel = courseLevels.models[currentIndex]
|
||||
currentPlaytime = levelSessionMap[currentLevel.get('original')]?.get('playtime') ? 0
|
||||
needsPractice = utils.needsPractice(currentPlaytime, currentLevel.get('practiceThresholdMinutes'))
|
||||
nextIndex = utils.findNextLevel(levels, currentIndex, needsPractice)
|
||||
nextLevel = courseLevels.models[nextIndex]
|
||||
nextLevel ?= _.find courseLevels.models, (level) -> not levelSessionMap[level.get('original')]?.get('state')?.complete
|
||||
|
||||
stats =
|
||||
levels:
|
||||
size: levelsTotal
|
||||
left: levelsLeft
|
||||
done: levelsLeft is 0
|
||||
numDone: levelsTotal - levelsLeft
|
||||
pctDone: (100 * (levelsTotal - levelsLeft) / levelsTotal).toFixed(1) + '%'
|
||||
lastPlayed: lastPlayed
|
||||
next: nextLevel
|
||||
first: courseLevels.first()
|
||||
arena: arena
|
||||
playtime: playtime
|
||||
stats
|
||||
|
||||
fetchForCourseInstance: (courseInstanceID, options={}) ->
|
||||
return unless courseInstanceID
|
||||
|
|
|
@ -262,9 +262,9 @@ module.exports = class Level extends CocoModel
|
|||
isLadder: ->
|
||||
return @get('type')?.indexOf('ladder') > -1
|
||||
|
||||
fetchNextForCourse: ({ levelOriginalID, courseInstanceID, courseID }, options={}) ->
|
||||
fetchNextForCourse: ({ levelOriginalID, courseInstanceID, courseID, sessionID }, options={}) ->
|
||||
if courseInstanceID
|
||||
options.url = "/db/course_instance/#{courseInstanceID}/levels/#{levelOriginalID}/next"
|
||||
options.url = "/db/course_instance/#{courseInstanceID}/levels/#{levelOriginalID}/sessions/#{sessionID}/next"
|
||||
else
|
||||
options.url = "/db/course/#{courseID}/levels/#{levelOriginalID}/next"
|
||||
@fetch(options)
|
||||
|
|
|
@ -66,6 +66,7 @@ _.extend CampaignSchema.properties, {
|
|||
original: { type: 'string', format: 'hidden' }
|
||||
adventurer: { type: 'boolean' }
|
||||
practice: { type: 'boolean' }
|
||||
practiceThresholdMinutes: {type: 'number'}
|
||||
adminOnly: { type: 'boolean' }
|
||||
disableSpaces: { type: ['boolean','number'] }
|
||||
hidesSubmitUntilRun: { type: 'boolean' }
|
||||
|
|
|
@ -23,6 +23,8 @@ _.extend ClassroomSchema.properties,
|
|||
courses: c.array { title: 'Courses' }, c.object { title: 'Course' }, {
|
||||
_id: c.objectId()
|
||||
levels: c.array { title: 'Levels' }, c.object { title: 'Level' }, {
|
||||
practice: {type: 'boolean'}
|
||||
practiceThresholdMinutes: {type: 'number'}
|
||||
type: c.shortString()
|
||||
original: c.objectId()
|
||||
name: {type: 'string'}
|
||||
|
|
|
@ -32,10 +32,6 @@ block content
|
|||
td(data-i18n="courses.total_students")
|
||||
td
|
||||
span.spr= _.size(view.classroom.get('members'))
|
||||
span (
|
||||
span.spr(data-i18n="courses.enrolled_courses")
|
||||
span= stats.enrolledUsers
|
||||
span )
|
||||
tr
|
||||
td(data-i18n="courses.average_time")
|
||||
td= stats.averagePlaytime
|
||||
|
@ -108,16 +104,17 @@ block content
|
|||
.progress
|
||||
each trimModel in levels.models
|
||||
- var level = view.levels.get(trimModel.get('original')); // get the level loaded through the db
|
||||
- var levelNumber = view.classroom.getLevelNumber(level.get('original'), i + 1)
|
||||
- i++
|
||||
- var session = sessionMap[level.get('original')];
|
||||
a(href=view.getLevelURL(level, course, courseInstance, session))
|
||||
- var content = view.levelPopoverContent(level, session, i);
|
||||
- var content = view.levelPopoverContent(level, session, levelNumber);
|
||||
if session && session.get('state') && session.get('state').complete
|
||||
.progress-bar.progress-bar-complete(style=css, data-content=content, data-toggle='popover')= i
|
||||
.progress-bar.progress-bar-complete(style=css, data-content=content, data-toggle='popover')= levelNumber
|
||||
else if session
|
||||
.progress-bar.progress-bar-started(style=css, data-content=content, data-toggle='popover')= i
|
||||
.progress-bar.progress-bar-started(style=css, data-content=content, data-toggle='popover')= levelNumber
|
||||
else
|
||||
.progress-bar.progress-bar-default(style=css, data-content=content, data-toggle='popover')= i
|
||||
.progress-bar.progress-bar-default(style=css, data-content=content, data-toggle='popover')= levelNumber
|
||||
else if paidFor
|
||||
.text-center
|
||||
button.enable-btn.btn.btn-info.btn-sm.text-uppercase(data-user-id=user.id, data-course-instance-cid=courseInstance.cid)
|
||||
|
|
|
@ -88,6 +88,7 @@ block content
|
|||
tr
|
||||
th
|
||||
th(data-i18n="clans.status")
|
||||
th(data-i18n="common.type")
|
||||
th(data-i18n="resources.level")
|
||||
th(data-i18n="courses.concepts")
|
||||
tbody
|
||||
|
@ -97,6 +98,7 @@ block content
|
|||
- var levelCount = 0;
|
||||
each level in view.levels.models
|
||||
- var levelStatus = null;
|
||||
- var levelNumber = view.classroom.getLevelNumber(level.get('original'), ++levelCount);
|
||||
if view.userLevelStateMap[me.id]
|
||||
- levelStatus = view.userLevelStateMap[me.id][level.get('original')]
|
||||
tr
|
||||
|
@ -107,10 +109,8 @@ block content
|
|||
td
|
||||
if view.userLevelStateMap[me.id]
|
||||
div= view.userLevelStateMap[me.id][level.get('original')]
|
||||
- previousLevelCompleted = view.userLevelStateMap[me.id][level.get('original')] === 'complete'
|
||||
else
|
||||
- previousLevelCompleted = false
|
||||
td= ++levelCount + '. ' + level.get('name').replace('Course: ', '')
|
||||
td #{level.get('practice') ? 'practice' : 'required'}
|
||||
td #{levelNumber}. #{level.get('name').replace('Course: ', '')}
|
||||
td
|
||||
if view.levelConceptMap[level.get('original')]
|
||||
each concept in view.course.get('concepts')
|
||||
|
@ -118,3 +118,8 @@ block content
|
|||
span.spr.concept(data-i18n="concepts." + concept)
|
||||
if level.get('original') === lastLevelCompleted
|
||||
- passedLastCompletedLevel = true
|
||||
if !level.get('practice')
|
||||
if view.userLevelStateMap[me.id]
|
||||
- previousLevelCompleted = view.userLevelStateMap[me.id][level.get('original')] === 'complete'
|
||||
else
|
||||
- previousLevelCompleted = false
|
||||
|
|
|
@ -309,7 +309,8 @@ mixin courseOverview
|
|||
.course-overview-progress
|
||||
each level, index in levels
|
||||
- var progress = state.get('progressData').get({ classroom: view.classroom, course: course, level: level })
|
||||
+allStudentsLevelProgressDot(progress, level, index+1)
|
||||
- var levelNumber = view.classroom.getLevelNumber(level.get('original'), index + 1)
|
||||
+allStudentsLevelProgressDot(progress, level, levelNumber)
|
||||
|
||||
mixin studentLevelsRow(student)
|
||||
.student-levels-row.alternating-background
|
||||
|
@ -321,7 +322,8 @@ mixin studentLevelsRow(student)
|
|||
- var levels = view.classroom.getLevels({courseID: course.id}).models
|
||||
each level, index in levels
|
||||
- var progress = state.get('progressData').get({ classroom: view.classroom, course: course, level: level, user: student })
|
||||
+studentLevelProgressDot(progress, level, index+1, session)
|
||||
- var levelNumber = view.classroom.getLevelNumber(level.get('original'), index + 1)
|
||||
+studentLevelProgressDot(progress, level, levelNumber, session)
|
||||
|
||||
mixin studentCourseProgressDot(progress, levelsTotal, level, label)
|
||||
//- TODO: Refactor with TeacherClassesView jade
|
||||
|
|
|
@ -56,12 +56,12 @@ block content
|
|||
| :
|
||||
select.level-select.form-control
|
||||
if view.campaigns.loaded
|
||||
each level, levelIndex in view.campaigns.get(course.get('campaignID')).getLevels().models
|
||||
if level.get('practice')
|
||||
- continue;
|
||||
- var campaign = view.campaigns.get(course.get('campaignID'))
|
||||
each level, levelIndex in campaign.getLevels().models
|
||||
- var levelNumber = campaign.getLevelNumber(level.get('original'), levelIndex + 1)
|
||||
option(value=level.get('slug'))
|
||||
span
|
||||
= levelIndex + 1
|
||||
= levelNumber
|
||||
span.spr
|
||||
| .
|
||||
span
|
||||
|
|
|
@ -22,7 +22,7 @@ else
|
|||
.level-name-area
|
||||
.level-label(data-i18n="play_level.level")
|
||||
.level-name(title=difficultyTitle || "")
|
||||
span= (campaignIndex ? campaignIndex + '. ' : '') + worldName.replace('Course: ', '')
|
||||
span #{view.levelNumber ? view.levelNumber + '. ' : ''}#{worldName.replace('Course: ', '')}
|
||||
if levelDifficulty
|
||||
sup.level-difficulty= levelDifficulty
|
||||
|
||||
|
|
|
@ -17,5 +17,9 @@ if started
|
|||
//- .small-details
|
||||
//- i(data-i18n='teacher.click_to_view_progress')
|
||||
else
|
||||
.small-details.nowrap
|
||||
span= levelNumber
|
||||
span.spr .
|
||||
span= levelName
|
||||
span.small-details.nowrap(data-i18n='teacher.no_progress')
|
||||
| No progress
|
||||
|
|
|
@ -13,7 +13,7 @@ if completed
|
|||
.small-details.nowrap
|
||||
span.spr(data-i18n='teacher.completed')
|
||||
| Completed
|
||||
span= new Date(dateFirstCompleted).toLocaleString()
|
||||
span= new Date(session.get('dateFirstCompleted')).toLocaleString()
|
||||
+timePlayed
|
||||
//- .small-details
|
||||
//- i(data-i18n='teacher.click_to_view_solution')
|
||||
|
@ -26,11 +26,15 @@ else if started
|
|||
.small-details.nowrap
|
||||
span.spr(data-i18n='teacher.last_played')
|
||||
| Last played
|
||||
span= new Date(lastPlayed).toLocaleString()
|
||||
span= new Date(session.get('changed')).toLocaleString()
|
||||
+timePlayed
|
||||
//- .small-details
|
||||
//- i(data-i18n='teacher.click_to_view_progress')
|
||||
//- | click to view progress
|
||||
else
|
||||
.small-details.nowrap
|
||||
span= levelNumber
|
||||
span.spr .
|
||||
span= levelName
|
||||
span.small-details.nowrap(data-i18n='teacher.no_progress')
|
||||
| No progress
|
||||
|
|
|
@ -55,7 +55,7 @@ module.exports = class ClassroomView extends RootView
|
|||
@ownedClassrooms.fetchMine({data: {project: '_id'}})
|
||||
@supermodel.trackCollection(@ownedClassrooms)
|
||||
@levels = new Levels()
|
||||
@levels.fetchForClassroom(classroomID, {data: {project: 'name,slug,original'}})
|
||||
@levels.fetchForClassroom(classroomID, {data: {project: 'name,original,practice,slug'}})
|
||||
@levels.on 'add', (model) -> @_byId[model.get('original')] = model # so you can 'get' them
|
||||
@supermodel.trackCollection(@levels)
|
||||
window.tracker?.trackEvent 'Students Class Loaded', category: 'Students', classroomID: classroomID, ['Mixpanel']
|
||||
|
@ -177,7 +177,9 @@ module.exports = class ClassroomView extends RootView
|
|||
stats.averagePlaytime = if playtime and total then moment.duration(playtime / total, "seconds").humanize() else 0
|
||||
stats.totalPlaytime = if playtime then moment.duration(playtime, "seconds").humanize() else 0
|
||||
|
||||
completeSessions = @sessions.filter (s) -> s.get('state')?.complete
|
||||
levelPracticeMap = {}
|
||||
levelPracticeMap[level.id] = level.get('practice') ? false for level in @levels.models
|
||||
completeSessions = @sessions.filter (s) -> s.get('state')?.complete and not levelPracticeMap[s.get('levelID')]
|
||||
stats.averageLevelsComplete = if @users.size() then (_.size(completeSessions) / @users.size()).toFixed(1) else 'N/A' # '
|
||||
stats.totalLevelsComplete = _.size(completeSessions)
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ module.exports = class CourseDetailsView extends RootView
|
|||
@supermodel.trackRequest(@classroom.fetch())
|
||||
|
||||
levelsLoaded = @supermodel.trackRequest(@levels.fetchForClassroomAndCourse(classroomID, @courseID, {
|
||||
data: { project: 'concepts,type,slug,name,original,description' }
|
||||
data: { project: 'concepts,practice,type,slug,name,original,description' }
|
||||
}))
|
||||
|
||||
@supermodel.trackRequest($.when(levelsLoaded, sessionsLoaded).then(=>
|
||||
|
|
|
@ -118,7 +118,7 @@ module.exports = class TeacherClassView extends RootView
|
|||
@supermodel.trackRequest @courseInstances.fetchForClassroom(classroomID)
|
||||
|
||||
@levels = new Levels()
|
||||
@supermodel.trackRequest @levels.fetchForClassroom(classroomID, {data: {project: 'original,concepts'}})
|
||||
@supermodel.trackRequest @levels.fetchForClassroom(classroomID, {data: {project: 'original,concepts,practice'}})
|
||||
|
||||
@attachMediatorEvents()
|
||||
window.tracker?.trackEvent 'Teachers Class Loaded', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
|
||||
|
@ -449,7 +449,9 @@ module.exports = class TeacherClassView extends RootView
|
|||
stats.totalPlaytime = if playtime then moment.duration(playtime, "seconds").humanize() else 0
|
||||
# TODO: Humanize differently ('1 hour' instead of 'an hour')
|
||||
|
||||
completeSessions = @classroom.sessions.filter (s) -> s.get('state')?.complete
|
||||
levelPracticeMap = {}
|
||||
levelPracticeMap[level.id] = level.get('practice') ? false for level in @levels.models
|
||||
completeSessions = @classroom.sessions.filter (s) -> s.get('state')?.complete and not levelPracticeMap[s.get('levelID')]
|
||||
stats.averageLevelsComplete = if @students.size() then (_.size(completeSessions) / @students.size()).toFixed(1) else 'N/A' # '
|
||||
stats.totalLevelsComplete = _.size(completeSessions)
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ CocoView = require 'views/core/CocoView'
|
|||
template = require 'templates/play/level/control_bar'
|
||||
{me} = require 'core/auth'
|
||||
|
||||
Campaign = require 'models/Campaign'
|
||||
Classroom = require 'models/Classroom'
|
||||
Course = require 'models/Course'
|
||||
CourseInstance = require 'models/CourseInstance'
|
||||
GameMenuModal = require 'views/play/menu/GameMenuModal'
|
||||
RealTimeModel = require 'models/RealTimeModel'
|
||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
||||
|
@ -28,6 +32,7 @@ module.exports = class ControlBarView extends CocoView
|
|||
'click #control-bar-sign-up-button': 'onClickSignupButton'
|
||||
|
||||
constructor: (options) ->
|
||||
@supermodel = options.supermodel
|
||||
@courseID = options.courseID
|
||||
@courseInstanceID = options.courseInstanceID
|
||||
|
||||
|
@ -38,6 +43,26 @@ module.exports = class ControlBarView extends CocoView
|
|||
@levelID = @levelSlug or @level.id
|
||||
@spectateGame = options.spectateGame ? false
|
||||
@observing = options.session.get('creator') isnt me.id
|
||||
|
||||
@levelNumber = ''
|
||||
if @level.get('type') is 'course' and @level.get('campaignIndex')?
|
||||
@levelNumber = @level.get('campaignIndex') + 1
|
||||
if @courseInstanceID
|
||||
@courseInstance = new CourseInstance(_id: @courseInstanceID)
|
||||
jqxhr = @courseInstance.fetch()
|
||||
@supermodel.trackRequest(jqxhr)
|
||||
new Promise(jqxhr.then).then(=>
|
||||
@classroom = new Classroom(_id: @courseInstance.get('classroomID'))
|
||||
@supermodel.trackRequest @classroom.fetch()
|
||||
)
|
||||
else if @courseID
|
||||
@course = new Course(_id: @courseID)
|
||||
jqxhr = @course.fetch()
|
||||
@supermodel.trackRequest(jqxhr)
|
||||
new Promise(jqxhr.then).then(=>
|
||||
@campaign = new Campaign(_id: @course.get('campaignID'))
|
||||
@supermodel.trackRequest(@campaign.fetch())
|
||||
)
|
||||
super options
|
||||
if @level.get('type') in ['hero-ladder', 'course-ladder'] and me.isAdmin()
|
||||
@isMultiplayerLevel = true
|
||||
|
@ -45,6 +70,13 @@ module.exports = class ControlBarView extends CocoView
|
|||
if @level.get 'replayable'
|
||||
@listenTo @session, 'change-difficulty', @onSessionDifficultyChanged
|
||||
|
||||
onLoaded: ->
|
||||
if @classroom
|
||||
@levelNumber = @classroom.getLevelNumber(@level.get('original'), @levelNumber)
|
||||
else if @campaign
|
||||
@levelNumber = @campaign.getLevelNumber(@level.get('original'), @levelNumber)
|
||||
super()
|
||||
|
||||
setBus: (@bus) ->
|
||||
|
||||
onPlayerStatesChanged: (e) ->
|
||||
|
@ -62,7 +94,6 @@ module.exports = class ControlBarView extends CocoView
|
|||
getRenderData: (c={}) ->
|
||||
super c
|
||||
c.worldName = @worldName
|
||||
c.campaignIndex = @level.get('campaignIndex') + 1 if @level.get('type') is 'course' and @level.get('campaignIndex')? # TODO: support 'game-dev' levels in courses
|
||||
c.multiplayerEnabled = @session.get('multiplayer')
|
||||
c.ladderGame = @level.get('type') in ['ladder', 'hero-ladder', 'course-ladder']
|
||||
if c.isMultiplayerLevel = @isMultiplayerLevel
|
||||
|
|
|
@ -43,7 +43,12 @@ module.exports = class CourseVictoryModal extends ModalView
|
|||
|
||||
@playSound 'victory'
|
||||
@nextLevel = new Level()
|
||||
@nextLevelRequest = @supermodel.trackRequest @nextLevel.fetchNextForCourse({ levelOriginalID: @level.get('original'), @courseInstanceID, @courseID })
|
||||
@nextLevelRequest = @supermodel.trackRequest(@nextLevel.fetchNextForCourse({
|
||||
levelOriginalID: @level.get('original')
|
||||
@courseInstanceID
|
||||
@courseID
|
||||
sessionID: @session.id
|
||||
}))
|
||||
|
||||
@course = options.course
|
||||
if @courseID and not @course
|
||||
|
|
|
@ -151,12 +151,10 @@ module.exports =
|
|||
courseData = { _id: course._id, levels: [] }
|
||||
campaign = campaignMap[course.get('campaignID').toString()]
|
||||
levels = _.values(campaign.get('levels'))
|
||||
# TODO: remove practice filter after classroom Ux supports practice levels
|
||||
levels = _.reject(levels, {'practice': true})
|
||||
levels = _.sortBy(levels, 'campaignIndex')
|
||||
for level in levels
|
||||
levelData = { original: mongoose.Types.ObjectId(level.original) }
|
||||
_.extend(levelData, _.pick(level, 'type', 'slug', 'name'))
|
||||
_.extend(levelData, _.pick(level, 'type', 'slug', 'name', 'practice', 'practiceThresholdMinutes'))
|
||||
courseData.levels.push(levelData)
|
||||
coursesData.push(courseData)
|
||||
classroom.set('courses', coursesData)
|
||||
|
|
|
@ -9,8 +9,10 @@ Classroom = require '../models/Classroom'
|
|||
Course = require '../models/Course'
|
||||
User = require '../models/User'
|
||||
Level = require '../models/Level'
|
||||
LevelSession = require '../models/LevelSession'
|
||||
parse = require '../commons/parse'
|
||||
{objectIdFromTimestamp} = require '../lib/utils'
|
||||
utils = require '../../app/core/utils'
|
||||
Prepaid = require '../models/Prepaid'
|
||||
|
||||
module.exports =
|
||||
|
@ -71,35 +73,46 @@ module.exports =
|
|||
|
||||
fetchNextLevel: wrap (req, res) ->
|
||||
levelOriginal = req.params.levelOriginal
|
||||
if not database.isID(levelOriginal)
|
||||
throw new errors.UnprocessableEntity('Invalid level original ObjectId')
|
||||
|
||||
unless database.isID(levelOriginal) then throw new errors.UnprocessableEntity('Invalid level original ObjectId')
|
||||
sessionID = req.params.sessionID
|
||||
unless database.isID(sessionID) then throw new errors.UnprocessableEntity('Invalid session ObjectId')
|
||||
courseInstance = yield database.getDocFromHandle(req, CourseInstance)
|
||||
if not courseInstance
|
||||
throw new errors.NotFound('Course Instance not found.')
|
||||
courseID = courseInstance.get('courseID')
|
||||
|
||||
unless courseInstance then throw new errors.NotFound('Course Instance not found.')
|
||||
classroom = yield Classroom.findById courseInstance.get('classroomID')
|
||||
if not classroom
|
||||
throw new errors.NotFound('Classroom not found.')
|
||||
unless classroom then throw new errors.NotFound('Classroom not found.')
|
||||
currentLevel = yield Level.findOne({original: mongoose.Types.ObjectId(levelOriginal)}, {practiceThresholdMinutes: 1, type: 1})
|
||||
unless currentLevel then throw new errors.NotFound('Current level not found.')
|
||||
|
||||
nextLevelOriginal = null
|
||||
foundLevelOriginal = false
|
||||
for course in classroom.get('courses') or []
|
||||
if not courseID.equals(course._id)
|
||||
continue
|
||||
for level, index in course.levels
|
||||
if level.original.toString() is levelOriginal
|
||||
foundLevelOriginal = true
|
||||
nextLevelOriginal = course.levels[index+1]?.original
|
||||
break
|
||||
courseID = courseInstance.get('courseID')
|
||||
courseLevels = []
|
||||
courseLevels = course.levels for course in classroom.get('courses') or [] when courseID.equals(course._id)
|
||||
|
||||
if not foundLevelOriginal
|
||||
throw new errors.NotFound('Level original ObjectId not found in Classroom courses')
|
||||
# Get level completions and playtime
|
||||
currentLevelSession = null
|
||||
levelIDs = (level.original.toString() for level in courseLevels)
|
||||
query = {$and: [{creator: req.user.id}, {'level.original': {$in: levelIDs}}]}
|
||||
levelSessions = yield LevelSession.find(query, {level: 1, playtime: 1, state: 1})
|
||||
levelCompleteMap = {}
|
||||
for levelSession in levelSessions
|
||||
currentLevelSession = levelSession if levelSession.id is sessionID
|
||||
levelCompleteMap[levelSession.get('level')?.original] = levelSession.get('state')?.complete
|
||||
unless currentLevelSession then throw new errors.NotFound('Level session not found.')
|
||||
needsPractice = utils.needsPractice(currentLevelSession.get('playtime'), currentLevel.get('practiceThresholdMinutes'))
|
||||
|
||||
if not nextLevelOriginal
|
||||
return res.status(200).send({})
|
||||
# Find next level
|
||||
levels = []
|
||||
currentIndex = -1
|
||||
for level, index in courseLevels
|
||||
currentIndex = index if level.original.toString() is levelOriginal
|
||||
levels.push
|
||||
practice: level.practice ? false
|
||||
complete: levelCompleteMap[level.original?.toString()] or currentIndex is index
|
||||
unless currentIndex >=0 then throw new errors.NotFound('Level original ObjectId not found in Classroom courses')
|
||||
nextLevelIndex = utils.findNextLevel(levels, currentIndex, needsPractice)
|
||||
nextLevelOriginal = courseLevels[nextLevelIndex]?.original
|
||||
unless nextLevelOriginal then return res.status(200).send({})
|
||||
|
||||
# Return full Level object
|
||||
dbq = Level.findOne({original: mongoose.Types.ObjectId(nextLevelOriginal)})
|
||||
dbq.sort({ 'version.major': -1, 'version.minor': -1 })
|
||||
dbq.select(parse.getProjectFromReq(req))
|
||||
|
@ -107,7 +120,6 @@ module.exports =
|
|||
level = level.toObject({req: req})
|
||||
res.status(200).send(level)
|
||||
|
||||
|
||||
fetchClassroom: wrap (req, res) ->
|
||||
courseInstance = yield database.getDocFromHandle(req, CourseInstance)
|
||||
if not courseInstance
|
||||
|
|
|
@ -55,6 +55,7 @@ module.exports =
|
|||
classroomID = courseInstance.get('classroomID')
|
||||
continue unless classroomID
|
||||
classroom = classroomMap[classroomID.toString()]
|
||||
continue unless classroom
|
||||
courseID = courseInstance.get('courseID')
|
||||
classroomCourse = _.find(classroom.get('courses'), (c) -> c._id.equals(courseID))
|
||||
for courseLevel in classroomCourse.levels
|
||||
|
|
|
@ -81,7 +81,7 @@ module.exports.setup = (app) ->
|
|||
app.get('/db/course/:handle/levels/:levelOriginal/next', mw.courses.fetchNextLevel)
|
||||
|
||||
app.post('/db/course_instance/-/recent', mw.auth.checkHasPermission(['admin']), mw.courseInstances.fetchRecent)
|
||||
app.get('/db/course_instance/:handle/levels/:levelOriginal/next', mw.courseInstances.fetchNextLevel)
|
||||
app.get('/db/course_instance/:handle/levels/:levelOriginal/sessions/:sessionID/next', mw.courseInstances.fetchNextLevel)
|
||||
app.post('/db/course_instance/:handle/members', mw.auth.checkLoggedIn(), mw.courseInstances.addMembers)
|
||||
app.get('/db/course_instance/:handle/classroom', mw.auth.checkLoggedIn(), mw.courseInstances.fetchClassroom)
|
||||
|
||||
|
|
|
@ -144,16 +144,6 @@ describe 'POST /db/classroom', ->
|
|||
expect(classroom.get('courses')[0].levels[0].name).toBe('Level A')
|
||||
done()
|
||||
|
||||
it 'makes a copy of the list of all non-practice levels in all courses', utils.wrap (done) ->
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
data = { name: 'tmp Classroom 2' }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
classroom = yield Classroom.findById(res.body._id)
|
||||
# console.log(JSON.stringify(classroom.get('courses')[0], null, 2));
|
||||
expect(classroom.get('courses')[0].levels.length).toEqual(2)
|
||||
done()
|
||||
|
||||
describe 'GET /db/classroom/:handle/levels', ->
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
|
|
|
@ -9,6 +9,7 @@ User = require '../../../server/models/User'
|
|||
Classroom = require '../../../server/models/Classroom'
|
||||
Campaign = require '../../../server/models/Campaign'
|
||||
Level = require '../../../server/models/Level'
|
||||
LevelSession = require '../../../server/models/LevelSession'
|
||||
Prepaid = require '../../../server/models/Prepaid'
|
||||
request = require '../request'
|
||||
moment = require 'moment'
|
||||
|
@ -246,6 +247,7 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
|||
yield utils.clearModels [User, Classroom, Course, Level, Campaign]
|
||||
admin = yield utils.initAdmin()
|
||||
yield utils.loginUser(admin)
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
|
||||
levelJSON = { name: 'A', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
||||
|
@ -253,12 +255,25 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
|||
@levelA = yield Level.findById(res.body._id)
|
||||
paredLevelA = _.pick(res.body, 'name', 'original', 'type')
|
||||
|
||||
@sessionA = new LevelSession
|
||||
creator: teacher.id
|
||||
level: original: @levelA.get('original').toString()
|
||||
permissions: simplePermissions
|
||||
state: complete: true
|
||||
yield @sessionA.save()
|
||||
|
||||
levelJSON = { name: 'B', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
||||
expect(res.statusCode).toBe(200)
|
||||
@levelB = yield Level.findById(res.body._id)
|
||||
paredLevelB = _.pick(res.body, 'name', 'original', 'type')
|
||||
|
||||
@sessionB = new LevelSession
|
||||
creator: teacher.id
|
||||
level: original: @levelB.get('original').toString()
|
||||
permissions: simplePermissions
|
||||
yield @sessionB.save()
|
||||
|
||||
levelJSON = { name: 'C', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
||||
expect(res.statusCode).toBe(200)
|
||||
|
@ -282,7 +297,6 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
|||
@courseB = Course({name: 'Course B', campaignID: @campaignB._id})
|
||||
yield @courseB.save()
|
||||
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
data = { name: 'Classroom 1' }
|
||||
classroomsURL = getURL('/db/classroom')
|
||||
|
@ -305,19 +319,19 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
|||
done()
|
||||
|
||||
it 'returns the next level for the course in the linked classroom', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceA.id}/levels/#{@levelA.id}/next"), json: true }
|
||||
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceA.id}/levels/#{@levelA.id}/sessions/#{@sessionA.id}/next"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.body.original).toBe(@levelB.original.toString())
|
||||
done()
|
||||
|
||||
it 'returns empty object if the given level is the last level in its course', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceA.id}/levels/#{@levelB.id}/next"), json: true }
|
||||
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceA.id}/levels/#{@levelB.id}/sessions/#{@sessionB.id}/next"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.body).toEqual({})
|
||||
done()
|
||||
|
||||
it 'returns 404 if the given level is not in the course instance\'s course', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceB.id}/levels/#{@levelA.id}/next"), json: true }
|
||||
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceB.id}/levels/#{@levelA.id}/sessions/#{@sessionA.id}/next"), json: true }
|
||||
expect(res.statusCode).toBe(404)
|
||||
done()
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
describe 'Utility library', ->
|
||||
util = require 'core/utils'
|
||||
utils = require 'core/utils'
|
||||
|
||||
describe 'i18n', ->
|
||||
beforeEach ->
|
||||
|
@ -25,24 +25,217 @@ describe 'Utility library', ->
|
|||
'text': 'Godagens, trollkarl! Kommit för att öva? Nå, låt oss börja...'
|
||||
|
||||
it 'i18n should find a valid target string', ->
|
||||
expect(util.i18n(this.fixture1, 'text', 'sv')).toEqual(this.fixture1.i18n['sv'].text)
|
||||
expect(util.i18n(this.fixture1, 'text', 'es-ES')).toEqual(this.fixture1.i18n['es-ES'].text)
|
||||
expect(utils.i18n(this.fixture1, 'text', 'sv')).toEqual(this.fixture1.i18n['sv'].text)
|
||||
expect(utils.i18n(this.fixture1, 'text', 'es-ES')).toEqual(this.fixture1.i18n['es-ES'].text)
|
||||
|
||||
it 'i18n picks the correct fallback for a specific language', ->
|
||||
expect(util.i18n(this.fixture1, 'text', 'fr-be')).toEqual(this.fixture1.i18n['fr'].text)
|
||||
expect(utils.i18n(this.fixture1, 'text', 'fr-be')).toEqual(this.fixture1.i18n['fr'].text)
|
||||
|
||||
it 'i18n picks the correct fallback', ->
|
||||
expect(util.i18n(this.fixture1, 'text', 'nl')).toEqual(this.fixture1.i18n['en'].text)
|
||||
expect(util.i18n(this.fixture1, 'text', 'nl', 'de')).toEqual(this.fixture1.i18n['de'].text)
|
||||
expect(utils.i18n(this.fixture1, 'text', 'nl')).toEqual(this.fixture1.i18n['en'].text)
|
||||
expect(utils.i18n(this.fixture1, 'text', 'nl', 'de')).toEqual(this.fixture1.i18n['de'].text)
|
||||
|
||||
it 'i18n falls back to the default text, even for other targets (like blurb)', ->
|
||||
delete this.fixture1.i18n['en']
|
||||
expect(util.i18n(this.fixture1, 'text', 'en')).toEqual(this.fixture1.text)
|
||||
expect(util.i18n(this.fixture1, 'blurb', 'en')).toEqual(this.fixture1.blurb)
|
||||
expect(utils.i18n(this.fixture1, 'text', 'en')).toEqual(this.fixture1.text)
|
||||
expect(utils.i18n(this.fixture1, 'blurb', 'en')).toEqual(this.fixture1.blurb)
|
||||
delete this.fixture1.blurb
|
||||
expect(util.i18n(this.fixture1, 'blurb', 'en')).toEqual(null)
|
||||
expect(utils.i18n(this.fixture1, 'blurb', 'en')).toEqual(null)
|
||||
|
||||
it 'i18n can fall forward if a general language is not found', ->
|
||||
expect(util.i18n(this.fixture1, 'text', 'pt')).toEqual(this.fixture1.i18n['pt-BR'].text)
|
||||
expect(utils.i18n(this.fixture1, 'text', 'pt')).toEqual(this.fixture1.i18n['pt-BR'].text)
|
||||
|
||||
describe 'Miscellaneous utility', ->
|
||||
describe 'createLevelNumberMap', ->
|
||||
it 'returns correct map for r', ->
|
||||
levels = [
|
||||
{key: 1, practice: false}
|
||||
]
|
||||
levelNumberMap = utils.createLevelNumberMap(levels)
|
||||
expect((val.toString() for key, val of levelNumberMap)).toEqual(['1'])
|
||||
it 'returns correct map for r r', ->
|
||||
levels = [
|
||||
{key: 1, practice: false}
|
||||
{key: 2, practice: false}
|
||||
]
|
||||
levelNumberMap = utils.createLevelNumberMap(levels)
|
||||
expect((val.toString() for key, val of levelNumberMap)).toEqual(['1', '2'])
|
||||
it 'returns correct map for p', ->
|
||||
levels = [
|
||||
{key: 1, practice: true}
|
||||
]
|
||||
levelNumberMap = utils.createLevelNumberMap(levels)
|
||||
expect((val.toString() for key, val of levelNumberMap)).toEqual(['0a'])
|
||||
it 'returns correct map for r p r', ->
|
||||
levels = [
|
||||
{key: 1, practice: false}
|
||||
{key: 2, practice: true}
|
||||
{key: 3, practice: false}
|
||||
]
|
||||
levelNumberMap = utils.createLevelNumberMap(levels)
|
||||
expect((val.toString() for key, val of levelNumberMap)).toEqual(['1', '1a', '2'])
|
||||
it 'returns correct map for r p p p', ->
|
||||
levels = [
|
||||
{key: 1, practice: false}
|
||||
{key: 2, practice: true}
|
||||
{key: 3, practice: true}
|
||||
{key: 4, practice: true}
|
||||
]
|
||||
levelNumberMap = utils.createLevelNumberMap(levels)
|
||||
expect((val.toString() for key, val of levelNumberMap)).toEqual(['1', '1a', '1b', '1c'])
|
||||
it 'returns correct map for r p p p r p p r r p r', ->
|
||||
levels = [
|
||||
{key: 1, practice: false}
|
||||
{key: 2, practice: true}
|
||||
{key: 3, practice: true}
|
||||
{key: 4, practice: true}
|
||||
{key: 5, practice: false}
|
||||
{key: 6, practice: true}
|
||||
{key: 7, practice: true}
|
||||
{key: 8, practice: false}
|
||||
{key: 9, practice: false}
|
||||
{key: 10, practice: true}
|
||||
{key: 11, practice: false}
|
||||
]
|
||||
levelNumberMap = utils.createLevelNumberMap(levels)
|
||||
expect((val.toString() for key, val of levelNumberMap)).toEqual(['1', '1a', '1b', '1c', '2', '2a', '2b', '3', '4', '4a', '5'])
|
||||
|
||||
describe 'findNextlevel', ->
|
||||
describe 'when no practice needed', ->
|
||||
needsPractice = false
|
||||
it 'returns next level when rc* p', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: false}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 0, needsPractice)).toEqual(2)
|
||||
done()
|
||||
it 'returns next level when pc* p r', (done) ->
|
||||
levels = [
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: false, complete: false}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 0, needsPractice)).toEqual(2)
|
||||
done()
|
||||
it 'returns next level when pc* p p', (done) ->
|
||||
levels = [
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: true, complete: false}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 0, needsPractice)).toEqual(3)
|
||||
done()
|
||||
it 'returns next level when rc* p rc', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: false, complete: true}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 0, needsPractice)).toEqual(3)
|
||||
done()
|
||||
describe 'when needs practice', ->
|
||||
needsPractice = true
|
||||
it 'returns next level when rc* p', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: false}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 0, needsPractice)).toEqual(1)
|
||||
done()
|
||||
it 'returns next level when rc* rc', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: false, complete: true}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 0, needsPractice)).toEqual(2)
|
||||
done()
|
||||
it 'returns next level when rc p rc*', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: false, complete: true}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 2, needsPractice)).toEqual(1)
|
||||
done()
|
||||
it 'returns next level when rc pc p rc*', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: false, complete: true}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 3, needsPractice)).toEqual(2)
|
||||
done()
|
||||
it 'returns next level when rc pc p rc* p', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: false}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 3, needsPractice)).toEqual(4)
|
||||
done()
|
||||
it 'returns next level when rc pc p rc* pc', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 3, needsPractice)).toEqual(5)
|
||||
done()
|
||||
it 'returns next level when rc pc p rc* pc p', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: false}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 3, needsPractice)).toEqual(5)
|
||||
done()
|
||||
it 'returns next level when rc pc p rc* pc r', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: false, complete: false}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 3, needsPractice)).toEqual(5)
|
||||
done()
|
||||
it 'returns next level when rc pc p rc* pc p r', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: false}
|
||||
{practice: false, complete: false}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 3, needsPractice)).toEqual(5)
|
||||
done()
|
||||
it 'returns next level when rc pc pc rc* r p', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: false, complete: true}
|
||||
{practice: false, complete: false}
|
||||
{practice: true, complete: false}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 3, needsPractice)).toEqual(4)
|
||||
done()
|
||||
it 'returns next level when rc* pc rc', (done) ->
|
||||
levels = [
|
||||
{practice: false, complete: true}
|
||||
{practice: true, complete: true}
|
||||
{practice: false, complete: true}
|
||||
]
|
||||
expect(utils.findNextLevel(levels, 0, needsPractice)).toEqual(3)
|
||||
done()
|
||||
|
|
Loading…
Reference in a new issue