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 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.
Attributes
Public Class Methods
Create a new Query
object. The parameters need to be sufficent to uniquely identify an attribute.
# 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.
# File lib/taskjuggler/Query.rb, line 219 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 92 def end=(date) if date.is_a?(TjTime) @end = date else raise "Unsupported type #{date.class}" end @endIdx = @project.dateToIdx(@end) end
# File lib/taskjuggler/Query.rb, line 101 def endIdx=(idx) if idx.is_a?(Integer) @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 122 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
Return the result in the orginal form. It may be nil.
# File lib/taskjuggler/Query.rb, line 262 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 281 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 289 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 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.
# 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
# File lib/taskjuggler/Query.rb, line 83 def startIdx=(idx) if idx.is_a?(Integer) @startIdx = idx @start = @project.idxToDate(idx) else raise "Unsupported type #{idx.class}" end end
Return the result of the Query
as Integer or Float. The result may be nil.
# File lib/taskjuggler/Query.rb, line 244 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 255 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 250 def to_sort @attr ? @attr.to_sort : @sortable end