File: RTFQuery.rb

package info (click to toggle)
tj3 3.8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,048 kB
  • sloc: ruby: 36,481; javascript: 1,113; sh: 19; makefile: 17
file content (221 lines) | stat: -rw-r--r-- 7,489 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
217
218
219
220
221
#!/usr/bin/env ruby -w
# encoding: UTF-8
#
# = RTFQuery.rb -- The TaskJuggler III Project Management Software
#
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014
#               by Chris Schlaeger <cs@taskjuggler.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#

require 'taskjuggler/RichText/RTFWithQuerySupport'
require 'taskjuggler/XMLElement'
require 'taskjuggler/Query'

class TaskJuggler

  # This class is a specialized RichTextFunctionHandler that can be used to
  # query the value of a project or property attribute.
  class RTFQuery < RTFWithQuerySupport

    def initialize(project, sourceFileInfo = nil)
      @project = project
      super('query', sourceFileInfo)
      @blockMode = false
    end

    # Return the result of the query as String.
    def to_s(args)
      return '' unless (query = prepareQuery(args))
      if query.ok
        query.to_s
      else
        error('query_error', query.errorMessage + recreateQuerySyntax(args))
        'Query Error: ' + query.errorMessage
      end
    end

    # Return a XMLElement tree that represents the navigator in HTML code.
    def to_html(args)
      return nil unless (query = prepareQuery(args))
      if query.ok
        if (rti = query.to_rti)
          rti.to_html
        elsif (str = query.to_s)
          XMLText.new(str)
        else
          nil
        end
      else
        error('query_error', query.errorMessage + recreateQuerySyntax(args))
        font = XMLElement.new('font', 'color' => '#FF0000')
        font << XMLText.new('Query Error: ' + query.errorMessage)
        font
      end
    end

    # Not supported for this function.
    def to_tagged(args)
      nil
    end

    private

    def prepareQuery(args)
      unless @query
        raise "No Query has been registered for this RichText yet!"
      end

      query = @query.dup

      # Check the user provided arguments. Only the following list is allowed.
      validArgs = %w( attribute currencyformat end family journalattributes
                      journalmode loadunit numberformat property scenario
                      scopeproperty start timeformat )
      expandedArgs = {}
      args.each do |arg, value|
        unless validArgs.include?(arg)
          error('bad_query_parameter', "Unknown query parameter '#{arg}'. " +
                "Use one of #{validArgs.join(', ')}!")
          return nil
        end
        expandedArgs[arg] =
          SimpleQueryExpander.new(value, @query, @sourceFileInfo).expand
      end

      if ((expandedArgs['property'] && expandedArgs['property'][0] != '!') ||
          expandedArgs['scopeproperty']) &&
          !(expandedArgs['family'] || @query.propertyType)
        error('missing_family',
              "If you provide a property or scope property you need to " +
              "provide a family type as well.")
      end

      # Every provided query parameter will overwrite the corresponding value
      # in the Query that was provided by the ReportContext.  The name of the
      # arguments don't always exactly match the Query variables Let's start
      # with the easy ones.
      if expandedArgs['property']
        query.propertyId = expandedArgs['property']
        query.property = nil unless query.propertyId[0] == '!'
      end
      if expandedArgs['scopeproperty']
        query.scopePropertyId = expandedArgs['scopeproperty']
        query.scopeProperty = nil
      end
      query.attributeId = expandedArgs['attribute'] if expandedArgs['attribute']
      query.start = TjTime.new(expandedArgs['start']) if expandedArgs['start']
      query.end = TjTime.new(expandedArgs['end']) if expandedArgs['end']
      if expandedArgs['numberformat']
        query.numberFormat = expandedArgs['numberformat']
      end
      query.timeFormat = expandedArgs['timeformat'] if expandedArgs['timeformat']
      if expandedArgs['currencyformat']
        query.currencyFormat = expandedArgs['currencyformat']
      end
      query.project = @project

      # And now the slighly more complicated ones.
      setScenarioIdx(query, expandedArgs)
      setPropertyType(query, expandedArgs)
      setLoadUnit(query, expandedArgs)
      setJournalMode(query, expandedArgs)
      setJournalAttributes(query, expandedArgs)

      # Now that we have put together the query, we can process it and return
      # the query object for result extraction.
      query.process
      query
    end

    # Regenerate the original query text based on the argument list.
    def recreateQuerySyntax(args)
      queryText = "\n<-query"
      args.each do |a, v|
        queryText += " #{a}=\"#{v}\""
      end
      queryText += "->"
    end

    def setPropertyType(query, args)
      validTypes = { 'account' => :Account,
                     'task' => :Task,
                     'resource' => :Resource }

      if args['family']
        unless validTypes[args['family']]
          error('rtfq_bad_query_family',
                "Unknown query family type '#{args['family']}'. " +
                "Use one of #{validTypes.keys.join(', ')}!")
        end
        query.propertyType = validTypes[args['family']]
        if query.propertyType == :Task
          query.scopePropertyType = :Resource
        elsif query.propertyType == :Resource
          query.scopePropertyType = :Task
        end
      end
    end

    def setLoadUnit(query, args)
      units = {
        'days' => :days, 'hours' => :hours, 'longauto' => :longauto,
        'minutes' => :minutes, 'months' => :months, 'quarters' => :quarters,
        'shortauto' => :shortauto, 'weeks' => :weeks, 'years' => :years
      }
      query.loadUnit = units[args['loadunit']] if args['loadunit']
    end

    def setScenarioIdx(query, args)
      if args['scenario']
        scenarioIdx = @project.scenarioIdx(args['scenario'])
        unless scenarioIdx
          error('rtfq_bad_scenario', "Unknown scenario #{args['scenario']}")
        end
        query.scenarioIdx = scenarioIdx
      end
      # Default to 0 in case no scenario was provided.
      query.scenarioIdx = 0 unless query.scenarioIdx
    end

    def setJournalMode(query, args)
      if (mode = args['journalmode'])
        validModes = %w( journal journal_sub status_up status_down alerts_down )
        unless validModes.include?(mode)
          error('rtfq_bad_journalmode',
                "Unknown journalmode #{mode}. Must be one of " +
                "#{validModes.join(', ')}.")
        end
        query.journalMode = mode.intern
      elsif !query.journalMode
        query.journalMode = :journal
      end
    end

    def setJournalAttributes(query, args)
      if (attrListStr = args['journalattributes'])
        attrs = attrListStr.split(', ').map { |a| a.delete(' ') }
        query.journalAttributes = []
        validAttrs = %w( author date details flags headline property propertyid
                         summary timesheet )
        attrs.each do |attr|
          if validAttrs.include?(attr)
            query.journalAttributes << attr
          else
            error('rtfq_bad_journalattr',
                  "Unknown journalattribute #{attr}. Must be one of " +
                  "#{validAttrs.join(', ')}.")
          end
        end
      elsif !query.journalAttributes
        query.journalAttributes = %w( date summary details )
      end
    end

  end

end