From b364f0cda540a77b4d094ad373813642f9607dde Mon Sep 17 00:00:00 2001 From: Radek Stepan Date: Thu, 18 Sep 2014 20:55:40 -0700 Subject: [PATCH] work with both labels and single points --- README.md | 1 + public/css/app.bundle.css | 2 +- public/css/app.css | 2 +- public/js/app.bundle.js | 38 ++++++++++++++------------ public/js/app.js | 38 ++++++++++++++------------ src/models/config.coffee | 5 ++-- src/modules/chart.coffee | 20 ++++++++------ src/modules/issues.coffee | 8 +++--- src/modules/project.coffee | 10 +++++-- src/styles/chart.styl | 2 +- src/templates/pages/showChart.mustache | 1 - 11 files changed, 69 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 748403d..2602621 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ GitHub Burndown Chart as a service. Public repos are free, for private access au - [ ] allow people to submit suggestions via GitHub Issues - [ ] find a way where, as a group, we can share repo data by trusting the other repo members that use our platform - [ ] support Jira & Gitlab +- [x] closed issues can be moved to a newly created milestone, this messes up the chart since we assume milestone is created first! ## Notes diff --git a/public/css/app.bundle.css b/public/css/app.bundle.css index e6efe10..4d1b39b 100644 --- a/public/css/app.bundle.css +++ b/public/css/app.bundle.css @@ -436,7 +436,7 @@ table { }@keyframes spin{0%{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)} 100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)} } -#chart{height:200px;position:relative;} +#chart{height:300px;position:relative;} #chart #tooltip{position:absolute;top:0;left:0} #chart svg path.line{fill:none;stroke-width:1px;clip-path:url("#clip");} #chart svg path.line.actual{stroke:#64584c;stroke-width:3px} diff --git a/public/css/app.css b/public/css/app.css index 0eb7431..df522d2 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -29,7 +29,7 @@ }@keyframes spin{0%{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)} 100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)} } -#chart{height:200px;position:relative;} +#chart{height:300px;position:relative;} #chart #tooltip{position:absolute;top:0;left:0} #chart svg path.line{fill:none;stroke-width:1px;clip-path:url("#clip");} #chart svg path.line.actual{stroke:#64584c;stroke-width:3px} diff --git a/public/js/app.bundle.js b/public/js/app.bundle.js index 65d7753..8d843d3 100644 --- a/public/js/app.bundle.js +++ b/public/js/app.bundle.js @@ -40038,7 +40038,7 @@ if (typeof exports === 'object') { "milestone": ["closed_issues", "created_at", "description", "due_on", "number", "open_issues", "title", "updated_at"] }, "chart": { - "off_days": [6, 7], + "off_days": [], "datetime": /^(\d{4}-\d{2}-\d{2})T(.*)/, "size_label": /^size (\d+)$/, "location": /^#!((\/[^\/]+){2,3})$/, @@ -40141,8 +40141,8 @@ if (typeof exports === 'object') { var head, max, min, range, rest; head = [ { - date: new Date(created_at), - points: total + 'date': new Date(created_at), + 'points': total } ]; min = +Infinity; @@ -40156,10 +40156,9 @@ if (typeof exports === 'object') { if (size > max) { max = size; } - return _.extend({}, issue, { - date: new Date(closed_at), - points: total -= size - }); + issue.date = new Date(closed_at); + issue.points = total -= size; + return issue; }); range = d3.scale.linear().domain([min, max]).range([5, 8]); rest = _.map(rest, function(issue) { @@ -40262,10 +40261,10 @@ if (typeof exports === 'object') { document.querySelector('#svg').innerHTML = ''; _ref = document.querySelector('#chart').getBoundingClientRect(), height = _ref.height, width = _ref.width; margin = { - top: 30, - right: 30, - bottom: 40, - left: 50 + 'top': 30, + 'right': 30, + 'bottom': 40, + 'left': 50 }; width -= margin.left + margin.right; height -= margin.top + margin.bottom; @@ -40411,7 +40410,7 @@ if (typeof exports === 'object') { }; return async.parallel([_.partial(one_status, 'open'), _.partial(one_status, 'closed')], cb); }, - 'filter': function(collection, regex, cb) { + 'filter': function(collection, cb) { var filtered, total; total = 0; switch (config.get('chart.points')) { @@ -40430,7 +40429,7 @@ if (typeof exports === 'object') { } issue.size = _.reduce(labels, function(sum, label) { var matches; - if (!(matches = label.name.match(regex))) { + if (!(matches = label.name.match(config.get('chart.size_label')))) { return sum; } return sum += parseInt(matches[1]); @@ -40531,11 +40530,11 @@ if (typeof exports === 'object') { return issues.get_all(opts, cb); }, function(all, cb) { return async.map(all, function(array, cb) { - return issues.filter(array, opts.size_label, function(err, filtered, total) { + return issues.filter(array, function(err, filtered, total) { return cb(err, [filtered, total]); }); }, function(err, _arg) { - var closed, open; + var closed, open, start; open = _arg[0], closed = _arg[1]; if (err) { return cb(err); @@ -40544,15 +40543,18 @@ if (typeof exports === 'object') { return cb('No matching issues found'); } opts.issues = { - closed: { + 'closed': { 'points': closed[1], 'data': closed[0] }, - open: { + 'open': { 'points': open[1], 'data': open[0] } }; + if ((start = closed[0][0].closed_at) < opts.milestone.created_at) { + opts.milestone.created_at = start; + } return cb(null); }); }, function(cb) { @@ -40759,7 +40761,7 @@ if (typeof exports === 'object') { // showChart.mustache root.require.register('burnchart/src/templates/pages/showChart.js', function(exports, require, module) { - module.exports = ["
","
","
","
","
","
"].join("\n"); + module.exports = ["
","
","
","
","
"].join("\n"); }); // projects.mustache diff --git a/public/js/app.js b/public/js/app.js index 64d79ca..fd5a3a3 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -72,7 +72,7 @@ "milestone": ["closed_issues", "created_at", "description", "due_on", "number", "open_issues", "title", "updated_at"] }, "chart": { - "off_days": [6, 7], + "off_days": [], "datetime": /^(\d{4}-\d{2}-\d{2})T(.*)/, "size_label": /^size (\d+)$/, "location": /^#!((\/[^\/]+){2,3})$/, @@ -175,8 +175,8 @@ var head, max, min, range, rest; head = [ { - date: new Date(created_at), - points: total + 'date': new Date(created_at), + 'points': total } ]; min = +Infinity; @@ -190,10 +190,9 @@ if (size > max) { max = size; } - return _.extend({}, issue, { - date: new Date(closed_at), - points: total -= size - }); + issue.date = new Date(closed_at); + issue.points = total -= size; + return issue; }); range = d3.scale.linear().domain([min, max]).range([5, 8]); rest = _.map(rest, function(issue) { @@ -296,10 +295,10 @@ document.querySelector('#svg').innerHTML = ''; _ref = document.querySelector('#chart').getBoundingClientRect(), height = _ref.height, width = _ref.width; margin = { - top: 30, - right: 30, - bottom: 40, - left: 50 + 'top': 30, + 'right': 30, + 'bottom': 40, + 'left': 50 }; width -= margin.left + margin.right; height -= margin.top + margin.bottom; @@ -445,7 +444,7 @@ }; return async.parallel([_.partial(one_status, 'open'), _.partial(one_status, 'closed')], cb); }, - 'filter': function(collection, regex, cb) { + 'filter': function(collection, cb) { var filtered, total; total = 0; switch (config.get('chart.points')) { @@ -464,7 +463,7 @@ } issue.size = _.reduce(labels, function(sum, label) { var matches; - if (!(matches = label.name.match(regex))) { + if (!(matches = label.name.match(config.get('chart.size_label')))) { return sum; } return sum += parseInt(matches[1]); @@ -565,11 +564,11 @@ return issues.get_all(opts, cb); }, function(all, cb) { return async.map(all, function(array, cb) { - return issues.filter(array, opts.size_label, function(err, filtered, total) { + return issues.filter(array, function(err, filtered, total) { return cb(err, [filtered, total]); }); }, function(err, _arg) { - var closed, open; + var closed, open, start; open = _arg[0], closed = _arg[1]; if (err) { return cb(err); @@ -578,15 +577,18 @@ return cb('No matching issues found'); } opts.issues = { - closed: { + 'closed': { 'points': closed[1], 'data': closed[0] }, - open: { + 'open': { 'points': open[1], 'data': open[0] } }; + if ((start = closed[0][0].closed_at) < opts.milestone.created_at) { + opts.milestone.created_at = start; + } return cb(null); }); }, function(cb) { @@ -793,7 +795,7 @@ // showChart.mustache root.require.register('burnchart/src/templates/pages/showChart.js', function(exports, require, module) { - module.exports = ["
","
","
","
","
","
"].join("\n"); + module.exports = ["
","
","
","
","
"].join("\n"); }); // projects.mustache diff --git a/src/models/config.coffee b/src/models/config.coffee index 6786add..cad1385 100644 --- a/src/models/config.coffee +++ b/src/models/config.coffee @@ -21,12 +21,13 @@ module.exports = new Model ] # Chart configuration. "chart": - "off_days": [ 6, 7 ] + # Days we are not working. + "off_days": [ ] # How do we parse GitHub dates? "datetime": /^(\d{4}-\d{2}-\d{2})T(.*)/ # How does a size label look like? "size_label": /^size (\d+)$/ # How do we specify which user/repo/(milestone) we want? "location": /^#!((\/[^\/]+){2,3})$/ - # Process all issues as one size. + # Process all issues as one size or use labels. "points": 'ALL_ONE_SIZE' \ No newline at end of file diff --git a/src/modules/chart.coffee b/src/modules/chart.coffee index a47dd82..be7b037 100644 --- a/src/modules/chart.coffee +++ b/src/modules/chart.coffee @@ -4,10 +4,13 @@ config = require '../models/config' module.exports = # A graph of closed issues. + # `collection`: issues + # `created_at`: milestone start date + # `total`: total number of points (open & closed issues) 'actual': (collection, created_at, total, cb) -> head = [ { - date: new Date(created_at) - points: total + 'date': new Date created_at + 'points': total } ] min = +Infinity ; max = -Infinity @@ -20,9 +23,9 @@ module.exports = max = size if size > max # Dropping points remaining. - _.extend {}, issue, - date: new Date(closed_at) - points: total -= size + issue.date = new Date closed_at + issue.points = total -= size + issue # Now add a radius in a range (will be used for a circle). range = d3.scale.linear().domain([ min, max ]).range([ 5, 8 ]) @@ -125,14 +128,14 @@ module.exports = # Get available space. { height, width } = document.querySelector('#chart').getBoundingClientRect() - margin = { top: 30, right: 30, bottom: 40, left: 50 } + margin = { 'top': 30, 'right': 30, 'bottom': 40, 'left': 50 } width -= margin.left + margin.right height -= margin.top + margin.bottom # Scales. x = d3.time.scale().range([ 0, width ]) y = d3.scale.linear().range([ height, 0 ]) - + # Axes. xAxis = d3.svg.axis().scale(x) .orient("bottom") @@ -225,9 +228,8 @@ module.exports = # Show when we closed an issue. svg.selectAll("a.issue") - .data(actual[1...]) # skip the starting point + .data(actual.slice(1)) # skip the starting point .enter() - # A wrapping link. .append('svg:a') .attr("xlink:href", ({ html_url }) -> html_url ) diff --git a/src/modules/issues.coffee b/src/modules/issues.coffee index 6e647b0..bfb5b09 100644 --- a/src/modules/issues.coffee +++ b/src/modules/issues.coffee @@ -31,10 +31,10 @@ module.exports = ], cb # Filter an array of incoming issues based on a regex & save size on them. - 'filter': (collection, regex, cb) -> + 'filter': (collection, cb) -> # The total size of all issues. total = 0 - + # Which point counting mode are we in? switch config.get 'chart.points' # All issues are the same size @@ -53,7 +53,7 @@ module.exports = # Determine the total issue size from all labels. issue.size = _.reduce labels, (sum, label) -> # Not matching. - return sum unless matches = label.name.match(regex) + return sum unless matches = label.name.match config.get 'chart.size_label' # Increase sum. sum += parseInt matches[1] , 0 @@ -63,5 +63,5 @@ module.exports = # Are we saving it? !!issue.size - + cb null, filtered, total \ No newline at end of file diff --git a/src/modules/project.coffee b/src/modules/project.coffee index 2c64f19..2efe0fa 100644 --- a/src/modules/project.coffee +++ b/src/modules/project.coffee @@ -21,7 +21,7 @@ module.exports = (opts, cb) -> # Filter them to labeled ones. (all, cb) -> async.map all, (array, cb) -> - issues.filter array, opts.size_label, (err, filtered, total) -> + issues.filter array, (err, filtered, total) -> cb err, [ filtered, total ] , (err, [ open, closed ]) -> return cb err if err @@ -29,8 +29,12 @@ module.exports = (opts, cb) -> return cb 'No matching issues found' if open[1] + closed[1] is 0 # Save the open/closed on us first. opts.issues = - closed: { 'points': closed[1], 'data': closed[0] } - open: { 'points': open[1], 'data': open[0] } + 'closed': { 'points': closed[1], 'data': closed[0] } + 'open': { 'points': open[1], 'data': open[0] } + # Do we need to move the milestone start date? + if (start = closed[0][0].closed_at) < opts.milestone.created_at + opts.milestone.created_at = start + cb null # Create actual and ideal lines & render. diff --git a/src/styles/chart.styl b/src/styles/chart.styl index d47e2a5..0d60149 100644 --- a/src/styles/chart.styl +++ b/src/styles/chart.styl @@ -10,7 +10,7 @@ $background2 = #CC9485 // where D3 renders to #chart - height: 200px + height: 300px position: relative // position will be adjusted dynamically diff --git a/src/templates/pages/showChart.mustache b/src/templates/pages/showChart.mustache index 812bda8..827f64a 100644 --- a/src/templates/pages/showChart.mustache +++ b/src/templates/pages/showChart.mustache @@ -1,6 +1,5 @@
-
\ No newline at end of file