class TaskJuggler::Query

A query can be used to retrieve any property attribute after the scheduling run has been completed. It is possible to make a Query before the scheduling run has been completed, but it only produces good results for static attributes. And for such queries, the TaskJuggler::PropertyTreeNode#get and [] functions are a lot more efficient.

When constructing a Query, a set of variables need to be set that is sufficient enough to identify a unique attribute. Some attribute are computed dynamically and further variables such as a start and end time will be incorporated into the result computation.

The result is returned as String (Query#result), in numerical form (Query#numericalResult) if available as number, and as an entity that can be used for sorting (Query#sortableResult). To get the result, #process needs to be called. In case an error occured, #ok is set to false and #errorMessage contains an error message.

Attributes

end[R]
endIdx[R]
errorMessage[RW]
numerical[W]
ok[RW]
rti[W]
sortable[W]
start[R]
startIdx[R]
string[W]

Public Class Methods

new(parameters = { }) click to toggle source

Create a new Query object. The parameters need to be sufficent to uniquely identify an attribute.

# File lib/taskjuggler/Query.rb, line 51
def initialize(parameters = { })
  @selfContained = false
  @@ps.each do |p|
    instance_variable_set('@' + p, parameters[p] ? parameters[p] : nil)
  end

  # instance_variable_set does not call writer functions. So we need to
  # handle @start, @end, @startIdx and @endIdx separately.
  %w( end endIdx start startIdx ).each do |p|
    send(p + '=', parameters[p]) if parameters[p]
  end
  # The custom data hash can be filled with results to be returned for
  # special attributes that are not directly property attributes or
  # computed attributes.
  @customData = {}

  reset
end

Public Instance Methods

assignList(listItems) click to toggle source

Converts the String items in listItems into a RichTextIntermediate objects and assigns it as result of the query.

# File lib/taskjuggler/Query.rb, line 218
def assignList(listItems)
  list = ''
  listItems.each do |item|
    case @listType
    when nil, :comma
      list += ', ' unless list.empty?
      list += item
    when :bullets
      list += "* #{item}\n"
    when :numbered
      list += "# #{item}\n"
    end
  end
  @sortable = @string = list
  rText = RichText.new(list)
  @rti = rText.generateIntermediateFormat
end
endIdx=(idx) click to toggle source
# File lib/taskjuggler/Query.rb, line 100
def endIdx=(idx)
  if idx.is_a?(Fixnum)
    @endIdx = idx
    @end = @project.idxToDate(idx)
  else
    raise "Unsupported type #{idx.class}"
  end
end
process() click to toggle source

This method tries to resolve the query and return a result. In case it finds an attribute that matches the query, it returns true; false otherwise. The actual result data is stored in the Query object. It can then be retrieved by the caller with the methods #to_s(), #to_num(), #to_sort() and result().

# File lib/taskjuggler/Query.rb, line 121
def process
  reset
  begin
    # Resolve property reference from property ID.
    if @propertyId && (@property.nil? || @propertyId[0] == '!')
      @property = resolvePropertyId(@propertyType, @propertyId)
      unless @property
        @errorMessage = "Unknown property '#{@propertyId}' queried"
        return @ok = false
      end
    end

    unless @property
      # No property was provided. We are looking for a project attribute.
      supportedAttrs = %w( copyright currency end journal name now projectid
                           start version )
      unless supportedAttrs.include?(@attributeId)
        @errorMessage = "Unsupported project attribute '#{@attributeId}'"
        return @ok = false
      end
      if @project.respond_to?(attributeId)
        @project.send(attributeId, self)
      else
        attr = @project[@attributeId]
      end
      if attr.is_a?(TjTime)
        @sortable = @numerical = attr
        @string = attr.to_s(@timeFormat)
      else
        @sortable = @string = attr
      end
      return @ok = true
    end

    # Same for the scope property.
    if !@scopeProperty.nil? && !@scopePropertyId.nil?
      @scopeProperty = resolvePropertyId(@scopePropertyType,
                                         @scopePropertyId)
      unless @scopeProperty
        @errorMessage = "Unknown scope property #{@scopePropertyId} queried"
        return @ok = false
      end
    end
    # Make sure the have a reference to the project.
    @project = @property.project unless @project

    if @scenario && !@scenarioIdx
      @scenarioIdx = @project.scenarioIdx(@scenario)
      unless @scenarioIdx
        raise "Query cannot resolve scenario '#{@scenario}'"
      end
    end

    queryMethodName = 'query_' + @attributeId
    # First we check for non-scenario-specific query functions.
    if (data = @customData[@attributeId])
      @sortable = data[:sortable]
      @numerical = data[:numerical]
      @string = data[:string]
      @rti = data[:rti]
    elsif @property.respond_to?(queryMethodName)
      @property.send(queryMethodName, self)
    elsif @scenarioIdx && @property.data &&
          @property.data[@scenarioIdx].respond_to?(queryMethodName)
      # Then we check for scenario-specific ones via the @data member.
      @property.send(queryMethodName, @scenarioIdx, self)
    else
      # The result is a BaseAttribute
      begin
        # The user may also provide a scenario index for
        # non-scenario-specific values. We need to check if the attribute
        # is really scenario specific or not because
        # PropertyTreeNode::getAttribute can only handle an index for
        # scenario-specific attributs.
        aType = @property.attributeDefinition(@attributeId)
        raise ArgumentError unless aType
        scIdx = aType.scenarioSpecific ? @scenarioIdx : nil
        @attr = @property.getAttribute(@attributeId, scIdx)
        if @attr.nil? && @attr.is_a?(DateAttribute)
          @errorMessage = "Attribute '#{@attributeId}' of property " +
            "'#{@property.fullId}' has undefined value."
          return @ok = false
        end
      rescue ArgumentError
        @errorMessage = "Unknown attribute '#{@attributeId}' queried"
        return @ok = false
      end
    end
  rescue TjException
    @errorMessage = $!.message
    return @ok = false
  end
  @ok = true
end
result() click to toggle source

Return the result in the orginal form. It may be nil.

# File lib/taskjuggler/Query.rb, line 261
def result
  if @attr
    if @attr.value && @attr.is_a?(ReferenceAttribute)
      @attr.value[0]
    else
      @attr.value
    end
  elsif @numerical
    @numerical
  elsif @rti
    @rti
  else
    @string
  end
end
scaleDuration(value) click to toggle source

Convert a duration to the format specified by @loadUnit. value is the duration effort in days. The return value is the converted value with optional unit as a String.

# File lib/taskjuggler/Query.rb, line 280
def scaleDuration(value)
  scaleValue(value, [ 24 * 60, 24, 1, 1.0 / 7, 1.0 / 30.42,
                      1.0 / 91.25, 1.0 / 365 ])
end
scaleLoad(value) click to toggle source

Convert a load or effort value to the format specified by @loadUnit. work is the effort in man days. The return value is the converted value with optional unit as a String.

# File lib/taskjuggler/Query.rb, line 288
def scaleLoad(value)
  scaleValue(value, [ @project.dailyWorkingHours * 60,
                      @project.dailyWorkingHours,
                      1.0,
                      1.0 / @project.weeklyWorkingDays,
                      1.0 / @project.monthlyWorkingDays,
                      1.0 / (@project.yearlyWorkingDays / 4),
                      1.0 / @project.yearlyWorkingDays ])
end
setCustomData(name, data) click to toggle source

Set a custom data entry. name is the name of the pseudo attribute. data must be a Hash that contains the value for :numberical, :string, :sortable or :rti results.

# File lib/taskjuggler/Query.rb, line 112
def setCustomData(name, data)
  @customData[name] = data
end
start=(date) click to toggle source

We probably need the start and end dates as TjTime and Scoreboard index. We store both, but we need to assure they are always in sync.

# File lib/taskjuggler/Query.rb, line 73
def start=(date)
  if date.is_a?(TjTime)
    @start = date
  else
    raise "Unsupported type #{date.class}"
  end
  @startIdx = @project.dateToIdx(@start)
end
startIdx=(idx) click to toggle source
# File lib/taskjuggler/Query.rb, line 82
def startIdx=(idx)
  if idx.is_a?(Fixnum)
    @startIdx = idx
    @start = @project.idxToDate(idx)
  else
    raise "Unsupported type #{idx.class}"
  end
end
to_num() click to toggle source

Return the result of the Query as Fixnum or Float. The result may be nil.

# File lib/taskjuggler/Query.rb, line 243
def to_num
  @attr ? @attr.to_num : @numerical
end
to_rti() click to toggle source

Return the result as RichTextIntermediate object. The result may be nil.

# File lib/taskjuggler/Query.rb, line 254
def to_rti
  return @attr.value if @attr.is_a?(RichTextAttribute)

  @attr ? @attr.to_rti(self) : @rti
end
to_s() click to toggle source

Return the result of the Query as String. The result may be nil.

# File lib/taskjuggler/Query.rb, line 237
def to_s
  @attr ? @attr.to_s(self) : (@rti ? @rti.to_s : (@string || ''))
end
to_sort() click to toggle source

Return the result in the best suited type and format for sorting. The result may be nil.

# File lib/taskjuggler/Query.rb, line 249
def to_sort
  @attr ? @attr.to_sort : @sortable
end