taskjuggler 3.2.0 → 3.3.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.
- 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
|