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
constructor: (obj, @verbose, @errListener) ->
@days = []
@resources = []
@criticalPaths = []
@verbose = yes
if obj.push # is a list
@list = obj
else if obj.activities
@list = obj.activities
if obj.resources?.push? then @resources = obj.resources
else
@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...) ->
if @verbose
@ -21,6 +24,24 @@ class PBS
else console.log "[ !Pert! ]", 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
maxa: (l) -> return Math.max.apply null, l
@ -32,6 +53,14 @@ class PBS
if x.id is id then item = x
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
calculateEndDay: (item) =>
if !item.startDay?
@ -83,11 +112,14 @@ class PBS
@log "calculating path from",path
lastID = path[path.length - 1]
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) =>
ii = @toActivity x
delay = ii.permittedDelay or 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
else
@log "dead end at", lastID, "-->", x, "because delay is", delay
@ -124,13 +156,16 @@ class PBS
for x,i in @list
if !x.depends? or x.depends.length is 0
@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 cb? then cb(JSON.stringify results)
JSON.stringify results
else
if cb? then cb(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'
controller: pertController
$stateProvider.state 'resources',
url: '/resources'
templateUrl: 'resources.html'
controller: pertController
# "Main" Controller
pertController = ($scope) ->
$scope.toLocalStorage = (data,options) ->
options ?= {}
data ?= []
if !data.push?
return swal 'Error', 'data is not a list', 'error'
if !data.push? and !data.activities?.push?
return swal 'Error', 'data is not a valid PBSlib object', 'error'
try
sdata = JSON.stringify data
console.log "Saving: "+sdata
@ -54,22 +59,17 @@ pertController = ($scope) ->
$scope.fromLocalStorage = (options) ->
options = options || {}
data = localStorage.getItem options.name || 'ganttpert'
if data is null then data = "[]"
if data is null then data = '{"activities":[], "resources":[]}'
try
jdata = JSON.parse data
if jdata is null then jdata = []
if jdata is null then jdata = activities: [], resources: []
catch e
unless options.silent
swal 'JSON Error', e, 'error'
if options.raw
#console.log 'Loading: []'
return []
else
#console.log 'Loading: {list: [], days: []}'
return list: [], days: []
return activities: [], resources: []
if options.raw
#console.log 'Loading: '+jdata
return jdata
else
#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.refreshTable = ->
ls = $scope.fromLocalStorage()
if ls?
$scope.list = ls.activities
data = $scope.fromLocalStorage()
if data?
$scope.list = getList data
$scope.$on 'dataChanged', $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) ->
$scope.buildGraph = (data) ->
if !data? then return
@ -31,7 +37,6 @@ pertApp.controller 'pertDiagController', ($scope) ->
style: 'arrow'
network = new vis.Network (document.getElementById 'pertDiagram'), { nodes: nodes, edges: connections }, options
$scope.rebuild = ->
console.log 'rebuild'
$scope.buildGraph $scope.fromLocalStorage()
$scope.$on 'dataChanged', $scope.rebuild
$scope.rebuild()
@ -52,7 +57,7 @@ pertApp.controller 'ganttDiagController', ($scope) ->
pertApp.controller 'rawEditorController', ($scope) ->
$scope.reset = ->
$scope.toLocalStorage []
$scope.toLocalStorage { activities: [], resources: [] }
$scope.saveData = ->
try
data = JSON.parse $scope.taData
@ -61,46 +66,55 @@ pertApp.controller 'rawEditorController', ($scope) ->
$scope.toLocalStorage data
$scope.reloadData = ->
$scope.taData = JSON.stringify $scope.fromLocalStorage silent: yes, raw: yes
$scope.$on 'dataChanged', ->
$scope.reloadData()
#$('#ta').val JSON.stringify $scope.fromLocalStorage silent: yes, raw: yes
$scope.$on 'dataChanged', $scope.reloadData
$scope.reloadData()
pertApp.controller 'editorController', ($scope) ->
$scope.list = []
$scope.clone = (id) ->
for i,j of $scope.fromLocalStorage({raw: yes, silent: yes})
$scope.activities = []
$scope.resources = []
$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
$scope.addNew j.id, j.duration, j.depends
swal 'Ok', id+' has been cloned', 'success'
return
swal 'Ops', 'could not find '+id, 'warning'
$scope.delete = (index,id) ->
$scope.delete = (isResource, index,id) ->
newdata = $scope.fromLocalStorage raw: yes
iter = if isResource then newdata.resources else newdata.activities
l = []
if id? then for i,j of newdata
if id isnt j.id
if id? then for i,j of iter
if id isnt j.id and id isnt j.name
l.push j
else for i,j of newdata
else for i,j of iter
if parseInt(i) isnt index
l.push j
diff = newdata.length - l.length
$scope.toLocalStorage l, silent: yes
diff = iter.length - l.length
if isResource
newdata.resources = l
else newdata.activities = l
$scope.toLocalStorage newdata, silent: yes
if diff isnt 1
swal 'Done', diff+' item(s) deleted', 'warning'
$scope.addNew = (id, dur, deps) ->
dur ?= $('#new-duration').val().trim()
id ?= $('#new-id').val().trim()
$scope.addNew = (isResource, id, dur, deps) ->
dur ?= if isResource then $scope.resName else $scope.actDur
id ?= if isResource then $scope.resID else $scope.actID
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 ''
deps = []
try
dur = parseInt dur
catch e
return swal 'Error', 'duration must be an integer', 'error'
if !isResource
try
dur = parseInt dur
catch e
return swal 'Error', 'duration must be an integer', 'error'
try
unless isNaN id
id = parseInt id
@ -110,13 +124,22 @@ pertApp.controller 'editorController', ($scope) ->
deps[i] = parseInt dep
catch e
newdata = $scope.fromLocalStorage silent: yes, raw: yes
if !newdata? or newdata is null or !newdata.push?
newdata = []
newdata.push { id: id, duration: dur, depends: deps }
$scope.toLocalStorage newdata, silent: yes
if isResource
if newdata?.resources?.push?
console.log newdata.resources
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 = ->
data = $scope.fromLocalStorage { silent: yes, raw: yes }
$scope.list = data || []
$scope.activities = data.activities || []
$scope.resources = data.resources || []
$scope.$on 'dataChanged', $scope.refreshEditor
$scope.refreshEditor()

View File

@ -4,7 +4,7 @@
<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>
</div>
<!-- new item -->
<!-- new activity -->
<div class="panel panel-default new-activity-panel">
<div class="panel-heading">
<i class="fa fa-plus"></i> New Activity
@ -12,21 +12,21 @@
<div class="panel-body">
<div class="input-group">
<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 class="input-group duration">
<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 class="input-group dependencies">
<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>
<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>
</div>
</div>
<!-- item list -->
<!-- activity list -->
<table id="tab" class="table">
<tr>
<td><b>#</b></td>
@ -35,11 +35,11 @@
<td><b>Dependencies</b></td>
<td><b>Commands</b></td>
</tr>
<tr ng-repeat="item in list">
<tr ng-repeat="item in activities">
<td>{{$index + 1}}</td>
<td>{{item.id}}</td>
<td>{{item.duration}}</td>
<td>{{item.depends || []}}</td>
<td>{{item.depends.join(', ') || "None"}}</td>
<td>
<button class="btn btn-info" ng-click="clone(item.id)">
<i class="fa fa-copy"></i>
@ -50,4 +50,51 @@
</td>
</tr>
</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>

View File

@ -37,7 +37,8 @@
<ul class="nav navbar-nav">
<li><a ui-sref="edit">Edit</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="gantt">Gantt</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>Depends On</b></td>
<td><b>Dependant</b></td>
<td><b>Resources</b></td>
<td><b>End Day</b></td>
<td><b>Free Delay</b></td>
<td><b>Chained Delay</b></td>
@ -15,8 +16,9 @@
<td>{{item.id}}</td>
<td>{{item.duration}}</td>
<td>{{item.startDay}}</td>
<td>{{item.depends || "N/A"}}</td>
<td>{{item.dependant || "N/A"}}</td>
<td>{{item.depends.join(', ') || "None"}}</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.permittedDelay || 0}}</td>
<td>{{item.chainedDelay || 0}}</td>