Merge branch 'master' into production

This commit is contained in:
Nick Winter 2015-01-21 11:50:05 -08:00
commit 04f63c7768
11 changed files with 76 additions and 71 deletions

View file

@ -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

View file

@ -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) ->

View file

@ -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'}

View file

@ -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

View file

@ -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?

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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'

View file

@ -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'

View file

@ -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)