class TaskJuggler::TraceReport

The trace report is used to periodically snapshot a specific list of property attributes and add them to a CSV file.

Public Class Methods

new(report) click to toggle source

Create a new object and set some default values.

Calls superclass method TaskJuggler::ReportBase.new
# File lib/taskjuggler/reports/TraceReport.rb, line 28
def initialize(report)
  super
  @table = nil
end

Public Instance Methods

generateIntermediateFormat() click to toggle source

Generate the table in the intermediate format.

# File lib/taskjuggler/reports/TraceReport.rb, line 34
def generateIntermediateFormat
  super

  queryAttrs = { 'project' => @project,
                 'scopeProperty' => nil,
                 'loadUnit' => a('loadUnit'),
                 'numberFormat' => a('numberFormat'),
                 # We use a hardcoded %Y-%m-%d format for tracereports.
                 'timeFormat' => "%Y-%m-%d",
                 'currencyFormat' => a('currencyFormat'),
                 'start' => a('start'), 'end' => a('end'),
                 'hideJournalEntry' => a('hideJournalEntry'),
                 'journalMode' => a('journalMode'),
                 'journalAttributes' => a('journalAttributes'),
                 'sortJournalEntries' => a('sortJournalEntries'),
                 'costAccount' => a('costaccount'),
                 'revenueAccount' => a('revenueaccount') }
  query = Query.new(queryAttrs)

  # Prepare the account list.
  accountList = PropertyList.new(@project.accounts)
  accountList.setSorting(a('sortAccounts'))
  accountList.query = query
  accountList = filterAccountList(accountList, a('hideAccount'),
                                  a('rollupAccount'), a('openNodes'))
  accountList.sort!

  # Prepare the resource list.
  resourceList = PropertyList.new(@project.resources)
  resourceList.setSorting(a('sortResources'))
  resourceList.query = query
  resourceList = filterTaskList(resourceList, nil, a('hideResource'),
                                 a('rollupResource'), a('openNodes'))
  resourceList.sort!

  # Prepare the task list.
  taskList = PropertyList.new(@project.tasks)
  taskList.includeAdopted
  taskList.setSorting(a('sortTasks'))
  taskList.query = query
  taskList = filterTaskList(taskList, nil, a('hideTask'), a('rollupTask'),
                            a('openNodes'))
  taskList.sort!

  @fileName = ((@report.name[0] == '/' ? '' : @project.outputDir) +
              @report.name + '.csv').untaint

  # Generate the table header.
  headers = [ 'Date' ] +
            generatePropertyListHeader(accountList, query) +
            generatePropertyListHeader(resourceList, query) +
            generatePropertyListHeader(taskList, query)

  discontinuedColumns = 0
  if File.exist?(@fileName)
    begin
      @table = CSVFile.new(nil, nil).read(@fileName)
    rescue
      error('tr_cannot_read_csv',
            "Cannot read CSV file #{@fileName}: #{$!}")
    end

    if @table[0] != headers
      # Some columns have changed. We move all discontinued columns to the
      # last columns and rearrange the others according to the new
      # headers. New columns will be filled with nil in previous rows.
      sorter = TableColumnSorter.new(@table)
      @table = sorter.sort(headers)
      discontinuedColumns = sorter.discontinuedColumns
    end
  else
    @table = [ headers ]
  end

  # Convert empty strings into nil objects and dates in %Y-%m-%d format
  # into TjTime objects.
  @table.each do |line|
    line.length.times do |i|
      if line[i] == ''
        line[i] = nil
      elsif line[i].is_a?(String) && /\d{4}-\d{2}-\d{2}/ =~ line[i]
        line[i] = TjTime.new(line[i])
      end
    end
  end

  query = @project.reportContexts.last.query.dup
  dateTag = @project['now'].midnight

  idx = @table.index { |line| line[0] == dateTag }
  discColumnValues = discontinuedColumns > 0 ?
                     Array.new(discontinuedColumns, nil) : []
  if idx
    # We already have an entry for the current date. All old values of
    # this line will be overwritten with the current values. The old
    # values in the discontinued columns will be kept.
    if discontinuedColumns > 0
      discColumnValues = @table[idx][headers.length..-1]
    end
    @table[idx] = []
  else
    # Append a new line of values to the table.
    @table << []
    idx = -1
  end
  # The first entry is always the current date.
  @table[idx] << dateTag

  # Now add the new values to the line
  generatePropertyListValues(idx, accountList, query)
  generatePropertyListValues(idx, resourceList, query)
  generatePropertyListValues(idx, taskList, query)

  # Fill the discontinued columns with old values or nil.
  @table[idx] += discColumnValues

  # Sort the table by ascending first column dates. We need to ensure that
  # the header remains the first line in the table.
  @table.sort! { |l1, l2| l1[0].is_a?(String) ? -1 :
                          (l2[0].is_a?(String) ? 1 : l1[0] <=> l2[0]) }
end
to_csv() click to toggle source
# File lib/taskjuggler/reports/TraceReport.rb, line 173
def to_csv
  # Convert all TjTime values into String with format %Y-%m-%d and nil
  # objects into empty Strings.
  @table.each do |line|
    line.length.times do |i|
      if line[i].nil?
        line[i] = ''
      elsif line[i].is_a?(TjTime)
        line[i] = line[i].to_s('%Y-%m-%d')
      end
    end
  end

  @table
end
to_html() click to toggle source
# File lib/taskjuggler/reports/TraceReport.rb, line 156
def to_html
  html = []
  html << rt_to_html('header')

  begin
    plotter = ChartPlotter.new(a('width'), a('height'), @table)
    plotter.generate
    html << plotter.to_svg
  rescue ChartPlotterError => exception
    warning('chartPlotterError', exception.message, @report.sourceFileInfo)
  end

  html << rt_to_html('footer')

  html
end