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 '
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 '
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
if me . isAdmin ( )
@campaigns = new Campaigns ( )
@ supermodel . trackRequest @ campaigns . fetchByType ( ' course ' , { data: { project: ' levels ' } } )
@courses = new CocoCollection ( [ ] , { url: " /db/course " , model: Course } )
@ supermodel . loadCollection ( @ courses , ' courses ' )
super ( )
onLoaded: ->
campaignCourseIndexMap = { }
for course , index in @ courses . models
campaignCourseIndexMap [ course . get ( ' campaignID ' ) ] = index + 1
@courseLevels = [ ]
for campaign in @ campaigns . models
continue unless campaignCourseIndexMap [ campaign . id ]
for levelID , level of campaign . get ( ' levels ' )
@ courseLevels . push ( {
levelID
slug: level . slug
courseIndex: campaignCourseIndexMap [ campaign . id ]
} )
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-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-05-26 19:46:03 -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></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: ->
return unless @ courseLevels ? . length > 0
$ ( ' .classroom-progress-csv ' ) . prop ( ' disabled ' , true )
classCode = $ ( ' .classroom-progress-class-code ' ) . val ( )
userMap = { }
new Promise ( (resolve, reject) =>
new Classroom ( ) . fetchByCode ( classCode , {
success: resolve
error: (model, response, options) => reject ( response )
} )
)
. then (classroom) =>
new Promise ( (resolve, reject) =>
new Classroom ( { _id: classroom . id } ) . fetch ( {
success: resolve
error: (model, response, options) => reject ( response )
} )
)
. then (classroom) =>
new Promise ( (resolve, reject) =>
new Users ( ) . fetchForClassroom ( classroom , {
2016-07-07 18:18:00 -04:00
success: (models, response, options) =>
resolve ( [ classroom , models ] ) if models ? . loaded
error: (models, response, options) => reject ( response )
} )
)
. then ([classroom, users]) =>
userMap [ user . id ] = user for user in users . models
new Promise ( (resolve, reject) =>
new LevelSessions ( ) . fetchForAllClassroomMembers ( classroom , {
success: (models, response, options) =>
resolve ( models ) if models ? . loaded
error: (models, response, options) => reject ( response )
2016-07-07 14:07:38 -04:00
} )
)
. then (sessions) =>
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 ' ]
for level in @ courseLevels
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
lastCourseIndex = 1
for level in @ courseLevels
unless level . courseIndex is lastCourseIndex
currentLevel = 1
lastCourseIndex = level . courseIndex
columnLabels += " ,CS #{ level . courseIndex } . #{ currentLevel ++ } #{ level . slug } "
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