mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-28 18:15:52 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
04f63c7768
11 changed files with 76 additions and 71 deletions
|
@ -73,7 +73,7 @@ module.exports = class Thang
|
|||
for [prop, type] in props
|
||||
unless type in ThangState.trackedPropertyTypes
|
||||
# How should errors for busted Components work? We can't recover from this and run the world.
|
||||
throw new Error "Type #{type} for property #{prop} is not a trackable property type: #{trackedPropertyTypes}"
|
||||
throw new Error "Type #{type} for property #{prop} is not a trackable property type: #{ThangState.trackedPropertyTypes}"
|
||||
oldPropIndex = @trackedPropertiesKeys.indexOf prop
|
||||
if oldPropIndex is -1
|
||||
@trackedPropertiesKeys.push prop
|
||||
|
|
|
@ -9,7 +9,6 @@ module.exports = class Level extends CocoModel
|
|||
@levels:
|
||||
'dungeons-of-kithgard': '5411cb3769152f1707be029c'
|
||||
'defense-of-plainswood': '541b67f71ccc8eaae19f3c62'
|
||||
'the-mighty-sand-yak': '5480b9d01bf0b10000711c5f'
|
||||
urlRoot: '/db/level'
|
||||
|
||||
serialize: (supermodel, session, otherSession, cached=false) ->
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
c = require 'schemas/schemas'
|
||||
|
||||
module.exports =
|
||||
'editor:campaign-analytics-modal-closed': c.object {title: 'Campaign editor analytics modal closed'},
|
||||
targetLevelSlug: {type: 'string'}
|
||||
|
||||
'editor:save-new-version': c.object {title: 'Save New Version', description: 'Published when a version gets saved', required: ['major', 'commitMessage']},
|
||||
major: {type: 'boolean'}
|
||||
commitMessage: {type: 'string'}
|
||||
|
|
|
@ -7,6 +7,7 @@ block modal-header-content
|
|||
input.form-control#input-startday(type='text', style='width:100px;', value=campaignCompletions.startDay)
|
||||
input.form-control#input-endday(type='text', style='width:100px;', value=campaignCompletions.endDay)
|
||||
button.btn.btn-default.btn-sm#reload-button(style='margin-left:10px;') Reload
|
||||
div(style='font-size:10px') Double-click row to open level details.
|
||||
|
||||
block modal-body-content
|
||||
if campaignCompletions && campaignCompletions.levels
|
||||
|
@ -23,7 +24,7 @@ block modal-body-content
|
|||
td Completion %
|
||||
tbody
|
||||
- for (var i = 0; i < campaignCompletions.levels.length; i++)
|
||||
tr
|
||||
tr.level(data-level-slug=campaignCompletions.levels[i].level)
|
||||
td.level-name-container= campaignCompletions.levels[i].level
|
||||
span.level-name-background(style="width:#{campaignCompletions.levels[i].usersRemaining || 0}%;")
|
||||
td= campaignCompletions.levels[i].started
|
||||
|
|
|
@ -13,6 +13,7 @@ module.exports = class CampaignAnalyticsModal extends ModalView
|
|||
|
||||
events:
|
||||
'click #reload-button': 'onClickReloadButton'
|
||||
'dblclick .level': 'onDblClickLevel'
|
||||
|
||||
constructor: (options, @campaignHandle, @campaignCompletions) ->
|
||||
super options
|
||||
|
@ -29,6 +30,20 @@ module.exports = class CampaignAnalyticsModal extends ModalView
|
|||
$("#input-endday").datepicker dateFormat: "yy-mm-dd"
|
||||
@addCompletionLineGraphs()
|
||||
|
||||
onClickReloadButton: () =>
|
||||
startDay = $('#input-startday').val()
|
||||
endDay = $('#input-endday').val()
|
||||
delete @campaignCompletions.levels
|
||||
@campaignCompletions.startDay = startDay
|
||||
@campaignCompletions.endDay = endDay
|
||||
@render()
|
||||
@getCampaignAnalytics startDay, endDay
|
||||
|
||||
onDblClickLevel: (e) ->
|
||||
row = $(e.target).parents('.level')
|
||||
Backbone.Mediator.publish 'editor:campaign-analytics-modal-closed', targetLevelSlug: row.data 'level-slug'
|
||||
@hide()
|
||||
|
||||
addCompletionLineGraphs: ->
|
||||
return unless @campaignCompletions.levels
|
||||
for level in @campaignCompletions.levels
|
||||
|
@ -38,57 +53,43 @@ module.exports = class CampaignAnalyticsModal extends ModalView
|
|||
days.push
|
||||
day: day
|
||||
rate: level['days'][day].finished / level['days'][day].started
|
||||
count: level['days'][day].started
|
||||
days.sort (a, b) -> a.day - b.day
|
||||
data = []
|
||||
for i in [0...days.length]
|
||||
data.push
|
||||
x: i
|
||||
y: days[i].rate
|
||||
c: days[i].count
|
||||
@addLineGraph '#background' + level.level, data
|
||||
|
||||
addLineGraph: (containerSelector, lineData, lineColor='green', min=0, max=1.0) ->
|
||||
# Add a line chart to the given container
|
||||
# Adjust stroke-weight based on segment count: width 0.3 to 3.0 for counts roughly 100 to 10000
|
||||
# TODO: Move this to a utility library
|
||||
vis = d3.select(containerSelector)
|
||||
width = $(containerSelector).width()
|
||||
height = $(containerSelector).height()
|
||||
xRange = d3.scale.linear().range([0, width]).domain([d3.min(lineData, (d) -> d.x), d3.max(lineData, (d) -> d.x)])
|
||||
yRange = d3.scale.linear().range([height, 0]).domain([min, max])
|
||||
xAxis = d3.svg.axis()
|
||||
.scale(xRange)
|
||||
.tickSize(5)
|
||||
.tickSubdivide(true)
|
||||
yAxis = d3.svg.axis()
|
||||
.scale(yRange)
|
||||
.tickSize(5)
|
||||
.orient('left')
|
||||
.tickSubdivide(true)
|
||||
vis.append('svg:g')
|
||||
.attr('class', 'x axis')
|
||||
.attr('transform', 'translate(0,' + height + ')')
|
||||
.call(xAxis)
|
||||
vis.append('svg:g')
|
||||
.attr('class', 'y axis')
|
||||
.attr('transform', 'translate(0,0)')
|
||||
.call(yAxis)
|
||||
lineFunc = d3.svg.line()
|
||||
.x((d) -> xRange(d.x))
|
||||
.y((d) -> yRange(d.y))
|
||||
.interpolate('linear')
|
||||
vis.append('svg:path')
|
||||
.attr('d', lineFunc(lineData))
|
||||
.attr('stroke', lineColor)
|
||||
.attr('stroke-width', 1)
|
||||
.attr('fill', 'none')
|
||||
|
||||
onClickReloadButton: () =>
|
||||
startDay = $('#input-startday').val()
|
||||
endDay = $('#input-endday').val()
|
||||
delete @campaignCompletions.levels
|
||||
@campaignCompletions.startDay = startDay
|
||||
@campaignCompletions.endDay = endDay
|
||||
@render()
|
||||
@getCampaignAnalytics startDay, endDay
|
||||
lines = []
|
||||
for i in [0...lineData.length-1]
|
||||
lines.push
|
||||
x1: xRange(lineData[i].x)
|
||||
y1: yRange(lineData[i].y)
|
||||
x2: xRange(lineData[i + 1].x)
|
||||
y2: yRange(lineData[i + 1].y)
|
||||
strokeWidth: Math.min(3, Math.max(0.3, Math.log(lineData[i].c/10)/2))
|
||||
vis.selectAll('.line')
|
||||
.data(lines)
|
||||
.enter()
|
||||
.append("line")
|
||||
.attr("x1", (d) -> d.x1)
|
||||
.attr("y1", (d) -> d.y1)
|
||||
.attr("x2", (d) -> d.x2)
|
||||
.attr("y2", (d) -> d.y2)
|
||||
.style("stroke-width", (d) -> d.strokeWidth)
|
||||
.style("stroke", lineColor)
|
||||
|
||||
getCampaignAnalytics: (startDay, endDay) =>
|
||||
if startDay?
|
||||
|
|
|
@ -24,6 +24,9 @@ module.exports = class CampaignEditorView extends RootView
|
|||
'click #analytics-button': 'onClickAnalyticsButton'
|
||||
'click #save-button': 'onClickSaveButton'
|
||||
|
||||
subscriptions:
|
||||
'editor:campaign-analytics-modal-closed' : 'onAnalyticsModalClosed'
|
||||
|
||||
constructor: (options, @campaignHandle) ->
|
||||
super(options)
|
||||
@campaign = new Campaign({_id:@campaignHandle})
|
||||
|
@ -140,6 +143,13 @@ module.exports = class CampaignEditorView extends RootView
|
|||
onClickAnalyticsButton: ->
|
||||
@openModalView new CampaignAnalyticsModal {}, @campaignHandle, @campaignAnalytics
|
||||
|
||||
onAnalyticsModalClosed: (options) ->
|
||||
if options.targetLevelSlug? and @treema.childrenTreemas?.levels?.childrenTreemas?
|
||||
for original, level of @treema.childrenTreemas.levels.childrenTreemas
|
||||
if level.data?.slug is options.targetLevelSlug
|
||||
@openCampaignLevelView @supermodel.getModelByOriginal Level, original
|
||||
break
|
||||
|
||||
onClickSaveButton: ->
|
||||
@toSave.set @toSave.filter (m) -> m.hasLocalChanges()
|
||||
@openModalView new SaveCampaignModal({}, @toSave)
|
||||
|
|
|
@ -200,5 +200,5 @@ module.exports = class LevelEditView extends RootView
|
|||
|
||||
incrementBuildTime: =>
|
||||
return if application.userIsIdle
|
||||
@levelBuildTime ?= @level.get('buildTime')
|
||||
@levelBuildTime ?= @level.get('buildTime') ? 0
|
||||
++@levelBuildTime
|
||||
|
|
|
@ -138,6 +138,7 @@ module.exports = class CampaignView extends RootView
|
|||
level.locked = not me.ownsLevel level.original
|
||||
level.locked = false if @levelStatusMap[level.slug] in ['started', 'complete']
|
||||
level.locked = false if @editorMode
|
||||
level.locked = false if @campaign.get('name') is 'Auditions'
|
||||
level.disabled = true if level.adminOnly and @levelStatusMap[level.slug] not in ['started', 'complete']
|
||||
level.color = 'rgb(255, 80, 60)'
|
||||
if level.requiresSubscription
|
||||
|
|
|
@ -88,7 +88,7 @@ module.exports = class HeroVictoryModal extends ModalView
|
|||
me.fetch cache: false unless me.loading
|
||||
|
||||
@readyToContinue = true if not @achievements.models.length
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
@ -314,7 +314,7 @@ module.exports = class HeroVictoryModal extends ModalView
|
|||
AudioPlayer.playSound name, 1
|
||||
|
||||
getNextLevelCampaign: ->
|
||||
{'kithgard-gates': 'forest', 'siege-of-stonehold': 'desert'}[@level.get('slug')] or @level.get 'campaign' # Much easier to just keep this updated than to dynamically figure it out.
|
||||
{'kithgard-gates': 'forest', 'siege-of-stonehold': 'desert', 'clash-of-clones': 'mountain'}[@level.get('slug')] or @level.get 'campaign' # Much easier to just keep this updated than to dynamically figure it out.
|
||||
|
||||
getNextLevelLink: ->
|
||||
link = '/play'
|
||||
|
|
|
@ -126,7 +126,7 @@ exports.config =
|
|||
'vendor/scripts/jasmine-html.js'
|
||||
'vendor/scripts/jasmine-boot.js'
|
||||
'vendor/scripts/jasmine-mock-ajax.js'
|
||||
|
||||
|
||||
# vendor.js ordering
|
||||
'bower_components/jquery/dist/jquery.js'
|
||||
'bower_components/lodash/dist/lodash.js'
|
||||
|
|
|
@ -101,35 +101,6 @@ class LinuxSetup(SetupFactory):
|
|||
|
||||
def distroSetup(self):
|
||||
distro = self.detectDistro()
|
||||
if distro == "arch":
|
||||
print("Arch Linux detected. Would you like to install \n"
|
||||
"NodeJS and MongoDB via pacman? [y/N]")
|
||||
if raw_input().lower() in ["y", "yes"]:
|
||||
try:
|
||||
subprocess.check_call(["pacman", "-S",
|
||||
"nodejs", "mongodb",
|
||||
"--noconfirm"])
|
||||
except subprocess.CalledProcessError as err:
|
||||
print("Installation failed. Retry, Continue, or "
|
||||
"Abort? [r/c/A]")
|
||||
answer = raw_input().lower()
|
||||
if answer in ["r", "retry"]:
|
||||
return(self.distroSetup())
|
||||
elif answer in ["c", "continue"]:
|
||||
return()
|
||||
else:
|
||||
exit(1)
|
||||
else:
|
||||
#try:
|
||||
#print("Enabling and starting MongoDB in systemd.")
|
||||
#subprocess.check_call(["systemctl", "enable",
|
||||
# "mongodb.service"])
|
||||
#subprocess.check_call(["systemctl", "start",
|
||||
# "mongodb.service"])
|
||||
#print("Node and Mongo installed. Continuing.")
|
||||
#except subprocess.CalledProcessError as err:
|
||||
#print("Mongo failed to start. Aborting")
|
||||
#exit(1)
|
||||
if distro == "ubuntu":
|
||||
print("Ubuntu installation detected. Would you like to install \n"
|
||||
"NodeJS and MongoDB via apt-get? [y/N]")
|
||||
|
@ -180,3 +151,22 @@ class LinuxSetup(SetupFactory):
|
|||
#except subprocess.CalledProcessError as err:
|
||||
#print("Mongo failed to start. Aborting.")
|
||||
#exit(1)
|
||||
if distro == "arch":
|
||||
print("Arch Linux detected. Would you like to install \n"
|
||||
"NodeJS and MongoDB via pacman? [y/N]")
|
||||
if raw_input().lower() in ["y", "yes"]:
|
||||
try:
|
||||
subprocess.check_call(["pacman", "-S",
|
||||
"nodejs", "mongodb",
|
||||
"--noconfirm"])
|
||||
except subprocess.CalledProcessError as err:
|
||||
print("Installation failed. Retry, Continue, or "
|
||||
"Abort? [r/c/A]")
|
||||
answer = raw_input().lower()
|
||||
if answer in ["r", "retry"]:
|
||||
return(self.distroSetup())
|
||||
elif answer in ["c", "continue"]:
|
||||
return()
|
||||
else:
|
||||
exit(1)
|
||||
|
||||
|
|
Loading…
Reference in a new issue