This specialization of ReportBase implements an export of the project data in the TJP syntax format.
Create a new object and set some default values.
# File lib/taskjuggler/reports/TjpExportRE.rb, line 23 23: def initialize(report) 24: super(report) 25: 26: @supportedTaskAttrs = %( booking complete depends flags maxend 27: maxstart minend minstart note priority 28: projectid responsible ) 29: @supportedResourceAttrs = %( flags vacation workinghours ) 30: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 32 32: def generateIntermediateFormat 33: super 34: end
Return the project data in TJP syntax format.
# File lib/taskjuggler/reports/TjpExportRE.rb, line 37 37: def to_tjp 38: # Prepare the resource list. 39: @resourceList = PropertyList.new(@project.resources) 40: @resourceList.setSorting(a('sortResources')) 41: @resourceList = filterResourceList(@resourceList, nil, a('hideResource'), 42: a('rollupResource'), a('openNodes')) 43: @resourceList.sort! 44: 45: # Prepare the task list. 46: @taskList = PropertyList.new(@project.tasks) 47: @taskList.setSorting(a('sortTasks')) 48: @taskList = filterTaskList(@taskList, nil, a('hideTask'), a('rollupTask'), 49: a('openNodes')) 50: @taskList.sort! 51: 52: getBookings 53: 54: @file = '' 55: 56: generateProjectProperty if a('definitions').include?('project') 57: 58: generateFlagDeclaration if a('definitions').include?('flags') 59: generateProjectIDs if a('definitions').include?('projectids') 60: generateResourceList if a('definitions').include?('resources') 61: generateTaskList if a('definitions').include?('tasks') 62: 63: generateTaskAttributes unless a('taskAttributes').empty? 64: generateResourceAttributes unless a('resourceAttributes').empty? 65: 66: @file 67: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 343 343: def generateAttribute(property, attrId, indent, scenarioIdx = nil) 344: val = scenarioIdx ? property[attrId, scenarioIdx] : property.get(attrId) 345: return if val.nil? || (val.is_a?(Array) && val.empty?) || 346: (scenarioIdx && inheritable?(property, attrId, scenarioIdx)) 347: 348: generateAttributeText(property.getAttribute(attrId, scenarioIdx).to_tjp, 349: indent, scenarioIdx) 350: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 352 352: def generateAttributeText(text, indent, scenarioIdx = nil) 353: @file << ' ' * indent 354: tag = '' 355: if !scenarioIdx.nil? && scenarioIdx != 0 356: tag = "#{@project.scenario(scenarioIdx).id}:" 357: @file << tag 358: end 359: @file << "#{indentBlock(text, indent + tag.length + 2)}\n" 360: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 390 390: def generateBooking(task, indent, scenarioIdx) 391: return unless @bookings[scenarioIdx].include?(task) 392: 393: # Convert Hash into an [ Resource, Booking ] Array sorted by Resource 394: # ID. This guarantees a reproducible order. 395: resourceBookings = @bookings[scenarioIdx][task].sort do |a, b| 396: a[0].fullId <=> b[0].fullId 397: end 398: 399: resourceBookings.each do |resourceId, booking| 400: generateAttributeText('booking ' + booking.to_tjp, indent, scenarioIdx) 401: end 402: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 96 96: def generateCustomAttributeDeclarations(tag, propertySet, attributes) 97: # First we search the attribute definitions for any user defined 98: # attributes and count them. 99: customAttributes = 0 100: propertySet.eachAttributeDefinition do |ad| 101: customAttributes += 1 if ad.userDefined 102: end 103: # Return if there are no user defined attributes. 104: return if customAttributes == 0 105: 106: # Generate definitions for each user defined attribute that is in the 107: # taskAttributes list. 108: @file << ' extend ' + tag + "{\n" 109: propertySet.eachAttributeDefinition do |ad| 110: next unless ad.userDefined && attributes.include?(ad.id) 111: 112: @file << " #{ad.objClass.tjpId} #{ad.id} " + 113: "#{quotedString(ad.name)}\n" 114: end 115: @file << " }\n" 116: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 118 118: def generateFlagDeclaration 119: flags = [] 120: 121: properties = @resourceList + @taskList 122: 123: properties.each do |property| 124: a('scenarios').each do |scenarioIdx| 125: property['flags', scenarioIdx].each do |flag| 126: flags << flag unless flags.include?(flag) 127: end 128: end 129: end 130: flags.sort 131: unless flags.empty? 132: @file << "flags #{flags.join(', ')}\n\n" 133: end 134: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 136 136: def generateProjectIDs 137: # Compile a list of all projectIDs from the tasks in the taskList. 138: projectIDs = [] 139: a('scenarios').each do |scenarioIdx| 140: @taskList.each do |task| 141: pid = task['projectid', scenarioIdx] 142: projectIDs << pid unless pid.nil? || projectIDs.include?(pid) 143: end 144: end 145: 146: @file << "projectids #{projectIDs.join(', ')}\n\n" unless projectIDs.empty? 147: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 71 71: def generateProjectProperty 72: @file << "project #{@project['projectid']} \"#{@project['name']}\" " + 73: "\"#{@project['version']}\" #{@project['start']} - " + 74: "#{@project['end']} {\n" 75: generateAttributeText("timezone \"#{@project['timezone']}\"", 2) 76: generateCustomAttributeDeclarations('resource', @project.resources, 77: a('resourceAttributes')) 78: generateCustomAttributeDeclarations('task', @project.tasks, 79: a('taskAttributes')) 80: generateScenarioDefinition(@project.scenario(0), 2) 81: @file << "}\n\n" 82: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 160 160: def generateResource(resource, indent) 161: Log.activity if resource.sequenceNo % 100 == 0 162: 163: @file << ' ' * indent + "resource #{resource.id} " + 164: "#{quotedString(resource.name)}" 165: @file << ' {' unless resource.children.empty? 166: @file << "\n" 167: 168: # Call this function recursively for all children that are included in the 169: # resource list as well. 170: resource.children.each do |subresource| 171: if @resourceList.include?(subresource) 172: generateResource(subresource, indent + 2) 173: end 174: end 175: 176: @file << ' ' * indent + "}\n" unless resource.children.empty? 177: end
Generate a list of resource supplement statements that include the rest of the attributes.
# File lib/taskjuggler/reports/TjpExportRE.rb, line 274 274: def generateResourceAttributes 275: @resourceList.each do |resource| 276: Log.activity if resource.sequenceNo % 100 == 0 277: 278: @file << "supplement resource #{resource.fullId} {\n" 279: @project.resources.eachAttributeDefinition do |attrDef| 280: id = attrDef.id 281: next if (!@supportedResourceAttrs.include?(id) && 282: !attrDef.userDefined) || 283: !a('resourceAttributes').include?(id) 284: 285: if attrDef.scenarioSpecific 286: a('scenarios').each do |scenarioIdx| 287: next if inheritable?(resource, id, scenarioIdx) 288: generateAttribute(resource, id, 2, scenarioIdx) 289: end 290: else 291: generateAttribute(resource, id, 2) 292: end 293: end 294: 295: @file << "}\n" 296: end 297: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 149 149: def generateResourceList 150: # The resource definitions are generated recursively. So we only need to 151: # start it for the top-level resources. 152: @resourceList.each do |resource| 153: if resource.parent.nil? 154: generateResource(resource, 0) 155: end 156: end 157: @file << "\n" 158: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 84 84: def generateScenarioDefinition(scenario, indent) 85: @file << "#{' ' * indent}scenario #{scenario.id} " + 86: "#{quotedString(scenario.name)} {\n" 87: scenario.children.each do |sc| 88: generateScenarioDefinition(sc, indent + 2) 89: end 90: @file << "#{' ' * (indent + 2)}active " + 91: "#{scenario.get('active') ? 'yes' : 'no'}\n" 92: @file << "#{' ' * indent}}\n" 93: end
Generate a task definition. It only contains a very small set of attributes that have to be passed on the the nested tasks at creation time. All other attributes are declared in subsequent supplement statements.
# File lib/taskjuggler/reports/TjpExportRE.rb, line 193 193: def generateTask(task, indent) 194: Log.activity if task.sequenceNo % 100 == 0 195: 196: @file << ' ' * indent + "task #{task.id} " + 197: "#{quotedString(task.name)} {\n" 198: 199: if a('taskAttributes').include?('depends') 200: a('scenarios').each do |scenarioIdx| 201: generateTaskDependency(scenarioIdx, task, 'depends', indent + 2) 202: generateTaskDependency(scenarioIdx, task, 'precedes', indent + 2) 203: end 204: end 205: 206: # Call this function recursively for all children that are included in the 207: # task list as well. 208: task.children.each do |subtask| 209: if @taskList.include?(subtask) 210: generateTask(subtask, indent + 2) 211: end 212: end 213: 214: # Determine whether this task has subtasks that are included in the 215: # report or whether this is a leaf task for the report. 216: isLeafTask = true 217: task.children.each do |subtask| 218: if @taskList.include?(subtask) 219: isLeafTask = false 220: break 221: end 222: end 223: 224: # For leaf tasks we put some attributes right here. 225: if isLeafTask 226: a('scenarios').each do |scenarioIdx| 227: generateAttribute(task, 'start', indent + 2, scenarioIdx) 228: if task['milestone', scenarioIdx] 229: if task['scheduled', scenarioIdx] 230: generateAttributeText('milestone', indent + 2, scenarioIdx) 231: end 232: else 233: generateAttribute(task, 'end', indent + 2, scenarioIdx) 234: generateAttributeText('scheduling ' + 235: (task['forward', scenarioIdx] ? 236: 'asap' : 'alap'), 237: indent + 2, scenarioIdx) 238: end 239: if task['scheduled', scenarioIdx] && 240: !inheritable?(task, 'scheduled', scenarioIdx) 241: generateAttributeText('scheduled', indent + 2, scenarioIdx) 242: end 243: end 244: end 245: 246: @file << ' ' * indent + "}\n" 247: end
Generate a list of task supplement statements that include the rest of the attributes.
# File lib/taskjuggler/reports/TjpExportRE.rb, line 301 301: def generateTaskAttributes 302: @taskList.each do |task| 303: Log.activity if task.sequenceNo % 100 == 0 304: 305: @file << "supplement task #{task.fullId} {\n" 306: # Declare adopted tasks. 307: adoptees = "" 308: task.adoptees.each do |adoptee| 309: next unless @taskList.include?(adoptee) 310: 311: adoptees += ', ' unless adoptees.empty? 312: adoptees += adoptee.fullId 313: end 314: generateAttributeText("adopt #{adoptees}", 2) unless adoptees.empty? 315: 316: @project.tasks.eachAttributeDefinition do |attrDef| 317: id = attrDef.id 318: 319: next if (!@supportedTaskAttrs.include?(id) && !attrDef.userDefined) || 320: !a('taskAttributes').include?(id) 321: 322: if attrDef.scenarioSpecific 323: a('scenarios').each do |scenarioIdx| 324: # Some attributes need special treatment. 325: case id 326: when 'depends' 327: next # already taken care of 328: when 'booking' 329: generateBooking(task, 2, scenarioIdx) 330: else 331: generateAttribute(task, id, 2, scenarioIdx) 332: end 333: end 334: else 335: generateAttribute(task, id, 2) 336: end 337: end 338: 339: @file << "}\n" 340: end 341: end
Generate ‘depends’ or ‘precedes’ attributes for a task.
# File lib/taskjuggler/reports/TjpExportRE.rb, line 250 250: def generateTaskDependency(scenarioIdx, task, tag, indent) 251: return unless a('taskAttributes').include?('depends') 252: 253: taskDeps = task[tag, scenarioIdx] 254: unless taskDeps.empty? 255: str = "#{tag} " 256: first = true 257: taskDeps.each do |dep| 258: next if inheritable?(task, tag, scenarioIdx, dep) || 259: (task.parent && task.parent[tag, scenarioIdx].include?(dep)) 260: 261: if first 262: first = false 263: else 264: str << ', ' 265: end 266: str << dep.task.fullId 267: end 268: generateAttributeText(str, indent, scenarioIdx) unless first 269: end 270: end
# File lib/taskjuggler/reports/TjpExportRE.rb, line 179 179: def generateTaskList 180: # The task definitions are generated recursively. So we only need to start 181: # it for the top-level tasks. 182: @taskList.each do |task| 183: if task.parent.nil? 184: generateTask(task, 0) 185: end 186: end 187: end
Get the booking data for all resources that should be included in the report.
# File lib/taskjuggler/reports/TjpExportRE.rb, line 364 364: def getBookings 365: @bookings = {} 366: if a('taskAttributes').include?('booking') 367: a('scenarios').each do |scenarioIdx| 368: @bookings[scenarioIdx] = {} 369: @resourceList.each do |resource| 370: # Get the bookings for this resource hashed by task. 371: bookings = resource.getBookings(scenarioIdx, 372: Interval.new(a('start'), a('end'))) 373: next if bookings.nil? 374: 375: # Now convert/add them to a tripple-stage hash by scenarioIdx, task 376: # and then resource. 377: bookings.each do |task, booking| 378: next unless @taskList.include?(task) 379: 380: if !@bookings[scenarioIdx].include?(task) 381: @bookings[scenarioIdx][task] = {} 382: end 383: @bookings[scenarioIdx][task][resource] = booking 384: end 385: end 386: end 387: end 388: end
This utility function is used to indent multi-line attributes. All attributes should be filtered through this function. Attributes that contain line breaks will be indented properly. In addition to the indentation specified by indent all but the first line will be indented after the first word of the first line. The text may not end with a line break.
# File lib/taskjuggler/reports/TjpExportRE.rb, line 410 410: def indentBlock(text, indent) 411: out = '' 412: firstSpace = 0 413: text.length.times do |i| 414: if firstSpace == 0 && text[i] == \ \# There must be a space after ? 415: firstSpace = i 416: end 417: out << text[i] 418: if text[i] == \n\ 419: out += ' ' * (indent + firstSpace - 1) 420: end 421: end 422: out 423: end
Return true if the attribute value for attrId can be inherited from the parent scenario.
# File lib/taskjuggler/reports/TjpExportRE.rb, line 436 436: def inheritable?(property, attrId, scenarioIdx, listItem = nil) 437: parentScenario = @project.scenario(scenarioIdx).parent 438: return false unless parentScenario 439: 440: parentScenarioIdx = @project.scenarioIdx(parentScenario) 441: parentAttr = property[attrId, parentScenarioIdx] 442: if parentAttr.is_a?(Array) && listItem 443: return parentAttr.include?(listItem) 444: else 445: return property[attrId, scenarioIdx] == parentAttr 446: end 447: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.