seemingly working adding of errors and stats

This commit is contained in:
Radek Stepan 2014-10-20 19:51:15 -07:00
parent 5d6bed2759
commit 367f26e517
12 changed files with 317 additions and 178 deletions

View File

@ -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

View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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)
} }
] ]

25
src/modules/stats.coffee Normal file
View File

@ -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 }
}

View File

@ -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>

View File

@ -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>

View File

@ -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 '&nbsp;' unless jsonDate return '&nbsp;' 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

View File

@ -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

View File

@ -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

View File

@ -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.