twb 3.7.5 → 3.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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