From 07d2fc5d358992a52908cccbbca4a11d01e98da3 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 10 Nov 2012 15:41:09 -0600 Subject: Add initial version of mirror status chart Still have some hardcoded stuff to rip out and replace to make this a bit more dynamic on things like sizing, but for now, this is a great start. Signed-off-by: Dan McGee --- mirrors/static/mirror_status.js | 97 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 mirrors/static/mirror_status.js (limited to 'mirrors/static') diff --git a/mirrors/static/mirror_status.js b/mirrors/static/mirror_status.js new file mode 100644 index 00000000..1c510cee --- /dev/null +++ b/mirrors/static/mirror_status.js @@ -0,0 +1,97 @@ +function mirror_status(chart_id, data_url) { + d3.json(data_url, function(json) { + data = jQuery.map(json['urls'], + function(url, i) { + return jQuery.map(url['logs'], + function(log, j) { + return { + url: url['url'], + duration: log['duration'], + check_time: new Date(log['check_time']) + }; + }); + }); + + var margin = {top: 20, right: 20, bottom: 30, left: 40}, + width = 1200 - margin.left - margin.right, + height = 450 - margin.top - margin.bottom; + + var color = d3.scale.category20(), + x = d3.time.scale.utc().range([0, width]), + y = d3.scale.linear().range([height, 0]), + x_axis = d3.svg.axis().scale(x).orient("bottom"), + y_axis = d3.svg.axis().scale(y).orient("left"); + + var svg = d3.select(chart_id).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.extent(data, function(d) { return d.check_time; })).nice(d3.time.hour); + y.domain(d3.extent(data, function(d) { return d.duration; })).nice(); + + svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + height + ")") + .call(x_axis) + .append("text") + .attr("class", "label") + .attr("x", width) + .attr("y", -6) + .style("text-anchor", "end") + .text("Check Time (UTC)"); + + svg.append("g") + .attr("class", "y axis") + .call(y_axis) + .append("text") + .attr("class", "label") + .attr("transform", "rotate(-90)") + .attr("y", 6) + .attr("dy", ".71em") + .style("text-anchor", "end") + .text("Duration (seconds)"); + + svg.selectAll(".dot") + .data(data) + .enter() + .append("circle") + .attr("class", "dot") + .attr("r", 3.5) + .attr("cx", function(d) { return x(d.check_time); }) + .attr("cy", function(d) { return y(d.duration); }) + .style("fill", function(d) { return color(d.url); }); + + var legend = svg.selectAll(".legend") + .data(color.domain()) + .enter().append("g") + .attr("class", "legend") + .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); + + legend.append("rect") + .attr("x", width - 18) + .attr("width", 18) + .attr("height", 18) + .style("fill", color); + + legend.append("text") + .attr("x", width - 24) + .attr("y", 9) + .attr("dy", ".35em") + .style("text-anchor", "end") + .text(function(d) { return d; }); + }); + + var resize_timeout = null; + var real_resize = function() { + resize_timeout = null; + /* TODO: implement resize */ + }; + jQuery(window).resize(function() { + if (resize_timeout) { + clearTimeout(resize_timeout); + } + resize_timeout = setTimeout(real_resize, 200); + }); +} -- cgit v1.2.3-2-g168b From aeeb4718e83cd2f82d94b1aa0c0ba36ba21a2b37 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 10 Nov 2012 16:12:15 -0600 Subject: Enable mirror status graph resizing Signed-off-by: Dan McGee --- mirrors/static/mirror_status.js | 43 +++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'mirrors/static') diff --git a/mirrors/static/mirror_status.js b/mirrors/static/mirror_status.js index 1c510cee..277d3c88 100644 --- a/mirrors/static/mirror_status.js +++ b/mirrors/static/mirror_status.js @@ -1,20 +1,10 @@ function mirror_status(chart_id, data_url) { - d3.json(data_url, function(json) { - data = jQuery.map(json['urls'], - function(url, i) { - return jQuery.map(url['logs'], - function(log, j) { - return { - url: url['url'], - duration: log['duration'], - check_time: new Date(log['check_time']) - }; - }); - }); + var jq_div = jQuery(chart_id); + var draw_graph = function(data) { var margin = {top: 20, right: 20, bottom: 30, left: 40}, - width = 1200 - margin.left - margin.right, - height = 450 - margin.top - margin.bottom; + width = jq_div.width() - margin.left - margin.right, + height = jq_div.height() - margin.top - margin.bottom; var color = d3.scale.category20(), x = d3.time.scale.utc().range([0, width]), @@ -22,6 +12,8 @@ function mirror_status(chart_id, data_url) { x_axis = d3.svg.axis().scale(x).orient("bottom"), y_axis = d3.svg.axis().scale(y).orient("left"); + /* remove any existing graph first if we are redrawing after resize */ + d3.select(chart_id).select("svg").remove(); var svg = d3.select(chart_id).append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) @@ -31,6 +23,7 @@ function mirror_status(chart_id, data_url) { x.domain(d3.extent(data, function(d) { return d.check_time; })).nice(d3.time.hour); y.domain(d3.extent(data, function(d) { return d.duration; })).nice(); + /* build the axis lines... */ svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") @@ -53,6 +46,7 @@ function mirror_status(chart_id, data_url) { .style("text-anchor", "end") .text("Duration (seconds)"); + /* ...then the points themselves. */ svg.selectAll(".dot") .data(data) .enter() @@ -63,6 +57,7 @@ function mirror_status(chart_id, data_url) { .attr("cy", function(d) { return y(d.duration); }) .style("fill", function(d) { return color(d.url); }); + /* add a legend for good measure */ var legend = svg.selectAll(".legend") .data(color.domain()) .enter().append("g") @@ -81,12 +76,30 @@ function mirror_status(chart_id, data_url) { .attr("dy", ".35em") .style("text-anchor", "end") .text(function(d) { return d; }); + }; + + /* invoke the data-fetch + first draw */ + var cached_data = null; + d3.json(data_url, function(json) { + cached_data = jQuery.map(json['urls'], + function(url, i) { + return jQuery.map(url['logs'], + function(log, j) { + return { + url: url['url'], + duration: log['duration'], + check_time: new Date(log['check_time']) + }; + }); + }); + draw_graph(cached_data); }); + /* then hook up a resize handler to redraw if necessary */ var resize_timeout = null; var real_resize = function() { resize_timeout = null; - /* TODO: implement resize */ + draw_graph(cached_data); }; jQuery(window).resize(function() { if (resize_timeout) { -- cgit v1.2.3-2-g168b From a358e132886972dc4e9f1f546e36a5f3a2218a39 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 10 Nov 2012 17:09:51 -0600 Subject: Mirror status graph, now with points AND lines We might have to tweak the interpolation method once we see this with real data, but for now it looks really pretty locally. Signed-off-by: Dan McGee --- mirrors/static/mirror_status.js | 58 +++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 16 deletions(-) (limited to 'mirrors/static') diff --git a/mirrors/static/mirror_status.js b/mirrors/static/mirror_status.js index 277d3c88..d107d7d1 100644 --- a/mirrors/static/mirror_status.js +++ b/mirrors/static/mirror_status.js @@ -6,7 +6,7 @@ function mirror_status(chart_id, data_url) { width = jq_div.width() - margin.left - margin.right, height = jq_div.height() - margin.top - margin.bottom; - var color = d3.scale.category20(), + var color = d3.scale.category10(), x = d3.time.scale.utc().range([0, width]), y = d3.scale.linear().range([height, 0]), x_axis = d3.svg.axis().scale(x).orient("bottom"), @@ -20,8 +20,14 @@ function mirror_status(chart_id, data_url) { .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - x.domain(d3.extent(data, function(d) { return d.check_time; })).nice(d3.time.hour); - y.domain(d3.extent(data, function(d) { return d.duration; })).nice(); + x.domain([ + d3.min(data, function(c) { return d3.min(c.logs, function(v) { return v.check_time; }); }), + d3.max(data, function(c) { return d3.max(c.logs, function(v) { return v.check_time; }); }) + ]); + y.domain([ + d3.min(data, function(c) { return d3.min(c.logs, function(v) { return v.duration; }); }), + d3.max(data, function(c) { return d3.max(c.logs, function(v) { return v.duration; }); }) + ]); /* build the axis lines... */ svg.append("g") @@ -46,12 +52,32 @@ function mirror_status(chart_id, data_url) { .style("text-anchor", "end") .text("Duration (seconds)"); - /* ...then the points themselves. */ - svg.selectAll(".dot") + var line = d3.svg.line() + .interpolate("basis") + .x(function(d) { return x(d.check_time); }) + .y(function(d) { return y(d.duration); }); + + /* ...then the points and lines between them. */ + var urls = svg.selectAll(".url") .data(data) .enter() + .append("g") + .attr("class", "url"); + + urls.append("path") + .attr("class", "url-line") + .attr("d", function(d) { return line(d.logs); }) + .style("stroke", function(d) { return color(d.url); }); + + urls.selectAll("circle") + .data(function(u) { + return jQuery.map(u.logs, function(l, i) { + return {url: u.url, check_time: l.check_time, duration: l.duration}; + }); + }) + .enter() .append("circle") - .attr("class", "dot") + .attr("class", "url-dot") .attr("r", 3.5) .attr("cx", function(d) { return x(d.check_time); }) .attr("cy", function(d) { return y(d.duration); }) @@ -81,16 +107,16 @@ function mirror_status(chart_id, data_url) { /* invoke the data-fetch + first draw */ var cached_data = null; d3.json(data_url, function(json) { - cached_data = jQuery.map(json['urls'], - function(url, i) { - return jQuery.map(url['logs'], - function(log, j) { - return { - url: url['url'], - duration: log['duration'], - check_time: new Date(log['check_time']) - }; - }); + cached_data = jQuery.map(json.urls, function(url, i) { + return { + url: url.url, + logs: jQuery.map(url.logs, function(log, j) { + return { + duration: log.duration, + check_time: new Date(log.check_time) + }; + }) + }; }); draw_graph(cached_data); }); -- cgit v1.2.3-2-g168b From 923ebbb53abf1d77a2f21b76e88faa085251af78 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 10 Nov 2012 17:20:26 -0600 Subject: Re-add nice() calls for mirror status axes Signed-off-by: Dan McGee --- mirrors/static/mirror_status.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'mirrors/static') diff --git a/mirrors/static/mirror_status.js b/mirrors/static/mirror_status.js index d107d7d1..1c352a9f 100644 --- a/mirrors/static/mirror_status.js +++ b/mirrors/static/mirror_status.js @@ -23,11 +23,11 @@ function mirror_status(chart_id, data_url) { x.domain([ d3.min(data, function(c) { return d3.min(c.logs, function(v) { return v.check_time; }); }), d3.max(data, function(c) { return d3.max(c.logs, function(v) { return v.check_time; }); }) - ]); + ]).nice(d3.time.hour); y.domain([ d3.min(data, function(c) { return d3.min(c.logs, function(v) { return v.duration; }); }), d3.max(data, function(c) { return d3.max(c.logs, function(v) { return v.duration; }); }) - ]); + ]).nice(); /* build the axis lines... */ svg.append("g") -- cgit v1.2.3-2-g168b From e26d5722289bd2e972633891d8dac09296b0cbc4 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 11 Nov 2012 14:55:37 -0600 Subject: Mirror graph tweaking after usage with real data * Clamp y-axis minimum to 0. * Don't plot `is_success == false` values. * Ensure URLs are sorted predictably. Signed-off-by: Dan McGee --- mirrors/static/mirror_status.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'mirrors/static') diff --git a/mirrors/static/mirror_status.js b/mirrors/static/mirror_status.js index 1c352a9f..decc8fb8 100644 --- a/mirrors/static/mirror_status.js +++ b/mirrors/static/mirror_status.js @@ -25,7 +25,7 @@ function mirror_status(chart_id, data_url) { d3.max(data, function(c) { return d3.max(c.logs, function(v) { return v.check_time; }); }) ]).nice(d3.time.hour); y.domain([ - d3.min(data, function(c) { return d3.min(c.logs, function(v) { return v.duration; }); }), + 0, d3.max(data, function(c) { return d3.max(c.logs, function(v) { return v.duration; }); }) ]).nice(); @@ -111,6 +111,9 @@ function mirror_status(chart_id, data_url) { return { url: url.url, logs: jQuery.map(url.logs, function(log, j) { + if (!log.is_success) { + return null; + } return { duration: log.duration, check_time: new Date(log.check_time) -- cgit v1.2.3-2-g168b From 99bfdda5f257107396179694b7c56aad8e5e6701 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 11 Nov 2012 16:40:21 -0600 Subject: Add title to mirror status data points Signed-off-by: Dan McGee --- mirrors/static/mirror_status.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'mirrors/static') diff --git a/mirrors/static/mirror_status.js b/mirrors/static/mirror_status.js index decc8fb8..8ec85c40 100644 --- a/mirrors/static/mirror_status.js +++ b/mirrors/static/mirror_status.js @@ -81,7 +81,9 @@ function mirror_status(chart_id, data_url) { .attr("r", 3.5) .attr("cx", function(d) { return x(d.check_time); }) .attr("cy", function(d) { return y(d.duration); }) - .style("fill", function(d) { return color(d.url); }); + .style("fill", function(d) { return color(d.url); }) + .append("title") + .text(function(d) { return d.url + "\n" + d.duration.toFixed(3) + " secs\n" + d.check_time.toUTCString(); }); /* add a legend for good measure */ var legend = svg.selectAll(".legend") -- cgit v1.2.3-2-g168b