Skip to content

Commit

Permalink
DepCard: Add tasks, tasksCompleted, and a progress bar
Browse files Browse the repository at this point in the history
The GitHub feature is described in [1].  I'm scraping it with a
regexp, since they don't seem to export parsed task information via
the API.

[1]: https://help.github.com/articles/about-task-lists/
  • Loading branch information
wking committed Nov 28, 2016
1 parent 9acd8d5 commit 25fadc4
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 18 deletions.
11 changes: 7 additions & 4 deletions webapp/__mocks__/github-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,14 @@ class Issues {
return [];
}
};

var bodyLines = dependencies(number).map(function (dep) {
return 'depends on ' + dep;
});
bodyLines.push('');
bodyLines.push('- [ ] an uncompleted task');
bodyLines.push('- [x] a completed task');
return {
body: dependencies(number).map(function (dep) {
return 'depends on ' + dep;
}).join('\n') + '\n',
body: bodyLines.join('\n') + '\n',
html_url: `https://github.com/${this._user}/${this._repo}/issues/${number}`,
number: number,
repository_url: `https://api.github.com/repos/${this._user}/${this._repo}`,
Expand Down
58 changes: 45 additions & 13 deletions webapp/src/DepCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,31 @@ class DepCard extends PureComponent {
dependencies={this.dependencyCount()}
related={this.relatedCount()}
dependents={this.dependentCount(nodes || {})}
done={this.props.done} />
done={this.props.done}
tasks={this.props.tasks}
tasksCompleted={this.props.tasksCompleted}
user={this.props.user} />
}

render() {
var width = 15;
var height = 3;
var radius = 0.5;
var color = Neutral;
var style = {
fill: color,
fillOpacity: '0.1',
fillOpacity: 0.1,
stroke: this.props.done ? Green : Red,
strokeWidth: 0.2,
};
var backgroundStyle = {
fill: 'white',
stroke: 'white',
strokeWidth: 0.1,
stroke: 'none',
};
var taskStyle = {
fill: Green,
fillOpacity: 0.1,
stroke: 'none',
};
var logo, host;
if (this.props.host === 'asana.com') {
Expand All @@ -87,31 +95,55 @@ class DepCard extends PureComponent {
} else {
throw new Error('unrecognized host: ' + this.props.host);
}
var taskRatio = 1;
if (this.props.tasks) {
taskRatio = this.props.tasksCompleted / this.props.tasks;
} else {
taskStyle.fill = 'white';
}
var left = this.props.cx - width/2;
var right = this.props.cx + width/2;
var leftCenter = left + radius;
var rightTask = left + width * taskRatio;
var top = this.props.cy - height/2;
var bottom = this.props.cy + height/2;
var topCenter = top + radius;
var bottomCenter = bottom - radius;
var taskPath = [
`M ${left} ${topCenter}`,
`A ${radius} ${radius} 0 0 1 ${leftCenter} ${top}`,
`L ${rightTask} ${top}`,
`L ${rightTask} ${bottom}`,
`L ${leftCenter} ${bottom}`,
`A ${radius} ${radius} 0 0 1 ${left} ${bottomCenter}`,
`Z`,
];
return <g className="DepCard" xmlnsXlink="http://www.w3.org/1999/xlink">
<rect
x={this.props.cx - width/2} y={this.props.cy - height/2}
width={width} height={height} rx="0.5" ry="0.5" style={backgroundStyle}>
</rect>
<rect
x={this.props.cx - width/2} y={this.props.cy - height/2}
width={width} height={height} rx="0.5" ry="0.5" style={style}>
x={left} y={top} width={width} height={height}
rx={radius} ry={radius} style={backgroundStyle}>
</rect>
<path d={taskPath.join(' ')} style={taskStyle} />
<a xlinkHref={host}>
<image
x={this.props.cx - width/2 + 0.5} y={this.props.cy - 0.4 * height}
x={leftCenter} y={this.props.cy - 0.4 * height}
width={0.8 * height} height={0.8 * height}
xlinkHref={logo}>
</image>
</a>
<a xlinkHref={this.props.href}>
<text
x={this.props.cx - width/2 + 0.5 + height}
x={leftCenter + height}
y={this.props.cy - height/2 + 1.5}>
{this.props.slug.replace(/^[^\/]*\//, '')}
</text>
</a>
<rect
x={left} y={top} width={width} height={height}
rx={radius} ry={radius} style={style}>
</rect>
<DepIndicators
cx={this.props.cx + width/2} cy={this.props.cy} dy={height/2}
cx={right} cy={this.props.cy} dy={height/2}
blockers={this.props.blockers}
dependencies={this.props.dependencies}
related={this.props.related}
Expand Down
1 change: 0 additions & 1 deletion webapp/src/DepCard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react';
import ReactDOM from 'react-dom';
import DepCard from './DepCard';


it('asana.com renders without crashing', () => {
const svg = document.createElement('svg');
ReactDOM.render(
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/DummyHost.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ class GetDummyHostNodes {
done: done(data.number),
dependencies: dependencies(data.number),
related: [],
tasks: Math.max(10, data.number),
tasksCompleted: data.number,
user: 'author' + data.number,
})
}
Expand Down
16 changes: 16 additions & 0 deletions webapp/src/GitHub.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ function nodeFromIssue(issue) {
var relatedKey = 'github.com/' + user + '/' + repo + '#' + number;
dependencies.push(relatedKey);
}
var tasks = 0;
var tasksCompleted = 0;
regexp = /^[^[]*\[([ x])].*$/gm;
for (;;) {
match = regexp.exec(issue.body);
if (match === null) {
break;
}
var check = match[1];
if (check === 'x') {
tasksCompleted += 1;
}
tasks += 1;
}
return new DepCard({
slug: key,
host: 'github.com',
Expand All @@ -74,6 +88,8 @@ function nodeFromIssue(issue) {
done: issue.state !== 'open',
dependencies: dependencies,
related: related,
tasks: tasks,
tasksCompleted: tasksCompleted,
user: issue.user.login,
});
}
Expand Down
21 changes: 21 additions & 0 deletions webapp/src/GitHub.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,27 @@ it('long and short references are understood', () => {
});
});

it('tasks are counted', () => {
var nodes = [];
function pushNodes(newNodes) {
for (var index in newNodes) {
if (true) {
nodes.push(newNodes[index]);
}
}
}
return new Promise(function (resolve, reject) {
GetGitHubNodes(
'github.com/jbenet/depviz#20', pushNodes
).then(function () {
expect(nodes.length).toBe(1);
expect(nodes[0].props.tasks).toBe(2);
expect(nodes[0].props.tasksCompleted).toBe(1);
resolve();
}).catch(reject);
});
});

it('repository keys are understood', () => {
var nodes = [];
function pushNodes(newNodes) {
Expand Down

0 comments on commit 25fadc4

Please sign in to comment.