taskjuggler 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +280 -0
- data/README +31 -0
- data/Rakefile +20 -0
- data/benchmarks/UTF-8-Strings.rb +58 -0
- data/benchmarks/allocate.tjp +30 -0
- data/benchmarks/booking.tjp +62 -0
- data/benchmarks/depends.tjp +112 -0
- data/benchmarks/htmltaskreport.tjp +45 -0
- data/benchmarks/runbench.rb +24 -0
- data/bin/tj3 +3 -0
- data/bin/tj3man +3 -0
- data/doc/classes/AppConfig.html +808 -0
- data/doc/classes/Arguments.html +226 -0
- data/doc/classes/String.html +395 -0
- data/doc/classes/TaskJuggler.html +1358 -0
- data/doc/classes/TaskJuggler/Account.html +257 -0
- data/doc/classes/TaskJuggler/AccountScenario.html +218 -0
- data/doc/classes/TaskJuggler/Allocation.html +419 -0
- data/doc/classes/TaskJuggler/AllocationAttribute.html +291 -0
- data/doc/classes/TaskJuggler/AttributeBase.html +608 -0
- data/doc/classes/TaskJuggler/AttributeDefinition.html +259 -0
- data/doc/classes/TaskJuggler/Booking.html +307 -0
- data/doc/classes/TaskJuggler/BookingListAttribute.html +263 -0
- data/doc/classes/TaskJuggler/BooleanAttribute.html +261 -0
- data/doc/classes/TaskJuggler/CSVFile.html +325 -0
- data/doc/classes/TaskJuggler/Charge.html +279 -0
- data/doc/classes/TaskJuggler/ChargeListAttribute.html +229 -0
- data/doc/classes/TaskJuggler/ChargeSet.html +440 -0
- data/doc/classes/TaskJuggler/ChargeSetListAttribute.html +276 -0
- data/doc/classes/TaskJuggler/ColumnTable.html +260 -0
- data/doc/classes/TaskJuggler/DateAttribute.html +194 -0
- data/doc/classes/TaskJuggler/DependencyListAttribute.html +267 -0
- data/doc/classes/TaskJuggler/DurationAttribute.html +229 -0
- data/doc/classes/TaskJuggler/FixnumAttribute.html +194 -0
- data/doc/classes/TaskJuggler/FlagListAttribute.html +263 -0
- data/doc/classes/TaskJuggler/FloatAttribute.html +229 -0
- data/doc/classes/TaskJuggler/GanttChart.html +667 -0
- data/doc/classes/TaskJuggler/GanttContainer.html +441 -0
- data/doc/classes/TaskJuggler/GanttHeader.html +280 -0
- data/doc/classes/TaskJuggler/GanttHeaderScaleItem.html +245 -0
- data/doc/classes/TaskJuggler/GanttLine.html +398 -0
- data/doc/classes/TaskJuggler/GanttLoadStack.html +327 -0
- data/doc/classes/TaskJuggler/GanttMilestone.html +415 -0
- data/doc/classes/TaskJuggler/GanttRouter.html +425 -0
- data/doc/classes/TaskJuggler/GanttTaskBar.html +429 -0
- data/doc/classes/TaskJuggler/HTMLDocument.html +240 -0
- data/doc/classes/TaskJuggler/HTMLGraphics.html +231 -0
- data/doc/classes/TaskJuggler/Interval.html +552 -0
- data/doc/classes/TaskJuggler/IntervalListAttribute.html +267 -0
- data/doc/classes/TaskJuggler/KeywordDocumentation.html +796 -0
- data/doc/classes/TaskJuggler/Limits.html +416 -0
- data/doc/classes/TaskJuggler/Limits/Limit.html +381 -0
- data/doc/classes/TaskJuggler/LimitsAttribute.html +261 -0
- data/doc/classes/TaskJuggler/Log.html +613 -0
- data/doc/classes/TaskJuggler/LogicalAttribute.html +226 -0
- data/doc/classes/TaskJuggler/LogicalExpression.html +251 -0
- data/doc/classes/TaskJuggler/LogicalFlag.html +229 -0
- data/doc/classes/TaskJuggler/LogicalFunction.html +324 -0
- data/doc/classes/TaskJuggler/LogicalOperation.html +299 -0
- data/doc/classes/TaskJuggler/Macro.html +194 -0
- data/doc/classes/TaskJuggler/MacroParser.html +360 -0
- data/doc/classes/TaskJuggler/MacroTable.html +366 -0
- data/doc/classes/TaskJuggler/Message.html +281 -0
- data/doc/classes/TaskJuggler/MessageHandler.html +215 -0
- data/doc/classes/TaskJuggler/Project.html +1606 -0
- data/doc/classes/TaskJuggler/ProjectFileParser.html +412 -0
- data/doc/classes/TaskJuggler/PropertyList.html +597 -0
- data/doc/classes/TaskJuggler/PropertySet.html +1200 -0
- data/doc/classes/TaskJuggler/PropertyTreeNode.html +1449 -0
- data/doc/classes/TaskJuggler/Query.html +600 -0
- data/doc/classes/TaskJuggler/RealFormat.html +252 -0
- data/doc/classes/TaskJuggler/ReferenceAttribute.html +194 -0
- data/doc/classes/TaskJuggler/Report.html +528 -0
- data/doc/classes/TaskJuggler/ReportElement.html +1070 -0
- data/doc/classes/TaskJuggler/ReportTable.html +497 -0
- data/doc/classes/TaskJuggler/ReportTableCell.html +518 -0
- data/doc/classes/TaskJuggler/ReportTableColumn.html +364 -0
- data/doc/classes/TaskJuggler/ReportTableElement.html +644 -0
- data/doc/classes/TaskJuggler/ReportTableLegend.html +343 -0
- data/doc/classes/TaskJuggler/ReportTableLine.html +431 -0
- data/doc/classes/TaskJuggler/Resource.html +211 -0
- data/doc/classes/TaskJuggler/ResourceListAttribute.html +267 -0
- data/doc/classes/TaskJuggler/ResourceListRE.html +249 -0
- data/doc/classes/TaskJuggler/ResourceScenario.html +1137 -0
- data/doc/classes/TaskJuggler/RichText.html +537 -0
- data/doc/classes/TaskJuggler/RichTextAttribute.html +229 -0
- data/doc/classes/TaskJuggler/RichTextDocument.html +418 -0
- data/doc/classes/TaskJuggler/RichTextElement.html +829 -0
- data/doc/classes/TaskJuggler/RichTextException.html +212 -0
- data/doc/classes/TaskJuggler/RichTextParser.html +317 -0
- data/doc/classes/TaskJuggler/RichTextProtocolExample.html +303 -0
- data/doc/classes/TaskJuggler/RichTextProtocolHandler.html +194 -0
- data/doc/classes/TaskJuggler/RichTextScanner.html +561 -0
- data/doc/classes/TaskJuggler/RichTextSnip.html +364 -0
- data/doc/classes/TaskJuggler/RichTextSyntaxRules.html +883 -0
- data/doc/classes/TaskJuggler/Scenario.html +163 -0
- data/doc/classes/TaskJuggler/ScenarioData.html +354 -0
- data/doc/classes/TaskJuggler/Scoreboard.html +638 -0
- data/doc/classes/TaskJuggler/Shift.html +255 -0
- data/doc/classes/TaskJuggler/ShiftAssignment.html +488 -0
- data/doc/classes/TaskJuggler/ShiftAssignments.html +715 -0
- data/doc/classes/TaskJuggler/ShiftAssignmentsAttribute.html +261 -0
- data/doc/classes/TaskJuggler/ShiftScenario.html +282 -0
- data/doc/classes/TaskJuggler/SourceFileInfo.html +247 -0
- data/doc/classes/TaskJuggler/StringAttribute.html +229 -0
- data/doc/classes/TaskJuggler/SymbolAttribute.html +194 -0
- data/doc/classes/TaskJuggler/SyntaxReference.html +516 -0
- data/doc/classes/TaskJuggler/TOCEntry.html +242 -0
- data/doc/classes/TaskJuggler/TableColumnDefinition.html +273 -0
- data/doc/classes/TaskJuggler/TableOfContents.html +256 -0
- data/doc/classes/TaskJuggler/Task.html +203 -0
- data/doc/classes/TaskJuggler/TaskDependency.html +251 -0
- data/doc/classes/TaskJuggler/TaskListAttribute.html +267 -0
- data/doc/classes/TaskJuggler/TaskListRE.html +250 -0
- data/doc/classes/TaskJuggler/TaskScenario.html +2206 -0
- data/doc/classes/TaskJuggler/TextParser.html +670 -0
- data/doc/classes/TaskJuggler/TextParser/Pattern.html +923 -0
- data/doc/classes/TaskJuggler/TextParser/Rule.html +779 -0
- data/doc/classes/TaskJuggler/TextParser/StackElement.html +267 -0
- data/doc/classes/TaskJuggler/TextParser/TextParserResultArray.html +212 -0
- data/doc/classes/TaskJuggler/TextParser/TokenDoc.html +221 -0
- data/doc/classes/TaskJuggler/TextScanner.html +708 -0
- data/doc/classes/TaskJuggler/TextScanner/BufferStreamHandle.html +355 -0
- data/doc/classes/TaskJuggler/TextScanner/FileStreamHandle.html +341 -0
- data/doc/classes/TaskJuggler/TextScanner/StreamHandle.html +260 -0
- data/doc/classes/TaskJuggler/TjException.html +185 -0
- data/doc/classes/TaskJuggler/TjTime.html +1845 -0
- data/doc/classes/TaskJuggler/TjpExample.html +310 -0
- data/doc/classes/TaskJuggler/TjpExportRE.html +329 -0
- data/doc/classes/TaskJuggler/TjpSyntaxRules.html +8928 -0
- data/doc/classes/TaskJuggler/UserManual.html +606 -0
- data/doc/classes/TaskJuggler/WorkingHours.html +582 -0
- data/doc/classes/TaskJuggler/WorkingHoursAttribute.html +284 -0
- data/doc/classes/TaskJuggler/XMLBlob.html +207 -0
- data/doc/classes/TaskJuggler/XMLComment.html +206 -0
- data/doc/classes/TaskJuggler/XMLDocument.html +293 -0
- data/doc/classes/TaskJuggler/XMLElement.html +341 -0
- data/doc/classes/TaskJuggler/XMLNamedText.html +174 -0
- data/doc/classes/TaskJuggler/XMLText.html +221 -0
- data/doc/files/COPYING.html +448 -0
- data/doc/files/README.html +134 -0
- data/doc/files/lib/AccountScenario_rb.html +116 -0
- data/doc/files/lib/Account_rb.html +118 -0
- data/doc/files/lib/Allocation_rb.html +118 -0
- data/doc/files/lib/AppConfig_rb.html +116 -0
- data/doc/files/lib/AttributeBase_rb.html +106 -0
- data/doc/files/lib/AttributeDefinition_rb.html +106 -0
- data/doc/files/lib/Attributes_rb.html +130 -0
- data/doc/files/lib/Booking_rb.html +106 -0
- data/doc/files/lib/ChargeSet_rb.html +116 -0
- data/doc/files/lib/Charge_rb.html +116 -0
- data/doc/files/lib/HTMLDocument_rb.html +116 -0
- data/doc/files/lib/Interval_rb.html +116 -0
- data/doc/files/lib/KeywordDocumentation_rb.html +122 -0
- data/doc/files/lib/Limits_rb.html +116 -0
- data/doc/files/lib/Log_rb.html +116 -0
- data/doc/files/lib/LogicalExpression_rb.html +122 -0
- data/doc/files/lib/LogicalFlag_rb.html +116 -0
- data/doc/files/lib/LogicalFunction_rb.html +116 -0
- data/doc/files/lib/LogicalOperation_rb.html +116 -0
- data/doc/files/lib/MacroParser_rb.html +118 -0
- data/doc/files/lib/MacroTable_rb.html +122 -0
- data/doc/files/lib/MessageHandler_rb.html +106 -0
- data/doc/files/lib/Message_rb.html +116 -0
- data/doc/files/lib/ProjectFileParser_rb.html +122 -0
- data/doc/files/lib/Project_rb.html +148 -0
- data/doc/files/lib/PropertyList_rb.html +106 -0
- data/doc/files/lib/PropertySet_rb.html +118 -0
- data/doc/files/lib/PropertyTreeNode_rb.html +106 -0
- data/doc/files/lib/Query_rb.html +116 -0
- data/doc/files/lib/RealFormat_rb.html +106 -0
- data/doc/files/lib/ResourceScenario_rb.html +116 -0
- data/doc/files/lib/Resource_rb.html +118 -0
- data/doc/files/lib/RichTextDocument_rb.html +120 -0
- data/doc/files/lib/RichTextElement_rb.html +120 -0
- data/doc/files/lib/RichTextParser_rb.html +120 -0
- data/doc/files/lib/RichTextProtocolExample_rb.html +120 -0
- data/doc/files/lib/RichTextProtocolHandler_rb.html +106 -0
- data/doc/files/lib/RichTextScanner_rb.html +116 -0
- data/doc/files/lib/RichTextSnip_rb.html +118 -0
- data/doc/files/lib/RichTextSyntaxRules_rb.html +106 -0
- data/doc/files/lib/RichText_rb.html +118 -0
- data/doc/files/lib/ScenarioData_rb.html +118 -0
- data/doc/files/lib/Scenario_rb.html +116 -0
- data/doc/files/lib/Scoreboard_rb.html +106 -0
- data/doc/files/lib/ShiftAssignments_rb.html +116 -0
- data/doc/files/lib/ShiftScenario_rb.html +116 -0
- data/doc/files/lib/Shift_rb.html +118 -0
- data/doc/files/lib/SourceFileInfo_rb.html +106 -0
- data/doc/files/lib/SyntaxReference_rb.html +122 -0
- data/doc/files/lib/TOCEntry_rb.html +118 -0
- data/doc/files/lib/TableColumnDefinition_rb.html +106 -0
- data/doc/files/lib/TableOfContents_rb.html +118 -0
- data/doc/files/lib/TaskDependency_rb.html +106 -0
- data/doc/files/lib/TaskJuggler_rb.html +120 -0
- data/doc/files/lib/TaskScenario_rb.html +116 -0
- data/doc/files/lib/Task_rb.html +118 -0
- data/doc/files/lib/TextParser/Pattern_rb.html +116 -0
- data/doc/files/lib/TextParser/Rule_rb.html +106 -0
- data/doc/files/lib/TextParser/StackElement_rb.html +106 -0
- data/doc/files/lib/TextParser/TokenDoc_rb.html +106 -0
- data/doc/files/lib/TextParser_rb.html +124 -0
- data/doc/files/lib/TextScanner_rb.html +128 -0
- data/doc/files/lib/Tj3Config_rb.html +118 -0
- data/doc/files/lib/TjException_rb.html +106 -0
- data/doc/files/lib/TjTime_rb.html +118 -0
- data/doc/files/lib/TjpExample_rb.html +116 -0
- data/doc/files/lib/TjpSyntaxRules_rb.html +106 -0
- data/doc/files/lib/UTF8String_rb.html +132 -0
- data/doc/files/lib/UserManual_rb.html +124 -0
- data/doc/files/lib/WorkingHours_rb.html +116 -0
- data/doc/files/lib/XMLDocument_rb.html +116 -0
- data/doc/files/lib/XMLElement_rb.html +116 -0
- data/doc/files/lib/reports/CSVFile_rb.html +116 -0
- data/doc/files/lib/reports/ColumnTable_rb.html +116 -0
- data/doc/files/lib/reports/GanttChart_rb.html +122 -0
- data/doc/files/lib/reports/GanttContainer_rb.html +116 -0
- data/doc/files/lib/reports/GanttHeaderScaleItem_rb.html +106 -0
- data/doc/files/lib/reports/GanttHeader_rb.html +116 -0
- data/doc/files/lib/reports/GanttLine_rb.html +126 -0
- data/doc/files/lib/reports/GanttLoadStack_rb.html +116 -0
- data/doc/files/lib/reports/GanttMilestone_rb.html +116 -0
- data/doc/files/lib/reports/GanttRouter_rb.html +106 -0
- data/doc/files/lib/reports/GanttTaskBar_rb.html +116 -0
- data/doc/files/lib/reports/HTMLGraphics_rb.html +106 -0
- data/doc/files/lib/reports/ReportElement_rb.html +118 -0
- data/doc/files/lib/reports/ReportTableCell_rb.html +106 -0
- data/doc/files/lib/reports/ReportTableColumn_rb.html +106 -0
- data/doc/files/lib/reports/ReportTableElement_rb.html +122 -0
- data/doc/files/lib/reports/ReportTableLegend_rb.html +106 -0
- data/doc/files/lib/reports/ReportTableLine_rb.html +116 -0
- data/doc/files/lib/reports/ReportTable_rb.html +118 -0
- data/doc/files/lib/reports/Report_rb.html +126 -0
- data/doc/files/lib/reports/ResourceListRE_rb.html +122 -0
- data/doc/files/lib/reports/TaskListRE_rb.html +122 -0
- data/doc/files/lib/reports/TjpExportRE_rb.html +116 -0
- data/doc/files/lib/taskjuggler3_rb.html +276 -0
- data/doc/files/lib/tj3man_rb.html +189 -0
- data/doc/fr_class_index.html +285 -0
- data/doc/fr_file_index.html +223 -0
- data/doc/fr_method_index.html +1953 -0
- data/doc/index.html +21 -0
- data/doc/rdoc-style.css +299 -0
- data/examples/tutorial.tjp +361 -0
- data/gem_spec.rb +30 -0
- data/lib/Account.rb +50 -0
- data/lib/AccountScenario.rb +39 -0
- data/lib/Allocation.rb +102 -0
- data/lib/AppConfig.rb +134 -0
- data/lib/AttributeBase.rb +131 -0
- data/lib/AttributeDefinition.rb +47 -0
- data/lib/Attributes.rb +478 -0
- data/lib/BatchProcessor.rb +209 -0
- data/lib/Booking.rb +59 -0
- data/lib/Charge.rb +71 -0
- data/lib/ChargeSet.rb +126 -0
- data/lib/HTMLDocument.rb +59 -0
- data/lib/Interval.rb +127 -0
- data/lib/KeywordDocumentation.rb +560 -0
- data/lib/Limits.rb +219 -0
- data/lib/Log.rb +160 -0
- data/lib/LogicalExpression.rb +71 -0
- data/lib/LogicalFlag.rb +34 -0
- data/lib/LogicalFunction.rb +102 -0
- data/lib/LogicalOperation.rb +118 -0
- data/lib/MacroParser.rb +77 -0
- data/lib/MacroTable.rb +84 -0
- data/lib/Message.rb +56 -0
- data/lib/MessageHandler.rb +34 -0
- data/lib/Project.rb +662 -0
- data/lib/ProjectFileParser.rb +333 -0
- data/lib/PropertyList.rb +181 -0
- data/lib/PropertySet.rb +304 -0
- data/lib/PropertyTreeNode.rb +461 -0
- data/lib/Query.rb +227 -0
- data/lib/RealFormat.rb +73 -0
- data/lib/Resource.rb +42 -0
- data/lib/ResourceScenario.rb +511 -0
- data/lib/RichText.rb +147 -0
- data/lib/RichTextDocument.rb +139 -0
- data/lib/RichTextElement.rb +391 -0
- data/lib/RichTextParser.rb +66 -0
- data/lib/RichTextProtocolExample.rb +65 -0
- data/lib/RichTextProtocolHandler.rb +35 -0
- data/lib/RichTextScanner.rb +390 -0
- data/lib/RichTextSnip.rb +104 -0
- data/lib/RichTextSyntaxRules.rb +265 -0
- data/lib/Scenario.rb +27 -0
- data/lib/ScenarioData.rb +65 -0
- data/lib/Scoreboard.rb +141 -0
- data/lib/Shift.rb +48 -0
- data/lib/ShiftAssignments.rb +291 -0
- data/lib/ShiftScenario.rb +46 -0
- data/lib/SourceFileInfo.rb +37 -0
- data/lib/SyntaxReference.rb +284 -0
- data/lib/TOCEntry.rb +76 -0
- data/lib/TableColumnDefinition.rb +54 -0
- data/lib/TableOfContents.rb +46 -0
- data/lib/Task.rb +37 -0
- data/lib/TaskDependency.rb +39 -0
- data/lib/TaskJuggler.rb +84 -0
- data/lib/TaskScenario.rb +1622 -0
- data/lib/TextParser.rb +416 -0
- data/lib/TextParser/Pattern.rb +263 -0
- data/lib/TextParser/Rule.rb +171 -0
- data/lib/TextParser/StackElement.rb +45 -0
- data/lib/TextParser/TokenDoc.rb +38 -0
- data/lib/TextScanner.rb +682 -0
- data/lib/Tj3Config.rb +27 -0
- data/lib/TjException.rb +27 -0
- data/lib/TjTime.rb +395 -0
- data/lib/TjpExample.rb +119 -0
- data/lib/TjpSyntaxRules.rb +4022 -0
- data/lib/UTF8String.rb +100 -0
- data/lib/UserManual.rb +282 -0
- data/lib/WorkingHours.rb +323 -0
- data/lib/XMLDocument.rb +54 -0
- data/lib/XMLElement.rb +175 -0
- data/lib/reports/CSVFile.rb +146 -0
- data/lib/reports/ColumnTable.rb +66 -0
- data/lib/reports/GanttChart.rb +308 -0
- data/lib/reports/GanttContainer.rb +107 -0
- data/lib/reports/GanttHeader.rb +141 -0
- data/lib/reports/GanttHeaderScaleItem.rb +42 -0
- data/lib/reports/GanttLine.rb +329 -0
- data/lib/reports/GanttLoadStack.rb +113 -0
- data/lib/reports/GanttMilestone.rb +80 -0
- data/lib/reports/GanttRouter.rb +375 -0
- data/lib/reports/GanttTaskBar.rb +95 -0
- data/lib/reports/HTMLGraphics.rb +65 -0
- data/lib/reports/Report.rb +344 -0
- data/lib/reports/ReportElement.rb +427 -0
- data/lib/reports/ReportTable.rb +144 -0
- data/lib/reports/ReportTableCell.rb +142 -0
- data/lib/reports/ReportTableColumn.rb +82 -0
- data/lib/reports/ReportTableElement.rb +852 -0
- data/lib/reports/ReportTableLegend.rb +167 -0
- data/lib/reports/ReportTableLine.rb +87 -0
- data/lib/reports/ResourceListRE.rb +72 -0
- data/lib/reports/TaskListRE.rb +73 -0
- data/lib/reports/TjpExportRE.rb +394 -0
- data/lib/taskjuggler3.rb +106 -0
- data/lib/tj3man.rb +88 -0
- data/manual/Day_To_Day_Juggling +168 -0
- data/manual/Getting_Started +61 -0
- data/manual/How_To_Contribute +185 -0
- data/manual/Installation +68 -0
- data/manual/Intro +102 -0
- data/manual/Reporting_Bugs +26 -0
- data/manual/Rich_Text_Attributes +90 -0
- data/manual/TaskJuggler_2x_Migration +40 -0
- data/manual/Tutorial +579 -0
- data/manual/fdl +450 -0
- data/prj_cfg.rb +43 -0
- data/setup.rb +1585 -0
- data/tasks/csts.rake +72 -0
- data/tasks/gem.rake +14 -0
- data/tasks/manual.rake +10 -0
- data/tasks/missing.rake +21 -0
- data/tasks/rcov.rake +14 -0
- data/tasks/rdoc.rake +17 -0
- data/tasks/rexml_fix.rb +16 -0
- data/tasks/rexml_fix_19.rb +49 -0
- data/tasks/show.rake +21 -0
- data/tasks/stats.rake +25 -0
- data/tasks/test.rake +11 -0
- data/test/MessageChecker.rb +53 -0
- data/test/TestSuite/CSV-Reports/celltext-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/celltext.tjp +7 -0
- data/test/TestSuite/CSV-Reports/genrefs +6 -0
- data/test/TestSuite/CSV-Reports/project-1.tji +57 -0
- data/test/TestSuite/CSV-Reports/resourcereport-Reference.csv +4 -0
- data/test/TestSuite/CSV-Reports/resourcereport.tjp +10 -0
- data/test/TestSuite/CSV-Reports/resourcereport_with_tasks-Reference.csv +22 -0
- data/test/TestSuite/CSV-Reports/resourcereport_with_tasks.tjp +11 -0
- data/test/TestSuite/CSV-Reports/sortByTree-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/sortByTree.tjp +8 -0
- data/test/TestSuite/CSV-Reports/sortBy_duration.down-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/sortBy_duration.down.tjp +10 -0
- data/test/TestSuite/CSV-Reports/sortBy_effort.up-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/sortBy_effort.up.tjp +10 -0
- data/test/TestSuite/CSV-Reports/sortBy_plan.start.down-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/sortBy_plan.start.down.tjp +10 -0
- data/test/TestSuite/CSV-Reports/taskreport-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/taskreport.tjp +10 -0
- data/test/TestSuite/CSV-Reports/taskreport_with_resources-Reference.csv +24 -0
- data/test/TestSuite/CSV-Reports/taskreport_with_resources.tjp +11 -0
- data/test/TestSuite/Scheduler/Correct/Allocate.tjp +86 -0
- data/test/TestSuite/Scheduler/Correct/AutomaticMilestones.tjp +63 -0
- data/test/TestSuite/Scheduler/Correct/Booking.tjp +161 -0
- data/test/TestSuite/Scheduler/Correct/Depends.tjp +50 -0
- data/test/TestSuite/Scheduler/Correct/Duration.tjp +34 -0
- data/test/TestSuite/Scheduler/Correct/InheritStartEnd.tjp +129 -0
- data/test/TestSuite/Scheduler/Correct/Limits.tjp +81 -0
- data/test/TestSuite/Scheduler/Correct/MultipleMandatories.tjp +43 -0
- data/test/TestSuite/Scheduler/Correct/Optimize-1.tjp +28 -0
- data/test/TestSuite/Scheduler/Correct/Optimize-2.tjp +33 -0
- data/test/TestSuite/Scheduler/Correct/Optimize-3.tjp +33 -0
- data/test/TestSuite/Scheduler/Correct/Optimize-4.tjp +34 -0
- data/test/TestSuite/Scheduler/Correct/Optimize-5.tjp +62 -0
- data/test/TestSuite/Scheduler/Correct/Precedes.tjp +50 -0
- data/test/TestSuite/Scheduler/Correct/Shift.tjp +102 -0
- data/test/TestSuite/Scheduler/Errors/account_no_leaf.tjp +13 -0
- data/test/TestSuite/Scheduler/Errors/booking_conflict.tjp +10 -0
- data/test/TestSuite/Scheduler/Errors/booking_no_duty.tjp +9 -0
- data/test/TestSuite/Scheduler/Errors/booking_on_vacation.tjp +9 -0
- data/test/TestSuite/Scheduler/Errors/container_booking.tjp +14 -0
- data/test/TestSuite/Scheduler/Errors/container_duration.tjp +11 -0
- data/test/TestSuite/Scheduler/Errors/effort_no_allocations.tjp +7 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_1.tjp +19 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_10.tjp +36 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_11.tjp +27 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_12.tjp +20 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_13.tjp +27 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_14.tjp +26 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_2.tjp +24 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_3.tjp +18 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_4.tjp +36 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_5.tjp +37 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_6.tjp +35 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_7.tjp +46 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_8.tjp +51 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_9.tjp +20 -0
- data/test/TestSuite/Scheduler/Errors/maxend.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/maxstart.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/milestone_booking.tjp +10 -0
- data/test/TestSuite/Scheduler/Errors/milestone_duration.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/milestone_start_end.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/minend.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/minstart.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/multiple_durations.tjp +11 -0
- data/test/TestSuite/Scheduler/Errors/no_tasks.tjp +6 -0
- data/test/TestSuite/Scheduler/Errors/not_scheduled.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/task_depend_child.tjp +10 -0
- data/test/TestSuite/Scheduler/Errors/task_depend_multi.tjp +13 -0
- data/test/TestSuite/Scheduler/Errors/task_depend_parent.tjp +11 -0
- data/test/TestSuite/Scheduler/Errors/task_depend_self.tjp +10 -0
- data/test/TestSuite/Scheduler/Errors/task_depend_unknown.tjp +10 -0
- data/test/TestSuite/Scheduler/Errors/task_overspecified_1.tjp +9 -0
- data/test/TestSuite/Scheduler/Errors/task_overspecified_2.tjp +14 -0
- data/test/TestSuite/Scheduler/Errors/task_overspecified_3.tjp +14 -0
- data/test/TestSuite/Scheduler/Errors/task_underspecified_1.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/task_underspecified_2.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/task_underspecified_3.tjp +9 -0
- data/test/TestSuite/Syntax/Correct/Account.tjp +53 -0
- data/test/TestSuite/Syntax/Correct/Allocate-1.tjp +24 -0
- data/test/TestSuite/Syntax/Correct/Alternative.tjp +13 -0
- data/test/TestSuite/Syntax/Correct/AutoMacros.tjp +14 -0
- data/test/TestSuite/Syntax/Correct/Booking.tjp +26 -0
- data/test/TestSuite/Syntax/Correct/Caption.tjp +33 -0
- data/test/TestSuite/Syntax/Correct/Celltext.tjp +28 -0
- data/test/TestSuite/Syntax/Correct/Comments.tjp +29 -0
- data/test/TestSuite/Syntax/Correct/Complete.tjp +16 -0
- data/test/TestSuite/Syntax/Correct/CompletedWork.tji +20 -0
- data/test/TestSuite/Syntax/Correct/CriticalPath.tjp +31 -0
- data/test/TestSuite/Syntax/Correct/Currencyformat.tjp +12 -0
- data/test/TestSuite/Syntax/Correct/CustomAttributes.tjp +14 -0
- data/test/TestSuite/Syntax/Correct/Depends1.tjp +22 -0
- data/test/TestSuite/Syntax/Correct/Durations.tjp +29 -0
- data/test/TestSuite/Syntax/Correct/Efficiency.tjp +19 -0
- data/test/TestSuite/Syntax/Correct/Export.tjp +40 -0
- data/test/TestSuite/Syntax/Correct/Flags.tjp +32 -0
- data/test/TestSuite/Syntax/Correct/Freeze.tjp +28 -0
- data/test/TestSuite/Syntax/Correct/Gap.tjp +15 -0
- data/test/TestSuite/Syntax/Correct/HtmlTaskReport.tjp +33 -0
- data/test/TestSuite/Syntax/Correct/Limits-1.tjp +71 -0
- data/test/TestSuite/Syntax/Correct/LoadUnits.tjp +31 -0
- data/test/TestSuite/Syntax/Correct/Macro-1.tjp +19 -0
- data/test/TestSuite/Syntax/Correct/Mandatory.tjp +17 -0
- data/test/TestSuite/Syntax/Correct/Milestone.tjp +12 -0
- data/test/TestSuite/Syntax/Correct/MinMax.tjp +17 -0
- data/test/TestSuite/Syntax/Correct/Numberformat.tjp +12 -0
- data/test/TestSuite/Syntax/Correct/Period.tjp +16 -0
- data/test/TestSuite/Syntax/Correct/Persistent.tjp +11 -0
- data/test/TestSuite/Syntax/Correct/Precedes1.tjp +17 -0
- data/test/TestSuite/Syntax/Correct/Priority.tjp +30 -0
- data/test/TestSuite/Syntax/Correct/Project.tjp +21 -0
- data/test/TestSuite/Syntax/Correct/ProjectIDs.tjp +23 -0
- data/test/TestSuite/Syntax/Correct/RawHTML.tjp +29 -0
- data/test/TestSuite/Syntax/Correct/Reports.tjp +54 -0
- data/test/TestSuite/Syntax/Correct/Resource.tjp +20 -0
- data/test/TestSuite/Syntax/Correct/Responsible.tjp +16 -0
- data/test/TestSuite/Syntax/Correct/Scenario.tjp +15 -0
- data/test/TestSuite/Syntax/Correct/Scheduling.tjp +26 -0
- data/test/TestSuite/Syntax/Correct/Select.tjp +27 -0
- data/test/TestSuite/Syntax/Correct/Shift.tjp +55 -0
- data/test/TestSuite/Syntax/Correct/Simple.tjp +25 -0
- data/test/TestSuite/Syntax/Correct/String.tjp +12 -0
- data/test/TestSuite/Syntax/Correct/Supplement.tjp +24 -0
- data/test/TestSuite/Syntax/Correct/TaskRoot.tjp +34 -0
- data/test/TestSuite/Syntax/Correct/TimeFrame.tjp +19 -0
- data/test/TestSuite/Syntax/Correct/Timezone.tjp +8 -0
- data/test/TestSuite/Syntax/Correct/Vacation.tjp +33 -0
- data/test/TestSuite/Syntax/Correct/csvtest +16 -0
- data/test/TestSuite/Syntax/Correct/manual2example.rb +24 -0
- data/test/TestSuite/Syntax/Correct/tutorial.tjp +485 -0
- data/test/TestSuite/Syntax/Errors/bad_include.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/booking_group.tjp +14 -0
- data/test/TestSuite/Syntax/Errors/booking_milestone.tjp +13 -0
- data/test/TestSuite/Syntax/Errors/booking_no_leaf.tjp +13 -0
- data/test/TestSuite/Syntax/Errors/chargeset.tjp +14 -0
- data/test/TestSuite/Syntax/Errors/chargeset_master.tjp +15 -0
- data/test/TestSuite/Syntax/Errors/container_attribute.tjp +13 -0
- data/test/TestSuite/Syntax/Errors/cost_acct_no_top.tjp +24 -0
- data/test/TestSuite/Syntax/Errors/cost_rev_same.tjp +24 -0
- data/test/TestSuite/Syntax/Errors/date_in_range.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/effort_zero.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/empty.tjp +1 -0
- data/test/TestSuite/Syntax/Errors/export_bad_extn.tjp +9 -0
- data/test/TestSuite/Syntax/Errors/extend_id_cap.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/interval_end_in_range.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/interval_start_in_range.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/leaf_resource_id_expected.tjp +12 -0
- data/test/TestSuite/Syntax/Errors/misaligned_date.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/no_csv_suffix.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/no_html_suffix.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/operand_attribute.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/operand_unkn_flag.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/operand_unkn_scen.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/overtime_range.tjp +13 -0
- data/test/TestSuite/Syntax/Errors/purge_no_list.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/purge_unknown_id.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/report_end.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/report_redifinition.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/report_start.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/resource_exists.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/resource_id_expected.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/rev_acct_no_top.tjp +24 -0
- data/test/TestSuite/Syntax/Errors/scenario_exists.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/shift_assignment_overlap.tjp +15 -0
- data/test/TestSuite/Syntax/Errors/shift_exists.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/shift_id_expected.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/sloppy_range.tjp +13 -0
- data/test/TestSuite/Syntax/Errors/sort_direction.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/sort_unknown_scen.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/sorting_crit_exptd1.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/sorting_crit_exptd2.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/sorting_wbs.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/start_before_end1.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/start_before_end2.tjp +6 -0
- data/test/TestSuite/Syntax/Errors/task_complete.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/task_exists.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/task_priority.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/task_without_chargeset.tjp +9 -0
- data/test/TestSuite/Syntax/Errors/time_interval.tjp +12 -0
- data/test/TestSuite/Syntax/Errors/too_many_bangs.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/undecl_flag.tjp +6 -0
- data/test/TestSuite/Syntax/Errors/unknown_projectid.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/unknown_scenario_id.tjp +6 -0
- data/test/TestSuite/Syntax/Errors/unknown_scenario_idx.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/unknown_task.tjp +10 -0
- data/test/all.rb +31 -0
- data/test/test_BatchProcessor.rb +54 -0
- data/test/test_CSV-Reports.rb +101 -0
- data/test/test_Limits.rb +104 -0
- data/test/test_LogicalExpression.rb +110 -0
- data/test/test_MacroTable.rb +51 -0
- data/test/test_Project.rb +57 -0
- data/test/test_PropertySet.rb +71 -0
- data/test/test_Query.rb +83 -0
- data/test/test_RealFormat.rb +83 -0
- data/test/test_RichText.rb +869 -0
- data/test/test_Scheduler.rb +42 -0
- data/test/test_ShiftAssignments.rb +77 -0
- data/test/test_Syntax.rb +41 -0
- data/test/test_TextScanner.rb +95 -0
- data/test/test_TjTime.rb +114 -0
- data/test/test_TjpExample.rb +169 -0
- data/test/test_UTF8String.rb +84 -0
- data/test/test_WorkingHours.rb +56 -0
- metadata +649 -0
data/lib/Query.rb
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
#
|
|
4
|
+
# = Query.rb -- The TaskJuggler III Project Management Software
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2006, 2007, 2008, 2009 by Chris Schlaeger <cs@kde.org>
|
|
7
|
+
#
|
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
|
10
|
+
# published by the Free Software Foundation.
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
require 'TjException'
|
|
14
|
+
|
|
15
|
+
class TaskJuggler
|
|
16
|
+
|
|
17
|
+
# A query can be used to retrieve any property attribute after the scheduling
|
|
18
|
+
# run has been completed. It is possible to make a Query before the scheduling
|
|
19
|
+
# run has been completed, but it only produces good results for static
|
|
20
|
+
# attributes. And for such queries, the PropertyTreeNode.get and [] functions
|
|
21
|
+
# are a lot more efficient.
|
|
22
|
+
# # When constructing a Query, a set of variables need to be set that is
|
|
23
|
+
# sufficient enough to identify a unique attribute. Some attribute are
|
|
24
|
+
# computed dynamically and further variables such as a start and end time will
|
|
25
|
+
# be incorporated into the result computation.
|
|
26
|
+
#
|
|
27
|
+
# The result is returned as String (Query#result), in numerical form
|
|
28
|
+
# (Query#numericalResult) if available as number, and as an entity that can be
|
|
29
|
+
# used for sorting (Query#sortableResult). To get the result, Query#process
|
|
30
|
+
# needs to be called. In case an error occured, Query#ok is set to false and
|
|
31
|
+
# Query#errorMessage contains an error message.
|
|
32
|
+
class Query
|
|
33
|
+
|
|
34
|
+
attr_accessor :project, :propertyType, :propertyId, :property,
|
|
35
|
+
:scopePropertyType, :scopePropertyId, :scopeProperty,
|
|
36
|
+
:attributeId, :scenarioIdx, :start, :end, :startIdx, :endIdx,
|
|
37
|
+
:numberFormat, :currencyFormat, :costAccount, :revenueAccount,
|
|
38
|
+
:loadUnit,
|
|
39
|
+
:result, :numericalResult, :sortableResult, :ok, :errorMessage
|
|
40
|
+
|
|
41
|
+
# Create a new Query object. The _parameters_ need to be sufficent to
|
|
42
|
+
# uniquely identify an attribute.
|
|
43
|
+
def initialize(parameters = { })
|
|
44
|
+
ps = %w( project propertyType propertyId property scopePropertyId
|
|
45
|
+
scopeProperty attributeId scenarioIdx start end startIdx endIdx
|
|
46
|
+
loadUnit numberFormat currencyFormat costAccount revenueAccount)
|
|
47
|
+
ps.each do |p|
|
|
48
|
+
instance_variable_set('@' + p, parameters[p] ? parameters[p] : nil)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
reset
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Queries object can be reused. Calling this function will clear the query
|
|
55
|
+
# result data.
|
|
56
|
+
def reset
|
|
57
|
+
@result = nil
|
|
58
|
+
@numericalResult = nil
|
|
59
|
+
@sortableResult = nil
|
|
60
|
+
@loadUnit = :days
|
|
61
|
+
@ok = nil
|
|
62
|
+
@errorMessage = nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# This method tries to resolve the query and return a result. In case it
|
|
66
|
+
# finds an attribute that matches the query, it returns true; false
|
|
67
|
+
# otherwise. The actual result data is put into the Query object.
|
|
68
|
+
def process
|
|
69
|
+
begin
|
|
70
|
+
# Resolve property reference from property ID.
|
|
71
|
+
if !@property.nil? && !@propertyId.nil?
|
|
72
|
+
@property = resolvePropertyId(@propertyType, @propertyId)
|
|
73
|
+
end
|
|
74
|
+
# Same for the scope property.
|
|
75
|
+
if !@scopeProperty.nil? && !@scopePropertyId.nil?
|
|
76
|
+
@scopeProperty = resolvePropertyId(@scopePropertyType, @scopePropertyId)
|
|
77
|
+
end
|
|
78
|
+
# Make sure the have a reference to the project.
|
|
79
|
+
@project = @property.project unless @project
|
|
80
|
+
|
|
81
|
+
@startIdx = @project.dateToIdx(@start, true) if @startIdx.nil? && @start
|
|
82
|
+
@endIdx = @project.dateToIdx(@end, true) - 1 if @endIdx.nil? && @end
|
|
83
|
+
|
|
84
|
+
if @property.hasQuery?(@attributeId, @scenarioIdx)
|
|
85
|
+
# Call the property query function to get the result.
|
|
86
|
+
if @scenarioIdx
|
|
87
|
+
@property.send('query_' + @attributeId, scenarioIdx, self)
|
|
88
|
+
else
|
|
89
|
+
@property.send('query_' + @attributeId, self)
|
|
90
|
+
end
|
|
91
|
+
else
|
|
92
|
+
# There is no query function. We simply use the property attribute
|
|
93
|
+
# value.
|
|
94
|
+
@sortableResult =
|
|
95
|
+
if @scenarioIdx
|
|
96
|
+
@property[@attributeId, @scenarioIdx]
|
|
97
|
+
else
|
|
98
|
+
@property.get(@attributeId)
|
|
99
|
+
end
|
|
100
|
+
if @sortableResult.is_a?(Array)
|
|
101
|
+
# This ugly special case is needed for custom attributes of type
|
|
102
|
+
# reference.
|
|
103
|
+
@sortableResult = @sortableResult[0]
|
|
104
|
+
end
|
|
105
|
+
@result = @sortableResult.to_s
|
|
106
|
+
@numericalResult = @result if @result.is_a?(Fixnum) or
|
|
107
|
+
@result.is_a?(Float)
|
|
108
|
+
end
|
|
109
|
+
rescue TjException
|
|
110
|
+
@errorMessage = $!
|
|
111
|
+
@result = ''
|
|
112
|
+
return @ok = false
|
|
113
|
+
end
|
|
114
|
+
@ok = true
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Convert a duration to the format specified by @loadUnit. _work_ is the
|
|
118
|
+
# effort in man days. The return value is the converted value with optional
|
|
119
|
+
# unit as a String.
|
|
120
|
+
def scaleDuration(value)
|
|
121
|
+
scaleValue(value, [ 24 * 60, 24, 1, 1.0 / 7, 1.0 / 30.42, 1.0 / 365 ])
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Convert a load or effort value to the format specified by @loadUnit.
|
|
125
|
+
# _work_ is the effort in man days. The return value is the converted value
|
|
126
|
+
# with optional unit as a String.
|
|
127
|
+
def scaleLoad(value)
|
|
128
|
+
scaleValue(value, [ @project.dailyWorkingHours * 60,
|
|
129
|
+
@project.dailyWorkingHours,
|
|
130
|
+
1.0,
|
|
131
|
+
1.0 / @project.weeklyWorkingDays,
|
|
132
|
+
1.0 / @project.monthlyWorkingDays,
|
|
133
|
+
1.0 / @project.yearlyWorkingDays ])
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
def resolvePropertyId(pType, pId)
|
|
139
|
+
unless @project
|
|
140
|
+
raise TjException.new, "Need Project reference to process the query"
|
|
141
|
+
end
|
|
142
|
+
case pType
|
|
143
|
+
when :Account
|
|
144
|
+
@project.account[pId]
|
|
145
|
+
when :Task
|
|
146
|
+
@project.task[pId]
|
|
147
|
+
when:Resource
|
|
148
|
+
@project.resource[pId]
|
|
149
|
+
else
|
|
150
|
+
raise TjException.new, "Unknown property type #{pType}"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# This function converts number to strings that may include a unit. The
|
|
155
|
+
# unit is determined by @loadUnit. In the automatic modes, the shortest
|
|
156
|
+
# possible result is shown and the unit is always appended. _value_ is the
|
|
157
|
+
# value to convert. _factors_ determines the conversion factors for the
|
|
158
|
+
# different units.
|
|
159
|
+
def scaleValue(value, factors)
|
|
160
|
+
if @loadUnit == :shortauto || @loadUnit == :longauto
|
|
161
|
+
# We try all possible units and store the resulting strings here.
|
|
162
|
+
options = []
|
|
163
|
+
# For each option we also save the delta between the String value and
|
|
164
|
+
# the original value.
|
|
165
|
+
delta = []
|
|
166
|
+
# For each of the units we can define a maximum value that the value
|
|
167
|
+
# should not exceed. nil means no limit.
|
|
168
|
+
max = [ 60, 48, nil, 8, 24, nil ]
|
|
169
|
+
|
|
170
|
+
i = 0
|
|
171
|
+
factors.each do |factor|
|
|
172
|
+
scaledValue = value * factor
|
|
173
|
+
str = @numberFormat.format(scaledValue)
|
|
174
|
+
delta[i] = ((scaledValue - str.to_f).abs * 1000).to_i
|
|
175
|
+
# We ignore results that are 0 or exceed the maximum. To ensure that
|
|
176
|
+
# we have at least one result the unscaled value is always taken.
|
|
177
|
+
if (factor != 1.0 && /^[0.]*$/ =~ str) ||
|
|
178
|
+
(max[i] && scaledValue > max[i])
|
|
179
|
+
options << nil
|
|
180
|
+
else
|
|
181
|
+
options << str
|
|
182
|
+
end
|
|
183
|
+
i += 1
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Find the value that is the closest to the original value. This will be
|
|
187
|
+
# the default if all values have the same length.
|
|
188
|
+
shortest = 2
|
|
189
|
+
delta.length.times do |j|
|
|
190
|
+
shortest = j if options[j] && options[j][0, 2] != '0.' &&
|
|
191
|
+
delta[j] <= delta[shortest]
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Find the shortest option.
|
|
195
|
+
6.times do |j|
|
|
196
|
+
shortest = j if options[j] && options[j][0, 2] != '0.' &&
|
|
197
|
+
options[j].length < options[shortest].length
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
str = options[shortest]
|
|
201
|
+
if @loadUnit == :longauto
|
|
202
|
+
# For the long units we handle singular and plural properly. For
|
|
203
|
+
# English we just need to append an 's', but this code will work for
|
|
204
|
+
# other languages as well.
|
|
205
|
+
units = []
|
|
206
|
+
if str == "1"
|
|
207
|
+
units = %w( minute hour day week month year )
|
|
208
|
+
else
|
|
209
|
+
units = %w( minutes hours days weeks months years )
|
|
210
|
+
end
|
|
211
|
+
str += ' ' + units[shortest]
|
|
212
|
+
else
|
|
213
|
+
str += %w( min h d w m y )[shortest]
|
|
214
|
+
end
|
|
215
|
+
else
|
|
216
|
+
# For fixed units we just need to do the conversion. No unit is
|
|
217
|
+
# included.
|
|
218
|
+
units = [ :minutes, :hours, :days, :weeks, :months, :years ]
|
|
219
|
+
str = @numberFormat.format(value * factors[units.index(@loadUnit)])
|
|
220
|
+
end
|
|
221
|
+
str
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
end
|
|
227
|
+
|
data/lib/RealFormat.rb
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
#
|
|
4
|
+
# = RealFormat.rb -- The TaskJuggler III Project Management Software
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2006, 2007, 2008, 2009 by Chris Schlaeger <cs@kde.org>
|
|
7
|
+
#
|
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
|
10
|
+
# published by the Free Software Foundation.
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
class TaskJuggler
|
|
14
|
+
|
|
15
|
+
# This class provides the functionality to format a Float according to certain
|
|
16
|
+
# rules. These rules determine how negative values are represented, how the
|
|
17
|
+
# fractional part is shown and how to structure the mantissa. The result is
|
|
18
|
+
# always a String.
|
|
19
|
+
class RealFormat
|
|
20
|
+
|
|
21
|
+
# Create a new RealFormat object and define the formating rules.
|
|
22
|
+
def initialize(args)
|
|
23
|
+
# Prefix used for negative numbers. (String)
|
|
24
|
+
@signPrefix = args[0]
|
|
25
|
+
# Suffix used for negative numbers. (String)
|
|
26
|
+
@signSuffix = args[1]
|
|
27
|
+
# Separator used after 3 integer digits. (String)
|
|
28
|
+
@thousandsSeparator = args[2]
|
|
29
|
+
# Separator used between the inter part and the fractional part. (String)
|
|
30
|
+
@fractionSeparator = args[3]
|
|
31
|
+
# Number of fractional digits to show. (Fixnum)
|
|
32
|
+
@fractionDigits = args[4]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Converts the Float _number_ into a String representation according to the
|
|
36
|
+
# formating rules.
|
|
37
|
+
def format(number)
|
|
38
|
+
# Check for negative number. Continue with the absolute part.
|
|
39
|
+
if number < 0
|
|
40
|
+
negate = true
|
|
41
|
+
number = -number
|
|
42
|
+
else
|
|
43
|
+
negate = false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Determine the integer part.
|
|
47
|
+
intNumber = (number * (10 ** @fractionDigits)).round.to_i.to_s
|
|
48
|
+
if intNumber.length <= @fractionDigits
|
|
49
|
+
intNumber = '0' * (@fractionDigits - intNumber.length + 1) + intNumber
|
|
50
|
+
end
|
|
51
|
+
intPart = intNumber[0..-(@fractionDigits + 1)]
|
|
52
|
+
fracPart =
|
|
53
|
+
@fractionDigits > 0 ? '.' + intNumber[-(@fractionDigits)..-1] : ''
|
|
54
|
+
|
|
55
|
+
if @thousandsSeparator.empty?
|
|
56
|
+
out = intPart
|
|
57
|
+
else
|
|
58
|
+
out = ''
|
|
59
|
+
1.upto(intPart.length) do |i|
|
|
60
|
+
out = intPart[-i, 1] + out
|
|
61
|
+
out = @thousandsSeparator + out if i % 3 == 0 && i < intPart.length
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
out += fracPart
|
|
65
|
+
# Now compose the result.
|
|
66
|
+
out = @signPrefix + out + @signSuffix if negate
|
|
67
|
+
out
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
|
data/lib/Resource.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
#
|
|
4
|
+
# = Resource.rb -- The TaskJuggler III Project Management Software
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2006, 2007, 2008, 2009 by Chris Schlaeger <cs@kde.org>
|
|
7
|
+
#
|
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
|
10
|
+
# published by the Free Software Foundation.
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
require 'PropertyTreeNode'
|
|
15
|
+
require 'ResourceScenario'
|
|
16
|
+
|
|
17
|
+
class TaskJuggler
|
|
18
|
+
|
|
19
|
+
class Resource < PropertyTreeNode
|
|
20
|
+
|
|
21
|
+
def initialize(project, id, name, parent)
|
|
22
|
+
super(project.resources, id, name, parent)
|
|
23
|
+
project.addResource(self)
|
|
24
|
+
|
|
25
|
+
@data = Array.new(@project.scenarioCount, nil)
|
|
26
|
+
@project.scenarioCount.times do |i|
|
|
27
|
+
@data[i] = ResourceScenario.new(self, i, @scenarioAttributes[i])
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Many Resource functions are scenario specific. These functions are
|
|
32
|
+
# provided by the class ResourceScenario. In case we can't find a
|
|
33
|
+
# function called for the Resource class we try to find it in
|
|
34
|
+
# ResourceScenario.
|
|
35
|
+
def method_missing(func, scenarioIdx, *args)
|
|
36
|
+
@data[scenarioIdx].method(func).call(*args)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
#
|
|
4
|
+
# = ResourceScenario.rb -- The TaskJuggler III Project Management Software
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2006, 2007, 2008, 2009 by Chris Schlaeger <cs@kde.org>
|
|
7
|
+
#
|
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
|
10
|
+
# published by the Free Software Foundation.
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
require 'ScenarioData'
|
|
15
|
+
|
|
16
|
+
class TaskJuggler
|
|
17
|
+
|
|
18
|
+
class ResourceScenario < ScenarioData
|
|
19
|
+
|
|
20
|
+
def initialize(resource, scenarioIdx, attributes)
|
|
21
|
+
super
|
|
22
|
+
|
|
23
|
+
# The scoreboard entries are either nil, a number or a task reference. nil
|
|
24
|
+
# means the slot is unassigned. The task reference means assigned to this
|
|
25
|
+
# task. The numbers have the following values:
|
|
26
|
+
# 1: Off hour
|
|
27
|
+
# 2: Vacation
|
|
28
|
+
# The scoreboard is only created when needed to save memory for projects
|
|
29
|
+
# which read-in the coporate employee database but only need a small
|
|
30
|
+
# subset.
|
|
31
|
+
@scoreboard = nil
|
|
32
|
+
|
|
33
|
+
@firstBookedSlot = nil
|
|
34
|
+
@lastBookedSlot = nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# This method must be called at the beginning of each scheduling run. It
|
|
38
|
+
# initializes variables used during the scheduling process.
|
|
39
|
+
def prepareScheduling
|
|
40
|
+
@property['effort', @scenarioIdx] = 0
|
|
41
|
+
initScoreboard
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The criticalness of a resource is a measure for the probabilty that all
|
|
45
|
+
# allocations can be fullfilled. The smaller the value, the more likely
|
|
46
|
+
# will the tasks get the resource. A value above 1.0 means that
|
|
47
|
+
# statistically some tasks will not get their resources. A value between
|
|
48
|
+
# 0 and 1 implies no guarantee, though.
|
|
49
|
+
def calcCriticalness
|
|
50
|
+
if @scoreboard.nil?
|
|
51
|
+
# Resources that are not allocated are not critical at all.
|
|
52
|
+
@property['criticalness', @scenarioIdx] = 0.0
|
|
53
|
+
else
|
|
54
|
+
freeSlots = 0
|
|
55
|
+
@scoreboard.each do |slot|
|
|
56
|
+
freeSlots += 1 if slot.nil?
|
|
57
|
+
end
|
|
58
|
+
@property['criticalness', @scenarioIdx] = freeSlots == 0 ? 1.0 :
|
|
59
|
+
a('alloctdeffort') / freeSlots
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns true if the resource is available at the time specified by
|
|
64
|
+
# _sbIdx_.
|
|
65
|
+
def available?(sbIdx)
|
|
66
|
+
return false unless @scoreboard[sbIdx].nil?
|
|
67
|
+
|
|
68
|
+
limits = a('limits')
|
|
69
|
+
return false if limits && !limits.ok?(@scoreboard.idxToDate(sbIdx))
|
|
70
|
+
|
|
71
|
+
true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Return true if the resource is booked for a tasks at the time specified by
|
|
75
|
+
# _sbIdx_.
|
|
76
|
+
def booked?(sbIdx)
|
|
77
|
+
@scoreboard[sbIdx].is_a?(Task)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Book the slot indicated by the scoreboard index +sbIdx+ for Task +task+.
|
|
81
|
+
# If +force+ is true, overwrite the existing booking for this slot. The
|
|
82
|
+
# method returns true if the slot was available.
|
|
83
|
+
def book(sbIdx, task, force = false)
|
|
84
|
+
return false if !force && !available?(sbIdx)
|
|
85
|
+
|
|
86
|
+
#puts "Booking resource #{@property.fullId} at " +
|
|
87
|
+
# "#{@scoreboard.idxToDate(sbIdx)}/#{sbIdx} for task #{task.fullId}\n"
|
|
88
|
+
@scoreboard[sbIdx] = task
|
|
89
|
+
# Track the total allocated slots for this resource and all parent
|
|
90
|
+
# resources.
|
|
91
|
+
t = @property
|
|
92
|
+
while t
|
|
93
|
+
t['effort', @scenarioIdx] += 1
|
|
94
|
+
t = t.parent
|
|
95
|
+
end
|
|
96
|
+
a('limits').inc(@scoreboard.idxToDate(sbIdx)) if a('limits')
|
|
97
|
+
|
|
98
|
+
# Make sure the task is in the list of duties.
|
|
99
|
+
@property['duties', @scenarioIdx] << task unless a('duties').include?(task)
|
|
100
|
+
|
|
101
|
+
if @firstBookedSlot.nil? || @firstBookedSlot > sbIdx
|
|
102
|
+
@firstBookedSlot = sbIdx
|
|
103
|
+
end
|
|
104
|
+
if @lastBookedSlot.nil? || @lastBookedSlot < sbIdx
|
|
105
|
+
@lastBookedSlot = sbIdx
|
|
106
|
+
end
|
|
107
|
+
true
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def bookBooking(sbIdx, booking)
|
|
111
|
+
initScoreboard if @scoreboard.nil?
|
|
112
|
+
|
|
113
|
+
unless @scoreboard[sbIdx].nil?
|
|
114
|
+
if booked?(sbIdx)
|
|
115
|
+
error('booking_conflict',
|
|
116
|
+
"Resource #{@property.fullId} has multiple conflicting " +
|
|
117
|
+
"bookings for #{@scoreboard.idxToDate(sbIdx)}. The " +
|
|
118
|
+
"conflicting tasks are #{@scoreboard[sbIdx].fullId} and " +
|
|
119
|
+
"#{booking.task.fullId}.", true, booking.sourceFileInfo)
|
|
120
|
+
end
|
|
121
|
+
if @scoreboard[sbIdx] > booking.overtime
|
|
122
|
+
if @scoreboard[sbIdx] == 1 && booking.sloppy == 0
|
|
123
|
+
error('booking_no_duty',
|
|
124
|
+
"Resource #{@property.fullId} has no duty at " +
|
|
125
|
+
"#{@scoreboard.idxToDate(sbIdx)}.", true,
|
|
126
|
+
booking.sourceFileInfo)
|
|
127
|
+
end
|
|
128
|
+
if @scoreboard[sbIdx] == 2 && booking.sloppy <= 1
|
|
129
|
+
error('booking_on_vacation',
|
|
130
|
+
"Resource #{@property.fullId} is on vacation at " +
|
|
131
|
+
"#{@scoreboard.idxToDate(sbIdx)}.", true,
|
|
132
|
+
booking.sourceFileInfo)
|
|
133
|
+
end
|
|
134
|
+
return false
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
book(sbIdx, booking.task, true)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Compute the cost generated by this Resource for a given Account during a
|
|
142
|
+
# given interval. If a Task is provided as scopeProperty only the turnover
|
|
143
|
+
# directly assiciated with the Task is taken into account.
|
|
144
|
+
def query_cost(query)
|
|
145
|
+
if query.costAccount
|
|
146
|
+
query.sortableResult = query.numericalResult =
|
|
147
|
+
turnover(query.startIdx, query.endIdx, query.costAccount,
|
|
148
|
+
query.scopeProperty)
|
|
149
|
+
query.result = query.currencyFormat.format(query.sortableResult)
|
|
150
|
+
else
|
|
151
|
+
query.result = 'No cost account'
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# The effort allocated to the Resource in the specified interval. In case a
|
|
156
|
+
# Task is given as scope property only the effort allocated to this Task is
|
|
157
|
+
# taken into account.
|
|
158
|
+
def query_effort(query)
|
|
159
|
+
query.sortableResult = query.numericalResult =
|
|
160
|
+
getEffectiveWork(query.startIdx, query.endIdx, query.scopeProperty)
|
|
161
|
+
query.result = query.scaleLoad(query.sortableResult)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# The unallocated effort of the Resource in the specified interval.
|
|
165
|
+
def query_freework(query)
|
|
166
|
+
query.sortableResult = query.numericalResult =
|
|
167
|
+
getEffectiveFreeWork(query.startIdx, query.endIdx)
|
|
168
|
+
query.result = query.scaleLoad(query.sortableResult)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Get the rate of the resource.
|
|
172
|
+
def query_rate(query)
|
|
173
|
+
query.sortableResult = query.numericalResult = rate
|
|
174
|
+
query.result = query.currencyFormat.format(query.sortableResult)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Compute the revenue generated by this Resource for a given Account during
|
|
178
|
+
# a given interval. If a Task is provided as scopeProperty only the
|
|
179
|
+
# revenue directly associated to this Task is taken into account.
|
|
180
|
+
def query_revenue(query)
|
|
181
|
+
if query.revenueAccount
|
|
182
|
+
query.sortableResult = query.numericalResult =
|
|
183
|
+
turnover(query.startIdx, query.endIdx, query.revenueAccount,
|
|
184
|
+
query.scopeProperty)
|
|
185
|
+
query.result = query.currencyFormat.format(query.sortableResult)
|
|
186
|
+
else
|
|
187
|
+
query.result = 'No revenue account'
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Returns the work of the resource (and its children) weighted by their
|
|
192
|
+
# efficiency.
|
|
193
|
+
def getEffectiveWork(startIdx, endIdx, task = nil)
|
|
194
|
+
# Convert the interval dates to indexes if needed.
|
|
195
|
+
startIdx = @project.dateToIdx(startIdx, true) if startIdx.is_a?(TjTime)
|
|
196
|
+
endIdx = @project.dateToIdx(endIdx, true) if endIdx.is_a?(TjTime)
|
|
197
|
+
|
|
198
|
+
work = 0.0
|
|
199
|
+
if @property.container?
|
|
200
|
+
@property.children.each do |resource|
|
|
201
|
+
work += resource.getEffectiveWork(@scenarioIdx, startIdx, endIdx, task)
|
|
202
|
+
end
|
|
203
|
+
else
|
|
204
|
+
return 0.0 if @scoreboard.nil?
|
|
205
|
+
|
|
206
|
+
work = @project.convertToDailyLoad(
|
|
207
|
+
getAllocatedSlots(startIdx, endIdx, task) *
|
|
208
|
+
@project['scheduleGranularity']) * a('efficiency')
|
|
209
|
+
end
|
|
210
|
+
work
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Returns the allocated work of the resource (and its children).
|
|
214
|
+
def getAllocatedWork(startIdx, endIdx, task = nil)
|
|
215
|
+
# Convert the interval dates to indexes if needed.
|
|
216
|
+
startIdx = @project.dateToIdx(startIdx, true) if startIdx.is_a?(TjTime)
|
|
217
|
+
endIdx = @project.dateToIdx(endIdx, true) if endIdx.is_a?(TjTime)
|
|
218
|
+
|
|
219
|
+
work = 0.0
|
|
220
|
+
if @property.container?
|
|
221
|
+
@property.children.each do |resource|
|
|
222
|
+
work += resource.getAllocatedWork(@scenarioIdx, startIdx, endIdx, task)
|
|
223
|
+
end
|
|
224
|
+
else
|
|
225
|
+
return 0.0 if @scoreboard.nil?
|
|
226
|
+
|
|
227
|
+
work = @project.convertToDailyLoad(
|
|
228
|
+
getAllocatedSlots(startIdx, endIdx, task) *
|
|
229
|
+
@project['scheduleGranularity'])
|
|
230
|
+
end
|
|
231
|
+
work
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Returns the allocated accumulated time of this resource and its children.
|
|
235
|
+
def getAllocatedTime(startIdx, endIdx, task = nil)
|
|
236
|
+
# Convert the interval dates to indexes if needed.
|
|
237
|
+
startIdx = @project.dateToIdx(startIdx, true) if startIdx.is_a?(TjTime)
|
|
238
|
+
endIdx = @project.dateToIdx(endIdx, true) if endIdx.is_a?(TjTime)
|
|
239
|
+
|
|
240
|
+
time = 0
|
|
241
|
+
if @property.container?
|
|
242
|
+
@property.children.each do |resource|
|
|
243
|
+
time += resource.getAllocatedWork(@scenarioIdx, startIdx, endIdx, task)
|
|
244
|
+
end
|
|
245
|
+
else
|
|
246
|
+
return 0 if @scoreboard.nil?
|
|
247
|
+
|
|
248
|
+
time = @project.convertToDailyLoad(@project['scheduleGranularity'] *
|
|
249
|
+
getAllocatedSlots(startIdx, endIdx, task))
|
|
250
|
+
end
|
|
251
|
+
time
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Return the unallocated work of the resource and its children wheighted by
|
|
255
|
+
# their efficiency.
|
|
256
|
+
def getEffectiveFreeWork(startIdx, endIdx)
|
|
257
|
+
# Convert the interval dates to indexes if needed.
|
|
258
|
+
startIdx = @project.dateToIdx(startIdx, true) if startIdx.is_a?(TjTime)
|
|
259
|
+
endIdx = @project.dateToIdx(endIdx, true) if endIdx.is_a?(TjTime)
|
|
260
|
+
|
|
261
|
+
work = 0.0
|
|
262
|
+
if @property.container?
|
|
263
|
+
@property.children.each do |resource|
|
|
264
|
+
work += resource.getEffectiveFreeWork(@scenarioIdx, startIdx, endIdx)
|
|
265
|
+
end
|
|
266
|
+
else
|
|
267
|
+
initScoreboard if @scoreboard.nil?
|
|
268
|
+
|
|
269
|
+
work = @project.convertToDailyLoad(
|
|
270
|
+
getFreeSlots(startIdx, endIdx) *
|
|
271
|
+
@project['scheduleGranularity']) * a('efficiency')
|
|
272
|
+
end
|
|
273
|
+
work
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def turnover(startIdx, endIdx, account, task = nil)
|
|
277
|
+
amount = 0.0
|
|
278
|
+
if @property.container?
|
|
279
|
+
@property.children.each do |child|
|
|
280
|
+
amount += child.turnover(@scenarioIdx, startIdx, endIdx, account, task)
|
|
281
|
+
end
|
|
282
|
+
else
|
|
283
|
+
a('duties').each do |duty|
|
|
284
|
+
amount += duty.turnover(@scenarioIdx, startIdx, endIdx, account,
|
|
285
|
+
@property)
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
amount
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Returns the cost for using this resource during the specified Interval
|
|
293
|
+
# _period_. If a Task _task_ is provided, only the work on this particular
|
|
294
|
+
# task is considered.
|
|
295
|
+
def cost(startIdx, endIdx, task = nil)
|
|
296
|
+
getAllocatedTime(startIdx, endIdx, task) * a('rate')
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Returns true if the resource or any of its children is allocated during
|
|
300
|
+
# the period specified with the Interval _iv_. If task is not nil
|
|
301
|
+
# only allocations to this tasks are respected.
|
|
302
|
+
def allocated?(iv, task = nil)
|
|
303
|
+
initScoreboard if @scoreboard.nil?
|
|
304
|
+
|
|
305
|
+
startIdx = @scoreboard.dateToIdx(iv.start, true)
|
|
306
|
+
endIdx = @scoreboard.dateToIdx(iv.end, true)
|
|
307
|
+
|
|
308
|
+
startIdx = @firstBookedSlot if @firstBookedSlot &&
|
|
309
|
+
startIdx < @firstBookedSlot
|
|
310
|
+
endIdx = @lastBookedSlot + 1 if @lastBookedSlot &&
|
|
311
|
+
endIdx < @lastBookedSlot + 1
|
|
312
|
+
return false if startIdx > endIdx
|
|
313
|
+
|
|
314
|
+
return allocatedSub(startIdx, endIdx, task)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Iterate over the scoreboard and turn its content into a set of Bookings.
|
|
318
|
+
def getBookings
|
|
319
|
+
return {} if @property.container? || @scoreboard.nil? ||
|
|
320
|
+
@firstBookedSlot.nil? || @lastBookedSlot.nil?
|
|
321
|
+
|
|
322
|
+
bookings = {}
|
|
323
|
+
lastTask = nil
|
|
324
|
+
bookingStart = nil
|
|
325
|
+
|
|
326
|
+
# To speedup the collection we start with the first booked slot and end
|
|
327
|
+
# with the last booked slot.
|
|
328
|
+
startIdx = @firstBookedSlot
|
|
329
|
+
endIdx = @lastBookedSlot + 1
|
|
330
|
+
|
|
331
|
+
# In case the index markers are still uninitialized, we have no bookings.
|
|
332
|
+
return [] if startIdx.nil? || endIdx.nil?
|
|
333
|
+
|
|
334
|
+
startIdx.upto(endIdx - 1) do |idx|
|
|
335
|
+
task = @scoreboard[idx]
|
|
336
|
+
# Now we watch for task changes.
|
|
337
|
+
if task != lastTask || (lastTask == nil && task.is_a?(Task)) ||
|
|
338
|
+
(task.is_a?(Task) && idx == endIdx - 1)
|
|
339
|
+
unless lastTask.nil?
|
|
340
|
+
# If we don't have a Booking for the task yet, we create one.
|
|
341
|
+
if bookings[lastTask].nil?
|
|
342
|
+
bookings[lastTask] = Booking.new(@property, lastTask, [])
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Make sure the index is correct even for the last task block.
|
|
346
|
+
idx += 1 if idx == endIdx - 1
|
|
347
|
+
# Append the new interval to the Booking.
|
|
348
|
+
bookings[lastTask].intervals <<
|
|
349
|
+
Interval.new(@scoreboard.idxToDate(bookingStart),
|
|
350
|
+
@scoreboard.idxToDate(idx))
|
|
351
|
+
end
|
|
352
|
+
# Get ready for the next task booking interval
|
|
353
|
+
if task.is_a?(Task)
|
|
354
|
+
lastTask = task
|
|
355
|
+
bookingStart = idx
|
|
356
|
+
else
|
|
357
|
+
lastTask = bookingStart = nil
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
bookings
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Return a list of scoreboard intervals that are at least _minDuration_ long
|
|
365
|
+
# and contain only 1 and 2. These values determine off-hours of the
|
|
366
|
+
# resource. The result is an Array of [ start, end ] TjTime values.
|
|
367
|
+
def collectTimeOffIntervals(iv, minDuration)
|
|
368
|
+
initScoreboard if @scoreboard.nil?
|
|
369
|
+
|
|
370
|
+
@scoreboard.collectTimeOffIntervals(iv, minDuration, [ 1, 2 ])
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
private
|
|
374
|
+
|
|
375
|
+
def initScoreboard
|
|
376
|
+
# Create scoreboard and mark all slots as unavailable
|
|
377
|
+
@scoreboard = Scoreboard.new(@project['start'], @project['end'],
|
|
378
|
+
@project['scheduleGranularity'], 1)
|
|
379
|
+
|
|
380
|
+
# We'll need this frequently and can savely cache it here.
|
|
381
|
+
@shifts = a('shifts')
|
|
382
|
+
@workinghours = a('workinghours')
|
|
383
|
+
|
|
384
|
+
# Change all work time slots to nil (available) again.
|
|
385
|
+
date = @scoreboard.idxToDate(0)
|
|
386
|
+
delta = @project['scheduleGranularity']
|
|
387
|
+
@project.scoreboardSize.times do |i|
|
|
388
|
+
@scoreboard[i] = nil if onShift?(date)
|
|
389
|
+
date += delta
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Mark all resource specific vacation slots as such (2)
|
|
393
|
+
a('vacations').each do |vacation|
|
|
394
|
+
startIdx = @scoreboard.dateToIdx(vacation.start, true)
|
|
395
|
+
endIdx = @scoreboard.dateToIdx(vacation.end, true)
|
|
396
|
+
startIdx.upto(endIdx - 1) do |i|
|
|
397
|
+
@scoreboard[i] = 2
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# Mark all global vacation slots as such (2)
|
|
402
|
+
@project['vacations'].each do |vacation|
|
|
403
|
+
startIdx = @scoreboard.dateToIdx(vacation.start, true)
|
|
404
|
+
endIdx = @scoreboard.dateToIdx(vacation.end, true)
|
|
405
|
+
startIdx.upto(endIdx - 1) do |i|
|
|
406
|
+
@scoreboard[i] = 2
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
unless @shifts.nil?
|
|
411
|
+
# Mark the vacations from all the shifts the resource is assigned to.
|
|
412
|
+
@project.scoreboardSize.times do |i|
|
|
413
|
+
v = @shifts.getSbSlot(@scoreboard.idxToDate(i))
|
|
414
|
+
# Check if the vacation replacement bit is set. In that case we copy
|
|
415
|
+
# the while interval over to the resource scoreboard overriding any
|
|
416
|
+
# global vacations.
|
|
417
|
+
if (v & (1 << 8)) > 0
|
|
418
|
+
# The ShiftAssignments scoreboard and the ResourceScenario scoreboard
|
|
419
|
+
# unfortunately can't use the same values for a certain meaning. So,
|
|
420
|
+
# we have to use a map to translate the values.
|
|
421
|
+
map = [ nil, nil, 1, 2 ]
|
|
422
|
+
@scoreboard[i] = map[v & 0xFF]
|
|
423
|
+
elsif (v & 0xFF) == 3
|
|
424
|
+
# 3 in ShiftAssignments means 2 in ResourceScenario (on vacation)
|
|
425
|
+
@scoreboard[i] = 2
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def onShift?(date)
|
|
432
|
+
# The more redable but slower form would be:
|
|
433
|
+
# if @shifts.assigned?(date)
|
|
434
|
+
# return @shifts.onShift?(date)
|
|
435
|
+
# else
|
|
436
|
+
# @workinghours.onShift?(date)
|
|
437
|
+
# end
|
|
438
|
+
if @shifts && (v = (@shifts.getSbSlot(date) & 0xFF)) > 0
|
|
439
|
+
v == 1
|
|
440
|
+
else
|
|
441
|
+
@workinghours.onShift?(date)
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Count the booked slots between the start and end index. If _task_ is not
|
|
446
|
+
# nil count only those slots that are assigned to this particular task.
|
|
447
|
+
def getAllocatedSlots(startIdx, endIdx, task)
|
|
448
|
+
# To speedup the counting we start with the first booked slot and end
|
|
449
|
+
# with the last booked slot.
|
|
450
|
+
startIdx = @firstBookedSlot if @firstBookedSlot &&
|
|
451
|
+
startIdx < @firstBookedSlot
|
|
452
|
+
endIdx = @lastBookedSlot + 1 if @lastBookedSlot &&
|
|
453
|
+
endIdx > @lastBookedSlot + 1
|
|
454
|
+
|
|
455
|
+
bookedSlots = 0
|
|
456
|
+
startIdx.upto(endIdx - 1) do |idx|
|
|
457
|
+
if (task.nil? && @scoreboard[idx].is_a?(Task)) ||
|
|
458
|
+
(task && @scoreboard[idx] == task)
|
|
459
|
+
bookedSlots += 1
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
bookedSlots
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# Count the free slots between the start and end index.
|
|
467
|
+
def getFreeSlots(startIdx, endIdx)
|
|
468
|
+
freeSlots = 0
|
|
469
|
+
startIdx.upto(endIdx - 1) do |idx|
|
|
470
|
+
freeSlots += 1 if @scoreboard[idx].nil?
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
freeSlots
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# Returns true if the resource or any of its children is allocated during
|
|
477
|
+
# the period specified with _startIdx_ and _endIdx_. If task is not nil
|
|
478
|
+
# only allocations to this tasks are respected.
|
|
479
|
+
def allocatedSub(startIdx, endIdx, task)
|
|
480
|
+
if @property.container?
|
|
481
|
+
@property.children.each do |resource|
|
|
482
|
+
return true if resource.allocatedSub(@scenarioIdx, startIdx, endIdx,
|
|
483
|
+
task)
|
|
484
|
+
end
|
|
485
|
+
else
|
|
486
|
+
return false unless a('duties').include?(task)
|
|
487
|
+
|
|
488
|
+
startIdx.upto(endIdx - 1) do |idx|
|
|
489
|
+
return true if @scoreboard[idx] == task
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
false
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# Return the daily cost of a resource or resource group.
|
|
496
|
+
def rate
|
|
497
|
+
if @property.container?
|
|
498
|
+
dailyRate = 0.0
|
|
499
|
+
@property.children.each do |resource|
|
|
500
|
+
dailyRate += resource.rate(@scenarioIdx)
|
|
501
|
+
end
|
|
502
|
+
dailyRate
|
|
503
|
+
else
|
|
504
|
+
a('rate')
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
end
|
|
511
|
+
|