twb 3.9.3 → 3.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,81 @@
1
+ # WorksheetFields.rb Copyright (C) 2017, 2018 Chris Gerrard
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'twb'
17
+ require 'csv'
18
+
19
+ module Twb
20
+ module Analysis
21
+
22
+ class SheetFiltersAnalyzerA
23
+
24
+ include TabTool
25
+
26
+ attr_accessor :localEmit
27
+
28
+ def initialize
29
+ init
30
+ @funcdoc = {:class=>self.class, :blurb=>'Analyze Worksheet filters.', :description=>'Documents Quick Filters and the values they employ, if any. Work in progess.',}
31
+ #--
32
+ docFileName = docFile('TWBWorksheetFiltersA.csv')
33
+ $sheetFieldsCSV = CSV.open( docFileName ,'w')
34
+ $sheetFieldsCSV << ['Workbook','Worksheet','Filter Type','Operation','Data Source','Field','Value','Alias', 'Alias?']
35
+ addDocFile docFileName, "Workbooks, Worksheets and the sheets' Quick Filters"
36
+ #--
37
+ @sheetCount = 0
38
+ @filterCount = 0
39
+ end
40
+
41
+ def metrics
42
+ {
43
+ '# of Worksheets' => @sheetCount,
44
+ '# of Worksheet Filters' => @filterCount
45
+ }
46
+ end
47
+
48
+ def processTWB twb
49
+ @twb = twb
50
+ @twbName = @twb.name
51
+ emit " -- #{@twbName}"
52
+ @twbDomainsLoaded = false
53
+ parseFilters
54
+ end
55
+
56
+ def parseFilters
57
+ @worksheets = @twb.worksheets
58
+ @worksheets.each do |sheet|
59
+ emit "\n\nSHEET: #{sheet.name}"
60
+ filters = sheet.filters
61
+ filters.each do |filter|
62
+ filter.emit "-----------------------------\nWORKSHEET:: #{sheet.name}\n-----------------------------"
63
+ if filter.values.empty?
64
+ # $sheetFieldsCSV << ['Workbook','Worksheet','Filter Type','Operation' ,'Data Source','Field','Value','Alias', 'Alias?']
65
+ $sheetFieldsCSV << [@twbName ,sheet.name, filter.type ,filter.inexclude, filter.dataSource.uiname, filter.uiname,nil,nil,nil]
66
+ end
67
+ filter.values.each do |valueMap|
68
+ value = valueMap[:value]
69
+ valias = valueMap[:alias]
70
+ same = value.eql? valias
71
+ # puts "RECORDING FILTER VALUES: %-25s -- %-25s same? %s " % [value,valias,same]
72
+ $sheetFieldsCSV << [@twbName ,sheet.name, filter.type ,filter.inexclude, filter.dataSource.uiname, filter.uiname, value, valias, same]
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ end # class SheetFiltersAnalyzerA
79
+
80
+ end # module Twb
81
+ end # module Analysis
@@ -18,7 +18,9 @@ require 'digest/md5'
18
18
 
19
19
  module Twb
20
20
 
21
- class CalculatedField < TabClass
21
+ class CalculatedField
22
+
23
+ include TabTool
22
24
 
23
25
  attr_reader :dataSource
24
26
  attr_reader :node, :properties
@@ -13,13 +13,15 @@
13
13
  # You should have received a copy of the GNU General Public License
14
14
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
15
 
16
- require 'nokogiri'
17
- require 'digest/md5'
16
+ # require 'nokogiri'
17
+ # require 'digest/md5'
18
18
  require 'json'
19
19
 
20
20
  module Twb
21
21
 
22
- class DataSource < TabClass
22
+ class DataSource
23
+
24
+ include TabTool
23
25
 
24
26
  @@hasher = Digest::SHA256.new
25
27
 
@@ -53,6 +55,7 @@ module Twb
53
55
  attr_reader :aliases
54
56
  attr_reader :calculatedFields, :calculatedFieldNamesMap, :calculatedFieldNames, :calculatedField
55
57
  attr_reader :allFields
58
+ attr_reader :groups
56
59
  attr_reader :filters
57
60
  attr_reader :node
58
61
 
@@ -222,6 +225,7 @@ module Twb
222
225
  def loadAliases
223
226
  @aliases = {}
224
227
  # puts $node.xpath('.//column/aliases/..').length
228
+ cnt = 0
225
229
  @node.xpath('./column//aliases/..').each do |anode|
226
230
  # puts "anode:: #{anode}"
227
231
  # puts " path:: #{anode.path}"
@@ -252,6 +256,7 @@ module Twb
252
256
  end
253
257
  @aliases[name] = aliasMap
254
258
  end
259
+ emit "FIELD ALIASES: @aliases"
255
260
  return @aliases
256
261
  end
257
262
 
@@ -366,6 +371,10 @@ module Twb
366
371
  @fieldUINames[fld.name] = fld.uiname
367
372
  @fieldUINames[fld.uiname] = fld.uiname
368
373
  end
374
+ groups.each do |fld|
375
+ @fieldUINames[fld.name] = fld.uiname
376
+ @fieldUINames[fld.uiname] = fld.uiname
377
+ end
369
378
  return @fieldUINames
370
379
  end
371
380
 
@@ -463,6 +472,20 @@ module Twb
463
472
  end
464
473
  end
465
474
 
475
+ def groups
476
+ @groups ||= loadGroups
477
+ end
478
+
479
+ def loadGroups
480
+ @groups = []
481
+ groupNodes = @node.xpath('.//group')
482
+ groupNodes.each do |groupNode|
483
+ groupField = Twb::GroupField.new(groupNode)
484
+ @groups << groupField
485
+ end
486
+ return @groups
487
+ end
488
+
466
489
  def processFilters
467
490
  if @filters.nil?
468
491
  @filters = {}
@@ -0,0 +1,55 @@
1
+ # Copyright (C) 2018 Chris Gerrard
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'nokogiri'
17
+
18
+ module Twb
19
+ # module Data
20
+
21
+ class GroupField
22
+
23
+ include TabTool
24
+
25
+ attr_reader :node
26
+ attr_reader :name, :uiname, :caption
27
+
28
+ def initialize node
29
+ @node = node
30
+ @caption = @node['caption']
31
+ @name = @node['name'].gsub(/^\[|\]$/,'')
32
+ @uiname = @caption.nil? ? @name : @caption
33
+ end
34
+
35
+ def id
36
+ @id ||= @id = @name.hash
37
+ end
38
+
39
+ def properties
40
+ @properties ||= loadProperties
41
+ end
42
+
43
+ def loadProperties
44
+ @properties= {}
45
+ @node.attributes.each do |name,attr|
46
+ @properties[name] = attr.value
47
+ end
48
+ @properties[:uiname] = @name
49
+ return @properties
50
+ end
51
+
52
+ end # class GroupField
53
+
54
+ # end # module Data
55
+ end # module Twb
@@ -0,0 +1,119 @@
1
+ # Copyright (C) 2018 Chris Gerrard
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'nokogiri'
17
+ require 'digest/md5'
18
+
19
+ module Twb
20
+
21
+ class Parameter
22
+
23
+ include TabTool
24
+
25
+ attr_reader :caption, :name, :uiname
26
+ attr_reader :dataType, :format, :dataTypeCustom
27
+ attr_reader :domainType, :role, :type
28
+ attr_reader :currentValue, :values
29
+ attr_reader :node
30
+
31
+ def initialize(paramNode)
32
+ @node = paramNode
33
+ # --
34
+ @name = @node['name'].gsub(/^\[|\]$/,'')
35
+ @caption = @node['caption']
36
+ @uiname = caption.nil? ? nameTech : caption
37
+ @dataType = @node['datatype']
38
+ @format = @node['default-format'].nil? ? '<<default>>' : @node['default-format']
39
+ @dataTypeCustom = @node['datatype-customized'].nil? ? false : true
40
+ @domainType = @node['param-domain-type']
41
+ @role = @node['role']
42
+ @type = @node['type']
43
+ @currentValue = paramNode.at_xpath('./calculation[@class="tableau"]')['formula'].gsub(/^[#"]|[#"]$/,'')
44
+ @values = []
45
+ processNode
46
+ end
47
+
48
+ def to_s
49
+ "%s => %s" % [uiname, values]
50
+ end
51
+
52
+ private
53
+
54
+ def processNode
55
+ processMembers
56
+ processRange
57
+ processAll
58
+ end
59
+
60
+ def processMembers
61
+ #-- for when there are members, i.e. Lists
62
+ # <column caption='Date - List' datatype='date' datatype-customized='true' name='[Date - All 9/5/2018 Std Short Date (copy)]' param-domain-type='list' role='measure' type='quantitative' value='#2002-01-02#'>
63
+ # <calculation class='tableau' formula='#2002-01-02#' />
64
+ # <members>
65
+ # <member value='#2002-01-02#' />
66
+ # <member value='#2006-12-05#' />
67
+ # <member value='#1999-11-01#' />
68
+ # </members>
69
+ members = @node.xpath('.//member')
70
+ @hasMembers = !members.empty?
71
+ emit "# members: #{members.length}"
72
+ members.each do |m|
73
+ mvalue = m['value'].gsub(/^[#"]|[#"]$/,'')
74
+ malias = m['alias']
75
+ mvalui = malias.nil? ? mvalue : malias
76
+ @values << {:valueTech => mvalue, :alias => malias, :value => mvalui}
77
+
78
+ end
79
+ end
80
+
81
+ def processRange
82
+ # <column caption='Date - Range CV 3/3/2003 : ...12/5/2006' datatype='date' datatype-customized='true' name='[Date - Range CV 8/29/2001 3/23/2000...12/5/2006 (copy)]' param-domain-type='range' role='measure' type='quantitative' value='#2003-03-03#'>
83
+ # <calculation class='tableau' formula='#2003-03-03#' />
84
+ # <range max='#2006-12-05#' />
85
+ #--
86
+ # <column caption='Integer List Range -73...2,109 SS1' datatype='integer' datatype-customized='true' name='[Integer List Primes (copy)]' param-domain-type='range' role='measure' type='quantitative' value='1'>
87
+ # <calculation class='tableau' formula='1' />
88
+ # <range max='2109' min='1' />
89
+ # </column>
90
+ #--
91
+ # <column caption='Date - Range CV 8/29/2001 3/23/2000...' datatype='date' datatype-customized='true' name='[Date - Range CV 8/29/2001 3/23/2000...12/5/2006 (copy 2)]' param-domain-type='range' role='measure' type='quantitative' value='#2001-09-29#'>
92
+ # <calculation class='tableau' formula='#2001-09-29#' />
93
+ # <range min='#2000-03-23#' />
94
+ # </column>
95
+ #--
96
+ # <column caption='Integer List Range -73...2,109 SS3' datatype='integer' datatype-customized='true' name='[Integer List Range -73...2,109 SS1 (copy)]' param-domain-type='range' role='measure' type='quantitative' value='1'>
97
+ # <calculation class='tableau' formula='1' />
98
+ # <range granularity='3' max='2109' min='1' />
99
+ # </column>
100
+ if 'range' == domainType
101
+ @hasRange = true
102
+ rangeNode = @node.at_xpath('./range')
103
+ min = rangeNode['min'].nil? ? nil : rangeNode['min'].gsub(/^[#"]|[#"]$/,'')
104
+ max = rangeNode['max'].nil? ? nil : rangeNode['max'].gsub(/^[#"]|[#"]$/,'')
105
+ range = "#{min}...#{max}"
106
+ @values << {:value => range}
107
+ end
108
+ end
109
+
110
+ def processAll
111
+ unless @hasMembers || @hasRange
112
+ @values << {:value => '<<All>>'}
113
+ end
114
+ end
115
+
116
+ end # class Parameter
117
+
118
+ end # module Twb
119
+
@@ -0,0 +1,442 @@
1
+ # Copyright (C) 2018 Chris Gerrard
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'nokogiri'
17
+ require 'digest/md5'
18
+
19
+ module Twb
20
+
21
+ class QuickFilter
22
+
23
+ include TabTool
24
+
25
+ attr_reader :field, :name, :uiname
26
+ attr_reader :type
27
+ attr_reader :dataSource
28
+ attr_reader :node, :values
29
+ attr_reader :inexclude, :inexMode, :includeNull
30
+
31
+ def initialize(node,twb)
32
+ init
33
+ emit "\nFILTER:\n#{node}\n====="
34
+ @node = node
35
+ filterClass = node['class']
36
+ @type = filterClass.gsub('-',' ').capitalize
37
+ fieldCode = node['column']
38
+ codedField = Twb::CodedField.new(fieldCode)
39
+ fieldTech = codedField.name
40
+ inexclusions = fieldTech =~ /^(Ex|In)clusions/
41
+ @measureNames = ':Measure Names' == fieldTech
42
+ srcTech = codedField.dataSource
43
+ @dataSource = twb.datasource(srcTech)
44
+ @dsAliases = @dataSource.aliases
45
+ field = @measureNames || inexclusions ? fieldTech : @dataSource.field(fieldTech)
46
+ uiname = @dataSource.fieldUIName(fieldTech)
47
+ @uiname = @measureNames || inexclusions ? fieldTech : uiname.nil? ? fieldTech : uiname
48
+ @field = @uiname
49
+ @includeNull = if @node['include-null'].nil?
50
+ true
51
+ elsif 'false' == @node['include-null']
52
+ false
53
+ else
54
+ true
55
+ end
56
+ @values = []
57
+ enode = @node.at_xpath('.//*[@user:ui-enumeration]')
58
+ @inexclude = if enode.nil?
59
+ 'Include'
60
+ else case enode['user:ui-enumeration']
61
+ when 'inclusive' then 'Include'
62
+ when 'exclusive' then 'Exclude'
63
+ when nil then 'Include'
64
+ when 'all' then 'Include'
65
+ else 'undefined'
66
+ end
67
+ end
68
+ @inexMode = enode.nil? ? 'Default' : 'Specified'
69
+ emit "\n:: FIELD :: #{field} == #{@uiname} -- #{fieldTech} -- #{codedField.rawCode}"
70
+ emit " filter class: #{filterClass}"
71
+ emit " field code: #{fieldCode}"
72
+ emit " coded field: #{codedField}"
73
+ emit " field tech: #{fieldTech}"
74
+ emit " field name: nil? #{@uiname.nil?} #{@uiname} "
75
+ emit " src tech: #{srcTech}"
76
+ emit " @measureNames: #{@measureNames}"
77
+ emit " ds: #{@dataSource.uiname}"
78
+ emit " field: #{@uiname}"
79
+ emit " @inexclude: #{@inexclude}"
80
+ emit " "
81
+ aaa = case filterClass
82
+ when 'relative-date' then resolveRelativeDate
83
+ when 'quantitative' then resolveQuantitative
84
+ when 'categorical' then resolveCategoricalValues
85
+ end
86
+ end
87
+
88
+ def to_s
89
+ "%s => %s" % [uiname, values]
90
+ end
91
+
92
+ private
93
+
94
+ def recordValue value, valias=nil
95
+ # puts "recordValue--: #{value} :: @'#{valias}'"
96
+ valias = value if valias.nil?
97
+ @values << {:value => value, :alias => valias}
98
+ # puts "recordValue--: done"
99
+ end
100
+
101
+
102
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='-1' include-future='true' include-null='false' last-period='-1' period-type='day' />
103
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='-1' include-future='true' include-null='false' last-period='-1' period-type='year' />
104
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='-1' include-future='true' include-null='false' last-period='0' period-type='year' />
105
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='-2' include-future='true' include-null='false' last-period='0' period-type='day' />
106
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='-2' include-future='true' include-null='false' last-period='0' period-type='year' />
107
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='-6' include-future='true' include-null='false' last-period='0' period-type='day' />
108
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='0' include-future='false' include-null='false' last-period='0' period-type='year' />
109
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='0' include-future='true' include-null='false' last-period='0' period-type='day' />
110
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='0' include-future='true' include-null='false' last-period='0' period-type='year' />
111
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='0' include-future='true' include-null='false' last-period='2' period-type='day' />
112
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='0' include-future='true' include-null='false' last-period='2' period-type='year' />
113
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='0' include-future='true' include-null='false' last-period='4' period-type='day' />
114
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='1' include-future='true' include-null='false' last-period='1' period-type='day' />
115
+ # filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='1' include-future='true' include-null='false' last-period='1' period-type='year' />
116
+ def resolveRelativeDate
117
+ emit "resolveRelativeDate"
118
+ periodType = @node['period-type']
119
+ inclFuture = @node['include-future'] == 'true'
120
+ firstPeriod = @node['first-period'].to_i
121
+ lastPeriod = @node['last-period'].to_i
122
+ sum = firstPeriod + lastPeriod
123
+ prod = firstPeriod * lastPeriod
124
+ periodTech = "#{periodType} : #{firstPeriod} -> #{lastPeriod}"
125
+ period = periodTech
126
+ if sum == 0 && prod == 0
127
+ period = case periodType
128
+ when 'day' then "Today"
129
+ else inclFuture ? "This #{periodType.capitalize}" : "#{periodType.capitalize} to date"
130
+ end
131
+ elsif firstPeriod == lastPeriod
132
+ future = firstPeriod > 0
133
+ reln = future ? 'Next' : 'Previous'
134
+ period = case periodType
135
+ when 'day' then future ? 'Tomorrow' : 'Yesterday'
136
+ else "#{reln} #{periodType.capitalize}"
137
+ end
138
+ else
139
+ span = lastPeriod - firstPeriod + 1
140
+ future = firstPeriod == 0
141
+ reln = future ? "Next" : "Last"
142
+ period = "#{reln} #{span} #{periodType.capitalize}s"
143
+ end
144
+ recordValue period, periodTech
145
+ end
146
+
147
+ # <filter class='quantitative' column='[Sample - Superstore].[none:Profit:qk]' included-values='in-range'>
148
+ # <filter class='quantitative' column='[Sample - Superstore].[none:Profit:qk]' included-values='in-range'>
149
+ # <filter class='quantitative' column='[Sample - Superstore].[none:Profit:qk]' included-values='in-range'>
150
+ # <filter class='quantitative' column='[Sample - Superstore].[none:Profit:qk]' included-values='all' />
151
+ # <filter class='quantitative' column='[Sample - Superstore].[none:Profit:qk]' included-values='non-null' />
152
+ # <filter class='quantitative' column='[Sample - Superstore].[none:Profit:qk]' included-values='null' />
153
+ def resolveQuantitative
154
+ emit "resolveQuantitative"
155
+ inclValues = @node['included-values']
156
+ qvalues = if 'in-range' == inclValues
157
+ quantRangeValues
158
+ else
159
+ "#{inclValues.capitalize} Values"
160
+ end
161
+ # recordValue qvalues, inclValues
162
+ end
163
+
164
+ def quantRangeValues
165
+ min = @node.at_xpath('min')
166
+ max = @node.at_xpath('max')
167
+ emit "min: nil? %-6s val: %-s " % [min.nil?,min]
168
+ emit "max: nil? %-6s val: %-s " % [max.nil?,max]
169
+ minrv = parseRangeVal min unless min.nil?
170
+ maxrv = parseRangeVal max unless max.nil?
171
+ mintxt = min.nil? ? '' : max.nil? ? "At least: #{minrv}" : "#{minrv}"
172
+ maxtxt = max.nil? ? '' : min.nil? ? "At most: #{maxrv}" : ",...,#{maxrv}"
173
+ recordValue "#{mintxt}#{maxtxt}"
174
+ # emit "#{mintxt}#{maxtxt}"
175
+ # return "#{mintxt} #{maxtxt}"
176
+ end
177
+
178
+ def parseRangeVal node
179
+ return 'nil' if node.nil?
180
+ text = node.text
181
+ if text.start_with?('#')
182
+ return text.gsub(/^[#]|[#]$/,'')
183
+ end
184
+ num = text.to_f
185
+ # num.negative? ? num.floor : num.ceil
186
+ result = if num < 0
187
+ num.floor
188
+ else
189
+ num.ceil
190
+ end
191
+ return result
192
+ end
193
+
194
+ # <filter class='categorical' column='[Sample - Superstore - English (Extract)].[none:Region:nk]'>
195
+ # <groupfilter from='&quot;East&quot;' function='range' level='[none:Region:nk]' to='&quot;West&quot;' user:ui-domain='relevant' user:ui-enumeration='inclusive' user:ui-marker='enumerate' />
196
+ # </filter>
197
+ #--
198
+ def resolveCategoricalValues
199
+ emit "resolveCategoricalValues"
200
+ emit "@measureNames: #{@measureNames}"
201
+ if @node.element_children.empty?
202
+ # <filter class='categorical' column='[Sample - Superstore].[Top Customers by Profit (copy)]' />
203
+ recordValue 'All'
204
+ return
205
+ end
206
+ firstChild = @node.at_xpath('./groupfilter')
207
+ unless firstChild.nil? # should not happen per 'if @node.element_children.empty?' above
208
+ function = firstChild['function']
209
+ emit "function : #{function}"
210
+ emit node.to_s
211
+ #-- single element filter
212
+ case function
213
+ when 'member'
214
+ member = firstChild['member']
215
+ value = @measureNames ? @dataSource.fieldUIName(Twb::CodedField.new(member).name) : member.gsub(/^"|"$/,'')
216
+ recordValue value, @dataSource.deAlias(@uiname,value)
217
+ when 'empty-level'
218
+ recordValue 'None'
219
+ when 'range'
220
+ filtersFromRangeNode firstChild
221
+ when 'union'
222
+ emit "No resolving to do here. 'union' function accommodated with collection of members"
223
+ when 'except'
224
+ emit "UNRESOLVED function: #{function}"
225
+ when 'reorder-dimensionality'
226
+ emit "UNRESOLVED function: #{function}, involved with Inclusions & Exclusions - complicated to handle"
227
+ when 'level-members'
228
+ uiEnum = firstChild['user:ui-enumeration']
229
+ case uiEnum
230
+ when 'all'
231
+ recordValue 'All'
232
+ else
233
+ recordValue 'N/A'
234
+ emit "###### ALERT - Unresolved Quick filter ######"
235
+ end
236
+ else
237
+ recordValue "UNRESOLVED fn: #{function}"
238
+ end
239
+ # if 'member'.eql? function
240
+ # emit "HANDLING SINGLE MEMBER FILTER"
241
+ # member = firstChild['member']
242
+ # value = @measureNames ? @dataSource.fieldUIName(Twb::CodedField.new(member).name) : member.gsub(/^"|"$/,'')
243
+ # alia = @dataSource.deAlias(@uiname,value)
244
+ # emit "value :%-25s => alias: %-s" % [value, alia]
245
+ # recordValue value, alia
246
+ # return
247
+ # end
248
+ # if 'empty-level' == function
249
+ # recordValue 'None', 'None'
250
+ # return
251
+ # end
252
+ # if 'range'.eql? function
253
+ # emit "HANDLING RANGE 1st Child FILTER"
254
+ # # values = filtersFromRangeNode firstChild
255
+ # filtersFromRangeNode firstChild
256
+ # # values.each do |valMap|
257
+ # # recordValue valMap[:value], valMap[:alias]
258
+ # # end
259
+ # end
260
+ #-- another single element filter
261
+ # <groupfilter function="level-members" level="[none:Business Line:nk]" user:ui-enumeration="all" user:ui-marker="enumerate"/>
262
+ # <filter class='categorical' column='[federated.1astm0q1hl2ydc1dyqhqq0igvxkp].[none:Business Line:nk]' context='true'>
263
+ # <groupfilter function='level-members'
264
+ # level='[none:Business Line:nk]'
265
+ # user:ui-enumeration='all'
266
+ # user:ui-exclude='true'
267
+ # user:ui-marker='enumerate' />
268
+ # </filter>
269
+ # <filter class='categorical' column='[federated.1astm0q1hl2ydc1dyqhqq0igvxkp].[none:Business Line:nk]' context='true'>
270
+ # <groupfilter function='level-members'
271
+ # level='[none:Business Line:nk]'
272
+ # user:ui-enumeration='all'
273
+ # user:ui-marker='enumerate' />
274
+ # </filter>
275
+ if 'level-members'.eql? function
276
+ emit "HANDLING level-members FILTER"
277
+ #--
278
+ # <filter class='categorical' column='[Sample - Superstore].[Top Customers by Profit (copy)]'>
279
+ # <groupfilter function='level-members' level='[Customer Name]' user:ui-enumeration='all' user:ui-marker='enumerate' />
280
+ # </filter>
281
+ #--
282
+ # <filter class='categorical' column='[Sample - Superstore].[none:Customer Name:nk]'>
283
+ # <groupfilter function='level-members' level='[none:Customer Name:nk]' user:ui-enumeration='all' user:ui-marker='enumerate' />
284
+ # </filter>
285
+ #--
286
+ # <filter class='categorical' column='[Sample - Superstore].[Top Customers by Profit (copy)]'>
287
+ # <groupfilter function='level-members' level='[Customer Name]' user:ui-enumeration='all' user:ui-exclude='true' user:ui-marker='enumerate' />
288
+ # </filter>
289
+ #--
290
+ # <filter class='categorical' column='[Sample - Superstore].[none:Customer Name:nk]'>
291
+ # <groupfilter function='level-members' level='[none:Customer Name:nk]' user:ui-enumeration='all' user:ui-marker='enumerate' />
292
+ # </filter>
293
+ #--
294
+ # <filter class='categorical' column='[Sample - Superstore].[Top Customers by Profit (copy)]'>
295
+ # <groupfilter function='except' user:ui-domain='relevant' user:ui-enumeration='exclusive' user:ui-marker='enumerate'>
296
+ # <groupfilter function='level-members' level='[Customer Name]' />
297
+ # <groupfilter function='union'>
298
+ # <groupfilter function='member' level='[Customer Name]' member='&quot;Adrian Barton&quot;' />
299
+ # <groupfilter function='member' level='[Customer Name]' member='&quot;Raymond Buch&quot;' />
300
+ # </groupfilter>
301
+ # </groupfilter>
302
+ # </filter>
303
+ #--
304
+ uiEnum = firstChild['user:ui-enumeration']
305
+ case uiEnum
306
+ when 'all'
307
+ recordValue 'All', 'All'
308
+ when 'inclusive'
309
+ @inexclude = 'Include'
310
+ when 'exclusive'
311
+ @inexclude = 'Exclude'
312
+ end
313
+ end
314
+ #-- otherwise filter contains multiple elements
315
+ #-- handle individual member elements
316
+ elements = firstChild.xpath('.//groupfilter')
317
+ elements.each do |element|
318
+ function = element.attribute('function').text
319
+ emit "element function: #{function}\n node:\n#{element}"
320
+ if 'member'.eql? function
321
+ member = element.attribute('member').text
322
+ name = @measureNames ? @dataSource.fieldUIName(Twb::CodedField.new(member).name) : member.gsub(/^"|"$/,'')
323
+ emit "%%%% member NAME:: #{name}"
324
+ if '%null%' == name then name = 'Null' end
325
+ alia = @dataSource.fieldAlias(@uiname,name) # $TableNameAliases[name]
326
+ recordValue name, alia
327
+ end
328
+ if 'range'.eql? function
329
+ emit "%%%% range element:: #{element}"
330
+ t = filtersFromRangeNode element
331
+ if t.empty?
332
+ fromAttr = element['from']
333
+ from = fromAttr.nil? ? '' : fromAttr.gsub(/^[#"']|[#"']$/,'')
334
+ toAttr = element['to']
335
+ to = toAttr.nil? ? '' : toAttr.gsub(/^[#"']|[#"']$/,'')
336
+ range = "#{from},...,#{to}"
337
+ recordValue range, range
338
+ end
339
+ t.each do |name,alia|
340
+ # emit "%%%% range Name: %-20s ALIAS: %-s " % [name, alia]
341
+ recordValue name, @dataSource.fieldAlias(@uiname,name)
342
+ end
343
+ end
344
+ end
345
+ end
346
+ end
347
+
348
+ def filtersFromRangeNode node
349
+ unless @twbDomainsLoaded
350
+ loadDomains
351
+ end
352
+ # results = []
353
+ emit "filtersFromRangeNode"
354
+ emit " from: #{node.attribute('from')}"
355
+ emit " to. : #{node.attribute('to')}"
356
+ from = node.attribute('from').text.gsub(/^"|"$/,'')
357
+ to = node.attribute( 'to').text.gsub(/^"|"$/,'')
358
+ emit " from: #{from}"
359
+ emit " to. : #{to}"
360
+ if @twbDomainsLoaded
361
+ # results = filtersInRange from, to
362
+ filtersInRange from, to
363
+ else
364
+ range = "#{from},...,#{to}"
365
+ recordValue range, range
366
+ end
367
+ # return results
368
+ end
369
+
370
+ def filtersInRange from, to
371
+ emit "filtersInRange"
372
+ # results = {}
373
+ dsFields = @twbFielddomains[@dataSource.uiname]
374
+ emit "dsFields : #{dsFields}"
375
+ if dsFields.nil? || dsFields.empty?
376
+ alert "#### ALERT #### - '#{@uiname}' FIELD DOMAIN VALUES FOR '#{@twb.name} DATASOURCE #{@dataSource.uiname} NOT LOADED ####"
377
+ emit @twbFieldDomains
378
+ emit "==========="
379
+ emit dsFields
380
+ emit "==========="
381
+ else
382
+ fieldVals = dsFields[@uiname].to_a
383
+ if @dataSource.fieldHasAliases @uiname
384
+ #-- resolve aliases
385
+ dbValues = SortedSet.new
386
+ aliases = @dataSource.fieldAliases @uiname
387
+ fieldVals.each do |fv|
388
+ fvAliased = aliases.has_value? fv
389
+ if fvAliased
390
+ dbValues << aliases.key(fv)
391
+ else
392
+ dbValues << fv
393
+ end
394
+ end
395
+ $fieldVals = dbValues.to_a
396
+ else
397
+ #-- use domain values as returned
398
+ $fieldVals = dsFields[@uiname].to_a
399
+ end
400
+ # domainVals = dsFields[field].to_a
401
+ # #-- need to dealias (unalias?) the field domain values
402
+ # dbVals = SortedSet.new
403
+ # domainVals.each do |dv|
404
+ emit "field values: #{$fieldVals}"
405
+ unless $fieldVals.nil? || $fieldVals.empty?
406
+ values = $fieldVals
407
+ f_i = values.index from
408
+ t_i = values.index to
409
+ emit " from: #{f_i} :: '#{from}'"
410
+ emit " to: #{t_i} :: '#{to}'"
411
+ badRange = f_i.nil? || t_i.nil?
412
+ emit "badRange: #{badRange}"
413
+ if badRange
414
+ emit "BAD RANGE"
415
+ else
416
+ tnames = values[f_i..t_i]
417
+ tnames.each do |tname|
418
+ # results[tname] = @dataSource.deAlias(@uiname,tname) # $TableNameAliases[tname]
419
+ recordValue tname, @dataSource.deAlias(@uiname,tname)
420
+ end
421
+ # emit " filtersInRange f:%-35s t:%-s " % ["'#{from}:#{f_i}'", "'#{to}:#{t_i}'"]
422
+ # emit " names: #{results.keys}"
423
+ # emit " aliases: #{results.values}"
424
+ end
425
+ end
426
+ end
427
+ # return results
428
+ end
429
+
430
+ def loadDomains
431
+ emit "def loadDomains\n=="
432
+ unless @twb.nil?
433
+ loader = Twb::Util::FieldDomainLoader.new
434
+ @twbFielddomains = loader.loadWorkbook @twb
435
+ emit "FIELD DOMAINS:: #{@twbFielddomains}\n=="
436
+ @twbDomainsLoaded = true
437
+ end
438
+ end
439
+
440
+ end # class QuickFilter
441
+
442
+ end # module Twb