{backboneFailure, genericFailure} = require 'core/errors' errors = require 'core/errors' RootView = require 'views/core/RootView' template = require 'templates/admin' AdministerUserModal = require 'views/admin/AdministerUserModal' forms = require 'core/forms' Campaigns = require 'collections/Campaigns' Classroom = require 'models/Classroom' CocoCollection = require 'collections/CocoCollection' Course = require 'models/Course' Courses = require 'collections/Courses' LevelSessions = require 'collections/LevelSessions' User = require 'models/User' Users = require 'collections/Users' module.exports = class MainAdminView extends RootView id: 'admin-view' template: template lastUserSearchValue: '' events: 'submit #espionage-form': 'onSubmitEspionageForm' 'submit #user-search-form': 'onSubmitUserSearchForm' 'click #stop-spying-btn': 'onClickStopSpyingButton' 'click #increment-button': 'incrementUserAttribute' 'click .user-spy-button': 'onClickUserSpyButton' 'click #user-search-result': 'onClickUserSearchResult' 'click #create-free-sub-btn': 'onClickFreeSubLink' 'click #terminal-create': 'onClickTerminalSubLink' 'click .classroom-progress-csv': 'onClickExportProgress' getTitle: -> return $.i18n.t('account_settings.admin') initialize: -> if window.amActually @amActually = new User({_id: window.amActually}) @amActually.fetch() @supermodel.trackModel(@amActually) super() onClickStopSpyingButton: -> button = @$('#stop-spying-btn') forms.disableSubmit(button) me.stopSpying({ success: -> document.location.reload() error: -> forms.enableSubmit(button) errors.showNotyNetworkError(arguments...) }) onSubmitEspionageForm: (e) -> e.preventDefault() button = @$('#enter-espionage-mode') userNameOrEmail = @$el.find('#espionage-name-or-email').val().toLowerCase() forms.disableSubmit(button) me.spy(userNameOrEmail, { success: -> window.location.reload() error: -> forms.enableSubmit(button) errors.showNotyNetworkError(arguments...) }) 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...) }) 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')) $.ajax type: 'POST', url: '/db/user/-/admin_search' data: {search: @lastUserSearchValue} success: @onSearchRequestSuccess error: @onSearchRequestFailure onSearchRequestSuccess: (users) => forms.enableSubmit(@$('#user-search-button')) result = '' if users.length result = ("#{user._id}#{_.escape(user.name or 'Anonymous')}#{_.escape(user.email)}" for user in users) result = "#{result.join('\n')}
" @$el.find('#user-search-result').html(result) onSearchRequestFailure: (jqxhr, status, error) => return if @destroyed forms.enableSubmit(@$('#user-search-button')) console.warn "There was an error looking up #{@lastUserSearchValue}:", error incrementUserAttribute: (e) -> val = $('#increment-field').val() me.set(val, me.get(val) + 1) me.save() onClickUserSearchResult: (e) -> userID = $(e.target).closest('tr').data('user-id') @openModalView new AdministerUserModal({}, userID) if userID onClickFreeSubLink: (e) => delete @freeSubLink return unless me.isAdmin() options = url: '/db/prepaid/-/create' data: {type: 'subscription', maxRedeemers: 1} 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() 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() onClickExportProgress: -> $('.classroom-progress-csv').prop('disabled', true) classCode = $('.classroom-progress-class-code').val() classroom = null courses = null courseLevels = [] sessions = null users = null userMap = {} Promise.resolve(new Classroom().fetchByCode(classCode)) .then (model) => classroom = new Classroom({ _id: model.data._id }) Promise.resolve(classroom.fetch()) .then (model) => courses = new Courses() Promise.resolve(courses.fetch()) .then (models) => for course, index in classroom.get('courses') for level in course.levels courseLevels.push courseIndex: index + 1 levelID: level.original slug: level.slug courseSlug: courses.get(course._id).get('slug') users = new Users() Promise.resolve($.when(users.fetchForClassroom(classroom)...)) .then (models) => userMap[user.id] = user for user in users.models sessions = new LevelSessions() Promise.resolve($.when(sessions.fetchForAllClassroomMembers(classroom)...)) .then (models) => 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 courseLabelIndexes = CS: 1, GD: 0, WD: 0 lastCourseIndex = 1 lastCourseLabel = 'CS1' for level in courseLevels unless level.courseIndex is lastCourseIndex currentLevel = 1 lastCourseIndex = level.courseIndex 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}" 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 throw error courseLabelsArray: (courses) -> labels = [] courseLabelIndexes = CS: 0, GD: 0, WD: 0 for course in courses labels.push acronym + ++courseLabelIndexes[acronym] labels