2014-11-28 20:49:41 -05:00
{ backboneFailure , genericFailure } = require ' core/errors '
2016-03-03 17:22:50 -05:00
errors = require ' core/errors '
2014-11-28 20:49:41 -05:00
RootView = require ' views/core/RootView '
2014-01-03 13:32:13 -05:00
template = require ' templates/admin '
2014-12-06 13:05:40 -05:00
AdministerUserModal = require ' views/admin/AdministerUserModal '
2016-03-03 17:22:50 -05:00
forms = require ' core/forms '
2016-07-07 14:07:38 -04:00
Campaigns = require ' collections/Campaigns '
Classroom = require ' models/Classroom '
CocoCollection = require ' collections/CocoCollection '
Course = require ' models/Course '
2016-07-16 03:31:53 -04:00
Courses = require ' collections/Courses '
2016-07-07 14:07:38 -04:00
LevelSessions = require ' collections/LevelSessions '
2016-03-03 17:22:50 -05:00
User = require ' models/User '
2016-07-07 14:07:38 -04:00
Users = require ' collections/Users '
2014-01-03 13:32:13 -05:00
2014-07-23 10:02:45 -04:00
module.exports = class MainAdminView extends RootView
2014-06-30 22:16:26 -04:00
id: ' admin-view '
2014-01-03 13:32:13 -05:00
template: template
2014-08-30 20:09:57 -04:00
lastUserSearchValue: ' '
2014-04-25 19:57:42 -04:00
2014-02-26 17:14:43 -05:00
events:
2016-03-03 17:22:50 -05:00
' submit # espionage-form ' : ' onSubmitEspionageForm '
' submit # user-search-form ' : ' onSubmitUserSearchForm '
' click # stop-spying-btn ' : ' onClickStopSpyingButton '
2014-06-09 05:59:27 -04:00
' click # increment-button ' : ' incrementUserAttribute '
2016-07-14 14:27:15 -04:00
' click .user-spy-button ' : ' onClickUserSpyButton '
2014-12-06 13:05:40 -05:00
' click # user-search-result ' : ' onClickUserSearchResult '
2015-03-19 18:02:45 -04:00
' click # create-free-sub-btn ' : ' onClickFreeSubLink '
2015-09-25 13:03:44 -04:00
' click # terminal-create ' : ' onClickTerminalSubLink '
2016-07-07 14:07:38 -04:00
' click .classroom-progress-csv ' : ' onClickExportProgress '
2016-05-27 12:40:46 -04:00
getTitle: -> return $ . i18n . t ( ' account_settings.admin ' )
2016-03-03 17:22:50 -05:00
initialize: ->
if window . amActually
@amActually = new User ( { _id: window . amActually } )
@ amActually . fetch ( )
@ supermodel . trackModel ( @ amActually )
2016-07-07 14:07:38 -04:00
super ( )
2015-03-19 18:02:45 -04:00
2016-03-03 17:22:50 -05:00
onClickStopSpyingButton: ->
button = @ $ ( ' # stop-spying-btn ' )
forms . disableSubmit ( button )
me . stopSpying ( {
success: -> document . location . reload ( )
error: ->
forms . enableSubmit ( button )
errors . showNotyNetworkError ( arguments . . . )
} )
2014-04-25 19:57:42 -04:00
2016-03-03 17:22:50 -05:00
onSubmitEspionageForm: (e) ->
e . preventDefault ( )
button = @ $ ( ' # enter-espionage-mode ' )
2014-08-30 20:09:57 -04:00
userNameOrEmail = @ $el . find ( ' # espionage-name-or-email ' ) . val ( ) . toLowerCase ( )
2016-03-03 17:22:50 -05:00
forms . disableSubmit ( button )
me . spy ( userNameOrEmail , {
success: -> window . location . reload ( )
error: ->
forms . enableSubmit ( button )
errors . showNotyNetworkError ( arguments . . . )
} )
2014-06-09 05:59:27 -04:00
2016-07-14 14:27:15 -04:00
onClickUserSpyButton: (e) ->
e . stopPropagation ( )
userID = $ ( e . target ) . closest ( ' tr ' ) . data ( ' user-id ' )
button = $ ( e . currentTarget )
forms . disableSubmit ( button )
me . spy ( userID , {
success: -> window . location . reload ( )
error: ->
forms . enableSubmit ( button )
errors . showNotyNetworkError ( arguments . . . )
} )
2016-03-03 17:22:50 -05:00
onSubmitUserSearchForm: (e) ->
e . preventDefault ( )
searchValue = @ $el . find ( ' # user-search ' ) . val ( )
return if searchValue is @ lastUserSearchValue
return @ onSearchRequestSuccess [ ] unless @lastUserSearchValue = searchValue . toLowerCase ( )
forms . disableSubmit ( @ $ ( ' # user-search-button ' ) )
2014-08-30 20:09:57 -04:00
$ . ajax
type: ' POST ' ,
url: ' /db/user/-/admin_search '
data: { search: @ lastUserSearchValue }
success: @ onSearchRequestSuccess
error: @ onSearchRequestFailure
onSearchRequestSuccess: (users) =>
2016-03-03 17:22:50 -05:00
forms . enableSubmit ( @ $ ( ' # user-search-button ' ) )
2014-08-30 20:09:57 -04:00
result = ' '
if users . length
2016-07-14 14:27:15 -04:00
result = ( " <tr data-user-id= ' #{ user . _id } ' ><td><code> #{ user . _id } </code></td><td> #{ _ . escape ( user . name or ' Anonymous ' ) } </td><td> #{ _ . escape ( user . email ) } </td><td><button class= ' user-spy-button ' >Spy</button></td></tr> " for user in users )
2014-08-30 20:09:57 -04:00
result = " <table class= \" table \" > #{ result . join ( ' \n ' ) } </table> "
@ $el . find ( ' # user-search-result ' ) . html ( result )
onSearchRequestFailure: (jqxhr, status, error) =>
return if @ destroyed
2016-03-03 17:22:50 -05:00
forms . enableSubmit ( @ $ ( ' # user-search-button ' ) )
2014-08-30 20:09:57 -04:00
console . warn " There was an error looking up #{ @ lastUserSearchValue } : " , error
2014-06-09 05:59:27 -04:00
incrementUserAttribute: (e) ->
val = $ ( ' # increment-field ' ) . val ( )
me . set ( val , me . get ( val ) + 1 )
me . save ( )
2015-03-19 18:02:45 -04:00
2014-12-06 13:05:40 -05:00
onClickUserSearchResult: (e) ->
userID = $ ( e . target ) . closest ( ' tr ' ) . data ( ' user-id ' )
@ openModalView new AdministerUserModal ( { } , userID ) if userID
2015-03-19 18:02:45 -04:00
onClickFreeSubLink: (e) =>
delete @ freeSubLink
return unless me . isAdmin ( )
options =
url: ' /db/prepaid/-/create '
2015-08-12 18:51:16 -04:00
data: { type: ' subscription ' , maxRedeemers: 1 }
2015-03-19 18:02:45 -04:00
method: ' POST '
options.success = (model, response, options) =>
# TODO: Don't hardcode domain.
if application . isProduction ( )
@freeSubLink = " https://codecombat.com/account/subscription?_ppc= #{ model . code } "
else
@freeSubLink = " http://localhost:3000/account/subscription?_ppc= #{ model . code } "
@ render ? ( )
options.error = (model, response, options) =>
console . error ' Failed to create prepaid ' , response
@ supermodel . addRequestResource ( ' create_prepaid ' , options , 0 ) . load ( )
2015-09-25 13:03:44 -04:00
onClickTerminalSubLink: (e) =>
@freeSubLink = ' '
return unless me . isAdmin ( )
options =
url: ' /db/prepaid/-/create '
method: ' POST '
data:
type: ' terminal_subscription '
maxRedeemers: parseInt ( $ ( " # users " ) . val ( ) )
months: parseInt ( $ ( " # months " ) . val ( ) )
options.success = (model, response, options) =>
# TODO: Don't hardcode domain.
if application . isProduction ( )
@freeSubLink = " https://codecombat.com/account/prepaid?_ppc= #{ model . code } "
else
@freeSubLink = " http://localhost:3000/account/prepaid?_ppc= #{ model . code } "
@ render ? ( )
options.error = (model, response, options) =>
console . error ' Failed to create prepaid ' , response
@ supermodel . addRequestResource ( ' create_prepaid ' , options , 0 ) . load ( )
2016-07-07 14:07:38 -04:00
onClickExportProgress: ->
$ ( ' .classroom-progress-csv ' ) . prop ( ' disabled ' , true )
classCode = $ ( ' .classroom-progress-class-code ' ) . val ( )
2016-07-08 21:24:42 -04:00
classroom = null
2016-07-16 03:31:53 -04:00
courses = null
2016-07-08 21:47:06 -04:00
courseLevels = [ ]
2016-07-08 21:24:42 -04:00
sessions = null
users = null
2016-07-07 14:07:38 -04:00
userMap = { }
2016-07-08 21:24:42 -04:00
Promise . resolve ( new Classroom ( ) . fetchByCode ( classCode ) )
. then (model) =>
classroom = new Classroom ( { _id: model . data . _id } )
Promise . resolve ( classroom . fetch ( ) )
. then (model) =>
2016-07-16 03:31:53 -04:00
courses = new Courses ( )
Promise . resolve ( courses . fetch ( ) )
. then (models) =>
2016-07-08 21:47:06 -04:00
for course , index in classroom . get ( ' courses ' )
for level in course . levels
courseLevels . push
courseIndex: index + 1
levelID: level . original
slug: level . slug
2016-07-16 03:31:53 -04:00
courseSlug: courses . get ( course . _id ) . get ( ' slug ' )
2016-07-08 21:24:42 -04:00
users = new Users ( )
Promise . resolve ( $ . when ( users . fetchForClassroom ( classroom ) . . . ) )
. then (models) =>
2016-07-07 18:18:00 -04:00
userMap [ user . id ] = user for user in users . models
2016-07-08 21:24:42 -04:00
sessions = new LevelSessions ( )
Promise . resolve ( $ . when ( sessions . fetchForAllClassroomMembers ( classroom ) . . . ) )
. then (models) =>
2016-07-07 14:07:38 -04:00
userLevelPlaytimeMap = { }
for session in sessions . models
continue unless session . get ( ' state ' ) ? . complete
levelID = session . get ( ' level ' ) . original
userID = session . get ( ' creator ' )
userLevelPlaytimeMap [ userID ] ? = { }
userLevelPlaytimeMap [ userID ] [ levelID ] ? = { }
userLevelPlaytimeMap [ userID ] [ levelID ] = session . get ( ' playtime ' )
userPlaytimes = [ ]
for userID , user of userMap
playtimes = [ user . get ( ' name ' ) ? ' Anonymous ' ]
2016-07-08 21:47:06 -04:00
for level in courseLevels
2016-07-07 14:07:38 -04:00
if userLevelPlaytimeMap [ userID ] ? [ level . levelID ] ?
rawSeconds = parseInt ( userLevelPlaytimeMap [ userID ] [ level . levelID ] )
hours = Math . floor ( rawSeconds / 60 / 60 )
minutes = Math . floor ( rawSeconds / 60 - hours * 60 )
seconds = Math . round ( rawSeconds - hours * 60 - minutes * 60 )
hours = " 0 #{ hours } " if hours < 10
minutes = " 0 #{ minutes } " if minutes < 10
seconds = " 0 #{ seconds } " if seconds < 10
playtimes . push " #{ hours } : #{ minutes } : #{ seconds } "
else
playtimes . push ' Incomplete '
userPlaytimes . push ( playtimes )
columnLabels = " Username "
currentLevel = 1
2016-07-16 03:31:53 -04:00
courseLabelIndexes = CS: 1 , GD: 0 , WD: 0
2016-07-07 14:07:38 -04:00
lastCourseIndex = 1
2016-07-16 03:31:53 -04:00
lastCourseLabel = ' CS1 '
2016-07-08 21:47:06 -04:00
for level in courseLevels
2016-07-07 14:07:38 -04:00
unless level . courseIndex is lastCourseIndex
currentLevel = 1
lastCourseIndex = level . courseIndex
2016-07-16 03:31:53 -04:00
acronym = switch
when /game-dev/ . test ( level . courseSlug ) then ' GD '
when /web-dev/ . test ( level . courseSlug ) then ' WD '
else ' CS '
lastCourseLabel = acronym + ++ courseLabelIndexes [ acronym ]
columnLabels += " , #{ lastCourseLabel } . #{ currentLevel ++ } #{ level . slug } "
2016-07-07 14:07:38 -04:00
csvContent = " data:text/csv;charset=utf-8, #{ columnLabels } \n "
for studentRow in userPlaytimes
csvContent += studentRow . join ( ' , ' ) + " \n "
csvContent = csvContent . substring ( 0 , csvContent . length - 1 )
encodedUri = encodeURI ( csvContent )
window . open ( encodedUri )
$ ( ' .classroom-progress-csv ' ) . prop ( ' disabled ' , false )
. catch (error) ->
$ ( ' .classroom-progress-csv ' ) . prop ( ' disabled ' , false )
console . error error
2016-07-08 21:24:42 -04:00
throw error