Class TaskJuggler::Query
In: lib/taskjuggler/Query.rb
Parent: Object

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 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, Query#process needs to be called. In case an error occured, Query#ok is set to false and Query#errorMessage contains an error message.

Methods

assignList   end   endIdx=   new   process   result   scaleDuration   scaleLoad   setCustomData   start=   startIdx=   to_num   to_rti   to_s   to_sort  

Attributes

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

Public Class methods

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

[Source]

# File lib/taskjuggler/Query.rb, line 52
    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

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

[Source]

# File lib/taskjuggler/Query.rb, line 214
    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

[Source]

# File lib/taskjuggler/Query.rb, line 92
    def end=(date)
      if date.is_a?(TjTime)
        @end = date
      else
        raise "Unsupported type #{date.class}"
      end
      @endIdx = @project.dateToIdx(@end)
    end

[Source]

# File lib/taskjuggler/Query.rb, line 101
    def endIdx=(idx)
      if idx.is_a?(Fixnum)
        @endIdx = idx
        @end = @project.idxToDate(idx)
      else
        raise "Unsupported type #{idx.class}"
      end
    end

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().

[Source]

# File lib/taskjuggler/Query.rb, line 122
    def process
      reset
      begin
        # Resolve property reference from property ID.
        if @property.nil? && !@propertyId.nil?
          @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)
          rescue ArgumentError
            @errorMessage = "Unknown attribute #{@attributeId} queried"
            return @ok = false
          end
        end
      rescue TjException
        @errorMessage = $!.message
        return @ok = false
      end
      @ok = true
    end

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

[Source]

# File lib/taskjuggler/Query.rb, line 257
    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

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.

[Source]

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

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.

[Source]

# File lib/taskjuggler/Query.rb, line 284
    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

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.

[Source]

# File lib/taskjuggler/Query.rb, line 113
    def setCustomData(name, data)
      @customData[name] = data
    end

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.

[Source]

# File lib/taskjuggler/Query.rb, line 74
    def start=(date)
      if date.is_a?(TjTime)
        @start = date
      else
        raise "Unsupported type #{date.class}"
      end
      @startIdx = @project.dateToIdx(@start)
    end

[Source]

# File lib/taskjuggler/Query.rb, line 83
    def startIdx=(idx)
      if idx.is_a?(Fixnum)
        @startIdx = idx
        @start = @project.idxToDate(idx)
      else
        raise "Unsupported type #{idx.class}"
      end
    end

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

[Source]

# File lib/taskjuggler/Query.rb, line 239
    def to_num
      @attr ? @attr.to_num : @numerical
    end

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

[Source]

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

      @attr ? @attr.to_rti(self) : @rti
    end

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

[Source]

# File lib/taskjuggler/Query.rb, line 233
    def to_s
      @attr ? @attr.to_s(self) : (@rti ? @rti.to_s : (@string || ''))
    end

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

[Source]

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

[Validate]