Class: TaskJuggler::Project

Inherits:
Object show all
Includes:
MessageHandler
Defined in:
lib/taskjuggler/Project.rb

Overview

This class implements objects that hold all project properties. Project generally consist of resources, tasks and a number of other optional properties. Tasks, Resources, Accounts and Shifts are all build on the same underlying storage class PropertyTreeNode. Properties of the same kind are kept in PropertySet objects. There is only one PropertySet for each type of property. Additionally, each property may belong to various PropertyList objects. In contrast to PropertySet objects, PropertyList object have well defined sorting order and no information about the attributes of each type of property. The PropertySet holds the blueprints for the data construction inside the PropertyTreeNode objects. It contains the list of known Attributes.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from MessageHandler

#critical, #debug, #error, #fatal, #info, #warning

Constructor Details

#initialize(id, name, version) ⇒ Project

Create a project with the specified id, name and version. The constructor will set default values for all project attributes.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/taskjuggler/Project.rb', line 65

def initialize(id, name, version)
  AttributeBase.setMode(0)
  @attributes = {
    # This nested Array defines the supported alert levels. The lowest
    # level comes first at index 0 and the level rises from there on.
    # Currently, these levels are hardcoded. Each level entry has 3
    # members: the tjp syntax token, the user visible name and the
    # associated color as RGB byte array.
    'alertLevels' => AlertLevelDefinitions.new,
    'auxdir' => '',
    'copyright' => nil,
    'costaccount' => nil,
    'currency' => "EUR",
    'currencyFormat' => RealFormat.new([ '-', '', '', ',', 2 ]),
    'dailyworkinghours' => 8.0,
    'end' => nil,
    'markdate' => nil,
    'flags' => [],
    'journal' => Journal.new,
    'limits' => nil,
    'leaves' => LeaveList.new,
    'loadUnit' => :days,
    'name' => name,
    'navigators' => {},
    'now' => TjTime.new.align(3600),
    'numberFormat' => RealFormat.new([ '-', '', '', '.', 1]),
    'priority' => 500,
    'projectid' => id || "prj",
    'projectids' => [ id ],
    'rate' => 0.0,
    'revenueaccount' => nil,
    'scheduleGranularity' => Project.maxScheduleGranularity,
    'shortTimeFormat' => "%H:%M",
    'start' => nil,
    'timeFormat' => "%Y-%m-%d",
    'timezone' => TjTime.timeZone,
    'trackingScenarioIdx' => nil,
    'version' => version || "1.0",
    'weekStartsMonday' => true,
    'workinghours' => nil,
    'yearlyworkingdays' => 260.714
  }

  # Before we can add any properties to this project, we need to define the
  # attributes that each of the property types will be using. In TaskJuggler
  # lingo, properties of a project are resources, tasks, accounts, shifts
  # and scenarios. Each of these properties can have lots of further
  # information attached to it. These bits of information are called
  # attributes. An attribute is defined by the AttributeDefinition class.
  # The PropertySet objects need to be fed with a list of such attribute
  # definitions to register the attributes with the properties.
  @scenarios = PropertySet.new(self, true)
  attrs = [
    # ID           Name          Type
    #     Inh.   Inh.Prj  Scen.  Default
    [ 'active',   'Enabled',    BooleanAttribute,
          true,  false,   false, true ],
    [ 'id',       'ID',         StringAttribute,
          false, false,   false, nil ],
    [ 'name',     'Name',       StringAttribute,
          false, false,   false, nil ],
    [ 'ownbookings', 'Own Bookings', BooleanAttribute,
          false, false,   false, true ],
    [ 'projection', 'Projection Mode', BooleanAttribute,
          true,  false,   false, false ],
    [ 'seqno',    'No',          IntegerAttribute,
          false, false,   false, nil ],
  ]
  attrs.each { |a| @scenarios.addAttributeType(AttributeDefinition.new(*a)) }

  @shifts = PropertySet.new(self, true)
  attrs = [
    # ID           Name            Type
    #     Inh.   Inh.Prj  Scen.  Default
    [ 'bsi',      'BSI',           StringAttribute,
          false, false,   false, "" ],
    [ 'id',       'ID',            StringAttribute,
          false, false,   false, nil ],
    [ 'index',     'Index',        IntegerAttribute,
          false, false,   false, -1 ],
    [ 'leaves',    'Leaves',       LeaveListAttribute,
          true,  true,    true,  LeaveList.new ],
    [ 'name',     'Name',          StringAttribute,
          false, false,   false, nil ],
    [ 'replace',   'Replace',      BooleanAttribute,
          true,  false,   true,  false ],
    [ 'seqno',    'No',            IntegerAttribute,
          false, false,   false, nil ],
    [ 'timezone',  'Time Zone',    StringAttribute,
          true,  true,    true,  TjTime.timeZone ],
    [ 'tree',      'Tree Index',   StringAttribute,
          false, false,   false, "" ],
    [ 'workinghours', 'Working Hours', WorkingHoursAttribute,
          true,  true,    true,  nil ]
  ]
  attrs.each { |a| @shifts.addAttributeType(AttributeDefinition.new(*a)) }

  @accounts = PropertySet.new(self, true)
  attrs = [
    # ID           Name            Type
    #     Inh.   Inh.Prj  Scen.  Default
    [ 'aggregate', 'Aggregate',    SymbolAttribute,
          true,  false,   false, :tasks ],
    [ 'bsi',       'BSI',          StringAttribute,
          false, false,   false, "" ],
    [ 'credits',   'Credits',      AccountCreditListAttribute,
          false, false,   true,  [] ],
    [ 'id',       'ID',         StringAttribute,
          false, false,   false, nil ],
    [ 'index',     'Index',        IntegerAttribute,
          false, false,   false, -1 ],
    [ 'flags',     'Flags',        FlagListAttribute,
          true,  false,   true,  [] ],
    [ 'name',     'Name',       StringAttribute,
          false, false,   false, nil ],
    [ 'seqno',    'No',          IntegerAttribute,
          false, false,   false, nil ],
    [ 'tree',      'Tree Index',   StringAttribute,
          false, false,   false, "" ]
  ]
  attrs.each { |a| @accounts.addAttributeType(AttributeDefinition.new(*a)) }

  @resources = PropertySet.new(self, true)
  attrs = [
    # ID           Name            Type
    #     Inh.   Inh.Prj  Scen.  Default
    [ 'alloctdeffort', 'Alloctd. Effort', FloatAttribute,
          false, false,   true,  0.0 ],
    [ 'bsi',       'BSI',          StringAttribute,
          false, false,   false, "" ],
    [ 'chargeset', 'Charge Sets',  ChargeSetListAttribute,
          true,  false,   true,  [] ],
    [ 'criticalness', 'Criticalness', FloatAttribute,
          false, false,   true,  0.0 ],
    [ 'duties',    'Duties',       TaskListAttribute,
          false, false,   true,  [] ],
    [ 'directreports', 'Direct Reports', ResourceListAttribute,
          false, false,   true,  [] ],
    [ 'efficiency','Efficiency',   FloatAttribute,
          true,  false,   true,  1.0 ],
    [ 'effort', 'Total Effort',    IntegerAttribute,
          false, false,   true,  0 ],
    [ 'email',     'Email',        StringAttribute,
          false, false,   false, nil ],
    [ 'fail',      'Failure Conditions', LogicalExpressionListAttribute,
          false, false,   false,  [] ],
    [ 'flags',     'Flags',        FlagListAttribute,
          true,  false,   true,  [] ],
    [ 'index',     'Index',        IntegerAttribute,
          false, false,   false, -1 ],
    [ 'leaveallowances', 'Leave Allowances', LeaveAllowanceListAttribute,
          true,  false,   true,  LeaveAllowanceList.new ],
    [ 'leaves',    'Leaves',       LeaveListAttribute,
          true,  true,    true,  LeaveList.new ],
    [ 'limits',    'Limits',       LimitsAttribute,
          true,  true,    true,  nil ],
    [ 'managers', 'Managers',      ResourceListAttribute,
          true,  false,   true,  [] ],
    [ 'rate',      'Rate',         FloatAttribute,
          true,  true,    true,  0.0 ],
    [ 'reports', 'Reports', ResourceListAttribute,
          false, false,   true,  [] ],
    [ 'seqno',    'No',          IntegerAttribute,
          false, false,   false, nil ],
    [ 'shifts',    'Shifts',       ShiftAssignmentsAttribute,
          true, false,    true,  nil ],
    [ 'tree',      'Tree Index',   StringAttribute,
          false, false,   false, "" ],
    [ 'warn',      'Warning Condition', LogicalExpressionListAttribute,
          false, false,   false, [] ],
    [ 'workinghours', 'Working Hours', WorkingHoursAttribute,
          true,  true,    true,  nil ]
  ]
  attrs.each { |a| @resources.addAttributeType(AttributeDefinition.new(*a)) }

  @tasks = PropertySet.new(self, false)
  attrs = [
    # ID           Name            Type
    #     Inh.   Inh.Prj  Scen.  Default
    [ 'allocate', 'Allocations',   AllocationAttribute,
          true,  false,   true,  [] ],
    [ 'assignedresources', 'Assigned Resources', ResourceListAttribute,
          false, false,   true,  [] ],
    [ 'booking',   'Bookings',     BookingListAttribute,
          false, false,   true,  [] ],
    [ 'bsi',       'BSI',          StringAttribute,
          false, false,   false, "" ],
    [ 'charge',    'Charges',      ChargeListAttribute,
          false, false,   true,  [] ],
    [ 'chargeset', 'Charge Sets',  ChargeSetListAttribute,
          true,  false,   true,  [] ],
    [ 'complete',  'Completion',   FloatAttribute,
          false, false,   true,  nil ],
    [ 'competitors', 'Competitors', TaskListAttribute,
          false, false,   true,  [] ],
    [ 'criticalness', 'Criticalness', FloatAttribute,
          false, false,   true,  0.0 ],
    [ 'depends',   'Preceding tasks', DependencyListAttribute,
          true,  false,   true,  [] ],
    [ 'duration',  'Duration',     DurationAttribute,
          false, false,   true,  0 ],
    [ 'effort',    'Effort',       DurationAttribute,
          false, false,   true,  0 ],
    [ 'effortdone', 'Completed Effort', IntegerAttribute,
          false, false,   true,  nil ],
    [ 'effortleft', 'Remaining Effort', IntegerAttribute,
          false, false,   true,  nil ],
    [ 'end',       'End',          DateAttribute,
          false, false,   true,  nil ],
    [ 'endpreds',  'End Preds.',   TaskDepListAttribute,
          false, false,   true,  [] ],
    [ 'endsuccs',  'End Succs.',   TaskDepListAttribute,
          false, false,   true,  [] ],
    [ 'fail',      'Failure Conditions', LogicalExpressionListAttribute,
          false, false,   false, [] ],
    [ 'flags',     'Flags',        FlagListAttribute,
          true,  false,   true,  [] ],
    [ 'forward',   'Scheduling',   BooleanAttribute,
          true,  false,   true,  true ],
    [ 'gauge',     'Schedule gauge', StringAttribute,
          false, false,   true,  nil ],
    [ 'id',       'ID',         StringAttribute,
          false, false,   false, nil ],
    [ 'index',     'Index',        IntegerAttribute,
          false, false,   false, -1 ],
    [ 'length',    'Length',       DurationAttribute,
          false, false,   true,  0 ],
    [ 'limits',    'Limits',       LimitsAttribute,
          false, false,   true,  nil ],
    [ 'maxend',    'Max. End',     DateAttribute,
          true,  false,   true,  nil ],
    [ 'maxstart',  'Max. Start',   DateAttribute,
          false, false,   true,  nil ],
    [ 'milestone', 'Milestone',    BooleanAttribute,
          false, false,   true,  false ],
    [ 'minend',    'Min. End',     DateAttribute,
          false, false,   true,  nil ],
    [ 'minstart',  'Min. Start',   DateAttribute,
          true,  false,   true,  nil ],
    [ 'name',     'Name',       StringAttribute,
          false, false,   false, nil ],
    [ 'note',      'Note',         RichTextAttribute,
          false, false,   false, nil ],
    [ 'pathcriticalness', 'Path Criticalness', FloatAttribute,
          false, false,   true, 0.0 ],
    [ 'precedes',  'Following tasks', DependencyListAttribute,
          true,  false,   true,  [] ],
    [ 'priority',  'Priority',     IntegerAttribute,
          true,  true,    true,  500 ],
    [ 'projectid', 'Project ID',   SymbolAttribute,
          true,  true,    true,  nil ],
    [ 'responsible', 'Responsible', ResourceListAttribute,
          true,  false,   true,  [] ],
    [ 'scheduled', 'Scheduled',    BooleanAttribute,
          true,  false,   true,  false ],
    [ 'projectionmode', 'Projection Mode', BooleanAttribute,
          true,  false,   true,  false ],
    [ 'seqno',    'No',          IntegerAttribute,
          false, false,   false, nil ],
    [ 'shifts',     'Shifts',      ShiftAssignmentsAttribute,
          true,  false,   true, nil ],
    [ 'start',     'Start',        DateAttribute,
          false, false,   true,  nil ],
    [ 'startpreds', 'Start Preds.', TaskDepListAttribute,
          false, false,   true,  [] ],
    [ 'startsuccs', 'Start Succs.', TaskDepListAttribute,
          false, false,   true,  [] ],
    [ 'status',    'Task Status',  StringAttribute,
          false, false,   true,  "" ],
    [ 'tree',      'Tree Index',   StringAttribute,
          false, false,   false, "" ],
    [ 'warn',      'Warning Condition', LogicalExpressionListAttribute,
          false, false,   false, [] ]
  ]
  attrs.each { |a| @tasks.addAttributeType(AttributeDefinition.new(*a)) }

  @reports = PropertySet.new(self, false)
  attrs = [
    # ID           Name            Type
    #     Inh.   Inh.Prj  Scen.  Default
    [ 'accountroot',  'Account Root',    PropertyAttribute,
          true,  false,   false, nil ],
    [ 'auxdir', 'Auxiliary files directory', StringAttribute,
          true,  true,    false, '' ],
    [ 'bsi',       'BSI',          StringAttribute,
          false, false,   false, '' ],
    [ 'caption',   'Caption',      RichTextAttribute,
          true,  false,   false, nil ],
    [ 'center',    'Center',       RichTextAttribute,
          true,  false,   false, nil ],
    [ 'columns',   'Columns',      ColumnListAttribute,
          true,  false,   false, [] ],
    [ 'costaccount', 'Cost Account', AccountAttribute,
          true,  true,    false, nil ],
    [ 'currencyFormat', 'Currency Format', RealFormatAttribute,
          true,  true,    false, nil ],
    [ 'definitions', 'Definitions', DefinitionListAttribute,
          true,  false,   false, KeywordArray.new([ '*' ]) ],
    [ 'end',       'End',          DateAttribute,
          true,  true,    false, nil ],
    [ 'markdate',  'Markdate',     DateAttribute,
          true,  true,    false, nil ],
    [ 'epilog',    'Epilog',       RichTextAttribute,
          true,  false,   false, nil ],
    [ 'flags',     'Flags',        FlagListAttribute,
          true,  false,   true,  [] ],
    [ 'footer',    'Footer',       RichTextAttribute,
          true,  false,   false, nil ],
    [ 'formats',   'Formats',      FormatListAttribute,
          true,  false,   false, [] ],
    [ 'ganttBars', 'Gantt Bars',   BooleanAttribute,
          true,  false,   false, true ],
    [ 'header',    'Header',       RichTextAttribute,
          true,  false,   false, nil ],
    [ 'headline',  'Headline',     RichTextAttribute,
          true,  false,   false, nil ],
    [ 'hideAccount', 'Hide Account', LogicalExpressionAttribute,
          true,  false,   false, nil ],
    [ 'hideJournalEntry', 'Hide JournalEntry', LogicalExpressionAttribute,
          true,  false,   false, nil ],
    [ 'hideResource', 'Hide Resource', LogicalExpressionAttribute,
          true,  false,   false, nil ],
    [ 'hideTask',  'Hide Task',    LogicalExpressionAttribute,
          true,  false,   false, nil ],
    [ 'height',    'Height',       IntegerAttribute,
          false,  false,   false, 480 ],
    [ 'id',       'ID',         StringAttribute,
          false, false,   false, nil ],
    [ 'index',     'Index',        IntegerAttribute,
          false, false,   false, -1 ],
    [ 'interactive', 'Interactive', BooleanAttribute,
          false, false,   false, false ],
    [ 'journalAttributes', 'Journal Attributes', SymbolListAttribute,
          true,  false,   false, KeywordArray.new([ '*' ]) ],
    [ 'journalMode', 'Journal Mode', SymbolAttribute,
          true,  false,   false, :journal ],
    [ 'left',      'Left',         RichTextAttribute,
          true,  false,   false, nil ],
    [ 'loadUnit',  'Load Unit',    StringAttribute,
          true,  true,    false, nil ],
    [ 'name',     'Name',       StringAttribute,
          false, false,   false, nil ],
    [ 'now',       'Now',          DateAttribute,
          true,  true,    false, nil ],
    [ 'numberFormat', 'Number Format', RealFormatAttribute,
          true,  true,    false, nil ],
    [ 'openNodes', 'Open Nodes',   NodeListAttribute,
          false, false,   false, nil ],
    [ 'prolog',    'Prolog',       RichTextAttribute,
          true, false,   false, nil ],
    [ 'rawHtmlHead', 'Raw HTML Header', StringAttribute,
          true, false,   false, nil ],
    [ 'resourceAttributes', 'Resource Attributes', FormatListAttribute,
          true,  false,   false, KeywordArray.new([ '*' ]) ],
    [ 'resourceroot',  'resource Root', PropertyAttribute,
          true,  false,   false, nil ],
    [ 'revenueaccount', 'Revenue Account', AccountAttribute,
          true,  true,    false, nil ],
    [ 'right',     'Right',        RichTextAttribute,
          true,  false,   false, nil ],
    [ 'rollupAccount', 'Rollup Account', LogicalExpressionAttribute,
          true,  false,   false, nil ],
    [ 'rollupResource', 'Rollup Resource', LogicalExpressionAttribute,
          true,  false,   false, nil ],
    [ 'rollupTask', 'Rollup Task', LogicalExpressionAttribute,
          true, false,    false, nil ],
    [ 'scenarios',  'Scenarios',   ScenarioListAttribute,
          true, false,    false, [ 0 ] ],
    [ 'selfcontained', 'Selfcontained', BooleanAttribute,
          true, false,    false, false ],
    [ 'seqno',    'No',          IntegerAttribute,
          false, false,   false, nil ],
    [ 'shortTimeFormat', 'Short Time Format', StringAttribute,
          true,  true,    false, nil ],
    [ 'sortAccounts', 'Sort Accounts', SortListAttribute,
          true,  false,   false, [[ 'seqno', true, -1 ]] ],
    [ 'sortJournalEntries', 'Sort Journal Entries', JournalSortListAttribute,
          true,  false,   false, [[ :alert, 1 ], [ :date, 1 ], [ :seqno, 1 ]] ],
    [ 'sortResources', 'Sort Resources', SortListAttribute,
          true,  false,   false, [[ 'seqno', true, -1 ]] ],
    [ 'sortTasks', 'Sort Tasks',   SortListAttribute,
          true,  false,   false, [[ 'seqno', true, -1 ]] ],
    [ 'start',     'Start',        DateAttribute,
          true,  true,    false, nil ],
    [ 'taskAttributes', 'Task Attributes', FormatListAttribute,
          true,  false,   false, KeywordArray.new([ '*' ]) ],
    [ 'taskroot',  'Task Root',    PropertyAttribute,
          true,  false,   false, nil ],
    [ 'timeFormat', 'Time Format', StringAttribute,
          true,  true,    false, nil ],
    [ 'timeOffId',  'Time Off ID', StringAttribute,
          false,  false,  false, nil ],
    [ 'timeOffName',  'Time Off Name', StringAttribute,
          false,  false,  false, nil ],
    [ 'timezone', 'Time Zone',     StringAttribute,
          true,  true,    false, TjTime.timeZone ],
    [ 'title',    'Title',         StringAttribute,
          true,  false,   false, nil ],
    [ 'tree',      'Tree Index',   StringAttribute,
          false, false,   false, "" ],
    [ 'weekStartsMonday', 'Week Starts Monday', BooleanAttribute,
          true,  true,    false, false ],
    [ 'width',     'Width',        IntegerAttribute,
          true,  false,   false, 640 ],
    [ 'novevents',  'No vevents in icalreports', BooleanAttribute,
          true,  false,   false, false ]
  ]
  attrs.each { |a| @reports.addAttributeType(AttributeDefinition.new(*a)) }

  Scenario.new(self, 'plan', 'Plan Scenario', nil)

  # A list of files that contained the project data.
  @inputFiles = FileList.new

  @timeSheets = TimeSheets.new

  # A scoreboard that reflects the global working hours and leaves.
  @scoreboard = nil
  # A scoreboard that reflects the global working hours but no leaves.
  @scoreboardNoLeaves = nil

  # The ReportContext provides additional settings to the report that can
  # complement or replace the report attributes. Reports can include other
  # reports. During report generation, only one context is active, but the
  # context of enclosing reports needs to be preserved. Therefor we use a
  # stack to implement this.
  @reportContexts = []
  @outputDir = './'
  @warnTsDeltas = false
end

Instance Attribute Details

#accountsObject (readonly)

Returns the value of attribute accounts.



59
60
61
# File 'lib/taskjuggler/Project.rb', line 59

def accounts
  @accounts
end

#inputFilesObject (readonly)

Returns the value of attribute inputFiles.



59
60
61
# File 'lib/taskjuggler/Project.rb', line 59

def inputFiles
  @inputFiles
end

#outputDirObject

Returns the value of attribute outputDir.



61
62
63
# File 'lib/taskjuggler/Project.rb', line 61

def outputDir
  @outputDir
end

#reportContextsObject

Returns the value of attribute reportContexts.



61
62
63
# File 'lib/taskjuggler/Project.rb', line 61

def reportContexts
  @reportContexts
end

#reportsObject (readonly)

Returns the value of attribute reports.



59
60
61
# File 'lib/taskjuggler/Project.rb', line 59

def reports
  @reports
end

#resourcesObject (readonly)

Returns the value of attribute resources.



59
60
61
# File 'lib/taskjuggler/Project.rb', line 59

def resources
  @resources
end

#scenariosObject (readonly)

Returns the value of attribute scenarios.



59
60
61
# File 'lib/taskjuggler/Project.rb', line 59

def scenarios
  @scenarios
end

#shiftsObject (readonly)

Returns the value of attribute shifts.



59
60
61
# File 'lib/taskjuggler/Project.rb', line 59

def shifts
  @shifts
end

#tasksObject (readonly)

Returns the value of attribute tasks.



59
60
61
# File 'lib/taskjuggler/Project.rb', line 59

def tasks
  @tasks
end

#timeSheetsObject (readonly)

Returns the value of attribute timeSheets.



59
60
61
# File 'lib/taskjuggler/Project.rb', line 59

def timeSheets
  @timeSheets
end

#warnTsDeltasObject

Returns the value of attribute warnTsDeltas.



61
62
63
# File 'lib/taskjuggler/Project.rb', line 61

def warnTsDeltas
  @warnTsDeltas
end

Class Method Details

.maxScheduleGranularityObject

TaskJuggler keeps all times in UTC. All time values must be multiples of the used scheduling granularity. If the local time zone is not hour-aligned to UTC, the maximum allowed schedule granularity is reduced.



1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
# File 'lib/taskjuggler/Project.rb', line 1016

def Project.maxScheduleGranularity
  refTime = Time.gm(2000, 1, 1, 0, 0, 0)
  case (min = refTime.getlocal.min)
  when 0
    # We are hour-aligned to UTC; scheduleGranularity is 1 hour
    60 * 60
  when 30
    # We are half-hour off from UTC; scheduleGranularity is 30 minutes
    30 * 60
  when 15, 45
    # We are 15 or 45 minutes off from UTC; scheduleGranularity is 15
    # minutes
    15 * 60
  else
    raise "Unknown Time zone alignment #{min}"
  end
end

Instance Method Details

#[](name) ⇒ Object

Query the value of a Project attribute. name is the ID of the attribute.



503
504
505
506
507
508
# File 'lib/taskjuggler/Project.rb', line 503

def [](name)
  if !@attributes.has_key?(name)
    raise "Unknown project attribute #{name}"
  end
  @attributes[name]
end

#[]=(name, value) ⇒ Object

Set the Project attribute with ID name to value.



511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/taskjuggler/Project.rb', line 511

def []=(name, value)
  if !@attributes.has_key?(name)
    raise "Unknown project attribute #{name}"
  end
  @attributes[name] = value

  # If the start, end or schedule granularity have been changed, we have
  # to reset the working hours.
  if %w(start end scheduleGranularity timezone timingresolution).
    include?(name)
    if @attributes['start'] && @attributes['end']
      @attributes['workinghours'] =
        WorkingHours.new(@attributes['scheduleGranularity'],
                         @attributes['start'], @attributes['end'],
                         @attributes['timezone'])
    end
  end
  value
end

#account(id) ⇒ Object

Return the Account with the ID id or return nil if it does not exist.



599
600
601
# File 'lib/taskjuggler/Project.rb', line 599

def (id)
  @accounts[id]
end

#addAccount(account) ⇒ Object

:nodoc:



848
849
850
# File 'lib/taskjuggler/Project.rb', line 848

def addAccount() # :nodoc:
  @accounts.addProperty()
end

#addReport(report) ⇒ Object

:nodoc:



860
861
862
# File 'lib/taskjuggler/Project.rb', line 860

def addReport(report) # :nodoc:
  @reports.addProperty(report)
end

#addResource(resource) ⇒ Object

:nodoc:



856
857
858
# File 'lib/taskjuggler/Project.rb', line 856

def addResource(resource) # :nodoc:
  @resources.addProperty(resource)
end

#addScenario(scenario) ⇒ Object

The following functions are not intended to be called from outside the TaskJuggler library. There is no guarantee that these functions will be usable or present in future releases.



840
841
842
# File 'lib/taskjuggler/Project.rb', line 840

def addScenario(scenario) # :nodoc:
  @scenarios.addProperty(scenario)
end

#addShift(shift) ⇒ Object

:nodoc:



844
845
846
# File 'lib/taskjuggler/Project.rb', line 844

def addShift(shift) # :nodoc:
  @shifts.addProperty(shift)
end

#addTask(task) ⇒ Object

:nodoc:



852
853
854
# File 'lib/taskjuggler/Project.rb', line 852

def addTask(task) # :nodoc:
  @tasks.addProperty(task)
end

#anyResourceAvailable?(sbIdx) ⇒ Boolean

Return true if for the date specified by the global scoreboard index sbIdx there is any resource that is available.

Returns:

  • (Boolean)


1008
1009
1010
# File 'lib/taskjuggler/Project.rb', line 1008

def anyResourceAvailable?(sbIdx)
  @resourceAvailability[sbIdx]
end

#attributeName(id) ⇒ Object

Return the name of the attribute id. Since we don’t know whether we are looking for a task, resource, etc. attribute, we prefer tasks over resources here.



1037
1038
1039
1040
1041
1042
1043
# File 'lib/taskjuggler/Project.rb', line 1037

def attributeName(id)
  # We have to see if the attribute id is a task or resource attribute and
  # return it's name.
  (name = @tasks.attributeName(id)).nil? &&
  (name = @resources.attributeName(id)).nil?
  name
end

#checkReportsObject

Make sure that we have a least one report defined that has an output format.



697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
# File 'lib/taskjuggler/Project.rb', line 697

def checkReports
  if @reports.empty?
    warning('no_report_defined',
            "This project has no reports defined. " +
            "No output data will be generated.")
  end

  unless @accounts.empty?
    @reports.each do |report|
      if (report.typeSpec != :accountreport) &&
         (report.get('costaccount').nil? ||
          report.get('revenueaccount').nil?)
        warning('report_without_balance',
                "The report #{report.fullId} has no 'balance' defined. " +
                "No cost or revenue computation will be possible.",
                report.sourceFileInfo)
      end
    end
  end

  @reports.each do |report|
    return unless report.get('formats').empty?
  end

  warning('all_formats_empty',
          "None of the reports has a 'formats' attribute. " +
          "No output data will be generated.")
end

#checkTimeSheetsObject



829
830
831
# File 'lib/taskjuggler/Project.rb', line 829

def checkTimeSheets
  @timeSheets.check
end

#collectTimeOffIntervals(iv, minDuration) ⇒ Object



975
976
977
978
979
# File 'lib/taskjuggler/Project.rb', line 975

def collectTimeOffIntervals(iv, minDuration)
  @scoreboard.collectIntervals(iv, minDuration) do |val|
    val.is_a?(Integer) && (val & 0x3E) != 0
  end
end

#convertToDailyLoad(seconds) ⇒ Object

Convert working seconds to working days. The result depends on the setting of the global ‘dailyworkinghours’ attribute.



934
935
936
# File 'lib/taskjuggler/Project.rb', line 934

def convertToDailyLoad(seconds)
  seconds / (@attributes['dailyworkinghours'] * 3600.0)
end

#dailyWorkingHoursObject

Return the average number of working hours per day. This defaults to 8 but can be set to other values by the user.



538
539
540
# File 'lib/taskjuggler/Project.rb', line 538

def dailyWorkingHours
  @attributes['dailyworkinghours'].to_f
end

#dateToIdx(date, forceIntoProject = true) ⇒ Object

Convert a date (TjTime) to the equivalent Scoreboard index. If forceIntoProject is true, the date will be pushed into the project time frame.



960
961
962
963
964
965
966
967
968
969
970
971
972
973
# File 'lib/taskjuggler/Project.rb', line 960

def dateToIdx(date, forceIntoProject = true)
  if (date < @attributes['start'] || date > @attributes['end'])
    # Date is out of range.
    if forceIntoProject
      return 0 if date < @attributes['start']
      return scoreboardSize - 1 if date > @attributes['end']
    else
      raise "Date #{date} is out of project time range " +
            "(#{@attributes['start']} - #{@attributes['end']})"
    end
  end
  # Calculate the corresponding index.
  ((date - @attributes['start']) / @attributes['scheduleGranularity']).to_i
end

#deep_cloneObject

Overload the deep_clone function so that references to the project don’t lead to deep copying of the whole project.



498
499
500
# File 'lib/taskjuggler/Project.rb', line 498

def deep_clone
  self
end

#enableTraceReports(enable) ⇒ Object

Add the CSV output format to all reports of type ‘tracereport’ if enable is true. Otherwise remove all CSV output formats.



679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
# File 'lib/taskjuggler/Project.rb', line 679

def enableTraceReports(enable)
  @reports.each do |report|
     next unless report.typeSpec == :tracereport

     if enable
       # Enable the CSV format for the tracereport
       unless report.get('formats').include?(:csv)
         report.get('formats') << :csv
       end
     else
       # Disabe CSV format for the tracereport
       report.get('formats').delete(:csv)
     end
  end
end

#generateReport(reportId, regExpMode, formats = nil, dynamicAttributes = nil) ⇒ Object



776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
# File 'lib/taskjuggler/Project.rb', line 776

def generateReport(reportId, regExpMode, formats = nil,
                   dynamicAttributes = nil)
  reportList = regExpMode ? reportList = matchingReports(reportId) :
                            [ reportId ]
  reportList.each do |id|
    unless (report = @reports[id])
      error('unknown_report_id',
            "Request to generate unknown report #{id}")
    end
    if formats.nil? && report.get('formats').empty?
      error('formats_empty',
            "The report #{report.fullId} has no 'formats' attribute. " +
            "No output data will be generated.", report.sourceFileInfo)
    end

    Log.startProgressMeter("Report #{report.name}")
    @reportContexts.push(context = ReportContext.new(self, report))

    # If we have dynamic attributes we need to backup the old attributes
    # first, then parse the dynamicAttributes String replacing the
    # original values.
    if dynamicAttributes
      unless dynamicAttributes.empty?
        context.attributeBackup = report.backupAttributes
        parser = ProjectFileParser.new
        parser.parseReportAttributes(report, dynamicAttributes)
      end
      report.set('interactive', true)
    end

    report.generate(formats)

    if dynamicAttributes && !dynamicAttributes.empty?
      report.restoreAttributes(context.attributeBackup)
    end
    @reportContexts.pop
    Log.stopProgressMeter
  end
end

#generateReports(maxCpuCores) ⇒ Object

Call this function to generate the reports based on the scheduling result. This function may only be called after Project#schedule has been called.



728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
# File 'lib/taskjuggler/Project.rb', line 728

def generateReports(maxCpuCores)
  @reports.index
  if maxCpuCores == 1
    @reports.each do |report|
      # Skip reports that don't have any format specified or trace reports
      # when generateTraces is false.
      next if report.get('formats').empty?

      Log.startProgressMeter("Report #{report.name}")
      @reportContexts.push(ReportContext.new(self, report))
      report.generate
      @reportContexts.pop
      Log.stopProgressMeter
    end
  else
    # Kickoff the generation of all reports by pushing the jobs into the
    # BatchProcessor queue.
    bp = BatchProcessor.new(maxCpuCores)
    @reports.each do |report|
      # Skip reports that don't have any format specified or trace reports
      # when generateTraces is false.
      next if report.get('formats').empty? ||
              (report.typeSpec == :trace && !generateTraces)

      bp.queue(report) {
        @reportContexts.push(ReportContext.new(self, report))
        res = report.generate
        @reportContexts.pop
        res
      }
    end
    # Now wait for all the jobs to finish.
    bp.wait do |report|
      Log.startProgressMeter("Report #{report.tag.name}")
      $stdout.print(report.stdout)
      $stderr.print(report.stderr)
      if report.retVal.signaled?
        error('rg_signal', "Signal raised")
      end
      unless report.retVal.success?
        error('rg_abort', "Process aborted")
      end
      Log.stopProgressMeter
    end
  end
  DataCache.instance.flush
end

#getWorkSlots(startIdx, endIdx) ⇒ Object

Return the number of global working slots during the given time interval specified by startIdx and endIdx. This method takes global leaves into account.



996
997
998
999
1000
1001
1002
1003
# File 'lib/taskjuggler/Project.rb', line 996

def getWorkSlots(startIdx, endIdx)
  slots = 0
  startIdx.upto(endIdx) do |idx|
    slots += 1 unless @scoreboard[idx]
  end

  slots
end

#hasWorkingTime(*args) ⇒ Object

call-seq:

hasWorkingTime(startTime, endTime) -> true or false
hasWorkingTime(interval) -> true or false

Return true if the interval overlaps with a globally defined working time or false if not. Global work time means, no global leaves defined and the slot lies within a defined global working time period.



911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
# File 'lib/taskjuggler/Project.rb', line 911

def hasWorkingTime(*args)
  # Normalize argument(s) to TimeInterval
  if args.length == 1
    if args[0].is_a?(TimeInterval)
      startIdx = dateToIdx(args[0].start)
      endIdx = dateToIdx(args[0].end)
    else
      raise ArgumentError, "Unsupported argument type #{args[0].class}"
    end
  else
    startIdx = dateToIdx(args[0])
    endIdx = dateToIdx(args[1])
  end

  startIdx.upto(endIdx) do |idx|
    return true if @scoreboard[idx]
  end

  false
end

#idxToDate(idx) ⇒ Object

Convert a Scoreboard index to the equivalent date. idx is the index and it must be within the range of the Scoreboard objects. If not, an exception is raised.



950
951
952
953
954
955
# File 'lib/taskjuggler/Project.rb', line 950

def idxToDate(idx)
  if $DEBUG && (idx < 0 || idx > scoreboardSize)
    raise "Scoreboard index out of range"
  end
  @attributes['start'] + idx * @attributes['scheduleGranularity']
end

#isWorkingTime(*args) ⇒ Object

call-seq:

isWorkingTime(slotIdx) -> true or false
isWorkingTime(slot) -> true or false
isWorkingTime(startTime, endTime) -> true or false
isWorkingTime(interval) -> true or false

Return true if the slot or interval is within globally defined working time or false if not. If the argument is a TimeInterval, all slots of the interval must be working time to return true as result. Global work time means, no global leaves defined and the slot lies within a defined global working time period.



879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
# File 'lib/taskjuggler/Project.rb', line 879

def isWorkingTime(*args)
  # Normalize argument(s) to TimeInterval
  if args.length == 1
    if args[0].is_a?(Integer)
      return @scoreboard[args[0]].nil?
    elsif args[0].is_a?(TjTime)
      return @scoreboard[dateToIdx(args[0])].nil?
    elsif args[0].is_a?(TimeInterval)
      startIdx = dateToIdx(args[0].start)
      endIdx = dateToIdx(args[0].end)
    else
      raise ArgumentError, "Unsupported argument type #{args[0].class}"
    end
  else
    startIdx = dateToIdx(args[0])
    endIdx = dateToIdx(args[1])
  end

  startIdx.upto(endIdx) do |idx|
    return false if @scoreboard[idx]
  end

  true
end

#journal(query) ⇒ Object



1045
1046
1047
# File 'lib/taskjuggler/Project.rb', line 1045

def journal(query)
  @attributes['journal'].to_rti(query)
end

#listReports(reportId, regExpMode) ⇒ Object



816
817
818
819
820
821
822
823
824
825
826
827
# File 'lib/taskjuggler/Project.rb', line 816

def listReports(reportId, regExpMode)
  reportList = regExpMode ? reportList = matchingReports(reportId) :
                            @reports[reportId] ? [ reportId ] : []
  puts "No match for #{reportId}" if reportList.empty?
  reportList.each do |id|
    report = @reports[id]
    formats = report.get('formats')
    next if formats.empty?

    puts sprintf("%s\t%s\t%s", id, formats.join(', '), report.name)
  end
end

#monthlyWorkingDaysObject

Return the average number of working days per month.



548
549
550
# File 'lib/taskjuggler/Project.rb', line 548

def monthlyWorkingDays
  @attributes['yearlyworkingdays'] / 12.0
end

#removeAccount(account) ⇒ Object

:nodoc:



864
865
866
# File 'lib/taskjuggler/Project.rb', line 864

def removeAccount() # :nodoc:
  @accounts.removeProperty()
end

#report(id) ⇒ Object

Return the Report with the ID id or return nil if it does not exist.



614
615
616
# File 'lib/taskjuggler/Project.rb', line 614

def report(id)
  @reports[id]
end

#reportByName(name) ⇒ Object

Return the Report with the name name or return nil if it does not exist.



620
621
622
623
624
625
# File 'lib/taskjuggler/Project.rb', line 620

def reportByName(name)
  @reports.each do |report|
    return report if report.name == name
  end
  nil
end

#resource(id) ⇒ Object

Return the Resource with the ID id or return nil if it does not exist.



609
610
611
# File 'lib/taskjuggler/Project.rb', line 609

def resource(id)
  @resources[id]
end

#scenario(arg) ⇒ Object

call-seq:

scenario(index) -> Scenario
scenario(id) -> Scenario

Return the Scenario with the given id or index.



567
568
569
570
571
572
573
574
575
576
# File 'lib/taskjuggler/Project.rb', line 567

def scenario(arg)
  if arg.is_a?(Integer)
    @scenarios.each do |sc|
      return sc if sc.sequenceNo - 1 == arg
    end
  else
    return @scenarios[arg]
  end
  nil
end

#scenarioCountObject

Return the number of defined scenarios for the project.



532
533
534
# File 'lib/taskjuggler/Project.rb', line 532

def scenarioCount
  @scenarios.items
end

#scenarioIdx(sc) ⇒ Object

call-seq:

scenarioIdx(scenario)
scenarioIdx(id)

Return the index of the given Scenario specified by scenario or id.



583
584
585
586
587
588
589
590
591
# File 'lib/taskjuggler/Project.rb', line 583

def scenarioIdx(sc)
  if sc.is_a?(Scenario)
    return sc.sequenceNo - 1
  elsif @scenarios[sc].nil?
    return nil
  else
    return @scenarios[sc].sequenceNo - 1
  end
end

#scheduleObject

This function must be called after the Project data structures have been filled with data. It schedules all scenario and stores the result in the data structures again.



630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
# File 'lib/taskjuggler/Project.rb', line 630

def schedule
  initScoreboards

  [ @accounts, @shifts, @resources, @tasks ].each do |p|
    # Set all index counters to their proper values.
    p.index
  end

  if @tasks.empty?
    error('no_tasks', "No tasks defined")
  end

  @scenarios.each do |sc|
    # Skip disabled scenarios
    next unless sc.get('active')

    scIdx = scenarioIdx(sc)

    # All user provided values are set now. The next step is to
    # propagate inherited values. These values must be marked as
    # inherited by setting the mode to 1. As we always call
    # PropertyTreeNode#inherit this is just a safeguard.
    AttributeBase.setMode(1)
    prepareScenario(scIdx)

    # Now change to mode 2 so all values that are modified are marked
    # as computed.
    AttributeBase.setMode(2)
    # Schedule the scenario.
    scheduleScenario(scIdx)

    # Complete the data sets, and check the result.
    finishScenario(scIdx)
  end

  resources.each do |resource|
    resource.checkFailsAndWarnings
  end

  tasks.each do |task|
    task.checkFailsAndWarnings
  end

  @timeSheets.warnOnDelta if @warnTsDeltas
  true
end

#scoreboardSizeObject

Many internal data structures use Scoreboard objects to keep track of scheduling data. These have one entry for every schedulable time slot in the project time frame. This functions returns the number of entries in the scoreboards.



942
943
944
945
# File 'lib/taskjuggler/Project.rb', line 942

def scoreboardSize
  ((@attributes['end'] - @attributes['start']) /
   @attributes['scheduleGranularity']).to_i
end

#shift(id) ⇒ Object

Return the Shift with the ID id or return nil if it does not exist.



594
595
596
# File 'lib/taskjuggler/Project.rb', line 594

def shift(id)
  @shifts[id]
end

#slotsToDays(slots) ⇒ Object

Convert timeSlots to working days.



558
559
560
# File 'lib/taskjuggler/Project.rb', line 558

def slotsToDays(slots)
  slots * @attributes['scheduleGranularity'] / (60 * 60 * dailyWorkingHours)
end

#task(id) ⇒ Object

Return the Task with the ID id or return nil if it does not exist.



604
605
606
# File 'lib/taskjuggler/Project.rb', line 604

def task(id)
  @tasks[id]
end

#to_sObject

Print the attribute values. It’s used for debugging only.



1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
# File 'lib/taskjuggler/Project.rb', line 1050

def to_s
  #raise "STOP!"
  str = ''
  @attributes.each do |attribute, value|
    if value
      str += "#{attribute}: " +
             "#{value.is_a?(PropertyTreeNode) ? value.fullId : value}"
    end
  end
  str
end

#weeklyWorkingDaysObject



542
543
544
545
# File 'lib/taskjuggler/Project.rb', line 542

def weeklyWorkingDays
  @attributes['workinghours'].weeklyWorkingHours /
    @attributes['dailyworkinghours']
end

#workingDays(interval) ⇒ Object

Return the number of working days (ignoring global leaves) during the given interval.



983
984
985
986
987
988
989
990
991
# File 'lib/taskjuggler/Project.rb', line 983

def workingDays(interval)
  startIdx = dateToIdx(interval.start)
  endIdx = dateToIdx(interval.end)
  slots = 0
  startIdx.upto(endIdx) do |idx|
    slots += 1 unless @scoreboardNoLeaves[idx]
  end
  slotsToDays(slots)
end

#yearlyWorkingDaysObject

Return the average number of working days per year.



553
554
555
# File 'lib/taskjuggler/Project.rb', line 553

def yearlyWorkingDays
  @attributes['yearlyworkingdays'].to_f
end