Permalink
Browse files

add lots of pretty graphs and designs

  • Loading branch information...
1 parent 9cbc211 commit 038b43225215b9018e335a5e70a7ec9c0dc91c35 @alex-stripe alex-stripe committed Jan 28, 2013
View
@@ -3,11 +3,12 @@ source :rubygems
gem 'sinatra'
gem 'sinatra-contrib'
gem 'thin'
-gem 'sprockets'
-gem 'coffee-script'
+gem 'catapult'
gem 'mongo_mapper'
gem 'bson_ext'
gem 'eco'
+gem 'useragent'
+gem 'erubis'
# group :development do
gem 'debugger'
View
@@ -13,6 +13,15 @@ GEM
bson_ext (1.8.0)
bson (~> 1.8.0)
builder (3.0.4)
+ catapult (0.0.4)
+ coffee-script (~> 2.2.0)
+ listen (~> 0.4.2)
+ rack (~> 1.4.1)
+ sprockets (~> 2.4.3)
+ sprockets-commonjs (= 0.0.6.pre)
+ stylus (~> 0.6.2)
+ thin
+ thor (~> 0.15.2)
coffee-script (2.2.0)
coffee-script-source
execjs
@@ -31,11 +40,17 @@ GEM
eco-source
execjs
eco-source (1.1.0.rc.1)
+ erubis (2.7.0)
eventmachine (1.0.0)
execjs (1.4.0)
multi_json (~> 1.0)
+ ffi (1.2.0)
hike (1.2.1)
i18n (0.6.1)
+ listen (0.4.7)
+ rb-fchange (~> 0.0.5)
+ rb-fsevent (~> 0.9.1)
+ rb-inotify (~> 0.8.8)
mongo (1.8.0)
bson (~> 1.8.0)
mongo_mapper (0.12.0)
@@ -50,6 +65,11 @@ GEM
rack
rack-test (0.6.2)
rack (>= 1.0)
+ rb-fchange (0.0.6)
+ ffi
+ rb-fsevent (0.9.2)
+ rb-inotify (0.8.8)
+ ffi (>= 0.5.0)
sinatra (1.3.3)
rack (~> 1.3, >= 1.3.6)
rack-protection (~> 1.2)
@@ -66,22 +86,31 @@ GEM
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
+ sprockets-commonjs (0.0.6.pre)
+ sprockets (~> 2.4.0)
+ stylus (0.6.2)
+ execjs
+ stylus-source
+ stylus-source (0.31.0)
thin (1.5.0)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
+ thor (0.15.4)
tilt (1.3.3)
+ useragent (0.4.16)
PLATFORMS
ruby
DEPENDENCIES
bson_ext
- coffee-script
+ catapult
debugger
eco
+ erubis
mongo_mapper
sinatra
sinatra-contrib
- sprockets
thin
+ useragent
View
@@ -17,6 +17,9 @@
'development' => {'uri' => 'mongodb://localhost:27017/abba-development'}
}, settings.environment.to_s)
+ Catapult.environment.append_path(settings.root + '/app/assets/javascripts')
+ Catapult.environment.append_path(settings.root + '/app/assets/stylesheets')
+
set :views, settings.root + '/app/views'
set :show_exceptions, true
set :erb, :escape_html => true
@@ -25,12 +28,18 @@
# Router
get '/' do
- @tests = Abba::Test.all
- erb :tests
+ redirect '/experiments'
+end
+
+get '/experiments' do
+ @experiments = Abba::Experiment.all
+ erb :experiments
end
-get '/test/:name' do
- @test = Abba::Test.find_by_name!(params[:name])
- @variants = @test.variants.all
- erb :test
+get '/experiment/:name' do
+ @experiment = Abba::Experiment.find_by_name!(params[:name])
+ @variants = @experiment.variants.all
+ @variant_graph = @experiment.granular_conversion_rate(7.days.ago, Time.current)
+
+ erb :experiment
end
View
24 app.rb
@@ -22,6 +22,10 @@
end
helpers do
+ def prevent_caching
+ headers['Cache-Control'] = 'no-cache, no-store'
+ end
+
def send_blank
send_file './public/blank.gif'
end
@@ -36,25 +40,27 @@ def required(*atts)
end
get '/v1/abba.js', :provides => 'application/javascript' do
- settings.sprockets['index'].to_s
+ settings.sprockets['client/index'].to_s
end
get '/start', :provides => 'image/gif' do
- required :test, :variant
+ required :experiment, :variant
- test = Abba::Test.find_or_create_by_name(params[:test])
- variant = test.variants.find_or_create_by_name(params[:variant])
- variant.start!(request.env)
+ experiment = Abba::Experiment.find_or_create_by_name(params[:experiment])
+ variant = experiment.variants.find_or_create_by_name(params[:variant])
+ variant.start!(request)
+ prevent_caching
send_blank
end
get '/complete', :provides => 'image/gif' do
- required :test, :variant
+ required :experiment, :variant
- test = Abba::Test.find_or_create_by_name(params[:test])
- variant = test.variants.find_or_create_by_name(params[:variant])
- variant.complete!(request.env)
+ experiment = Abba::Experiment.find_or_create_by_name(params[:experiment])
+ variant = experiment.variants.find_or_create_by_name(params[:variant])
+ variant.complete!(request)
+ prevent_caching
send_blank
end
View
@@ -1,4 +1,5 @@
module Abba
- autoload :Test, 'app/models/test'
+ autoload :Experiment, 'app/models/experiment'
autoload :Variant, 'app/models/variant'
+ autoload :Request, 'app/models/request'
end
@@ -0,0 +1,109 @@
+Controller = require('controller')
+moment = require('moment')
+
+class Graph extends Controller
+ constructor: ->
+ super
+
+ render: (variants) ->
+ @$el.empty()
+
+ margin = {top: 30, right: 20, bottom: 30, left: 30}
+ width = @$el.width() - margin.left - margin.right
+ height = 300 - margin.top - margin.bottom
+
+ x = d3.time.scale()
+ .range([0, width])
+
+ y = d3.scale.linear()
+ .range([height, 0])
+
+ xAxis = d3.svg.axis()
+ .scale(x)
+ .tickSize(1)
+ .tickPadding(12)
+ .ticks(d3.time.days, 1)
+ .orient('bottom')
+
+ yAxis = d3.svg.axis()
+ .scale(y)
+ .ticks(5)
+ .orient('left')
+
+ line = d3.svg.line()
+ .x((d) -> x(new Date(d.time)))
+ .y((d) -> y(d.rate))
+
+ area = d3.svg.area()
+ .x((d) -> x(new Date(d.time)))
+ .y0(height)
+ .y1((d) -> y(d.rate))
+
+ svg = d3.select(@$el[0]).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})")
+
+ x.domain([
+ d3.min(variants, (c) -> d3.min(c.values, (v) -> new Date(v.time))),
+ d3.max(variants, (c) -> d3.max(c.values, (v) -> new Date(v.time)))
+ ])
+
+ y.domain([
+ d3.min(variants, (c) -> d3.min(c.values, (v) -> v.rate)),
+ d3.max(variants, (c) -> d3.max(c.values, (v) -> v.rate))
+ ])
+
+ svg.append('g')
+ .attr('class', 'x axis')
+ .attr('transform', 'translate(0,' + height + ')')
+ .call(xAxis)
+
+ svg.append('g')
+ .attr('class', 'y axis')
+ .call(yAxis)
+
+ svgVariant = svg.selectAll('.variants')
+ .data(variants)
+ .enter().append('g')
+ .attr('class', (d, i) -> "variants variant-#{i}")
+
+ # TODO - select first variant
+ # rules = svg.selectAll('g.rule')
+ # .data([variants.a])
+ # .enter().append('g')
+ # .attr('class', 'rule')
+ #
+ # rules.append('text')
+ # .attr('class', 'label')
+ # .attr('y', 400)
+ # .attr('x', (d, i) -> x(i))
+ # .attr('dy', '.35em')
+ # .attr('text-anchor', 'middle')
+ # .text((d) -> moment(d.time).format('MMMM Do').toUpperCase())
+
+ svgVariant.append('path')
+ .attr('class', 'line')
+ .attr('d', (d) -> line(d.values))
+
+ svgVariant.append('path')
+ .attr('class', 'area')
+ .attr('d', (d) -> area(d.values))
+
+ svgVariant.selectAll('circle')
+ .data((d) -> d.values)
+ .enter()
+ .append('circle')
+ .attr('class', (d, i) -> "circle circle-#{i}")
+ .attr('cx', (d, i) -> x(new Date(d.time)))
+ .attr('cy', (d, i) -> y(d.rate))
+ .attr('r', 4)
+
+ showToolTip: (e) =>
+
+
+ hideToolTip: (e) =>
+
+
+module.exports = Graph
@@ -0,0 +1,5 @@
+#= require d3
+#= require jquery
+#= require controller.module
+#= require moment.module
+#= require_tree .
@@ -3,7 +3,7 @@ class @Abba
constructor: (name, options = {}) ->
unless name
- throw new Error('Test name required')
+ throw new Error('Experiment name required')
# Force constructor
if this not instanceof Abba
@@ -53,8 +53,8 @@ class @Abba
throw new Error('No valid variant') unless variant
- # Record which test was run on the server
- @request('/start', test: @name, variant: variant.name)
+ # Record which experiment was run on the server
+ @request('/start', experiment: @name, variant: variant.name)
# Set the variant we chose as a cookie
@setVariantCookie(variant.name)
@@ -67,11 +67,15 @@ class @Abba
# Optionally pass a name, or read from the cookie
name or= @getVariantCookie()
- # Record the test was completed on the server
- @request('/complete', test: @name, variant: name) if name
+ # Record the experiment was completed on the server
+ @request('/complete', experiment: @name, variant: name) if name
@removeVariantCookie()
this
+ reset: =>
+ @removeVariantCookie()
+ @result = null
+
# Private
getVariantCookie: =>
@@ -89,8 +93,10 @@ class @Abba
# Utils
- request: (url, params) =>
- params = ("#{k}=#{encodeURIComponent(v)}" for k,v of params).join('&')
+ request: (url, params = {}) =>
+ # Prevent caching
+ params.i = new Date().getTime()
+ params = ("#{k}=#{encodeURIComponent(v)}" for k,v of params).join('&')
(new Image).src = "#{@constructor.endpoint}#{url}?#{params}"
true
Oops, something went wrong.

0 comments on commit 038b432

Please sign in to comment.