seemingly working adding of errors and stats
This commit is contained in:
parent
5d6bed2759
commit
367f26e517
4
Makefile
4
Makefile
|
@ -6,10 +6,6 @@ build:
|
||||||
./node_modules/.bin/browserify -e ./src/app.coffee -o public/js/app.js -d
|
./node_modules/.bin/browserify -e ./src/app.coffee -o public/js/app.js -d
|
||||||
grunt
|
grunt
|
||||||
|
|
||||||
watch:
|
|
||||||
grunt
|
|
||||||
grunt watchify
|
|
||||||
|
|
||||||
serve:
|
serve:
|
||||||
cd public; python -m SimpleHTTPServer 8000
|
cd public; python -m SimpleHTTPServer 8000
|
||||||
|
|
||||||
|
|
2
TODO.md
2
TODO.md
|
@ -3,7 +3,7 @@
|
||||||
##Release: Assembly
|
##Release: Assembly
|
||||||
|
|
||||||
- [ ] use Browserify as an app build pipeline
|
- [ ] use Browserify as an app build pipeline
|
||||||
- [ ] sort milestones on index and project page based on priority (most delayed first); Trend - actual = different in days and those overdue come first
|
- [ ] sort milestones on index and project page based on priority (most delayed first); Trend - actual = difference in days and those overdue come first
|
||||||
|
|
||||||
1. in a projects collection observe the list prop and resort index; already sorted flag passed in as yes)
|
1. in a projects collection observe the list prop and resort index; already sorted flag passed in as yes)
|
||||||
1. The index is not already sorted when sort order changes
|
1. The index is not already sorted when sort order changes
|
||||||
|
|
322
public/js/app.js
322
public/js/app.js
File diff suppressed because one or more lines are too long
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
config = require '../models/config.coffee'
|
config = require '../models/config.coffee'
|
||||||
mediator = require '../modules/mediator.coffee'
|
mediator = require '../modules/mediator.coffee'
|
||||||
|
stats = require '../modules/stats.coffee'
|
||||||
Model = require '../utils/model.coffee'
|
Model = require '../utils/model.coffee'
|
||||||
date = require '../utils/date.coffee'
|
date = require '../utils/date.coffee'
|
||||||
search = require '../utils/search.coffee'
|
search = require '../utils/search.coffee'
|
||||||
|
@ -12,7 +13,7 @@ module.exports = new Model
|
||||||
'name': 'models/projects'
|
'name': 'models/projects'
|
||||||
|
|
||||||
'data':
|
'data':
|
||||||
'sortKey': 'priority'
|
'sortBy': 'priority'
|
||||||
|
|
||||||
find: (project) ->
|
find: (project) ->
|
||||||
_.find @data.list, project
|
_.find @data.list, project
|
||||||
|
@ -24,15 +25,34 @@ module.exports = new Model
|
||||||
add: (project) ->
|
add: (project) ->
|
||||||
@push 'list', project unless @exists project
|
@push 'list', project unless @exists project
|
||||||
|
|
||||||
|
# Find index of a project.
|
||||||
|
findIndex: ({ owner, name }) ->
|
||||||
|
_.findIndex @data.list, { owner, name }
|
||||||
|
|
||||||
addMilestone: (project, milestone) ->
|
addMilestone: (project, milestone) ->
|
||||||
if (idx = _.findIndex(@data.list, project)) > -1
|
# Add in the stats.
|
||||||
|
_.extend milestone, { 'stats': stats(milestone) }
|
||||||
|
|
||||||
|
if (idx = @findIndex(project)) > -1
|
||||||
if project.milestones?
|
if project.milestones?
|
||||||
@push "list.#{idx}.milestones", milestone
|
@push "list.#{idx}.milestones", milestone
|
||||||
else
|
else
|
||||||
@set "list.#{idx}.milestones", [ milestone ]
|
@set "list.#{idx}.milestones", [ milestone ]
|
||||||
else
|
else
|
||||||
|
# We are supposed to exist already.
|
||||||
throw 500
|
throw 500
|
||||||
|
|
||||||
|
# Save an error from loading milestones or issues
|
||||||
|
saveError: (project, err) ->
|
||||||
|
if (idx = @findIndex(project)) > -1
|
||||||
|
if project.errors?
|
||||||
|
@push "list.#{idx}.errors", err
|
||||||
|
else
|
||||||
|
@set "list.#{idx}.errors", [ err ]
|
||||||
|
else
|
||||||
|
# We are supposed to exist already.
|
||||||
|
throw 500
|
||||||
|
|
||||||
clear: ->
|
clear: ->
|
||||||
@set 'list', []
|
@set 'list', []
|
||||||
|
|
||||||
|
@ -42,9 +62,10 @@ module.exports = new Model
|
||||||
index = @data.index or []
|
index = @data.index or []
|
||||||
|
|
||||||
for p in @data.list
|
for p in @data.list
|
||||||
|
continue unless p.milestones?
|
||||||
for m in p.milestones
|
for m in p.milestones
|
||||||
# Run a comparator here inserting into index.
|
# Run a comparator here inserting into index.
|
||||||
@data.sortKey
|
console.log @data.sortBy, m
|
||||||
|
|
||||||
# Save the index.
|
# Save the index.
|
||||||
@set 'index', index
|
@set 'index', index
|
||||||
|
@ -67,6 +88,6 @@ module.exports = new Model
|
||||||
# Reset our index and re-sort.
|
# Reset our index and re-sort.
|
||||||
@observe 'sortKey', ->
|
@observe 'sortKey', ->
|
||||||
# Use pop as Ractive is glitchy.
|
# Use pop as Ractive is glitchy.
|
||||||
@pop 'index' while @data.index.length
|
( @pop 'index' while @data.index.length ) if @data.index?
|
||||||
# Run the sort again.
|
# Run the sort again.
|
||||||
do @sort
|
do @sort
|
|
@ -116,10 +116,10 @@ module.exports =
|
||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
date: created_at
|
'date': created_at
|
||||||
points: fn(a)
|
'points': fn(a)
|
||||||
}, {
|
}, {
|
||||||
date: due_on
|
'date': due_on
|
||||||
points: fn(b)
|
'points': fn(b)
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Progress in %.
|
||||||
|
progress = (a, b) -> 100 * (a / (b + a))
|
||||||
|
|
||||||
|
# Calculate the stats for a milestone.
|
||||||
|
# Is it on time? What is the progress?
|
||||||
|
module.exports = (milestone) ->
|
||||||
|
# Progress in points.
|
||||||
|
points = progress milestone.issues.closed.size, milestone.issues.open.size
|
||||||
|
|
||||||
|
# Milestones with no due date are always on track.
|
||||||
|
return { 'isOnTime': yes, 'progress': { points } } unless milestone.due_on
|
||||||
|
|
||||||
|
a = +new Date milestone.created_at
|
||||||
|
b = +new Date
|
||||||
|
c = +new Date milestone.due_on
|
||||||
|
|
||||||
|
# Progress in time.
|
||||||
|
time = progress b - a, c - b
|
||||||
|
|
||||||
|
isOnTime = points > time
|
||||||
|
|
||||||
|
{
|
||||||
|
'isOnTime': isOnTime
|
||||||
|
'progress': { points, time }
|
||||||
|
}
|
|
@ -12,10 +12,10 @@
|
||||||
</td>
|
</td>
|
||||||
<td style="width:1%">
|
<td style="width:1%">
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<span class="percent">{{Math.floor(format.progress(issues.closed.size, issues.open.size))}}%</span>
|
<span class="percent">{{Math.floor(stats.progress.points)}}%</span>
|
||||||
<span class="due">{{{ format.due(due_on) }}}</span>
|
<span class="due">{{{ format.due(due_on) }}}</span>
|
||||||
<div class="outer bar">
|
<div class="outer bar">
|
||||||
<div class="inner bar {{format.onTime(number, due_on, created_at, issues.closed.size, issues.open.size)}}" style="width:{{format.progress(issues.closed.size, issues.open.size)}}%"></div>
|
<div class="inner bar {{(stats.isOnTime) ? 'green' : 'red'}}" style="width:{{stats.progress.points}}%"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
{{#projects.list}}
|
{{#projects.list}}
|
||||||
{{#if error}}
|
{{#if errors}}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="3" class="repo">
|
<td colspan="3" class="repo">
|
||||||
<div class="project">{{owner}}/{{name}} <span class="error" title="{{error}}"><Icons icon="attention"/></span></div>
|
<div class="project">{{owner}}/{{name}} <span class="error" title="{{errors.join('\n')}}"><Icons icon="attention"/></span></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -23,10 +23,10 @@
|
||||||
</td>
|
</td>
|
||||||
<td style="width:1%">
|
<td style="width:1%">
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<span class="percent">{{Math.floor(format.progress(issues.closed.size, issues.open.size))}}%</span>
|
<span class="percent">{{Math.floor(stats.progress.points)}}%</span>
|
||||||
<span class="due">{{{ format.due(due_on) }}}</span>
|
<span class="due">{{{ format.due(due_on) }}}</span>
|
||||||
<div class="outer bar">
|
<div class="outer bar">
|
||||||
<div class="inner bar {{format.onTime(number, due_on, created_at, issues.closed.size, issues.open.size)}}" style="width:{{format.progress(issues.closed.size, issues.open.size)}}%"></div>
|
<div class="inner bar {{(stats.isOnTime) ? 'green' : 'red'}}" style="width:{{stats.progress.points}}%"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -2,48 +2,26 @@
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
|
|
||||||
# Progress in percentages.
|
|
||||||
'progress': _.memoize (a, b) ->
|
|
||||||
100 * (a / (b + a))
|
|
||||||
|
|
||||||
# Is a milestone on time?
|
|
||||||
'onTime': _.memoize (number, due_on, created_at, closed_size, open_size) ->
|
|
||||||
# Milestones with no due date are always on track.
|
|
||||||
return 'green' unless due_on
|
|
||||||
|
|
||||||
# Calculate the progress in days.
|
|
||||||
a = +new Date created_at
|
|
||||||
b = +new Date
|
|
||||||
c = +new Date due_on
|
|
||||||
|
|
||||||
# Progress in time.
|
|
||||||
time = @progress b - a, c - b
|
|
||||||
|
|
||||||
# Progress in size.
|
|
||||||
[ 'red', 'green' ][ +(@progress(closed_size, open_size) > time) ]
|
|
||||||
, (args...) -> # resolver
|
|
||||||
args.join '/'
|
|
||||||
|
|
||||||
# Time from now.
|
# Time from now.
|
||||||
'fromNow': _.memoize (jsonDate) ->
|
fromNow: _.memoize (jsonDate) ->
|
||||||
moment(new Date(jsonDate)).fromNow()
|
moment(new Date(jsonDate)).fromNow()
|
||||||
|
|
||||||
# When is a milestone due?
|
# When is a milestone due?
|
||||||
'due': (jsonDate) ->
|
due: (jsonDate) ->
|
||||||
return ' ' unless jsonDate
|
return ' ' unless jsonDate
|
||||||
[ 'due', @fromNow jsonDate ].join(' ')
|
[ 'due', @fromNow jsonDate ].join(' ')
|
||||||
|
|
||||||
# Markdown formatting.
|
# Markdown formatting.
|
||||||
'markdown': (markup) ->
|
markdown: (markup) ->
|
||||||
marked markup
|
marked markup
|
||||||
|
|
||||||
# Format milestone title.
|
# Format milestone title.
|
||||||
'title': (text) ->
|
title: (text) ->
|
||||||
if text.toLowerCase().indexOf('milestone') > -1
|
if text.toLowerCase().indexOf('milestone') > -1
|
||||||
text
|
text
|
||||||
else
|
else
|
||||||
[ 'Milestone', text ].join(' ')
|
[ 'Milestone', text ].join(' ')
|
||||||
|
|
||||||
# Hex to decimal.
|
# Hex to decimal.
|
||||||
hexToDecimal: (hex) ->
|
hexToDec: (hex) ->
|
||||||
parseInt hex, 16
|
parseInt hex, 16
|
|
@ -29,6 +29,6 @@ module.exports = Ractive.extend
|
||||||
onrender: ->
|
onrender: ->
|
||||||
@observe 'icon', (icon) ->
|
@observe 'icon', (icon) ->
|
||||||
if icon and hex = codes[icon]
|
if icon and hex = codes[icon]
|
||||||
@set 'code', format.hexToDecimal hex
|
@set 'code', format.hexToDec hex
|
||||||
else
|
else
|
||||||
@set 'code', null
|
@set 'code', null
|
|
@ -9,7 +9,6 @@ milestones = require '../../modules/github/milestones.coffee'
|
||||||
issues = require '../../modules/github/issues.coffee'
|
issues = require '../../modules/github/issues.coffee'
|
||||||
mediator = require '../../modules/mediator.coffee'
|
mediator = require '../../modules/mediator.coffee'
|
||||||
|
|
||||||
|
|
||||||
module.exports = Ractive.extend
|
module.exports = Ractive.extend
|
||||||
|
|
||||||
'name': 'views/pages/index'
|
'name': 'views/pages/index'
|
||||||
|
@ -32,28 +31,41 @@ module.exports = Ractive.extend
|
||||||
|
|
||||||
done = do system.async
|
done = do system.async
|
||||||
|
|
||||||
|
# For all projects.
|
||||||
async.map projects.data.list, (project, cb) ->
|
async.map projects.data.list, (project, cb) ->
|
||||||
# Skip if we have milestones already.
|
# Fetch their milestones.
|
||||||
return cb null, project if project.milestones
|
milestones.fetchAll project, (err, list) ->
|
||||||
# Otherwise fetch them.
|
|
||||||
milestones.fetchAll project, (error, list) ->
|
|
||||||
# Save the error if project does not exist.
|
# Save the error if project does not exist.
|
||||||
return cb null, _.extend project, { error } if error
|
if err
|
||||||
# Now map in the issues.
|
projects.saveError project, err
|
||||||
async.map list, (milestone, cb) ->
|
return do cb
|
||||||
issues.fetchAll _.extend(project, { 'milestone': milestone.number }), (err, obj) ->
|
|
||||||
cb err, _.extend milestone, { 'issues': obj }
|
|
||||||
, (error, list) ->
|
|
||||||
delete project.milestone # from fetchAll or do deep clone
|
|
||||||
# Save any errors.
|
|
||||||
return cb null, _.extend project, { error } if error
|
|
||||||
# Otherwise add the milestones.
|
|
||||||
cb null, _.extend project, { 'milestones': list }
|
|
||||||
|
|
||||||
, (err, data) =>
|
# Now add in the issues.
|
||||||
# TODO: Errors are saved on projects. Show them as a notification here too.
|
async.each list, (milestone, cb) ->
|
||||||
# Save the projects.
|
# Do we have this milestone already?
|
||||||
|
return cb null if _.find project.milestones, ({ number }) ->
|
||||||
|
milestone.number is number
|
||||||
|
|
||||||
|
# OK fetch all the issues for this milestone then.
|
||||||
|
issues.fetchAll
|
||||||
|
'owner': project.owner
|
||||||
|
'name': project.name
|
||||||
|
'milestone': milestone.number
|
||||||
|
, (err, obj) ->
|
||||||
|
# Save any errors on the project.
|
||||||
|
if err
|
||||||
|
projects.saveError project, err
|
||||||
|
return do cb
|
||||||
|
|
||||||
|
# Add in the issues to the milestone.
|
||||||
|
_.extend milestone, { 'issues': obj }
|
||||||
|
# Save the milestone.
|
||||||
|
projects.addMilestone project, milestone
|
||||||
|
# Done
|
||||||
|
do cb
|
||||||
|
|
||||||
|
, cb
|
||||||
|
|
||||||
|
, =>
|
||||||
do done
|
do done
|
||||||
@set
|
@set 'ready', yes
|
||||||
'projects.list': data
|
|
||||||
'ready': yes
|
|
|
@ -48,7 +48,8 @@ module.exports = Ractive.extend
|
||||||
return cb err if err
|
return cb err if err
|
||||||
# Save the milestone with issues.
|
# Save the milestone with issues.
|
||||||
projects.addMilestone project, _.extend milestone, { 'issues': obj }
|
projects.addMilestone project, _.extend milestone, { 'issues': obj }
|
||||||
cb null
|
# Next.
|
||||||
|
do cb
|
||||||
, cb
|
, cb
|
||||||
|
|
||||||
# Run it.
|
# Run it.
|
||||||
|
|
Loading…
Reference in New Issue