class TaskJuggler::TimeSheetRecord

This class holds the work related bits of a time sheet that are specific to a single Task. This can be an existing Task or a new one identified by it’s ID String. For effort based task, it stores the remaining effort, for other task the expected end date. For all tasks it stores the completed work during the reporting time frame.

Attributes

expectedEnd[RW]
name[RW]
priority[RW]
remaining[RW]
sourceFileInfo[RW]
status[RW]
task[R]
work[R]

Public Class Methods

new(timeSheet, task) click to toggle source
# File lib/taskjuggler/TimeSheets.rb, line 29
def initialize(timeSheet, task)
  # This is a reference to a Task object for existing tasks or an ID as
  # String for new tasks.
  @task = task
  # Add the new TimeSheetRecord to the TimeSheet it belongs to.
  (@timeSheet = timeSheet) << self
  # Work done will be measured in time slots.
  @work = nil
  # Remaining work will be measured in time slots.
  @remaining = nil
  @expectedEnd = nil
  # For new task, we also need to store the name.
  @name = nil
  # Reference to the JournalEntry object that holds the status for this
  # record.
  @status = nil
  @priority = 0
  @sourceFileInfo = nil
end

Public Instance Methods

actualEnd() click to toggle source

The reported expected end of the task.

# File lib/taskjuggler/TimeSheets.rb, line 227
def actualEnd
  @expectedEnd
end
actualRemaining() click to toggle source

The reporting remaining effort in days.

# File lib/taskjuggler/TimeSheets.rb, line 211
def actualRemaining
  project = @timeSheet.resource.project
  project.convertToDailyLoad(@remaining * project['scheduleGranularity'])
end
actualWorkPercent() click to toggle source

The reported work in % (0.0 - 100.0) of the average working time.

# File lib/taskjuggler/TimeSheets.rb, line 194
def actualWorkPercent
  (@work.to_f / @timeSheet.totalGrossWorkingSlots) * 100.0
end
check() click to toggle source

Perform all kinds of consistency checks.

# File lib/taskjuggler/TimeSheets.rb, line 62
def check
  scIdx = @timeSheet.scenarioIdx
  taskId = @task.is_a?(Task) ? @task.fullId : @task
  # All TimeSheetRecords must have a 'work' attribute.
  if @work.nil?
    error('ts_no_work',
          "The time sheet record for task #{taskId} must " +
          "have a 'work' attribute to specify how much was done " +
          "for this task during the reported period.")
  end
  if @task.is_a?(Task)
    # This is already known tasks.
    if @task['effort', scIdx] > 0
      unless @remaining
        error('ts_no_remaining',
              "The time sheet record for task #{taskId} must " +
              "have a 'remaining' attribute to specify how much " +
              "effort is left for this task.")
      end
    else
      unless @expectedEnd
        error('ts_no_expected_end',
              "The time sheet record for task #{taskId} must " +
              "have an 'end' attribute to specify the expected end " +
              "of this task.")
      end
    end
  else
    # This is for new tasks.
    if @remaining.nil? && @expectedEnd.nil?
      error('ts_no_rem_or_end',
            "New task #{taskId} requires either a 'remaining' or a " +
            "'end' attribute.")
    end
  end

  if @work >= @timeSheet.daysToSlots(1) && @status.nil?
    error('ts_no_status_work',
          "You must specify a status for task #{taskId}. It was worked " +
          "on for a day or more.")
  end

  if @status
    if @status.headline.empty?
      error('ts_no_headline',
            "You must provide a headline for the status of " +
            "task #{taskId}")
    end
    if @status.summary &&
      @status.summary.richText.inputText == "A summary text\n"
      error('ts_default_summary',
            "You must change the default summary text of the status " +
            "for task #{taskId}.")
    end
    if @status.alertLevel > 0 && @status.summary.nil? &&
       @status.details.nil?
      error('ts_alert1_more_details',
            "Task #{taskId} has an elevated alert level and must " +
            "have a summary or details section.")
    end
    if @status.alertLevel > 1 && @status.details.nil?
      error('ts_alert2_more_details',
            "Task #{taskId} has a high alert level and must have " +
            "a details section.")
    end
  end
end
planEnd() click to toggle source

The planned end of the task.

# File lib/taskjuggler/TimeSheets.rb, line 232
def planEnd
  @task['end', @timeSheet.scenarioIdx]
end
planRemaining() click to toggle source

The remaining effort according to the plan.

# File lib/taskjuggler/TimeSheets.rb, line 217
def planRemaining
  resource = @timeSheet.resource
  project = resource.project
  scenarioIdx = @timeSheet.scenarioIdx
  startIdx = project.dateToIdx(project['now'])
  endIdx = project.dateToIdx(@task['end', scenarioIdx])
  @task.getEffectiveWork(scenarioIdx, startIdx, endIdx, resource)
end
planWorkPercent() click to toggle source

The planned work in % (0.0 - 100.0) of the average working time.

# File lib/taskjuggler/TimeSheets.rb, line 199
def planWorkPercent
  resource = @timeSheet.resource
  project = resource.project
  scenarioIdx = @timeSheet.scenarioIdx
  startIdx = project.dateToIdx(@timeSheet.interval.start)
  endIdx = project.dateToIdx(@timeSheet.interval.end)
  (@timeSheet.resource.getAllocatedSlots(scenarioIdx, startIdx, endIdx,
                                         @task).to_f /
   @timeSheet.totalGrossWorkingSlots) * 100.0
end
taskId() click to toggle source
# File lib/taskjuggler/TimeSheets.rb, line 189
def taskId
  @task.is_a?(Task) ? @task.fullId : task
end
warnOnDelta(startIdx, endIdx) click to toggle source
# File lib/taskjuggler/TimeSheets.rb, line 130
def warnOnDelta(startIdx, endIdx)
  # Ignore personal entries.
  return unless @task

  resource = @timeSheet.resource
  if @task.is_a?(String)
    # A resource has requested a new Task to be created.
    warning('ts_res_new_task',
            "#{resource.name} is requesting a new task:\n" +
            "  ID: #{@task}\n" +
            "  Name: #{@name}\n" +
            "  Work: #{@timeSheet.slotsToDays(@work)}d  " +
            (@remaining ?
             "Remaining: #{@timeSheet.slotsToDays(@remaining)}d" :
             "End: #{@end.to_s}"))
    return
  end

  scenarioIdx = @timeSheet.scenarioIdx
  project = resource.project
  plannedWork = @task.getEffectiveWork(scenarioIdx, startIdx, endIdx,
                                       resource)
  # Convert the @work slots into a daily load.
  work = project.convertToDailyLoad(@work * project['scheduleGranularity'])

  if work != plannedWork
    warning('ts_res_work_delta',
            "#{resource.name} worked " +
            "#{work < plannedWork ? 'less' : 'more'} " +
            "on #{@task.fullId}\n" +
            "#{work}d instead of #{plannedWork}d")
  end
  if @task['effort', scenarioIdx] > 0
    startIdx = endIdx
    endIdx = project.dateToIdx(@task['end', scenarioIdx])
    remainingWork = @task.getEffectiveWork(scenarioIdx, startIdx, endIdx,
                                           resource)
    # Convert the @remaining slots into a daily load.
    remaining = project.convertToDailyLoad(@remaining *
                                           project['scheduleGranularity'])
    if remaining != remainingWork
      warning('ts_res_remain_delta',
              "#{resource.name} requests " +
              "#{remaining < remainingWork ? 'less' : 'more'} " +
              "remaining effort for task #{@task.fullId}\n" +
              "#{remaining}d instead of #{remainingWork}d")
    end
  else
    if @expectedEnd != @task['end', scenarioIdx]
      warning('ts_res_end_delta',
              "#{resource.name} requests " +
              "#{@expectedEnd < @task['end', scenarioIdx] ?
                'earlier' : 'later'} end (#{@expectedEnd}) for task " +
              "#{@task.fullId}. Planned end is " +
              "#{@task['end', scenarioIdx]}.")
    end
  end
end
work=(value) click to toggle source

Store the number of worked time slots. If the value is an Integer, it can be directly assigned. A Float is interpreted as percentage and must be in the rage of 0.0 to 1.0.

# File lib/taskjuggler/TimeSheets.rb, line 52
def work=(value)
  if value.is_a?(Integer)
    @work = value
  else
    # Must be percent value
    @work = @timeSheet.percentToSlots(value)
  end
end