taskjuggler 3.2.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +30 -0
- data/README.rdoc +1 -0
- data/data/tjp.vim +13 -9
- data/lib/taskjuggler/Allocation.rb +2 -2
- data/lib/taskjuggler/AttributeBase.rb +10 -0
- data/lib/taskjuggler/Attributes.rb +2 -8
- data/lib/taskjuggler/Journal.rb +20 -19
- data/lib/taskjuggler/PTNProxy.rb +14 -1
- data/lib/taskjuggler/ProjectFileParser.rb +3 -0
- data/lib/taskjuggler/ProjectFileScanner.rb +54 -44
- data/lib/taskjuggler/PropertyList.rb +32 -0
- data/lib/taskjuggler/PropertyTreeNode.rb +4 -0
- data/lib/taskjuggler/ResourceScenario.rb +53 -20
- data/lib/taskjuggler/RichText/Document.rb +7 -5
- data/lib/taskjuggler/RichText/Scanner.rb +38 -38
- data/lib/taskjuggler/RichText/TOCEntry.rb +1 -1
- data/lib/taskjuggler/TaskScenario.rb +78 -62
- data/lib/taskjuggler/TextParser.rb +4 -3
- data/lib/taskjuggler/TextParser/MacroTable.rb +3 -1
- data/lib/taskjuggler/TextParser/Scanner.rb +73 -58
- data/lib/taskjuggler/Tj3Config.rb +1 -1
- data/lib/taskjuggler/TjpSyntaxRules.rb +215 -67
- data/lib/taskjuggler/apps/Tj3Client.rb +1 -1
- data/lib/taskjuggler/daemon/ReportServer.rb +8 -1
- data/lib/taskjuggler/reports/ExportRE.rb +46 -0
- data/lib/taskjuggler/reports/MspXmlRE.rb +409 -0
- data/lib/taskjuggler/reports/Report.rb +29 -4
- data/lib/taskjuggler/reports/ReportBase.rb +0 -8
- data/lib/taskjuggler/reports/TableReport.rb +13 -3
- data/lib/taskjuggler/reports/TaskListRE.rb +1 -0
- data/lib/taskjuggler/reports/TjpExportRE.rb +1 -1
- data/manual/Rich_Text_Attributes +2 -2
- data/manual/Tutorial +15 -11
- data/manual/html/Day_To_Day_Juggling.html +1 -1
- data/manual/html/Getting_Started.html +1 -1
- data/manual/html/How_To_Contribute.html +1 -1
- data/manual/html/Installation.html +1 -1
- data/manual/html/Intro.html +1 -1
- data/manual/html/Reporting_Bugs.html +1 -1
- data/manual/html/Rich_Text_Attributes.html +1 -1
- data/manual/html/Software.html +1 -1
- data/manual/html/TaskJuggler_2x_Migration.html +1 -1
- data/manual/html/TaskJuggler_Internals.html +1 -1
- data/manual/html/The_TaskJuggler_Syntax.html +1 -1
- data/manual/html/Tutorial.html +5 -2
- data/manual/html/account.html +1 -1
- data/manual/html/account.task.html +1 -1
- data/manual/html/accountprefix.html +1 -1
- data/manual/html/accountreport.html +9 -3
- data/manual/html/accountroot.html +1 -1
- data/manual/html/active.html +1 -1
- data/manual/html/adopt.task.html +3 -3
- data/manual/html/aggregate.html +1 -1
- data/manual/html/alert.html +1 -1
- data/manual/html/alertlevels.html +1 -1
- data/manual/html/allocate.html +5 -2
- data/manual/html/alphabet.html +1 -1
- data/manual/html/alternative.html +1 -1
- data/manual/html/author.html +1 -1
- data/manual/html/balance.html +1 -1
- data/manual/html/booking.resource.html +1 -1
- data/manual/html/booking.task.html +1 -1
- data/manual/html/caption.html +1 -1
- data/manual/html/cellcolor.column.html +1 -1
- data/manual/html/celltext.column.html +43 -4
- data/manual/html/center.html +1 -1
- data/manual/html/charge.html +1 -1
- data/manual/html/chargeset.html +2 -2
- data/manual/html/columnid.html +6 -6
- data/manual/html/columns.html +1 -1
- data/manual/html/complete.html +1 -1
- data/manual/html/copyright.html +1 -1
- data/manual/html/credits.html +1 -1
- data/manual/html/currency.html +1 -1
- data/manual/html/currencyformat.html +1 -1
- data/manual/html/dailymax.html +1 -1
- data/manual/html/dailymin.html +1 -1
- data/manual/html/dailyworkinghours.html +1 -1
- data/manual/html/date.extend.html +1 -1
- data/manual/html/date.html +2 -2
- data/manual/html/definitions.html +1 -1
- data/manual/html/depends.html +1 -1
- data/manual/html/details.html +1 -1
- data/manual/html/disabled.html +1 -1
- data/manual/html/duration.html +1 -1
- data/manual/html/efficiency.html +1 -1
- data/manual/html/effort.html +1 -1
- data/manual/html/email.html +1 -1
- data/manual/html/enabled.html +1 -1
- data/manual/html/end.column.html +1 -1
- data/manual/html/end.html +1 -1
- data/manual/html/end.limit.html +1 -1
- data/manual/html/end.report.html +9 -3
- data/manual/html/end.timesheet.html +1 -1
- data/manual/html/endcredit.html +1 -1
- data/manual/html/epilog.html +1 -1
- data/manual/html/export.html +73 -5
- data/manual/html/extend.html +1 -1
- data/manual/html/fail.html +1 -1
- data/manual/html/fdl.html +1 -1
- data/manual/html/flags.account.html +1 -1
- data/manual/html/flags.html +1 -1
- data/manual/html/flags.journalentry.html +1 -1
- data/manual/html/flags.report.html +1 -1
- data/manual/html/flags.resource.html +1 -1
- data/manual/html/flags.statussheet.html +1 -1
- data/manual/html/flags.task.html +1 -1
- data/manual/html/flags.timesheet.html +1 -1
- data/manual/html/fontcolor.column.html +1 -1
- data/manual/html/footer.html +1 -1
- data/manual/html/formats.export.html +72 -0
- data/manual/html/formats.html +3 -3
- data/manual/html/functions.html +3 -3
- data/manual/html/gapduration.html +2 -2
- data/manual/html/gaplength.html +1 -1
- data/manual/html/halign.center.html +1 -1
- data/manual/html/halign.column.html +1 -1
- data/manual/html/halign.left.html +1 -1
- data/manual/html/halign.right.html +1 -1
- data/manual/html/hasalert.html +1 -1
- data/manual/html/header.html +1 -1
- data/manual/html/headline.html +1 -1
- data/manual/html/height.html +1 -1
- data/manual/html/hideaccount.html +1 -1
- data/manual/html/hidejournalentry.html +1 -1
- data/manual/html/hidereport.html +1 -1
- data/manual/html/hideresource.html +1 -1
- data/manual/html/hidetask.html +1 -1
- data/manual/html/icalreport.html +1 -1
- data/manual/html/include.macro.html +1 -1
- data/manual/html/include.project.html +1 -1
- data/manual/html/include.properties.html +1 -1
- data/manual/html/index.html +1 -1
- data/manual/html/inherit.extend.html +1 -1
- data/manual/html/interval1.html +2 -2
- data/manual/html/interval2.html +2 -2
- data/manual/html/interval3.html +2 -2
- data/manual/html/interval4.html +2 -2
- data/manual/html/isactive.html +1 -1
- data/manual/html/ischildof.html +1 -1
- data/manual/html/isdependencyof.html +1 -1
- data/manual/html/isdutyof.html +1 -1
- data/manual/html/isfeatureof.html +1 -1
- data/manual/html/isleaf.html +1 -1
- data/manual/html/ismilestone.html +1 -1
- data/manual/html/isongoing.html +1 -1
- data/manual/html/isresource.html +1 -1
- data/manual/html/isresponsibilityof.html +1 -1
- data/manual/html/istask.html +1 -1
- data/manual/html/journalattributes.html +1 -1
- data/manual/html/journalentry.html +1 -1
- data/manual/html/journalmode.html +11 -3
- data/manual/html/leaveallowance.html +1 -1
- data/manual/html/leaves.html +1 -1
- data/manual/html/left.html +1 -1
- data/manual/html/length.html +1 -1
- data/manual/html/limits.allocate.html +1 -1
- data/manual/html/limits.html +1 -1
- data/manual/html/limits.resource.html +1 -1
- data/manual/html/limits.task.html +1 -1
- data/manual/html/listitem.column.html +1 -1
- data/manual/html/listtype.column.html +1 -1
- data/manual/html/loadunit.html +2 -2
- data/manual/html/logicalexpression.html +1 -1
- data/manual/html/logicalflagexpression.html +1 -1
- data/manual/html/macro.html +1 -1
- data/manual/html/managers.html +1 -1
- data/manual/html/mandatory.html +1 -1
- data/manual/html/maxend.html +1 -1
- data/manual/html/maximum.html +1 -1
- data/manual/html/maxstart.html +1 -1
- data/manual/html/milestone.html +1 -1
- data/manual/html/minend.html +1 -1
- data/manual/html/minimum.html +1 -1
- data/manual/html/minstart.html +1 -1
- data/manual/html/monthlymax.html +1 -1
- data/manual/html/monthlymin.html +1 -1
- data/manual/html/navbar.html +5 -1
- data/manual/html/navigator.html +1 -1
- data/manual/html/newtask.html +1 -1
- data/manual/html/nikureport.html +1 -1
- data/manual/html/note.task.html +1 -1
- data/manual/html/now.html +1 -1
- data/manual/html/numberformat.html +1 -1
- data/manual/html/onend.html +1 -1
- data/manual/html/onstart.html +1 -1
- data/manual/html/opennodes.html +1 -1
- data/manual/html/overtime.booking.html +1 -1
- data/manual/html/period.column.html +1 -1
- data/manual/html/period.limit.html +1 -1
- data/manual/html/period.report.html +1 -1
- data/manual/html/period.task.html +1 -1
- data/manual/html/persistent.html +1 -1
- data/manual/html/precedes.html +1 -1
- data/manual/html/priority.html +1 -1
- data/manual/html/priority.timesheet.html +1 -1
- data/manual/html/project.html +1 -1
- data/manual/html/projectid.html +1 -1
- data/manual/html/projectid.task.html +1 -1
- data/manual/html/projectids.html +1 -1
- data/manual/html/projection.html +1 -1
- data/manual/html/prolog.html +1 -1
- data/manual/html/properties.html +1 -1
- data/manual/html/purge.html +8 -5
- data/manual/html/rate.html +1 -1
- data/manual/html/rate.resource.html +1 -1
- data/manual/html/reference.extend.html +1 -1
- data/manual/html/remaining.html +1 -1
- data/manual/html/replace.html +1 -1
- data/manual/html/reportprefix.html +1 -1
- data/manual/html/resource.html +1 -1
- data/manual/html/resourceattributes.html +1 -1
- data/manual/html/resourceprefix.html +1 -1
- data/manual/html/resourcereport.html +9 -3
- data/manual/html/resourceroot.html +1 -1
- data/manual/html/resources.limit.html +1 -1
- data/manual/html/responsible.html +1 -1
- data/manual/html/richtext.extend.html +1 -1
- data/manual/html/right.html +1 -1
- data/manual/html/rollupaccount.html +1 -1
- data/manual/html/rollupresource.html +1 -1
- data/manual/html/rolluptask.html +1 -1
- data/manual/html/scale.column.html +1 -1
- data/manual/html/scenario.html +1 -1
- data/manual/html/scenario.ical.html +1 -1
- data/manual/html/scenarios.export.html +1 -1
- data/manual/html/scenarios.html +1 -1
- data/manual/html/scenariospecific.extend.html +1 -1
- data/manual/html/scheduled.html +2 -2
- data/manual/html/scheduling.html +1 -1
- data/manual/html/select.html +1 -1
- data/manual/html/selfcontained.html +1 -1
- data/manual/html/shift.allocate.html +1 -1
- data/manual/html/shift.html +1 -1
- data/manual/html/shift.resource.html +1 -1
- data/manual/html/shift.task.html +1 -1
- data/manual/html/shift.timesheet.html +1 -1
- data/manual/html/shifts.allocate.html +1 -1
- data/manual/html/shifts.resource.html +1 -1
- data/manual/html/shifts.task.html +1 -1
- data/manual/html/shorttimeformat.html +1 -1
- data/manual/html/sloppy.booking.html +1 -1
- data/manual/html/sloppy.projection.html +1 -1
- data/manual/html/sortaccounts.html +1 -1
- data/manual/html/sortjournalentries.html +1 -1
- data/manual/html/sortresources.html +1 -1
- data/manual/html/sorttasks.html +1 -1
- data/manual/html/start.column.html +1 -1
- data/manual/html/start.html +1 -1
- data/manual/html/start.limit.html +1 -1
- data/manual/html/start.report.html +1 -1
- data/manual/html/startcredit.html +1 -1
- data/manual/html/status.statussheet.html +1 -1
- data/manual/html/status.timesheet.html +1 -1
- data/manual/html/statussheet.html +1 -1
- data/manual/html/statussheetreport.html +1 -1
- data/manual/html/strict.projection.html +1 -1
- data/manual/html/summary.html +1 -1
- data/manual/html/supplement.html +1 -1
- data/manual/html/supplement.resource.html +1 -1
- data/manual/html/supplement.task.html +1 -1
- data/manual/html/tagfile.html +1 -1
- data/manual/html/task.html +1 -1
- data/manual/html/task.statussheet.html +1 -1
- data/manual/html/task.timesheet.html +1 -1
- data/manual/html/taskattributes.html +1 -1
- data/manual/html/taskprefix.html +1 -1
- data/manual/html/taskreport.html +9 -3
- data/manual/html/taskroot.export.html +104 -0
- data/manual/html/taskroot.html +3 -3
- data/manual/html/text.extend.html +3 -3
- data/manual/html/textreport.html +9 -3
- data/manual/html/timeformat.html +1 -1
- data/manual/html/timeoff.nikureport.html +1 -1
- data/manual/html/timesheet.html +1 -1
- data/manual/html/timesheetreport.html +1 -1
- data/manual/html/timezone.export.html +1 -1
- data/manual/html/timezone.html +1 -1
- data/manual/html/timezone.report.html +1 -1
- data/manual/html/timezone.shift.html +1 -1
- data/manual/html/timingresolution.html +1 -1
- data/manual/html/title.column.html +1 -1
- data/manual/html/title.html +1 -1
- data/manual/html/toc.html +203 -189
- data/manual/html/tooltip.column.html +1 -1
- data/manual/html/tracereport.html +9 -3
- data/manual/html/trackingscenario.html +1 -1
- data/manual/html/treelevel.html +1 -1
- data/manual/html/vacation.html +1 -1
- data/manual/html/vacation.resource.html +1 -1
- data/manual/html/vacation.shift.html +1 -1
- data/manual/html/warn.html +1 -1
- data/manual/html/weeklymax.html +1 -1
- data/manual/html/weeklymin.html +1 -1
- data/manual/html/weekstartsmonday.html +1 -1
- data/manual/html/weekstartssunday.html +1 -1
- data/manual/html/width.column.html +1 -1
- data/manual/html/width.html +1 -1
- data/manual/html/work.html +1 -1
- data/manual/html/workinghours.project.html +1 -1
- data/manual/html/workinghours.resource.html +1 -1
- data/manual/html/workinghours.shift.html +1 -1
- data/manual/html/yearlyworkingdays.html +1 -1
- data/test/TestSuite/CSV-Reports/project-1.tji +0 -6
- data/test/TestSuite/CSV-Reports/refs/alert.csv +0 -7
- data/test/TestSuite/CSV-Reports/refs/celltext.csv +0 -7
- data/test/TestSuite/CSV-Reports/refs/resourcereport_with_tasks.csv +0 -3
- data/test/TestSuite/CSV-Reports/refs/sortByTree.csv +0 -7
- data/test/TestSuite/CSV-Reports/refs/sortBy_duration.down.csv +0 -7
- data/test/TestSuite/CSV-Reports/refs/sortBy_effort.up.csv +0 -7
- data/test/TestSuite/CSV-Reports/refs/sortBy_plan.start.down.csv +0 -7
- data/test/TestSuite/CSV-Reports/refs/taskreport.csv +0 -7
- data/test/TestSuite/CSV-Reports/refs/taskreport_with_resources.csv +0 -18
- data/test/TestSuite/CSV-Reports/refs/weekly.csv +0 -1
- data/test/TestSuite/HTML-Reports/Alerts-2.tjp +46 -0
- data/test/TestSuite/Scheduler/Correct/purge.tjp +30 -0
- data/test/TestSuite/Syntax/Correct/Export.tjp +8 -2
- data/test/TestSuite/Syntax/Correct/tutorial.tjp +0 -1
- metadata +16 -10
- data/test/TestSuite/CSV-Reports/refs/taskcounter.csv +0 -9
@@ -12,6 +12,7 @@
|
|
12
12
|
#
|
13
13
|
|
14
14
|
require 'taskjuggler/PTNProxy'
|
15
|
+
require 'taskjuggler/MessageHandler'
|
15
16
|
|
16
17
|
class TaskJuggler
|
17
18
|
|
@@ -24,6 +25,8 @@ class TaskJuggler
|
|
24
25
|
# PropertySet.
|
25
26
|
class PropertyList
|
26
27
|
|
28
|
+
include MessageHandler
|
29
|
+
|
27
30
|
attr_writer :query
|
28
31
|
attr_reader :propertySet, :query, :sortingLevels, :sortingCriteria,
|
29
32
|
:sortingUp, :scenarioIdx
|
@@ -72,6 +75,34 @@ class TaskJuggler
|
|
72
75
|
append(adopted)
|
73
76
|
end
|
74
77
|
|
78
|
+
# Make sure that the list does not contain the same PropertyTreeNode more
|
79
|
+
# than once. This could happen for adopted tasks. If you use
|
80
|
+
# includeAdopted(), you should call this method after filtering to see if
|
81
|
+
# the filter was strict enough.
|
82
|
+
def checkForDuplicates(sourceFileInfo)
|
83
|
+
ptns = {}
|
84
|
+
@items.each do |i|
|
85
|
+
if ptns.include?(i.ptn)
|
86
|
+
error('proplist_duplicate',
|
87
|
+
"An adopted property is included as #{i.logicalId} and " +
|
88
|
+
"as #{ptns[i.ptn].logicalId}. Please use stronger filtering " +
|
89
|
+
'to avoid including the property more than once!',
|
90
|
+
sourceFileInfo)
|
91
|
+
end
|
92
|
+
ptns[i.ptn] = i
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
# Specialized version of Array::include? that also matches adopted tasks.
|
98
|
+
def include?(node)
|
99
|
+
!@items.find { |p| p.ptn == node.ptn }.nil?
|
100
|
+
end
|
101
|
+
|
102
|
+
def [](node)
|
103
|
+
@items.find { |n| n.ptn == node.ptn }
|
104
|
+
end
|
105
|
+
|
75
106
|
def to_ary
|
76
107
|
@items.dup
|
77
108
|
end
|
@@ -104,6 +135,7 @@ class TaskJuggler
|
|
104
135
|
end
|
105
136
|
|
106
137
|
@items.concat(list)
|
138
|
+
raise "Duplicate items" if @items != @items.uniq
|
107
139
|
sort!
|
108
140
|
end
|
109
141
|
|
@@ -283,6 +283,10 @@ class TaskJuggler
|
|
283
283
|
res
|
284
284
|
end
|
285
285
|
|
286
|
+
def logicalId
|
287
|
+
fullId
|
288
|
+
end
|
289
|
+
|
286
290
|
# Return the full id of this node. For PropertySet objects with a flat
|
287
291
|
# namespace, this is just the ID. Otherwise, the full ID is composed of all
|
288
292
|
# IDs from the root node to this node, separating the IDs by a dot.
|
@@ -184,13 +184,8 @@ class TaskJuggler
|
|
184
184
|
#puts "Booking resource #{@property.fullId} at " +
|
185
185
|
# "#{@scoreboard.idxToDate(sbIdx)}/#{sbIdx} for task #{task.fullId}\n"
|
186
186
|
@scoreboard[sbIdx] = task
|
187
|
-
# Track the total allocated slots for this resource
|
188
|
-
|
189
|
-
t = @property
|
190
|
-
while t
|
191
|
-
t['effort', @scenarioIdx] += 1
|
192
|
-
t = t.parent
|
193
|
-
end
|
187
|
+
# Track the total allocated slots for this resource.
|
188
|
+
@effort += @efficiency
|
194
189
|
@limits.inc(sbIdx) if @limits
|
195
190
|
|
196
191
|
# Scoreboard iterations are fairly expensive but they are very frequent
|
@@ -198,19 +193,16 @@ class TaskJuggler
|
|
198
193
|
# relevant intervals, we store the interval for all bookings and for
|
199
194
|
# each individual task.
|
200
195
|
if @firstBookedSlot.nil? || @firstBookedSlot > sbIdx
|
201
|
-
@firstBookedSlot = sbIdx
|
196
|
+
@firstBookedSlot = @firstBookedSlots[task] = sbIdx
|
197
|
+
elsif @firstBookedSlots[task].nil? || @firstBookedSlots[task] > sbIdx
|
198
|
+
@firstBookedSlots[task] = sbIdx
|
202
199
|
end
|
203
200
|
if @lastBookedSlot.nil? || @lastBookedSlot < sbIdx
|
204
|
-
@lastBookedSlot = sbIdx
|
205
|
-
|
206
|
-
|
207
|
-
if @firstBookedSlots[task].nil? || @firstBookedSlots[task] > sbIdx
|
208
|
-
@firstBookedSlots[task] = sbIdx
|
209
|
-
end
|
210
|
-
if @lastBookedSlots[task].nil? || @lastBookedSlots[task] < sbIdx
|
211
|
-
@lastBookedSlots[task] = sbIdx
|
212
|
-
end
|
201
|
+
@lastBookedSlot = @lastBookedSlots[task] = sbIdx
|
202
|
+
elsif @lastBookedSlots[task].nil? || @lastBookedSlots[task] < sbIdx
|
203
|
+
@lastBookedSlots[task] = sbIdx
|
213
204
|
end
|
205
|
+
|
214
206
|
true
|
215
207
|
end
|
216
208
|
|
@@ -253,6 +245,21 @@ class TaskJuggler
|
|
253
245
|
book(sbIdx, booking.task, true)
|
254
246
|
end
|
255
247
|
|
248
|
+
# @effort only trackes the already allocated effort for leaf resources. It's
|
249
|
+
# too expensive to propagate this to the group resources on every booking.
|
250
|
+
# If a value for a group effort is needed, it's computed here.
|
251
|
+
def bookedEffort
|
252
|
+
if @property.leaf?
|
253
|
+
@effort
|
254
|
+
else
|
255
|
+
effort = 0
|
256
|
+
@property.kids.each do |r|
|
257
|
+
effort += r.bookedEffort(@scenarioIdx)
|
258
|
+
end
|
259
|
+
effort
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
256
263
|
# Compute the annual leave days within the period specified by the
|
257
264
|
# _query_. The result is in days.
|
258
265
|
def query_annualleave(query)
|
@@ -296,6 +303,31 @@ class TaskJuggler
|
|
296
303
|
query.string = query.scaleLoad(effort)
|
297
304
|
end
|
298
305
|
|
306
|
+
# The completed (as of 'now') effort allocated for the resource in the
|
307
|
+
# specified interval. In case a Task is given as scope property only
|
308
|
+
# the effort allocated for this Task is taken into account.
|
309
|
+
def query_effortdone(query)
|
310
|
+
# For this query, we always override the query period.
|
311
|
+
query.sortable = query.numerical = effort =
|
312
|
+
getEffectiveWork(@project.dateToIdx(@project['start'], false),
|
313
|
+
@project.dateToIdx(@project['now']),
|
314
|
+
query.scopeProperty)
|
315
|
+
query.string = query.scaleLoad(effort)
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
# The remaining (as of 'now') effort allocated for the resource in the
|
320
|
+
# specified interval. In case a Task is given as scope property only
|
321
|
+
# the effort allocated for this Task is taken into account.
|
322
|
+
def query_effortleft(query)
|
323
|
+
# For this query, we always override the query period.
|
324
|
+
query.sortable = query.numerical = effort =
|
325
|
+
getEffectiveWork(@project.dateToIdx(@project['now']),
|
326
|
+
@project.dateToIdx(@project['end'], false),
|
327
|
+
query.scopeProperty)
|
328
|
+
query.string = query.scaleLoad(effort)
|
329
|
+
end
|
330
|
+
|
299
331
|
# The unallocated work time of the Resource during the specified interval.
|
300
332
|
def query_freetime(query)
|
301
333
|
query.sortable = query.numerical = time =
|
@@ -449,9 +481,10 @@ class TaskJuggler
|
|
449
481
|
# There can't be any effective work if the start is after the end or the
|
450
482
|
# todo list doesn't contain the specified task.
|
451
483
|
return 0.0 if startIdx >= endIdx || (task && !@duties.include?(task))
|
484
|
+
# Temporary workaround until @duties is fixed again.
|
452
485
|
|
453
486
|
# The unique key we use to address the result in the cache.
|
454
|
-
@dCache.cached(self, :
|
487
|
+
@dCache.cached(self, :ResourceScenarioGetEffectiveWork, startIdx, endIdx,
|
455
488
|
task) do
|
456
489
|
work = 0.0
|
457
490
|
if @property.container?
|
@@ -516,14 +549,14 @@ class TaskJuggler
|
|
516
549
|
end
|
517
550
|
else
|
518
551
|
if task
|
519
|
-
# If we have a
|
552
|
+
# If we have a known task, we only include the amount that is
|
520
553
|
# specific to this resource, this task and the chargeset of the
|
521
554
|
# task.
|
522
555
|
amount += task.turnover(@scenarioIdx, startIdx, endIdx, account,
|
523
556
|
@property)
|
524
557
|
elsif !@chargeset.empty?
|
525
558
|
# If no tasks was provided, we include the amount of this resource,
|
526
|
-
# weighted by the
|
559
|
+
# weighted by the chargeset of this resource.
|
527
560
|
totalResourceCost = cost(startIdx, endIdx)
|
528
561
|
@chargeset.each do |set|
|
529
562
|
set.each do |accnt, share|
|
@@ -63,14 +63,16 @@ class TaskJuggler
|
|
63
63
|
@toc = TableOfContents.new
|
64
64
|
@references = {}
|
65
65
|
@anchors = []
|
66
|
+
# Collect all file names as potentencial anchors.
|
66
67
|
@snippets.each do |snip|
|
67
68
|
snip.tableOfContents(@toc, snip.name)
|
68
69
|
@anchors << snip.name
|
69
|
-
@
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
(refs = snip.internalReferences).empty? || @references[snip.name] = refs
|
71
|
+
end
|
72
|
+
# Then add all section entries as well. We use the HTML style
|
73
|
+
# <file>#<tag> notation.
|
74
|
+
@toc.each do |tocEntry|
|
75
|
+
@anchors << tocEntry.file + '#' + tocEntry.tag
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
@@ -33,66 +33,66 @@ class TaskJuggler
|
|
33
33
|
def initialize(masterFile, log)
|
34
34
|
tokenPatterns = [
|
35
35
|
# :bol mode rules
|
36
|
-
[ :LINEBREAK, /\s*\n/, :bol, method('linebreak') ],
|
37
|
-
[ nil, /\s+/, :bol, method('inlineMode') ],
|
36
|
+
[ :LINEBREAK, '\s*\n', /\s*\n/, :bol, method('linebreak') ],
|
37
|
+
[ nil, '\s+', /\s+/, :bol, method('inlineMode') ],
|
38
38
|
|
39
39
|
# :bop mode rules
|
40
|
-
[ :PRE, / [^\n]+\n?/, :bop, method('pre') ],
|
41
|
-
[ nil, /\s*\n/, :bop, method('linebreak') ],
|
40
|
+
[ :PRE, ' [^\n]+\n?', / [^\n]+\n?/, :bop, method('pre') ],
|
41
|
+
[ nil, '\s*\n', /\s*\n/, :bop, method('linebreak') ],
|
42
42
|
|
43
43
|
# :inline mode rules
|
44
|
-
[ :SPACE, /[ \t\n]+/, :inline, method('space') ],
|
44
|
+
[ :SPACE, '[ \t\n]+', /[ \t\n]+/, :inline, method('space') ],
|
45
45
|
|
46
46
|
# :bop and :bol mode rules
|
47
|
-
[ :INLINEFUNCSTART, /<-/, [ :bop, :bol, :inline ],
|
47
|
+
[ :INLINEFUNCSTART, '<-', /<-/, [ :bop, :bol, :inline ],
|
48
48
|
method('functionStart') ],
|
49
|
-
[ :BLOCKFUNCSTART, /<\[/, [ :bop, :bol ], method('functionStart') ],
|
50
|
-
[ ':TITLE*', /={2,5}/, [ :bop, :bol ], method('titleStart') ],
|
51
|
-
[ 'TITLE*END', /={2,5}/, :inline, method('titleEnd') ],
|
52
|
-
[ 'BULLET*', /\*{1,4}[ \t]+/, [ :bop, :bol ], method('bullet') ],
|
53
|
-
[ 'NUMBER*', /\#{1,4}[ \t]+/, [ :bop, :bol ], method('number') ],
|
54
|
-
[ :HLINE, /----/, [ :bop, :bol ], method('inlineMode') ],
|
49
|
+
[ :BLOCKFUNCSTART, '<\[', /<\[/, [ :bop, :bol ], method('functionStart') ],
|
50
|
+
[ ':TITLE*', '={2,5}', /={2,5}/, [ :bop, :bol ], method('titleStart') ],
|
51
|
+
[ 'TITLE*END', '={2,5}', /={2,5}/, :inline, method('titleEnd') ],
|
52
|
+
[ 'BULLET*', '\*{1,4}[ \t]+', /\*{1,4}[ \t]+/, [ :bop, :bol ], method('bullet') ],
|
53
|
+
[ 'NUMBER*', '\#{1,4}[ \t]+', /\#{1,4}[ \t]+/, [ :bop, :bol ], method('number') ],
|
54
|
+
[ :HLINE, '----', /----/, [ :bop, :bol ], method('inlineMode') ],
|
55
55
|
|
56
56
|
# :bop, :bol and :inline mode rules
|
57
57
|
# The <nowiki> token puts the scanner into :nowiki mode.
|
58
|
-
[ nil, /<nowiki>/, [ :bop, :bol, :inline ], method('nowikiStart') ],
|
59
|
-
[ :FCOLSTART, /<fcol:([a-z]+|#[0-9A-Fa-f]{3,6})>/, [ :bop, :bol,
|
58
|
+
[ nil, '<nowiki>', /<nowiki>/, [ :bop, :bol, :inline ], method('nowikiStart') ],
|
59
|
+
[ :FCOLSTART, '<fcol:([a-z]+|#[0-9A-Fa-f]{3,6})>', /<fcol:([a-z]+|#[0-9A-Fa-f]{3,6})>/, [ :bop, :bol,
|
60
60
|
:inline ],
|
61
61
|
method('fontColorStart') ],
|
62
|
-
[ :FCOLEND, /<\/fcol>/, [ :bop, :bol, :inline ],
|
62
|
+
[ :FCOLEND, '<\/fcol>', /<\/fcol>/, [ :bop, :bol, :inline ],
|
63
63
|
method('fontColorEnd') ],
|
64
|
-
[ :QUOTES, /'{2,5}/, [ :bop, :bol, :inline ], method('quotes') ],
|
65
|
-
[ :REF, /\[\[/, [ :bop, :bol, :inline ], method('refStart') ],
|
66
|
-
[ :HREF, /\[/, [ :bop, :bol, :inline], method('hrefStart') ],
|
67
|
-
[ :WORD, /.[^ \n\t\[<']*/, [ :bop, :bol, :inline ],
|
64
|
+
[ :QUOTES, '\'{2,5}', /'{2,5}/, [ :bop, :bol, :inline ], method('quotes') ],
|
65
|
+
[ :REF, '\[\[', /\[\[/, [ :bop, :bol, :inline ], method('refStart') ],
|
66
|
+
[ :HREF, '\[', /\[/, [ :bop, :bol, :inline], method('hrefStart') ],
|
67
|
+
[ :WORD, '.[^ \n\t\[<\']*', /.[^ \n\t\[<']*/, [ :bop, :bol, :inline ],
|
68
68
|
method('inlineMode') ],
|
69
69
|
|
70
70
|
# :nowiki mode rules
|
71
|
-
[ nil, /<\/nowiki>/, :nowiki, method('nowikiEnd') ],
|
72
|
-
[ :WORD, /(<(?!\/nowiki>)|[^ \t\n<])+/, :nowiki ],
|
73
|
-
[ :SPACE, /[ \t]+/, :nowiki ],
|
74
|
-
[ :LINEBREAK, /\s*\n/, :nowiki ],
|
71
|
+
[ nil, '<\/nowiki>', /<\/nowiki>/, :nowiki, method('nowikiEnd') ],
|
72
|
+
[ :WORD, '(<(?!\/nowiki>)|[^ \t\n<])+', /(<(?!\/nowiki>)|[^ \t\n<])+/, :nowiki ],
|
73
|
+
[ :SPACE, '[ \t]+', /[ \t]+/, :nowiki ],
|
74
|
+
[ :LINEBREAK, '\s*\n', /\s*\n/, :nowiki ],
|
75
75
|
|
76
76
|
# :ref mode rules
|
77
|
-
[ :REFEND, /\]\]/, :ref, method('refEnd') ],
|
78
|
-
[ :WORD, /(<(?!-)|(\](?!\])|[^|<\]]))+/, :ref ],
|
79
|
-
[ :QUERY, /<-\w+->/, :ref, method('query') ],
|
80
|
-
[ :LITERAL, /./, :ref ],
|
77
|
+
[ :REFEND, '\]\]', /\]\]/, :ref, method('refEnd') ],
|
78
|
+
[ :WORD, '(<(?!-)|(\](?!\])|[^|<\]]))+', /(<(?!-)|(\](?!\])|[^|<\]]))+/, :ref ],
|
79
|
+
[ :QUERY, '<-\w+->', /<-\w+->/, :ref, method('query') ],
|
80
|
+
[ :LITERAL, '.', /./, :ref ],
|
81
81
|
|
82
82
|
# :href mode rules
|
83
|
-
[ :HREFEND, /\]/, :href, method('hrefEnd') ],
|
84
|
-
[ :WORD, /(<(?!-)|[^ \t\n\]<])+/, :href ],
|
85
|
-
[ :QUERY, /<-\w+->/, :href, method('query') ],
|
86
|
-
[ :SPACE, /[ \t\n]+/, :href ],
|
83
|
+
[ :HREFEND, '\]', /\]/, :href, method('hrefEnd') ],
|
84
|
+
[ :WORD, '(<(?!-)|[^ \t\n\]<])+', /(<(?!-)|[^ \t\n\]<])+/, :href ],
|
85
|
+
[ :QUERY, '<-\w+->', /<-\w+->/, :href, method('query') ],
|
86
|
+
[ :SPACE, '[ \t\n]+', /[ \t\n]+/, :href ],
|
87
87
|
|
88
88
|
# :func mode rules
|
89
|
-
[ :INLINEFUNCEND, /->/ , :func, method('functionEnd') ],
|
90
|
-
[ :BLOCKFUNCEND, /\]>/, :func, method('functionEnd') ],
|
91
|
-
[ :ID, /[a-zA-Z_]\w*/, :func ],
|
92
|
-
[ :STRING, /"(\\"|[^"])*"/, :func, method('dqString') ],
|
93
|
-
[ :STRING, /'(\\'|[^'])*'/, :func, method('sqString') ],
|
94
|
-
[ nil, /[ \t\n]+/, :func ],
|
95
|
-
[ :LITERAL, /./, :func ]
|
89
|
+
[ :INLINEFUNCEND, '->' , /->/ , :func, method('functionEnd') ],
|
90
|
+
[ :BLOCKFUNCEND, '\]>', /\]>/, :func, method('functionEnd') ],
|
91
|
+
[ :ID, '[a-zA-Z_]\w*', /[a-zA-Z_]\w*/, :func ],
|
92
|
+
[ :STRING, '"(\\\\"|[^"])*"', /"(\\"|[^"])*"/, :func, method('dqString') ],
|
93
|
+
[ :STRING, '\'(\\\\\'|[^\'])*\'', /'(\\'|[^'])*'/, :func, method('sqString') ],
|
94
|
+
[ nil, '[ \t\n]+', /[ \t\n]+/, :func ],
|
95
|
+
[ :LITERAL, '.', /./, :func ]
|
96
96
|
]
|
97
97
|
super(masterFile, log, tokenPatterns, :bop)
|
98
98
|
end
|
@@ -18,7 +18,7 @@ class TaskJuggler
|
|
18
18
|
|
19
19
|
class TaskScenario < ScenarioData
|
20
20
|
|
21
|
-
attr_reader :isRunAway
|
21
|
+
attr_reader :isRunAway, :hasDurationSpec
|
22
22
|
|
23
23
|
# Create a new TaskScenario object.
|
24
24
|
def initialize(task, scenarioIdx, attributes)
|
@@ -67,19 +67,27 @@ class TaskJuggler
|
|
67
67
|
@startPropagated = false
|
68
68
|
@endPropagated = false
|
69
69
|
|
70
|
+
@durationType =
|
71
|
+
if @effort > 0
|
72
|
+
@hasDurationSpec = true
|
73
|
+
:effortTask
|
74
|
+
elsif @length > 0
|
75
|
+
@hasDurationSpec = true
|
76
|
+
:lengthTask
|
77
|
+
elsif @duration > 0
|
78
|
+
@hasDurationSpec = true
|
79
|
+
:durationTask
|
80
|
+
else
|
81
|
+
# If the task is set as milestone is has a duration spec.
|
82
|
+
@hasDurationSpec = @milestone
|
83
|
+
:startEndTask
|
84
|
+
end
|
85
|
+
|
70
86
|
markMilestone
|
71
|
-
# Milestones may only have start or end date even when the 'scheduled'
|
72
|
-
# attribute is set. For further processing, we need to add the missing
|
73
|
-
# date.
|
74
|
-
if @milestone
|
75
|
-
@end = @start if @start && !@end
|
76
|
-
@start = @end if !@start && @end
|
77
|
-
end
|
78
87
|
|
79
88
|
# For start-end-tasks without allocation, we don't have to do
|
80
89
|
# anything but to set the 'scheduled' flag.
|
81
|
-
if @
|
82
|
-
@allocate.empty?
|
90
|
+
if @durationType == :startEndTask && @start && @end && @allocate.empty?
|
83
91
|
@scheduled = true
|
84
92
|
Log.msg { "Task #{@property.fullId}: #{period_to_s}" }
|
85
93
|
end
|
@@ -106,16 +114,10 @@ class TaskJuggler
|
|
106
114
|
|
107
115
|
bookBookings
|
108
116
|
|
109
|
-
@durationType
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
:lengthTask
|
114
|
-
elsif @duration > 0
|
115
|
-
:durationTask
|
116
|
-
else
|
117
|
-
:startEndTask
|
118
|
-
end
|
117
|
+
if @durationType == :startEndTask
|
118
|
+
@startIdx = @project.dateToIdx(@start) if @start
|
119
|
+
@endIdx = @project.dateToIdx(@end) if @end
|
120
|
+
end
|
119
121
|
end
|
120
122
|
|
121
123
|
# The parser only stores the full task IDs for each of the dependencies.
|
@@ -377,9 +379,11 @@ class TaskJuggler
|
|
377
379
|
end
|
378
380
|
end
|
379
381
|
|
380
|
-
#
|
382
|
+
# These lists are no longer needed, so let's save some memory. Set it to
|
381
383
|
# nil so we can detect accidental use.
|
382
384
|
@candidates = nil
|
385
|
+
@mandatories = nil
|
386
|
+
@allLimits = nil
|
383
387
|
end
|
384
388
|
|
385
389
|
# This function is not essential but does perform a large number of
|
@@ -880,9 +884,9 @@ class TaskJuggler
|
|
880
884
|
return false if @isRunAway
|
881
885
|
|
882
886
|
if @forward
|
883
|
-
return true if @start && (hasDurationSpec
|
887
|
+
return true if @start && (@hasDurationSpec || @end)
|
884
888
|
else
|
885
|
-
return true if @end && (hasDurationSpec
|
889
|
+
return true if @end && (@hasDurationSpec || @start)
|
886
890
|
end
|
887
891
|
|
888
892
|
false
|
@@ -962,6 +966,10 @@ class TaskJuggler
|
|
962
966
|
# are only set in scheduleContainer().
|
963
967
|
if @property.leaf?
|
964
968
|
instance_variable_set(('@' + thisEnd).intern, date)
|
969
|
+
if @durationType == :startEndTask
|
970
|
+
instance_variable_set(('@' + thisEnd + 'Idx').intern,
|
971
|
+
@project.dateToIdx(date))
|
972
|
+
end
|
965
973
|
Log.msg { "Task #{@property.fullId}: #{period_to_s}" }
|
966
974
|
end
|
967
975
|
|
@@ -1053,21 +1061,20 @@ class TaskJuggler
|
|
1053
1061
|
|
1054
1062
|
hasThatSpec = !instance_variable_get('@' + thatEnd).nil? ||
|
1055
1063
|
hasStrongDeps?(!atEnd)
|
1056
|
-
hasDurationSpec = hasDurationSpec?
|
1057
1064
|
|
1058
1065
|
# Check for tasks that have no start and end spec, no duration spec but
|
1059
1066
|
# allocates. They can inherit the start and end date.
|
1060
|
-
return true if hasThatSpec &&
|
1067
|
+
return true if hasThatSpec && !@hasDurationSpec && !@allocate.empty?
|
1061
1068
|
|
1062
1069
|
if @forward ^ atEnd
|
1063
1070
|
# the scheduling direction is pointing away from this end
|
1064
|
-
return true if hasDurationSpec || !@booking.empty?
|
1071
|
+
return true if @hasDurationSpec || !@booking.empty?
|
1065
1072
|
|
1066
1073
|
return hasThatSpec
|
1067
1074
|
else
|
1068
1075
|
# the scheduling direction is pointing towards this end
|
1069
1076
|
return !instance_variable_get('@' + thatEnd).nil? &&
|
1070
|
-
|
1077
|
+
!@hasDurationSpec && @booking.empty? #&& @allocate.empty?
|
1071
1078
|
end
|
1072
1079
|
end
|
1073
1080
|
|
@@ -1117,11 +1124,6 @@ class TaskJuggler
|
|
1117
1124
|
propagateDate(nEnd, true) if endSet
|
1118
1125
|
end
|
1119
1126
|
|
1120
|
-
# Return true if the task has a effort, length or duration setting.
|
1121
|
-
def hasDurationSpec?
|
1122
|
-
@length > 0 || @duration > 0 || @effort > 0 || @milestone
|
1123
|
-
end
|
1124
|
-
|
1125
1127
|
# Find the earliest possible start date for the task. This date must be
|
1126
1128
|
# after the end date of all the task that this task depends on.
|
1127
1129
|
# Dependencies may also require a minimum gap between the tasks.
|
@@ -1669,7 +1671,8 @@ class TaskJuggler
|
|
1669
1671
|
|
1670
1672
|
# Gather a list of Resource objects that have been assigned to the task
|
1671
1673
|
# (including sub tasks) for the given Interval _interval_.
|
1672
|
-
def assignedResources(interval)
|
1674
|
+
def assignedResources(interval = nil)
|
1675
|
+
interval = Interval.new(a('start'), a('end')) unless interval
|
1673
1676
|
list = []
|
1674
1677
|
|
1675
1678
|
if @property.container?
|
@@ -1746,9 +1749,8 @@ class TaskJuggler
|
|
1746
1749
|
|
1747
1750
|
# Depending on the scheduling direction we can mark the task as
|
1748
1751
|
# scheduled once we have reached the other end.
|
1749
|
-
|
1750
|
-
|
1751
|
-
(!@forward && currentSlot <= @start)
|
1752
|
+
if (@forward && @currentSlotIdx >= @endIdx) |
|
1753
|
+
(!@forward && @currentSlotIdx <= @startIdx)
|
1752
1754
|
@scheduled = true
|
1753
1755
|
@property.parents.each do |parent|
|
1754
1756
|
parent.scheduleContainer(@scenarioIdx)
|
@@ -1920,6 +1922,7 @@ class TaskJuggler
|
|
1920
1922
|
error('booking_forward_only',
|
1921
1923
|
"Only forward scheduled tasks may have booking statements.")
|
1922
1924
|
end
|
1925
|
+
booked = false
|
1923
1926
|
booking.intervals.each do |interval|
|
1924
1927
|
startIdx = @project.dateToIdx(interval.start, false)
|
1925
1928
|
endIdx = @project.dateToIdx(interval.end, false)
|
@@ -1927,18 +1930,18 @@ class TaskJuggler
|
|
1927
1930
|
if booking.resource.bookBooking(@scenarioIdx, idx, booking)
|
1928
1931
|
# Booking was successful for this time slot.
|
1929
1932
|
@doneEffort += booking.resource['efficiency', @scenarioIdx]
|
1933
|
+
booked = true
|
1930
1934
|
|
1931
1935
|
# Store the indexes of the first slot and the slot after the
|
1932
1936
|
# last slot.
|
1933
1937
|
firstSlotIdx = idx if !firstSlotIdx || firstSlotIdx > idx
|
1934
1938
|
lastSlotIdx = idx if !lastSlotIdx || lastSlotIdx < idx
|
1935
|
-
|
1936
|
-
unless @assignedresources.include?(booking.resource)
|
1937
|
-
@assignedresources << booking.resource
|
1938
|
-
end
|
1939
1939
|
end
|
1940
1940
|
end
|
1941
1941
|
end
|
1942
|
+
if booked && !@assignedresources.include?(booking.resource)
|
1943
|
+
@assignedresources << booking.resource
|
1944
|
+
end
|
1942
1945
|
end
|
1943
1946
|
|
1944
1947
|
# For effort based tasks, or tasks without a start date, with bookings
|
@@ -2094,7 +2097,7 @@ class TaskJuggler
|
|
2094
2097
|
# This function determines if a task is a milestones and marks it
|
2095
2098
|
# accordingly.
|
2096
2099
|
def markMilestone
|
2097
|
-
return if @property.container? || hasDurationSpec
|
2100
|
+
return if @property.container? || @hasDurationSpec ||
|
2098
2101
|
!@booking.empty? || !@allocate.empty?
|
2099
2102
|
|
2100
2103
|
# The following cases qualify for an automatic milestone promotion.
|
@@ -2112,6 +2115,15 @@ class TaskJuggler
|
|
2112
2115
|
@milestone = (hasStartSpec && @forward && !hasEndSpec) ||
|
2113
2116
|
(!hasStartSpec && !@forward && hasEndSpec) ||
|
2114
2117
|
(!hasStartSpec && !hasEndSpec)
|
2118
|
+
|
2119
|
+
# Milestones may only have start or end date even when the 'scheduled'
|
2120
|
+
# attribute is set. For further processing, we need to add the missing
|
2121
|
+
# date.
|
2122
|
+
if @milestone
|
2123
|
+
@hasDurationSpec = true
|
2124
|
+
@end = @start if @start && !@end
|
2125
|
+
@start = @end if !@start && @end
|
2126
|
+
end
|
2115
2127
|
end
|
2116
2128
|
|
2117
2129
|
def checkDependency(dependency, depType)
|
@@ -2180,7 +2192,7 @@ class TaskJuggler
|
|
2180
2192
|
|
2181
2193
|
# Don't propagate if the task has a duration or is a milestone and the
|
2182
2194
|
# task end to set is in the scheduling direction.
|
2183
|
-
return if task.hasDurationSpec
|
2195
|
+
return if task.hasDurationSpec(@scenarioIdx) &&
|
2184
2196
|
!(atEnd ^ task['forward', @scenarioIdx])
|
2185
2197
|
|
2186
2198
|
# Check if all other dependencies for that task end have been determined
|
@@ -2245,7 +2257,7 @@ class TaskJuggler
|
|
2245
2257
|
return nil unless (comp = child.calcCompletion(@scenarioIdx))
|
2246
2258
|
completion += comp
|
2247
2259
|
end
|
2248
|
-
completion /= @property.
|
2260
|
+
completion /= @property.kids.length
|
2249
2261
|
else
|
2250
2262
|
# For leaf tasks we first compare the start and end dates against the
|
2251
2263
|
# current date.
|
@@ -2380,42 +2392,42 @@ class TaskJuggler
|
|
2380
2392
|
# current task.
|
2381
2393
|
def inputs(foundInputs, includeChildren, checkedTasks = [])
|
2382
2394
|
# Ignore tasks that we have already included in the checked tasks list.
|
2383
|
-
return if checkedTasks.include?(@property)
|
2384
|
-
checkedTasks << @property
|
2395
|
+
return if checkedTasks.include?([ @property, includeChildren ])
|
2396
|
+
checkedTasks << [ @property, includeChildren ]
|
2385
2397
|
|
2386
|
-
#
|
2387
|
-
#
|
2388
|
-
# targets.
|
2398
|
+
# An "input" must be a leaf task that has no direct or indirect (through
|
2399
|
+
# parent) following tasks. Only milestones are recognized as inputs.
|
2389
2400
|
if @property.leaf? && !hasPredecessors && @milestone
|
2390
2401
|
foundInputs << @property
|
2391
2402
|
return
|
2392
2403
|
end
|
2393
2404
|
|
2405
|
+
# We also include inputs of child tasks if requested. The recursive
|
2406
|
+
# iteration of child tasks is limited to the tested task only. The
|
2407
|
+
# predecessors children are not iterated. (see further below)
|
2408
|
+
if includeChildren
|
2409
|
+
@property.kids.each do |child|
|
2410
|
+
child.inputs(@scenarioIdx, foundInputs, true, checkedTasks)
|
2411
|
+
end
|
2412
|
+
end
|
2413
|
+
|
2414
|
+
# Now check the direct predecessors.
|
2394
2415
|
@startpreds.each do |t, onEnd|
|
2395
2416
|
t.inputs(@scenarioIdx, foundInputs, false, checkedTasks)
|
2396
2417
|
end
|
2397
2418
|
|
2398
|
-
# Check for indirect predecessors.
|
2419
|
+
# Check for indirect predecessors inherited from the ancestors.
|
2399
2420
|
if @property.parent
|
2400
2421
|
@property.parent.inputs(@scenarioIdx, foundInputs, false, checkedTasks)
|
2401
2422
|
end
|
2402
|
-
|
2403
|
-
# Also include targets of child tasks. The recursive iteration of child
|
2404
|
-
# tasks is limited to the tested task only. The predecessors are not
|
2405
|
-
# iterated.
|
2406
|
-
if includeChildren
|
2407
|
-
@property.kids.each do |child|
|
2408
|
-
child.inputs(@scenarioIdx, foundInputs, true, checkedTasks)
|
2409
|
-
end
|
2410
|
-
end
|
2411
2423
|
end
|
2412
2424
|
|
2413
2425
|
# Recursively compile a list of Task properties which depend on the
|
2414
2426
|
# current task.
|
2415
2427
|
def targets(foundTargets, includeChildren, checkedTasks = [])
|
2416
2428
|
# Ignore tasks that we have already included in the checked tasks list.
|
2417
|
-
return if checkedTasks.include?(@property)
|
2418
|
-
checkedTasks << @property
|
2429
|
+
return if checkedTasks.include?([ @property, includeChildren ])
|
2430
|
+
checkedTasks << [ @property, includeChildren ]
|
2419
2431
|
|
2420
2432
|
# A target must be a leaf function that has no direct or indirect
|
2421
2433
|
# (through parent) following tasks. Only milestones are recognized as
|
@@ -2458,9 +2470,13 @@ class TaskJuggler
|
|
2458
2470
|
end
|
2459
2471
|
end
|
2460
2472
|
|
2473
|
+
# If we are evaluating the task in the context of a specific resource,
|
2474
|
+
# we use the chargeset of that resource, not the chargeset of the task.
|
2475
|
+
chargeset = resource ? resource['chargeset', @scenarioIdx] : @chargeset
|
2476
|
+
|
2461
2477
|
# If there are no chargeset defined for this task, we don't need to
|
2462
2478
|
# compute the resource related or other cost.
|
2463
|
-
unless
|
2479
|
+
unless chargeset.empty?
|
2464
2480
|
resourceCost = 0.0
|
2465
2481
|
otherCost = 0.0
|
2466
2482
|
|
@@ -2490,7 +2506,7 @@ class TaskJuggler
|
|
2490
2506
|
|
2491
2507
|
totalCost = resourceCost + otherCost
|
2492
2508
|
# Now weight the total cost by the share of the account
|
2493
|
-
|
2509
|
+
chargeset.each do |set|
|
2494
2510
|
set.each do |accnt, share|
|
2495
2511
|
if share > 0.0 && (accnt == account || accnt.isChildOf?(account))
|
2496
2512
|
amount += totalCost * share
|