Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dashboard + Detail charts are now cumulative. #38

Merged
merged 5 commits into from Mar 10, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 18 additions & 18 deletions sixpack/static/css/style.css
Expand Up @@ -211,36 +211,36 @@
/*End Chrome bug workaround*/
/* END: Experiments */

/* BEGIN: Placeholder rules for D3 graph */
.graph .axis path, .graph .axis line {
/* BEGIN: Placeholder rules for D3 chart */
.chart .axis path, .chart .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.graph .x.axis path {
.chart .x.axis path {
display: none
}
.graph .line {
.chart .line {
fill: none;
stroke-width: 2.2px;
}
.graph g.tick text {
.chart g.tick text {
font-weight: 400;
font-size: 12px;
fill: #3e4547;
}
.graph .grid .tick {
.chart .grid .tick {
stroke: #2e3335
}
.graph .grid path {
.chart .grid path {
stroke-width: 0
}
.graph .area {
.chart .area {
fill: #fff;
opacity: 0.02;
stroke-width: 0;
}
/* END: Placeholder rules for D3 graph */
/* END: Placeholder rules for D3 chart */
div.header {
min-height: 65px;
position: relative;
Expand Down Expand Up @@ -385,7 +385,7 @@ ul.experiments h4 {
#dashboard-page ul.experiments li table tr td:nth-child(4) {
min-width: 124px;
}
#dashboard-page .graph {
#dashboard-page .chart {
position: absolute;
bottom: 40px;
right: 70px;
Expand All @@ -396,7 +396,7 @@ ul.experiments h4 {
#details-page .header .controls .collapse {
display: inline-block;
}
#details-page .graph {
#details-page .chart {
width: 330px;
height: 200px;
margin-bottom: 50px;
Expand Down Expand Up @@ -424,7 +424,7 @@ ul.experiments h4 {

/* BEGIN: Responsive rules */
@media (max-width: 1200px) {
#dashboard-page .graph {
#dashboard-page .chart {
display: none;
}
.container {
Expand Down Expand Up @@ -514,27 +514,27 @@ hr {
border: none;
margin: 40px;
}
#details-page .graph {
#details-page .chart {
float:left;
padding-bottom: 50px;
padding-right: 125px;
}
#details-page #graphs {
#details-page #charts {
max-width: 910px;
margin: 0 auto;
}
.graph p {
.chart p {
color: #6D6D6D;
font-weight: 600;
text-transform: uppercase;
}
#dashboard-page .graph p {
#dashboard-page .chart p {
margin-left: 15px;
margin-top: 90px;
text-align: center;
}

/* Graph color values.*/
/* Chart color values.*/
.color-1 {
background: #714A33;
stroke: #714A33;
Expand Down Expand Up @@ -591,4 +591,4 @@ hr {
background: #E99E62;
stroke: #E99E62;
}
/* End graph color values */
/* End chart color values */
199 changes: 113 additions & 86 deletions sixpack/static/js/chart.js
@@ -1,20 +1,25 @@
var GraphMaker;
var Chart;
$(function () {

GraphMaker = function (element) {
Chart = function (experiment, callback) {
var that = {}, my = {};

my.$element = $(element);
my.margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
my.el = null;
my.experiment = experiment;
my.callback = callback;

my.getMeasurements = function () {
my.margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
};
my.width = my.el.width();
my.height = my.el.height();
my.xScale = d3.time.scale().range([0, my.width]);
my.yScale = d3.scale.linear().range([my.height, 0]);
};
my.width = my.$element.width();
my.height = my.$element.height();
my.xScale = d3.time.scale().range([0, my.width]);
my.yScale = d3.scale.linear().range([my.height, 0]);

my.drawLabels = function (data) {
var xValues, yValues, yMin, yMax;
Expand Down Expand Up @@ -78,24 +83,8 @@ $(function () {
.attr("d", area);
};

// Calculate conversion rates each time interval
my.formatRateData = function (participants, conversions) {
var rate = 0;
return _.map(participants, function (participant, key) {
conversion = _.find(conversions, function (conversion) {
return conversion[0] === participant[0]
});
if (conversion === undefined) {
conversion = [participant[0], 0]
}
rate = Number(conversion[1] / participant[1]).toFixed(2);
if (isNaN(rate)) rate = 0.00;
return [participant[0], rate];
});
};

// Compose a D3-friendly data structure
my.formatGraphData = function (rates) {
my.formatChartData = function (rates) {
return rates.map(function (d) {
return {
date: d3.time.format("%Y-%m-%d").parse(d[0]),
Expand All @@ -105,7 +94,7 @@ $(function () {
};

my.drawBase = function () {
my.svg = d3.select(element).append("svg")
my.svg = d3.select('#' + my.el.attr('id')).append("svg")
.attr("width", my.width + my.margin.left + my.margin.right)
.attr("height", my.height + my.margin.top + my.margin.bottom)
.append("g")
Expand Down Expand Up @@ -142,77 +131,115 @@ $(function () {
};

my.dataExists = function (data) {
if (data.participants.length <= 2) {
my.$element.append("<p>Not enough data to graph</p>");
if (data.rate_data.length <= 2) {
my.el.append("<p>Not enough data to chart</p>");
return false;
}
return true;
};

/**
* Takes an array of alt(s) and draws them to graph.
* @param {Array} alt An array of objects
* @param {Array<Array<String>>} conversions
* @param {Array<Array<String>>} participants
* @param {String} color
* @return
*/
that.draw = function (alts) {
var data, rate_data, d3_data, aggregate_rates, data_intervals, min_rate, max_rate;

if (alts.length === 1) {
if (!my.dataExists(alts[0])) return;

rate_data = my.formatRateData(alts[0].participants, alts[0].conversions);
d3_data = my.formatGraphData(rate_data);

my.drawBase();
my.drawLabels(rate_data);
my.drawBackground(d3_data);
my.drawLine(d3_data, alts[0].color);
my.drawArea(d3_data);
} else {
// TODO: better data check
if (!my.dataExists(alts[0])) return;

// Get the aggregate data intervals for drawing labels + background
aggregate_rates = [];
_.each(alts, function (alt, k) {
_.each(my.formatRateData(alt.participants, alt.conversions), function (rate, k) {
aggregate_rates.push(rate);
my.getData = function (callback) {
var url = '/experiment/' + my.experiment + '.json?period=day';
$.getJSON(url, function (data) {
var alternatives = {};
var cumulative = {
participants: 0,
conversions: 0
}
var rate_data = [];
var rate = 0;

_.each(data.alternatives, function (alt, k) {
cumulative.participants = 0;
cumulative.conversions = 0;
rate_data = [];

_.each(alt.data, function (period) {
cumulative.participants += period.participants;
cumulative.conversions += period.conversions;

rate = Number(cumulative.conversions / cumulative.participants).toFixed(5);
if (isNaN(rate)) rate = 0.00;
rate_data.push([period.date, rate]);
});

alternatives[alt.name] = {
'rate_data': rate_data,
'd3_data': my.formatChartData(rate_data)
};
});

data_intervals = _.uniq(_.map(aggregate_rates, function (d, k) {
return d[0];
}));
callback(alternatives);
});
};


min_rate = _.min(_.map(aggregate_rates, function(n) {
return parseFloat(n[1]);
}));
max_rate = _.max(_.map(aggregate_rates, function(n) {
return parseFloat(n[1]);
}));

rate_data = _.map(data_intervals, function (date, index) {
return [date, min_rate];
that.drawExperiment = function (experiment_name, colors) {
my.el = $('#chart-' + experiment_name);

// Get the aggregate data intervals for drawing labels + background
var aggregate_rates = [];
_.each(my.data, function (alt, k) {
_.each(alt.rate_data, function (rate, k) {
aggregate_rates.push(rate);
});
rate_data[0][1] = max_rate;
});

d3_data = my.formatGraphData(rate_data);
var data_intervals = _.uniq(_.map(aggregate_rates, function (d, k) {
return d[0];
}));

my.drawBase();
my.drawLabels(rate_data);
my.drawBackground(d3_data);
var min_rate = _.min(_.map(aggregate_rates, function (n) {
return parseFloat(n[1]);
}));

_.each(alts, function (alt, k) {
rate_data = my.formatRateData(alt.participants, alt.conversions);
d3_data = my.formatGraphData(rate_data);
my.drawLine(d3_data, alt.color);
});
}
var max_rate = _.max(_.map(aggregate_rates, function (n) {
return parseFloat(n[1]);
}));

var rate_data = _.map(data_intervals, function (date, index) {
return [date, min_rate];
});
rate_data[0][1] = max_rate;

var data = {
rate_data: rate_data,
d3_data: my.formatChartData(rate_data),
};

if (!my.dataExists(data)) return;

my.getMeasurements();
my.drawBase();
my.drawLabels(data.rate_data);
my.drawBackground(data.d3_data);

var i = 0;
_.each(my.data, function (data) {
my.drawLine(data.d3_data, colors[i]);
i++;
});
};

that.drawAlternative = function (alternative_name, color) {
var data = my.data[alternative_name];
my.el = $('#chart-' + alternative_name);
if (!my.dataExists(data)) return;

my.getMeasurements();
my.drawBase();
my.drawLabels(data.rate_data);
my.drawBackground(data.d3_data);
my.drawLine(data.d3_data, color);
my.drawArea(data.d3_data);
};

my.getData(function (data) {
my.data = data;
my.callback();
});

return that;
};

});