2015-11-30 14:14:27 -05:00
Campaign = require ' models/Campaign '
CocoCollection = require ' collections/CocoCollection '
Course = require ' models/Course '
CourseInstance = require ' models/CourseInstance '
Classroom = require ' models/Classroom '
2016-03-30 16:20:37 -07:00
Classrooms = require ' collections/Classrooms '
2015-11-30 14:14:27 -05:00
LevelSession = require ' models/LevelSession '
2015-12-21 15:45:14 -08:00
Prepaids = require ' collections/Prepaids '
2016-04-13 09:54:24 -07:00
Levels = require ' collections/Levels '
2015-11-30 14:14:27 -05:00
RootView = require ' views/core/RootView '
template = require ' templates/courses/classroom-view '
User = require ' models/User '
utils = require ' core/utils '
Prepaid = require ' models/Prepaid '
ClassroomSettingsModal = require ' views/courses/ClassroomSettingsModal '
2015-11-30 16:59:22 -05:00
ActivateLicensesModal = require ' views/courses/ActivateLicensesModal '
2015-12-01 15:17:21 -06:00
InviteToClassroomModal = require ' views/courses/InviteToClassroomModal '
2015-12-02 11:56:38 -08:00
RemoveStudentModal = require ' views/courses/RemoveStudentModal '
2015-12-03 12:07:12 -08:00
popoverTemplate = require ' templates/courses/classroom-level-popover '
2015-11-30 14:14:27 -05:00
module.exports = class ClassroomView extends RootView
id: ' classroom-view '
template: template
2015-12-03 12:07:12 -08:00
teacherMode: false
2015-12-04 11:59:51 -08:00
2015-11-30 14:14:27 -05:00
events:
' click # edit-class-details-link ' : ' onClickEditClassDetailsLink '
2015-11-30 16:59:22 -05:00
' click # activate-licenses-btn ' : ' onClickActivateLicensesButton '
' click .activate-single-license-btn ' : ' onClickActivateSingleLicenseButton '
2015-12-01 15:17:21 -06:00
' click # add-students-btn ' : ' onClickAddStudentsButton '
2015-12-01 15:27:12 -06:00
' click .enable-btn ' : ' onClickEnableButton '
2015-12-02 11:56:38 -08:00
' click .remove-student-link ' : ' onClickRemoveStudentLink '
2015-11-30 14:14:27 -05:00
initialize: (options, classroomID) ->
2015-12-04 16:37:13 -08:00
return if me . isAnonymous ( )
2015-11-30 14:14:27 -05:00
@classroom = new Classroom ( { _id: classroomID } )
2016-01-25 16:52:14 -08:00
@ supermodel . loadModel @ classroom
2015-11-30 14:14:27 -05:00
@courses = new CocoCollection ( [ ] , { url: " /db/course " , model: Course } )
@courses.comparator = ' _id '
2016-01-25 16:52:14 -08:00
@ supermodel . loadCollection ( @ courses )
2015-11-30 14:14:27 -05:00
@courses.comparator = ' _id '
@courseInstances = new CocoCollection ( [ ] , { url: " /db/course_instance " , model: CourseInstance } )
@courseInstances.comparator = ' courseID '
2016-01-25 16:52:14 -08:00
@ supermodel . loadCollection ( @ courseInstances , { data: { classroomID: classroomID } } )
2015-12-21 15:45:14 -08:00
@prepaids = new Prepaids ( )
@prepaids.comparator = ' _id '
@ prepaids . fetchByCreator ( me . id )
2016-01-25 16:52:14 -08:00
@ supermodel . loadCollection ( @ prepaids )
2016-03-30 16:20:37 -07:00
@users = new CocoCollection ( [ ] , { url: " /db/classroom/ #{ classroomID } /members?memberLimit=100 " , model: User } )
2015-11-30 16:59:22 -05:00
@users.comparator = (user) => user . broadName ( ) . toLowerCase ( )
2016-01-25 16:52:14 -08:00
@ supermodel . loadCollection ( @ users )
2015-11-30 14:14:27 -05:00
@ listenToOnce @ courseInstances , ' sync ' , @ onCourseInstancesSync
2015-12-03 14:51:38 -08:00
@sessions = new CocoCollection ( [ ] , { model: LevelSession } )
2016-03-30 16:20:37 -07:00
@ownedClassrooms = new Classrooms ( )
@ ownedClassrooms . fetchMine ( { data: { project: ' _id ' } } )
@ supermodel . trackCollection ( @ ownedClassrooms )
2016-04-13 09:54:24 -07:00
@levels = new Levels ( )
@ levels . fetchForClassroom ( classroomID , { data: { project: ' name,slug,original ' } } )
@ levels . on ' add ' , (model) -> @ _byId [ model . get ( ' original ' ) ] = model # so you can 'get' them
@ supermodel . trackCollection ( @ levels )
2015-11-30 14:14:27 -05:00
onCourseInstancesSync: ->
@sessions = new CocoCollection ( [ ] , { model: LevelSession } )
for courseInstance in @ courseInstances . models
sessions = new CocoCollection ( [ ] , { url: " /db/course_instance/ #{ courseInstance . id } /level_sessions " , model: LevelSession } )
2016-01-25 16:52:14 -08:00
@ supermodel . loadCollection ( sessions , { data: { project: [ ' level ' , ' playtime ' , ' creator ' , ' changed ' , ' state.complete ' ] . join ( ' ' ) } } )
2015-11-30 14:14:27 -05:00
courseInstance.sessions = sessions
sessions.courseInstance = courseInstance
2015-12-02 16:07:46 -08:00
courseInstance.sessionsByUser = { }
2015-11-30 14:14:27 -05:00
@ listenToOnce sessions , ' sync ' , (sessions) ->
@ sessions . add ( sessions . slice ( ) )
2015-12-07 14:15:53 -08:00
for courseInstance in @ courseInstances . models
courseInstance.sessionsByUser = courseInstance . sessions . groupBy ( ' creator ' )
2015-12-04 11:59:51 -08:00
2015-12-07 14:15:53 -08:00
# Generate course instance JIT, in the meantime have models w/out equivalents in the db
2015-12-02 16:07:46 -08:00
for course in @ courses . models
query = { courseID: course . id , classroomID: @ classroom . id }
courseInstance = @ courseInstances . findWhere ( query )
if not courseInstance
courseInstance = new CourseInstance ( query )
@ courseInstances . add ( courseInstance )
courseInstance.sessions = new CocoCollection ( [ ] , { model: LevelSession } )
sessions.courseInstance = courseInstance
courseInstance.sessionsByUser = { }
2015-11-30 14:14:27 -05:00
onLoaded: ->
2015-12-03 13:16:57 -08:00
@teacherMode = me . isAdmin ( ) or @ classroom . get ( ' ownerID ' ) is me . id
2015-11-30 14:14:27 -05:00
userSessions = @ sessions . groupBy ( ' creator ' )
for user in @ users . models
user.sessions = new CocoCollection ( userSessions [ user . id ] , { model: LevelSession } )
user.sessions.comparator = ' changed '
user . sessions . sort ( )
for courseInstance in @ courseInstances . models
courseID = courseInstance . get ( ' courseID ' )
course = @ courses . get ( courseID )
2016-04-13 09:54:24 -07:00
courseInstance.sessions.course = course
2015-11-30 14:14:27 -05:00
super ( )
2015-12-04 11:59:51 -08:00
2015-12-03 12:07:12 -08:00
afterRender: ->
@ $ ( ' [data-toggle= " popover " ] ' ) . popover ( {
html: true
trigger: ' hover '
placement: ' top '
} )
super ( )
2015-11-30 14:14:27 -05:00
2015-11-30 16:59:22 -05:00
onClickActivateLicensesButton: ->
modal = new ActivateLicensesModal ( {
classroom: @ classroom
users: @ users
} )
@ openModalView ( modal )
modal . once ' redeem-users ' , -> document . location . reload ( )
2015-12-04 14:11:17 -08:00
application . tracker ? . trackEvent ' Classroom started enroll students ' , category: ' Courses '
2015-11-30 16:59:22 -05:00
onClickActivateSingleLicenseButton: (e) ->
2015-12-09 06:57:33 -08:00
userID = $ ( e . target ) . closest ( ' .btn ' ) . data ( ' user-id ' )
2015-12-21 15:45:14 -08:00
if @ prepaids . totalMaxRedeemers ( ) - @ prepaids . totalRedeemers ( ) > 0
# Have an unused enrollment, enroll student immediately instead of opening the enroll modal
2016-05-09 15:16:54 -07:00
prepaid = @ prepaids . find ( (prepaid) -> prepaid . status ( ) is ' available ' )
2015-12-21 15:45:14 -08:00
$ . ajax ( {
method: ' POST '
url: _ . result ( prepaid , ' url ' ) + ' /redeemers '
data: { userID: userID }
success: =>
application . tracker ? . trackEvent ' Classroom finished enroll student ' , category: ' Courses ' , userID: userID
# TODO: do a lighter refresh here. @render() did not work out.
document . location . reload ( )
error: (jqxhr, textStatus, errorThrown) ->
if jqxhr . status is 402
message = arguments [ 2 ]
else
message = " #{ jqxhr . status } : #{ jqxhr . responseText } "
console . err message
} )
else
user = @ users . get ( userID )
modal = new ActivateLicensesModal ( {
classroom: @ classroom
users: @ users
user: user
} )
@ openModalView ( modal )
modal . once ' redeem-users ' , -> document . location . reload ( )
application . tracker ? . trackEvent ' Classroom started enroll student ' , category: ' Courses ' , userID: userID
2015-11-30 16:59:22 -05:00
2015-11-30 14:14:27 -05:00
onClickEditClassDetailsLink: ->
modal = new ClassroomSettingsModal ( { classroom: @ classroom } )
@ openModalView ( modal )
@ listenToOnce modal , ' hidden ' , @ render
2015-12-03 14:51:38 -08:00
userLastPlayedString: (user) ->
2015-12-07 14:15:53 -08:00
return ' ' unless user . sessions ?
2015-11-30 14:14:27 -05:00
session = user . sessions . last ( )
2015-12-07 14:15:53 -08:00
return ' ' unless session
2016-04-13 09:54:24 -07:00
course = session . collection . course
2015-11-30 14:14:27 -05:00
levelOriginal = session . get ( ' level ' ) . original
2016-04-13 09:54:24 -07:00
level = @ levels . findWhere ( { original: levelOriginal } )
return " #{ course . get ( ' name ' ) } , #{ level . get ( ' name ' ) } "
2015-12-01 15:17:21 -06:00
2015-12-03 14:51:38 -08:00
userPlaytimeString: (user) ->
2015-12-07 14:15:53 -08:00
return ' ' unless user . sessions ?
2015-12-03 12:29:22 -08:00
playtime = _ . reduce user . sessions . pluck ( ' playtime ' ) , (s1, s2) -> ( s1 or 0 ) + ( s2 or 0 )
return ' ' unless playtime
return moment . duration ( playtime , ' seconds ' ) . humanize ( )
2015-12-03 14:51:38 -08:00
classStats: ->
stats = { }
2015-12-04 11:59:51 -08:00
2015-12-03 14:51:38 -08:00
playtime = 0
total = 0
for session in @ sessions . models
pt = session . get ( ' playtime ' ) or 0
playtime += pt
total += 1
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
2015-12-04 11:59:51 -08:00
completeSessions = @ sessions . filter (s) -> s . get ( ' state ' ) ? . complete
2015-12-09 06:57:33 -08:00
stats.averageLevelsComplete = if @ users . size ( ) then ( _ . size ( completeSessions ) / @ users . size ( ) ) . toFixed ( 1 ) else ' N/A ' # '
2015-12-03 14:51:38 -08:00
stats.totalLevelsComplete = _ . size ( completeSessions )
2015-12-04 14:40:22 -08:00
2016-05-09 15:16:54 -07:00
enrolledUsers = @ users . filter (user) -> user . isEnrolled ( )
2015-12-04 14:40:22 -08:00
stats.enrolledUsers = _ . size ( enrolledUsers )
2015-12-03 14:51:38 -08:00
return stats
2015-12-01 15:17:21 -06:00
onClickAddStudentsButton: (e) ->
modal = new InviteToClassroomModal ( { classroom: @ classroom } )
2015-12-01 15:27:12 -06:00
@ openModalView ( modal )
2015-12-04 14:11:17 -08:00
application . tracker ? . trackEvent ' Classroom started add students ' , category: ' Courses ' , classroomID: @ classroom . id
2015-12-01 15:27:12 -06:00
onClickEnableButton: (e) ->
2015-12-09 06:57:33 -08:00
$button = $ ( e . target ) . closest ( ' .btn ' )
courseInstance = @ courseInstances . get ( $button . data ( ' course-instance-cid ' ) )
console . log ' looking for course instance ' , courseInstance , ' for ' , $button . data ( ' course-instance-cid ' ) , ' out of ' , @ courseInstances
userID = $button . data ( ' user-id ' )
$button . attr ( ' disabled ' , true )
2015-12-04 14:11:17 -08:00
application . tracker ? . trackEvent ' Course assign student ' , category: ' Courses ' , courseInstanceID: courseInstance . id , userID: userID
2015-12-02 16:07:46 -08:00
onCourseInstanceCreated = =>
courseInstance . addMember ( userID )
@ listenToOnce courseInstance , ' sync ' , @ render
2015-12-04 11:59:51 -08:00
2015-12-02 16:07:46 -08:00
if courseInstance . isNew ( )
# adding the first student to this course, so generate the course instance for it
2016-02-08 12:48:15 -08:00
if not courseInstance . saving
courseInstance . save ( null , { validate: false } )
courseInstance.saving = true
2015-12-02 16:07:46 -08:00
courseInstance . once ' sync ' , onCourseInstanceCreated
else
onCourseInstanceCreated ( )
2015-12-02 11:56:38 -08:00
2015-12-06 15:00:00 -08:00
# TODO: update newly visible level progress bar (currently all white)
2015-12-02 11:56:38 -08:00
onClickRemoveStudentLink: (e) ->
user = @ users . get ( $ ( e . target ) . closest ( ' a ' ) . data ( ' user-id ' ) )
modal = new RemoveStudentModal ( {
classroom: @ classroom
user: user
courseInstances: @ courseInstances
} )
@ openModalView ( modal )
modal . once ' remove-student ' , @ onStudentRemoved , @
2015-12-04 11:59:51 -08:00
2015-12-02 11:56:38 -08:00
onStudentRemoved: (e) ->
@ users . remove ( e . user )
2015-12-03 12:07:12 -08:00
@ render ( )
2015-12-06 20:01:52 -08:00
application . tracker ? . trackEvent ' Classroom removed student ' , category: ' Courses ' , classroomID: @ classroom . id , userID: e . user . id
2015-12-03 12:07:12 -08:00
levelPopoverContent: (level, session, i) ->
2015-12-03 13:32:22 -08:00
return null unless level
2015-12-03 12:07:12 -08:00
context = {
moment: moment
level: level
session: session
i: i
2015-12-03 13:16:57 -08:00
canViewSolution: @ teacherMode
2015-12-03 12:07:12 -08:00
}
return popoverTemplate ( context )
getLevelURL: (level, course, courseInstance, session) ->
2015-12-03 13:16:57 -08:00
return null unless @ teacherMode and _ . all ( arguments )
2016-04-13 09:54:24 -07:00
" /play/level/ #{ level . get ( ' slug ' ) } ?course= #{ course . id } &course-instance= #{ courseInstance . id } &session= #{ session . id } &observing=true "