major new feature: Resources

This commit is contained in:
Enrico Fasoli 2015-04-09 16:25:05 +02:00
parent 0ad54bee53
commit d1414f2532
7 changed files with 181 additions and 59 deletions

View File

@ -1,14 +1,17 @@
class PBS class PBS
constructor: (obj, @verbose, @errListener) -> constructor: (obj, @verbose, @errListener) ->
@days = [] @days = []
@resources = []
@criticalPaths = [] @criticalPaths = []
@verbose = yes
if obj.push # is a list if obj.push # is a list
@list = obj @list = obj
else if obj.activities else if obj.activities
@list = obj.activities @list = obj.activities
if obj.resources?.push? then @resources = obj.resources
else else
@list = [] @list = []
@err 'data is not an array nor a object with "activities" array' @err 'data is not an array nor a object with "activities" array'
log: (x...) -> log: (x...) ->
if @verbose if @verbose
@ -21,6 +24,24 @@ class PBS
else console.log "[ !Pert! ]", x... else console.log "[ !Pert! ]", x...
if @errListener?.call? then @errListener x if @errListener?.call? then @errListener x
compileResources: =>
@log 'compiling resources...'
if not @resources? then return
@resources.forEach (x) =>
@log 'processing resource', x
if x.assignedTo?.push? then x.assignedTo.forEach (i) =>
a = @toActivity i
a.assigned ?= []
a.assigned.push x.name or x.id
@list.forEach (x) =>
item = @toActivity x
if item.assigned?.push? then item.assigned.forEach (i) =>
res = @toResource i
if res
@log 'found', res, 'assigned to', item
res.assignedTo ?= []
res.assignedTo.push i
# Returns the highest number in an array of numbers # Returns the highest number in an array of numbers
maxa: (l) -> return Math.max.apply null, l maxa: (l) -> return Math.max.apply null, l
@ -32,6 +53,14 @@ class PBS
if x.id is id then item = x if x.id is id then item = x
return item return item
# Find the activity with given id
toResource: (id) =>
unless @resources?.push? then return
item = {}
for i,x of @resources
if x.id is id or x.name is id then item = x
return item
# Compute the item's end day # Compute the item's end day
calculateEndDay: (item) => calculateEndDay: (item) =>
if !item.startDay? if !item.startDay?
@ -83,11 +112,14 @@ class PBS
@log "calculating path from",path @log "calculating path from",path
lastID = path[path.length - 1] lastID = path[path.length - 1]
last = @toActivity lastID last = @toActivity lastID
if last.dependant? and last.dependant.length > 0 if last.permittedDelay > 0
@log "dead end at", lastID, "because its delay is", last.permittedDelay
else if last.dependant? and last.dependant.length > 0
last.dependant.forEach (x) => last.dependant.forEach (x) =>
ii = @toActivity x ii = @toActivity x
delay = ii.permittedDelay or 0 delay = ii.permittedDelay or 0
if delay is 0 if delay is 0
@log 'following path from', last.id, 'to', ii.id, 'because', ii, 'has', ii.permittedDelay, 'days of free delay'
@calculateCriticalPaths path.concat x @calculateCriticalPaths path.concat x
else else
@log "dead end at", lastID, "-->", x, "because delay is", delay @log "dead end at", lastID, "-->", x, "because delay is", delay
@ -124,13 +156,16 @@ class PBS
for x,i in @list for x,i in @list
if !x.depends? or x.depends.length is 0 if !x.depends? or x.depends.length is 0
@calculateCriticalPaths [x.id] @calculateCriticalPaths [x.id]
results = activities: @list, days: @days, criticalPaths: @criticalPaths @compileResources()
results =
activities: @list
days: @days
criticalPaths: @criticalPaths
resources: @resources || []
@log 'Done', results
if options?.json if options?.json
if cb? then cb(JSON.stringify results) if cb? then cb(JSON.stringify results)
JSON.stringify results JSON.stringify results
else else
if cb? then cb(results) if cb? then cb(results)
results results
# export module to node environment
if module?.exports? then module.exports = PBS

View File

@ -33,13 +33,18 @@ pertApp.config ($stateProvider,$urlRouterProvider,$locationProvider) ->
templateUrl: 'table.html' templateUrl: 'table.html'
controller: pertController controller: pertController
$stateProvider.state 'resources',
url: '/resources'
templateUrl: 'resources.html'
controller: pertController
# "Main" Controller # "Main" Controller
pertController = ($scope) -> pertController = ($scope) ->
$scope.toLocalStorage = (data,options) -> $scope.toLocalStorage = (data,options) ->
options ?= {} options ?= {}
data ?= [] data ?= []
if !data.push? if !data.push? and !data.activities?.push?
return swal 'Error', 'data is not a list', 'error' return swal 'Error', 'data is not a valid PBSlib object', 'error'
try try
sdata = JSON.stringify data sdata = JSON.stringify data
console.log "Saving: "+sdata console.log "Saving: "+sdata
@ -54,22 +59,17 @@ pertController = ($scope) ->
$scope.fromLocalStorage = (options) -> $scope.fromLocalStorage = (options) ->
options = options || {} options = options || {}
data = localStorage.getItem options.name || 'ganttpert' data = localStorage.getItem options.name || 'ganttpert'
if data is null then data = "[]" if data is null then data = '{"activities":[], "resources":[]}'
try try
jdata = JSON.parse data jdata = JSON.parse data
if jdata is null then jdata = [] if jdata is null then jdata = activities: [], resources: []
catch e catch e
unless options.silent unless options.silent
swal 'JSON Error', e, 'error' swal 'JSON Error', e, 'error'
if options.raw return activities: [], resources: []
#console.log 'Loading: []'
return []
else
#console.log 'Loading: {list: [], days: []}'
return list: [], days: []
if options.raw if options.raw
#console.log 'Loading: '+jdata #console.log 'Loading: '+jdata
return jdata return jdata
else else
#console.log 'Loading: '+$scope.pbs #console.log 'Loading: '+$scope.pbs
return $scope.pbs ?= new PBS(jdata).calculate() return $scope.pbs = new PBS(jdata).calculate()

View File

@ -1,12 +1,18 @@
pertApp.controller 'tableController', ($scope) -> tableController = ($scope,getList) ->
$scope.list = [] $scope.list = []
$scope.refreshTable = -> $scope.refreshTable = ->
ls = $scope.fromLocalStorage() data = $scope.fromLocalStorage()
if ls? if data?
$scope.list = ls.activities $scope.list = getList data
$scope.$on 'dataChanged', $scope.refreshTable $scope.$on 'dataChanged', $scope.refreshTable
$scope.refreshTable() $scope.refreshTable()
pertApp.controller 'tableController', ($scope) ->
tableController $scope, (data) -> data.activities or []
pertApp.controller 'resourceTableController', ($scope) ->
tableController $scope, (data) -> data.resources or []
pertApp.controller 'pertDiagController', ($scope) -> pertApp.controller 'pertDiagController', ($scope) ->
$scope.buildGraph = (data) -> $scope.buildGraph = (data) ->
if !data? then return if !data? then return
@ -31,7 +37,6 @@ pertApp.controller 'pertDiagController', ($scope) ->
style: 'arrow' style: 'arrow'
network = new vis.Network (document.getElementById 'pertDiagram'), { nodes: nodes, edges: connections }, options network = new vis.Network (document.getElementById 'pertDiagram'), { nodes: nodes, edges: connections }, options
$scope.rebuild = -> $scope.rebuild = ->
console.log 'rebuild'
$scope.buildGraph $scope.fromLocalStorage() $scope.buildGraph $scope.fromLocalStorage()
$scope.$on 'dataChanged', $scope.rebuild $scope.$on 'dataChanged', $scope.rebuild
$scope.rebuild() $scope.rebuild()
@ -52,7 +57,7 @@ pertApp.controller 'ganttDiagController', ($scope) ->
pertApp.controller 'rawEditorController', ($scope) -> pertApp.controller 'rawEditorController', ($scope) ->
$scope.reset = -> $scope.reset = ->
$scope.toLocalStorage [] $scope.toLocalStorage { activities: [], resources: [] }
$scope.saveData = -> $scope.saveData = ->
try try
data = JSON.parse $scope.taData data = JSON.parse $scope.taData
@ -61,46 +66,55 @@ pertApp.controller 'rawEditorController', ($scope) ->
$scope.toLocalStorage data $scope.toLocalStorage data
$scope.reloadData = -> $scope.reloadData = ->
$scope.taData = JSON.stringify $scope.fromLocalStorage silent: yes, raw: yes $scope.taData = JSON.stringify $scope.fromLocalStorage silent: yes, raw: yes
$scope.$on 'dataChanged', -> $scope.$on 'dataChanged', $scope.reloadData
$scope.reloadData()
#$('#ta').val JSON.stringify $scope.fromLocalStorage silent: yes, raw: yes
$scope.reloadData() $scope.reloadData()
pertApp.controller 'editorController', ($scope) -> pertApp.controller 'editorController', ($scope) ->
$scope.list = [] $scope.activities = []
$scope.clone = (id) -> $scope.resources = []
for i,j of $scope.fromLocalStorage({raw: yes, silent: yes}) $scope.actID = ''; $scope.actDur = ''; $scope.actDeps = ''
$scope.resID = ''; $scope.resName = ''; $scope.resAss = ''
$scope.clone = (isResource, id) ->
data = $scope.fromLocalStorage({raw: yes, silent: yes})
l = if isResource then data.resources else data.activities
for i,j of $scope.fromLocalStorage({raw: yes, silent: yes}).activities
if j.id is id if j.id is id
$scope.addNew j.id, j.duration, j.depends $scope.addNew j.id, j.duration, j.depends
swal 'Ok', id+' has been cloned', 'success' swal 'Ok', id+' has been cloned', 'success'
return return
swal 'Ops', 'could not find '+id, 'warning' swal 'Ops', 'could not find '+id, 'warning'
$scope.delete = (index,id) -> $scope.delete = (isResource, index,id) ->
newdata = $scope.fromLocalStorage raw: yes newdata = $scope.fromLocalStorage raw: yes
iter = if isResource then newdata.resources else newdata.activities
l = [] l = []
if id? then for i,j of newdata if id? then for i,j of iter
if id isnt j.id if id isnt j.id and id isnt j.name
l.push j l.push j
else for i,j of newdata else for i,j of iter
if parseInt(i) isnt index if parseInt(i) isnt index
l.push j l.push j
diff = newdata.length - l.length diff = iter.length - l.length
$scope.toLocalStorage l, silent: yes if isResource
newdata.resources = l
else newdata.activities = l
$scope.toLocalStorage newdata, silent: yes
if diff isnt 1 if diff isnt 1
swal 'Done', diff+' item(s) deleted', 'warning' swal 'Done', diff+' item(s) deleted', 'warning'
$scope.addNew = (id, dur, deps) -> $scope.addNew = (isResource, id, dur, deps) ->
dur ?= $('#new-duration').val().trim() dur ?= if isResource then $scope.resName else $scope.actDur
id ?= $('#new-id').val().trim() id ?= if isResource then $scope.resID else $scope.actID
if !deps? if !deps?
deps = $('#new-deps').val().split(' ') deps = if isResource then $scope.resAss else $scope.actDeps
deps = deps.split ' '
if deps.length is 1 and deps[0] is '' if deps.length is 1 and deps[0] is ''
deps = [] deps = []
try if !isResource
dur = parseInt dur try
catch e dur = parseInt dur
return swal 'Error', 'duration must be an integer', 'error' catch e
return swal 'Error', 'duration must be an integer', 'error'
try try
unless isNaN id unless isNaN id
id = parseInt id id = parseInt id
@ -110,13 +124,22 @@ pertApp.controller 'editorController', ($scope) ->
deps[i] = parseInt dep deps[i] = parseInt dep
catch e catch e
newdata = $scope.fromLocalStorage silent: yes, raw: yes newdata = $scope.fromLocalStorage silent: yes, raw: yes
if !newdata? or newdata is null or !newdata.push? if isResource
newdata = [] if newdata?.resources?.push?
newdata.push { id: id, duration: dur, depends: deps } console.log newdata.resources
$scope.toLocalStorage newdata, silent: yes newdata.resources.push { id: id, name: dur, assignedTo: deps }
$scope.toLocalStorage newdata, silent: yes
else console.log "wtf cant add, data broken"
else
if newdata?.activities?.push?
newdata.activities.push { id: id, duration: dur, depends: deps }
$scope.toLocalStorage newdata, silent: yes
else console.log "wtf cant add, data broken"
$scope.refreshEditor = -> $scope.refreshEditor = ->
data = $scope.fromLocalStorage { silent: yes, raw: yes } data = $scope.fromLocalStorage { silent: yes, raw: yes }
$scope.list = data || [] $scope.activities = data.activities || []
$scope.resources = data.resources || []
$scope.$on 'dataChanged', $scope.refreshEditor $scope.$on 'dataChanged', $scope.refreshEditor
$scope.refreshEditor() $scope.refreshEditor()

View File

@ -4,7 +4,7 @@
<p class="lead">Dependencies must be a list of IDs separated by space<br> <p class="lead">Dependencies must be a list of IDs separated by space<br>
the program does not check cyclic or invalid dependencies for you</p> the program does not check cyclic or invalid dependencies for you</p>
</div> </div>
<!-- new item --> <!-- new activity -->
<div class="panel panel-default new-activity-panel"> <div class="panel panel-default new-activity-panel">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-plus"></i> New Activity <i class="fa fa-plus"></i> New Activity
@ -12,21 +12,21 @@
<div class="panel-body"> <div class="panel-body">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon">ID</span> <span class="input-group-addon">ID</span>
<input class="form-control" placeholder="New Activity" ng-model="newid" id="new-id"> <input class="form-control" placeholder="New Activity" ng-model="actID" id="new-id">
</div> </div>
<div class="input-group duration"> <div class="input-group duration">
<span class="input-group-addon">Duration</span> <span class="input-group-addon">Duration</span>
<input class="form-control" id="new-duration" placeholder="(days)"> <input class="form-control" ng-model="actDur" placeholder="(days)">
</div> </div>
<div class="input-group dependencies"> <div class="input-group dependencies">
<span class="input-group-addon">Dependencies</span> <span class="input-group-addon">Dependencies</span>
<input class="form-control" placeholder="id1 id2 id3" id="new-deps"> <input class="form-control" placeholder="id1 id2 id3" ng-model="actDeps">
</div> </div>
<button ng-click="addNew()" class="btn btn-primary"> <button ng-click="addNew(false)" class="btn btn-primary">
<i class="fa fa-plus"></i> Add</button> <i class="fa fa-plus"></i> Add</button>
</div> </div>
</div> </div>
<!-- item list --> <!-- activity list -->
<table id="tab" class="table"> <table id="tab" class="table">
<tr> <tr>
<td><b>#</b></td> <td><b>#</b></td>
@ -35,11 +35,11 @@
<td><b>Dependencies</b></td> <td><b>Dependencies</b></td>
<td><b>Commands</b></td> <td><b>Commands</b></td>
</tr> </tr>
<tr ng-repeat="item in list"> <tr ng-repeat="item in activities">
<td>{{$index + 1}}</td> <td>{{$index + 1}}</td>
<td>{{item.id}}</td> <td>{{item.id}}</td>
<td>{{item.duration}}</td> <td>{{item.duration}}</td>
<td>{{item.depends || []}}</td> <td>{{item.depends.join(', ') || "None"}}</td>
<td> <td>
<button class="btn btn-info" ng-click="clone(item.id)"> <button class="btn btn-info" ng-click="clone(item.id)">
<i class="fa fa-copy"></i> <i class="fa fa-copy"></i>
@ -50,4 +50,51 @@
</td> </td>
</tr> </tr>
</table> </table>
<hr>
<!-- new resource -->
<div class="panel panel-default new-activity-panel">
<div class="panel-heading">
<i class="fa fa-plus"></i> New Resource
</div>
<div class="panel-body">
<div class="input-group">
<span class="input-group-addon">ID</span>
<input class="form-control" placeholder="New Resource" ng-model="resID">
</div>
<div class="input-group duration">
<span class="input-group-addon">Name</span>
<input class="form-control" ng-model="resName" placeholder="new resource name">
</div>
<div class="input-group dependencies">
<span class="input-group-addon">Assigned To</span>
<input class="form-control" placeholder="id1 id2 id3" ng-model="resAss">
</div>
<button ng-click="addNew(true)" class="btn btn-primary">
<i class="fa fa-plus"></i> Add</button>
</div>
</div>
<!-- resource list -->
<table id="tab" class="table">
<tr>
<td><b>#</b></td>
<td><b>ID</b></td>
<td><b>Name</b></td>
<td><b>Assignations</b></td>
<td><b>Commands</b></td>
</tr>
<tr ng-repeat="item in resources">
<td>{{$index + 1}}</td>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.assignedTo || []}}</td>
<td>
<button class="btn btn-info" ng-click="clone(true,item.id)">
<i class="fa fa-copy"></i>
</button>
<button class="btn btn-danger" ng-click="delete(true,$index)">
<i class="fa fa-remove"></i>
</button>
</td>
</tr>
</table>
</div> </div>

View File

@ -37,7 +37,8 @@
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a ui-sref="edit">Edit</a></li> <li><a ui-sref="edit">Edit</a></li>
<li><a ui-sref="rawedit">RawEdit</a></li> <li><a ui-sref="rawedit">RawEdit</a></li>
<li><a ui-sref="table">Table</a></li> <li><a ui-sref="table">Activities</a></li>
<li><a ui-sref="resources">Resources</a></li>
<li><a ui-sref="pert">Pert</a></li> <li><a ui-sref="pert">Pert</a></li>
<li><a ui-sref="gantt">Gantt</a></li> <li><a ui-sref="gantt">Gantt</a></li>
<li class="github"><a href="http://github.com/fazo96/pbs">GitHub</a></li> <li class="github"><a href="http://github.com/fazo96/pbs">GitHub</a></li>

14
src/resources.html Normal file
View File

@ -0,0 +1,14 @@
<table id="tab" class="table" ng-controller="resourceTableController">
<tr>
<td><b>#</b></td>
<td><b>ID</b></td>
<td><b>Name</b></td>
<td><b>Assigned To</b></td>
</tr>
<tr ng-repeat="item in list">
<td>{{$index + 1}}</td>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.assignedTo.join(', ') || "None"}}</td>
</tr>
</table>

View File

@ -6,6 +6,7 @@
<td><b>Start Day</b></td> <td><b>Start Day</b></td>
<td><b>Depends On</b></td> <td><b>Depends On</b></td>
<td><b>Dependant</b></td> <td><b>Dependant</b></td>
<td><b>Resources</b></td>
<td><b>End Day</b></td> <td><b>End Day</b></td>
<td><b>Free Delay</b></td> <td><b>Free Delay</b></td>
<td><b>Chained Delay</b></td> <td><b>Chained Delay</b></td>
@ -15,8 +16,9 @@
<td>{{item.id}}</td> <td>{{item.id}}</td>
<td>{{item.duration}}</td> <td>{{item.duration}}</td>
<td>{{item.startDay}}</td> <td>{{item.startDay}}</td>
<td>{{item.depends || "N/A"}}</td> <td>{{item.depends.join(', ') || "None"}}</td>
<td>{{item.dependant || "N/A"}}</td> <td>{{item.dependant.join(', ') || "None"}}</td>
<td>{{item.assigned.join(', ') || "None"}}</td>
<td>{{item.endDay}} --- {{item.endDay + (item.permittedDelay || 0) + (item.chainedDelay || 0)}}</td> <td>{{item.endDay}} --- {{item.endDay + (item.permittedDelay || 0) + (item.chainedDelay || 0)}}</td>
<td>{{item.permittedDelay || 0}}</td> <td>{{item.permittedDelay || 0}}</td>
<td>{{item.chainedDelay || 0}}</td> <td>{{item.chainedDelay || 0}}</td>