mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-18 11:32:25 -05:00
b803080bed
Closes #3906
229 lines
11 KiB
CoffeeScript
229 lines
11 KiB
CoffeeScript
Levels = require 'collections/Levels'
|
|
|
|
module.exports =
|
|
# Result: Each course instance gains a property, numCompleted, that is the
|
|
# number of students in that course instance who have completed ALL of
|
|
# the levels in thate course
|
|
# TODO: simplify, classroom.sessions only includes sessions for assigned courses now
|
|
calculateDots: (classrooms, courses, courseInstances) ->
|
|
for classroom in classrooms.models
|
|
# map [user, level] => session so we don't have to do find TODO
|
|
for course, courseIndex in courses.models
|
|
instance = courseInstances.findWhere({ courseID: course.id, classroomID: classroom.id })
|
|
continue if not instance
|
|
instance.numCompleted = 0
|
|
instance.started = false
|
|
levels = classroom.getLevels({courseID: course.id})
|
|
levels.remove(levels.filter((level) => level.get('practice')))
|
|
for userID in instance.get('members')
|
|
instance.started ||= _.any levels.models, (level) ->
|
|
session = _.find classroom.sessions.models, (session) ->
|
|
session.get('creator') is userID and session.get('level').original is level.get('original')
|
|
session?
|
|
levelCompletes = _.map levels.models, (level) ->
|
|
#TODO: Hella slow! Do the mapping first!
|
|
sessions = _.filter classroom.sessions.models, (session) ->
|
|
session.get('creator') is userID and session.get('level').original is level.get('original')
|
|
# sessionMap[userID][level].completed()
|
|
_.find(sessions, (s) -> s.completed())
|
|
if _.every levelCompletes
|
|
instance.numCompleted += 1
|
|
|
|
calculateEarliestIncomplete: (classroom, courses, courseInstances, students) ->
|
|
# Loop through all the combinations of things, return the first one that somebody hasn't finished
|
|
for course, courseIndex in courses.models
|
|
instance = courseInstances.findWhere({ courseID: course.id, classroomID: classroom.id })
|
|
continue if not instance
|
|
levels = classroom.getLevels({courseID: course.id})
|
|
for level, levelIndex in levels.models
|
|
userIDs = []
|
|
for user in students.models
|
|
userID = user.id
|
|
sessions = _.filter classroom.sessions.models, (session) ->
|
|
session.get('creator') is userID and session.get('level').original is level.get('original')
|
|
if not _.find(sessions, (s) -> s.completed())
|
|
userIDs.push userID
|
|
if userIDs.length > 0
|
|
users = _.map userIDs, (id) ->
|
|
students.get(id)
|
|
return {
|
|
courseName: course.get('name')
|
|
courseNumber: courseIndex + 1
|
|
levelNumber: levelIndex + 1
|
|
levelName: level.get('name')
|
|
users: users
|
|
}
|
|
null
|
|
|
|
calculateLatestComplete: (classroom, courses, courseInstances, students) ->
|
|
# Loop through all the combinations of things in reverse order, return the level that anyone's finished
|
|
courseModels = courses.models.slice()
|
|
for course, courseIndex in courseModels.reverse() #
|
|
courseIndex = courses.models.length - courseIndex - 1 #compensate for reverse
|
|
instance = courseInstances.findWhere({ courseID: course.id, classroomID: classroom.id })
|
|
continue if not instance
|
|
levels = classroom.getLevels({courseID: course.id})
|
|
levelModels = levels.models.slice()
|
|
for level, levelIndex in levelModels.reverse() #
|
|
levelIndex = levelModels.length - levelIndex - 1 #compensate for reverse
|
|
userIDs = []
|
|
for user in students.models
|
|
userID = user.id
|
|
sessions = _.filter classroom.sessions.models, (session) ->
|
|
session.get('creator') is userID and session.get('level').original is level.get('original')
|
|
if _.find(sessions, (s) -> s.completed()) #
|
|
userIDs.push userID
|
|
if userIDs.length > 0
|
|
users = _.map userIDs, (id) ->
|
|
students.get(id)
|
|
return {
|
|
courseName: course.get('name')
|
|
courseNumber: courseIndex + 1
|
|
levelNumber: levelIndex + 1
|
|
levelName: level.get('name')
|
|
users: users
|
|
}
|
|
null
|
|
|
|
calculateConceptsCovered: (classrooms, courses, campaigns, courseInstances, students) ->
|
|
# Loop through all level/user combination and record
|
|
# whether they've started, and completed, each concept
|
|
conceptData = {}
|
|
for classroom in classrooms.models
|
|
conceptData[classroom.id] = {}
|
|
|
|
for course, courseIndex in courses.models
|
|
levels = classroom.getLevels({courseID: course.id})
|
|
|
|
for level in levels.models
|
|
levelID = level.get('original')
|
|
|
|
for concept in level.get('concepts')
|
|
unless conceptData[classroom.id][concept]
|
|
conceptData[classroom.id][concept] = { completed: true, started: false }
|
|
|
|
for concept in level.get('concepts')
|
|
for userID in classroom.get('members')
|
|
sessions = _.filter classroom.sessions.models, (session) ->
|
|
session.get('creator') is userID and session.get('level').original is levelID
|
|
|
|
if _.size(sessions) is 0 # haven't gotten to this level yet, but might have completed others before
|
|
for concept in level.get('concepts')
|
|
conceptData[classroom.id][concept].completed = false
|
|
if _.size(sessions) > 0 # have gotten to the level and at least started it
|
|
for concept in level.get('concepts')
|
|
conceptData[classroom.id][concept].started = true
|
|
if not _.find(sessions, (s) -> s.completed()) # level started but not completed
|
|
for concept in level.get('concepts')
|
|
conceptData[classroom.id][concept].completed = false
|
|
conceptData
|
|
|
|
calculateAllProgress: (classrooms, courses, courseInstances, students) ->
|
|
# Loop through all combinations and record:
|
|
# Completeness for each student/course
|
|
# Completeness for each student/level
|
|
# Completeness for each class/course (across all students)
|
|
# Completeness for each class/level (across all students)
|
|
|
|
# class -> course
|
|
# class -> course -> student
|
|
# class -> course -> level
|
|
# class -> course -> level -> student
|
|
|
|
progressData = {}
|
|
for classroom in classrooms.models
|
|
progressData[classroom.id] = {}
|
|
|
|
for course, courseIndex in courses.models
|
|
instance = courseInstances.findWhere({ courseID: course.id, classroomID: classroom.id })
|
|
if not instance
|
|
progressData[classroom.id][course.id] = { completed: false, started: false }
|
|
continue
|
|
progressData[classroom.id][course.id] = { completed: true, started: false } # to be updated
|
|
|
|
levels = classroom.getLevels({courseID: course.id})
|
|
for level in levels.models
|
|
levelID = level.get('original')
|
|
progressData[classroom.id][course.id][levelID] = {
|
|
completed: students.size() > 0,
|
|
started: false
|
|
numStarted: 0
|
|
# numCompleted: 0
|
|
}
|
|
|
|
for user in students.models
|
|
userID = user.id
|
|
courseProgress = progressData[classroom.id][course.id]
|
|
courseProgress[userID] ?= { completed: true, started: false, levelsCompleted: 0 } # Only set it the first time through a user
|
|
courseProgress[levelID][userID] = { completed: true, started: false } # These don't matter, will always be set
|
|
sessions = _.filter classroom.sessions.models, (session) ->
|
|
session.get('creator') is userID and session.get('level').original is levelID
|
|
|
|
courseProgress[levelID][userID].session = _.find(sessions, (s) -> s.completed()) or _.first(sessions)
|
|
|
|
if _.size(sessions) is 0 # haven't gotten to this level yet, but might have completed others before
|
|
courseProgress.started ||= false #no-op
|
|
courseProgress.completed = false
|
|
courseProgress[userID].started ||= false #no-op
|
|
courseProgress[userID].completed = false
|
|
courseProgress[levelID].started ||= false #no-op
|
|
courseProgress[levelID].completed = false
|
|
courseProgress[levelID][userID].started = false
|
|
courseProgress[levelID][userID].completed = false
|
|
|
|
if _.size(sessions) > 0 # have gotten to the level and at least started it
|
|
courseProgress.started = true
|
|
courseProgress[userID].started = true
|
|
courseProgress[levelID].started = true
|
|
courseProgress[levelID][userID].started = true
|
|
courseProgress[levelID][userID].lastPlayed = new Date(Math.max(_.map(sessions, 'changed')))
|
|
courseProgress[levelID].numStarted += 1
|
|
|
|
if _.find(sessions, (s) -> s.completed()) # have finished this level
|
|
courseProgress.completed &&= true #no-op
|
|
courseProgress[userID].completed &&= true #no-op
|
|
courseProgress[userID].levelsCompleted += 1 unless level.get('practice')
|
|
courseProgress[levelID].completed &&= true #no-op
|
|
# courseProgress[levelID].numCompleted += 1
|
|
courseProgress[levelID][userID].completed = true
|
|
dates = (s.get('dateFirstCompleted') || s.get('changed') for s in sessions)
|
|
courseProgress[levelID][userID].dateFirstCompleted = new Date(Math.max(dates...))
|
|
else # level started but not completed
|
|
courseProgress.completed = false
|
|
courseProgress[userID].completed = false
|
|
courseProgress[levelID].completed = false
|
|
courseProgress[levelID][userID].completed = false
|
|
courseProgress[levelID].dateFirstCompleted = null
|
|
courseProgress[levelID][userID].dateFirstCompleted = null
|
|
|
|
_.assign(progressData, progressMixin)
|
|
return progressData
|
|
|
|
courseLabelsArray: (courses) ->
|
|
labels = []
|
|
courseLabelIndexes = CS: 0, GD: 0, WD: 0
|
|
for course in courses
|
|
acronym = switch
|
|
when /game-dev/.test(course.get('slug')) then 'GD'
|
|
when /web-dev/.test(course.get('slug')) then 'WD'
|
|
else 'CS'
|
|
labels.push acronym + ++courseLabelIndexes[acronym]
|
|
labels
|
|
|
|
progressMixin =
|
|
get: (options={}) ->
|
|
{ classroom, course, level, user } = options
|
|
throw new Error "You must provide a classroom" unless classroom
|
|
throw new Error "You must provide a course" unless course
|
|
defaultValue = { completed: false, started: false }
|
|
if options.level
|
|
levelID = level.get('original')
|
|
if options.user
|
|
return @[classroom.id]?[course.id]?[levelID]?[user.id] or defaultValue
|
|
else
|
|
return @[classroom.id]?[course.id]?[levelID] or defaultValue
|
|
else
|
|
if options.user
|
|
return @[classroom.id]?[course.id]?[user.id] or defaultValue
|
|
else
|
|
return @[classroom.id]?[course.id] or defaultValue
|