taskjuggler 3.3.0 → 3.4.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 +35 -0
- data/data/tjp.vim +21 -7
- data/lib/taskjuggler/Allocation.rb +27 -3
- data/lib/taskjuggler/Attributes.rb +3 -2
- data/lib/taskjuggler/HTMLDocument.rb +3 -1
- data/lib/taskjuggler/KateSyntax.rb +213 -0
- data/lib/taskjuggler/Limits.rb +22 -0
- data/lib/taskjuggler/Log.rb +1 -1
- data/lib/taskjuggler/MessageHandler.rb +20 -10
- data/lib/taskjuggler/PTNProxy.rb +6 -1
- data/lib/taskjuggler/Project.rb +10 -4
- data/lib/taskjuggler/ProjectFileParser.rb +1 -2
- data/lib/taskjuggler/ProjectFileScanner.rb +41 -42
- data/lib/taskjuggler/ResourceScenario.rb +41 -1
- data/lib/taskjuggler/RichText/Element.rb +8 -0
- data/lib/taskjuggler/RichText/Parser.rb +1 -1
- data/lib/taskjuggler/RichText/RTFReportLink.rb +20 -10
- data/lib/taskjuggler/RichText/Scanner.rb +54 -38
- data/lib/taskjuggler/RichText/SyntaxRules.rb +12 -0
- data/lib/taskjuggler/SheetReceiver.rb +1 -1
- data/lib/taskjuggler/TableColumnDefinition.rb +5 -1
- data/lib/taskjuggler/TaskJuggler.rb +2 -2
- data/lib/taskjuggler/TaskScenario.rb +117 -33
- data/lib/taskjuggler/TextParser.rb +2 -4
- data/lib/taskjuggler/TextParser/MacroTable.rb +7 -0
- data/lib/taskjuggler/TextParser/Scanner.rb +10 -9
- data/lib/taskjuggler/Tj3Config.rb +1 -1
- data/lib/taskjuggler/TjTime.rb +5 -28
- data/lib/taskjuggler/TjpSyntaxRules.rb +85 -13
- data/lib/taskjuggler/UTF8String.rb +5 -0
- data/lib/taskjuggler/XMLElement.rb +2 -1
- data/lib/taskjuggler/apps/Tj3.rb +29 -5
- data/lib/taskjuggler/daemon/ProcessIntercom.rb +1 -1
- data/lib/taskjuggler/daemon/ProjectServer.rb +5 -2
- data/lib/taskjuggler/daemon/ReportServer.rb +7 -4
- data/lib/taskjuggler/reports/AccountListRE.rb +2 -0
- data/lib/taskjuggler/reports/CSVFile.rb +24 -2
- data/lib/taskjuggler/reports/GanttChart.rb +3 -2
- data/lib/taskjuggler/reports/GanttHeader.rb +18 -18
- data/lib/taskjuggler/reports/GanttLoadStack.rb +1 -1
- data/lib/taskjuggler/reports/MspXmlRE.rb +5 -1
- data/lib/taskjuggler/reports/Report.rb +29 -18
- data/lib/taskjuggler/reports/ReportTable.rb +5 -1
- data/lib/taskjuggler/reports/ReportTableCell.rb +16 -10
- data/lib/taskjuggler/reports/ResourceListRE.rb +2 -0
- data/lib/taskjuggler/reports/TableReport.rb +23 -16
- data/lib/taskjuggler/reports/TaskListRE.rb +2 -0
- data/lib/taskjuggler/reports/TraceReport.rb +1 -1
- data/manual/Installation +5 -1
- data/manual/Intro +1 -0
- data/manual/Rich_Text_Attributes +5 -0
- data/manual/TaskJuggler_Internals +82 -34
- data/manual/Tutorial +1 -1
- 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 +3 -2
- data/manual/html/Intro.html +2 -1
- data/manual/html/Reporting_Bugs.html +1 -1
- data/manual/html/Rich_Text_Attributes.html +2 -1
- data/manual/html/Software.html +1 -1
- data/manual/html/TaskJuggler_2x_Migration.html +1 -1
- data/manual/html/TaskJuggler_Internals.html +15 -13
- data/manual/html/The_TaskJuggler_Syntax.html +1 -1
- data/manual/html/Tutorial.html +2 -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 +14 -2
- data/manual/html/accountroot.html +1 -1
- data/manual/html/active.html +1 -1
- data/manual/html/adopt.task.html +1 -1
- 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 +1 -1
- data/manual/html/alphabet.html +1 -1
- data/manual/html/alternative.html +1 -1
- data/manual/html/author.html +3 -3
- data/manual/html/auxdir.html +69 -0
- data/manual/html/balance.html +3 -3
- 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 +1 -1
- data/manual/html/center.html +1 -1
- data/manual/html/charge.html +1 -1
- data/manual/html/chargeset.html +1 -1
- data/manual/html/columnid.html +21 -9
- data/manual/html/columns.html +2 -2
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- data/manual/html/formats.html +1 -1
- data/manual/html/functions.html +1 -1
- data/manual/html/gapduration.html +1 -1
- 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 +1 -1
- data/manual/html/interval2.html +1 -1
- data/manual/html/interval3.html +1 -1
- data/manual/html/interval4.html +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +9 -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 +1 -1
- data/manual/html/rate.html +1 -1
- data/manual/html/rate.resource.html +3 -3
- data/manual/html/rawhtmlhead.html +68 -0
- data/manual/html/reference.extend.html +3 -3
- 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 +14 -2
- 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 +1 -1
- data/manual/html/scheduling.html +2 -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 +2 -2
- 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 +14 -2
- data/manual/html/taskroot.export.html +1 -1
- data/manual/html/taskroot.html +1 -1
- data/manual/html/text.extend.html +1 -1
- data/manual/html/textreport.html +14 -2
- data/manual/html/timeformat.html +3 -3
- data/manual/html/timeformat1.html +67 -0
- data/manual/html/timeformat2.html +67 -0
- data/manual/html/timeoff.nikureport.html +3 -3
- 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 +270 -242
- data/manual/html/tooltip.column.html +1 -1
- data/manual/html/tracereport.html +14 -2
- 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/tasks/kate.rake +8 -0
- data/test/TestSuite/HTML-Reports/Calendars.tjp +10 -0
- data/test/TestSuite/Scheduler/Correct/PersistentResources-2.tjp +33 -0
- data/test/TestSuite/Scheduler/Correct/PersistentResources.tjp +30 -0
- data/test/TestSuite/Scheduler/Correct/PriorityInversion.tjp +2 -0
- data/test/TestSuite/Syntax/Correct/Macro-4.tjp +4 -0
- data/test/TestSuite/Syntax/Errors/empty.tjp +1 -1
- data/test/test_BatchProcessor.rb +1 -1
- data/test/test_CSVFile.rb +10 -0
- data/test/test_RichText.rb +20 -0
- metadata +38 -9
|
@@ -36,7 +36,7 @@ class TaskJuggler
|
|
|
36
36
|
@variables = [ :LINEBREAK, :SPACE, :WORD,
|
|
37
37
|
:BOLD, :ITALIC, :CODE, :BOLDITALIC, :PRE,
|
|
38
38
|
:HREF, :HREFEND, :REF, :REFEND, :HLINE,
|
|
39
|
-
:FCOLSTART, :FCOLEND,
|
|
39
|
+
:HTMLBLOB, :FCOLSTART, :FCOLEND,
|
|
40
40
|
:QUERY,
|
|
41
41
|
:INLINEFUNCSTART, :INLINEFUNCEND,
|
|
42
42
|
:BLOCKFUNCSTART, :BLOCKFUNCEND, :ID, :STRING,
|
|
@@ -31,20 +31,14 @@ class TaskJuggler
|
|
|
31
31
|
|
|
32
32
|
# Not supported for this function
|
|
33
33
|
def to_s(args)
|
|
34
|
-
|
|
34
|
+
report = checkArgs(args)
|
|
35
|
+
|
|
36
|
+
report.name
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
# Return a HTML tree for the report.
|
|
38
40
|
def to_html(args)
|
|
39
|
-
|
|
40
|
-
error('rtp_report_id',
|
|
41
|
-
"Argument 'id' missing to specify the report to be used.")
|
|
42
|
-
return nil
|
|
43
|
-
end
|
|
44
|
-
unless (report = @project.report(id))
|
|
45
|
-
error('rtp_report_unknown_id', "Unknown report #{id}")
|
|
46
|
-
return nil
|
|
47
|
-
end
|
|
41
|
+
report = checkArgs(args)
|
|
48
42
|
|
|
49
43
|
# The URL for interactive reports is different than for static reports.
|
|
50
44
|
if report.interactive?
|
|
@@ -71,6 +65,22 @@ class TaskJuggler
|
|
|
71
65
|
nil
|
|
72
66
|
end
|
|
73
67
|
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def checkArgs(args)
|
|
71
|
+
if args.nil? || (id = args['id']).nil?
|
|
72
|
+
error('rtp_report_id',
|
|
73
|
+
"Argument 'id' missing to specify the report to be used.")
|
|
74
|
+
return nil
|
|
75
|
+
end
|
|
76
|
+
unless (report = @project.report(id))
|
|
77
|
+
error('rtp_report_unknown_id', "Unknown report #{id}")
|
|
78
|
+
return nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
report
|
|
82
|
+
end
|
|
83
|
+
|
|
74
84
|
end
|
|
75
85
|
|
|
76
86
|
end
|
|
@@ -25,6 +25,7 @@ class TaskJuggler
|
|
|
25
25
|
# :bol : at the begining of a line.
|
|
26
26
|
# :inline : in the middle of a line
|
|
27
27
|
# :nowiki : ignoring all MediaWiki special tokens
|
|
28
|
+
# :html : read anything until </html>
|
|
28
29
|
# :ref : inside of a REF [[ .. ]]
|
|
29
30
|
# :href : inside of an HREF [ .. ]
|
|
30
31
|
# :func : inside of a block <[ .. ]> or inline <- .. -> function
|
|
@@ -33,66 +34,71 @@ class TaskJuggler
|
|
|
33
34
|
def initialize(masterFile, log)
|
|
34
35
|
tokenPatterns = [
|
|
35
36
|
# :bol mode rules
|
|
36
|
-
[ :LINEBREAK,
|
|
37
|
-
[ nil,
|
|
37
|
+
[ :LINEBREAK, /\s*\n/, :bol, method('linebreak') ],
|
|
38
|
+
[ nil, /\s+/, :bol, method('inlineMode') ],
|
|
38
39
|
|
|
39
40
|
# :bop mode rules
|
|
40
|
-
[ :PRE,
|
|
41
|
-
[ nil,
|
|
41
|
+
[ :PRE, / [^\n]+\n?/, :bop, method('pre') ],
|
|
42
|
+
[ nil, /\s*\n/, :bop, method('linebreak') ],
|
|
42
43
|
|
|
43
44
|
# :inline mode rules
|
|
44
|
-
[ :SPACE,
|
|
45
|
+
[ :SPACE, /[ \t\n]+/, :inline, method('space') ],
|
|
45
46
|
|
|
46
47
|
# :bop and :bol mode rules
|
|
47
|
-
[ :INLINEFUNCSTART,
|
|
48
|
+
[ :INLINEFUNCSTART, /<-/, [ :bop, :bol, :inline ],
|
|
48
49
|
method('functionStart') ],
|
|
49
|
-
[ :BLOCKFUNCSTART,
|
|
50
|
-
[ ':TITLE*',
|
|
51
|
-
[ 'TITLE*END',
|
|
52
|
-
[ 'BULLET*',
|
|
53
|
-
[ 'NUMBER*',
|
|
54
|
-
[ :HLINE,
|
|
50
|
+
[ :BLOCKFUNCSTART, /<\[/, [ :bop, :bol ], method('functionStart') ],
|
|
51
|
+
[ ':TITLE*', /={2,5}/, [ :bop, :bol ], method('titleStart') ],
|
|
52
|
+
[ 'TITLE*END', /={2,5}/, :inline, method('titleEnd') ],
|
|
53
|
+
[ 'BULLET*', /\*{1,4}[ \t]+/, [ :bop, :bol ], method('bullet') ],
|
|
54
|
+
[ 'NUMBER*', /\#{1,4}[ \t]+/, [ :bop, :bol ], method('number') ],
|
|
55
|
+
[ :HLINE, /----/, [ :bop, :bol ], method('inlineMode') ],
|
|
55
56
|
|
|
56
57
|
# :bop, :bol and :inline mode rules
|
|
57
58
|
# The <nowiki> token puts the scanner into :nowiki mode.
|
|
58
|
-
[ nil,
|
|
59
|
-
[
|
|
59
|
+
[ nil, /<nowiki>/, [ :bop, :bol, :inline ], method('nowikiStart') ],
|
|
60
|
+
[ nil, /<html>/, [ :bop, :bol, :inline ], method('htmlStart') ],
|
|
61
|
+
[ :FCOLSTART, /<fcol:([a-z]+|#[0-9A-Fa-f]{3,6})>/, [ :bop, :bol,
|
|
60
62
|
:inline ],
|
|
61
63
|
method('fontColorStart') ],
|
|
62
|
-
[ :FCOLEND,
|
|
64
|
+
[ :FCOLEND, /<\/fcol>/, [ :bop, :bol, :inline ],
|
|
63
65
|
method('fontColorEnd') ],
|
|
64
|
-
[ :QUOTES,
|
|
65
|
-
[ :REF,
|
|
66
|
-
[ :HREF,
|
|
67
|
-
[ :WORD,
|
|
66
|
+
[ :QUOTES, /'{2,5}/, [ :bop, :bol, :inline ], method('quotes') ],
|
|
67
|
+
[ :REF, /\[\[/, [ :bop, :bol, :inline ], method('refStart') ],
|
|
68
|
+
[ :HREF, /\[/, [ :bop, :bol, :inline], method('hrefStart') ],
|
|
69
|
+
[ :WORD, /.[^ \n\t\[<']*/, [ :bop, :bol, :inline ],
|
|
68
70
|
method('inlineMode') ],
|
|
69
71
|
|
|
70
72
|
# :nowiki mode rules
|
|
71
|
-
[ nil,
|
|
72
|
-
[ :WORD,
|
|
73
|
-
[ :SPACE,
|
|
74
|
-
[ :LINEBREAK,
|
|
73
|
+
[ nil, /<\/nowiki>/, :nowiki, method('nowikiEnd') ],
|
|
74
|
+
[ :WORD, /(<(?!\/nowiki>)|[^ \t\n<])+/, :nowiki ],
|
|
75
|
+
[ :SPACE, /[ \t]+/, :nowiki ],
|
|
76
|
+
[ :LINEBREAK, /\s*\n/, :nowiki ],
|
|
77
|
+
|
|
78
|
+
# :html mode rules
|
|
79
|
+
[ :HTMLBLOB, /(.|\n)*<\/html>/ , :html, method('htmlEnd') ],
|
|
80
|
+
[ :HTMLBLOB, /.*\n/ , :html ],
|
|
75
81
|
|
|
76
82
|
# :ref mode rules
|
|
77
|
-
[ :REFEND,
|
|
78
|
-
[ :WORD,
|
|
79
|
-
[ :QUERY,
|
|
80
|
-
[ :LITERAL,
|
|
83
|
+
[ :REFEND, /\]\]/, :ref, method('refEnd') ],
|
|
84
|
+
[ :WORD, /(<(?!-)|(\](?!\])|[^|<\]]))+/, :ref ],
|
|
85
|
+
[ :QUERY, /<-\w+->/, :ref, method('query') ],
|
|
86
|
+
[ :LITERAL, /./, :ref ],
|
|
81
87
|
|
|
82
88
|
# :href mode rules
|
|
83
|
-
[ :HREFEND,
|
|
84
|
-
[ :WORD,
|
|
85
|
-
[ :QUERY,
|
|
86
|
-
[ :SPACE,
|
|
89
|
+
[ :HREFEND, /\]/, :href, method('hrefEnd') ],
|
|
90
|
+
[ :WORD, /(<(?!-)|[^ \t\n\]<])+/, :href ],
|
|
91
|
+
[ :QUERY, /<-\w+->/, :href, method('query') ],
|
|
92
|
+
[ :SPACE, /[ \t\n]+/, :href ],
|
|
87
93
|
|
|
88
94
|
# :func mode rules
|
|
89
|
-
[ :INLINEFUNCEND,
|
|
90
|
-
[ :BLOCKFUNCEND,
|
|
91
|
-
[ :ID,
|
|
92
|
-
[ :STRING,
|
|
93
|
-
[ :STRING,
|
|
94
|
-
[ nil,
|
|
95
|
-
[ :LITERAL,
|
|
95
|
+
[ :INLINEFUNCEND, /->/ , :func, method('functionEnd') ],
|
|
96
|
+
[ :BLOCKFUNCEND, /\]>/, :func, method('functionEnd') ],
|
|
97
|
+
[ :ID, /[a-zA-Z_]\w*/, :func ],
|
|
98
|
+
[ :STRING, /"(\\"|[^"])*"/, :func, method('dqString') ],
|
|
99
|
+
[ :STRING, /'(\\'|[^'])*'/, :func, method('sqString') ],
|
|
100
|
+
[ nil, /[ \t\n]+/, :func ],
|
|
101
|
+
[ :LITERAL, /./, :func ]
|
|
96
102
|
]
|
|
97
103
|
super(masterFile, log, tokenPatterns, :bop)
|
|
98
104
|
end
|
|
@@ -166,6 +172,16 @@ class TaskJuggler
|
|
|
166
172
|
[ types[match.length], match ]
|
|
167
173
|
end
|
|
168
174
|
|
|
175
|
+
def htmlStart(type, match)
|
|
176
|
+
self.mode = :html
|
|
177
|
+
[ type, match ]
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def htmlEnd(type, match)
|
|
181
|
+
self.mode = :inline
|
|
182
|
+
[ type, match[0..-8] ]
|
|
183
|
+
end
|
|
184
|
+
|
|
169
185
|
def nowikiStart(type, match)
|
|
170
186
|
self.mode = :nowiki
|
|
171
187
|
[ type, match ]
|
|
@@ -371,6 +371,11 @@ class TaskJuggler
|
|
|
371
371
|
def rule_plainText
|
|
372
372
|
repeatable
|
|
373
373
|
optional
|
|
374
|
+
pattern(%w( !htmlBlob !space ), lambda {
|
|
375
|
+
el = RichTextElement.new(@richTextI, :htmlblob, @val[0].join)
|
|
376
|
+
el.appendSpace = !@val[1].nil?
|
|
377
|
+
el
|
|
378
|
+
})
|
|
374
379
|
pattern(%w( $WORD !space ), lambda {
|
|
375
380
|
el = RichTextElement.new(@richTextI, :text, @val[0])
|
|
376
381
|
el.appendSpace = !@val[1].nil?
|
|
@@ -387,6 +392,13 @@ class TaskJuggler
|
|
|
387
392
|
})
|
|
388
393
|
end
|
|
389
394
|
|
|
395
|
+
def rule_htmlBlob
|
|
396
|
+
repeatable
|
|
397
|
+
pattern(%w( $HTMLBLOB ), lambda {
|
|
398
|
+
@val[0]
|
|
399
|
+
})
|
|
400
|
+
end
|
|
401
|
+
|
|
390
402
|
def rule_space
|
|
391
403
|
optional
|
|
392
404
|
repeatable
|
|
@@ -60,7 +60,7 @@ class TaskJuggler
|
|
|
60
60
|
|
|
61
61
|
attr_reader :id, :cellText, :tooltip, :hAlign, :cellColor, :fontColor
|
|
62
62
|
attr_accessor :title, :start, :end, :scale, :listItem, :listType,
|
|
63
|
-
:width, :content, :column
|
|
63
|
+
:width, :content, :column, :timeformat1, :timeformat2
|
|
64
64
|
|
|
65
65
|
def initialize(id, title)
|
|
66
66
|
# The column ID. It must be unique within the report.
|
|
@@ -99,6 +99,10 @@ class TaskJuggler
|
|
|
99
99
|
@scale = 'week'
|
|
100
100
|
# The width of columns.
|
|
101
101
|
@width = nil
|
|
102
|
+
# Format of the upper calendar header line
|
|
103
|
+
@timeformat1 = nil
|
|
104
|
+
# Format of the lower calendar header line
|
|
105
|
+
@timeformat2 = nil
|
|
102
106
|
|
|
103
107
|
# Reference to the ReportTableColumn object that was created based on this
|
|
104
108
|
# definition.
|
|
@@ -218,13 +218,13 @@ class TaskJuggler
|
|
|
218
218
|
# Check the master file is really a file and not stdin.
|
|
219
219
|
unless (masterFile = @project.inputFiles.masterFile)
|
|
220
220
|
error('cannot_freeze_stdin',
|
|
221
|
-
"The project freeze feature only when the " +
|
|
221
|
+
"The project freeze feature can only be used when the " +
|
|
222
222
|
"master file is a real file, not standard input.")
|
|
223
223
|
end
|
|
224
224
|
|
|
225
225
|
# Derive the file names for the header and bookings file from the base
|
|
226
226
|
# name of the master file.
|
|
227
|
-
masterFileBase = File.basename(masterFile, '.tjp')
|
|
227
|
+
masterFileBase = Dir.pwd + '/' + File.basename(masterFile, '.tjp')
|
|
228
228
|
headerFile = masterFileBase + '-header.tji'
|
|
229
229
|
bookingsFileBase = masterFileBase + '-bookings'
|
|
230
230
|
bookingsFile = bookingsFileBase + '.tji'
|
|
@@ -27,7 +27,8 @@ class TaskJuggler
|
|
|
27
27
|
# time. So make sure some needed attributes really exist so we don't
|
|
28
28
|
# have to check for existance each time we access them.
|
|
29
29
|
%w( allocate assignedresources booking charge chargeset complete
|
|
30
|
-
criticalness depends duration
|
|
30
|
+
competitors criticalness depends duration
|
|
31
|
+
effort end forward gauge length
|
|
31
32
|
maxend maxstart minend minstart milestone pathcriticalness
|
|
32
33
|
precedes priority scheduled shifts start status ).each do |attr|
|
|
33
34
|
@property[attr, @scenarioIdx]
|
|
@@ -38,6 +39,20 @@ class TaskJuggler
|
|
|
38
39
|
@dCache = DataCache.instance
|
|
39
40
|
end
|
|
40
41
|
|
|
42
|
+
def markAsScheduled
|
|
43
|
+
return if @scheduled
|
|
44
|
+
@scheduled = true
|
|
45
|
+
if @milestone
|
|
46
|
+
typename = 'Milestone'
|
|
47
|
+
elsif @property.leaf?
|
|
48
|
+
typename = 'Task'
|
|
49
|
+
else
|
|
50
|
+
typename = 'Container'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
Log.msg { "#{typename} #{@property.fullId} has been scheduled." }
|
|
54
|
+
end
|
|
55
|
+
|
|
41
56
|
# Call this function to reset all scheduling related data prior to
|
|
42
57
|
# scheduling.
|
|
43
58
|
def prepareScheduling
|
|
@@ -78,18 +93,17 @@ class TaskJuggler
|
|
|
78
93
|
@hasDurationSpec = true
|
|
79
94
|
:durationTask
|
|
80
95
|
else
|
|
81
|
-
# If the task is set as milestone
|
|
96
|
+
# If the task is set as milestone it has a duration spec.
|
|
82
97
|
@hasDurationSpec = @milestone
|
|
83
98
|
:startEndTask
|
|
84
99
|
end
|
|
85
100
|
|
|
86
|
-
|
|
101
|
+
markAsMilestone
|
|
87
102
|
|
|
88
103
|
# For start-end-tasks without allocation, we don't have to do
|
|
89
104
|
# anything but to set the 'scheduled' flag.
|
|
90
105
|
if @durationType == :startEndTask && @start && @end && @allocate.empty?
|
|
91
|
-
|
|
92
|
-
Log.msg { "Task #{@property.fullId}: #{period_to_s}" }
|
|
106
|
+
markAsScheduled
|
|
93
107
|
end
|
|
94
108
|
|
|
95
109
|
# Collect the limits of this task and all parent tasks into a single
|
|
@@ -575,6 +589,21 @@ class TaskJuggler
|
|
|
575
589
|
"to ensure allocations or use a higher 'priority'.")
|
|
576
590
|
end
|
|
577
591
|
|
|
592
|
+
thieves = []
|
|
593
|
+
@competitors.each do |t|
|
|
594
|
+
thieves << t if t['priority', @scenarioIdx] < @priority
|
|
595
|
+
end
|
|
596
|
+
unless thieves.empty?
|
|
597
|
+
warning('priority_inversion',
|
|
598
|
+
"Due to a mix of ALAP and ASAP scheduled tasks or a " +
|
|
599
|
+
"dependency on a lower priority tasks the following " +
|
|
600
|
+
"task#{thieves.length > 1 ? 's' : ''} stole resources from " +
|
|
601
|
+
"#{@property.fullId} despite having a lower priority:")
|
|
602
|
+
thieves.each do |t|
|
|
603
|
+
info('priority_inversion_info', "Task #{t.fullId}", t.sourceFileInfo)
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
578
607
|
@errors == 0
|
|
579
608
|
end
|
|
580
609
|
|
|
@@ -966,25 +995,27 @@ class TaskJuggler
|
|
|
966
995
|
# are only set in scheduleContainer().
|
|
967
996
|
if @property.leaf?
|
|
968
997
|
instance_variable_set(('@' + thisEnd).intern, date)
|
|
998
|
+
typename = 'Task'
|
|
969
999
|
if @durationType == :startEndTask
|
|
970
1000
|
instance_variable_set(('@' + thisEnd + 'Idx').intern,
|
|
971
1001
|
@project.dateToIdx(date))
|
|
1002
|
+
if @milestone
|
|
1003
|
+
typename = 'Milestone'
|
|
1004
|
+
end
|
|
972
1005
|
end
|
|
973
|
-
Log.msg { "
|
|
1006
|
+
Log.msg { "Update #{typename} #{@property.fullId}: #{period_to_s}" }
|
|
974
1007
|
end
|
|
975
1008
|
|
|
976
1009
|
if @milestone
|
|
977
1010
|
# Start and end date of a milestone are identical.
|
|
978
|
-
|
|
1011
|
+
markAsScheduled
|
|
979
1012
|
if a(otherEnd).nil?
|
|
980
1013
|
propagateDate(a(thisEnd), !atEnd)
|
|
981
1014
|
end
|
|
982
|
-
Log.msg { "Milestone #{@property.fullId}: #{period_to_s}" }
|
|
983
1015
|
elsif !@scheduled && @start && @end &&
|
|
984
1016
|
!(@length == 0 && @duration == 0 && @effort == 0 &&
|
|
985
1017
|
!@allocate.empty?)
|
|
986
|
-
|
|
987
|
-
Log.msg { "Task #{@property.fullId} has been scheduled" }
|
|
1018
|
+
markAsScheduled
|
|
988
1019
|
end
|
|
989
1020
|
|
|
990
1021
|
# Propagate date to all dependent tasks. Don't do this for start
|
|
@@ -1115,8 +1146,8 @@ class TaskJuggler
|
|
|
1115
1146
|
unless @start && @end
|
|
1116
1147
|
raise "Start (#{@start}) and end (#{@end}) must be set"
|
|
1117
1148
|
end
|
|
1118
|
-
@scheduled = true
|
|
1119
1149
|
Log.msg { "Container task #{@property.fullId} completed: #{period_to_s}" }
|
|
1150
|
+
markAsScheduled
|
|
1120
1151
|
|
|
1121
1152
|
# If we have modified the start or end date, we need to communicate this
|
|
1122
1153
|
# new date to surrounding tasks.
|
|
@@ -1260,6 +1291,11 @@ class TaskJuggler
|
|
|
1260
1291
|
query.string = "#{count.to_i}"
|
|
1261
1292
|
end
|
|
1262
1293
|
|
|
1294
|
+
def query_competitorcount(query)
|
|
1295
|
+
query.sortable = query.numerical = @competitors.length
|
|
1296
|
+
query.string = "#{@competitors.length}"
|
|
1297
|
+
end
|
|
1298
|
+
|
|
1263
1299
|
def query_complete(query)
|
|
1264
1300
|
# If we haven't calculated the value yet, calculate it first.
|
|
1265
1301
|
unless @complete
|
|
@@ -1481,6 +1517,10 @@ class TaskJuggler
|
|
|
1481
1517
|
end
|
|
1482
1518
|
end
|
|
1483
1519
|
|
|
1520
|
+
def query_scheduling(query)
|
|
1521
|
+
query.string = @forward ? 'ASAP' : 'ASAP' if @property.leaf?
|
|
1522
|
+
end
|
|
1523
|
+
|
|
1484
1524
|
def query_status(query)
|
|
1485
1525
|
# If we haven't calculated the completion yet, calculate it first.
|
|
1486
1526
|
calcStatus if @status.empty?
|
|
@@ -1751,7 +1791,7 @@ class TaskJuggler
|
|
|
1751
1791
|
# scheduled once we have reached the other end.
|
|
1752
1792
|
if (@forward && @currentSlotIdx >= @endIdx) |
|
|
1753
1793
|
(!@forward && @currentSlotIdx <= @startIdx)
|
|
1754
|
-
|
|
1794
|
+
markAsScheduled
|
|
1755
1795
|
@property.parents.each do |parent|
|
|
1756
1796
|
parent.scheduleContainer(@scenarioIdx)
|
|
1757
1797
|
end
|
|
@@ -1821,16 +1861,35 @@ class TaskJuggler
|
|
|
1821
1861
|
|
|
1822
1862
|
# In case we have a persistent allocation we need to check if there
|
|
1823
1863
|
# is already a locked resource and use it.
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1864
|
+
locked_candidate = allocation.lockedResource
|
|
1865
|
+
if locked_candidate
|
|
1866
|
+
next if bookResource(locked_candidate)
|
|
1867
|
+
|
|
1868
|
+
if allocation.atomic &&
|
|
1869
|
+
locked_candidate.bookedTask(@scenarioIdx, @currentSlotIdx)
|
|
1870
|
+
rollbackBookings
|
|
1871
|
+
return
|
|
1872
|
+
end
|
|
1873
|
+
|
|
1874
|
+
if @forward
|
|
1875
|
+
next if @currentSlotIdx < locked_candidate.getMaxSlot(@scenarioIdx)
|
|
1876
|
+
else
|
|
1877
|
+
next if @currentSlotIdx > locked_candidate.getMinSlot(@scenarioIdx)
|
|
1878
|
+
end
|
|
1879
|
+
# Persistent candidate is gone for the rest of the project!
|
|
1880
|
+
# Warn and assign somebody else, if available!
|
|
1881
|
+
warning('broken_persistence',
|
|
1882
|
+
"Persistence broken for Task #{@property.fullId} " +
|
|
1883
|
+
"- resource #{locked_candidate.name} is gone")
|
|
1884
|
+
allocation.lockedResource = nil
|
|
1885
|
+
end
|
|
1886
|
+
|
|
1887
|
+
# Create a list of candidates in the proper order and
|
|
1888
|
+
# assign the first one available.
|
|
1889
|
+
allocation.candidates(@scenarioIdx).each do |candidate|
|
|
1890
|
+
if bookResource(candidate)
|
|
1891
|
+
allocation.lockedResource = candidate if allocation.persistent
|
|
1892
|
+
break
|
|
1834
1893
|
end
|
|
1835
1894
|
end
|
|
1836
1895
|
end
|
|
@@ -1846,6 +1905,10 @@ class TaskJuggler
|
|
|
1846
1905
|
@doneEffort >= @effort) || !limitsOk?(@currentSlotIdx, r)
|
|
1847
1906
|
|
|
1848
1907
|
if r.book(@scenarioIdx, @currentSlotIdx, @property)
|
|
1908
|
+
# This method is _very_ performance sensitive. Uncomment this log
|
|
1909
|
+
# message only if you really need it.
|
|
1910
|
+
#Log.msg { "Book #{resource.name} on task #{@property.fullId}" }
|
|
1911
|
+
|
|
1849
1912
|
# For effort based task we adjust the the start end (as defined by
|
|
1850
1913
|
# the scheduling direction) to align with the first booked time
|
|
1851
1914
|
# slot.
|
|
@@ -1878,6 +1941,11 @@ class TaskJuggler
|
|
|
1878
1941
|
@assignedresources << r
|
|
1879
1942
|
end
|
|
1880
1943
|
booked = true
|
|
1944
|
+
elsif (competitor = r.bookedTask(@scenarioIdx, @currentSlotIdx))
|
|
1945
|
+
# Keep a list of all the Tasks that have successfully competed for
|
|
1946
|
+
# the same resources and are potentially delaying the progress of
|
|
1947
|
+
# this Task.
|
|
1948
|
+
@competitors << competitor unless @competitors.include?(competitor)
|
|
1881
1949
|
end
|
|
1882
1950
|
end
|
|
1883
1951
|
|
|
@@ -1966,7 +2034,7 @@ class TaskJuggler
|
|
|
1966
2034
|
if @effort > 0
|
|
1967
2035
|
if @doneEffort >= @effort
|
|
1968
2036
|
@end = tentativeEnd
|
|
1969
|
-
|
|
2037
|
+
markAsScheduled
|
|
1970
2038
|
end
|
|
1971
2039
|
elsif @length > 0
|
|
1972
2040
|
@doneLength = 0
|
|
@@ -1980,7 +2048,7 @@ class TaskJuggler
|
|
|
1980
2048
|
if @doneLength >= @length && date >= tentativeEnd
|
|
1981
2049
|
endDate = @project.idxToDate(idx + 1)
|
|
1982
2050
|
@end = [ endDate, tentativeEnd ].max
|
|
1983
|
-
|
|
2051
|
+
markAsScheduled
|
|
1984
2052
|
break
|
|
1985
2053
|
end
|
|
1986
2054
|
end
|
|
@@ -1988,13 +2056,13 @@ class TaskJuggler
|
|
|
1988
2056
|
@doneDuration = ((tentativeEnd - @start) / slotDuration).to_i
|
|
1989
2057
|
if @doneDuration >= @duration
|
|
1990
2058
|
@end = tentativeEnd
|
|
1991
|
-
|
|
2059
|
+
markAsScheduled
|
|
1992
2060
|
elsif @duration * slotDuration < (@project['now'] - @start)
|
|
1993
2061
|
# This handles the case where the bookings don't provide enough
|
|
1994
2062
|
# @doneDuration to reach @duration, but the now date would be
|
|
1995
2063
|
# after the @start + @duration date.
|
|
1996
2064
|
@end = @start + @duration * slotDuration
|
|
1997
|
-
|
|
2065
|
+
markAsScheduled
|
|
1998
2066
|
end
|
|
1999
2067
|
end
|
|
2000
2068
|
end
|
|
@@ -2041,6 +2109,19 @@ class TaskJuggler
|
|
|
2041
2109
|
end
|
|
2042
2110
|
end
|
|
2043
2111
|
|
|
2112
|
+
def rollbackBookings
|
|
2113
|
+
@doneEffort = 0.0
|
|
2114
|
+
|
|
2115
|
+
@allocate.each do |allocation|
|
|
2116
|
+
allocation.lockedResource = nil
|
|
2117
|
+
allocation.candidates(@scenarioIdx).each do |resource|
|
|
2118
|
+
resource.allLeaves.each do |r|
|
|
2119
|
+
r.rollbackBookings(@scenarioIdx, @property)
|
|
2120
|
+
end
|
|
2121
|
+
end
|
|
2122
|
+
end
|
|
2123
|
+
end
|
|
2124
|
+
|
|
2044
2125
|
# This function checks if the task has a dependency on another task or
|
|
2045
2126
|
# fixed date for a certain end. If +atEnd+ is true, the task end will be
|
|
2046
2127
|
# checked. Otherwise the start.
|
|
@@ -2096,7 +2177,7 @@ class TaskJuggler
|
|
|
2096
2177
|
|
|
2097
2178
|
# This function determines if a task is a milestones and marks it
|
|
2098
2179
|
# accordingly.
|
|
2099
|
-
def
|
|
2180
|
+
def markAsMilestone
|
|
2100
2181
|
return if @property.container? || @hasDurationSpec ||
|
|
2101
2182
|
!@booking.empty? || !@allocate.empty?
|
|
2102
2183
|
|
|
@@ -2123,6 +2204,7 @@ class TaskJuggler
|
|
|
2123
2204
|
@hasDurationSpec = true
|
|
2124
2205
|
@end = @start if @start && !@end
|
|
2125
2206
|
@start = @end if !@start && @end
|
|
2207
|
+
Log.msg { "Mark as milestone #{@property.fullId}" }
|
|
2126
2208
|
end
|
|
2127
2209
|
end
|
|
2128
2210
|
|
|
@@ -2390,10 +2472,11 @@ class TaskJuggler
|
|
|
2390
2472
|
|
|
2391
2473
|
# Recursively compile a list of Task properties which depend on the
|
|
2392
2474
|
# current task.
|
|
2393
|
-
def inputs(foundInputs, includeChildren, checkedTasks =
|
|
2475
|
+
def inputs(foundInputs, includeChildren, checkedTasks = {})
|
|
2394
2476
|
# Ignore tasks that we have already included in the checked tasks list.
|
|
2395
|
-
|
|
2396
|
-
|
|
2477
|
+
taskSignature = [ @property, includeChildren ]
|
|
2478
|
+
return if checkedTasks.include?(taskSignature)
|
|
2479
|
+
checkedTasks[taskSignature] = true
|
|
2397
2480
|
|
|
2398
2481
|
# An "input" must be a leaf task that has no direct or indirect (through
|
|
2399
2482
|
# parent) following tasks. Only milestones are recognized as inputs.
|
|
@@ -2424,10 +2507,11 @@ class TaskJuggler
|
|
|
2424
2507
|
|
|
2425
2508
|
# Recursively compile a list of Task properties which depend on the
|
|
2426
2509
|
# current task.
|
|
2427
|
-
def targets(foundTargets, includeChildren, checkedTasks =
|
|
2510
|
+
def targets(foundTargets, includeChildren, checkedTasks = {})
|
|
2428
2511
|
# Ignore tasks that we have already included in the checked tasks list.
|
|
2429
|
-
|
|
2430
|
-
|
|
2512
|
+
taskSignature = [ @property, includeChildren ]
|
|
2513
|
+
return if checkedTasks.include?(taskSignature)
|
|
2514
|
+
checkedTasks[taskSignature] = true
|
|
2431
2515
|
|
|
2432
2516
|
# A target must be a leaf function that has no direct or indirect
|
|
2433
2517
|
# (through parent) following tasks. Only milestones are recognized as
|