class TaskJuggler::TjpExportRE
This specialization of ReportBase
implements an export of the project data in the TJP syntax format.
Public Class Methods
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 24 def initialize(report) super(report) @supportedTaskAttrs = %w( booking complete depends flags maxend maxstart minend minstart note priority projectid responsible ) @supportedResourceAttrs = %w( booking flags shifts vacation workinghours ) end
Create a new object and set some default values.
TaskJuggler::ReportBase::new
Public Instance Methods
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 33 def generateIntermediateFormat super end
TaskJuggler::ReportBase#generateIntermediateFormat
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 38 def to_tjp # Prepare the resource list. @resourceList = PropertyList.new(@project.resources) @resourceList.setSorting(a('sortResources')) @resourceList = filterResourceList(@resourceList, nil, a('hideResource'), a('rollupResource'), a('openNodes')) @resourceList.sort! # Prepare the task list. @taskList = PropertyList.new(@project.tasks) @taskList.setSorting(a('sortTasks')) @taskList = filterTaskList(@taskList, nil, a('hideTask'), a('rollupTask'), a('openNodes')) @taskList.sort! getBookings @file = +'' generateProjectProperty if a('definitions').include?('project') generateFlagDeclaration if a('definitions').include?('flags') generateProjectIDs if a('definitions').include?('projectids') generateShiftList if a('definitions').include?('shifts') generateResourceList if a('definitions').include?('resources') generateTaskList if a('definitions').include?('tasks') generateTaskAttributes unless a('taskAttributes').empty? generateResourceAttributes unless a('resourceAttributes').empty? @file end
Return the project data in TJP syntax format.
Private Instance Methods
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 389 def generateAttribute(property, attrId, indent, scenarioIdx = nil) val = scenarioIdx ? property[attrId, scenarioIdx] : property.get(attrId) return if val.nil? || (val.is_a?(Array) && val.empty?) || (scenarioIdx && inheritable?(property, attrId, scenarioIdx)) generateAttributeText(property.getAttribute(attrId, scenarioIdx).to_tjp, indent, scenarioIdx) end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 398 def generateAttributeText(text, indent, scenarioIdx = nil) @file << ' ' * indent tag = +'' if !scenarioIdx.nil? && scenarioIdx != 0 tag = "#{@project.scenario(scenarioIdx).id}:" @file << tag end @file << "#{indentBlock(text, indent + tag.length + 2)}\n" end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 452 def generateBookingsByResource(resource, indent, scenarioIdx) # Get the bookings for this resource hashed by task. bookings = resource.getBookings(scenarioIdx, TimeInterval.new(a('start'), a('end')), false) bookings.each do |booking| next unless @taskList.include?(booking.task) generateAttributeText('booking ' + booking.to_tjp(true), indent, scenarioIdx) end end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 437 def generateBookingsByTask(task, indent, scenarioIdx) return unless @bookings[scenarioIdx].include?(task) # Convert Hash into an [ Resource, Booking ] Array sorted by Resource # ID. This guarantees a reproducible order. resourceBookings = @bookings[scenarioIdx][task].sort do |a, b| a[0].fullId <=> b[0].fullId end resourceBookings.each do |resourceId, booking| generateAttributeText('booking ' + booking.to_tjp(false), indent, scenarioIdx) end end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 108 def generateCustomAttributeDeclarations(tag, propertySet, attributes) # First we search the attribute definitions for any user defined # attributes and count them. customAttributes = 0 propertySet.eachAttributeDefinition do |ad| customAttributes += 1 if ad.userDefined end # Return if there are no user defined attributes. return if customAttributes == 0 # Generate definitions for each user defined attribute that is in the # taskAttributes list. @file << ' extend ' + tag + "{\n" propertySet.eachAttributeDefinition do |ad| next unless ad.userDefined && attributes.include?(ad.id) @file << " #{ad.objClass.tjpId} #{ad.id} " + "#{quotedString(ad.name)}" if ad.scenarioSpecific || ad.inheritedFromParent @file << " { " @file << "scenariospecific " if ad.scenarioSpecific @file << "inherit " if ad.inheritedFromParent @file << "}" end @file << "\n" end @file << " }\n" end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 137 def generateFlagDeclaration flags = [] properties = @resourceList + @taskList properties.each do |property| a('scenarios').each do |scenarioIdx| property['flags', scenarioIdx].each do |flag| flags << flag unless flags.include?(flag) end end end flags.sort unless flags.empty? @file << "flags #{flags.join(', ')}\n\n" end end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 155 def generateProjectIDs # Compile a list of all projectIDs from the tasks in the taskList. projectIDs = [] a('scenarios').each do |scenarioIdx| @taskList.each do |task| pid = task['projectid', scenarioIdx] projectIDs << pid unless pid.nil? || projectIDs.include?(pid) end end @file << "projectids #{projectIDs.join(', ')}\n\n" unless projectIDs.empty? end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 75 def generateProjectProperty @file << "project #{@project['projectid']} \"#{@project['name']}\" " + "\"#{@project['version']}\" #{@project['start']} - " + "#{@project['end']} {\n" # Add timingresolution attribute if it's not the default value. if @project['scheduleGranularity'] != 3600 generateAttributeText("timingresolution " + "#{@project['scheduleGranularity'] / 60}min", 2) end generateAttributeText("timezone \"#{@project['timezone']}\"", 2) if @project['alertLevels'].modified? generateAttributeText(@project['alertLevels'].to_tjp, 2) end generateCustomAttributeDeclarations('resource', @project.resources, a('resourceAttributes')) generateCustomAttributeDeclarations('task', @project.tasks, a('taskAttributes')) generateScenarioDefinition(@project.scenario(0), 2) @file << "}\n\n" end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 201 def generateResource(resource, indent) Log.activity if resource.sequenceNo % 100 == 0 @file << ' ' * indent + "resource #{resource.id} " + "#{quotedString(resource.name)}" @file << ' {' unless resource.children.empty? @file << "\n" # Call this function recursively for all children that are included in the # resource list as well. resource.children.each do |subresource| if @resourceList.include?(subresource) generateResource(subresource, indent + 2) end end @file << ' ' * indent + "}\n" unless resource.children.empty? end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 311 def generateResourceAttributes @resourceList.each do |resource| Log.activity if resource.sequenceNo % 100 == 0 @file << "supplement resource #{resource.fullId} {\n" @project.resources.eachAttributeDefinition do |attrDef| id = attrDef.id next if (!@supportedResourceAttrs.include?(id) && !attrDef.userDefined) || !a('resourceAttributes').include?(id) if attrDef.scenarioSpecific a('scenarios').each do |scenarioIdx| next if inheritable?(resource, id, scenarioIdx) generateAttribute(resource, id, 2, scenarioIdx) end else generateAttribute(resource, id, 2) end end # Since 'booking' is a task attribute, we need a special handling if # we want to list them in the resource context. if a('resourceAttributes').include?('booking') && a('resourceAttributes')[0] != '*' a('scenarios').each do |scenarioIdx| generateBookingsByResource(resource, 2, scenarioIdx) end end @file << "}\n" end end
Generate a list of resource supplement statements that include the rest of the attributes.
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 190 def generateResourceList # The resource definitions are generated recursively. So we only need to # start it for the top-level resources. @resourceList.each do |resource| if resource.parent.nil? generateResource(resource, 0) end end @file << "\n" end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 96 def generateScenarioDefinition(scenario, indent) @file << "#{' ' * indent}scenario #{scenario.id} " + "#{quotedString(scenario.name)} {\n" scenario.children.each do |sc| generateScenarioDefinition(sc, indent + 2) end @file << "#{' ' * (indent + 2)}active " + "#{scenario.get('active') ? 'yes' : 'no'}\n" @file << "#{' ' * indent}}\n" end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 174 def generateShift(shift, indent) @file << ' ' * indent + "shift #{shift.id} " + "#{quotedString(shift.name)} {\n" a('scenarios').each do |scenarioIdx| generateAttribute(shift, 'workinghours', indent + 2, scenarioIdx) end # Call this method recursively for all children. shift.children.each do |subshift| generateShift(subshift, indent + 2) end @file << ' ' * indent + "}\n" end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 168 def generateShiftList @project.shifts.each do |shift| generateShift(shift, 0) unless shift.parent end end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 234 def generateTask(task, indent) Log.activity if task.sequenceNo % 100 == 0 @file << ' ' * indent + "task #{task.subId} " + "#{quotedString(task.name)} {\n" if a('taskAttributes').include?('depends') a('scenarios').each do |scenarioIdx| generateTaskDependency(scenarioIdx, task, 'depends', indent + 2) generateTaskDependency(scenarioIdx, task, 'precedes', indent + 2) end end # Call this function recursively for all children that are included in the # task list as well. task.children.each do |subtask| if @taskList.include?(subtask) generateTask(subtask, indent + 2) end end # Determine whether this task has subtasks that are included in the # report or whether this is a leaf task for the report. isLeafTask = true task.children.each do |subtask| if @taskList.include?(subtask) isLeafTask = false break end end # For leaf tasks we put some attributes right here. if isLeafTask a('scenarios').each do |scenarioIdx| generateAttribute(task, 'start', indent + 2, scenarioIdx) if !task['milestone', scenarioIdx] generateAttribute(task, 'end', indent + 2, scenarioIdx) generateAttributeText('scheduling ' + (task['forward', scenarioIdx] ? 'asap' : 'alap'), indent + 2, scenarioIdx) end if task['scheduled', scenarioIdx] && !inheritable?(task, 'scheduled', scenarioIdx) generateAttributeText('scheduled', indent + 2, scenarioIdx) end end end @file << ' ' * indent + "}\n" 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.
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 347 def generateTaskAttributes @taskList.each do |task| Log.activity if task.sequenceNo % 100 == 0 @file << "supplement task #{task.fullId} {\n" # Declare adopted tasks. adoptees = "" task.adoptees.each do |adoptee| next unless @taskList.include?(adoptee) adoptees += ', ' unless adoptees.empty? adoptees += adoptee.fullId end generateAttributeText("adopt #{adoptees}", 2) unless adoptees.empty? @project.tasks.eachAttributeDefinition do |attrDef| id = attrDef.id next if (!@supportedTaskAttrs.include?(id) && !attrDef.userDefined) || !a('taskAttributes').include?(id) if attrDef.scenarioSpecific a('scenarios').each do |scenarioIdx| # Some attributes need special treatment. case id when 'depends' next # already taken care of when 'booking' generateBookingsByTask(task, 2, scenarioIdx) else generateAttribute(task, id, 2, scenarioIdx) end end else generateAttribute(task, id, 2) end end @file << "}\n" end end
Generate a list of task supplement statements that include the rest of the attributes.
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 287 def generateTaskDependency(scenarioIdx, task, tag, indent) return unless a('taskAttributes').include?('depends') taskDeps = task[tag, scenarioIdx] unless taskDeps.empty? str = "#{tag} " first = true taskDeps.each do |dep| next if inheritable?(task, tag, scenarioIdx, dep) || (task.parent && task.parent[tag, scenarioIdx].include?(dep)) if first first = false else str << ', ' end str << dep.task.fullId end generateAttributeText(str, indent, scenarioIdx) unless first end end
Generate ‘depends’ or ‘precedes’ attributes for a task.
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 220 def generateTaskList # The task definitions are generated recursively. So we only need to start # it for the top-level tasks. @taskList.each do |task| if task.parent.nil? generateTask(task, 0) end end end
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 410 def getBookings @bookings = {} if a('taskAttributes').include?('booking') || a('resourceAttributes').include?('booking') a('scenarios').each do |scenarioIdx| @bookings[scenarioIdx] = {} @resourceList.each do |resource| # Get the bookings for this resource hashed by task. bookings = resource.getBookings( scenarioIdx, TimeInterval.new(a('start'), a('end'))) next if bookings.nil? # Now convert/add them to a tripple-stage hash by scenarioIdx, task # and then resource. bookings.each do |task, booking| next unless @taskList.include?(task) if !@bookings[scenarioIdx].include?(task) @bookings[scenarioIdx][task] = {} end @bookings[scenarioIdx][task][resource] = booking end end end end end
Get the booking data for all resources that should be included in the report.
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 471 def indentBlock(text, indent) out = +'' firstSpace = 0 text.length.times do |i| if firstSpace == 0 && text[i] == ?\ # There must be a space after ? firstSpace = i end out << text[i] if text[i] == ?\n out += ' ' * (indent + firstSpace - 1) end end out 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.
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 497 def inheritable?(property, attrId, scenarioIdx, listItem = nil) parentScenario = @project.scenario(scenarioIdx).parent return false unless parentScenario parentScenarioIdx = @project.scenarioIdx(parentScenario) parentAttr = property[attrId, parentScenarioIdx] if parentAttr.is_a?(Array) && listItem return parentAttr.include?(listItem) else return property[attrId, scenarioIdx] == parentAttr end end
Return true if the attribute value for attrId can be inherited from the parent scenario.
Source
# File lib/taskjuggler/reports/TjpExportRE.rb, line 486 def quotedString(str) if str.include?("\n") "-8<-\n#{str}\n->8-" else escaped = str.gsub("\"", '\"') "\"#{escaped}\"" end end