twb 3.7.5 → 3.9.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,14 +20,17 @@ module Analysis
20
20
  module CalculatedFields
21
21
 
22
22
  class MarkdownEmitter
23
+ include TabTool
23
24
 
24
25
  attr_reader :docFileName
25
26
 
26
27
  def initialize
27
- #@docFile.puts ""
28
+ init
29
+ @funcdoc = {:class=>self.class, :blurb=>'Generate Markdown doc for Calculated Fields.', :description=>'Creates a Markdown file documenting the Calculated Fields for an individual Workbook.',}
30
+ @metrics = {}
28
31
  end
29
32
 
30
- def processTwb twb
33
+ def processTWB twb
31
34
  # twb = File.basename(twb)
32
35
  @twb = twb #Twb::Workbook.new twb
33
36
  @docFileName = './ttdoc/' + @twb.name + '.CalculatedFields.md'
@@ -35,8 +38,6 @@ module CalculatedFields
35
38
  @docFile.puts "# #{twb.name}"
36
39
  dsNames = @twb.datasourceUINames
37
40
  @docFile.puts "#{dsNames.length} Data Sources"
38
-
39
-
40
41
  @twb.datasources.each do |ds|
41
42
  @docFile.puts "## #{ds.uiname}"
42
43
  @docFile.puts "__has #{ds.calculatedFields.length} calculated fields__\n "
@@ -77,6 +78,7 @@ module CalculatedFields
77
78
  end
78
79
  @docFile.puts "counted #{cnt} calculated fields\n "
79
80
  end # twb.datasources.each
81
+ finis
80
82
  end # def processTwb twb
81
83
 
82
84
  end # class MarkdownEmitter
@@ -0,0 +1,93 @@
1
+ # sheetfieldsanalyzer.rb 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 'twb'
17
+ require 'csv'
18
+
19
+ module Twb
20
+ module Analysis
21
+
22
+ class GroupFieldsAnalyzer
23
+
24
+ include TabTool
25
+
26
+ attr_accessor :localEmit
27
+
28
+ def initialize
29
+ init
30
+ @funcdoc = {:class=>self.class, :blurb=>'Analyze Group Fields.', :description=>'Identifies the Groups and their Members for grouped fields.',}
31
+ #--
32
+ docFileName = docFile('TwbGroupFields.csv')
33
+ @csv = CSV.open(docFileName,'w')
34
+ @csv << ['Workbook','Data Source','Field','Group','Member']
35
+ addDocFile docFileName, "Workbooks, Data Sources, and their Grouped Fields"
36
+ #--
37
+ @twbCount = 0
38
+ @dsCount = 0
39
+ @gfCount = 0
40
+ @gfmCount = 0
41
+ end
42
+
43
+ def metrics
44
+ {
45
+ '# of Workbooks' => @twbCount,
46
+ '# of Data Sources' => @dsCount,
47
+ '# of Group Fields' => @gfCount,
48
+ '# of Group Field Members' => @gfmCount
49
+ }
50
+ end
51
+
52
+ def processTWB twb
53
+ @twb = twb
54
+ @twbName = @twb.name
55
+ emit " -- #{@twbName}"
56
+ @twbCount += 1
57
+ initMarkdown
58
+ parseDataSources
59
+ finis
60
+ $mdFile.close unless $mdFile.nil?
61
+ end
62
+
63
+ def initMarkdown
64
+ $mdFile = File.open(docFile("#{@twbName}.GroupFields.md"), 'w')
65
+ $mdFile << "# #{@twbName}\n "
66
+ end
67
+
68
+ def parseDataSources
69
+ @twb.datasources.each do |ds|
70
+ $mdFile.puts "\n## #{ds.uiname}"
71
+ @dsCount += 1
72
+ cfs = ds.calculatedFields
73
+ cfs.each do |cf|
74
+ if cf.isGroup
75
+ @gfCount += 1
76
+ $mdFile.puts "### #{cf.name}"
77
+ cf.groupMembers.each do |lead,values|
78
+ $mdFile.puts "* #{lead}"
79
+ values.each do |v|
80
+ @gfmCount += 1
81
+ $mdFile.puts " > #{v}"
82
+ @csv << [@twbName, ds.uiname,cf.uiname,lead,v]
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ end #class SheetFieldsAnalyzer
91
+
92
+ end # module Analysis
93
+ end # module Twb
@@ -31,12 +31,8 @@ module Analysis
31
31
  #-- set up metrics
32
32
  @twbcount = 0
33
33
  @dscount = 0
34
+ @filecount = 0
34
35
  @sheetcount = 0
35
- @metrics = {
36
- '# of Workbooks' => @twbcount ,
37
- '# of Worksheets' => @dscount ,
38
- '# of Worksheets' => @sheetcount
39
- }
40
36
  #--
41
37
  @funcdoc = {:class=>self.class, :blurb=>'Analyzing Google Sheet Data Sources from Tableau Workbooks.', :description=>nil,}
42
38
  docFileName = docFile('TWBGoogleSheetDataSources.csv')
@@ -57,16 +53,19 @@ module Analysis
57
53
  dss = twb.datasources
58
54
  dss.each do |ds|
59
55
  emit ds.uiname
56
+ @dscount += 1
60
57
  conns = ds.node.xpath(".//connection[@class='google-sheets']")
61
58
  if conns.length > 0
62
- @relation = ds.node.at_xpath('./connection/relation')
63
- @relName = @relation.attribute('name').text
64
- @relType = @relation.attribute('type').text
65
- @fileName = ds.node.at_xpath('.//named-connection/connection').attribute('filename')
59
+ @relation = ds.node.at_xpath('./connection/relation')
60
+ @relName = @relation.attribute('name').text
61
+ @relType = @relation.attribute('type').text
62
+ @fileName = ds.node.at_xpath('.//named-connection/connection').attribute('filename')
63
+ @filecount += 1
66
64
  emit "FILENAME: #{@fileName}"
67
65
  tables = ds.node.xpath(".//relation[@type='table']")
68
66
  # emit "# Tables: #{tables.length}"
69
67
  tables.each do |table|
68
+ @sheetcount += 1
70
69
  tableName = table.attribute('name')
71
70
  columns = table.xpath('.//column')
72
71
  columns.each do |column|
@@ -78,6 +77,16 @@ module Analysis
78
77
  end
79
78
  emit " "
80
79
  end
80
+ finis
81
+ end
82
+
83
+ def metrics
84
+ {
85
+ '# of Workbooks' => @twbcount,
86
+ '# of Data Sources' => @dscount,
87
+ '# of Google Docs' => @filecount,
88
+ '# of Worksheets' => @sheetcount
89
+ }
81
90
  end
82
91
 
83
92
  end #class SheetFieldsAnalyzer
@@ -0,0 +1,50 @@
1
+ # analyzeSheetFields.rb 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
+ system 'cls'
17
+
18
+ require 'twb'
19
+
20
+ $twbCount = 0
21
+
22
+ $analyzer = Twb::Analysis::DashSheetsAnalyzer.new
23
+
24
+ def processTwb twbName
25
+ twb = Twb::Workbook.new twbName
26
+ puts "\t - #{twb.name}"
27
+ # --
28
+ $analyzer.processTWB twb
29
+ # --
30
+ $twbCount += 1
31
+ end
32
+
33
+ puts "\n\n "
34
+ puts " #{$analyzer.funcdoc[:class]}"
35
+ puts " #{$analyzer.funcdoc[:blurb]}"
36
+ puts "\n "
37
+
38
+ path = if ARGV.empty? then ['*.twb','*.twbx'] else ARGV[0] end
39
+ puts " Processing Workbooks matching: '#{path}'\n "
40
+ Dir.glob(path) { |twb| processTwb twb if twb =~ /twb[x]?$/ && twb !~ /dot[.]twb[x]?$/}
41
+
42
+ puts "\n "
43
+ puts " Analysis complete, found: #{$twbCount} Workbooks"
44
+ puts "\n"
45
+
46
+ $analyzer.docfilesdoc.each do |l|
47
+ puts ' ' + l
48
+ end
49
+
50
+ puts "\n\n That's all, folks.\n "
@@ -0,0 +1,75 @@
1
+ # sheetfieldsanalyzer.rb 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 'twb'
17
+ require 'csv'
18
+
19
+ module Twb
20
+ module Analysis
21
+
22
+ class DashSheetsAnalyzer
23
+
24
+ include TabTool
25
+
26
+ attr_accessor :localEmit
27
+
28
+ def initialize
29
+ init
30
+ @funcdoc = {:class=>self.class, :blurb=>'Analyze Dashboard Sheets from Tableau Workbooks.', :description=>'Identifies the Worksheets present in Dashboards.',}
31
+ #--
32
+ docFileName = docFile('TwbDashboardSheets.csv')
33
+ @dashSheetsCSV = CSV.open(docFileName,'w')
34
+ @dashSheetsCSV << ['Workbook','Dashboard','Worksheet','Hidden','Visible']
35
+ addDocFile docFileName, "Workbooks, Dashboards, and Worksheets in the Dashboards"
36
+ #--
37
+ @twbCount = 0
38
+ @dashCount = 0
39
+ @sheetCount = 0
40
+ end
41
+
42
+ def metrics
43
+ {
44
+ '# of Workbooks' => @twbCount,
45
+ '# of Dashboards' => @dashCount,
46
+ '# of Worksheets' => @sheetCount
47
+ }
48
+ end
49
+
50
+ def processTWB twb
51
+ @twb = twb
52
+ @twbName = @twb.name
53
+ emit " -- #{@twbName}"
54
+ @twbCount += 1
55
+ parseDashes
56
+ finis
57
+ end
58
+
59
+ def parseDashes
60
+ @dashboards = @twb.dashboards
61
+ @dashboards.each do |dash|
62
+ emit "DASH:: #{dash.name}"
63
+ @dashCount += 1
64
+ dash.worksheets.each do |sheet|
65
+ @sheetCount += 1
66
+ emit "SHEET: #{sheet.name}"
67
+ @dashSheetsCSV << [@twbName, dash.name, sheet.name, sheet.hidden, sheet.visible ]
68
+ end
69
+ end
70
+ end
71
+
72
+ end #class SheetFieldsAnalyzer
73
+
74
+ end # module Analysis
75
+ end # module Twb
@@ -26,14 +26,25 @@ module Analysis
26
26
  attr_accessor :localEmit
27
27
 
28
28
  def initialize
29
- @twbCnt = 0
30
- @sheetCnt = 0
31
- @filterCnt = 0
32
- @funcdoc = {:class=>self.class, :blurb=>'Analyzing Sheet Fields from Tableau Workbooks.', :description=>nil,}
33
- docFileName = docFile('TWBWorksheetFields.csv')
29
+ init
30
+ @funcdoc = {:class=>self.class, :blurb=>'Analyze Sheet Fields from Tableau Workbooks.', :description=>nil,}
31
+ #--
32
+ docFileName = docFile('TwbWorksheetFields.csv')
34
33
  @sheetFieldsCSV = CSV.open(docFileName,'w')
35
34
  @sheetFieldsCSV << ['Workbook','Worksheet','Data Source','Data Source (tech)','Field','Field (tech)']
36
- addDocFile docFileName, "CSV File containing the data relating Workbooks,Worksheets, and the sheets' Data Sources and Fields"
35
+ addDocFile docFileName, "Workbooks, Worksheets, and the Sheets' Data Sources and Fields"
36
+ #--
37
+ @twbCnt = 0
38
+ @sheetCnt = 0
39
+ @fieldsCnt = 0
40
+ end
41
+
42
+ def metrics
43
+ {
44
+ '# of Workbooks' => @twbcount,
45
+ '# of Worksheets' => @sheetCnt,
46
+ '# of Worksheet Fields' => @fieldsCnt
47
+ }
37
48
  end
38
49
 
39
50
  def processTWB twb
@@ -42,11 +53,13 @@ module Analysis
42
53
  @twbCnt += 1
43
54
  @twbDomainsLoaded = false
44
55
  parseSheets
56
+ finis
45
57
  end
46
58
 
47
59
  def parseSheets
48
60
  @worksheets = @twb.worksheets
49
61
  @worksheets.each do |sheet|
62
+ @sheetCnt += 1
50
63
  emit "SHEET: #{sheet.name}"
51
64
  showFields sheet unless sheet.datasourceFields.nil?
52
65
  end
@@ -64,6 +77,7 @@ module Analysis
64
77
  emit " - #{ds.uiname}"
65
78
  emit " : #{ds.class}"
66
79
  dsfields.each do |sheetField|
80
+ @fieldsCnt += 1
67
81
  emit " f: #{sheetField}"
68
82
  emit " c: #{sheetField.class}"
69
83
  fuiName = ds.fieldUIName sheetField #Fields[sheetField]
@@ -15,7 +15,6 @@
15
15
 
16
16
  require 'twb'
17
17
  require 'csv'
18
- require 'set'
19
18
 
20
19
  module Twb
21
20
  module Analysis
@@ -27,20 +26,34 @@ module Analysis
27
26
  attr_accessor :localEmit
28
27
 
29
28
  def initialize
30
- @twbCnt = 0
31
- @sheetCnt = 0
32
- @filterCnt = 0
33
- # @ttlogfile = File.new('./ttdoc/SheetFiltersAnalyzer.ttlog','w')
34
- $sheetFieldsCSV = CSV.open(docFile('TWBWorksheetFilters.csv') ,'w')
35
- $sheetFieldsCSV << ['Workbook','Worksheet','Filter Type','Data Source','Field','Value','Alias', 'Alias?']
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('TWBWorksheetFilters.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
+ @twbCount = 0
38
+ @sheetCount = 0
39
+ @filterCount = 0
40
+ end
41
+
42
+ def metrics
43
+ {
44
+ '# of Workbooks' => @twbcount,
45
+ '# of Worksheets' => @sheetCount,
46
+ '# of Worksheet Filters' => @filterCount
47
+ }
36
48
  end
37
49
 
38
50
  def processTWB twb
39
51
  @twb = twb
40
52
  emit " -- #{@twb.name}"
41
- @twbCnt += 1
53
+ @twbCount += 1
42
54
  @twbDomainsLoaded = false
43
55
  parseFilters
56
+ finis
44
57
  end
45
58
 
46
59
 
@@ -48,72 +61,230 @@ module Analysis
48
61
  @worksheets = @twb.worksheets
49
62
  @worksheets.each do |sheet|
50
63
  emit "\n\nSHEET: #{sheet.name}"
51
- @sheetCnt += 1
64
+ @sheetCount += 1
52
65
  filters = sheet.node.xpath('.//filter[@column]')
53
66
  filters.each do |filter|
67
+ @filterCount += 1
54
68
  emit "\nFILTER:\n#{filter}\n====="
55
- fieldCode = filter.attribute('column').text
56
- codedField = Twb::CodedField.new(fieldCode)
57
- fieldTech = codedField.name
58
- srcTech = codedField.dataSource
59
- dataSource = @twb.datasource(srcTech)
60
- @dsAliases = dataSource.aliases
61
- field = dataSource.fieldUIName fieldTech
62
- emit "\n:: FIELD :: #{field} -- #{fieldTech} -- #{codedField.rawCode}"
63
- filterValues = collectFilterValues sheet, dataSource, field, filter
69
+ filterClass = filter.attribute('class').text
70
+ fieldCode = filter.attribute('column').text
71
+ codedField = Twb::CodedField.new(fieldCode)
72
+ fieldTech = codedField.name
73
+ inexclude = fieldTech =~ /^(Ex|In)clusions/
74
+ measureNames = ':Measure Names' == fieldTech
75
+ srcTech = codedField.dataSource
76
+ dataSource = @twb.datasource(srcTech)
77
+ @dsAliases = dataSource.aliases
78
+ field = measureNames || inexclude ? fieldTech : dataSource.field(fieldTech)
79
+ fieldName = measureNames || inexclude ? fieldTech : dataSource.fieldUIName(fieldTech)
80
+ emit "\n:: FIELD :: #{field} == #{fieldName} -- #{fieldTech} -- #{codedField.rawCode}"
81
+ emit " filter class: #{filterClass}"
82
+ emit " field code: #{fieldCode}"
83
+ emit " coded field: #{codedField}"
84
+ emit " field tech: #{fieldTech}"
85
+ emit " field name: nil? #{fieldName.nil?} #{fieldName} "
86
+ emit " src tech: #{srcTech}"
87
+ emit " measureNames: #{measureNames}"
88
+ emit " sheet: #{sheet.name}"
89
+ emit " node: #{filter}"
90
+ emit " ds: #{dataSource.uiname}"
91
+ emit " field: #{field}"
92
+ emit " "
93
+ aaa = case filterClass
94
+ when 'relative-date' then resolveRelativeDate(sheet, dataSource, fieldName, filter)
95
+ when 'quantitative' then resolveQuantitative(sheet, dataSource, fieldName, filter)
96
+ when 'categorical' then resolveCategoricalValues(sheet, dataSource, fieldName, filter, measureNames)
97
+ end
64
98
  end
65
99
  end
66
100
  end
67
101
 
68
- def collectFilterValues sheet, dataSource, field, node
69
- emit "collectFilterValues"
70
- emit " sheet: #{sheet.name}"
71
- emit " ds: #{dataSource.uiname}"
72
- emit " field: #{field}"
73
- emit " node: #{node}"
74
- emit " "
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 sheet, dataSource, field, node
117
+ emit "resolveRelativeDate"
118
+ operation = 'Inclusive'
119
+ periodType = node['period-type']
120
+ inclFuture = node['include-future'] == 'true'
121
+ firstPeriod = node['first-period'].to_i
122
+ lastPeriod = node['last-period'].to_i
123
+ sum = firstPeriod + lastPeriod
124
+ prod = firstPeriod * lastPeriod
125
+ periodTech = "#{periodType} : #{firstPeriod} -> #{lastPeriod}"
126
+ period = periodTech
127
+ if sum == 0 && prod == 0
128
+ period = case periodType
129
+ when 'day' then "Today"
130
+ else inclFuture ? "This #{periodType.capitalize}" : "#{periodType.capitalize} to date"
131
+ end
132
+ elsif firstPeriod == lastPeriod
133
+ future = firstPeriod > 0
134
+ reln = future ? 'Next' : 'Previous'
135
+ period = case periodType
136
+ when 'day' then future ? 'Tomorrow' : 'Yesterday'
137
+ else "#{reln} #{periodType.capitalize}"
138
+ end
139
+ else
140
+ span = lastPeriod - firstPeriod + 1
141
+ future = firstPeriod == 0
142
+ reln = future ? "Next" : "Last"
143
+ period = "#{reln} #{span} #{periodType.capitalize}s"
144
+ end
145
+ $sheetFieldsCSV << [ @twb.name, sheet.name, 'Relative Date',operation, dataSource.uiname, field, period, periodTech ]
146
+ end
147
+
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='in-range'>
151
+ # <filter class='quantitative' column='[Sample - Superstore].[none:Profit:qk]' included-values='all' />
152
+ # <filter class='quantitative' column='[Sample - Superstore].[none:Profit:qk]' included-values='non-null' />
153
+ # <filter class='quantitative' column='[Sample - Superstore].[none:Profit:qk]' included-values='null' />
154
+ def resolveQuantitative sheet, dataSource, field, node
155
+ emit "resolveQuantitative"
156
+ operation = 'Inclusive'
157
+ inclValues = node['included-values']
158
+ values = if 'in-range' == inclValues
159
+ quantRangeValues node
160
+ else
161
+ "#{inclValues.capitalize} Values"
162
+ end
163
+ $sheetFieldsCSV << [ @twb.name, sheet.name, 'Range (row)',operation.capitalize, dataSource.uiname, field, values, inclValues ]
164
+ end
165
+
166
+ def quantRangeValues node
167
+ min = node.at_xpath('min')
168
+ max = node.at_xpath('max')
169
+ emit "min: nil? %-6s val: %-s " % [min.nil?,min]
170
+ emit "max: nil? %-6s val: %-s " % [max.nil?,max]
171
+ minrv = parseRangeVal min unless min.nil?
172
+ maxrv = parseRangeVal max unless max.nil?
173
+ mintxt = min.nil? ? '' : max.nil? ? "At least: #{minrv}" : "Range #{minrv}"
174
+ maxtxt = max.nil? ? '' : min.nil? ? "At most: #{maxrv}" : " ... #{maxrv}"
175
+ emit "#{mintxt} #{maxtxt}"
176
+ return "#{mintxt} #{maxtxt}"
177
+ end
178
+
179
+ def parseRangeVal node
180
+ return 'nil' if node.nil?
181
+ text = node.text
182
+ if text.start_with?('#')
183
+ return text.gsub(/^[#]|[#]$/,'')
184
+ end
185
+ num = text.to_f
186
+ # num.negative? ? num.floor : num.ceil
187
+ result = if num < 0
188
+ num.floor
189
+ else
190
+ num.ceil
191
+ end
192
+ return result
193
+ end
194
+
195
+ def resolveCategoricalValues sheet, dataSource, field, node, measureNames
196
+ emit "resolveCategoricalValues"
197
+ emit "measureNames: #{measureNames}"
75
198
  firstChild = node.at_xpath('./groupfilter')
76
199
  results = {}
200
+ #-- handling Inclusions & Exclusions, in-sheet pick-data filters
201
+ if field =~ /^(Ex|In)clusions/
202
+ enumerate = firstChild['user:ui-enumeration']
203
+ operation = enumerate.nil? ? 'Inclusive' : enumerate.capitalize
204
+ $sheetFieldsCSV << [ @twb.name, sheet.name, 'In-Sheet', operation, dataSource.uiname, field, '+++', '+++' ]
205
+ return results
206
+ end
207
+ #--
77
208
  unless firstChild.nil?
78
209
  function = firstChild.attribute('function').text
79
- emit "function: #{function}"
210
+ enumerate = firstChild['user:ui-enumeration']
211
+ operation = enumerate.nil? ? 'Inclusive' : enumerate.capitalize
212
+ emit "function : #{function}"
213
+ emit "enumerate: #{enumerate}"
214
+ emit "operation: #{operation}"
80
215
  emit node.to_s
81
216
  #-- single element filter
82
217
  if 'member'.eql? function
83
218
  emit "HANDLING SINGLE MEMBER FILTER"
84
- value = firstChild.attribute('member').text.gsub(/^"|"$/,'')
219
+ member = firstChild['member']
220
+ value = measureNames ? dataSource.fieldUIName(Twb::CodedField.new(member).name) : member.gsub(/^"|"$/,'')
85
221
  alia = dataSource.deAlias(field,value)
86
222
  emit "value :%-25s => alias: %-s" % [value, alia]
87
- $sheetFieldsCSV << [ @twb.name, sheet.name, 'single', dataSource.uiname, field, value, alia ]
223
+ $sheetFieldsCSV << [ @twb.name, sheet.name, 'single', operation, dataSource.uiname, field, value, alia ]
88
224
  return {value => alia}
89
225
  end
226
+ #-- another single element filter
227
+ # <groupfilter function="level-members" level="[none:Business Line:nk]" user:ui-enumeration="all" user:ui-marker="enumerate"/>
228
+ # <filter class='categorical' column='[federated.1astm0q1hl2ydc1dyqhqq0igvxkp].[none:Business Line:nk]' context='true'>
229
+ # <groupfilter function='level-members'
230
+ # level='[none:Business Line:nk]'
231
+ # user:ui-enumeration='all'
232
+ # user:ui-exclude='true'
233
+ # user:ui-marker='enumerate' />
234
+ # </filter>
235
+ # <filter class='categorical' column='[federated.1astm0q1hl2ydc1dyqhqq0igvxkp].[none:Business Line:nk]' context='true'>
236
+ # <groupfilter function='level-members'
237
+ # level='[none:Business Line:nk]'
238
+ # user:ui-enumeration='all'
239
+ # user:ui-marker='enumerate' />
240
+ # </filter>
241
+ if 'level-members'.eql? function
242
+ emit "HANDLING level-members FILTER"
243
+ inclorexcl = firstChild.has_attribute?('user:ui-exclude') ? 'Exclusive' : 'Inclusive'
244
+ $sheetFieldsCSV << [ @twb.name, sheet.name, 'single', inclorexcl, dataSource.uiname, field, operation, enumerate ]
245
+ end
90
246
  #-- otherwise filter contains multiple elements
91
247
  #-- handle individual member elements
92
248
  elements = firstChild.xpath('.//groupfilter')
93
249
  elements.each do |element|
94
250
  function = element.attribute('function').text
95
- emit "firstCHild\nfunction: #{function}\n node:\n#{element}"
251
+ emit "element function: #{function}\n node:\n#{element}"
96
252
  if 'member'.eql? function
97
- name = element.attribute('member').text.gsub(/^"|"$/,'')
98
- emit "%%%% NAME:: #{name}"
253
+ member = element.attribute('member').text
254
+ name = measureNames ? dataSource.fieldUIName(Twb::CodedField.new(member).name) : member.gsub(/^"|"$/,'')
255
+ emit "%%%% member NAME:: #{name}"
256
+ if '%null%' == name then name = 'Null' end
99
257
  alia = dataSource.fieldAlias(field,name) # $TableNameAliases[name]
100
258
  results[name] = alia
101
259
  end
102
260
  if 'range'.eql? function
261
+ emit "%%%% range element:: #{element}"
103
262
  t = filtersFromRangeNode(dataSource, field, element)
263
+
264
+ if t.empty?
265
+ from = element['from']
266
+ to = element['to']
267
+ range = "#{from} ... #{to}"
268
+ results[range] = range
269
+ end
270
+
104
271
  t.each do |name,alia|
105
- emit "RANGE ELEMENT Name: %-20s ALIAS: %-s " % [name, alia]
272
+ emit "%%%% range Name: %-20s ALIAS: %-s " % [name, alia]
106
273
  results[name] = dataSource.fieldAlias(field,name)
107
274
  end
108
275
  end
109
276
  end
110
277
  results.each do |name,alia|
111
- $sheetFieldsCSV << [ @twb.name, sheet.name, 'single', dataSource.uiname, field, name, alia, !name.eql?(alia) ]
278
+ $sheetFieldsCSV << [ @twb.name, sheet.name, 'single',operation, dataSource.uiname, field, name, alia, !name.eql?(alia) ]
112
279
  end
113
280
  end
114
281
  return results
115
282
  end
116
283
 
284
+ def processMeasureNames sheet, dataSource, field, node
285
+ emit 'processMeasureNames'
286
+ end
287
+
117
288
  def filtersFromRangeNode dataSource, field, node
118
289
  unless @twbDomainsLoaded
119
290
  loadDomains
@@ -134,11 +305,11 @@ module Analysis
134
305
  dsFields = @twbFielddomains[dataSource.uiname]
135
306
  emit "dsFields : #{dsFields}"
136
307
  if dsFields.nil? || dsFields.empty?
137
- emit "#### ALERT - FIELD DOMAIN VALUES FOR DATASOURCE #{dataSource.uiname} NOT LOADED ####"
138
- emit @twbFieldDomains
139
- emit "==========="
140
- emit dsFields
141
- emit "==========="
308
+ alert "#### ALERT #### - '#{field}' FIELD DOMAIN VALUES FOR '#{@twb.name} DATASOURCE #{dataSource.uiname} NOT LOADED ####"
309
+ emit @twbFieldDomains
310
+ emit "==========="
311
+ emit dsFields
312
+ emit "==========="
142
313
  else
143
314
  fieldVals = dsFields[field].to_a
144
315
  if dataSource.fieldHasAliases field
@@ -191,23 +362,10 @@ module Analysis
191
362
  emit "def loadDomains\n=="
192
363
  loader = Twb::Util::FieldDomainLoader.new
193
364
  @twbFielddomains = loader.loadWorkbook @twb
194
- emit "#{@twbFielddomains}\n=="
365
+ emit "FIELD DOMAINS:: #{@twbFielddomains}\n=="
195
366
  @twbDomainsLoaded = true
196
367
  end
197
368
 
198
- # def emit(local=@localEmit, stuff)
199
- # if stuff.is_a? String then
200
- # lines = stuff.split(/\n/)
201
- # lines.each do |line|
202
- # @ttlogfile.puts "#{$emitPrefix}#{line}"
203
- # puts "#{$emitPrefix}#{line}" if local
204
- # end
205
- # else
206
- # @ttlogfile.puts "#{$emitPrefix}#{stuff}"
207
- # puts "#{$emitPrefix}#{stuff}" if local
208
- # end
209
- # end
210
-
211
369
  end # class
212
370
 
213
371
  end # module Twb