Graphs with D3!

This commit is contained in:
Michael Schmatz 2014-04-02 18:41:10 -07:00
parent 943bcf3162
commit 3aa31c7246
6 changed files with 89 additions and 24 deletions

View file

@ -0,0 +1,13 @@
#my-matches-tab-view
.axis path, .axis line
fill: none
stroke: #000
shape-rendering: crispEdges
.x.axis.path
display: none
.line
fill: none
stroke: steelblue
stroke-width: 1.5px

View file

@ -11,14 +11,14 @@ div#columns.row
tr
th(colspan=4, style="color: #{team.primaryColor}")
span(data-i18n="ladder.summary_your") Your
span(data-i18n="ladder.summary_your") Your
|#{team.name}
|
span(data-i18n="ladder.summary_matches") Matches -
span(data-i18n="ladder.summary_matches") Matches -
|#{team.wins}
span(data-i18n="ladder.summary_wins") Wins,
span(data-i18n="ladder.summary_wins") Wins,
|#{team.losses}
span(data-i18n="ladder.summary_losses") Losses
span(data-i18n="ladder.summary_losses") Losses
if team.session
tr
@ -34,7 +34,9 @@ div#columns.row
if team.chartData
tr
th(colspan=4, style="color: #{team.primaryColor}")
img(src="https://chart.googleapis.com/chart?chs=450x125&cht=lxy&chco=#{team.chartColor}&chtt=Score%3A+#{team.currentScore}&chts=#{team.chartColor},16,r&chf=a,s,000000FF&chls=2&chm=o,#{team.chartColor},0,4&chd=t:#{team.chartData}&chxt=y&chxr=0,#{team.minScore},#{team.maxScore}")
div(class="score-chart-wrapper", data-team-name=team.name, id="score-chart-#{team.name}")
tr
th(data-i18n="general.result") Result

View file

@ -48,6 +48,8 @@ module.exports = class MyMatchesTabView extends CocoView
@startsLoading = false
@render()
getRenderData: ->
ctx = super()
ctx.level = @level
@ -80,7 +82,9 @@ module.exports = class MyMatchesTabView extends CocoView
team.losses = _.filter(team.matches, {state: 'loss'}).length
scoreHistory = team.session?.get('scoreHistory')
if scoreHistory?.length > 1
team.scoreHistory = scoreHistory
scoreHistory = _.last scoreHistory, 100 # Chart URL needs to be under 2048 characters for GET
team.currentScore = Math.round scoreHistory[scoreHistory.length - 1][1] * 100
team.chartColor = team.primaryColor.replace '#', ''
#times = (s[0] for s in scoreHistory)
@ -109,7 +113,69 @@ module.exports = class MyMatchesTabView extends CocoView
else if session.get 'isRanking'
rankingState = 'ranking'
@setRankingButtonText button, rankingState
@$el.find('.score-chart-wrapper').each (i, el) =>
scoreWrapper = $(el)
team = _.find @teams, name: scoreWrapper.data('team-name')
@generateScoreLineChart(scoreWrapper.attr('id'), team.scoreHistory)
generateScoreLineChart: (wrapperID, scoreHistory) =>
margin =
top: 20
right: 20
bottom: 30
left: 50
width = 450 - margin.left - margin.right
height = 125
x = d3.time.scale().range([0,width])
y = d3.scale.linear().range([height,0])
xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(4).outerTickSize(0)
yAxis = d3.svg.axis().scale(y).orient("left").ticks(4).outerTickSize(0)
line = d3.svg.line().x(((d) -> x(d.date))).y((d) -> y(d.close))
selector = "#" + wrapperID
svg = d3.select(selector).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(#{margin.left},#{margin.top})")
time = 0
data = scoreHistory.map (d) ->
time +=1
return {
date: time
close: d[1] * 100
}
x.domain(d3.extent(data, (d) -> d.date))
y.domain(d3.extent(data, (d) -> d.close))
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y",4)
.attr("dy", ".75em")
.style("text-anchor","end")
.text("Score")
svg.append("path")
.datum(data)
.attr("class","line")
.attr("d",line)
readyToRank: (session) ->
return false unless session?.get('levelID') # If it hasn't been denormalized, then it's not ready.
return false unless c1 = session.get('code')

View file

@ -35,7 +35,8 @@
"aether": "~0.1.18",
"underscore.string": "~2.3.3",
"firebase": "~1.0.2",
"catiline": "~2.9.3"
"catiline": "~2.9.3",
"d3": "~3.4.4"
},
"overrides": {
"backbone": {

View file

@ -65,6 +65,7 @@ exports.config =
# Aether before box2d for some strange Object.defineProperty thing
'bower_components/aether/build/aether.js'
'bower_components/d3/d3.min.js'
]
stylesheets:
defaultExtension: 'sass'

View file

@ -100,8 +100,6 @@ resimulateSession = (originalLevelID, levelMajorVersion, session, cb) =>
cb null
module.exports.createNewTask = (req, res) ->
requestSessionID = req.body.session
originalLevelID = req.body.originalLevelID
@ -514,8 +512,6 @@ findNearestBetterSessionID = (cb) ->
opponentSessionTotalScore = @newScoresObject[opponentSessionID].totalScore
opposingTeam = calculateOpposingTeam(@clientResponseObject.originalSessionTeam)
retrieveAllOpponentSessionIDs sessionID, (err, opponentSessionIDs) ->
if err? then return cb err, null
@ -577,18 +573,10 @@ addNewSessionsToQueue = (sessionID, callback) ->
sessions = [@clientResponseObject.originalSessionID, sessionID]
addPairwiseTaskToQueue sessions, callback
messageIsInvalid = (message) -> (not message?) or message.isEmpty()
sendEachTaskPairToTheQueue = (taskPairs, callback) -> async.each taskPairs, sendTaskPairToQueue, callback
generateTaskPairs = (submittedSessions, sessionToScore) ->
taskPairs = []
for session in submittedSessions
@ -609,10 +597,6 @@ isUserAnonymous = (req) -> if req.user? then return req.user.get('anonymous') el
isUserAdmin = (req) -> return Boolean(req.user?.isAdmin())
sendResponseObject = (req,res,object) ->
res.setHeader('Content-Type', 'application/json')
res.send(object)
@ -622,8 +606,6 @@ hasTaskTimedOut = (taskSentTimestamp) -> taskSentTimestamp + scoringTaskTimeoutI
handleTimedOutTask = (req, res, taskBody) -> errors.clientTimeout res, "The results weren't provided within the timeout"
putRankingFromMetricsIntoScoreObject = (taskObject,scoreObject) ->
scoreObject = _.indexBy scoreObject, 'id'
scoreObject[session.sessionID].gameRanking = session.metrics.rank for session in taskObject.sessions