mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-01-05 12:12:26 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
5a98926d52
27 changed files with 118 additions and 267 deletions
|
@ -56,6 +56,7 @@ so we can accept your pull requests. It is easy.
|
||||||
![Josh Callebaut](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Josh%20Callebaut/josh_callebaut_100.png "Josh Callebaut")
|
![Josh Callebaut](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Josh%20Callebaut/josh_callebaut_100.png "Josh Callebaut")
|
||||||
![Michael Schmatz](http://codecombat.com/images/pages/about/michael_small.png "Michael Schmatz")
|
![Michael Schmatz](http://codecombat.com/images/pages/about/michael_small.png "Michael Schmatz")
|
||||||
![Josh Lee](http://codecombat.com/images/pages/about/josh_small.png "Josh Lee")
|
![Josh Lee](http://codecombat.com/images/pages/about/josh_small.png "Josh Lee")
|
||||||
|
![Dan TDM](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Dan_TDM/dan_tdm_100.png "Dan TDM")
|
||||||
![Alex Cotsarelis](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Alex%20Cotsarelis/alex_100.png "Alex Cotsarelis")
|
![Alex Cotsarelis](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Alex%20Cotsarelis/alex_100.png "Alex Cotsarelis")
|
||||||
![Alex Crooks](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Alex%20Crooks/alex_100.png "Alex Crooks")
|
![Alex Crooks](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Alex%20Crooks/alex_100.png "Alex Crooks")
|
||||||
![Alexandru Caciulescu](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Alexandru%20Caciulescu/alexandru_100.png "Alexandru Caciulescu")
|
![Alexandru Caciulescu](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Alexandru%20Caciulescu/alexandru_100.png "Alexandru Caciulescu")
|
||||||
|
|
|
@ -5,12 +5,6 @@ module.exports = class LevelSessionCollection extends CocoCollection
|
||||||
url: '/db/level.session'
|
url: '/db/level.session'
|
||||||
model: LevelSession
|
model: LevelSession
|
||||||
|
|
||||||
fetchMineForCourseInstance: (courseInstanceID, options) ->
|
|
||||||
options = _.extend({
|
|
||||||
url: "/db/course_instance/#{courseInstanceID}/my-course-level-sessions"
|
|
||||||
}, options)
|
|
||||||
@fetch(options)
|
|
||||||
|
|
||||||
fetchForCourseInstance: (courseInstanceID, options) ->
|
fetchForCourseInstance: (courseInstanceID, options) ->
|
||||||
options = _.extend({
|
options = _.extend({
|
||||||
url: "/db/course_instance/#{courseInstanceID}/my-course-level-sessions"
|
url: "/db/course_instance/#{courseInstanceID}/my-course-level-sessions"
|
||||||
|
|
|
@ -478,7 +478,7 @@ module.exports = class LevelLoader extends CocoClass
|
||||||
@world.difficulty = @session?.get('state')?.difficulty ? 0
|
@world.difficulty = @session?.get('state')?.difficulty ? 0
|
||||||
if @observing
|
if @observing
|
||||||
@world.difficulty = Math.max 0, @world.difficulty - 1 # Show the difficulty they won, not the next one.
|
@world.difficulty = Math.max 0, @world.difficulty - 1 # Show the difficulty they won, not the next one.
|
||||||
serializedLevel = @level.serialize(@supermodel, @session, @opponentSession)
|
serializedLevel = @level.serialize {@supermodel, @session, @opponentSession, @headless, @sessionless}
|
||||||
@world.loadFromLevel serializedLevel, false
|
@world.loadFromLevel serializedLevel, false
|
||||||
console.log 'World has been initialized from level loader.' if LOG
|
console.log 'World has been initialized from level loader.' if LOG
|
||||||
|
|
||||||
|
|
|
@ -226,7 +226,7 @@ module.exports = class Simulator extends CocoClass
|
||||||
@levelLoader = null
|
@levelLoader = null
|
||||||
|
|
||||||
setupGod: ->
|
setupGod: ->
|
||||||
@god.setLevel @level.serialize(@supermodel, @session, @otherSession)
|
@god.setLevel @level.serialize {@supermodel, @session, @otherSession, headless: true, sessionless: false}
|
||||||
@god.setLevelSessionIDs (session.sessionID for session in @task.getSessions())
|
@god.setLevelSessionIDs (session.sessionID for session in @task.getSessions())
|
||||||
@god.setWorldClassMap @world.classMap
|
@god.setWorldClassMap @world.classMap
|
||||||
@god.setGoalManager new GoalManager @world, @level.get('goals'), null, {headless: true}
|
@god.setGoalManager new GoalManager @world, @level.get('goals'), null, {headless: true}
|
||||||
|
|
|
@ -12,7 +12,8 @@ module.exports = class Level extends CocoModel
|
||||||
urlRoot: '/db/level'
|
urlRoot: '/db/level'
|
||||||
editableByArtisans: true
|
editableByArtisans: true
|
||||||
|
|
||||||
serialize: (supermodel, session, otherSession, cached=false) ->
|
serialize: (options) ->
|
||||||
|
{supermodel, session, otherSession, @headless, @sessionless, cached=false} = options
|
||||||
o = @denormalize supermodel, session, otherSession # hot spot to optimize
|
o = @denormalize supermodel, session, otherSession # hot spot to optimize
|
||||||
|
|
||||||
# Figure out Components
|
# Figure out Components
|
||||||
|
@ -146,7 +147,7 @@ module.exports = class Level extends CocoModel
|
||||||
levelThang.components.push placeholderComponent
|
levelThang.components.push placeholderComponent
|
||||||
|
|
||||||
# Load the user's chosen hero AFTER getting stats from default char
|
# Load the user's chosen hero AFTER getting stats from default char
|
||||||
if /Hero Placeholder/.test(levelThang.id) and @get('type', true) in ['course']
|
if /Hero Placeholder/.test(levelThang.id) and @get('type', true) in ['course'] and not @headless and not @sessionless
|
||||||
heroThangType = me.get('heroConfig')?.thangType or ThangType.heroes.captain
|
heroThangType = me.get('heroConfig')?.thangType or ThangType.heroes.captain
|
||||||
levelThang.thangType = heroThangType if heroThangType
|
levelThang.thangType = heroThangType if heroThangType
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ _.extend LevelSessionSchema.properties,
|
||||||
changed: c.date
|
changed: c.date
|
||||||
title: 'Changed'
|
title: 'Changed'
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
|
||||||
dateFirstCompleted: {} # c.stringDate
|
dateFirstCompleted: {} # c.stringDate
|
||||||
# title: 'Completed'
|
# title: 'Completed'
|
||||||
# readOnly: true
|
# readOnly: true
|
||||||
|
@ -62,9 +62,6 @@ _.extend LevelSessionSchema.properties,
|
||||||
team: c.shortString()
|
team: c.shortString()
|
||||||
level: LevelSessionLevelSchema
|
level: LevelSessionLevelSchema
|
||||||
|
|
||||||
screenshot:
|
|
||||||
type: 'string'
|
|
||||||
|
|
||||||
heroConfig: c.HeroConfigSchema
|
heroConfig: c.HeroConfigSchema
|
||||||
|
|
||||||
state: c.object {},
|
state: c.object {},
|
||||||
|
@ -208,14 +205,6 @@ _.extend LevelSessionSchema.properties,
|
||||||
submittedCodeLanguage:
|
submittedCodeLanguage:
|
||||||
type: 'string'
|
type: 'string'
|
||||||
|
|
||||||
transpiledCode:
|
|
||||||
type: 'object'
|
|
||||||
additionalProperties:
|
|
||||||
type: 'object'
|
|
||||||
additionalProperties:
|
|
||||||
type: 'string'
|
|
||||||
format: 'code'
|
|
||||||
|
|
||||||
isRanking:
|
isRanking:
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
description: 'Whether this session is still in the first ranking chain after being submitted.'
|
description: 'Whether this session is still in the first ranking chain after being submitted.'
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
#admin-level-sessions-view
|
|
||||||
.session_tile
|
|
||||||
display: inline-block
|
|
||||||
position: relative
|
|
||||||
margin: 8px
|
|
||||||
|
|
||||||
.session_info
|
|
||||||
position: absolute
|
|
||||||
top: 0
|
|
||||||
left: 0
|
|
||||||
right: 0
|
|
||||||
text-align: center
|
|
||||||
background: rgba(0, 0, 0, 0.5)
|
|
||||||
color: white
|
|
|
@ -30,8 +30,6 @@ block content
|
||||||
h4 Entities
|
h4 Entities
|
||||||
|
|
||||||
ul
|
ul
|
||||||
li
|
|
||||||
a(href="/admin/level-sessions") Active Instances
|
|
||||||
li
|
li
|
||||||
a(href="/admin/trial-requests") Trial Requests
|
a(href="/admin/trial-requests") Trial Requests
|
||||||
li
|
li
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
extends /templates/base
|
|
||||||
|
|
||||||
block content
|
|
||||||
|
|
||||||
h1 Latest Games
|
|
||||||
|
|
||||||
each session in view.sessions.models
|
|
||||||
- var url = '/play/level/'+session.get('levelID')+'?session='+session.id
|
|
||||||
.session_tile
|
|
||||||
a(href=url)
|
|
||||||
if session.get('screenshot')
|
|
||||||
img(src=session.get('screenshot'))
|
|
||||||
else
|
|
||||||
img(src="/images/generic-icon.png")
|
|
||||||
.session_info
|
|
||||||
.level_name= session.get('levelName')
|
|
||||||
.creator_name= session.get('creatorName')
|
|
|
@ -132,8 +132,9 @@ block outer_content
|
||||||
|
|
||||||
div.tab-pane#editor-level-tasks-tab-view
|
div.tab-pane#editor-level-tasks-tab-view
|
||||||
|
|
||||||
div.tab-pane#editor-level-patches
|
div.tab-pane#editor-level-patches.nano
|
||||||
.patches-view
|
.nano-content
|
||||||
|
.patches-view
|
||||||
|
|
||||||
div.tab-pane#related-achievements-view
|
div.tab-pane#related-achievements-view
|
||||||
|
|
||||||
|
|
|
@ -83,21 +83,21 @@ module.exports = class AnalyticsView extends RootView
|
||||||
campaignDauTotal += count
|
campaignDauTotal += count
|
||||||
else if event.indexOf('DAU classroom') >= 0
|
else if event.indexOf('DAU classroom') >= 0
|
||||||
classroomDauTotal += count
|
classroomDauTotal += count
|
||||||
eventMap[event] = true;
|
eventMap[event] = true
|
||||||
entry.events['DAU campaign total'] = campaignDauTotal
|
entry.events['DAU campaign total'] = campaignDauTotal
|
||||||
eventMap['DAU campaign total'] = true;
|
eventMap['DAU campaign total'] = true
|
||||||
campaignDauTotals.unshift(campaignDauTotal)
|
campaignDauTotals.unshift(campaignDauTotal)
|
||||||
campaignDauTotals.pop() while campaignDauTotals.length > 30
|
campaignDauTotals.pop() while campaignDauTotals.length > 30
|
||||||
if campaignDauTotals.length is 30
|
if campaignDauTotals.length is 30
|
||||||
entry.events['DAU campaign 30-day average'] = Math.round(_.reduce(campaignDauTotals, (a, b) -> a + b) / 30)
|
entry.events['DAU campaign 30-day average'] = Math.round(_.reduce(campaignDauTotals, (a, b) -> a + b) / 30)
|
||||||
eventMap['DAU campaign 30-day average'] = true;
|
eventMap['DAU campaign 30-day average'] = true
|
||||||
entry.events['DAU classroom total'] = classroomDauTotal
|
entry.events['DAU classroom total'] = classroomDauTotal
|
||||||
eventMap['DAU classroom total'] = true;
|
eventMap['DAU classroom total'] = true
|
||||||
classroomDauTotals.unshift(classroomDauTotal)
|
classroomDauTotals.unshift(classroomDauTotal)
|
||||||
classroomDauTotals.pop() while classroomDauTotals.length > 30
|
classroomDauTotals.pop() while classroomDauTotals.length > 30
|
||||||
if classroomDauTotals.length is 30
|
if classroomDauTotals.length is 30
|
||||||
entry.events['DAU classroom 30-day average'] = Math.round(_.reduce(classroomDauTotals, (a, b) -> a + b) / 30)
|
entry.events['DAU classroom 30-day average'] = Math.round(_.reduce(classroomDauTotals, (a, b) -> a + b) / 30)
|
||||||
eventMap['DAU classroom 30-day average'] = true;
|
eventMap['DAU classroom 30-day average'] = true
|
||||||
|
|
||||||
@activeUsers.sort (a, b) -> b.day.localeCompare(a.day)
|
@activeUsers.sort (a, b) -> b.day.localeCompare(a.day)
|
||||||
@activeUserEventNames = Object.keys(eventMap)
|
@activeUserEventNames = Object.keys(eventMap)
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
RootView = require 'views/core/RootView'
|
|
||||||
template = require 'templates/admin/level_sessions'
|
|
||||||
LevelSession = require 'models/LevelSession'
|
|
||||||
CocoCollection = require 'collections/CocoCollection'
|
|
||||||
|
|
||||||
class LevelSessionCollection extends CocoCollection
|
|
||||||
url: '/db/level.session/x/active?project=screenshot,levelName,creatorName'
|
|
||||||
model: LevelSession
|
|
||||||
|
|
||||||
module.exports = class LevelSessionsView extends RootView
|
|
||||||
id: 'admin-level-sessions-view'
|
|
||||||
template: template
|
|
||||||
|
|
||||||
constructor: (options) ->
|
|
||||||
super options
|
|
||||||
@getLevelSessions()
|
|
||||||
|
|
||||||
getLevelSessions: ->
|
|
||||||
@sessions = @supermodel.loadCollection(new LevelSessionCollection(), 'sessions', {cache: false}).model
|
|
|
@ -23,6 +23,7 @@ module.exports = class MainAdminView extends RootView
|
||||||
'submit #user-search-form': 'onSubmitUserSearchForm'
|
'submit #user-search-form': 'onSubmitUserSearchForm'
|
||||||
'click #stop-spying-btn': 'onClickStopSpyingButton'
|
'click #stop-spying-btn': 'onClickStopSpyingButton'
|
||||||
'click #increment-button': 'incrementUserAttribute'
|
'click #increment-button': 'incrementUserAttribute'
|
||||||
|
'click .user-spy-button': 'onClickUserSpyButton'
|
||||||
'click #user-search-result': 'onClickUserSearchResult'
|
'click #user-search-result': 'onClickUserSearchResult'
|
||||||
'click #create-free-sub-btn': 'onClickFreeSubLink'
|
'click #create-free-sub-btn': 'onClickFreeSubLink'
|
||||||
'click #terminal-create': 'onClickTerminalSubLink'
|
'click #terminal-create': 'onClickTerminalSubLink'
|
||||||
|
@ -59,6 +60,18 @@ module.exports = class MainAdminView extends RootView
|
||||||
errors.showNotyNetworkError(arguments...)
|
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) ->
|
onSubmitUserSearchForm: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
searchValue = @$el.find('#user-search').val()
|
searchValue = @$el.find('#user-search').val()
|
||||||
|
@ -76,7 +89,7 @@ module.exports = class MainAdminView extends RootView
|
||||||
forms.enableSubmit(@$('#user-search-button'))
|
forms.enableSubmit(@$('#user-search-button'))
|
||||||
result = ''
|
result = ''
|
||||||
if users.length
|
if users.length
|
||||||
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)
|
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)
|
||||||
result = "<table class=\"table\">#{result.join('\n')}</table>"
|
result = "<table class=\"table\">#{result.join('\n')}</table>"
|
||||||
@$el.find('#user-search-result').html(result)
|
@$el.find('#user-search-result').html(result)
|
||||||
|
|
||||||
|
|
|
@ -595,7 +595,7 @@ module.exports = class ThangsTabView extends CocoView
|
||||||
@level.set 'thangs', thangs
|
@level.set 'thangs', thangs
|
||||||
return if @editThangView
|
return if @editThangView
|
||||||
return if skipSerialization
|
return if skipSerialization
|
||||||
serializedLevel = @level.serialize @supermodel, null, null, true
|
serializedLevel = @level.serialize {@supermodel, session: null, otherSession: null, headless: false, sessionless: true, cached: true}
|
||||||
try
|
try
|
||||||
@world.loadFromLevel serializedLevel, false
|
@world.loadFromLevel serializedLevel, false
|
||||||
catch error
|
catch error
|
||||||
|
|
|
@ -25,9 +25,9 @@ module.exports = class VerifierTest extends CocoClass
|
||||||
@loadStartTime = new Date()
|
@loadStartTime = new Date()
|
||||||
@god = new God maxAngels: 1, headless: true
|
@god = new God maxAngels: 1, headless: true
|
||||||
@levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, headless: true, fakeSessionConfig: {codeLanguage: @language, callback: @configureSession}
|
@levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, headless: true, fakeSessionConfig: {codeLanguage: @language, callback: @configureSession}
|
||||||
@listenToOnce @levelLoader, 'world-necessities-loaded', @onWorldNecessitiesLoaded
|
@listenToOnce @levelLoader, 'world-necessities-loaded', -> _.defer @onWorldNecessitiesLoaded
|
||||||
|
|
||||||
onWorldNecessitiesLoaded: ->
|
onWorldNecessitiesLoaded: =>
|
||||||
# Called when we have enough to build the world, but not everything is loaded
|
# Called when we have enough to build the world, but not everything is loaded
|
||||||
@grabLevelLoaderData()
|
@grabLevelLoaderData()
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ module.exports = class VerifierTest extends CocoClass
|
||||||
@solution = @levelLoader.session.solution
|
@solution = @levelLoader.session.solution
|
||||||
|
|
||||||
setupGod: ->
|
setupGod: ->
|
||||||
@god.setLevel @level.serialize @supermodel, @session
|
@god.setLevel @level.serialize {@supermodel, @session, otherSession: null, headless: true, sessionless: false}
|
||||||
@god.setLevelSessionIDs [@session.id]
|
@god.setLevelSessionIDs [@session.id]
|
||||||
@god.setWorldClassMap @world.classMap
|
@god.setWorldClassMap @world.classMap
|
||||||
@god.lastFlagHistory = @session.get('state').flagHistory
|
@god.lastFlagHistory = @session.get('state').flagHistory
|
||||||
|
@ -134,8 +134,10 @@ module.exports = class VerifierTest extends CocoClass
|
||||||
setTimeout @cleanup, 100
|
setTimeout @cleanup, 100
|
||||||
|
|
||||||
cleanup: =>
|
cleanup: =>
|
||||||
|
if @levelLoader
|
||||||
|
@stopListening @levelLoader
|
||||||
|
@levelLoader.destroy()
|
||||||
if @god
|
if @god
|
||||||
@stopListening @god
|
@stopListening @god
|
||||||
@god.destroy()
|
@god.destroy()
|
||||||
|
|
||||||
@world = null
|
@world = null
|
||||||
|
|
|
@ -42,6 +42,7 @@ module.exports = class LadderView extends RootView
|
||||||
initialize: (options, @levelID, @leagueType, @leagueID) ->
|
initialize: (options, @levelID, @leagueType, @leagueID) ->
|
||||||
@level = @supermodel.loadModel(new Level(_id: @levelID)).model
|
@level = @supermodel.loadModel(new Level(_id: @levelID)).model
|
||||||
@level.once 'sync', =>
|
@level.once 'sync', =>
|
||||||
|
return if @destroyed
|
||||||
@levelDescription = marked(@level.get('description')) if @level.get('description')
|
@levelDescription = marked(@level.get('description')) if @level.get('description')
|
||||||
@teams = teamDataFromLevel @level
|
@teams = teamDataFromLevel @level
|
||||||
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(@levelID), 'your_sessions', {cache: false}).model
|
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(@levelID), 'your_sessions', {cache: false}).model
|
||||||
|
|
|
@ -69,7 +69,7 @@ module.exports = class SpectateLevelView extends RootView
|
||||||
@load()
|
@load()
|
||||||
|
|
||||||
setLevel: (@level, @supermodel) ->
|
setLevel: (@level, @supermodel) ->
|
||||||
serializedLevel = @level.serialize @supermodel, @session, @otherSession
|
serializedLevel = @level.serialize {@supermodel, @session, @otherSession, headless: false, sessionless: false}
|
||||||
@god?.setLevel serializedLevel
|
@god?.setLevel serializedLevel
|
||||||
if @world
|
if @world
|
||||||
@world.loadFromLevel serializedLevel, false
|
@world.loadFromLevel serializedLevel, false
|
||||||
|
@ -106,7 +106,7 @@ module.exports = class SpectateLevelView extends RootView
|
||||||
#at this point, all requisite data is loaded, and sessions are not denormalized
|
#at this point, all requisite data is loaded, and sessions are not denormalized
|
||||||
team = @world.teamForPlayer(0)
|
team = @world.teamForPlayer(0)
|
||||||
@loadOpponentTeam(team)
|
@loadOpponentTeam(team)
|
||||||
@god.setLevel @level.serialize @supermodel, @session, @otherSession
|
@god.setLevel @level.serialize {@supermodel, @session, @otherSession, headless: false, sessionless: false}
|
||||||
@god.setLevelSessionIDs if @otherSession then [@session.id, @otherSession.id] else [@session.id]
|
@god.setLevelSessionIDs if @otherSession then [@session.id, @otherSession.id] else [@session.id]
|
||||||
@god.setWorldClassMap @world.classMap
|
@god.setWorldClassMap @world.classMap
|
||||||
@setTeam team
|
@setTeam team
|
||||||
|
|
|
@ -69,7 +69,6 @@ module.exports = class PlayLevelView extends RootView
|
||||||
'god:infinite-loop': 'onInfiniteLoop'
|
'god:infinite-loop': 'onInfiniteLoop'
|
||||||
'level:reload-from-data': 'onLevelReloadFromData'
|
'level:reload-from-data': 'onLevelReloadFromData'
|
||||||
'level:reload-thang-type': 'onLevelReloadThangType'
|
'level:reload-thang-type': 'onLevelReloadThangType'
|
||||||
'level:session-will-save': 'onSessionWillSave'
|
|
||||||
'level:started': 'onLevelStarted'
|
'level:started': 'onLevelStarted'
|
||||||
'level:loading-view-unveiling': 'onLoadingViewUnveiling'
|
'level:loading-view-unveiling': 'onLoadingViewUnveiling'
|
||||||
'level:loading-view-unveiled': 'onLoadingViewUnveiled'
|
'level:loading-view-unveiled': 'onLoadingViewUnveiled'
|
||||||
|
@ -112,7 +111,6 @@ module.exports = class PlayLevelView extends RootView
|
||||||
@gameUIState = new GameUIState()
|
@gameUIState = new GameUIState()
|
||||||
|
|
||||||
$(window).on 'resize', @onWindowResize
|
$(window).on 'resize', @onWindowResize
|
||||||
@saveScreenshot = _.throttle @saveScreenshot, 30000
|
|
||||||
|
|
||||||
application.tracker?.enableInspectletJS(@levelID)
|
application.tracker?.enableInspectletJS(@levelID)
|
||||||
|
|
||||||
|
@ -130,7 +128,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
@supermodel.collections = givenSupermodel.collections
|
@supermodel.collections = givenSupermodel.collections
|
||||||
@supermodel.shouldSaveBackups = givenSupermodel.shouldSaveBackups
|
@supermodel.shouldSaveBackups = givenSupermodel.shouldSaveBackups
|
||||||
|
|
||||||
serializedLevel = @level.serialize @supermodel, @session, @otherSession
|
serializedLevel = @level.serialize {@supermodel, @session, @otherSession, headless: false, sessionless: false}
|
||||||
@god?.setLevel serializedLevel
|
@god?.setLevel serializedLevel
|
||||||
if @world
|
if @world
|
||||||
@world.loadFromLevel serializedLevel, false
|
@world.loadFromLevel serializedLevel, false
|
||||||
|
@ -246,7 +244,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
@session.set 'multiplayer', false
|
@session.set 'multiplayer', false
|
||||||
|
|
||||||
setupGod: ->
|
setupGod: ->
|
||||||
@god.setLevel @level.serialize @supermodel, @session, @otherSession
|
@god.setLevel @level.serialize {@supermodel, @session, @otherSession, headless: false, sessionless: false}
|
||||||
@god.setLevelSessionIDs if @otherSession then [@session.id, @otherSession.id] else [@session.id]
|
@god.setLevelSessionIDs if @otherSession then [@session.id, @otherSession.id] else [@session.id]
|
||||||
@god.setWorldClassMap @world.classMap
|
@god.setWorldClassMap @world.classMap
|
||||||
|
|
||||||
|
@ -594,15 +592,6 @@ module.exports = class PlayLevelView extends RootView
|
||||||
@bus.removeFirebaseData =>
|
@bus.removeFirebaseData =>
|
||||||
@bus.disconnect()
|
@bus.disconnect()
|
||||||
|
|
||||||
onSessionWillSave: (e) ->
|
|
||||||
# Something interesting has happened, so (at a lower frequency), we'll save a screenshot.
|
|
||||||
#@saveScreenshot e.session
|
|
||||||
|
|
||||||
# Throttled
|
|
||||||
saveScreenshot: (session) =>
|
|
||||||
return unless screenshot = @surface?.screenshot()
|
|
||||||
session.save {screenshot: screenshot}, {patch: true, type: 'PUT'}
|
|
||||||
|
|
||||||
onContactClicked: (e) ->
|
onContactClicked: (e) ->
|
||||||
Backbone.Mediator.publish 'level:contact-button-pressed', {}
|
Backbone.Mediator.publish 'level:contact-button-pressed', {}
|
||||||
@openModalView contactModal = new ContactModal levelID: @level.get('slug') or @level.id, courseID: @courseID, courseInstanceID: @courseInstanceID
|
@openModalView contactModal = new ContactModal levelID: @level.get('slug') or @level.id, courseID: @courseID, courseInstanceID: @courseInstanceID
|
||||||
|
@ -954,20 +943,6 @@ module.exports = class PlayLevelView extends RootView
|
||||||
console.error 'Failed to read sessionState in onRealTimeMultiplayerCast'
|
console.error 'Failed to read sessionState in onRealTimeMultiplayerCast'
|
||||||
|
|
||||||
console.info 'Submitting my code'
|
console.info 'Submitting my code'
|
||||||
# Transpiling code copied from scripts/transpile.coffee
|
|
||||||
# TODO: Should this live somewhere else?
|
|
||||||
transpiledCode = {}
|
|
||||||
for thang, spells of @session.get('code')
|
|
||||||
transpiledCode[thang] = {}
|
|
||||||
for spellID, spell of spells
|
|
||||||
spellName = thang + '/' + spellID
|
|
||||||
continue if @session.get('teamSpells') and not (spellName in @session.get('teamSpells')[@session.get('team')])
|
|
||||||
# console.log "PlayLevelView Transpiling spell #{spellName}"
|
|
||||||
aetherOptions = createAetherOptions functionName: spellID, codeLanguage: @session.get('submittedCodeLanguage'), includeFlow: true
|
|
||||||
aether = new Aether aetherOptions
|
|
||||||
transpiledCode[thang][spellID] = aether.transpile spell
|
|
||||||
# console.log "PlayLevelView transpiled code", transpiledCode
|
|
||||||
@session.set 'transpiledCode', transpiledCode
|
|
||||||
permissions = @session.get 'permissions' ? []
|
permissions = @session.get 'permissions' ? []
|
||||||
unless _.find(permissions, (p) -> p.target is 'public' and p.access is 'read')
|
unless _.find(permissions, (p) -> p.target is 'public' and p.access is 'read')
|
||||||
permissions.push target:'public', access:'read'
|
permissions.push target:'public', access:'read'
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
ModalView = require 'views/core/ModalView'
|
ModalView = require 'views/core/ModalView'
|
||||||
template = require 'templates/play/level/modal/course-victory-modal'
|
template = require 'templates/play/level/modal/course-victory-modal'
|
||||||
Achievements = require 'collections/Achievements'
|
|
||||||
Level = require 'models/Level'
|
Level = require 'models/Level'
|
||||||
Course = require 'models/Course'
|
Course = require 'models/Course'
|
||||||
ThangType = require 'models/ThangType'
|
|
||||||
ThangTypes = require 'collections/ThangTypes'
|
|
||||||
LevelSessions = require 'collections/LevelSessions'
|
LevelSessions = require 'collections/LevelSessions'
|
||||||
EarnedAchievement = require 'models/EarnedAchievement'
|
|
||||||
LocalMongo = require 'lib/LocalMongo'
|
|
||||||
ProgressView = require './ProgressView'
|
ProgressView = require './ProgressView'
|
||||||
NewItemView = require './NewItemView'
|
|
||||||
Classroom = require 'models/Classroom'
|
Classroom = require 'models/Classroom'
|
||||||
utils = require 'core/utils'
|
utils = require 'core/utils'
|
||||||
|
|
||||||
|
@ -18,7 +12,6 @@ module.exports = class CourseVictoryModal extends ModalView
|
||||||
template: template
|
template: template
|
||||||
closesOnClickOutside: false
|
closesOnClickOutside: false
|
||||||
|
|
||||||
|
|
||||||
initialize: (options) ->
|
initialize: (options) ->
|
||||||
@courseID = options.courseID
|
@courseID = options.courseID
|
||||||
@courseInstanceID = options.courseInstanceID
|
@courseInstanceID = options.courseInstanceID
|
||||||
|
@ -26,20 +19,10 @@ module.exports = class CourseVictoryModal extends ModalView
|
||||||
|
|
||||||
@session = options.session
|
@session = options.session
|
||||||
@level = options.level
|
@level = options.level
|
||||||
@newItems = new ThangTypes()
|
|
||||||
@newHeroes = new ThangTypes()
|
|
||||||
|
|
||||||
if @courseInstanceID
|
if @courseInstanceID
|
||||||
@classroom = new Classroom()
|
@classroom = new Classroom()
|
||||||
@supermodel.trackRequest(@classroom.fetchForCourseInstance(@courseInstanceID))
|
@supermodel.trackRequest(@classroom.fetchForCourseInstance(@courseInstanceID))
|
||||||
@achievements = options.achievements
|
|
||||||
if not @achievements
|
|
||||||
@achievements = new Achievements()
|
|
||||||
@achievements.fetchRelatedToLevel(@session.get('level').original)
|
|
||||||
@achievements = @supermodel.loadCollection(@achievements, 'achievements').model
|
|
||||||
@listenToOnce @achievements, 'sync', @onAchievementsLoaded
|
|
||||||
else
|
|
||||||
@onAchievementsLoaded()
|
|
||||||
|
|
||||||
@playSound 'victory'
|
@playSound 'victory'
|
||||||
@nextLevel = new Level()
|
@nextLevel = new Level()
|
||||||
|
@ -68,69 +51,12 @@ module.exports = class CourseVictoryModal extends ModalView
|
||||||
return
|
return
|
||||||
super(arguments...)
|
super(arguments...)
|
||||||
|
|
||||||
|
|
||||||
onAchievementsLoaded: ->
|
|
||||||
@achievements.models = _.filter @achievements.models, (m) -> not m.get('query')?.ladderAchievementDifficulty # Don't show higher AI difficulty achievements
|
|
||||||
itemOriginals = []
|
|
||||||
heroOriginals = []
|
|
||||||
achievementIDs = []
|
|
||||||
for achievement in @achievements.models
|
|
||||||
rewards = achievement.get('rewards') or {}
|
|
||||||
heroOriginals.push rewards.heroes or []
|
|
||||||
itemOriginals.push rewards.items or []
|
|
||||||
achievement.completed = LocalMongo.matchesQuery(@session.attributes, achievement.get('query'))
|
|
||||||
achievementIDs.push(achievement.id) if achievement.completed
|
|
||||||
|
|
||||||
itemOriginals = _.uniq _.flatten itemOriginals
|
|
||||||
heroOriginals = _.uniq _.flatten heroOriginals
|
|
||||||
#project = ['original', 'rasterIcon', 'name', 'soundTriggers', 'i18n'] # This is what we need, but the PlayHeroesModal needs more, and so we load more to fill up the supermodel.
|
|
||||||
project = ['original', 'rasterIcon', 'name', 'slug', 'soundTriggers', 'featureImages', 'gems', 'heroClass', 'description', 'components', 'extendedName', 'unlockLevelName', 'i18n']
|
|
||||||
for [newThangTypeCollection, originals] in [[@newItems, itemOriginals], [@newHeroes, heroOriginals]]
|
|
||||||
for original in originals
|
|
||||||
thang= new ThangType()
|
|
||||||
thang.url = "/db/thang.type/#{original}/version"
|
|
||||||
thang.project = project
|
|
||||||
@supermodel.loadModel(thang)
|
|
||||||
newThangTypeCollection.add(thang)
|
|
||||||
|
|
||||||
@newEarnedAchievements = []
|
|
||||||
for achievement in @achievements.models
|
|
||||||
continue unless achievement.completed
|
|
||||||
ea = new EarnedAchievement({
|
|
||||||
collection: achievement.get('collection')
|
|
||||||
triggeredBy: @session.id
|
|
||||||
achievement: achievement.id
|
|
||||||
})
|
|
||||||
if me.isSessionless()
|
|
||||||
@newEarnedAchievements.push ea
|
|
||||||
else
|
|
||||||
ea.save()
|
|
||||||
# Can't just add models to supermodel because each ea has the same url
|
|
||||||
ea.sr = @supermodel.addSomethingResource(ea.cid)
|
|
||||||
@newEarnedAchievements.push ea
|
|
||||||
@listenToOnce ea, 'sync', (model) ->
|
|
||||||
model.sr.markLoaded()
|
|
||||||
if _.all((ea.id for ea in @newEarnedAchievements))
|
|
||||||
unless me.loading
|
|
||||||
@supermodel.loadModel(me, {cache: false})
|
|
||||||
@newEarnedAchievementsResource.markLoaded()
|
|
||||||
|
|
||||||
unless me.isSessionless()
|
|
||||||
# have to use a something resource because addModelResource doesn't handle models being upserted/fetched via POST like we're doing here
|
|
||||||
@newEarnedAchievementsResource = @supermodel.addSomethingResource('earned achievements') if @newEarnedAchievements.length
|
|
||||||
|
|
||||||
|
|
||||||
onLoaded: ->
|
onLoaded: ->
|
||||||
super()
|
super()
|
||||||
@views = []
|
@views = []
|
||||||
|
|
||||||
# TODO: Add main victory view
|
@levelSessions?.remove(@session)
|
||||||
# TODO: Add level up view
|
@levelSessions?.add(@session)
|
||||||
# TODO: Add new hero view?
|
|
||||||
|
|
||||||
for newItem in @newItems.models
|
|
||||||
@views.push(new NewItemView({item: newItem}))
|
|
||||||
|
|
||||||
progressView = new ProgressView({
|
progressView = new ProgressView({
|
||||||
level: @level
|
level: @level
|
||||||
nextLevel: @nextLevel
|
nextLevel: @nextLevel
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Follow up on Close.io leads
|
// Follow up on Close.io leads
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
if (process.argv.length !== 7) {
|
if (process.argv.length !== 8) {
|
||||||
log("Usage: node <script> <Close.io general API key> <Close.io mail API key1> <Close.io mail API key2> <Close.io mail API key3> <mongo connection Url>");
|
log("Usage: node <script> <Close.io general API key> <Close.io mail API key1> <Close.io mail API key2> <Close.io mail API key3> <mongo connection Url>");
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ const demoRequestEmailTemplatesAuto2 = ['tmpl_HJ5zebh1SqC1QydDto05VPUMu4F7i5M35L
|
||||||
|
|
||||||
const scriptStartTime = new Date();
|
const scriptStartTime = new Date();
|
||||||
const closeIoApiKey = process.argv[2];
|
const closeIoApiKey = process.argv[2];
|
||||||
const closeIoMailApiKeys = [process.argv[3], process.argv[4], process.argv[5]]; // Automatic mails sent as API owners
|
const closeIoMailApiKeys = [process.argv[3], process.argv[4], process.argv[5], process.argv[6]]; // Automatic mails sent as API owners
|
||||||
const mongoConnUrl = process.argv[6];
|
const mongoConnUrl = process.argv[7];
|
||||||
const MongoClient = require('mongodb').MongoClient;
|
const MongoClient = require('mongodb').MongoClient;
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const request = require('request');
|
const request = require('request');
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Upsert new lead data into Close.io
|
// Upsert new lead data into Close.io
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
if (process.argv.length !== 9) {
|
if (process.argv.length !== 10) {
|
||||||
log("Usage: node <script> <Close.io general API key> <Close.io mail API key1> <Close.io mail API key2> <Close.io mail API key3> <Close.io EU mail API key> <Intercom 'App ID:API key'> <mongo connection Url>");
|
log("Usage: node <script> <Close.io general API key> <Close.io mail API key1> <Close.io mail API key2> <Close.io mail API key3> <Close.io mail API key4> <Close.io EU mail API key> <Intercom 'App ID:API key'> <mongo connection Url>");
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,18 +64,22 @@ const closeIoMailApiKeys = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apiKey: process.argv[4],
|
apiKey: process.argv[4],
|
||||||
weight: .25
|
weight: .20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apiKey: process.argv[5],
|
apiKey: process.argv[5],
|
||||||
weight: .05
|
weight: .05
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
apiKey: process.argv[6],
|
||||||
|
weight: .05
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const closeIoEuMailApiKey = process.argv[6];
|
const closeIoEuMailApiKey = process.argv[7];
|
||||||
const intercomAppIdApiKey = process.argv[7];
|
const intercomAppIdApiKey = process.argv[8];
|
||||||
const intercomAppId = intercomAppIdApiKey.split(':')[0];
|
const intercomAppId = intercomAppIdApiKey.split(':')[0];
|
||||||
const intercomApiKey = intercomAppIdApiKey.split(':')[1];
|
const intercomApiKey = intercomAppIdApiKey.split(':')[1];
|
||||||
const mongoConnUrl = process.argv[8];
|
const mongoConnUrl = process.argv[9];
|
||||||
const MongoClient = require('mongodb').MongoClient;
|
const MongoClient = require('mongodb').MongoClient;
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const countryData = require('country-data');
|
const countryData = require('country-data');
|
||||||
|
|
|
@ -142,20 +142,20 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler
|
||||||
CourseInstance.findById courseInstanceID, (err, courseInstance) =>
|
CourseInstance.findById courseInstanceID, (err, courseInstance) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
return @sendNotFoundError(res) unless courseInstance
|
return @sendNotFoundError(res) unless courseInstance
|
||||||
Course.findById courseInstance.get('courseID'), (err, course) =>
|
Classroom.findById courseInstance.get('classroomID'), (err, classroom) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
return @sendNotFoundError(res) unless course
|
return @sendNotFoundError(res) unless classroom
|
||||||
Campaign.findById course.get('campaignID'), (err, campaign) =>
|
levelIDs = []
|
||||||
return @sendDatabaseError(res, err) if err
|
for course in classroom.get('courses') when course._id.equals(courseInstance.get('courseID'))
|
||||||
return @sendNotFoundError(res) unless campaign
|
for level in course.levels when not _.contains(level.type, 'ladder')
|
||||||
levelIDs = (levelID for levelID, level of campaign.get('levels') when not _.contains(level.type, 'ladder'))
|
levelIDs.push(level.original + "")
|
||||||
query = {$and: [{creator: req.user.id}, {'level.original': {$in: levelIDs}}]}
|
query = {$and: [{creator: req.user.id}, {'level.original': {$in: levelIDs}}]}
|
||||||
cursor = LevelSession.find(query)
|
cursor = LevelSession.find(query)
|
||||||
cursor = cursor.select(req.query.project) if req.query.project
|
cursor = cursor.select(req.query.project) if req.query.project
|
||||||
cursor.exec (err, documents) =>
|
cursor.exec (err, documents) =>
|
||||||
return @sendDatabaseError(res, err) if err?
|
return @sendDatabaseError(res, err) if err?
|
||||||
cleandocs = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
|
cleandocs = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
|
||||||
@sendSuccess(res, cleandocs)
|
@sendSuccess(res, cleandocs)
|
||||||
|
|
||||||
getMembersAPI: (req, res, courseInstanceID) ->
|
getMembersAPI: (req, res, courseInstanceID) ->
|
||||||
return @sendUnauthorizedError(res) if not req.user?
|
return @sendUnauthorizedError(res) if not req.user?
|
||||||
|
|
|
@ -195,7 +195,7 @@ LevelHandler = class LevelHandler extends Handler
|
||||||
majorVersion: level.version.major
|
majorVersion: level.version.major
|
||||||
creator: req.user._id+''
|
creator: req.user._id+''
|
||||||
|
|
||||||
query = Session.find(sessionQuery).select('-screenshot -transpiledCode')
|
query = Session.find(sessionQuery)
|
||||||
# TODO: take out "code" as well, since that can get huge containing the transpiled code for the lat hero, and find another way of having the LadderSubmissionViews in the MyMatchesTab determine ranking readiness
|
# TODO: take out "code" as well, since that can get huge containing the transpiled code for the lat hero, and find another way of having the LadderSubmissionViews in the MyMatchesTab determine ranking readiness
|
||||||
query.exec (err, results) =>
|
query.exec (err, results) =>
|
||||||
if err then @sendDatabaseError(res, err) else @sendSuccess res, results
|
if err then @sendDatabaseError(res, err) else @sendSuccess res, results
|
||||||
|
|
|
@ -31,6 +31,6 @@ AnalyticsLogEventSchema.statics.logEvent = (user, event, properties={}) ->
|
||||||
unless config.proxy
|
unless config.proxy
|
||||||
analyticsMongoose = mongoose.createConnection()
|
analyticsMongoose = mongoose.createConnection()
|
||||||
analyticsMongoose.open "mongodb://#{config.mongo.analytics_host}:#{config.mongo.analytics_port}/#{config.mongo.analytics_db}", (error) ->
|
analyticsMongoose.open "mongodb://#{config.mongo.analytics_host}:#{config.mongo.analytics_port}/#{config.mongo.analytics_db}", (error) ->
|
||||||
log.warn "Couldnt connect to analytics", error
|
log.error "Couldnt connect to analytics", error if error
|
||||||
|
|
||||||
module.exports = AnalyticsLogEvent = analyticsMongoose.model('analytics.log.event', AnalyticsLogEventSchema, config.mongo.analytics_collection)
|
module.exports = AnalyticsLogEvent = analyticsMongoose.model('analytics.log.event', AnalyticsLogEventSchema, config.mongo.analytics_collection)
|
||||||
|
|
|
@ -84,9 +84,9 @@ LevelSessionSchema.pre 'save', (next) ->
|
||||||
|
|
||||||
LevelSessionSchema.statics.privateProperties = ['code', 'submittedCode', 'unsubscribed']
|
LevelSessionSchema.statics.privateProperties = ['code', 'submittedCode', 'unsubscribed']
|
||||||
LevelSessionSchema.statics.editableProperties = ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state',
|
LevelSessionSchema.statics.editableProperties = ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state',
|
||||||
'levelName', 'creatorName', 'levelID', 'screenshot',
|
'levelName', 'creatorName', 'levelID',
|
||||||
'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage',
|
'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage',
|
||||||
'unsubscribed', 'playtime', 'heroConfig', 'team', 'transpiledCode',
|
'unsubscribed', 'playtime', 'heroConfig', 'team',
|
||||||
'browser']
|
'browser']
|
||||||
LevelSessionSchema.statics.jsonSchema = jsonschema
|
LevelSessionSchema.statics.jsonSchema = jsonschema
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ LevelSessionSchema.set('toObject', {
|
||||||
transform: (doc, ret, options) ->
|
transform: (doc, ret, options) ->
|
||||||
req = options.req
|
req = options.req
|
||||||
return ret unless req # TODO: Make deleting properties the default, but the consequences are far reaching
|
return ret unless req # TODO: Make deleting properties the default, but the consequences are far reaching
|
||||||
|
|
||||||
submittedCode = doc.get('submittedCode')
|
submittedCode = doc.get('submittedCode')
|
||||||
unless req.user?.isAdmin() or req.user?.id is doc.get('creator') or ('employer' in (req.user?.get('permissions') ? [])) or not doc.get('submittedCode') # TODO: only allow leaderboard access to non-top-5 solutions
|
unless req.user?.isAdmin() or req.user?.id is doc.get('creator') or ('employer' in (req.user?.get('permissions') ? [])) or not doc.get('submittedCode') # TODO: only allow leaderboard access to non-top-5 solutions
|
||||||
ret = _.omit ret, LevelSession.privateProperties
|
ret = _.omit ret, LevelSession.privateProperties
|
||||||
|
@ -106,4 +106,32 @@ LevelSessionSchema.set('toObject', {
|
||||||
return ret
|
return ret
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = LevelSession = mongoose.model('level.session', LevelSessionSchema, 'level.sessions')
|
if config.mongo.level_session_replica_string?
|
||||||
|
levelSessionMongo = mongoose.createConnection()
|
||||||
|
levelSessionMongo.open config.mongo.level_session_replica_string, (error) ->
|
||||||
|
if error
|
||||||
|
log.error "Couldnt connect to session mongo!", error
|
||||||
|
else
|
||||||
|
log.info "Connected to seperate level session server with string", config.mongo.level_session_replica_string
|
||||||
|
else
|
||||||
|
levelSessionMongo = mongoose
|
||||||
|
|
||||||
|
LevelSession = levelSessionMongo.model('level.session', LevelSessionSchema, 'level.sessions')
|
||||||
|
|
||||||
|
if config.mongo.level_session_aux_replica_string?
|
||||||
|
auxLevelSessionMongo = mongoose.createConnection()
|
||||||
|
auxLevelSessionMongo.open config.mongo.level_session_aux_replica_string, (error) ->
|
||||||
|
if error
|
||||||
|
log.error "Couldnt connect to AUX session mongo!", error
|
||||||
|
else
|
||||||
|
log.info "Connected to seperate level AUX session server with string", config.mongo.level_session_aux_replica_string
|
||||||
|
|
||||||
|
auxLevelSession = auxLevelSessionMongo.model('level.session', LevelSessionSchema, 'level.sessions')
|
||||||
|
|
||||||
|
LevelSessionSchema.post 'save', (d) ->
|
||||||
|
return unless d instanceof LevelSession
|
||||||
|
o = d.toObject {transform: ((x, r) -> r), virtuals: false}
|
||||||
|
auxLevelSession.collection.save o, {w:1}, (err, v) ->
|
||||||
|
log.error err.stack if err
|
||||||
|
|
||||||
|
module.exports = LevelSession
|
||||||
|
|
|
@ -26,6 +26,11 @@ config.mongo =
|
||||||
mongoose_tokyo_replica_string: process.env.COCO_MONGO_MONGOOSE_TOKYO_REPLICA_STRING or ''
|
mongoose_tokyo_replica_string: process.env.COCO_MONGO_MONGOOSE_TOKYO_REPLICA_STRING or ''
|
||||||
mongoose_saoPaulo_replica_string : process.env.COCO_MONGO_MONGOOSE_SAOPAULO_REPLICA_STRING or ''
|
mongoose_saoPaulo_replica_string : process.env.COCO_MONGO_MONGOOSE_SAOPAULO_REPLICA_STRING or ''
|
||||||
|
|
||||||
|
if process.env.COCO_MONGO_LS_REPLICA_STRING?
|
||||||
|
config.mongo.level_session_replica_string = process.env.COCO_MONGO_LS_REPLICA_STRING
|
||||||
|
|
||||||
|
if process.env.COCO_MONGO_LS_AUX_REPLICA_STRING?
|
||||||
|
config.mongo.level_session_aux_replica_string = process.env.COCO_MONGO_LS_AUX_REPLICA_STRING
|
||||||
|
|
||||||
|
|
||||||
if config.tokyo or config.saoPaulo
|
if config.tokyo or config.saoPaulo
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
Course = require 'models/Course'
|
Course = require 'models/Course'
|
||||||
Level = require 'models/Level'
|
Level = require 'models/Level'
|
||||||
LevelSession = require 'models/LevelSession'
|
LevelSession = require 'models/LevelSession'
|
||||||
Achievements = require 'collections/Achievements'
|
|
||||||
CourseVictoryModal = require 'views/play/level/modal/CourseVictoryModal'
|
CourseVictoryModal = require 'views/play/level/modal/CourseVictoryModal'
|
||||||
NewItemView = require 'views/play/level/modal/NewItemView'
|
|
||||||
ProgressView = require 'views/play/level/modal/ProgressView'
|
ProgressView = require 'views/play/level/modal/ProgressView'
|
||||||
factories = require 'test/app/factories'
|
factories = require 'test/app/factories'
|
||||||
|
|
||||||
|
@ -12,7 +10,7 @@ describe 'CourseVictoryModal', ->
|
||||||
me.clear()
|
me.clear()
|
||||||
|
|
||||||
it 'will eventually be the only victory modal'
|
it 'will eventually be the only victory modal'
|
||||||
|
|
||||||
makeViewOptions = ->
|
makeViewOptions = ->
|
||||||
level = factories.makeLevel()
|
level = factories.makeLevel()
|
||||||
course = factories.makeCourse()
|
course = factories.makeCourse()
|
||||||
|
@ -21,24 +19,18 @@ describe 'CourseVictoryModal', ->
|
||||||
course: factories.makeCourse()
|
course: factories.makeCourse()
|
||||||
level: level
|
level: level
|
||||||
session: factories.makeLevelSession({ state: { complete: true } }, { level })
|
session: factories.makeLevelSession({ state: { complete: true } }, { level })
|
||||||
achievements: new Achievements([factories.makeLevelCompleteAchievement({}, {level: level})])
|
|
||||||
nextLevel: factories.makeLevel()
|
nextLevel: factories.makeLevel()
|
||||||
courseInstanceID: courseInstance.id
|
courseInstanceID: courseInstance.id
|
||||||
courseID: course.id
|
courseID: course.id
|
||||||
}
|
}
|
||||||
|
|
||||||
nextLevelRequest = null
|
nextLevelRequest = null
|
||||||
|
|
||||||
handleRequests = (modal) ->
|
handleRequests = (modal) ->
|
||||||
requests = jasmine.Ajax.requests.all()
|
requests = jasmine.Ajax.requests.all()
|
||||||
thangRequest = _.find(requests, (r) -> _.string.startsWith(r.url, '/db/thang.type'))
|
|
||||||
thangRequest?.respondWith({status: 200, responseText: factories.makeThangType().stringify()})
|
|
||||||
modal.newEarnedAchievements[0].fakeRequests[0].respondWith({
|
|
||||||
status: 200, responseText: factories.makeEarnedAchievement().stringify()
|
|
||||||
})
|
|
||||||
modal.levelSessions.fakeRequests[0].respondWith({ status: 200, responseText: '[]' })
|
modal.levelSessions.fakeRequests[0].respondWith({ status: 200, responseText: '[]' })
|
||||||
modal.classroom.fakeRequests[0].respondWith({
|
modal.classroom.fakeRequests[0].respondWith({
|
||||||
status: 200, responseText: factories.makeClassroom().stringify()
|
status: 200, responseText: factories.makeClassroom().stringify()
|
||||||
})
|
})
|
||||||
if me.fakeRequests
|
if me.fakeRequests
|
||||||
lastRequest = _.last(me.fakeRequests)
|
lastRequest = _.last(me.fakeRequests)
|
||||||
|
@ -47,7 +39,7 @@ describe 'CourseVictoryModal', ->
|
||||||
status: 200, responseText: factories.makeUser().stringify()
|
status: 200, responseText: factories.makeUser().stringify()
|
||||||
})
|
})
|
||||||
nextLevelRequest = modal.nextLevel.fakeRequests[0]
|
nextLevelRequest = modal.nextLevel.fakeRequests[0]
|
||||||
|
|
||||||
describe 'given a course level with a next level and no item or hero rewards', ->
|
describe 'given a course level with a next level and no item or hero rewards', ->
|
||||||
modal = null
|
modal = null
|
||||||
|
|
||||||
|
@ -63,7 +55,7 @@ describe 'CourseVictoryModal', ->
|
||||||
expect(modal.views[0] instanceof ProgressView).toBe(true)
|
expect(modal.views[0] instanceof ProgressView).toBe(true)
|
||||||
|
|
||||||
it '(demo)', -> jasmine.demoModal(modal)
|
it '(demo)', -> jasmine.demoModal(modal)
|
||||||
|
|
||||||
describe 'its ProgressView', ->
|
describe 'its ProgressView', ->
|
||||||
it 'has a next level button which navigates to the next level on click', ->
|
it 'has a next level button which navigates to the next level on click', ->
|
||||||
spyOn(application.router, 'navigate')
|
spyOn(application.router, 'navigate')
|
||||||
|
@ -71,12 +63,12 @@ describe 'CourseVictoryModal', ->
|
||||||
expect(button.length).toBe(1)
|
expect(button.length).toBe(1)
|
||||||
button.click()
|
button.click()
|
||||||
expect(application.router.navigate).toHaveBeenCalled()
|
expect(application.router.navigate).toHaveBeenCalled()
|
||||||
|
|
||||||
it 'has two columns', ->
|
it 'has two columns', ->
|
||||||
expect(modal.$('.row:first .col-sm-12').length).toBe(0)
|
expect(modal.$('.row:first .col-sm-12').length).toBe(0)
|
||||||
expect(modal.$('.row:first .col-sm-5').length).toBe(1)
|
expect(modal.$('.row:first .col-sm-5').length).toBe(1)
|
||||||
expect(modal.$('.row:first .col-sm-7').length).toBe(1)
|
expect(modal.$('.row:first .col-sm-7').length).toBe(1)
|
||||||
|
|
||||||
describe 'given a course level without a next level', ->
|
describe 'given a course level without a next level', ->
|
||||||
modal = null
|
modal = null
|
||||||
|
|
||||||
|
@ -91,13 +83,13 @@ describe 'CourseVictoryModal', ->
|
||||||
handleRequests(modal)
|
handleRequests(modal)
|
||||||
nextLevelRequest.respondWith({status: 404, responseText: '{}'})
|
nextLevelRequest.respondWith({status: 404, responseText: '{}'})
|
||||||
_.defer done
|
_.defer done
|
||||||
|
|
||||||
describe 'its ProgressView', ->
|
describe 'its ProgressView', ->
|
||||||
it 'has a single large column, since there is no next level to display', ->
|
it 'has a single large column, since there is no next level to display', ->
|
||||||
expect(modal.$('.row:first .col-sm-12').length).toBe(1)
|
expect(modal.$('.row:first .col-sm-12').length).toBe(1)
|
||||||
expect(modal.$('.row:first .col-sm-5').length).toBe(0)
|
expect(modal.$('.row:first .col-sm-5').length).toBe(0)
|
||||||
expect(modal.$('.row:first .col-sm-7').length).toBe(0)
|
expect(modal.$('.row:first .col-sm-7').length).toBe(0)
|
||||||
|
|
||||||
it 'has a done button which navigates to the CourseDetailsView for the given course instance', ->
|
it 'has a done button which navigates to the CourseDetailsView for the given course instance', ->
|
||||||
spyOn(application.router, 'navigate')
|
spyOn(application.router, 'navigate')
|
||||||
button = modal.$el.find('#done-btn')
|
button = modal.$el.find('#done-btn')
|
||||||
|
@ -106,32 +98,3 @@ describe 'CourseVictoryModal', ->
|
||||||
expect(application.router.navigate).toHaveBeenCalled()
|
expect(application.router.navigate).toHaveBeenCalled()
|
||||||
|
|
||||||
it '(demo)', -> jasmine.demoModal(modal)
|
it '(demo)', -> jasmine.demoModal(modal)
|
||||||
|
|
||||||
|
|
||||||
describe 'given a course level with a new item', ->
|
|
||||||
modal = null
|
|
||||||
|
|
||||||
beforeEach (done) ->
|
|
||||||
options = makeViewOptions()
|
|
||||||
|
|
||||||
# insert new item into achievement properties
|
|
||||||
achievement = options.achievements.first()
|
|
||||||
rewards = _.cloneDeep(achievement.get('rewards'))
|
|
||||||
rewards.items = ["53e4108204c00d4607a89f78"]
|
|
||||||
achievement.set('rewards', rewards)
|
|
||||||
|
|
||||||
modal = new CourseVictoryModal(options)
|
|
||||||
handleRequests(modal)
|
|
||||||
nextLevelRequest.respondWith({status: 200, responseText: factories.makeLevel().stringify()})
|
|
||||||
_.defer done
|
|
||||||
|
|
||||||
it 'includes a NewItemView when the level rewards a new item', ->
|
|
||||||
expect(_.size(modal.views)).toBe(2)
|
|
||||||
expect(modal.views[0] instanceof NewItemView).toBe(true)
|
|
||||||
|
|
||||||
it 'continues to the ProgressView when you click the continue button', ->
|
|
||||||
expect(modal.currentView instanceof NewItemView).toBe(true)
|
|
||||||
modal.$el.find('#continue-btn').click()
|
|
||||||
expect(modal.currentView instanceof ProgressView).toBe(true)
|
|
||||||
|
|
||||||
it '(demo)', -> jasmine.demoModal(modal)
|
|
||||||
|
|
Loading…
Reference in a new issue