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.



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
495
# File 'lib/taskjuggler/Project.rb', line 66

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.



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

def accounts
  @accounts
end

#inputFilesObject (readonly)

Returns the value of attribute inputFiles.



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

def inputFiles
  @inputFiles
end

#outputDirObject

Returns the value of attribute outputDir.



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

def outputDir
  @outputDir
end

#reportContextsObject

Returns the value of attribute reportContexts.



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

def reportContexts
  @reportContexts
end

#reportsObject (readonly)

Returns the value of attribute reports.



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

def reports
  @reports
end

#resourcesObject (readonly)

Returns the value of attribute resources.



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

def resources
  @resources
end

#scenariosObject (readonly)

Returns the value of attribute scenarios.



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

def scenarios
  @scenarios
end

#shiftsObject (readonly)

Returns the value of attribute shifts.



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

def shifts
  @shifts
end

#tasksObject (readonly)

Returns the value of attribute tasks.



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

def tasks
  @tasks
end

#timeSheetsObject (readonly)

Returns the value of attribute timeSheets.



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

def timeSheets
  @timeSheets
end

#warnTsDeltasObject

Returns the value of attribute warnTsDeltas.



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

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.



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

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.



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

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.



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

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.



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

def (id)
  @accounts[id]
end

#addAccount(account) ⇒ Object

:nodoc:



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

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

#addReport(report) ⇒ Object

:nodoc:



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

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

#addResource(resource) ⇒ Object

:nodoc:



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

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.



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

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

#addShift(shift) ⇒ Object

:nodoc:



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

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

#addTask(task) ⇒ Object

:nodoc:



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

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)


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

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.



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

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.



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
725
# File 'lib/taskjuggler/Project.rb', line 698

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



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

def checkTimeSheets
  @timeSheets.check
end

#collectTimeOffIntervals(iv, minDuration) ⇒ Object



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

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.



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

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.



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

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.



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

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.



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

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.



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

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



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
815
# File 'lib/taskjuggler/Project.rb', line 777

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.



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
775
# File 'lib/taskjuggler/Project.rb', line 729

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.



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

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.



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

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.



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

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.



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

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



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

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

#listReports(reportId, regExpMode) ⇒ Object



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

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.



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

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

#removeAccount(account) ⇒ Object

:nodoc:



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

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

#report(id) ⇒ Object

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



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

def report(id)
  @reports[id]
end

#reportByName(name) ⇒ Object

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



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

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.



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

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.



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

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.



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

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.



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

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.



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
676
# File 'lib/taskjuggler/Project.rb', line 631

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.



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

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.



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

def shift(id)
  @shifts[id]
end

#slotsToDays(slots) ⇒ Object

Convert timeSlots to working days.



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

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.



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

def task(id)
  @tasks[id]
end

#to_sObject

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



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

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



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

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

#workingDays(interval) ⇒ Object

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



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

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.



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

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