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 (#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.
Create a new Query object. The parameters need to be sufficent to uniquely identify an attribute.
# File lib/taskjuggler/Query.rb, line 50 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
Converts the String items in listItems into a RichTextIntermediate objects and assigns it as result of the query.
# File lib/taskjuggler/Query.rb, line 212 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
# File lib/taskjuggler/Query.rb, line 99 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().
# File lib/taskjuggler/Query.rb, line 120 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.
# File lib/taskjuggler/Query.rb, line 255 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.
# File lib/taskjuggler/Query.rb, line 274 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.
# File lib/taskjuggler/Query.rb, line 282 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.
# File lib/taskjuggler/Query.rb, line 111 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.
# File lib/taskjuggler/Query.rb, line 72 def start=(date) if date.is_a?(TjTime) @start = date else raise "Unsupported type #{date.class}" end @startIdx = @project.dateToIdx(@start) end
# File lib/taskjuggler/Query.rb, line 81 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.
# File lib/taskjuggler/Query.rb, line 237 def to_num @attr ? @attr.to_num : @numerical end
Return the result as RichTextIntermediate object. The result may be nil.
# File lib/taskjuggler/Query.rb, line 248 def to_rti return @attr.value if @attr.is_a?(RichTextAttribute) @attr ? @attr.to_rti(self) : @rti end
Return the result in the best suited type and format for sorting. The result may be nil.
# File lib/taskjuggler/Query.rb, line 243 def to_sort @attr ? @attr.to_sort : @sortable end