more intelligent time ticks; closes #65
This commit is contained in:
parent
687a32e7de
commit
e3953f6ce5
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "burnchart",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "GitHub Burndown Chart as a Service",
|
||||
"author": "Radek Stepan <dev@radekstepan.com> (http://radekstepan.com)",
|
||||
"license": "AGPL-3.0",
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -67,8 +67,8 @@ export default React.createClass({
|
|||
let y = d3.scale.linear().range([ height, 0 ]);
|
||||
|
||||
// Axes.
|
||||
let xAxis = axes.horizontal(height, x);
|
||||
let yAxis = axes.vertical(width, y);
|
||||
let xAxis = axes.time(height, x, milestone.stats.span);
|
||||
let yAxis = axes.points(width, y);
|
||||
|
||||
// Line generator.
|
||||
let line = d3.svg.line()
|
||||
|
@ -104,22 +104,13 @@ export default React.createClass({
|
|||
.attr("transform", `translate(0,${height})`)
|
||||
.call(xAxis);
|
||||
|
||||
// Add the months x-axis.
|
||||
let m = [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||
];
|
||||
|
||||
let mAxis = xAxis
|
||||
.orient("top")
|
||||
.tickSize(height)
|
||||
.tickFormat((d) => m[d.getMonth()])
|
||||
.ticks(2);
|
||||
// Add the years x-axis?
|
||||
let yrAxis = axes.year(height, xAxis, milestone.stats.span);
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "x axis month")
|
||||
.attr("class", "x axis year")
|
||||
.attr("transform", `translate(0,${height})`)
|
||||
.call(mAxis);
|
||||
.call(yrAxis);
|
||||
|
||||
// Add the y-axis.
|
||||
svg.append("g")
|
||||
|
|
|
@ -1,19 +1,32 @@
|
|||
import d3 from 'd3';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
|
||||
horizontal(height, x) {
|
||||
time(height, x, span) {
|
||||
// Tick time format based on number of days we display.
|
||||
let specifier = (span < 4) ? '' : (span < 14) ? '%a' : (span < 32) ? '%m/%d' : '%b';
|
||||
let format = d3.time.format.utc(specifier);
|
||||
|
||||
return d3.svg.axis().scale(x)
|
||||
.orient("bottom")
|
||||
// Show vertical lines...
|
||||
.tickSize(-height)
|
||||
// with day of the month...
|
||||
.tickFormat((d) => { return d.getDate(); })
|
||||
// tick time format...
|
||||
.tickFormat(format)
|
||||
// and give us a spacer.
|
||||
.tickPadding(10);
|
||||
},
|
||||
|
||||
vertical(width, y) {
|
||||
year(height, xAxis, span) {
|
||||
return xAxis
|
||||
.orient("top")
|
||||
.tickSize(height)
|
||||
.tickFormat(d3.time.format.utc('%Y'))
|
||||
.ticks(span / 365);
|
||||
},
|
||||
|
||||
points(width, y) {
|
||||
return d3.svg.axis().scale(y)
|
||||
.orient("left")
|
||||
.tickSize(-width)
|
||||
|
|
|
@ -16,17 +16,23 @@ export default (milestone) => {
|
|||
// Makes testing easier...
|
||||
if (milestone.stats != null) return milestone.stats;
|
||||
|
||||
let isDone = false, isOnTime = true, isOverdue = false,
|
||||
isEmpty = true, points = 0, a, b, c, time, days;
|
||||
let points = 0, a, b, c, time, days, span;
|
||||
|
||||
let stats = {
|
||||
'isDone': false,
|
||||
'isOnTime': true,
|
||||
'isOverdue': false,
|
||||
'isEmpty': true
|
||||
};
|
||||
|
||||
// Progress in points.
|
||||
a = milestone.issues.closed.size;
|
||||
b = milestone.issues.open.size;
|
||||
if (a) {
|
||||
isEmpty = false;
|
||||
if (a + b > 0) {
|
||||
points = progress(a, b);
|
||||
if (points === 100) isDone = true;
|
||||
let i = milestone.issues.closed.size,
|
||||
j = milestone.issues.open.size;
|
||||
if (i) {
|
||||
stats.isEmpty = false;
|
||||
if (i + j > 0) {
|
||||
points = progress(i, j);
|
||||
if (points === 100) stats.isDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,30 +43,33 @@ export default (milestone) => {
|
|||
, milestone.created_at);
|
||||
}
|
||||
|
||||
// Milestones with no due date are always on track.
|
||||
if (!(milestone.due_on != null)) {
|
||||
return { isOverdue, isOnTime, isDone, isEmpty, 'progress': { points } };
|
||||
}
|
||||
|
||||
// The dates in this milestone.
|
||||
a = moment(milestone.created_at, moment.ISO_8601);
|
||||
b = moment.utc();
|
||||
c = moment(milestone.due_on, moment.ISO_8601);
|
||||
|
||||
// Milestones with no due date are always on track.
|
||||
if (!(milestone.due_on != null)) {
|
||||
// The number of days from start to now.
|
||||
span = b.diff(a, 'days');
|
||||
return _.extend(stats, { span, 'progress': { points } });
|
||||
}
|
||||
|
||||
// Overdue? Regardless of the date, if we have closed all
|
||||
// issues, we are no longer overdue.
|
||||
if (b.isAfter(c) && !isDone) isOverdue = true;
|
||||
if (b.isAfter(c) && !stats.isDone) stats.isOverdue = true;
|
||||
|
||||
// Progress in time.
|
||||
time = progress(b.diff(a), c.diff(b));
|
||||
|
||||
// How many days is 1% of the time?
|
||||
// Number of days between start and due date or today if overdue.
|
||||
span = (stats.isOverdue ? b : c).diff(a, 'days');
|
||||
|
||||
// How many days is 1% of the time until now?
|
||||
days = (b.diff(a, 'days')) / 100;
|
||||
|
||||
// Are we on time?
|
||||
isOnTime = points > time;
|
||||
|
||||
// If we have closed all issues, we are "on time".
|
||||
if (isDone) isOnTime = true;
|
||||
stats.isOnTime = stats.isDone || points > time;
|
||||
|
||||
return { isOverdue, isOnTime, isDone, isEmpty, days, 'progress': { points, time } };
|
||||
return _.extend(stats, { days, span, 'progress': { points, time } });
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue