taskjuggler 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +37 -5
- data/README.rdoc +4 -2
- data/benchmarks/UTF-8-Strings.rb +58 -0
- data/benchmarks/css/tjmanual.css +0 -1
- data/benchmarks/htmltaskreport-1.html +47702 -0
- data/benchmarks/htmltaskreport-2.html +23345 -0
- data/benchmarks/htmltaskreport-3.html +23265 -0
- data/benchmarks/htmltaskreport-4.html +22135 -0
- data/benchmarks/profile.clt +36082 -0
- data/benchmarks/tj3.profile +0 -0
- data/benchmarks/tj3.profile.grind +6185 -0
- data/benchmarks/tj3.profile.symbols +432 -0
- data/benchmarks/tj3.profile.txt +448 -0
- data/data/tjp.vim +689 -0
- data/doc/AppConfig.html +26 -2
- data/doc/CHANGELOG.html +60 -8
- data/doc/COPYING.html +26 -2
- data/doc/Diff/Hunk.html +26 -2
- data/doc/Diff.html +26 -2
- data/doc/Diffable.html +26 -2
- data/doc/DiffableString.html +26 -2
- data/doc/Object.html +26 -104
- data/doc/README_rdoc.html +39 -7
- data/doc/RuntimeConfig.html +26 -2
- data/doc/String.html +26 -2
- data/doc/TaskJuggler/Account.html +27 -3
- data/doc/TaskJuggler/AccountAttribute.html +30 -6
- data/doc/TaskJuggler/AccountScenario.html +28 -4
- data/doc/TaskJuggler/Allocation.html +33 -9
- data/doc/TaskJuggler/AllocationAttribute.html +31 -7
- data/doc/TaskJuggler/AttributeBase.html +162 -111
- data/doc/TaskJuggler/AttributeDefinition.html +26 -2
- data/doc/TaskJuggler/AttributeOverwrite.html +26 -2
- data/doc/TaskJuggler/BatchProcessor.html +26 -2
- data/doc/TaskJuggler/Booking.html +29 -5
- data/doc/TaskJuggler/BookingListAttribute.html +29 -5
- data/doc/TaskJuggler/BooleanAttribute.html +30 -6
- data/doc/TaskJuggler/CSVFile.html +26 -2
- data/doc/TaskJuggler/CellSettingPattern.html +26 -2
- data/doc/TaskJuggler/CellSettingPatternList.html +26 -2
- data/doc/TaskJuggler/Charge.html +30 -6
- data/doc/TaskJuggler/ChargeListAttribute.html +29 -5
- data/doc/TaskJuggler/ChargeSet.html +26 -2
- data/doc/TaskJuggler/ChargeSetListAttribute.html +30 -6
- data/doc/TaskJuggler/CollisionDetector.html +26 -2
- data/doc/TaskJuggler/ColumnListAttribute.html +28 -4
- data/doc/TaskJuggler/ColumnTable.html +26 -2
- data/doc/TaskJuggler/Daemon.html +26 -2
- data/doc/TaskJuggler/DataCache.html +26 -2
- data/doc/TaskJuggler/DataCacheEntry.html +26 -2
- data/doc/TaskJuggler/DateAttribute.html +30 -6
- data/doc/TaskJuggler/DefinitionListAttribute.html +28 -4
- data/doc/TaskJuggler/DependencyListAttribute.html +30 -6
- data/doc/TaskJuggler/DurationAttribute.html +31 -7
- data/doc/TaskJuggler/FileList.html +26 -2
- data/doc/TaskJuggler/FileRecord.html +26 -2
- data/doc/TaskJuggler/FixnumAttribute.html +28 -4
- data/doc/TaskJuggler/FlagListAttribute.html +30 -6
- data/doc/TaskJuggler/FloatAttribute.html +29 -5
- data/doc/TaskJuggler/FormatListAttribute.html +29 -5
- data/doc/TaskJuggler/GanttChart.html +26 -2
- data/doc/TaskJuggler/GanttContainer.html +26 -2
- data/doc/TaskJuggler/GanttHeader.html +26 -2
- data/doc/TaskJuggler/GanttHeaderScaleItem.html +26 -2
- data/doc/TaskJuggler/GanttLine.html +27 -3
- data/doc/TaskJuggler/GanttLoadStack.html +26 -2
- data/doc/TaskJuggler/GanttMilestone.html +26 -2
- data/doc/TaskJuggler/GanttRouter.html +26 -2
- data/doc/TaskJuggler/GanttTaskBar.html +26 -2
- data/doc/TaskJuggler/HTMLDocument.html +26 -2
- data/doc/TaskJuggler/HTMLElements.html +623 -0
- data/doc/TaskJuggler/HTMLGraphics.html +26 -2
- data/doc/TaskJuggler/ICalReport.html +864 -0
- data/doc/TaskJuggler/ICalendar/Component.html +969 -0
- data/doc/TaskJuggler/ICalendar/Event.html +742 -0
- data/doc/TaskJuggler/ICalendar/Journal.html +722 -0
- data/doc/TaskJuggler/ICalendar/Person.html +633 -0
- data/doc/TaskJuggler/ICalendar/Todo.html +789 -0
- data/doc/TaskJuggler/ICalendar.html +1035 -0
- data/doc/TaskJuggler/Interval.html +99 -172
- data/doc/TaskJuggler/IntervalList.html +31 -7
- data/doc/TaskJuggler/JobInfo.html +26 -2
- data/doc/TaskJuggler/Journal.html +26 -2
- data/doc/TaskJuggler/JournalEntry.html +26 -2
- data/doc/TaskJuggler/JournalEntryList.html +26 -2
- data/doc/TaskJuggler/KeywordArray.html +26 -2
- data/doc/TaskJuggler/KeywordDocumentation.html +26 -2
- data/doc/TaskJuggler/Limits/Limit.html +124 -49
- data/doc/TaskJuggler/Limits.html +139 -156
- data/doc/TaskJuggler/LimitsAttribute.html +39 -14
- data/doc/TaskJuggler/ListAttributeBase.html +35 -11
- data/doc/TaskJuggler/Log.html +26 -2
- data/doc/TaskJuggler/LogFile.html +26 -2
- data/doc/TaskJuggler/LogicalAttribute.html +26 -2
- data/doc/TaskJuggler/LogicalExpression.html +26 -2
- data/doc/TaskJuggler/LogicalExpressionAttribute.html +35 -11
- data/doc/TaskJuggler/LogicalExpressionListAttribute.html +35 -11
- data/doc/TaskJuggler/LogicalFlag.html +26 -2
- data/doc/TaskJuggler/LogicalFunction.html +28 -4
- data/doc/TaskJuggler/LogicalOperation.html +26 -2
- data/doc/TaskJuggler/ManagerResponsibilities.html +26 -2
- data/doc/TaskJuggler/ManagerStatusRecord.html +26 -2
- data/doc/TaskJuggler/Message.html +26 -2
- data/doc/TaskJuggler/MessageHandler.html +108 -62
- data/doc/TaskJuggler/Navigator.html +26 -2
- data/doc/TaskJuggler/NavigatorElement.html +26 -2
- data/doc/TaskJuggler/NikuProject.html +26 -2
- data/doc/TaskJuggler/NikuReport.html +27 -3
- data/doc/TaskJuggler/NikuResource.html +26 -2
- data/doc/TaskJuggler/NodeListAttribute.html +31 -7
- data/doc/TaskJuggler/PlaceHolderCell.html +26 -2
- data/doc/TaskJuggler/ProcessIntercom.html +26 -2
- data/doc/TaskJuggler/ProcessIntercomIface.html +26 -2
- data/doc/TaskJuggler/Project.html +1236 -973
- data/doc/TaskJuggler/ProjectBroker.html +26 -2
- data/doc/TaskJuggler/ProjectBrokerIface.html +26 -2
- data/doc/TaskJuggler/ProjectFileParser.html +29 -4
- data/doc/TaskJuggler/ProjectFileScanner.html +56 -31
- data/doc/TaskJuggler/ProjectRecord.html +26 -2
- data/doc/TaskJuggler/ProjectServer.html +26 -2
- data/doc/TaskJuggler/ProjectServerIface.html +26 -2
- data/doc/TaskJuggler/PropertyAttribute.html +35 -11
- data/doc/TaskJuggler/PropertyList.html +26 -2
- data/doc/TaskJuggler/PropertySet.html +126 -106
- data/doc/TaskJuggler/PropertyTreeNode.html +595 -551
- data/doc/TaskJuggler/Query.html +127 -100
- data/doc/TaskJuggler/RTFHandlers.html +26 -2
- data/doc/TaskJuggler/RTFNavigator.html +26 -2
- data/doc/TaskJuggler/RTFQuery.html +28 -4
- data/doc/TaskJuggler/RTFReport.html +26 -2
- data/doc/TaskJuggler/RTFReportLink.html +26 -2
- data/doc/TaskJuggler/RTFWithQuerySupport.html +26 -2
- data/doc/TaskJuggler/RealFormat.html +26 -2
- data/doc/TaskJuggler/RealFormatAttribute.html +31 -7
- data/doc/TaskJuggler/ReferenceAttribute.html +58 -34
- data/doc/TaskJuggler/Report.html +427 -284
- data/doc/TaskJuggler/ReportBase.html +28 -102
- data/doc/TaskJuggler/ReportContext.html +26 -2
- data/doc/TaskJuggler/ReportScenario.html +633 -0
- data/doc/TaskJuggler/ReportServer.html +26 -2
- data/doc/TaskJuggler/ReportServerIface.html +26 -2
- data/doc/TaskJuggler/ReportServerRecord.html +26 -2
- data/doc/TaskJuggler/ReportServlet.html +26 -2
- data/doc/TaskJuggler/ReportTable.html +26 -2
- data/doc/TaskJuggler/ReportTableCell.html +26 -2
- data/doc/TaskJuggler/ReportTableColumn.html +26 -2
- data/doc/TaskJuggler/ReportTableLegend.html +26 -2
- data/doc/TaskJuggler/ReportTableLine.html +26 -2
- data/doc/TaskJuggler/Resource.html +225 -163
- data/doc/TaskJuggler/ResourceListAttribute.html +71 -47
- data/doc/TaskJuggler/ResourceListRE.html +26 -2
- data/doc/TaskJuggler/ResourceScenario.html +645 -603
- data/doc/TaskJuggler/RichText.html +26 -2
- data/doc/TaskJuggler/RichTextAttribute.html +53 -29
- data/doc/TaskJuggler/RichTextDocument.html +26 -2
- data/doc/TaskJuggler/RichTextElement.html +26 -2
- data/doc/TaskJuggler/RichTextFunctionExample.html +26 -2
- data/doc/TaskJuggler/RichTextFunctionHandler.html +26 -2
- data/doc/TaskJuggler/RichTextImage.html +26 -2
- data/doc/TaskJuggler/RichTextIntermediate.html +29 -5
- data/doc/TaskJuggler/RichTextParser.html +26 -2
- data/doc/TaskJuggler/RichTextScanner.html +26 -2
- data/doc/TaskJuggler/RichTextSnip.html +26 -2
- data/doc/TaskJuggler/RichTextSyntaxRules.html +26 -2
- data/doc/TaskJuggler/Scenario.html +26 -2
- data/doc/TaskJuggler/ScenarioData.html +95 -28
- data/doc/TaskJuggler/ScenarioListAttribute.html +39 -15
- data/doc/TaskJuggler/Scoreboard.html +137 -73
- data/doc/TaskJuggler/ScoreboardInterval.html +1116 -0
- data/doc/TaskJuggler/SheetHandlerBase.html +26 -2
- data/doc/TaskJuggler/SheetReceiver.html +26 -2
- data/doc/TaskJuggler/SheetSender.html +26 -2
- data/doc/TaskJuggler/Shift.html +27 -3
- data/doc/TaskJuggler/ShiftAssignment.html +27 -3
- data/doc/TaskJuggler/ShiftAssignments.html +174 -149
- data/doc/TaskJuggler/ShiftAssignmentsAttribute.html +41 -16
- data/doc/TaskJuggler/ShiftScenario.html +26 -2
- data/doc/TaskJuggler/SimpleQueryExpander.html +26 -2
- data/doc/TaskJuggler/SortListAttribute.html +35 -11
- data/doc/TaskJuggler/StatusSheetReceiver.html +26 -2
- data/doc/TaskJuggler/StatusSheetReport.html +26 -2
- data/doc/TaskJuggler/StatusSheetSender.html +26 -2
- data/doc/TaskJuggler/StdIoWrapper.html +26 -2
- data/doc/TaskJuggler/StringAttribute.html +39 -15
- data/doc/TaskJuggler/SymbolAttribute.html +35 -11
- data/doc/TaskJuggler/SyntaxReference.html +26 -2
- data/doc/TaskJuggler/TOCEntry.html +26 -2
- data/doc/TaskJuggler/TSResourceRecord.html +26 -2
- data/doc/TaskJuggler/TSTaskRecord.html +26 -2
- data/doc/TaskJuggler/TableColumnDefinition.html +26 -2
- data/doc/TaskJuggler/TableOfContents.html +26 -2
- data/doc/TaskJuggler/TableReport.html +239 -214
- data/doc/TaskJuggler/TagFile/TagFileEntry.html +841 -0
- data/doc/TaskJuggler/TagFile.html +817 -0
- data/doc/TaskJuggler/Task.html +27 -3
- data/doc/TaskJuggler/TaskDepListAttribute.html +47 -23
- data/doc/TaskJuggler/TaskDependency.html +26 -2
- data/doc/TaskJuggler/TaskListAttribute.html +47 -23
- data/doc/TaskJuggler/TaskListRE.html +26 -2
- data/doc/TaskJuggler/TaskScenario.html +2235 -2178
- data/doc/TaskJuggler/TernarySearchTree.html +26 -2
- data/doc/TaskJuggler/TextFormatter.html +26 -2
- data/doc/TaskJuggler/TextParser/Macro.html +26 -2
- data/doc/TaskJuggler/TextParser/MacroTable.html +26 -2
- data/doc/TaskJuggler/TextParser/Pattern.html +26 -2
- data/doc/TaskJuggler/TextParser/Rule.html +26 -2
- data/doc/TaskJuggler/TextParser/Scanner/BufferStreamHandle.html +32 -8
- data/doc/TaskJuggler/TextParser/Scanner/FileStreamHandle.html +39 -15
- data/doc/TaskJuggler/TextParser/Scanner/MacroStackEntry.html +26 -2
- data/doc/TaskJuggler/TextParser/Scanner/StreamHandle.html +119 -86
- data/doc/TaskJuggler/TextParser/Scanner.html +317 -291
- data/doc/TaskJuggler/TextParser/SourceFileInfo.html +26 -2
- data/doc/TaskJuggler/TextParser/StackElement.html +26 -2
- data/doc/TaskJuggler/TextParser/State.html +26 -2
- data/doc/TaskJuggler/TextParser/StateTransition.html +26 -2
- data/doc/TaskJuggler/TextParser/TextParserResultArray.html +26 -2
- data/doc/TaskJuggler/TextParser/TokenDoc.html +26 -2
- data/doc/TaskJuggler/TextParser.html +85 -61
- data/doc/TaskJuggler/TextReport.html +26 -2
- data/doc/TaskJuggler/TimeInterval.html +841 -0
- data/doc/TaskJuggler/{IntervalListAttribute.html → TimeIntervalListAttribute.html} +33 -9
- data/doc/TaskJuggler/TimeSheet.html +26 -2
- data/doc/TaskJuggler/TimeSheetReceiver.html +26 -2
- data/doc/TaskJuggler/TimeSheetRecord.html +27 -3
- data/doc/TaskJuggler/TimeSheetReport.html +29 -5
- data/doc/TaskJuggler/TimeSheetSender.html +26 -2
- data/doc/TaskJuggler/TimeSheetSummary.html +26 -2
- data/doc/TaskJuggler/TimeSheets.html +26 -2
- data/doc/TaskJuggler/Tj3.html +150 -111
- data/doc/TaskJuggler/Tj3AppBase.html +26 -2
- data/doc/TaskJuggler/Tj3Client.html +26 -2
- data/doc/TaskJuggler/Tj3Daemon.html +26 -2
- data/doc/TaskJuggler/Tj3Man.html +26 -2
- data/doc/TaskJuggler/Tj3SheetAppBase.html +26 -2
- data/doc/TaskJuggler/Tj3SsReceiver.html +26 -2
- data/doc/TaskJuggler/Tj3SsSender.html +26 -2
- data/doc/TaskJuggler/Tj3TsReceiver.html +26 -2
- data/doc/TaskJuggler/Tj3TsSender.html +26 -2
- data/doc/TaskJuggler/Tj3TsSummary.html +26 -2
- data/doc/TaskJuggler/TjException.html +26 -2
- data/doc/TaskJuggler/TjRuntimeError.html +26 -2
- data/doc/TaskJuggler/TjTime.html +539 -477
- data/doc/TaskJuggler/TjpExample.html +26 -2
- data/doc/TaskJuggler/TjpExportRE.html +240 -161
- data/doc/TaskJuggler/TjpSyntaxRules.html +4236 -3844
- data/doc/TaskJuggler/URLParameter.html +26 -2
- data/doc/TaskJuggler/UserManual.html +26 -2
- data/doc/TaskJuggler/VimSyntax.html +31 -7
- data/doc/TaskJuggler/WebServer.html +26 -2
- data/doc/TaskJuggler/WelcomePage.html +26 -2
- data/doc/TaskJuggler/WorkingHours.html +227 -152
- data/doc/TaskJuggler/WorkingHoursAttribute.html +62 -38
- data/doc/TaskJuggler/XMLBlob.html +26 -2
- data/doc/TaskJuggler/XMLComment.html +26 -2
- data/doc/TaskJuggler/XMLDocument.html +26 -2
- data/doc/TaskJuggler/XMLElement.html +26 -2
- data/doc/TaskJuggler/XMLNamedText.html +26 -2
- data/doc/TaskJuggler/XMLText.html +26 -2
- data/doc/TaskJuggler.html +486 -427
- data/doc/index.html +801 -659
- data/doc/lib/taskjuggler/AccountScenario_rb.html +1 -1
- data/doc/lib/taskjuggler/Account_rb.html +1 -1
- data/doc/lib/taskjuggler/AlgorithmDiff_rb.html +1 -1
- data/doc/lib/taskjuggler/Allocation_rb.html +1 -1
- data/doc/lib/taskjuggler/AppConfig_rb.html +1 -1
- data/doc/lib/taskjuggler/AttributeBase_rb.html +1 -1
- data/doc/lib/taskjuggler/AttributeDefinition_rb.html +1 -1
- data/doc/lib/taskjuggler/Attributes_rb.html +1 -1
- data/doc/lib/taskjuggler/BatchProcessor_rb.html +1 -1
- data/doc/lib/taskjuggler/Booking_rb.html +1 -1
- data/doc/lib/taskjuggler/ChargeSet_rb.html +1 -1
- data/doc/lib/taskjuggler/Charge_rb.html +1 -1
- data/doc/lib/taskjuggler/DataCache_rb.html +1 -1
- data/doc/lib/taskjuggler/FileList_rb.html +1 -1
- data/doc/lib/taskjuggler/HTMLDocument_rb.html +1 -1
- data/doc/lib/{ruby-signal-bug_rb.html → taskjuggler/HTMLElements_rb.html} +9 -9
- data/doc/lib/{exchangebug_rb.html → taskjuggler/ICalendar_rb.html} +9 -9
- data/doc/lib/taskjuggler/IntervalList_rb.html +1 -1
- data/doc/lib/taskjuggler/Interval_rb.html +1 -1
- data/doc/lib/taskjuggler/Journal_rb.html +1 -1
- data/doc/lib/taskjuggler/KeywordArray_rb.html +1 -1
- data/doc/lib/taskjuggler/KeywordDocumentation_rb.html +1 -1
- data/doc/lib/taskjuggler/Limits_rb.html +1 -1
- data/doc/lib/taskjuggler/LogFile_rb.html +1 -1
- data/doc/lib/taskjuggler/Log_rb.html +1 -1
- data/doc/lib/taskjuggler/LogicalExpression_rb.html +1 -1
- data/doc/lib/taskjuggler/LogicalFunction_rb.html +1 -1
- data/doc/lib/taskjuggler/LogicalOperation_rb.html +1 -1
- data/doc/lib/taskjuggler/MessageHandler_rb.html +1 -1
- data/doc/lib/taskjuggler/ProjectFileParser_rb.html +1 -1
- data/doc/lib/taskjuggler/ProjectFileScanner_rb.html +1 -1
- data/doc/lib/taskjuggler/Project_rb.html +1 -1
- data/doc/lib/taskjuggler/PropertyList_rb.html +1 -1
- data/doc/lib/taskjuggler/PropertySet_rb.html +1 -1
- data/doc/lib/taskjuggler/PropertyTreeNode_rb.html +1 -1
- data/doc/lib/taskjuggler/Query_rb.html +1 -1
- data/doc/lib/taskjuggler/RealFormat_rb.html +1 -1
- data/doc/lib/taskjuggler/ResourceScenario_rb.html +1 -1
- data/doc/lib/taskjuggler/Resource_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/Document_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/Element_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/FunctionExample_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/FunctionHandler_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/Parser_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/RTFHandlers_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/RTFNavigator_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/RTFQuery_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/RTFReportLink_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/RTFReport_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/RTFWithQuerySupport_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/Scanner_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/Snip_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/SyntaxRules_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/TOCEntry_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText/TableOfContents_rb.html +1 -1
- data/doc/lib/taskjuggler/RichText_rb.html +1 -1
- data/doc/lib/taskjuggler/RuntimeConfig_rb.html +1 -1
- data/doc/lib/taskjuggler/ScenarioData_rb.html +1 -1
- data/doc/lib/taskjuggler/Scenario_rb.html +1 -1
- data/doc/lib/taskjuggler/Scoreboard_rb.html +1 -1
- data/doc/lib/taskjuggler/SheetHandlerBase_rb.html +1 -1
- data/doc/lib/taskjuggler/SheetReceiver_rb.html +1 -1
- data/doc/lib/taskjuggler/SheetSender_rb.html +1 -1
- data/doc/lib/taskjuggler/ShiftAssignments_rb.html +1 -1
- data/doc/lib/taskjuggler/ShiftScenario_rb.html +1 -1
- data/doc/lib/taskjuggler/Shift_rb.html +1 -1
- data/doc/lib/taskjuggler/SimpleQueryExpander_rb.html +1 -1
- data/doc/lib/taskjuggler/StatusSheetReceiver_rb.html +1 -1
- data/doc/lib/taskjuggler/StatusSheetSender_rb.html +1 -1
- data/doc/lib/taskjuggler/StdIoWrapper_rb.html +1 -1
- data/doc/lib/taskjuggler/SyntaxReference_rb.html +1 -1
- data/doc/lib/taskjuggler/TableColumnDefinition_rb.html +1 -1
- data/doc/lib/taskjuggler/TaskDependency_rb.html +1 -1
- data/doc/lib/taskjuggler/TaskJuggler_rb.html +1 -1
- data/doc/lib/taskjuggler/TaskScenario_rb.html +1 -1
- data/doc/lib/taskjuggler/Task_rb.html +1 -1
- data/doc/lib/taskjuggler/TernarySearchTree_rb.html +1 -1
- data/doc/lib/taskjuggler/TextFormatter_rb.html +1 -1
- data/doc/lib/taskjuggler/TextParser/MacroTable_rb.html +1 -1
- data/doc/lib/taskjuggler/TextParser/Pattern_rb.html +1 -1
- data/doc/lib/taskjuggler/TextParser/Rule_rb.html +1 -1
- data/doc/lib/taskjuggler/TextParser/Scanner_rb.html +1 -1
- data/doc/lib/taskjuggler/TextParser/SourceFileInfo_rb.html +1 -1
- data/doc/lib/taskjuggler/TextParser/StackElement_rb.html +1 -1
- data/doc/lib/taskjuggler/TextParser/State_rb.html +1 -1
- data/doc/lib/taskjuggler/TextParser/TokenDoc_rb.html +1 -1
- data/doc/lib/taskjuggler/TextParser_rb.html +1 -1
- data/doc/lib/taskjuggler/TimeSheetReceiver_rb.html +1 -1
- data/doc/lib/taskjuggler/TimeSheetSender_rb.html +1 -1
- data/doc/lib/taskjuggler/TimeSheetSummary_rb.html +1 -1
- data/doc/lib/taskjuggler/TimeSheets_rb.html +1 -1
- data/doc/lib/taskjuggler/Tj3AppBase_rb.html +1 -1
- data/doc/lib/taskjuggler/Tj3Config_rb.html +1 -1
- data/doc/lib/taskjuggler/Tj3SheetAppBase_rb.html +1 -1
- data/doc/lib/taskjuggler/TjException_rb.html +1 -1
- data/doc/lib/taskjuggler/TjTime_rb.html +1 -1
- data/doc/lib/taskjuggler/TjpExample_rb.html +1 -1
- data/doc/lib/taskjuggler/TjpSyntaxRules_rb.html +1 -1
- data/doc/lib/taskjuggler/URLParameter_rb.html +1 -1
- data/doc/lib/taskjuggler/UTF8String_rb.html +1 -1
- data/doc/lib/taskjuggler/UserManual_rb.html +1 -1
- data/doc/lib/taskjuggler/VimSyntax_rb.html +1 -1
- data/doc/lib/taskjuggler/WorkingHours_rb.html +1 -1
- data/doc/lib/taskjuggler/XMLDocument_rb.html +1 -1
- data/doc/lib/taskjuggler/XMLElement_rb.html +1 -1
- data/doc/lib/taskjuggler/apps/Tj3Client_rb.html +1 -1
- data/doc/lib/taskjuggler/apps/Tj3Daemon_rb.html +1 -1
- data/doc/lib/taskjuggler/apps/Tj3Man_rb.html +1 -1
- data/doc/lib/taskjuggler/apps/Tj3SsReceiver_rb.html +1 -1
- data/doc/lib/taskjuggler/apps/Tj3SsSender_rb.html +1 -1
- data/doc/lib/taskjuggler/apps/Tj3TsReceiver_rb.html +1 -1
- data/doc/lib/taskjuggler/apps/Tj3TsSender_rb.html +1 -1
- data/doc/lib/taskjuggler/apps/Tj3TsSummary_rb.html +1 -1
- data/doc/lib/taskjuggler/apps/Tj3_rb.html +1 -1
- data/doc/lib/taskjuggler/daemon/Daemon_rb.html +1 -1
- data/doc/lib/taskjuggler/daemon/ProcessIntercom_rb.html +1 -1
- data/doc/lib/taskjuggler/daemon/ProjectBroker_rb.html +1 -1
- data/doc/lib/taskjuggler/daemon/ProjectServer_rb.html +1 -1
- data/doc/lib/taskjuggler/daemon/ReportServer_rb.html +1 -1
- data/doc/lib/taskjuggler/daemon/ReportServlet_rb.html +1 -1
- data/doc/lib/taskjuggler/daemon/WebServer_rb.html +1 -1
- data/doc/lib/taskjuggler/daemon/WelcomePage_rb.html +1 -1
- data/doc/lib/taskjuggler/deep_copy_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/CSVFile_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/CollisionDetector_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/ColumnTable_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/GanttChart_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/GanttContainer_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/GanttHeaderScaleItem_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/GanttHeader_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/GanttLine_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/GanttLoadStack_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/GanttMilestone_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/GanttRouter_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/GanttTaskBar_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/HTMLGraphics_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/ICalReport_rb.html +71 -0
- data/doc/lib/taskjuggler/reports/Navigator_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/NikuReport_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/ReportBase_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/ReportContext_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/ReportTableCell_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/ReportTableColumn_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/ReportTableLegend_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/ReportTableLine_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/ReportTable_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/Report_rb.html +5 -1
- data/doc/lib/taskjuggler/reports/ResourceListRE_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/StatusSheetReport_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/TableReport_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/TagFile_rb.html +71 -0
- data/doc/lib/taskjuggler/reports/TaskListRE_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/TextReport_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/TimeSheetReport_rb.html +1 -1
- data/doc/lib/taskjuggler/reports/TjpExportRE_rb.html +1 -1
- data/doc/lib/tj3_rb.html +1 -1
- data/doc/lib/tj3client_rb.html +1 -1
- data/doc/lib/tj3d_rb.html +1 -1
- data/doc/lib/tj3man_rb.html +1 -1
- data/doc/lib/tj3ss_receiver_rb.html +1 -1
- data/doc/lib/tj3ss_sender_rb.html +1 -1
- data/doc/lib/tj3ts_receiver_rb.html +1 -1
- data/doc/lib/tj3ts_sender_rb.html +1 -1
- data/doc/lib/tj3ts_summary_rb.html +1 -1
- data/lib/taskjuggler/Account.rb +1 -1
- data/lib/taskjuggler/AccountScenario.rb +1 -1
- data/lib/taskjuggler/Allocation.rb +5 -5
- data/lib/taskjuggler/AttributeBase.rb +30 -22
- data/lib/taskjuggler/Attributes.rb +80 -78
- data/lib/taskjuggler/Booking.rb +2 -2
- data/lib/taskjuggler/Charge.rb +3 -3
- data/lib/taskjuggler/ICalendar.rb +251 -0
- data/lib/taskjuggler/Interval.rb +215 -53
- data/lib/taskjuggler/IntervalList.rb +5 -5
- data/lib/taskjuggler/Limits.rb +79 -69
- data/lib/taskjuggler/LogicalFunction.rb +2 -2
- data/lib/taskjuggler/MessageHandler.rb +11 -6
- data/lib/taskjuggler/Project.rb +208 -95
- data/lib/taskjuggler/ProjectFileParser.rb +2 -2
- data/lib/taskjuggler/ProjectFileScanner.rb +7 -6
- data/lib/taskjuggler/PropertySet.rb +1 -5
- data/lib/taskjuggler/PropertyTreeNode.rb +21 -16
- data/lib/taskjuggler/Query.rb +10 -7
- data/lib/taskjuggler/Resource.rb +6 -1
- data/lib/taskjuggler/ResourceScenario.rb +85 -69
- data/lib/taskjuggler/RichText/RTFQuery.rb +2 -2
- data/lib/taskjuggler/ScenarioData.rb +11 -1
- data/lib/taskjuggler/Scoreboard.rb +8 -1
- data/lib/taskjuggler/Shift.rb +1 -1
- data/lib/taskjuggler/ShiftAssignments.rb +22 -20
- data/lib/taskjuggler/Task.rb +1 -1
- data/lib/taskjuggler/TaskJuggler.rb +65 -51
- data/lib/taskjuggler/TaskScenario.rb +486 -419
- data/lib/taskjuggler/TextParser/Scanner.rb +22 -11
- data/lib/taskjuggler/TimeSheets.rb +1 -1
- data/lib/taskjuggler/Tj3Config.rb +1 -1
- data/lib/taskjuggler/TjTime.rb +7 -3
- data/lib/taskjuggler/TjpSyntaxRules.rb +157 -23
- data/lib/taskjuggler/VimSyntax.rb +18 -0
- data/lib/taskjuggler/WorkingHours.rb +20 -4
- data/lib/taskjuggler/apps/Tj3.rb +16 -1
- data/lib/taskjuggler/reports/GanttLine.rb +1 -1
- data/lib/taskjuggler/reports/ICalReport.rb +136 -0
- data/lib/taskjuggler/reports/NikuReport.rb +1 -1
- data/lib/taskjuggler/reports/Report.rb +59 -0
- data/lib/taskjuggler/reports/ReportBase.rb +2 -67
- data/lib/taskjuggler/reports/TableReport.rb +9 -8
- data/lib/taskjuggler/reports/TagFile.rb +120 -0
- data/lib/taskjuggler/reports/TimeSheetReport.rb +3 -3
- data/lib/taskjuggler/reports/TjpExportRE.rb +33 -9
- data/manual/Installation +126 -0
- data/spec/ICalendar_spec.rb +45 -0
- data/spec/IntervalList_spec.rb +11 -11
- data/tasks/changelog.rake +150 -60
- data/tasks/gem.rake +1 -0
- data/tasks/vim.rake +2 -2
- data/test/TestSuite/Export-Reports/refs/Booking.tjp +1 -1
- data/test/TestSuite/Export-Reports/refs/CompletedWork.tji.tjp +40 -0
- data/test/TestSuite/Export-Reports/refs/Macro-2.tjp +10 -1
- data/test/TestSuite/Export-Reports/refs/TaskPrefix.tjp +1 -1
- data/test/TestSuite/HTML-Reports/Alerts.html +160 -412
- data/test/TestSuite/HTML-Reports/CellText.html +758 -0
- data/test/TestSuite/HTML-Reports/ColumnPeriods.html +156 -0
- data/test/TestSuite/HTML-Reports/IsOngoing.html +172 -0
- data/test/TestSuite/HTML-Reports/LogicalFunctions.html +245 -0
- data/test/TestSuite/HTML-Reports/Query.html +31 -0
- data/test/TestSuite/HTML-Reports/css/tjmanual.css +0 -20
- data/test/TestSuite/HTML-Reports/css/tjreport.css +6 -7
- data/test/TestSuite/HTML-Reports/depArrows.html +851 -0
- data/test/TestSuite/HTML-Reports/profile.html +37581 -0
- data/test/TestSuite/{Scheduler/Correct → HTML-Reports/scripts}/scripts/wz_tooltip.js +20 -20
- data/test/TestSuite/Scheduler/Correct/Booking.tjp +3 -3
- data/test/TestSuite/Scheduler/Correct/Duration.tjp +3 -3
- data/test/TestSuite/Scheduler/Errors/R.html +113 -0
- data/test/TestSuite/Scheduler/Errors/allocate_no_assigned.tjp +15 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/css/tjmanual.css +0 -1
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/css/tjreport.css +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/details.png +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/flag-green.png +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/flag-red.png +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/flag-yellow.png +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/resource.png +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/resourcegroup.png +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/task.png +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/taskgroup.png +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/trend-down.png +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/trend-flat.png +0 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/icons/trend-up.png +0 -0
- data/test/TestSuite/Scheduler/Errors/overbooked_duration.tjp +11 -0
- data/test/TestSuite/Scheduler/Errors/overbooked_effort.tjp +11 -0
- data/test/TestSuite/Scheduler/Errors/overbooked_length.tjp +13 -0
- data/test/TestSuite/{ReportGenerator/Correct → Scheduler/Errors}/scripts/wz_tooltip.js +0 -0
- data/test/TestSuite/StatusSheets/dev2.tji +22 -0
- data/test/TestSuite/Syntax/Correct/CompletedWork.tji.tjp +38 -0
- data/test/TestSuite/Syntax/Correct/Macro-2.tjp +5 -2
- data/test/TestSuite/Syntax/Errors/scenario_after_tracking.tjp +8 -0
- data/test/TestSuite/TimeSheets/resrep.tji +7 -0
- data/test/TestSuite/TimeSheets/ts.tji +351 -0
- data/test/TestSuite/TimeSheets/tsdef.tji +2 -0
- data/test/test_Limits.rb +38 -30
- data/test/test_Scheduler.rb +2 -2
- data/test/test_ShiftAssignments.rb +8 -8
- data/test/test_WorkingHours.rb +4 -4
- metadata +1015 -1005
- data/lib/exchangebug.rb +0 -42
- data/lib/ruby-signal-bug.rb +0 -55
- data/test/TestSuite/Export-Reports/refs/DST.tjp +0 -60
- data/test/TestSuite/Export-Reports/refs/ReleasePlan.tjp +0 -80
- data/test/TestSuite/HTML-Reports/TimeSheet.html +0 -79
- data/test/TestSuite/HTML-Reports/reference.html +0 -51
- data/test/TestSuite/ReportGenerator/Correct/Alerts-1.csv +0 -412
- data/test/TestSuite/ReportGenerator/Correct/Alerts-1.html +0 -680
- data/test/TestSuite/ReportGenerator/Correct/DependencyList.csv +0 -4
- data/test/TestSuite/ReportGenerator/Correct/DependencyList.tjp +0 -25
- data/test/TestSuite/ReportGenerator/Correct/Journal-1.csv +0 -8
- data/test/TestSuite/ReportGenerator/Correct/Journal-2.csv +0 -8
- data/test/TestSuite/ReportGenerator/Correct/Journal-3.html +0 -28
- data/test/TestSuite/ReportGenerator/Correct/LogicalFunctions2.csv +0 -3
- data/test/TestSuite/ReportGenerator/Correct/opennodes-1.csv +0 -2
- data/test/TestSuite/ReportGenerator/Correct/opennodes.csv +0 -2
- data/test/TestSuite/ReportGenerator/Correct/opennodes.tjp +0 -26
- data/test/TestSuite/ReportGenerator/Correct/refs/opennodes-1.csv +0 -2
- data/test/TestSuite/Scheduler/' +0 -23
- data/test/TestSuite/Scheduler/Correct/css/tjmanual.css +0 -86
- data/test/TestSuite/Scheduler/Correct/css/tjreport.css +0 -413
- data/test/TestSuite/Scheduler/Correct/icons/details.png +0 -0
- data/test/TestSuite/Scheduler/Correct/icons/flag-green.png +0 -0
- data/test/TestSuite/Scheduler/Correct/icons/flag-red.png +0 -0
- data/test/TestSuite/Scheduler/Correct/icons/flag-yellow.png +0 -0
- data/test/TestSuite/Scheduler/Correct/icons/resource.png +0 -0
- data/test/TestSuite/Scheduler/Correct/icons/resourcegroup.png +0 -0
- data/test/TestSuite/Scheduler/Correct/icons/task.png +0 -0
- data/test/TestSuite/Scheduler/Correct/icons/taskgroup.png +0 -0
- data/test/TestSuite/Scheduler/Correct/icons/trend-down.png +0 -0
- data/test/TestSuite/Scheduler/Correct/icons/trend-flat.png +0 -0
- data/test/TestSuite/Scheduler/Correct/icons/trend-up.png +0 -0
- data/test/TestSuite/StatusSheetTemplates/project.tji +0 -35
- data/test/TestSuite/StatusSheetTemplates/project.tjp +0 -56
- data/test/TestSuite/StatusSheets/TimeSheets/2002-03-01/resource1_2002-03-01.tji +0 -0
- data/test/TestSuite/Syntax/Correct/DST.tjp +0 -11
- data/test/TestSuite/Syntax/Correct/ReleasePlan.tjp +0 -32
- data/test/TestSuite/TimeSheets/receiver.log.mod +0 -1056
- data/test/TestSuite/TimeSheets/uu.txt +0 -29
@@ -23,6 +23,15 @@ class TaskJuggler
|
|
23
23
|
# Create a new TaskScenario object.
|
24
24
|
def initialize(task, scenarioIdx, attributes)
|
25
25
|
super
|
26
|
+
# Attributed are only really created when they are accessed the first
|
27
|
+
# time. So make sure some needed attributes really exist so we don't
|
28
|
+
# have to check for existance each time we access them.
|
29
|
+
%w( allocate assignedresources booking charge chargeset complete
|
30
|
+
criticalness depends duration effort end forward length
|
31
|
+
maxend maxstart minend minstart milestone pathcriticalness
|
32
|
+
precedes scheduled shifts start status ).each do |attr|
|
33
|
+
@property[attr, @scenarioIdx]
|
34
|
+
end
|
26
35
|
|
27
36
|
# A list of all allocated leaf resources.
|
28
37
|
@candidates = []
|
@@ -33,59 +42,79 @@ class TaskJuggler
|
|
33
42
|
# scheduling.
|
34
43
|
def prepareScheduling
|
35
44
|
@property['startpreds', @scenarioIdx] = []
|
36
|
-
@property['startsuccs', @scenarioIdx] =[]
|
45
|
+
@property['startsuccs', @scenarioIdx] = []
|
37
46
|
@property['endpreds', @scenarioIdx] = []
|
38
47
|
@property['endsuccs', @scenarioIdx] = []
|
39
48
|
|
40
49
|
@isRunAway = false
|
41
50
|
|
42
|
-
#
|
43
|
-
@
|
51
|
+
# And as global scoreboard index
|
52
|
+
@currentSlotIdx = nil
|
44
53
|
# The 'done' variables count scheduled values in number of time slots.
|
45
54
|
@doneDuration = 0
|
46
55
|
@doneLength = 0
|
47
56
|
# Due to the 'efficiency' factor the effort slots must be a float.
|
48
57
|
@doneEffort = 0.0
|
49
58
|
|
59
|
+
@projectionMode = @project.scenario(@scenarioIdx).get('projection')
|
60
|
+
|
50
61
|
@startIsDetermed = nil
|
51
62
|
@endIsDetermed = nil
|
52
|
-
@tentativeStart = @tentativeEnd = nil
|
53
63
|
|
54
64
|
# To avoid multiple calls to propagateDate() we use these flags to know
|
55
65
|
# when we've done it already.
|
56
66
|
@startPropagated = false
|
57
67
|
@endPropagated = false
|
58
68
|
|
69
|
+
markMilestone
|
59
70
|
# Milestones may only have start or end date even when the 'scheduled'
|
60
71
|
# attribute is set. For further processing, we need to add the missing
|
61
72
|
# date.
|
62
|
-
if
|
63
|
-
@
|
64
|
-
@
|
65
|
-
|
73
|
+
if @milestone
|
74
|
+
@end = @start if @start && !@end
|
75
|
+
@start = @end if !@start && @end
|
76
|
+
end
|
77
|
+
|
78
|
+
# For start-end-tasks without allocation, we don't have to do
|
79
|
+
# anything but to set the 'scheduled' flag.
|
80
|
+
if @start && @end && @effort == 0 && @length == 0 && @duration == 0 &&
|
81
|
+
@allocate.empty?
|
82
|
+
@scheduled = true
|
83
|
+
Log << "Task #{@property.fullId}: #{@start} -> #{@end}"
|
66
84
|
end
|
67
85
|
|
68
86
|
# Collect the limits of this task and all parent tasks into a single
|
69
87
|
# Array.
|
70
|
-
@
|
88
|
+
@allLimits = []
|
71
89
|
task = @property
|
72
90
|
# Reset the counters of all limits of this task.
|
73
91
|
task['limits', @scenarioIdx].reset if task['limits', @scenarioIdx]
|
74
92
|
until task.nil?
|
75
93
|
if task['limits', @scenarioIdx]
|
76
|
-
@
|
94
|
+
@allLimits << task['limits', @scenarioIdx]
|
77
95
|
end
|
78
96
|
task = task.parent
|
79
97
|
end
|
80
98
|
|
81
99
|
# Collect the mandatory allocations.
|
82
100
|
@mandatories = []
|
83
|
-
|
101
|
+
@allocate.each do |allocation|
|
84
102
|
@mandatories << allocation if allocation.mandatory
|
103
|
+
allocation.lockedResource = nil
|
85
104
|
end
|
86
105
|
|
87
106
|
bookBookings
|
88
|
-
|
107
|
+
|
108
|
+
@durationType =
|
109
|
+
if @effort > 0
|
110
|
+
:effortTask
|
111
|
+
elsif @length > 0
|
112
|
+
:lengthTask
|
113
|
+
elsif @duration > 0
|
114
|
+
:durationTask
|
115
|
+
else
|
116
|
+
:startEndTask
|
117
|
+
end
|
89
118
|
end
|
90
119
|
|
91
120
|
# The parser only stores the full task IDs for each of the dependencies.
|
@@ -100,16 +129,16 @@ class TaskJuggler
|
|
100
129
|
# points to the dependent task and the boolean specifies whether the
|
101
130
|
# dependency originates from the end of the task or not.
|
102
131
|
def Xref
|
103
|
-
@
|
132
|
+
@depends.each do |dependency|
|
104
133
|
depTask = checkDependency(dependency, 'depends')
|
105
|
-
|
134
|
+
@startpreds.push([ depTask, dependency.onEnd ])
|
106
135
|
depTask[dependency.onEnd ? 'endsuccs' : 'startsuccs', @scenarioIdx].
|
107
136
|
push([ @property, false ])
|
108
137
|
end
|
109
138
|
|
110
|
-
@
|
139
|
+
@precedes.each do |dependency|
|
111
140
|
predTask = checkDependency(dependency, 'precedes')
|
112
|
-
|
141
|
+
@endsuccs.push([ predTask, dependency.onEnd ])
|
113
142
|
predTask[dependency.onEnd ? 'endpreds' : 'startpreds', @scenarioIdx].
|
114
143
|
push([@property, true ])
|
115
144
|
end
|
@@ -123,8 +152,8 @@ class TaskJuggler
|
|
123
152
|
|
124
153
|
def propagateInitialValues
|
125
154
|
unless @startPropagated
|
126
|
-
if
|
127
|
-
propagateDate(
|
155
|
+
if @start
|
156
|
+
propagateDate(@start, false)
|
128
157
|
elsif @property.parent.nil? &&
|
129
158
|
@property.canInheritDate?(@scenarioIdx, false)
|
130
159
|
propagateDate(@project['start'], false)
|
@@ -132,8 +161,8 @@ class TaskJuggler
|
|
132
161
|
end
|
133
162
|
|
134
163
|
unless @endPropagated
|
135
|
-
if
|
136
|
-
propagateDate(
|
164
|
+
if @end
|
165
|
+
propagateDate(@end, true)
|
137
166
|
elsif @property.parent.nil? &&
|
138
167
|
@property.canInheritDate?(@scenarioIdx, true)
|
139
168
|
propagateDate(@project['end'], true)
|
@@ -146,7 +175,7 @@ class TaskJuggler
|
|
146
175
|
def preScheduleCheck
|
147
176
|
# Accounts can have sub accounts added after being used in a chargetset.
|
148
177
|
# So we need to re-test here.
|
149
|
-
|
178
|
+
@chargeset.each do |chargeset|
|
150
179
|
chargeset.each do |account, share|
|
151
180
|
unless account.leaf?
|
152
181
|
error('account_no_leaf',
|
@@ -157,36 +186,36 @@ class TaskJuggler
|
|
157
186
|
|
158
187
|
# Leaf tasks can be turned into containers after bookings have been added.
|
159
188
|
# We need to check for this.
|
160
|
-
unless @property.leaf? ||
|
189
|
+
unless @property.leaf? || @booking.empty?
|
161
190
|
error('container_booking',
|
162
191
|
"Container task #{@property.fullId} may not have bookings.")
|
163
192
|
end
|
164
193
|
|
165
194
|
# Milestones may not have bookings.
|
166
|
-
if
|
195
|
+
if @milestone && !@booking.empty?
|
167
196
|
error('milestone_booking',
|
168
197
|
"Milestone #{@property.fullId} may not have bookings.")
|
169
198
|
end
|
170
199
|
|
171
200
|
# All 'scheduled' tasks must have a fixed start and end date.
|
172
|
-
if
|
201
|
+
if @scheduled && (@start.nil? || @end.nil?)
|
173
202
|
error('not_scheduled',
|
174
203
|
"Task #{@property.fullId} is marked as scheduled but does not " +
|
175
204
|
'have a fixed start and end date.')
|
176
205
|
end
|
177
206
|
|
178
207
|
# If an effort has been specified resources must be allocated as well.
|
179
|
-
if
|
208
|
+
if @effort > 0 && @allocate.empty?
|
180
209
|
error('effort_no_allocations',
|
181
210
|
"Task #{@property.fullId} has an effort but no resource " +
|
182
211
|
"allocations.")
|
183
212
|
end
|
184
213
|
|
185
214
|
durationSpecs = 0
|
186
|
-
durationSpecs += 1 if
|
187
|
-
durationSpecs += 1 if
|
188
|
-
durationSpecs += 1 if
|
189
|
-
durationSpecs += 1 if
|
215
|
+
durationSpecs += 1 if @effort > 0
|
216
|
+
durationSpecs += 1 if @length > 0
|
217
|
+
durationSpecs += 1 if @duration > 0
|
218
|
+
durationSpecs += 1 if @milestone
|
190
219
|
|
191
220
|
# The rest of this function performs a number of plausibility tests with
|
192
221
|
# regards to task start and end critiria. To explain the various cases,
|
@@ -207,7 +236,7 @@ class TaskJuggler
|
|
207
236
|
"Container task #{@property.fullId} may not have a duration " +
|
208
237
|
"or be marked as milestones.")
|
209
238
|
end
|
210
|
-
elsif
|
239
|
+
elsif @milestone
|
211
240
|
if durationSpecs > 1
|
212
241
|
error('milestone_duration',
|
213
242
|
"Milestone task #{@property.fullId} may not have a duration.")
|
@@ -223,9 +252,9 @@ class TaskJuggler
|
|
223
252
|
# already handled by 'start_undetermed' or 'end_undetermed'
|
224
253
|
|
225
254
|
# err2: differnt start and end dates
|
226
|
-
if
|
255
|
+
if @start && @end && @start != @end
|
227
256
|
error('milestone_start_end',
|
228
|
-
"Start (#{
|
257
|
+
"Start (#{@start}) and end (#{@end}) dates of " +
|
229
258
|
"milestone task #{@property.fullId} must be identical.")
|
230
259
|
end
|
231
260
|
else
|
@@ -268,8 +297,8 @@ class TaskJuggler
|
|
268
297
|
# - <-- -D
|
269
298
|
# - <-- |D
|
270
299
|
if durationSpecs == 0 &&
|
271
|
-
((
|
272
|
-
(
|
300
|
+
((@forward && @end.nil? && !hasDependencies(true)) ||
|
301
|
+
(!@forward && @start.nil? && !hasDependencies(false)))
|
273
302
|
error('task_underspecified',
|
274
303
|
"Task #{@property.fullId} has too few specifations to be " +
|
275
304
|
"scheduled.")
|
@@ -296,8 +325,8 @@ class TaskJuggler
|
|
296
325
|
startSpeced = @property.provided('start', @scenarioIdx)
|
297
326
|
endSpeced = @property.provided('end', @scenarioIdx)
|
298
327
|
if ((startSpeced && endSpeced) ||
|
299
|
-
(hasDependencies(false) &&
|
300
|
-
(hasDependencies(true) &&
|
328
|
+
(hasDependencies(false) && @forward && endSpeced) ||
|
329
|
+
(hasDependencies(true) && !@forward && startSpeced)) &&
|
301
330
|
durationSpecs > 0 && !@property.provided('scheduled', @scenarioIdx)
|
302
331
|
error('task_overspecified',
|
303
332
|
"Task #{@property.fullId} has a start, an end and a " +
|
@@ -305,7 +334,7 @@ class TaskJuggler
|
|
305
334
|
end
|
306
335
|
end
|
307
336
|
|
308
|
-
if
|
337
|
+
if !@booking.empty? && !@forward && !@scheduled
|
309
338
|
error('alap_booking',
|
310
339
|
'A task scheduled in ALAP mode may only have bookings if it ' +
|
311
340
|
'has been marked as fully scheduled. Keep in mind that ' +
|
@@ -313,14 +342,14 @@ class TaskJuggler
|
|
313
342
|
'switch the task to ALAP mode.')
|
314
343
|
end
|
315
344
|
|
316
|
-
|
345
|
+
@startsuccs.each do |task, onEnd|
|
317
346
|
unless task['forward', @scenarioIdx]
|
318
347
|
task.data[@scenarioIdx].error(
|
319
348
|
'onstart_wrong_direction',
|
320
349
|
'Tasks with on-start dependencies must be ASAP scheduled')
|
321
350
|
end
|
322
351
|
end
|
323
|
-
|
352
|
+
@endpreds.each do |task, onEnd|
|
324
353
|
if task['forward', @scenarioIdx]
|
325
354
|
task.data[@scenarioIdx].error(
|
326
355
|
'onend_wrong_direction',
|
@@ -340,7 +369,7 @@ class TaskJuggler
|
|
340
369
|
|
341
370
|
if (parent = @property.parent)
|
342
371
|
# Add the assigned resources to the parent task list.
|
343
|
-
|
372
|
+
@assignedresources.each do |resource|
|
344
373
|
unless parent['assignedresources', @scenarioIdx].include?(resource)
|
345
374
|
parent['assignedresources', @scenarioIdx] << resource
|
346
375
|
end
|
@@ -371,65 +400,65 @@ class TaskJuggler
|
|
371
400
|
end
|
372
401
|
|
373
402
|
# Make sure the task is marked complete
|
374
|
-
unless
|
403
|
+
unless @scheduled
|
375
404
|
error('not_scheduled',
|
376
405
|
"Task #{@property.fullId} has not been marked as scheduled.")
|
377
406
|
end
|
378
407
|
|
379
408
|
# If the task has a follower or predecessor that is a runaway this task
|
380
409
|
# is also incomplete.
|
381
|
-
(
|
410
|
+
(@startsuccs + @endsuccs).each do |task, onEnd|
|
382
411
|
return false if task.isRunAway(@scenarioIdx)
|
383
412
|
end
|
384
|
-
(
|
413
|
+
(@startpreds + @endpreds).each do |task, onEnd|
|
385
414
|
return false if task.isRunAway(@scenarioIdx)
|
386
415
|
end
|
387
416
|
|
388
417
|
# Check if the start time is ok
|
389
|
-
if
|
418
|
+
if @start.nil?
|
390
419
|
error('task_start_undef',
|
391
420
|
"Task #{@property.fullId} has undefined start time")
|
392
421
|
end
|
393
|
-
if
|
422
|
+
if @start < @project['start'] || @start > @project['end']
|
394
423
|
error('task_start_range',
|
395
|
-
"The start time (#{
|
424
|
+
"The start time (#{@start}) of task #{@property.fullId} " +
|
396
425
|
"is outside the project interval (#{@project['start']} - " +
|
397
426
|
"#{@project['end']})")
|
398
427
|
end
|
399
|
-
if
|
428
|
+
if !@minstart.nil? && @start < @minstart
|
400
429
|
warning('minstart',
|
401
|
-
"The start time (#{
|
402
|
-
"is too early. Must be after #{
|
430
|
+
"The start time (#{@start}) of task #{@property.fullId} " +
|
431
|
+
"is too early. Must be after #{@minstart}.")
|
403
432
|
end
|
404
|
-
if
|
433
|
+
if !@maxstart.nil? && @start > @maxstart
|
405
434
|
warning('maxstart',
|
406
|
-
"The start time (#{
|
407
|
-
"is too late. Must be before #{
|
435
|
+
"The start time (#{@start}) of task #{@property.fullId} " +
|
436
|
+
"is too late. Must be before #{@maxstart}.")
|
408
437
|
end
|
409
438
|
# Check if the end time is ok
|
410
439
|
error('task_end_undef',
|
411
|
-
"Task #{@property.fullId} has undefined end time") if
|
412
|
-
if
|
440
|
+
"Task #{@property.fullId} has undefined end time") if @end.nil?
|
441
|
+
if @end < @project['start'] || @end > @project['end']
|
413
442
|
error('task_end_range',
|
414
|
-
"The end time (#{
|
443
|
+
"The end time (#{@end}) of task #{@property.fullId} " +
|
415
444
|
"is outside the project interval (#{@project['start']} - " +
|
416
445
|
"#{@project['end']})")
|
417
446
|
end
|
418
|
-
if
|
447
|
+
if !@minend.nil? && @end < @minend
|
419
448
|
warning('minend',
|
420
|
-
"The end time (#{
|
421
|
-
"is too early. Must be after #{
|
449
|
+
"The end time (#{@end}) of task #{@property.fullId} " +
|
450
|
+
"is too early. Must be after #{@minend}.")
|
422
451
|
end
|
423
|
-
if
|
452
|
+
if !@maxend.nil? && @end > @maxend
|
424
453
|
warning('maxend',
|
425
|
-
"The end time (#{
|
426
|
-
"is too late. Must be before #{
|
454
|
+
"The end time (#{@end}) of task #{@property.fullId} " +
|
455
|
+
"is too late. Must be before #{@maxend}.")
|
427
456
|
end
|
428
457
|
# Make sure the start is before the end
|
429
|
-
if
|
458
|
+
if @start > @end
|
430
459
|
error('start_after_end',
|
431
|
-
"The start time (#{
|
432
|
-
"is after the end time (#{
|
460
|
+
"The start time (#{@start}) of task #{@property.fullId} " +
|
461
|
+
"is after the end time (#{@end}).")
|
433
462
|
end
|
434
463
|
|
435
464
|
|
@@ -437,33 +466,33 @@ class TaskJuggler
|
|
437
466
|
unless (parent = @property.parent).nil? ||
|
438
467
|
parent['start', @scenarioIdx].nil? ||
|
439
468
|
parent['end', @scenarioIdx].nil?
|
440
|
-
if
|
469
|
+
if @start < parent['start', @scenarioIdx]
|
441
470
|
error('task_start_in_parent',
|
442
|
-
"The start date (#{
|
443
|
-
"is before the start date
|
444
|
-
"
|
471
|
+
"The start date (#{@start}) of task #{@property.fullId} " +
|
472
|
+
"is before the start date (#{parent['start', @scenarioIdx]}) " +
|
473
|
+
"of the enclosing task.")
|
445
474
|
end
|
446
|
-
if
|
475
|
+
if @end > parent['end', @scenarioIdx]
|
447
476
|
error('task_end_in_parent',
|
448
|
-
"The end date (#{
|
449
|
-
"is after the end date
|
450
|
-
"
|
477
|
+
"The end date (#{@end}) of task #{@property.fullId} " +
|
478
|
+
"is after the end date (#{parent['end', @scenarioIdx]}) " +
|
479
|
+
"of the enclosing task.")
|
451
480
|
end
|
452
481
|
end
|
453
482
|
|
454
483
|
# Check that all preceding tasks start/end before this task.
|
455
|
-
@
|
484
|
+
@depends.each do |dependency|
|
456
485
|
task = dependency.task
|
457
486
|
limit = task[dependency.onEnd ? 'end' : 'start', @scenarioIdx]
|
458
487
|
next if limit.nil?
|
459
|
-
if
|
488
|
+
if @start < limit
|
460
489
|
error('task_pred_before',
|
461
|
-
"Task #{@property.fullId} must start after " +
|
462
|
-
"#{dependency.onEnd ? 'end' : 'start'} of task " +
|
490
|
+
"Task #{@property.fullId} (#{@start}) must start after " +
|
491
|
+
"#{dependency.onEnd ? 'end' : 'start'} (#{limit}) of task " +
|
463
492
|
"#{task.fullId}.")
|
464
493
|
end
|
465
494
|
if dependency.gapDuration > 0
|
466
|
-
if limit + dependency.gapDuration >
|
495
|
+
if limit + dependency.gapDuration > @start
|
467
496
|
error('task_pred_before_gd',
|
468
497
|
"Task #{@property.fullId} must start " +
|
469
498
|
"#{dependency.gapDuration / (60 * 60 * 24)} days after " +
|
@@ -474,7 +503,7 @@ class TaskJuggler
|
|
474
503
|
end
|
475
504
|
end
|
476
505
|
if dependency.gapLength > 0
|
477
|
-
if calcLength(limit,
|
506
|
+
if calcLength(limit, @start) < dependency.gapLength
|
478
507
|
error('task_pred_before_gl',
|
479
508
|
"Task #{@property.fullId} must start " +
|
480
509
|
"#{@project.slotsToDays(dependency.gapLength)} " +
|
@@ -488,17 +517,18 @@ class TaskJuggler
|
|
488
517
|
end
|
489
518
|
|
490
519
|
# Check that all following tasks end before this task
|
491
|
-
@
|
520
|
+
@precedes.each do |dependency|
|
492
521
|
task = dependency.task
|
493
522
|
limit = task[dependency.onEnd ? 'end' : 'start', @scenarioIdx]
|
494
523
|
next if limit.nil?
|
495
|
-
if limit <
|
524
|
+
if limit < @end
|
496
525
|
error('task_succ_after',
|
497
|
-
"Task #{@property.fullId} must end before " +
|
498
|
-
"#{dependency.onEnd ? 'end' : 'start'} of task
|
526
|
+
"Task #{@property.fullId} (#{@end}) must end before " +
|
527
|
+
"#{dependency.onEnd ? 'end' : 'start'} (#{limit}) of task " +
|
528
|
+
"#{task.fullId}.")
|
499
529
|
end
|
500
530
|
if dependency.gapDuration > 0
|
501
|
-
if limit - dependency.gapDuration <
|
531
|
+
if limit - dependency.gapDuration < @end
|
502
532
|
error('task_succ_after_gd',
|
503
533
|
"Task #{@property.fullId} must end " +
|
504
534
|
"#{dependency.gapDuration / (60 * 60 * 24)} days before " +
|
@@ -509,7 +539,7 @@ class TaskJuggler
|
|
509
539
|
end
|
510
540
|
end
|
511
541
|
if dependency.gapLength > 0
|
512
|
-
if calcLength(
|
542
|
+
if calcLength(@end, limit) < dependency.gapLength
|
513
543
|
error('task_succ_after_gl',
|
514
544
|
"Task #{@property.fullId} must end " +
|
515
545
|
"#{@project.slotsToDays(dependency.gapLength)} " +
|
@@ -522,12 +552,24 @@ class TaskJuggler
|
|
522
552
|
end
|
523
553
|
end
|
524
554
|
|
525
|
-
if
|
555
|
+
if @milestone && @start != @end
|
526
556
|
error('milestone_times_equal',
|
527
557
|
"Milestone #{@property.fullId} must have identical start and " +
|
528
558
|
"end date.")
|
529
559
|
end
|
530
560
|
|
561
|
+
if @effort == 0 && !@milestone && !@allocate.empty? &&
|
562
|
+
@assignedresources.empty?
|
563
|
+
# The user used an 'allocate' for the task, but did not specify any
|
564
|
+
# 'effort'. Actual allocations will only happen when resources are
|
565
|
+
# available by chance. If there are no assigned resources, we generate
|
566
|
+
# a warning as this is probably not what the user intended.
|
567
|
+
warning('allocate_no_assigned',
|
568
|
+
"Task #{@property.id} has resource allocation requested, but " +
|
569
|
+
"did not get any resources assigned. Either use 'effort' " +
|
570
|
+
"to ensure allocations or use a higher 'priority'.")
|
571
|
+
end
|
572
|
+
|
531
573
|
@errors == 0
|
532
574
|
end
|
533
575
|
|
@@ -618,12 +660,12 @@ class TaskJuggler
|
|
618
660
|
# -->| o---->
|
619
661
|
# +--------
|
620
662
|
#
|
621
|
-
if (forward &&
|
663
|
+
if (forward && @forward) || @milestone
|
622
664
|
checkForLoops(path, true, false, true)
|
623
665
|
end
|
624
666
|
end
|
625
667
|
else
|
626
|
-
if
|
668
|
+
if @startpreds.empty?
|
627
669
|
#
|
628
670
|
# ^
|
629
671
|
# |
|
@@ -645,7 +687,7 @@ class TaskJuggler
|
|
645
687
|
# ^
|
646
688
|
# |
|
647
689
|
#
|
648
|
-
|
690
|
+
@startpreds.each do |task, targetEnd|
|
649
691
|
task.checkForLoops(@scenarioIdx, path, targetEnd, true, forward)
|
650
692
|
end
|
651
693
|
end
|
@@ -672,12 +714,12 @@ class TaskJuggler
|
|
672
714
|
# <----o |<--
|
673
715
|
# --------+
|
674
716
|
#
|
675
|
-
if (!forward &&
|
717
|
+
if (!forward && !@forward) || @milestone
|
676
718
|
checkForLoops(path, false, false, false)
|
677
719
|
end
|
678
720
|
end
|
679
721
|
else
|
680
|
-
if
|
722
|
+
if @endsuccs.empty?
|
681
723
|
#
|
682
724
|
# ^
|
683
725
|
# |
|
@@ -698,7 +740,7 @@ class TaskJuggler
|
|
698
740
|
# ^
|
699
741
|
# |
|
700
742
|
#
|
701
|
-
|
743
|
+
@endsuccs.each do |task, targetEnd|
|
702
744
|
task.checkForLoops(@scenarioIdx, path, targetEnd, true, forward)
|
703
745
|
end
|
704
746
|
end
|
@@ -715,7 +757,7 @@ class TaskJuggler
|
|
715
757
|
# list of leaf resources that are allocated to this task.
|
716
758
|
def candidates
|
717
759
|
@candidates = []
|
718
|
-
|
760
|
+
@allocate.each do |allocation|
|
719
761
|
allocation.candidates.each do |candidate|
|
720
762
|
candidate.allLeaves.each do |resource|
|
721
763
|
@candidates << resource unless @candidates.include?(resource)
|
@@ -730,9 +772,9 @@ class TaskJuggler
|
|
730
772
|
# stores it in @candidates. It also adds the allocated effort to
|
731
773
|
# the 'alloctdeffort' counter of each resource.
|
732
774
|
def countResourceAllocations
|
733
|
-
return if @candidates.empty? ||
|
775
|
+
return if @candidates.empty? || @effort <= 0
|
734
776
|
|
735
|
-
avgEffort =
|
777
|
+
avgEffort = @effort / @candidates.length
|
736
778
|
@candidates.each do |resource|
|
737
779
|
resource['alloctdeffort', @scenarioIdx] += avgEffort
|
738
780
|
end
|
@@ -744,20 +786,20 @@ class TaskJuggler
|
|
744
786
|
# exception are milestones which get an arbitrary value between 0 and 2
|
745
787
|
# based on their priority.
|
746
788
|
def calcCriticalness
|
747
|
-
@
|
748
|
-
@
|
789
|
+
@criticalness = 0.0
|
790
|
+
@pathcriticalness = nil
|
749
791
|
|
750
792
|
# Users feel that milestones are somewhat important. So we use an
|
751
793
|
# arbitrary value larger than 0 for them. We make it priority dependent,
|
752
794
|
# so the user has some control over it. Priority 0 is 0, 500 is 1.0 and
|
753
795
|
# 1000 is 2.0. These values are pretty much randomly picked and probably
|
754
796
|
# require some more tuning based on real projects.
|
755
|
-
if
|
756
|
-
@
|
797
|
+
if @milestone
|
798
|
+
@criticalness = @priority / 500.0
|
757
799
|
end
|
758
800
|
|
759
801
|
# Task without efforts of allocations are not critical.
|
760
|
-
return if
|
802
|
+
return if @effort <= 0 || @candidates.empty?
|
761
803
|
|
762
804
|
# Determine the average criticalness of all allocated resources.
|
763
805
|
criticalness = 0.0
|
@@ -768,7 +810,7 @@ class TaskJuggler
|
|
768
810
|
|
769
811
|
# The task criticalness is the product of effort and average resource
|
770
812
|
# criticalness.
|
771
|
-
@
|
813
|
+
@criticalness = @effort * criticalness
|
772
814
|
end
|
773
815
|
|
774
816
|
# The path criticalness is a measure for the overall criticalness of the
|
@@ -781,8 +823,8 @@ class TaskJuggler
|
|
781
823
|
# If we have computed this already, just return the value. If we are only
|
782
824
|
# at the end of the task, we do not include the criticalness of this task
|
783
825
|
# as it is not really part of the path.
|
784
|
-
if
|
785
|
-
return
|
826
|
+
if @pathcriticalness
|
827
|
+
return @pathcriticalness - (atEnd ? 0 : @criticalness)
|
786
828
|
end
|
787
829
|
|
788
830
|
maxCriticalness = 0.0
|
@@ -808,7 +850,7 @@ class TaskJuggler
|
|
808
850
|
# For leaf tasks, we check all pathes through the start successors and
|
809
851
|
# then the pathes through the end successors of this task and all its
|
810
852
|
# parent tasks.
|
811
|
-
|
853
|
+
@startsuccs.each do |task, onEnd|
|
812
854
|
if (criticalness = task.calcPathCriticalness(@scenarioIdx, onEnd)) >
|
813
855
|
maxCriticalness
|
814
856
|
maxCriticalness = criticalness
|
@@ -819,23 +861,23 @@ class TaskJuggler
|
|
819
861
|
maxCriticalness = criticalness
|
820
862
|
end
|
821
863
|
|
822
|
-
maxCriticalness +=
|
864
|
+
maxCriticalness += @criticalness
|
823
865
|
end
|
824
866
|
end
|
825
867
|
|
826
|
-
@
|
868
|
+
@pathcriticalness = maxCriticalness
|
827
869
|
end
|
828
870
|
|
829
871
|
# Check if the task is ready to be scheduled. For this it needs to have at
|
830
872
|
# least one specified end date and a duration criteria or the other end
|
831
873
|
# date.
|
832
874
|
def readyForScheduling?
|
833
|
-
return false if
|
875
|
+
return false if @scheduled || @isRunAway
|
834
876
|
|
835
|
-
if
|
836
|
-
return true if
|
877
|
+
if @forward
|
878
|
+
return true if @start && (hasDurationSpec? || @end)
|
837
879
|
else
|
838
|
-
return true if
|
880
|
+
return true if @end && (hasDurationSpec? || @start)
|
839
881
|
end
|
840
882
|
|
841
883
|
false
|
@@ -846,31 +888,39 @@ class TaskJuggler
|
|
846
888
|
# or end date has been determined and other tasks may be ready for
|
847
889
|
# scheduling now.
|
848
890
|
def schedule
|
849
|
-
#
|
850
|
-
|
851
|
-
#
|
852
|
-
|
853
|
-
#
|
854
|
-
|
855
|
-
|
891
|
+
# Compute the date of the next slot this task wants to have scheduled.
|
892
|
+
# This must either be the first slot ever or it must be directly
|
893
|
+
# adjecent to the previous slot. If this task has not yet been scheduled
|
894
|
+
# at all, @currentSlotIdx is still nil. Otherwise it contains the index
|
895
|
+
# of the last scheduled slot.
|
896
|
+
if @forward
|
897
|
+
# On first call, the @currentSlotIdx is not set yet. We set it to the
|
898
|
+
# start slot index or the 'now' slot if we are in projection mode and
|
899
|
+
# the tasks has allocations.
|
900
|
+
if @currentSlotIdx.nil?
|
901
|
+
@currentSlotIdx = @project.dateToIdx(
|
902
|
+
@projectionMode && (@project['now'] > @start) && !@allocate.empty? ?
|
903
|
+
@project['now'] : @start)
|
904
|
+
end
|
905
|
+
else
|
906
|
+
# On first call, the @currentSlotIdx is not set yet. We set it to the
|
907
|
+
# slot index of the slot before the end slot.
|
908
|
+
if @currentSlotIdx.nil?
|
909
|
+
@currentSlotIdx = @project.dateToIdx(@end) - 1
|
910
|
+
end
|
911
|
+
end
|
856
912
|
|
857
913
|
# Schedule all time slots from slot in the scheduling direction until
|
858
914
|
# the task is completed or a problem has been found.
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
# The task is scheduled from end to start.
|
869
|
-
slot -= slotDuration
|
870
|
-
if slot < limit
|
871
|
-
markAsRunaway
|
872
|
-
return false
|
873
|
-
end
|
915
|
+
# The task may not excede the project interval.
|
916
|
+
lowerLimit = @project.dateToIdx(@project['start'])
|
917
|
+
upperLimit = @project.dateToIdx(@project['end'])
|
918
|
+
delta = @forward ? 1 : -1
|
919
|
+
while scheduleSlot
|
920
|
+
@currentSlotIdx += delta
|
921
|
+
if @currentSlotIdx < lowerLimit || upperLimit < @currentSlotIdx
|
922
|
+
markAsRunaway
|
923
|
+
return false
|
874
924
|
end
|
875
925
|
end
|
876
926
|
|
@@ -896,21 +946,21 @@ class TaskJuggler
|
|
896
946
|
# For leaf tasks, propagate start may set the date. Container task dates
|
897
947
|
# are only set in scheduleContainer().
|
898
948
|
if @property.leaf?
|
899
|
-
@
|
900
|
-
Log << "Task #{@property.fullId}: #{
|
949
|
+
instance_variable_set(('@' + thisEnd).intern, date)
|
950
|
+
Log << "Task #{@property.fullId}: #{@start} -> #{@end}"
|
901
951
|
end
|
902
952
|
|
903
|
-
if
|
953
|
+
if @milestone
|
904
954
|
# Start and end date of a milestone are identical.
|
905
|
-
@
|
955
|
+
@scheduled = true
|
906
956
|
if a(otherEnd).nil?
|
907
957
|
propagateDate(a(thisEnd), !atEnd)
|
908
958
|
end
|
909
|
-
Log << "Milestone #{@property.fullId}: #{
|
910
|
-
elsif
|
911
|
-
!(
|
912
|
-
|
913
|
-
@
|
959
|
+
Log << "Milestone #{@property.fullId}: #{@start} -> #{@end}"
|
960
|
+
elsif !@scheduled && @start && @end &&
|
961
|
+
!(@length == 0 && @duration == 0 && @effort == 0 &&
|
962
|
+
!@allocate.empty?)
|
963
|
+
@scheduled = true
|
914
964
|
Log << "Task #{@property.fullId} has been scheduled"
|
915
965
|
end
|
916
966
|
|
@@ -920,21 +970,21 @@ class TaskJuggler
|
|
920
970
|
# allocation. In these cases, bookResource() has to propagate the final
|
921
971
|
# date.
|
922
972
|
if atEnd
|
923
|
-
if ignoreEffort ||
|
924
|
-
|
973
|
+
if ignoreEffort || @effort == 0
|
974
|
+
@endpreds.each do |task, onEnd|
|
925
975
|
propagateDateToDep(task, onEnd)
|
926
976
|
end
|
927
977
|
end
|
928
|
-
|
978
|
+
@endsuccs.each do |task, onEnd|
|
929
979
|
propagateDateToDep(task, onEnd)
|
930
980
|
end
|
931
981
|
else
|
932
|
-
if ignoreEffort ||
|
933
|
-
|
982
|
+
if ignoreEffort || @effort == 0
|
983
|
+
@startsuccs.each do |task, onEnd|
|
934
984
|
propagateDateToDep(task, onEnd)
|
935
985
|
end
|
936
986
|
end
|
937
|
-
|
987
|
+
@startpreds.each do |task, onEnd|
|
938
988
|
propagateDateToDep(task, onEnd)
|
939
989
|
end
|
940
990
|
end
|
@@ -988,24 +1038,24 @@ class TaskJuggler
|
|
988
1038
|
|
989
1039
|
# Check for tasks that have no start and end spec, no duration spec but
|
990
1040
|
# allocates. They can inherit the start and end date.
|
991
|
-
return true if hasThatSpec && !hasDurationSpec &&
|
1041
|
+
return true if hasThatSpec && !hasDurationSpec && !@allocate.empty?
|
992
1042
|
|
993
|
-
if
|
1043
|
+
if @forward ^ atEnd
|
994
1044
|
# the scheduling direction is pointing away from this end
|
995
|
-
return true if hasDurationSpec ||
|
1045
|
+
return true if hasDurationSpec || !@booking.empty?
|
996
1046
|
|
997
1047
|
return hasThatSpec
|
998
1048
|
else
|
999
1049
|
# the scheduling direction is pointing towards this end
|
1000
1050
|
return a(thatEnd) && !hasDurationSpec &&
|
1001
|
-
|
1051
|
+
@booking.empty? #&& @allocate.empty?
|
1002
1052
|
end
|
1003
1053
|
end
|
1004
1054
|
|
1005
1055
|
# Find the smallest possible interval that encloses all child tasks. Abort
|
1006
1056
|
# the operation if any of the child tasks are not yet scheduled.
|
1007
1057
|
def scheduleContainer
|
1008
|
-
return if
|
1058
|
+
return if @scheduled || !@property.container?
|
1009
1059
|
|
1010
1060
|
nStart = nil
|
1011
1061
|
nEnd = nil
|
@@ -1028,19 +1078,19 @@ class TaskJuggler
|
|
1028
1078
|
|
1029
1079
|
startSet = endSet = false
|
1030
1080
|
# Propagate the dates to other dependent tasks.
|
1031
|
-
if
|
1032
|
-
@
|
1081
|
+
if @start.nil? || @start > nStart
|
1082
|
+
@start = nStart
|
1033
1083
|
startSet = true
|
1034
1084
|
end
|
1035
|
-
if
|
1036
|
-
@
|
1085
|
+
if @end.nil? || @end < nEnd
|
1086
|
+
@end = nEnd
|
1037
1087
|
endSet = true
|
1038
1088
|
end
|
1039
|
-
unless
|
1040
|
-
raise "Start (#{
|
1089
|
+
unless @start && @end
|
1090
|
+
raise "Start (#{@start}) and end (#{@end}) must be set"
|
1041
1091
|
end
|
1042
|
-
@
|
1043
|
-
Log << "Container task #{@property.fullId} completed: #{
|
1092
|
+
@scheduled = true
|
1093
|
+
Log << "Container task #{@property.fullId} completed: #{@start} -> #{@end}"
|
1044
1094
|
|
1045
1095
|
# If we have modified the start or end date, we need to communicate this
|
1046
1096
|
# new date to surrounding tasks.
|
@@ -1050,7 +1100,7 @@ class TaskJuggler
|
|
1050
1100
|
|
1051
1101
|
# Return true if the task has a effort, length or duration setting.
|
1052
1102
|
def hasDurationSpec?
|
1053
|
-
|
1103
|
+
@length > 0 || @duration > 0 || @effort > 0 || @milestone
|
1054
1104
|
end
|
1055
1105
|
|
1056
1106
|
# Find the earliest possible start date for the task. This date must be
|
@@ -1059,7 +1109,7 @@ class TaskJuggler
|
|
1059
1109
|
def earliestStart
|
1060
1110
|
# This is the date that we will return.
|
1061
1111
|
startDate = nil
|
1062
|
-
|
1112
|
+
@depends.each do |dependency|
|
1063
1113
|
potentialStartDate =
|
1064
1114
|
dependency.task[dependency.onEnd ? 'end' : 'start', @scenarioIdx]
|
1065
1115
|
return nil if potentialStartDate.nil?
|
@@ -1101,7 +1151,7 @@ class TaskJuggler
|
|
1101
1151
|
# task B depends on A and they are specified this way:
|
1102
1152
|
# task A: | --> D-
|
1103
1153
|
# task B: -D <-- |
|
1104
|
-
if
|
1154
|
+
if @end && startDate > @end
|
1105
1155
|
error('weak_start_dep',
|
1106
1156
|
"Task #{@property.fullId} has a too weak start dependencies " +
|
1107
1157
|
"to be scheduled properly.")
|
@@ -1116,7 +1166,7 @@ class TaskJuggler
|
|
1116
1166
|
def latestEnd
|
1117
1167
|
# This is the date that we will return.
|
1118
1168
|
endDate = nil
|
1119
|
-
|
1169
|
+
@precedes.each do |dependency|
|
1120
1170
|
potentialEndDate =
|
1121
1171
|
dependency.task[dependency.onEnd ? 'end' : 'start', @scenarioIdx]
|
1122
1172
|
return nil if potentialEndDate.nil?
|
@@ -1158,7 +1208,7 @@ class TaskJuggler
|
|
1158
1208
|
# task A precedes B and they are specified this way:
|
1159
1209
|
# task A: | --> D-
|
1160
1210
|
# task B: -D <-- |
|
1161
|
-
if
|
1211
|
+
if @start && (endDate.nil? || endDate > @start)
|
1162
1212
|
error('weak_end_dep',
|
1163
1213
|
"Task #{@property.fullId} has a too weak end dependencies " +
|
1164
1214
|
"to be scheduled properly.")
|
@@ -1170,14 +1220,14 @@ class TaskJuggler
|
|
1170
1220
|
def addBooking(booking)
|
1171
1221
|
# This append operation will not trigger a copy to sub-scenarios.
|
1172
1222
|
# Bookings are only valid for the scenario they are defined in.
|
1173
|
-
@
|
1223
|
+
@booking << booking
|
1174
1224
|
end
|
1175
1225
|
|
1176
1226
|
def query_complete(query)
|
1177
1227
|
# If we haven't calculated the value yet, calculate it first.
|
1178
|
-
unless (complete =
|
1228
|
+
unless (complete = @complete)
|
1179
1229
|
calcCompletion
|
1180
|
-
complete =
|
1230
|
+
complete = @complete
|
1181
1231
|
end
|
1182
1232
|
|
1183
1233
|
query.sortable = query.numerical = complete
|
@@ -1195,7 +1245,7 @@ class TaskJuggler
|
|
1195
1245
|
query.scopeProperty)
|
1196
1246
|
query.string = query.currencyFormat.format(cost)
|
1197
1247
|
else
|
1198
|
-
query.string = 'No
|
1248
|
+
query.string = 'No \'balance\' defined!'
|
1199
1249
|
end
|
1200
1250
|
end
|
1201
1251
|
|
@@ -1203,7 +1253,7 @@ class TaskJuggler
|
|
1203
1253
|
# all tasks. Also for those who did not have a 'duration' attribute.
|
1204
1254
|
def query_duration(query)
|
1205
1255
|
query.sortable = query.numerical = duration =
|
1206
|
-
(
|
1256
|
+
(@end - @start) / (60 * 60 * 24)
|
1207
1257
|
query.string = query.scaleDuration(duration)
|
1208
1258
|
end
|
1209
1259
|
|
@@ -1245,7 +1295,7 @@ class TaskJuggler
|
|
1245
1295
|
list = []
|
1246
1296
|
|
1247
1297
|
# First gather the task that depend on the start of this task.
|
1248
|
-
|
1298
|
+
@startsuccs.each do |task, onEnd|
|
1249
1299
|
if onEnd
|
1250
1300
|
date = task['end', query.scenarioIdx].to_s(query.timeFormat)
|
1251
1301
|
dep = "[->]"
|
@@ -1256,7 +1306,7 @@ class TaskJuggler
|
|
1256
1306
|
list << generateDepencyListItem(query, task, dep, date)
|
1257
1307
|
end
|
1258
1308
|
# Than add the tasks that depend on the end of this task.
|
1259
|
-
|
1309
|
+
@endsuccs.each do |task, onEnd|
|
1260
1310
|
if onEnd
|
1261
1311
|
date = task['end', query.scenarioIdx].to_s(query.timeFormat)
|
1262
1312
|
dep = "]->]"
|
@@ -1274,7 +1324,7 @@ class TaskJuggler
|
|
1274
1324
|
list = []
|
1275
1325
|
|
1276
1326
|
# First gather the task that depend on the start of this task.
|
1277
|
-
|
1327
|
+
@startpreds.each do |task, onEnd|
|
1278
1328
|
if onEnd
|
1279
1329
|
date = task['end', query.scenarioIdx].to_s(query.timeFormat)
|
1280
1330
|
dep = "]->["
|
@@ -1285,7 +1335,7 @@ class TaskJuggler
|
|
1285
1335
|
list << generateDepencyListItem(query, task, dep, date)
|
1286
1336
|
end
|
1287
1337
|
# Than add the tasks that depend on the end of this task.
|
1288
|
-
|
1338
|
+
@endpreds.each do |task, onEnd|
|
1289
1339
|
if onEnd
|
1290
1340
|
date = task['end', query.scenarioIdx].to_s(query.timeFormat)
|
1291
1341
|
dep = "]->]"
|
@@ -1305,9 +1355,10 @@ class TaskJuggler
|
|
1305
1355
|
return '' unless @property.leaf?
|
1306
1356
|
|
1307
1357
|
list = []
|
1308
|
-
|
1358
|
+
@assignedresources.each do |resource|
|
1309
1359
|
if resource.allocated?(@scenarioIdx,
|
1310
|
-
|
1360
|
+
TimeInterval.new(query.start, query.end),
|
1361
|
+
@property)
|
1311
1362
|
if query.listItem
|
1312
1363
|
rti = RichText.new(query.listItem, RTFHandlers.create(@project),
|
1313
1364
|
@project.messageHandler).
|
@@ -1334,15 +1385,15 @@ class TaskJuggler
|
|
1334
1385
|
query.scopeProperty)
|
1335
1386
|
query.string = query.currencyFormat.format(revenue)
|
1336
1387
|
else
|
1337
|
-
query.string = 'No
|
1388
|
+
query.string = 'No \'balance\' defined!'
|
1338
1389
|
end
|
1339
1390
|
end
|
1340
1391
|
|
1341
1392
|
def query_status(query)
|
1342
1393
|
# If we haven't calculated the completion yet, calculate it first.
|
1343
|
-
if (status =
|
1394
|
+
if (status = @status).empty?
|
1344
1395
|
calcCompletion
|
1345
|
-
status =
|
1396
|
+
status = @status
|
1346
1397
|
end
|
1347
1398
|
|
1348
1399
|
query.string = status
|
@@ -1374,8 +1425,8 @@ class TaskJuggler
|
|
1374
1425
|
# Compute the total time _resource_ or all resources are allocated during
|
1375
1426
|
# interval specified by _startIdx_ and _endIdx_.
|
1376
1427
|
def getAllocatedTime(startIdx, endIdx, resource = nil)
|
1377
|
-
return 0.0 if
|
1378
|
-
(resource &&
|
1428
|
+
return 0.0 if @milestone || startIdx >= endIdx ||
|
1429
|
+
(resource && !@assignedresources.include?(resource))
|
1379
1430
|
|
1380
1431
|
key = [ self, :TaskScenarioAllocatedTime, startIdx, endIdx, resource ].hash
|
1381
1432
|
@dCache.cached(key) do
|
@@ -1391,7 +1442,7 @@ class TaskJuggler
|
|
1391
1442
|
startIdx, endIdx,
|
1392
1443
|
@property)
|
1393
1444
|
else
|
1394
|
-
|
1445
|
+
@assignedresources.each do |r|
|
1395
1446
|
allocatedTime += r.getAllocatedTime(@scenarioIdx, startIdx, endIdx,
|
1396
1447
|
@property)
|
1397
1448
|
end
|
@@ -1405,8 +1456,8 @@ class TaskJuggler
|
|
1405
1456
|
# interval specified by _startIdx_ and _endIdx_. The effective work is the
|
1406
1457
|
# actual work multiplied by the efficiency of the resource.
|
1407
1458
|
def getEffectiveWork(startIdx, endIdx, resource = nil)
|
1408
|
-
return 0.0 if
|
1409
|
-
(resource &&
|
1459
|
+
return 0.0 if @milestone || startIdx >= endIdx ||
|
1460
|
+
(resource && !@assignedresources.include?(resource))
|
1410
1461
|
|
1411
1462
|
key = [ self, :TaskScenarioEffectiveWork, startIdx, endIdx, resource ].hash
|
1412
1463
|
@dCache.cached(key) do
|
@@ -1421,7 +1472,7 @@ class TaskJuggler
|
|
1421
1472
|
workLoad += resource.getEffectiveWork(@scenarioIdx, startIdx, endIdx,
|
1422
1473
|
@property)
|
1423
1474
|
else
|
1424
|
-
|
1475
|
+
@assignedresources.each do |r|
|
1425
1476
|
workLoad += r.getEffectiveWork(@scenarioIdx, startIdx, endIdx,
|
1426
1477
|
@property)
|
1427
1478
|
end
|
@@ -1441,9 +1492,9 @@ class TaskJuggler
|
|
1441
1492
|
@dCache.cached(key) do
|
1442
1493
|
workLoad = @dCache.load(key)
|
1443
1494
|
il = IntervalList.new
|
1444
|
-
il <<
|
1495
|
+
il << TimeInterval.new(@project['start'], @project['end'])
|
1445
1496
|
if @property.leaf?
|
1446
|
-
unless (resources =
|
1497
|
+
unless (resources = @assignedresources).empty?
|
1447
1498
|
# The task has assigned resources, so we can use their common time
|
1448
1499
|
# off intervals.
|
1449
1500
|
resources.each do |resource|
|
@@ -1480,7 +1531,7 @@ class TaskJuggler
|
|
1480
1531
|
end
|
1481
1532
|
|
1482
1533
|
|
1483
|
-
|
1534
|
+
@startsuccs.each do |dep|
|
1484
1535
|
unless dep[1]
|
1485
1536
|
# must be a start->start dependency
|
1486
1537
|
return true if dep[0].isDependencyOf(@scenarioIdx, task, depth)
|
@@ -1489,7 +1540,7 @@ class TaskJuggler
|
|
1489
1540
|
|
1490
1541
|
return false if depth == 1
|
1491
1542
|
|
1492
|
-
|
1543
|
+
@endsuccs.each do |dep|
|
1493
1544
|
unless dep[1]
|
1494
1545
|
# must be an end->start dependency
|
1495
1546
|
return true if dep[0].isDependencyOf(@scenarioIdx, task, depth - 1)
|
@@ -1517,7 +1568,7 @@ class TaskJuggler
|
|
1517
1568
|
# Returns true of the _resource_ is assigned to this task or any of its
|
1518
1569
|
# children.
|
1519
1570
|
def hasResourceAllocated?(interval, resource)
|
1520
|
-
return false unless
|
1571
|
+
return false unless @assignedresources.include?(resource)
|
1521
1572
|
|
1522
1573
|
if @property.leaf?
|
1523
1574
|
return resource.allocated?(@scenarioIdx, interval, @property)
|
@@ -1531,147 +1582,99 @@ class TaskJuggler
|
|
1531
1582
|
end
|
1532
1583
|
|
1533
1584
|
private
|
1534
|
-
def scheduleSlot(slot, slotDuration)
|
1535
|
-
# Tasks must always be scheduled in a single contigous fashion. @lastSlot
|
1536
|
-
# indicates the slot that was used for the previous call. Depending on the
|
1537
|
-
# scheduling direction the next slot must be scheduled either right before
|
1538
|
-
# or after this slot. If the current slot is not directly aligned, we'll
|
1539
|
-
# wait for another call with a proper slot. The function returns true
|
1540
|
-
# only if a slot could be scheduled.
|
1541
|
-
if a('forward')
|
1542
|
-
# On first call, the @lastSlot is not set yet. We set it to the slot
|
1543
|
-
# before the start slot.
|
1544
|
-
if @lastSlot.nil?
|
1545
|
-
@lastSlot = a('start') - slotDuration
|
1546
|
-
@tentativeEnd = slot + slotDuration
|
1547
|
-
end
|
1548
|
-
|
1549
|
-
return false unless slot == @lastSlot + slotDuration
|
1550
|
-
else
|
1551
|
-
# On first call, the @lastSlot is not set yet. We set it to the slot
|
1552
|
-
# to the end slot.
|
1553
|
-
if @lastSlot.nil?
|
1554
|
-
@lastSlot = a('end')
|
1555
|
-
@tentativeStart = slot
|
1556
|
-
end
|
1557
|
-
|
1558
|
-
return false unless slot == @lastSlot - slotDuration
|
1559
|
-
end
|
1560
|
-
@lastSlot = slot
|
1561
1585
|
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1586
|
+
def scheduleSlot
|
1587
|
+
# Tasks must always be scheduled in a single contigous fashion.
|
1588
|
+
# Depending on the scheduling direction the next slot must be scheduled
|
1589
|
+
# either right before or after this slot. If the current slot is not
|
1590
|
+
# directly aligned, we'll wait for another call with a proper slot. The
|
1591
|
+
# function returns false if the task has been completely scheduled.
|
1592
|
+
case @durationType
|
1593
|
+
when :effortTask
|
1594
|
+
bookResources if @doneEffort < @effort
|
1595
|
+
if @doneEffort >= @effort
|
1596
|
+
# The specified effort has been reached. The task has been fully
|
1597
|
+
# scheduled now.
|
1598
|
+
if @forward
|
1599
|
+
propagateDate(@project.idxToDate(@currentSlotIdx + 1), true, true)
|
1600
|
+
else
|
1601
|
+
propagateDate(@project.idxToDate(@currentSlotIdx), false, true)
|
1602
|
+
end
|
1603
|
+
return false
|
1570
1604
|
end
|
1605
|
+
when :lengthTask
|
1606
|
+
bookResources
|
1607
|
+
# The doneLength is only increased for global working time slots.
|
1608
|
+
@doneLength += 1 if @project.isWorkingTime(@currentSlotIdx)
|
1571
1609
|
|
1572
1610
|
# If we have reached the specified duration or lengths, we set the end
|
1573
1611
|
# or start date and propagate the value to neighbouring tasks.
|
1574
|
-
if
|
1575
|
-
|
1576
|
-
|
1577
|
-
propagateDate(slot + slotDuration, true)
|
1612
|
+
if @doneLength >= @length
|
1613
|
+
if @forward
|
1614
|
+
propagateDate(@project.idxToDate(@currentSlotIdx + 1), true)
|
1578
1615
|
else
|
1579
|
-
propagateDate(
|
1616
|
+
propagateDate(@project.idxToDate(@currentSlotIdx), false)
|
1580
1617
|
end
|
1581
|
-
return
|
1618
|
+
return false
|
1582
1619
|
end
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1620
|
+
when :durationTask
|
1621
|
+
# The doneDuration counts the number of scheduled slots. It is increased
|
1622
|
+
# by one with every scheduled slot.
|
1623
|
+
bookResources
|
1624
|
+
@doneDuration += 1
|
1625
|
+
|
1626
|
+
# If we have reached the specified duration or lengths, we set the end
|
1627
|
+
# or start date and propagate the value to neighbouring tasks.
|
1628
|
+
if @doneDuration >= @duration
|
1629
|
+
if @forward
|
1630
|
+
propagateDate(@project.idxToDate(@currentSlotIdx + 1), true)
|
1590
1631
|
else
|
1591
|
-
propagateDate(@
|
1632
|
+
propagateDate(@project.idxToDate(@currentSlotIdx), false)
|
1592
1633
|
end
|
1593
|
-
return
|
1594
|
-
end
|
1595
|
-
elsif a('milestone')
|
1596
|
-
if a('forward')
|
1597
|
-
propagateDate(a('start'), true)
|
1598
|
-
else
|
1599
|
-
propagateDate(a('end'), false)
|
1634
|
+
return false
|
1600
1635
|
end
|
1601
|
-
|
1602
|
-
elsif a('start') && a('end')
|
1636
|
+
when :startEndTask
|
1603
1637
|
# Task with start and end date but no duration criteria
|
1604
|
-
|
1605
|
-
# For start-end-tasks without allocation, we don't have to do
|
1606
|
-
# anything but to set the 'scheduled' flag.
|
1607
|
-
@property['scheduled', @scenarioIdx] = true
|
1608
|
-
@property.parents.each do |parent|
|
1609
|
-
parent.scheduleContainer(@scenarioIdx)
|
1610
|
-
end
|
1611
|
-
return true
|
1612
|
-
end
|
1613
|
-
|
1614
|
-
bookResources(slot, slotDuration)
|
1638
|
+
bookResources
|
1615
1639
|
|
1616
1640
|
# Depending on the scheduling direction we can mark the task as
|
1617
1641
|
# scheduled once we have reached the other end.
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1642
|
+
currentSlot = @project.idxToDate(@currentSlotIdx)
|
1643
|
+
if (@forward && currentSlot + @project['scheduleGranularity'] >= @end) ||
|
1644
|
+
(!@forward && currentSlot <= @start)
|
1645
|
+
@scheduled = true
|
1621
1646
|
@property.parents.each do |parent|
|
1622
1647
|
parent.scheduleContainer(@scenarioIdx)
|
1623
1648
|
end
|
1624
|
-
return
|
1649
|
+
return false
|
1625
1650
|
end
|
1651
|
+
else
|
1652
|
+
raise "Unknown task duration type #{@durationType}"
|
1626
1653
|
end
|
1627
1654
|
|
1628
|
-
|
1655
|
+
true
|
1629
1656
|
end
|
1630
1657
|
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
# is still nil. Otherwise it contains the date of the last scheduled slot.
|
1635
|
-
def nextSlot(slotDuration)
|
1636
|
-
return nil if a('scheduled') || @isRunAway
|
1658
|
+
def bookResources
|
1659
|
+
# First check if there is any resource at all for this slot.
|
1660
|
+
return true unless @project.anyResourceAvailable?(@currentSlotIdx)
|
1637
1661
|
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
@lastSlot.nil? ? a('end') - slotDuration : @lastSlot - slotDuration
|
1642
|
-
end
|
1643
|
-
end
|
1644
|
-
|
1645
|
-
def bookResources(date, slotDuration)
|
1646
|
-
# If there are no allocations defined, we can't do any bookings.
|
1647
|
-
# In projection mode we do not allow allocations prior to the current
|
1648
|
-
# date. If the scenario is not scheduled in projection mode, this
|
1649
|
-
# restriction only applies to tasks with bookings.
|
1650
|
-
if a('allocate').empty? ||
|
1651
|
-
((@project.scenario(@scenarioIdx).get('projection') ||
|
1652
|
-
!a('booking').empty?) &&
|
1653
|
-
date < @project['now'])
|
1654
|
-
return
|
1655
|
-
end
|
1662
|
+
# If the task has resource independent allocation limits we need to make
|
1663
|
+
# sure that none of them is already exceeded.
|
1664
|
+
return unless limitsOk?(@currentSlotIdx)
|
1656
1665
|
|
1657
1666
|
# If the task has shifts to limit the allocations, we check that we are
|
1658
1667
|
# within a defined shift interval. If yes, we need to be on shift to
|
1659
1668
|
# continue.
|
1660
|
-
if
|
1661
|
-
return if
|
1669
|
+
if @shifts && @shifts.assigned?(@currentSlotIdx)
|
1670
|
+
return if !@shifts.onShift?(@currentSlotIdx)
|
1662
1671
|
end
|
1663
1672
|
|
1664
|
-
# If the task has resource independent allocation limits we need to make
|
1665
|
-
# sure that none of them is already exceeded.
|
1666
|
-
return unless limitsOk?(date)
|
1667
|
-
|
1668
|
-
sbIdx = @project.dateToIdx(date, false)
|
1669
|
-
|
1670
1673
|
# We first have to make sure that if there are mandatory resources
|
1671
1674
|
# that these are all available for the time slot.
|
1672
1675
|
takenMandatories = []
|
1673
1676
|
@mandatories.each do |allocation|
|
1674
|
-
return unless allocation.onShift?(
|
1677
|
+
return unless allocation.onShift?(@currentSlotIdx)
|
1675
1678
|
|
1676
1679
|
# For mandatory allocations with alternatives at least one of the
|
1677
1680
|
# alternatives must be available.
|
@@ -1681,8 +1684,8 @@ class TaskJuggler
|
|
1681
1684
|
# group must be available.
|
1682
1685
|
allAvailable = true
|
1683
1686
|
candidate.allLeaves.each do |resource|
|
1684
|
-
if !limitsOk?(
|
1685
|
-
!resource.available?(@scenarioIdx,
|
1687
|
+
if !limitsOk?(@currentSlotIdx, resource) ||
|
1688
|
+
!resource.available?(@scenarioIdx, @currentSlotIdx) ||
|
1686
1689
|
takenMandatories.include?(resource)
|
1687
1690
|
# We've found a mandatory resource that is not available for
|
1688
1691
|
# the slot.
|
@@ -1702,19 +1705,19 @@ class TaskJuggler
|
|
1702
1705
|
return unless found
|
1703
1706
|
end
|
1704
1707
|
|
1705
|
-
|
1706
|
-
next unless allocation.onShift?(
|
1708
|
+
@allocate.each do |allocation|
|
1709
|
+
next unless allocation.onShift?(@currentSlotIdx)
|
1707
1710
|
|
1708
|
-
# In case we have a persistent allocation we need to check if there
|
1709
|
-
# already a locked resource and use it.
|
1710
|
-
if allocation.
|
1711
|
-
bookResource(allocation.lockedResource
|
1711
|
+
# In case we have a persistent allocation we need to check if there
|
1712
|
+
# is already a locked resource and use it.
|
1713
|
+
if allocation.lockedResource
|
1714
|
+
bookResource(allocation.lockedResource)
|
1712
1715
|
else
|
1713
1716
|
# If not, we create a list of candidates in the proper order and
|
1714
1717
|
# assign the first one available.
|
1715
1718
|
allocation.candidates(@scenarioIdx).each do |candidate|
|
1716
|
-
if bookResource(candidate
|
1717
|
-
allocation.lockedResource = candidate
|
1719
|
+
if bookResource(candidate)
|
1720
|
+
allocation.lockedResource = candidate if allocation.persistent
|
1718
1721
|
break
|
1719
1722
|
end
|
1720
1723
|
end
|
@@ -1722,49 +1725,46 @@ class TaskJuggler
|
|
1722
1725
|
end
|
1723
1726
|
end
|
1724
1727
|
|
1725
|
-
def bookResource(resource
|
1728
|
+
def bookResource(resource)
|
1726
1729
|
booked = false
|
1727
1730
|
resource.allLeaves.each do |r|
|
1728
1731
|
# Prevent overbooking when multiple resources are allocated and
|
1729
1732
|
# available. If the task has allocation limits we need to make sure
|
1730
1733
|
# that none of them is already exceeded.
|
1731
|
-
break if
|
1732
|
-
!limitsOk?(
|
1734
|
+
break if @effort > 0 && @doneEffort >= @effort ||
|
1735
|
+
!limitsOk?(@currentSlotIdx, resource)
|
1733
1736
|
|
1734
|
-
if r.book(@scenarioIdx,
|
1737
|
+
if r.book(@scenarioIdx, @currentSlotIdx, @property)
|
1735
1738
|
# For effort based task we adjust the the start end (as defined by
|
1736
1739
|
# the scheduling direction) to align with the first booked time
|
1737
1740
|
# slot.
|
1738
|
-
if
|
1739
|
-
if
|
1740
|
-
@
|
1741
|
+
if @effort > 0 && @assignedresources.empty?
|
1742
|
+
if @forward
|
1743
|
+
@start = @project.idxToDate(@currentSlotIdx)
|
1741
1744
|
Log << "Task #{@property.fullId} first assignment: " +
|
1742
|
-
"#{
|
1743
|
-
|
1744
|
-
task.propagateDate(@scenarioIdx,
|
1745
|
+
"#{@start} -> #{@end}"
|
1746
|
+
@startsuccs.each do |task, onEnd|
|
1747
|
+
task.propagateDate(@scenarioIdx, @start, false, true)
|
1745
1748
|
end
|
1746
1749
|
else
|
1747
|
-
@
|
1750
|
+
@end = @project.idxToDate(@currentSlotIdx + 1)
|
1748
1751
|
Log << "Task #{@property.fullId} last assignment: " +
|
1749
|
-
"#{
|
1750
|
-
|
1751
|
-
task.propagateDate(@scenarioIdx,
|
1752
|
+
"#{@start} -> #{@end}"
|
1753
|
+
@endpreds.each do |task, onEnd|
|
1754
|
+
task.propagateDate(@scenarioIdx, @end, true, true)
|
1752
1755
|
end
|
1753
1756
|
end
|
1754
1757
|
end
|
1755
1758
|
|
1756
|
-
@tentativeStart = @project.idxToDate(sbIdx)
|
1757
|
-
@tentativeEnd = @project.idxToDate(sbIdx + 1)
|
1758
|
-
|
1759
1759
|
@doneEffort += r['efficiency', @scenarioIdx]
|
1760
1760
|
# Limits do not take efficiency into account. Limits are usage limits,
|
1761
1761
|
# not effort limits.
|
1762
|
-
@
|
1763
|
-
limit.inc(
|
1762
|
+
@allLimits.each do |limit|
|
1763
|
+
limit.inc(@currentSlotIdx, resource)
|
1764
1764
|
end
|
1765
1765
|
|
1766
|
-
unless
|
1767
|
-
@
|
1766
|
+
unless @assignedresources.include?(r)
|
1767
|
+
@assignedresources << r
|
1768
1768
|
end
|
1769
1769
|
booked = true
|
1770
1770
|
end
|
@@ -1773,13 +1773,13 @@ class TaskJuggler
|
|
1773
1773
|
booked
|
1774
1774
|
end
|
1775
1775
|
|
1776
|
-
# Check if all of the task limits are not exceded at the given
|
1776
|
+
# Check if all of the task limits are not exceded at the given _sbIdx_. If
|
1777
1777
|
# a _resource_ is provided, the limit for that particular resource is
|
1778
1778
|
# checked. If no resource is provided, only non-resource-specific limits
|
1779
1779
|
# are checked.
|
1780
|
-
def limitsOk?(
|
1781
|
-
@
|
1782
|
-
return false
|
1780
|
+
def limitsOk?(sbIdx, resource = nil)
|
1781
|
+
@allLimits.each do |limit|
|
1782
|
+
return false unless limit.ok?(sbIdx, true, resource)
|
1783
1783
|
end
|
1784
1784
|
true
|
1785
1785
|
end
|
@@ -1797,67 +1797,137 @@ class TaskJuggler
|
|
1797
1797
|
|
1798
1798
|
# Register the user provided bookings with the Resource scoreboards. A
|
1799
1799
|
# booking describes the assignment of a Resource to a certain Task for a
|
1800
|
-
# specified
|
1800
|
+
# specified TimeInterval.
|
1801
1801
|
def bookBookings
|
1802
|
-
|
1803
|
-
|
1802
|
+
firstSlotIdx = nil
|
1803
|
+
lastSlotIdx = nil
|
1804
1804
|
findBookings.each do |booking|
|
1805
1805
|
unless booking.resource.leaf?
|
1806
1806
|
error('booking_resource_not_leaf',
|
1807
1807
|
"Booked resources may not be group resources",
|
1808
1808
|
booking.sourceFileInfo)
|
1809
1809
|
end
|
1810
|
-
unless
|
1810
|
+
unless @forward || @scheduled
|
1811
1811
|
error('booking_forward_only',
|
1812
1812
|
"Only forward scheduled tasks may have booking statements.")
|
1813
1813
|
end
|
1814
1814
|
booking.intervals.each do |interval|
|
1815
1815
|
startIdx = @project.dateToIdx(interval.start, false)
|
1816
|
-
date = interval.start
|
1817
1816
|
endIdx = @project.dateToIdx(interval.end, false)
|
1818
|
-
tEnd = nil
|
1819
1817
|
startIdx.upto(endIdx - 1) do |idx|
|
1820
|
-
tEnd = date + slotDuration
|
1821
1818
|
if booking.resource.bookBooking(@scenarioIdx, idx, booking)
|
1822
1819
|
# Booking was successful for this time slot.
|
1823
1820
|
@doneEffort += booking.resource['efficiency', @scenarioIdx]
|
1824
1821
|
|
1825
|
-
#
|
1826
|
-
#
|
1827
|
-
|
1828
|
-
|
1829
|
-
@tentativeEnd = tEnd if @tentativeEnd.nil? || @tentativeEnd < tEnd
|
1830
|
-
if !scheduled && (a('start').nil? || date < a('start'))
|
1831
|
-
@property['start', @scenarioIdx] = date
|
1832
|
-
Log << "Task #{@property.fullId} first booking: " +
|
1833
|
-
"#{a('start')} -> #{a('end')}"
|
1834
|
-
end
|
1822
|
+
# Store the indexes of the first slot and the slot after the
|
1823
|
+
# last slot.
|
1824
|
+
firstSlotIdx = idx if !firstSlotIdx || firstSlotIdx > idx
|
1825
|
+
lastSlotIdx = idx if !lastSlotIdx || lastSlotIdx < idx
|
1835
1826
|
|
1836
|
-
unless
|
1837
|
-
@
|
1827
|
+
unless @assignedresources.include?(booking.resource)
|
1828
|
+
@assignedresources << booking.resource
|
1838
1829
|
end
|
1839
1830
|
end
|
1840
|
-
if a('length') > 0 && @project.isWorkingTime(date)
|
1841
|
-
# For tasks with a 'length' we track the covered work time and
|
1842
|
-
# set the task to 'scheduled' when we have enough length.
|
1843
|
-
@doneLength += 1
|
1844
|
-
if !scheduled && @doneLength >= a('length')
|
1845
|
-
@property['end', @scenarioIdx] = tEnd
|
1846
|
-
@property['scheduled', @scenarioIdx] = true
|
1847
|
-
end
|
1848
|
-
end
|
1849
|
-
date = tEnd
|
1850
1831
|
end
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1832
|
+
end
|
1833
|
+
end
|
1834
|
+
|
1835
|
+
# For effort based tasks, or tasks without a start date, with bookings
|
1836
|
+
# that have not yet been marked as scheduled we set the start date to
|
1837
|
+
# the date of the first booked slot.
|
1838
|
+
if (@start.nil? || (@doneEffort > 0 && @effort > 0)) &&
|
1839
|
+
!@scheduled && firstSlotIdx
|
1840
|
+
firstSlotDate = @project.idxToDate(firstSlotIdx)
|
1841
|
+
if @start.nil? || firstSlotDate > @start
|
1842
|
+
@start = firstSlotDate
|
1843
|
+
Log << "Task #{@property.fullId} first booking: " +
|
1844
|
+
"#{@start} -> #{@end}"
|
1845
|
+
end
|
1846
|
+
end
|
1847
|
+
|
1848
|
+
# Check if the the duration criteria has already been reached by the
|
1849
|
+
# supplied bookings and set the task end to the last booked slot.
|
1850
|
+
# Also the task is marked as scheduled.
|
1851
|
+
if lastSlotIdx && !@scheduled
|
1852
|
+
tentativeEnd = @project.idxToDate(lastSlotIdx + 1)
|
1853
|
+
slotDuration = @project['scheduleGranularity']
|
1854
|
+
|
1855
|
+
if @effort > 0
|
1856
|
+
if @doneEffort >= @effort
|
1857
|
+
@end = tentativeEnd
|
1858
|
+
@scheduled = true
|
1859
|
+
end
|
1860
|
+
elsif @length > 0
|
1861
|
+
@doneLength = 0
|
1862
|
+
startIdx = @project.dateToIdx(date = @start)
|
1863
|
+
endIdx = @project.dateToIdx(@project['now'])
|
1864
|
+
startIdx.upto(endIdx) do |idx|
|
1865
|
+
@doneLength += 1 if @project.isWorkingTime(idx)
|
1866
|
+
date += slotDuration
|
1867
|
+
# Continue not only until the @length has been reached, but also
|
1868
|
+
# the tentativeEnd date. This allows us to detect overbookings.
|
1869
|
+
if @doneLength >= @length && date >= tentativeEnd
|
1870
|
+
endDate = @project.idxToDate(idx + 1)
|
1871
|
+
@end = [ endDate, tentativeEnd ].max
|
1872
|
+
@scheduled = true
|
1873
|
+
break
|
1857
1874
|
end
|
1858
1875
|
end
|
1876
|
+
elsif @duration > 0
|
1877
|
+
@doneDuration = ((tentativeEnd - @start) / slotDuration).to_i
|
1878
|
+
if @doneDuration >= @duration
|
1879
|
+
@end = tentativeEnd
|
1880
|
+
@scheduled = true
|
1881
|
+
elsif @duration * slotDuration < (@project['now'] - @start)
|
1882
|
+
# This handles the case where the bookings don't provide enough
|
1883
|
+
# @doneDuration to reach @duration, but the now date would be
|
1884
|
+
# after the @start + @duration date.
|
1885
|
+
@end = @start + @duration * slotDuration
|
1886
|
+
@scheduled = true
|
1887
|
+
end
|
1888
|
+
end
|
1889
|
+
end
|
1890
|
+
|
1891
|
+
# If the task has bookings, we assume that the bookings describe all
|
1892
|
+
# work up to the 'now' date.
|
1893
|
+
if @doneEffort > 0
|
1894
|
+
@currentSlotIdx = @project.dateToIdx(@project['now'])
|
1895
|
+
end
|
1896
|
+
|
1897
|
+
# Finally, we check if the bookings caused more effort, length or
|
1898
|
+
# duration than was requested by the user. This is only flagged as a
|
1899
|
+
# warning.
|
1900
|
+
if @effort > 0
|
1901
|
+
effort = @project.slotsToDays(@doneEffort)
|
1902
|
+
effortHours = effort * @project['dailyworkinghours']
|
1903
|
+
requestedEffort = @project.slotsToDays(@effort)
|
1904
|
+
requestedEffortHours = requestedEffort * @project['dailyworkinghours']
|
1905
|
+
if effort > requestedEffort
|
1906
|
+
warning('overbooked_effort',
|
1907
|
+
"The total effort (#{effort}d or #{effortHours}h) of the " +
|
1908
|
+
"provided bookings for task #{@property.fullId} exceeds " +
|
1909
|
+
"the specified effort of #{requestedEffort}d or " +
|
1910
|
+
"#{requestedEffortHours}h.")
|
1859
1911
|
end
|
1860
1912
|
end
|
1913
|
+
if @length > 0 && @doneLength > @length
|
1914
|
+
length = @project.slotsToDays(@doneLength)
|
1915
|
+
requestedLength = @project.slotsToDays(@length)
|
1916
|
+
warning('overbooked_length',
|
1917
|
+
"The total length (#{length}d) of the provided bookings " +
|
1918
|
+
"for task #{@property.fullId} exceeds the specified length of " +
|
1919
|
+
"#{requestedLength}d.")
|
1920
|
+
end
|
1921
|
+
if @duration > 0 && @doneDuration > @duration
|
1922
|
+
duration = @doneDuration * @project['scheduleGranularity'] /
|
1923
|
+
(60.0 * 60 * 24)
|
1924
|
+
requestedDuration = @duration * @project['scheduleGranularity'] /
|
1925
|
+
(60.0 * 60 * 24)
|
1926
|
+
warning('overbooked_duration',
|
1927
|
+
"The total duration (#{duration}d) of the provided bookings " +
|
1928
|
+
"for task #{@property.fullId} exceeds the specified duration " +
|
1929
|
+
"of #{requestedDuration}d.")
|
1930
|
+
end
|
1861
1931
|
end
|
1862
1932
|
|
1863
1933
|
# This function checks if the task has a dependency on another task or
|
@@ -1943,23 +2013,23 @@ class TaskJuggler
|
|
1943
2013
|
# dependency no matter how it was set.
|
1944
2014
|
unless atEnd
|
1945
2015
|
# Row 1
|
1946
|
-
|
2016
|
+
@startpreds.each do |task, onEnd|
|
1947
2017
|
if (onEnd && (task['forward', @scenarioIdx] ||
|
1948
2018
|
task['end', @scenarioIdx])) || !onEnd
|
1949
2019
|
return true
|
1950
2020
|
end
|
1951
2021
|
end
|
1952
2022
|
# Row 2
|
1953
|
-
|
2023
|
+
@startsuccs.each do |task, onEnd|
|
1954
2024
|
return true if task[onEnd ? 'end' : 'start', @scenarioIdx]
|
1955
2025
|
end
|
1956
2026
|
else
|
1957
2027
|
# Row 3
|
1958
|
-
|
2028
|
+
@endpreds.each do |task, onEnd|
|
1959
2029
|
return true if task[onEnd ? 'end' : 'start', @scenarioIdx]
|
1960
2030
|
end
|
1961
2031
|
# Row 4
|
1962
|
-
|
2032
|
+
@endsuccs.each do |task, onEnd|
|
1963
2033
|
if (!onEnd && (!task['forward', @scenarioIdx] ||
|
1964
2034
|
task['start', @scenarioIdx])) || onEnd
|
1965
2035
|
return true
|
@@ -1977,11 +2047,11 @@ class TaskJuggler
|
|
1977
2047
|
@isRunAway = true
|
1978
2048
|
end
|
1979
2049
|
|
1980
|
-
# This function determines if a task is
|
2050
|
+
# This function determines if a task is a milestones and marks it
|
1981
2051
|
# accordingly.
|
1982
2052
|
def markMilestone
|
1983
2053
|
return if @property.container? || hasDurationSpec? ||
|
1984
|
-
|
2054
|
+
!@booking.empty? || !@allocate.empty?
|
1985
2055
|
|
1986
2056
|
# The following cases qualify for an automatic milestone promotion.
|
1987
2057
|
# - --> -
|
@@ -1992,19 +2062,19 @@ class TaskJuggler
|
|
1992
2062
|
# - <-- |
|
1993
2063
|
# - <-- -D
|
1994
2064
|
# - <-- |D
|
1995
|
-
hasStartSpec =
|
1996
|
-
hasEndSpec =
|
2065
|
+
hasStartSpec = !@start.nil? || !@depends.empty?
|
2066
|
+
hasEndSpec = !@end.nil? || !@precedes.empty?
|
1997
2067
|
|
1998
|
-
@
|
1999
|
-
|
2000
|
-
|
2001
|
-
(!hasStartSpec && !hasEndSpec)
|
2068
|
+
@milestone = (hasStartSpec && @forward && !hasEndSpec) ||
|
2069
|
+
(!hasStartSpec && !@forward && hasEndSpec) ||
|
2070
|
+
(!hasStartSpec && !hasEndSpec)
|
2002
2071
|
end
|
2003
2072
|
|
2004
2073
|
def checkDependency(dependency, depType)
|
2074
|
+
depList = instance_variable_get(('@' + depType).intern)
|
2005
2075
|
if (depTask = dependency.resolve(@project)).nil?
|
2006
2076
|
# Remove the broken dependency. It could cause trouble later on.
|
2007
|
-
|
2077
|
+
depList.delete(dependency)
|
2008
2078
|
error('task_depend_unknown',
|
2009
2079
|
"Task #{@property.fullId} has unknown #{depType} " +
|
2010
2080
|
"#{dependency.taskId}")
|
@@ -2012,14 +2082,14 @@ class TaskJuggler
|
|
2012
2082
|
|
2013
2083
|
if depTask == @property
|
2014
2084
|
# Remove the broken dependency. It could cause trouble later on.
|
2015
|
-
|
2085
|
+
depList.delete(dependency)
|
2016
2086
|
error('task_depend_self', "Task #{@property.fullId} cannot " +
|
2017
2087
|
"depend on self")
|
2018
2088
|
end
|
2019
2089
|
|
2020
2090
|
if depTask.isChildOf?(@property)
|
2021
2091
|
# Remove the broken dependency. It could cause trouble later on.
|
2022
|
-
|
2092
|
+
depList.delete(dependency)
|
2023
2093
|
error('task_depend_child',
|
2024
2094
|
"Task #{@property.fullId} cannot depend on child " +
|
2025
2095
|
"#{depTask.fullId}")
|
@@ -2027,16 +2097,16 @@ class TaskJuggler
|
|
2027
2097
|
|
2028
2098
|
if @property.isChildOf?(depTask)
|
2029
2099
|
# Remove the broken dependency. It could cause trouble later on.
|
2030
|
-
|
2100
|
+
depList.delete(dependency)
|
2031
2101
|
error('task_depend_parent',
|
2032
2102
|
"Task #{@property.fullId} cannot depend on parent " +
|
2033
2103
|
"#{depTask.fullId}")
|
2034
2104
|
end
|
2035
2105
|
|
2036
|
-
|
2106
|
+
depList.each do |dep|
|
2037
2107
|
if dep.task == depTask && !dep.equal?(dependency)
|
2038
2108
|
# Remove the broken dependency. It could cause trouble later on.
|
2039
|
-
|
2109
|
+
depList.delete(dependency)
|
2040
2110
|
error('task_depend_multi',
|
2041
2111
|
"No need to specify dependency #{depTask.fullId} multiple " +
|
2042
2112
|
"times for task #{@property.fullId}.")
|
@@ -2111,23 +2181,21 @@ class TaskJuggler
|
|
2111
2181
|
# If the user provided a completion degree we are not touching it.
|
2112
2182
|
if @property.provided('complete', @scenarioIdx)
|
2113
2183
|
calcStatus
|
2114
|
-
return
|
2184
|
+
return @complete
|
2115
2185
|
end
|
2116
2186
|
|
2117
2187
|
# We cannot compute a completion degree without a start or end date.
|
2118
|
-
if
|
2119
|
-
@
|
2120
|
-
@
|
2188
|
+
if @start.nil? || @end.nil?
|
2189
|
+
@complete = 0.0
|
2190
|
+
@status = 'unknown'
|
2121
2191
|
return nil
|
2122
2192
|
end
|
2123
2193
|
|
2124
2194
|
completion = 0.0
|
2125
|
-
if
|
2195
|
+
if @milestone
|
2126
2196
|
# Milestones are either 0% or 100% complete.
|
2127
|
-
@
|
2128
|
-
|
2129
|
-
@property['status', @scenarioIdx] =
|
2130
|
-
a('end') <= @project['now'] ? 'done' : 'not reached'
|
2197
|
+
@complete = completion = @end <= @project['now'] ? 100.0 : 0.0
|
2198
|
+
@status = @end <= @project['now'] ? 'done' : 'not reached'
|
2131
2199
|
else
|
2132
2200
|
# The task is in progress. Calculate the current completion
|
2133
2201
|
# degree.
|
@@ -2140,26 +2208,26 @@ class TaskJuggler
|
|
2140
2208
|
completion += comp
|
2141
2209
|
end
|
2142
2210
|
completion /= @property.children.length
|
2143
|
-
elsif
|
2211
|
+
elsif @end <= @project['now']
|
2144
2212
|
# The task has ended already. It's 100% complete.
|
2145
2213
|
completion = 100.0
|
2146
|
-
elsif @project['now'] <=
|
2214
|
+
elsif @project['now'] <= @start
|
2147
2215
|
# The task has not started yet. Its' 0% complete.
|
2148
2216
|
completion = 0.0
|
2149
|
-
elsif
|
2217
|
+
elsif @effort > 0
|
2150
2218
|
# Effort based leaf tasks. The completion degree is the percentage
|
2151
2219
|
# of effort that has been done already.
|
2152
|
-
done = getEffectiveWork(@project.dateToIdx(
|
2220
|
+
done = getEffectiveWork(@project.dateToIdx(@start, false),
|
2153
2221
|
@project.dateToIdx(@project['now']))
|
2154
2222
|
total = @project.convertToDailyLoad(
|
2155
|
-
|
2223
|
+
@effort * @project['scheduleGranularity'])
|
2156
2224
|
completion = done / total * 100.0
|
2157
2225
|
else
|
2158
2226
|
# Length/duration leaf tasks.
|
2159
|
-
completion = ((@project['now'] -
|
2160
|
-
(
|
2227
|
+
completion = ((@project['now'] - @start) /
|
2228
|
+
(@end - @start)) * 100.0
|
2161
2229
|
end
|
2162
|
-
@
|
2230
|
+
@complete = completion
|
2163
2231
|
calcStatus
|
2164
2232
|
end
|
2165
2233
|
|
@@ -2168,14 +2236,13 @@ class TaskJuggler
|
|
2168
2236
|
|
2169
2237
|
# Calculate the status of the task based on the 'complete' attribute.
|
2170
2238
|
def calcStatus
|
2171
|
-
@
|
2172
|
-
|
2173
|
-
|
2174
|
-
|
2175
|
-
|
2176
|
-
|
2177
|
-
|
2178
|
-
end
|
2239
|
+
@status = if @complete == 0.0
|
2240
|
+
'not started'
|
2241
|
+
elsif @complete >= 100.0
|
2242
|
+
'done'
|
2243
|
+
else
|
2244
|
+
'in progress'
|
2245
|
+
end
|
2179
2246
|
end
|
2180
2247
|
|
2181
2248
|
# Recursively compile a list of Task properties which depend on the
|
@@ -2187,12 +2254,12 @@ class TaskJuggler
|
|
2187
2254
|
# A target must be a leaf function that has no direct or indirect
|
2188
2255
|
# (through parent) following tasks. Only milestones are recognized as
|
2189
2256
|
# targets.
|
2190
|
-
if @property.leaf? && !hasPredecessors &&
|
2257
|
+
if @property.leaf? && !hasPredecessors && @milestone
|
2191
2258
|
list << @property
|
2192
2259
|
return
|
2193
2260
|
end
|
2194
2261
|
|
2195
|
-
|
2262
|
+
@startpreds.each do |t, onEnd|
|
2196
2263
|
t.inputs(@scenarioIdx, list, false)
|
2197
2264
|
end
|
2198
2265
|
|
@@ -2220,12 +2287,12 @@ class TaskJuggler
|
|
2220
2287
|
# A target must be a leaf function that has no direct or indirect
|
2221
2288
|
# (through parent) following tasks. Only milestones are recognized as
|
2222
2289
|
# targets.
|
2223
|
-
if @property.leaf? && !hasSuccessors &&
|
2290
|
+
if @property.leaf? && !hasSuccessors && @milestone
|
2224
2291
|
list << @property
|
2225
2292
|
return
|
2226
2293
|
end
|
2227
2294
|
|
2228
|
-
|
2295
|
+
@endsuccs.each do |t, onEnd|
|
2229
2296
|
t.targets(@scenarioIdx, list, false)
|
2230
2297
|
end
|
2231
2298
|
|
@@ -2260,7 +2327,7 @@ class TaskJuggler
|
|
2260
2327
|
|
2261
2328
|
# If there are no chargeset defined for this task, we don't need to
|
2262
2329
|
# compute the resource related or other cost.
|
2263
|
-
unless
|
2330
|
+
unless @chargeset.empty?
|
2264
2331
|
resourceCost = 0.0
|
2265
2332
|
otherCost = 0.0
|
2266
2333
|
|
@@ -2270,27 +2337,27 @@ class TaskJuggler
|
|
2270
2337
|
resourceCost = resource.cost(@scenarioIdx, startIdx, endIdx,
|
2271
2338
|
@property)
|
2272
2339
|
else
|
2273
|
-
|
2340
|
+
@assignedresources.each do |r|
|
2274
2341
|
resourceCost += r.cost(@scenarioIdx, startIdx, endIdx, @property)
|
2275
2342
|
end
|
2276
2343
|
end
|
2277
2344
|
end
|
2278
2345
|
|
2279
|
-
unless
|
2346
|
+
unless @charge.empty?
|
2280
2347
|
# Add one-time and periodic charges to the amount.
|
2281
2348
|
startDate = startIdx.is_a?(TjTime) ? startIdx :
|
2282
2349
|
@project.idxToDate(startIdx)
|
2283
2350
|
endDate = endIdx.is_a?(TjTime) ? endIdx :
|
2284
2351
|
@project.idxToDate(endIdx)
|
2285
|
-
iv =
|
2286
|
-
|
2352
|
+
iv = TimeInterval.new(startDate, endDate)
|
2353
|
+
@charge.each do |charge|
|
2287
2354
|
otherCost += charge.turnover(iv)
|
2288
2355
|
end
|
2289
2356
|
end
|
2290
2357
|
|
2291
2358
|
totalCost = resourceCost + otherCost
|
2292
2359
|
# Now weight the total cost by the share of the account
|
2293
|
-
|
2360
|
+
@chargeset.each do |set|
|
2294
2361
|
set.each do |accnt, share|
|
2295
2362
|
if share > 0.0 && (accnt == account || accnt.isChildOf?(account))
|
2296
2363
|
amount += totalCost * share
|