precedence 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,80 +1,60 @@
1
+ # Requirements from Ruby standard library
1
2
  require('erb')
3
+ require('yaml')
2
4
 
3
- module Precedence
4
-
5
- # An overwritten Hash that has all keys as strings
6
- class ActivityHash < Hash #:nodoc:
7
- def[]=(ref,value)
8
- super(ref.to_s, value)
9
- end
10
-
11
- def[](ref)
12
- super(ref.to_s)
13
- end
14
- end
5
+ module Precedence
15
6
 
16
- # Represents an entire precedence network.
7
+ # Represents an entire precedence network. It wraps around an underlying
8
+ # directed graph of Activity objects and adds a number of utility methods
9
+ # and extra functionality that would not be available if one just used
10
+ # Activity objects.
17
11
  class Network
18
12
 
19
13
  # A hashed collection of all the activities in the network. The index
20
14
  # is the reference of the activity.
21
15
  attr_reader :activities
22
16
 
17
+ # The reference for the StartActivity in the network.
18
+ START = Precedence::StartActivity::REFERENCE
19
+
20
+ # The reference for the FinishActivity in the network.
21
+ FINISH = Precedence::FinishActivity::REFERENCE
22
+
23
23
  # Initialises the precedence network. The descriptions for the start
24
24
  # and finish activities can be set in the parameters. The start and
25
25
  # finish activities of the network have as references 'start' and
26
- # 'finish' respectively (and as such these references are reserved)
26
+ # 'finish' (held in constants Network::START and Network::FINISH)
27
+ # respectively (and as such these references are reserved)
27
28
  # and a duration of 0.
28
- def initialize(startDescription='Start',
29
- finishDescription='Finish')
30
- @start = Precedence::StartFinishActivity.new('start',
31
- startDescription)
32
- @finish = Precedence::StartFinishActivity.new('finish',
33
- finishDescription)
34
- @activities = Precedence::ActivityHash.new
35
- @activities[@start.reference] = @start
36
- @activities[@finish.reference] = @finish
29
+ #
30
+ # Example usage:
31
+ # precNetwork = Precedence::Network.new('Begin','End')
32
+ # will create a new precedence network with the start and finish
33
+ # activities having descriptions of 'Begin' and 'End' respectively.
34
+ def initialize()
35
+ @start = Precedence::StartActivity.new
36
+ @finish = Precedence::FinishActivity.new
37
+ @activities = Precedence::Utilities::ActivityHash.new(@start,@finish)
38
+ #@activities[START] = @start
39
+ #@activities[FINISH] = @finish
37
40
  end
38
41
 
42
+
39
43
  # Creates a new activity and adds it to the precedence network. The
40
- # reference is the only mandatory parameter. The post_activities and
41
- # pre_activities parameters should contain the references to activities
42
- # already defined within the network and not the actual
43
- # Precedence::Activity object.
44
+ # reference is the only mandatory parameter. This method will take the
45
+ # block given and pass it to the Activity.new method.
44
46
  #
45
47
  # Exapmple usage:
46
- # precNetwork.new_activity('a2',3,'Activity2',['a1'])
48
+ # precNetwork.new_activity('a2') do |activity|
49
+ # activity.duration = 3
50
+ # activity.description = 'Activity2'
51
+ # end
52
+ # precNetwork.connect('a2','a1')
47
53
  # will create a new activity in the network with referecne 'a2' and with
48
- # activity 'a1' as a post-activity.
49
- def new_activity(reference,duration=0,description="",
50
- post_activities=[],pre_activities=[])
51
-
52
- post_activities.each do |post_activity_ref|
53
- unless @activities[post_activity_ref]
54
- raise "Post-activity with reference #{post_activity_ref} "+
55
- "was not found."
56
- end
57
- end
58
-
59
- pre_activities.each do |pre_activity_ref|
60
- unless @activities[pre_activity_ref]
61
- raise "Pre-activity with reference #{pre_activity_ref} "+
62
- "was not found."
63
- end
64
- end
65
-
66
- activity =
67
- Precedence::Activity.new(reference,duration,description)
68
-
69
- post_activities.each do |post_activity_ref|
70
- activity.add_post_activities(@activities[post_activity_ref])
71
- end
72
-
73
- pre_activities.each do |pre_activity_ref|
74
- activity.add_pre_activities(@activities[pre_activity_ref])
75
- end
76
-
77
- @activities[reference]=activity
54
+ # activity 'a1' as a post-activity.
55
+ def new_activity(reference,&block)
56
+ activity = Precedence::Activity.new(reference,&block)
57
+ activities[reference]=activity
78
58
  end
79
59
 
80
60
  # Adds a Precedence::Activity object to the network. This should be a
@@ -86,21 +66,20 @@ module Precedence
86
66
  # precNetwork.add_activity(activity)
87
67
  # will add the existing activity 'a1' to the network.
88
68
  def add_activity(activity)
89
- if @activities[activity.reference]
69
+ if (reference_exists?(activity.reference))
90
70
  raise "Activity #{activity.reference} already exists in the "+
91
71
  "network."
92
72
  end
93
73
 
94
- unless activity.post_activities == []
74
+ unless (activity.post_activities == [])
95
75
  raise "Can not add an activity with post activities."
96
76
  end
97
77
 
98
- unless activity.pre_activities == []
78
+ unless (activity.pre_activities == [])
99
79
  raise "Can not add an activity with pre activities."
100
80
  end
101
81
 
102
- @activities[activity.reference] = activity
103
-
82
+ @activities[activity.reference] = activity
104
83
  end
105
84
 
106
85
  # Connects two or more activities together. The pre_ref activity will
@@ -111,43 +90,208 @@ module Precedence
111
90
  # precNetwork.connect(:h1,:h2,:h3)
112
91
  # will add activity 'h1' as a pre-activity to activities 'h2' and 'h3'.
113
92
  def connect(pre_ref,*post_refs)
114
- unless @activities[pre_ref]
93
+ unless reference_exists?(pre_ref)
115
94
  raise "Pre-activity with reference #{pre_ref} "+
116
95
  "was not found."
117
96
  end
118
97
 
119
98
  post_refs.each do |post_ref|
120
- unless @activities[post_ref]
99
+ unless reference_exists?(post_ref)
121
100
  raise "Post-activity with reference #{post_ref} "+
122
101
  "was not found."
123
102
  end
124
103
  end
125
104
 
126
105
  post_refs.each do |post_ref|
127
- @activities[pre_ref].add_post_activities(@activities[post_ref])
106
+ activities[pre_ref].add_post_activities(activities[post_ref])
128
107
  end
129
108
  end
130
109
 
110
+ # Disconnects an activity from one or more post activities.
111
+ #
112
+ # Example usage:
113
+ # precNetwork.disconnect(:h1,:h2,:h3)
114
+ # will remove activity 'h1' as a pre-activity to activities 'h2' and
115
+ # 'h3'.
116
+ def disconnect(pre_ref,*post_refs)
117
+ unless reference_exists?(pre_ref)
118
+ raise "Pre-activity with reference #{pre_ref} "+
119
+ "was not found."
120
+ end
121
+
122
+ post_refs.each do |post_ref|
123
+ unless reference_exists?(post_ref)
124
+ raise "Post-activity with reference #{post_ref} "+
125
+ "was not found."
126
+ end
127
+ end
128
+
129
+ post_refs.each do |post_ref|
130
+ activities[pre_ref].remove_post_activities(activities[post_ref])
131
+ end
132
+ end
133
+
134
+ # Returns true if the reference is currently in the network. This
135
+ # includes START and FINISH.
136
+ def reference_exists?(reference)
137
+ if ((!activities[reference].nil?) ||
138
+ (reference == START) ||
139
+ (reference == FINISH))
140
+ return true
141
+ end
142
+ return false
143
+ end
144
+
131
145
  # Ensures that the network is properly connected by connecting any
132
146
  # activity without pre-activities to the start node and to the finish
133
- # node if it has no post-activities.
147
+ # node if it has no post-activities. Must be called before any analysis
148
+ # on the network is done.
149
+ #
150
+ # Example usage:
151
+ # precNetwork.connect('h1','h2')
152
+ # precNetwork.connect('h2','h3','h4)
153
+ # precNetwork.fix_connections!
154
+ # precNetwork.finish
155
+ # precNetwork.activities['h2'].earliest_finish
134
156
  def fix_connections!
135
- @activities.each do |ref,activity|
136
- next if (ref == 'start' or ref == 'finish')
157
+ activities.each do |ref,activity|
137
158
  if activity.pre_activities.size == 0
138
- connect(:start,ref)
159
+ connect(START,ref)
139
160
  end
140
161
 
141
162
  if activity.post_activities.size == 0
142
- connect(ref,:finish)
163
+ connect(ref,FINISH)
164
+ end
165
+ end
166
+ end
167
+
168
+ # Returns a dot file capable of being rendered by the Dot graph renderer
169
+ # available from GraphViz (http://www.graphviz.org).
170
+ #
171
+ # Example usage:
172
+ # File.open('test_to_dot.dot',File::CREAT|File::TRUNC|File::WRONLY) do|f|
173
+ # f.puts(net.to_dot)
174
+ # end
175
+ def to_dot(template=nil)
176
+ unless template
177
+ template = Precedence::Network.get_rdot_template
178
+ end
179
+ return ERB.new(template).result(binding)
180
+ end
181
+
182
+ # Returns a YAML representation of the network.For more information on
183
+ # YAML go to http://yaml.org
184
+ #
185
+ # Example usage:
186
+ # File.open('test_to_yaml.yaml',File::CREAT|File::TRUNC|File::WRONLY) do|f|
187
+ # f.puts(net.to_yaml)
188
+ # end
189
+ def to_yaml
190
+ (activities.values.collect do |activity|
191
+ activity.to_yaml
192
+ end).join("\n")
193
+ end
194
+
195
+
196
+ # Returns a Precedence::Network object that is represented by the
197
+ # YAML document.
198
+ #
199
+ # Example usage:
200
+ # precNetwork = nil
201
+ # File.open('precnetwork.yaml',File::RDONLY) do |f|
202
+ # precNetwork = Precedence::Network.from_yaml(f)
203
+ # end
204
+ def self.from_yaml(yaml)
205
+ activities = {}
206
+ connections = {}
207
+
208
+ YAML::load_documents(yaml) do |doc|
209
+ activity = Precedence::Activity.from_yaml_object(doc)
210
+ activities[activity.reference] = activity
211
+ connections[activity.reference] =
212
+ doc[activity.reference]['post activities']
213
+
214
+ end
215
+
216
+ network = Precedence::Network.new()
217
+
218
+ activities.values.each do |activity|
219
+ unless ((activity.reference == START) or
220
+ (activity.reference == FINISH))
221
+ network.add_activity(activity)
143
222
  end
223
+ end
224
+
225
+ connections.each do |ref,connections|
226
+ connections.to_a.each do |post_ref|
227
+ network.connect(ref,post_ref)
228
+ end
144
229
  end
230
+ return network
145
231
  end
146
232
 
233
+ # The time it will take to finish all activities in the network.
234
+ # (Shortcut to precNetwork.activities['finish'].finish)
235
+ #
236
+ # Example usage:
237
+ # precNetwork.finish
238
+ def finish
239
+ return @finish.finish
240
+ end
241
+
242
+ # Iterates over the networks duration yielding an array of activities
243
+ # that are active at that time to a block. The size of the time jumps
244
+ # can be set using the tick parameter. The duration starts at time 0
245
+ # and ends at time network.finish - tick.
246
+ #
247
+ # Example usage:
248
+ # precNetwork.each_time_period do |activities|
249
+ # coffeeUsed == 0
250
+ # activities.each do |activity|
251
+ # coffeeUsed += activity.resources['coffee']
252
+ # end
253
+ # puts "Used #{coffeeUsed} units of coffee in #{activities.size} activities."
254
+ # end
255
+ def each_time_period(tick=1)
256
+ 0.step(finish-tick,tick) do |current_time|
257
+ yield(activities_at_time(current_time))
258
+ end
259
+ end
260
+
261
+ # The same functionality as each_time_period except that the time is
262
+ # yielded along with the array of active activities.
263
+ #
264
+ # Example usage:
265
+ # precNetwork.each_time_period_with_index do |activities,time|
266
+ # puts "At time #{time}, #{activities.size} activities were active."
267
+ # end
268
+ def each_time_period_with_index(tick=1)
269
+ 0.step(finish-tick,tick) do |current_time|
270
+ yield(activities_at_time(current_time),current_time)
271
+ end
272
+ end
273
+
274
+ # Returns an array of activities with each activity being active at
275
+ # the time of the parameter.
276
+ def activities_at_time(time)
277
+ return (@activities.values.find_all do |activity|
278
+ activity.active_on?(time)
279
+ end)
280
+ end
281
+
282
+ # Returns an array of activities with each acitivity being active
283
+ # deuring the time range of the parameter
284
+ def activities_during_time(range)
285
+ return (@activities.values.find_all do |activity|
286
+ activity.active_during?(range)
287
+ end)
288
+ end
289
+
290
+
147
291
  # Returns the rdot (Ruby embedded in a dot file) template that is used
148
292
  # by default when generating the precedence network diagrams.
149
- def get_rdot_template
150
- return <<EOS
293
+ def self.get_rdot_template #:nodoc:
294
+ return <<END_OF_STRING
151
295
  /* Generated by Precedence on <%= Time.now.to_s %> */
152
296
  digraph network {
153
297
  rankdir=LR;
@@ -155,12 +299,12 @@ digraph network {
155
299
 
156
300
  /* Activities */
157
301
  <% @activities.each do |ref,activity| %>
158
- <% case ref == 'start'
159
- when 'start'%>
302
+ <% case ref
303
+ when START%>
160
304
  "<%= ref %>" [label="<%= activity.description %>"];
161
- <% when 'finish' %>
305
+ <% when FINISH %>
162
306
  "<%= ref %>" [label="{<%= activity.description%>|<%= activity.earliest_finish %>}"];
163
- <% else %>
307
+ <% else %>
164
308
  "<%= ref %>" [label="<%=ref%>|{{<%=activity.earliest_start%>|<%=activity.latest_start%>}|{<%=activity.description%>|{<%=activity.total_float%>|<%=activity.early_float%>}}|{<%=activity.earliest_finish%>|<%=activity.latest_finish%>}}|<%=activity.duration%>"];
165
309
  <% end %>
166
310
  <% end %>
@@ -168,20 +312,11 @@ digraph network {
168
312
  /* Dependencies */
169
313
  <% @activities.each do |ref,activity|%>
170
314
  <% activity.post_activities.each do |post_activity| %>
171
- "<%= activity.reference %>" -> "<%= post_activity.reference %>" <% if (activity.on_critical_path? and post_activity.on_critical_path?)%>[style=bold];<% else %>;<% end %>
315
+ "<%= activity.reference %>" -> "<%= post_activity.reference %>" <% if (activity.on_critical_path? and post_activity.on_critical_path?)%>[style=bold]<% end %>;
172
316
  <% end %>
173
317
  <% end %>
174
318
  }
175
- EOS
176
- end
177
-
178
- # Returns a dot file capable of being rendered by the Dot graph renderer
179
- # (available from GraphViz http://www.graphviz.org).
180
- def to_dot(template=nil)
181
- unless template
182
- template = get_rdot_template
183
- end
184
- return ERB.new(template).result(binding)
319
+ END_OF_STRING
185
320
  end
186
321
  end
187
322
  end
@@ -0,0 +1,44 @@
1
+ module Precedence
2
+ module Utilities #:nodoc:
3
+ # A modified Hash that has all keys as strings
4
+ class ActivityHash < Hash #:nodoc:
5
+
6
+ def initialize(startActivity,finishActivity)
7
+ super(nil)
8
+ @start = startActivity
9
+ @finish = finishActivity
10
+ end
11
+
12
+ def[]=(ref,value)
13
+ if ((ref != @start.reference) && (ref != @finish.reference))
14
+ super(ref.to_s, value)
15
+ end
16
+ end
17
+
18
+ def[](ref)
19
+ if (ref.to_s == @start.reference)
20
+ return @start
21
+ elsif (ref.to_s == @finish.reference)
22
+ return @finish
23
+ else
24
+ return super(ref.to_s)
25
+ end
26
+ end
27
+ end
28
+
29
+ # A modified Hash that has all keys as strings and all values as floats
30
+ class ResourceHash < Hash #:nodoc:
31
+ def initialze
32
+ super(0.0)
33
+ end
34
+
35
+ def [](resource)
36
+ return super(resource.to_s)
37
+ end
38
+
39
+ def []=(resource,value)
40
+ return super(resource.to_s,value.to_f)
41
+ end
42
+ end
43
+ end
44
+ end
data/lib/precedence.rb CHANGED
@@ -1,2 +1,3 @@
1
+ require('precedence/utilities')
1
2
  require('precedence/activity')
2
3
  require('precedence/network')