codecombat/app/lib/coursesHelper.coffee

211 lines
9.9 KiB
CoffeeScript
Raw Normal View History

2016-04-13 12:54:24 -04:00
Levels = require 'collections/Levels'
2016-03-30 16:57:19 -04:00
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
2016-04-13 12:54:24 -04:00
calculateDots: (classrooms, courses, courseInstances) ->
2016-03-30 16:57:19 -04:00
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.numStarted = 0
2016-04-13 12:54:24 -04:00
levels = classroom.getLevels({courseID: course.id, withoutLadderLevels: true})
2016-03-30 16:57:19 -04:00
for userID in instance.get('members')
2016-04-13 12:54:24 -04:00
levelCompletes = _.map levels.models, (level) ->
2016-03-30 16:57:19 -04:00
return true if level.isLadder()
#TODO: Hella slow! Do the mapping first!
session = _.find classroom.sessions.models, (session) ->
session.get('creator') is userID and session.get('level').original is level.get('original')
# sessionMap[userID][level].completed()
session?.completed()
if _.every levelCompletes
2016-03-30 16:57:19 -04:00
instance.numCompleted += 1
if _.any levelCompletes
instance.numStarted += 1
2016-03-30 16:57:19 -04:00
2016-04-13 12:54:24 -04:00
calculateEarliestIncomplete: (classroom, courses, courseInstances, students) ->
2016-03-30 16:57:19 -04:00
# 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
2016-04-13 12:54:24 -04:00
levels = classroom.getLevels({courseID: course.id, withoutLadderLevels: true})
for level, levelIndex in levels.models
2016-03-30 16:57:19 -04:00
userIDs = []
for user in students.models
userID = user.id
session = _.find classroom.sessions.models, (session) ->
session.get('creator') is userID and session.get('level').original is level.get('original')
if not session?.completed()
userIDs.push userID
if userIDs.length > 0
users = _.map userIDs, (id) ->
students.get(id)
return {
courseNumber: courseIndex + 1
levelNumber: levelIndex + 1
levelName: level.get('name')
users: users
}
null
2016-04-13 12:54:24 -04:00
calculateLatestComplete: (classroom, courses, courseInstances, students) ->
2016-03-30 16:57:19 -04:00
# 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
2016-04-13 12:54:24 -04:00
levels = classroom.getLevels({courseID: course.id, withoutLadderLevels: true})
levelModels = levels.models.slice()
2016-03-30 16:57:19 -04:00
for level, levelIndex in levelModels.reverse() #
levelIndex = levelModels.length - levelIndex - 1 #compensate for reverse
userIDs = []
for user in students.models
userID = user.id
session = _.find classroom.sessions.models, (session) ->
session.get('creator') is userID and session.get('level').original is level.get('original')
if session?.completed() #
userIDs.push userID
if userIDs.length > 0
users = _.map userIDs, (id) ->
students.get(id)
return {
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
2016-04-13 12:54:24 -04:00
levels = classroom.getLevels({courseID: course.id, withoutLadderLevels: true})
2016-03-30 16:57:19 -04:00
2016-04-13 12:54:24 -04:00
for level in levels.models
2016-03-30 16:57:19 -04:00
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')
session = _.find classroom.sessions.models, (session) ->
session.get('creator') is userID and session.get('level').original is levelID
if not session # 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 session # have gotten to the level and at least started it
for concept in level.get('concepts')
conceptData[classroom.id][concept].started = true
if not session?.completed() # level started but not completed
for concept in level.get('concepts')
conceptData[classroom.id][concept].completed = false
conceptData
2016-04-13 12:54:24 -04:00
calculateAllProgress: (classrooms, courses, courseInstances, students) ->
2016-03-30 16:57:19 -04:00
# 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
2016-04-13 12:54:24 -04:00
levels = classroom.getLevels({courseID: course.id, withoutLadderLevels: true})
for level in levels.models
2016-03-30 16:57:19 -04:00
levelID = level.get('original')
Refactor and update teacher-dashboard This updates TeacherClassView and ActivateLicensesModal to use the new state-based rendering system, making it much snappier and less clunky feeling, and improving data consistency. Features also included in this: - Hover details for progress dots in TeacherClassView - ActivateLicensesModal has an "All Students" option and better handling when you switch classrooms in the dropdown - Unenrolled/Unassigned students are shown separately in Course Progress and can be enrolled/assigned from there. Add Back to Classes button on demo-request submitted view Delete temporary patch file Show unenrolled students separately in Course Progress (incomplete) Migrate TeacherClassView to use orchestrator-style events, add unassigned students section, replace bootstrap tabs with state-based tabs Convert missed instance variables to be in @state Fix merge errors (in progress) Convert a bunch of stuff to use state and events (removing student needs fixing) Fix up modal interactions, some bugs Switch state to be a Model, sync up course dropdowns Convert student sorting to use state model Add hover tooltips to TeacherClassView Students tab Don't keep tooltip open when you mouse into it Add dateFirstCompleted and Course Progress tooltips Course Overview progress tooltips Refactor ActivateLicensesModal Refactors: Uses state object for view state Passes back the updated users in 'redeem-users' event instead of modifying given collection Features: Add 'All Students' dropdown option Don't forget checked students if you change classroom from dropdown, but only enroll the ones visible when you click "Enroll (n) Students" Separate enrolled students; improve style Rearrange error text Disable enroll-students button when none are selected Remove console.logs Move style-flat variables to another file This prevents .style-flat from being copied in multiple times to the resulting CSS. Show Unarchive button when on the page for an archived class Move text to en.coffee Only sort students on first classroom sync Fix merge error Handle sessions missing completion date in view logic instead of migration script Listen to classroom sync more than once in case it gets unarchived
2016-04-19 16:44:48 -04:00
progressData[classroom.id][course.id][levelID] = {
completed: students.size() > 0,
started: false
numStarted: 0
# numCompleted: 0
}
2016-03-30 16:57:19 -04:00
for user in students.models
userID = user.id
courseProgress = progressData[classroom.id][course.id]
Refactor and update teacher-dashboard This updates TeacherClassView and ActivateLicensesModal to use the new state-based rendering system, making it much snappier and less clunky feeling, and improving data consistency. Features also included in this: - Hover details for progress dots in TeacherClassView - ActivateLicensesModal has an "All Students" option and better handling when you switch classrooms in the dropdown - Unenrolled/Unassigned students are shown separately in Course Progress and can be enrolled/assigned from there. Add Back to Classes button on demo-request submitted view Delete temporary patch file Show unenrolled students separately in Course Progress (incomplete) Migrate TeacherClassView to use orchestrator-style events, add unassigned students section, replace bootstrap tabs with state-based tabs Convert missed instance variables to be in @state Fix merge errors (in progress) Convert a bunch of stuff to use state and events (removing student needs fixing) Fix up modal interactions, some bugs Switch state to be a Model, sync up course dropdowns Convert student sorting to use state model Add hover tooltips to TeacherClassView Students tab Don't keep tooltip open when you mouse into it Add dateFirstCompleted and Course Progress tooltips Course Overview progress tooltips Refactor ActivateLicensesModal Refactors: Uses state object for view state Passes back the updated users in 'redeem-users' event instead of modifying given collection Features: Add 'All Students' dropdown option Don't forget checked students if you change classroom from dropdown, but only enroll the ones visible when you click "Enroll (n) Students" Separate enrolled students; improve style Rearrange error text Disable enroll-students button when none are selected Remove console.logs Move style-flat variables to another file This prevents .style-flat from being copied in multiple times to the resulting CSS. Show Unarchive button when on the page for an archived class Move text to en.coffee Only sort students on first classroom sync Fix merge error Handle sessions missing completion date in view logic instead of migration script Listen to classroom sync more than once in case it gets unarchived
2016-04-19 16:44:48 -04:00
courseProgress[userID] ?= { completed: true, started: false, levelsCompleted: 0 } # Only set it the first time through a user
2016-03-30 16:57:19 -04:00
courseProgress[levelID][userID] = { completed: true, started: false } # These don't matter, will always be set
session = _.find classroom.sessions.models, (session) ->
session.get('creator') is userID and session.get('level').original is levelID
Refactor and update teacher-dashboard This updates TeacherClassView and ActivateLicensesModal to use the new state-based rendering system, making it much snappier and less clunky feeling, and improving data consistency. Features also included in this: - Hover details for progress dots in TeacherClassView - ActivateLicensesModal has an "All Students" option and better handling when you switch classrooms in the dropdown - Unenrolled/Unassigned students are shown separately in Course Progress and can be enrolled/assigned from there. Add Back to Classes button on demo-request submitted view Delete temporary patch file Show unenrolled students separately in Course Progress (incomplete) Migrate TeacherClassView to use orchestrator-style events, add unassigned students section, replace bootstrap tabs with state-based tabs Convert missed instance variables to be in @state Fix merge errors (in progress) Convert a bunch of stuff to use state and events (removing student needs fixing) Fix up modal interactions, some bugs Switch state to be a Model, sync up course dropdowns Convert student sorting to use state model Add hover tooltips to TeacherClassView Students tab Don't keep tooltip open when you mouse into it Add dateFirstCompleted and Course Progress tooltips Course Overview progress tooltips Refactor ActivateLicensesModal Refactors: Uses state object for view state Passes back the updated users in 'redeem-users' event instead of modifying given collection Features: Add 'All Students' dropdown option Don't forget checked students if you change classroom from dropdown, but only enroll the ones visible when you click "Enroll (n) Students" Separate enrolled students; improve style Rearrange error text Disable enroll-students button when none are selected Remove console.logs Move style-flat variables to another file This prevents .style-flat from being copied in multiple times to the resulting CSS. Show Unarchive button when on the page for an archived class Move text to en.coffee Only sort students on first classroom sync Fix merge error Handle sessions missing completion date in view logic instead of migration script Listen to classroom sync more than once in case it gets unarchived
2016-04-19 16:44:48 -04:00
2016-03-30 16:57:19 -04:00
if not session # 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
Refactor and update teacher-dashboard This updates TeacherClassView and ActivateLicensesModal to use the new state-based rendering system, making it much snappier and less clunky feeling, and improving data consistency. Features also included in this: - Hover details for progress dots in TeacherClassView - ActivateLicensesModal has an "All Students" option and better handling when you switch classrooms in the dropdown - Unenrolled/Unassigned students are shown separately in Course Progress and can be enrolled/assigned from there. Add Back to Classes button on demo-request submitted view Delete temporary patch file Show unenrolled students separately in Course Progress (incomplete) Migrate TeacherClassView to use orchestrator-style events, add unassigned students section, replace bootstrap tabs with state-based tabs Convert missed instance variables to be in @state Fix merge errors (in progress) Convert a bunch of stuff to use state and events (removing student needs fixing) Fix up modal interactions, some bugs Switch state to be a Model, sync up course dropdowns Convert student sorting to use state model Add hover tooltips to TeacherClassView Students tab Don't keep tooltip open when you mouse into it Add dateFirstCompleted and Course Progress tooltips Course Overview progress tooltips Refactor ActivateLicensesModal Refactors: Uses state object for view state Passes back the updated users in 'redeem-users' event instead of modifying given collection Features: Add 'All Students' dropdown option Don't forget checked students if you change classroom from dropdown, but only enroll the ones visible when you click "Enroll (n) Students" Separate enrolled students; improve style Rearrange error text Disable enroll-students button when none are selected Remove console.logs Move style-flat variables to another file This prevents .style-flat from being copied in multiple times to the resulting CSS. Show Unarchive button when on the page for an archived class Move text to en.coffee Only sort students on first classroom sync Fix merge error Handle sessions missing completion date in view logic instead of migration script Listen to classroom sync more than once in case it gets unarchived
2016-04-19 16:44:48 -04:00
2016-03-30 16:57:19 -04:00
if session # 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
Refactor and update teacher-dashboard This updates TeacherClassView and ActivateLicensesModal to use the new state-based rendering system, making it much snappier and less clunky feeling, and improving data consistency. Features also included in this: - Hover details for progress dots in TeacherClassView - ActivateLicensesModal has an "All Students" option and better handling when you switch classrooms in the dropdown - Unenrolled/Unassigned students are shown separately in Course Progress and can be enrolled/assigned from there. Add Back to Classes button on demo-request submitted view Delete temporary patch file Show unenrolled students separately in Course Progress (incomplete) Migrate TeacherClassView to use orchestrator-style events, add unassigned students section, replace bootstrap tabs with state-based tabs Convert missed instance variables to be in @state Fix merge errors (in progress) Convert a bunch of stuff to use state and events (removing student needs fixing) Fix up modal interactions, some bugs Switch state to be a Model, sync up course dropdowns Convert student sorting to use state model Add hover tooltips to TeacherClassView Students tab Don't keep tooltip open when you mouse into it Add dateFirstCompleted and Course Progress tooltips Course Overview progress tooltips Refactor ActivateLicensesModal Refactors: Uses state object for view state Passes back the updated users in 'redeem-users' event instead of modifying given collection Features: Add 'All Students' dropdown option Don't forget checked students if you change classroom from dropdown, but only enroll the ones visible when you click "Enroll (n) Students" Separate enrolled students; improve style Rearrange error text Disable enroll-students button when none are selected Remove console.logs Move style-flat variables to another file This prevents .style-flat from being copied in multiple times to the resulting CSS. Show Unarchive button when on the page for an archived class Move text to en.coffee Only sort students on first classroom sync Fix merge error Handle sessions missing completion date in view logic instead of migration script Listen to classroom sync more than once in case it gets unarchived
2016-04-19 16:44:48 -04:00
courseProgress[levelID][userID].lastPlayed = new Date(session.get('changed'))
courseProgress[levelID].numStarted += 1
2016-03-30 16:57:19 -04:00
if session?.completed() # have finished this level
courseProgress.completed &&= true #no-op
Refactor and update teacher-dashboard This updates TeacherClassView and ActivateLicensesModal to use the new state-based rendering system, making it much snappier and less clunky feeling, and improving data consistency. Features also included in this: - Hover details for progress dots in TeacherClassView - ActivateLicensesModal has an "All Students" option and better handling when you switch classrooms in the dropdown - Unenrolled/Unassigned students are shown separately in Course Progress and can be enrolled/assigned from there. Add Back to Classes button on demo-request submitted view Delete temporary patch file Show unenrolled students separately in Course Progress (incomplete) Migrate TeacherClassView to use orchestrator-style events, add unassigned students section, replace bootstrap tabs with state-based tabs Convert missed instance variables to be in @state Fix merge errors (in progress) Convert a bunch of stuff to use state and events (removing student needs fixing) Fix up modal interactions, some bugs Switch state to be a Model, sync up course dropdowns Convert student sorting to use state model Add hover tooltips to TeacherClassView Students tab Don't keep tooltip open when you mouse into it Add dateFirstCompleted and Course Progress tooltips Course Overview progress tooltips Refactor ActivateLicensesModal Refactors: Uses state object for view state Passes back the updated users in 'redeem-users' event instead of modifying given collection Features: Add 'All Students' dropdown option Don't forget checked students if you change classroom from dropdown, but only enroll the ones visible when you click "Enroll (n) Students" Separate enrolled students; improve style Rearrange error text Disable enroll-students button when none are selected Remove console.logs Move style-flat variables to another file This prevents .style-flat from being copied in multiple times to the resulting CSS. Show Unarchive button when on the page for an archived class Move text to en.coffee Only sort students on first classroom sync Fix merge error Handle sessions missing completion date in view logic instead of migration script Listen to classroom sync more than once in case it gets unarchived
2016-04-19 16:44:48 -04:00
courseProgress[userID].completed &&= true #no-op
courseProgress[userID].levelsCompleted += 1
2016-03-30 16:57:19 -04:00
courseProgress[levelID].completed &&= true #no-op
Refactor and update teacher-dashboard This updates TeacherClassView and ActivateLicensesModal to use the new state-based rendering system, making it much snappier and less clunky feeling, and improving data consistency. Features also included in this: - Hover details for progress dots in TeacherClassView - ActivateLicensesModal has an "All Students" option and better handling when you switch classrooms in the dropdown - Unenrolled/Unassigned students are shown separately in Course Progress and can be enrolled/assigned from there. Add Back to Classes button on demo-request submitted view Delete temporary patch file Show unenrolled students separately in Course Progress (incomplete) Migrate TeacherClassView to use orchestrator-style events, add unassigned students section, replace bootstrap tabs with state-based tabs Convert missed instance variables to be in @state Fix merge errors (in progress) Convert a bunch of stuff to use state and events (removing student needs fixing) Fix up modal interactions, some bugs Switch state to be a Model, sync up course dropdowns Convert student sorting to use state model Add hover tooltips to TeacherClassView Students tab Don't keep tooltip open when you mouse into it Add dateFirstCompleted and Course Progress tooltips Course Overview progress tooltips Refactor ActivateLicensesModal Refactors: Uses state object for view state Passes back the updated users in 'redeem-users' event instead of modifying given collection Features: Add 'All Students' dropdown option Don't forget checked students if you change classroom from dropdown, but only enroll the ones visible when you click "Enroll (n) Students" Separate enrolled students; improve style Rearrange error text Disable enroll-students button when none are selected Remove console.logs Move style-flat variables to another file This prevents .style-flat from being copied in multiple times to the resulting CSS. Show Unarchive button when on the page for an archived class Move text to en.coffee Only sort students on first classroom sync Fix merge error Handle sessions missing completion date in view logic instead of migration script Listen to classroom sync more than once in case it gets unarchived
2016-04-19 16:44:48 -04:00
# courseProgress[levelID].numCompleted += 1
2016-03-30 16:57:19 -04:00
courseProgress[levelID][userID].completed = true
Refactor and update teacher-dashboard This updates TeacherClassView and ActivateLicensesModal to use the new state-based rendering system, making it much snappier and less clunky feeling, and improving data consistency. Features also included in this: - Hover details for progress dots in TeacherClassView - ActivateLicensesModal has an "All Students" option and better handling when you switch classrooms in the dropdown - Unenrolled/Unassigned students are shown separately in Course Progress and can be enrolled/assigned from there. Add Back to Classes button on demo-request submitted view Delete temporary patch file Show unenrolled students separately in Course Progress (incomplete) Migrate TeacherClassView to use orchestrator-style events, add unassigned students section, replace bootstrap tabs with state-based tabs Convert missed instance variables to be in @state Fix merge errors (in progress) Convert a bunch of stuff to use state and events (removing student needs fixing) Fix up modal interactions, some bugs Switch state to be a Model, sync up course dropdowns Convert student sorting to use state model Add hover tooltips to TeacherClassView Students tab Don't keep tooltip open when you mouse into it Add dateFirstCompleted and Course Progress tooltips Course Overview progress tooltips Refactor ActivateLicensesModal Refactors: Uses state object for view state Passes back the updated users in 'redeem-users' event instead of modifying given collection Features: Add 'All Students' dropdown option Don't forget checked students if you change classroom from dropdown, but only enroll the ones visible when you click "Enroll (n) Students" Separate enrolled students; improve style Rearrange error text Disable enroll-students button when none are selected Remove console.logs Move style-flat variables to another file This prevents .style-flat from being copied in multiple times to the resulting CSS. Show Unarchive button when on the page for an archived class Move text to en.coffee Only sort students on first classroom sync Fix merge error Handle sessions missing completion date in view logic instead of migration script Listen to classroom sync more than once in case it gets unarchived
2016-04-19 16:44:48 -04:00
courseProgress[levelID][userID].dateFirstCompleted = new Date(session.get('dateFirstCompleted') || session.get('changed'))
2016-03-30 16:57:19 -04:00
else # level started but not completed
courseProgress.completed = false
courseProgress[userID].completed = false
courseProgress[levelID].completed = false
courseProgress[levelID][userID].completed = false
Refactor and update teacher-dashboard This updates TeacherClassView and ActivateLicensesModal to use the new state-based rendering system, making it much snappier and less clunky feeling, and improving data consistency. Features also included in this: - Hover details for progress dots in TeacherClassView - ActivateLicensesModal has an "All Students" option and better handling when you switch classrooms in the dropdown - Unenrolled/Unassigned students are shown separately in Course Progress and can be enrolled/assigned from there. Add Back to Classes button on demo-request submitted view Delete temporary patch file Show unenrolled students separately in Course Progress (incomplete) Migrate TeacherClassView to use orchestrator-style events, add unassigned students section, replace bootstrap tabs with state-based tabs Convert missed instance variables to be in @state Fix merge errors (in progress) Convert a bunch of stuff to use state and events (removing student needs fixing) Fix up modal interactions, some bugs Switch state to be a Model, sync up course dropdowns Convert student sorting to use state model Add hover tooltips to TeacherClassView Students tab Don't keep tooltip open when you mouse into it Add dateFirstCompleted and Course Progress tooltips Course Overview progress tooltips Refactor ActivateLicensesModal Refactors: Uses state object for view state Passes back the updated users in 'redeem-users' event instead of modifying given collection Features: Add 'All Students' dropdown option Don't forget checked students if you change classroom from dropdown, but only enroll the ones visible when you click "Enroll (n) Students" Separate enrolled students; improve style Rearrange error text Disable enroll-students button when none are selected Remove console.logs Move style-flat variables to another file This prevents .style-flat from being copied in multiple times to the resulting CSS. Show Unarchive button when on the page for an archived class Move text to en.coffee Only sort students on first classroom sync Fix merge error Handle sessions missing completion date in view logic instead of migration script Listen to classroom sync more than once in case it gets unarchived
2016-04-19 16:44:48 -04:00
courseProgress[levelID].dateFirstCompleted = null
courseProgress[levelID][userID].dateFirstCompleted = null
2016-03-30 16:57:19 -04:00
_.assign(progressData, progressMixin)
return progressData
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