yaml config

This commit is contained in:
Radek Stepan 2012-05-23 16:17:39 +01:00
parent 23d0cb4001
commit afec4bd24f
7 changed files with 96 additions and 33 deletions

33
README.md Normal file
View File

@ -0,0 +1,33 @@
Server for connect to GitHub Issues API and displaying a burndown chart for a current milestone.
## Requirements:
You can install all the following dependencies by running:
```bash
npm install -d
```
- [CoffeeScript](http://coffeescript.org/)
- [express](http://expressjs.com/)
- [eco](https://github.com/sstephenson/eco)
- [js-yaml](https://github.com/visionmedia/js-yaml)
## Configure:
The app is configured by pointing to a public GitHub user/project. Do so in `config.yml`:
```yaml
github_user: ̈́'intermine'
github_project: 'InterMine'
project_name: 'Core InterMine Project'
```
## Use:
1. Start a node server using `.webserver.sh`.
2. Visit [http://0.0.0.0:3000/](http://0.0.0.0:3000/)
## Example:
![image](https://raw.github.com/radekstepan/github-burndown-chart/master/example.png)

View File

@ -2,21 +2,41 @@ express = require 'express'
eco = require 'eco' eco = require 'eco'
https = require 'https' https = require 'https'
fs = require "fs" fs = require "fs"
yaml = require "js-yaml"
# Make HTTPS GET to GitHub API v3. # Helper object for GitHub Issues.
apiGet = (path, type, callback) -> Issues =
options =
host: "api.github.com"
method: "GET"
path: path
https.request(options, (response) -> # Make HTTPS GET to GitHub API v3.
if response.statusCode is 200 get: (path, type, callback) ->
json = "" options =
response.on "data", (chunk) -> json += chunk host: "api.github.com"
method: "GET"
response.on "end", -> callback JSON.parse(json), type path: path
).end()
https.request(options, (response) ->
if response.statusCode is 200
json = ""
response.on "data", (chunk) -> json += chunk
response.on "end", -> callback JSON.parse(json), type
).end()
# URLs to API.
getOpenIssues: (callback) -> Issues.get "/repos/#{Issues.config.github_user}/#{Issues.config.github_project}/issues?state=open", 'issues', callback
getClosedIssues: (callback) -> Issues.get "/repos/#{Issues.config.github_user}/#{Issues.config.github_project}/issues?state=closed", 'issues', callback
getMilestones: (callback) -> Issues.get "/repos/#{Issues.config.github_user}/#{Issues.config.github_project}/milestones", 'milestones', callback
# Convert GitHub ISO to JS ISO date and then time
dateToTime: (date) -> new Date(date[0...date.length - 1] + '.000' + date.charAt date.length-1).getTime()
# Format issues for display in a listing.
format: (issue) ->
# Format the timestamps.
if issue.created_at? then issue.created_at = new Date(Issues.dateToTime(issue.created_at)).toUTCString()
if issue.updated_at? then issue.updated_at = new Date(Issues.dateToTime(issue.updated_at)).toUTCString()
issue
# Express. # Express.
app = express.createServer() app = express.createServer()
@ -55,10 +75,7 @@ app.get '/burndown', (req, res) ->
when 'milestones' then store.milestones = store.milestones.concat data when 'milestones' then store.milestones = store.milestones.concat data
# Are we done? # Are we done?
if resources is 0 if resources is 0
# Convert GitHub ISO to JS ISO date and then time
dateToTime = (date) -> new Date(date[0...date.length - 1] + '.000' + date.charAt date.length-1).getTime()
# Store the current milestone and its size. # Store the current milestone and its size.
current = { 'milestone': {}, 'diff': +Infinity, 'size': 0 } current = { 'milestone': {}, 'diff': +Infinity, 'size': 0 }
@ -67,7 +84,7 @@ app.get '/burndown', (req, res) ->
for milestone in store.milestones for milestone in store.milestones
due = milestone['due_on'] due = milestone['due_on']
# JS expects more accuracy. # JS expects more accuracy.
due = dateToTime due due = Issues.dateToTime due
# Is this the 'current' one? # Is this the 'current' one?
diff = due - now diff = due - now
if diff > 0 and diff < current.diff if diff > 0 and diff < current.diff
@ -75,7 +92,7 @@ app.get '/burndown', (req, res) ->
# Create n dict with all dates in the milestone span. # Create n dict with all dates in the milestone span.
days = {} ; totalDays = 0 days = {} ; totalDays = 0
day = dateToTime current.milestone.created_at # TODO: shift this to the start of the day and deal with time shifts. day = Issues.dateToTime current.milestone.created_at # TODO: shift this to the start of the day and deal with time shifts.
while day < current.due while day < current.due
# Save the day. # Save the day.
days[day] = { 'issue': {}, 'actual': 0, 'ideal': 0 } days[day] = { 'issue': {}, 'actual': 0, 'ideal': 0 }
@ -100,7 +117,7 @@ app.get '/burndown', (req, res) ->
current.size += issue.size current.size += issue.size
# Is it closed? # Is it closed?
if issue.closed_at? if issue.closed_at?
closed = dateToTime issue.closed_at closed = Issues.dateToTime issue.closed_at
# Find when was it closed (will be made faster) # Find when was it closed (will be made faster)
day = do () -> day = do () ->
for day, x of days for day, x of days
@ -124,22 +141,31 @@ app.get '/burndown', (req, res) ->
# Finally send to client. # Finally send to client.
res.render 'burndown', res.render 'burndown',
'days': days 'days': days
'project': Issues.config.project_name
, (html) -> res.send html, 'Content-Type': 'text/html', 200 , (html) -> res.send html, 'Content-Type': 'text/html', 200
# Get Milestones, Opened and Closed Tickets. # Get Milestones, Opened and Closed Tickets.
apiGet "/repos/intermine/InterMine/milestones", 'milestones', done Issues.getMilestones done
apiGet "/repos/intermine/InterMine/issues?state=open", 'issues', done Issues.getOpenIssues done
apiGet "/repos/intermine/InterMine/issues?state=closed", 'issues', done Issues.getClosedIssues done
# Show open issues. # Show open issues.
app.get '/issues', (req, res) -> app.get '/issues', (req, res) ->
apiGet "/repos/intermine/InterMine/issues?state=open", 'issues', (issues) -> Issues.getOpenIssues (issues) ->
# Vanilla render.
# Replace the dates in issues with nice dates.
issues = ( Issues.format(issue) for issue in issues )
res.render 'issues', res.render 'issues',
'issues': issues 'issues': issues
'project': Issues.config.project_name
, (html) -> res.send html, 'Content-Type': 'text/html', 200 , (html) -> res.send html, 'Content-Type': 'text/html', 200
app.listen 3000 # Fetch config and start server.
console.log "Express server listening to port 3000" fs.readFile "config.yml", "utf8", (err, data) ->
Issues.config = yaml.load data
app.listen 3000
console.log "Express server listening to port 3000"

3
config.yml Normal file
View File

@ -0,0 +1,3 @@
github_user: 'intermine'
github_project: 'InterMine'
project_name: 'Core InterMine Project'

BIN
example.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

View File

@ -3,7 +3,8 @@
, "version": "0.0.0" , "version": "0.0.0"
, "private": true , "private": true
, "dependencies": { , "dependencies": {
"express": ">= 3.0", "express": ">= 3.0",
"eco": "latest" "eco": "latest",
"js-yaml": "latest"
} }
} }

View File

@ -22,7 +22,7 @@
<li><a><i class="icon-white icon-fire"></i> Burndown App</a></li> <li><a><i class="icon-white icon-fire"></i> Burndown App</a></li>
</ul> </ul>
<ul class="nav pull-right"> <ul class="nav pull-right">
<li><a><i class="icon-white icon-book"></i> Core InterMine Project</a></li> <li><a><i class="icon-white icon-book"></i> <%= @project %></a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -17,7 +17,7 @@
<li><a><i class="icon-white icon-fire"></i> Burndown App</a></li> <li><a><i class="icon-white icon-fire"></i> Burndown App</a></li>
</ul> </ul>
<ul class="nav pull-right"> <ul class="nav pull-right">
<li><a><i class="icon-white icon-book"></i> Core InterMine Project</a></li> <li><a><i class="icon-white icon-book"></i> <%= @project %></a></li>
</ul> </ul>
</div> </div>
</div> </div>