Class: TaskJuggler::Query

Inherits:
Object show all
Defined in:
lib/taskjuggler/Query.rb

Overview

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.

Constant Summary collapse

@@ps =
%w( project propertyType propertyId property
scopePropertyType scopePropertyId scopeProperty
attributeId scenario scenarioIdx
loadUnit numberFormat currencyFormat timeFormat
listItem listType hideJournalEntry
journalMode journalAttributes sortJournalEntries
costAccount revenueAccount selfContained )

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parameters = { }) ⇒ Query

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



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/taskjuggler/Query.rb', line 53

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

Instance Attribute Details

#endObject

Returns the value of attribute end.



48
49
50
# File 'lib/taskjuggler/Query.rb', line 48

def end
  @end
end

#endIdxObject

Returns the value of attribute endIdx.



48
49
50
# File 'lib/taskjuggler/Query.rb', line 48

def endIdx
  @endIdx
end

#errorMessageObject

Returns the value of attribute errorMessage.



47
48
49
# File 'lib/taskjuggler/Query.rb', line 47

def errorMessage
  @errorMessage
end

#numerical=(value) ⇒ Object (writeonly)

Sets the attribute numerical

Parameters:

  • value

    the value to set the attribute numerical to.



49
50
51
# File 'lib/taskjuggler/Query.rb', line 49

def numerical=(value)
  @numerical = value
end

#okObject

Returns the value of attribute ok.



47
48
49
# File 'lib/taskjuggler/Query.rb', line 47

def ok
  @ok
end

#rti=(value) ⇒ Object (writeonly)

Sets the attribute rti

Parameters:

  • value

    the value to set the attribute rti to.



49
50
51
# File 'lib/taskjuggler/Query.rb', line 49

def rti=(value)
  @rti = value
end

#sortable=(value) ⇒ Object (writeonly)

Sets the attribute sortable

Parameters:

  • value

    the value to set the attribute sortable to.



49
50
51
# File 'lib/taskjuggler/Query.rb', line 49

def sortable=(value)
  @sortable = value
end

#startObject

Returns the value of attribute start.



48
49
50
# File 'lib/taskjuggler/Query.rb', line 48

def start
  @start
end

#startIdxObject

Returns the value of attribute startIdx.



48
49
50
# File 'lib/taskjuggler/Query.rb', line 48

def startIdx
  @startIdx
end

#string=(value) ⇒ Object (writeonly)

Sets the attribute string

Parameters:

  • value

    the value to set the attribute string to.



49
50
51
# File 'lib/taskjuggler/Query.rb', line 49

def string=(value)
  @string = value
end

Instance Method Details

#assignList(listItems) ⇒ Object

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



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/taskjuggler/Query.rb', line 220

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

#processObject

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



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/taskjuggler/Query.rb', line 123

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

#resultObject

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



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/taskjuggler/Query.rb', line 263

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) ⇒ Object

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.



282
283
284
285
# File 'lib/taskjuggler/Query.rb', line 282

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) ⇒ Object

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.



290
291
292
293
294
295
296
297
298
# File 'lib/taskjuggler/Query.rb', line 290

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) ⇒ Object

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.



114
115
116
# File 'lib/taskjuggler/Query.rb', line 114

def setCustomData(name, data)
  @customData[name] = data
end

#to_numObject

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



245
246
247
# File 'lib/taskjuggler/Query.rb', line 245

def to_num
  @attr ? @attr.to_num : @numerical
end

#to_rtiObject

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



256
257
258
259
260
# File 'lib/taskjuggler/Query.rb', line 256

def to_rti
  return @attr.value if @attr.is_a?(RichTextAttribute)

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

#to_sObject

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



239
240
241
# File 'lib/taskjuggler/Query.rb', line 239

def to_s
  @attr ? @attr.to_s(self) : (@rti ? @rti.to_s : (@string || ''))
end

#to_sortObject

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



251
252
253
# File 'lib/taskjuggler/Query.rb', line 251

def to_sort
  @attr ? @attr.to_sort : @sortable
end