twb 4.6.3 → 4.7.1
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.
- checksums.yaml +4 -4
- data/lib/twb.rb +3 -1
- data/lib/twb/analysis/calculatedfields/calculatedfieldsanalyzer.rb +34 -29
- data/lib/twb/analysis/calculatedfields/groupfieldsanalyzer.rb +1 -1
- data/lib/twb/analysis/datasources/categoricalcolorcodinganalyzer.rb +1 -1
- data/lib/twb/analysis/datasources/datasourcefieldsanalyzer.rb +117 -33
- data/lib/twb/analysis/datasources/datasourceoriginsanalyzer.rb +1 -1
- data/lib/twb/analysis/datasources/datasourcesenumerator.rb +103 -0
- data/lib/twb/analysis/datasources/datasourceslocationsanalyzer.rb +1 -1
- data/lib/twb/analysis/datasources/googlesheetdatasourcesanalyzer.rb +1 -1
- data/lib/twb/analysis/sheets/dashsheetsanalyzer.rb +6 -5
- data/lib/twb/analysis/sheets/sheetfieldsanalyzer.rb +2 -1
- data/lib/twb/analysis/sheets/sheetsintooltipanalyzer.rb +114 -0
- data/lib/twb/analysis/sheets/sheetsourcesanalyzer.rb +1 -1
- data/lib/twb/analysis/workbooksummaryanalyzer.rb +1 -1
- data/lib/twb/columnfield.rb +37 -5
- data/lib/twb/datasource.rb +44 -34
- data/lib/twb/fieldcalculation.rb +6 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ba8a4bcf6b220aaf9d3b94a91a903ec055a1637d7d78c063a58a5def3027844
|
4
|
+
data.tar.gz: a7f76a9e0fb057cf7aee2c38c82b974e0a59575eb955bd41636e79a38a251f65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 685229ff4ae9181176c42fb85cf9900e64823fa2cbcf4a8ef49a48b0ebbfa96ecf491e27e88c545478a2e4ccad0ba314f648baa28406964032d39e3daefe0904
|
7
|
+
data.tar.gz: 86eb832a11d3bb87a66835f29d5eb312477f55421e007dea39c8178568573d39f0ff5d529ab22dfbfe53128b6a1b1145948b5f92bd785cc780b57c3df76d7137
|
data/lib/twb.rb
CHANGED
@@ -57,6 +57,7 @@ require_relative 'twb/analysis/calculatedfields/groupfieldsanalyzer'
|
|
57
57
|
require_relative 'twb/analysis/calculatedfields/markdownemitter'
|
58
58
|
require_relative 'twb/analysis/calculatedfields/csvemitter'
|
59
59
|
# require_relative 'twb/analysis/datasources/datasourcefieldscsvemitter'
|
60
|
+
require_relative 'twb/analysis/datasources/datasourcesenumerator.rb'
|
60
61
|
require_relative 'twb/analysis/datasources/datasourcefieldsanalyzer'
|
61
62
|
require_relative 'twb/analysis/datasources/datasourcetablefieldscsvemitter'
|
62
63
|
require_relative 'twb/analysis/datasources/categoricalcolorcodinganalyzer'
|
@@ -67,10 +68,11 @@ require_relative 'twb/analysis/sheets/worksheetdatastructurecsvemitter'
|
|
67
68
|
require_relative 'twb/analysis/sheets/sheetfiltersanalyzer'
|
68
69
|
require_relative 'twb/analysis/sheets/sheetfieldsanalyzer'
|
69
70
|
require_relative 'twb/analysis/sheets/dashsheetsanalyzer'
|
71
|
+
require_relative 'twb/analysis/sheets/sheetsintooltipanalyzer'
|
70
72
|
|
71
73
|
|
72
74
|
# Represents Tableau Workbooks, their contents, and classes that analyze and manipulate them.
|
73
75
|
#
|
74
76
|
module Twb
|
75
|
-
VERSION = '4.
|
77
|
+
VERSION = '4.7.1'
|
76
78
|
end
|
@@ -43,7 +43,8 @@ module Analysis
|
|
43
43
|
'Data Type', 'Role', 'Type',
|
44
44
|
'Class',
|
45
45
|
'Scope Isolation',
|
46
|
-
'Formula
|
46
|
+
'Formula # Chars',
|
47
|
+
'Formula # Lines',
|
47
48
|
'Formula',
|
48
49
|
'Formula (tech)',
|
49
50
|
'Formula Comments',
|
@@ -51,7 +52,7 @@ module Analysis
|
|
51
52
|
]
|
52
53
|
|
53
54
|
@@calcLinesCSVFileName = 'CalculatedFieldsFormulaLines.csv'
|
54
|
-
@@calcLinesCSVFileHeader = ['
|
55
|
+
@@calcLinesCSVFileHeader = ['Calc Field #',
|
55
56
|
'Workbook',
|
56
57
|
# 'Workbook Modified',
|
57
58
|
'Data Source', 'Data Source Caption', 'Data Source Name (tech)',
|
@@ -94,9 +95,8 @@ DOTHEADER
|
|
94
95
|
init
|
95
96
|
@funcdoc = {:class=>self.class, :blurb=>'Analyze Calculated Fields', :description=>'Calculated fields can be complex, this tool provides robust coverage.',}
|
96
97
|
#-- CSV records collectors
|
97
|
-
@
|
98
|
-
@
|
99
|
-
@csvFormulaLines = Array.new
|
98
|
+
@csvFormulaFields = Set.new
|
99
|
+
@csvFormulaLines = Set.new
|
100
100
|
#-- Counters setup --
|
101
101
|
@twbCount = 0
|
102
102
|
@dataSourcesCount = 0
|
@@ -153,7 +153,6 @@ DOTHEADER
|
|
153
153
|
|
154
154
|
def loadMetrics
|
155
155
|
@metrics = {
|
156
|
-
'# of Workbooks' => @twbCount,
|
157
156
|
'# of Data Sources' => @dataSourcesCount,
|
158
157
|
'# of Calculated Fields' => @calculatedFieldsCount,
|
159
158
|
'# of Referenced Fields' => @referencedFieldsCount,
|
@@ -166,7 +165,7 @@ DOTHEADER
|
|
166
165
|
|
167
166
|
def emitGml
|
168
167
|
gml = Twb::Util::GML.new
|
169
|
-
gml.fileName = @twb.name
|
168
|
+
gml.fileName = "#{@twb.name}.calculatedFields"
|
170
169
|
gml.nodes = @nodes
|
171
170
|
gml.edges = @edges
|
172
171
|
gmlFile = gml.render # resets gml.fileName in preparing it for output
|
@@ -182,6 +181,8 @@ DOTHEADER
|
|
182
181
|
calculatedFields = SortedSet.new
|
183
182
|
fieldFormulaLines = Array.new
|
184
183
|
referencedFields = SortedSet.new
|
184
|
+
@csvCalculatedFields = Set.new
|
185
|
+
|
185
186
|
# if @doGraph
|
186
187
|
dataSourceNode = Twb::Util::Graphnode.new(name: ds.uiname, id: ds.id, type: ds, properties: {workbook: @twb.name})
|
187
188
|
@nodes.add dataSourceNode
|
@@ -200,28 +201,32 @@ DOTHEADER
|
|
200
201
|
calculation = calcField.calculation
|
201
202
|
if calculation.has_formula
|
202
203
|
#-- collect field formulas as single lines
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
204
|
+
csvRec = [
|
205
|
+
@calculatedFieldsCount += 1,
|
206
|
+
@twb.name,
|
207
|
+
# @modTime,
|
208
|
+
ds.uiname,
|
209
|
+
ds.caption,
|
210
|
+
ds.name,
|
211
|
+
calcField.uiname,
|
212
|
+
calcField.caption,
|
213
|
+
calcField.name,
|
214
|
+
ds.name + '::' + calcField.name,
|
215
|
+
calcField.datatype,
|
216
|
+
calcField.role,
|
217
|
+
calcField.type,
|
218
|
+
calculation.class,
|
219
|
+
calculation.scopeIsolation,
|
220
|
+
calculation.formulaFlat.length,
|
221
|
+
calculation.formulaLinesCount,
|
222
|
+
calculation.formulaFlatResolved,
|
223
|
+
calculation.formulaFlat.strip,
|
224
|
+
calculation.comments,
|
225
|
+
calculation.is_lod
|
226
|
+
]
|
227
|
+
# puts csvRec.to_s
|
228
|
+
# puts "incl? #{@csvCalculatedFields.include?(csvRec)}"
|
229
|
+
@csvCalculatedFields << csvRec
|
225
230
|
#-- collect individual formula lines
|
226
231
|
flnum = 0
|
227
232
|
emit "@@ calcField.uiname: #{calcField.uiname}"
|
@@ -49,6 +49,7 @@ module DataSources
|
|
49
49
|
'Workbook',
|
50
50
|
'Data Source',
|
51
51
|
'Field',
|
52
|
+
'Field - tech',
|
52
53
|
'Field - source',
|
53
54
|
'Field - class',
|
54
55
|
'Field - path',
|
@@ -56,21 +57,31 @@ module DataSources
|
|
56
57
|
'Property - Value'
|
57
58
|
]
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
60
|
+
@@colsFileType = 'DataSourceFields'
|
61
|
+
@@colsFileName = @@colsFileType + '.csv'
|
62
|
+
@@colsHeader = [
|
63
|
+
'Field #',
|
64
|
+
'Workbook',
|
65
|
+
'Data Source',
|
66
|
+
'Field - tech',
|
67
|
+
'Caption',
|
68
|
+
'Field',
|
69
|
+
'Alias',
|
70
|
+
'Datatype',
|
71
|
+
'Datatype-customized',
|
72
|
+
'Default Format',
|
73
|
+
'Param-domain-type',
|
74
|
+
'Role',
|
75
|
+
'Type',
|
76
|
+
'Calculated?',
|
77
|
+
'Value',
|
78
|
+
'Hidden',
|
79
|
+
'Path'
|
80
|
+
]
|
70
81
|
|
71
82
|
def initialize(**args)
|
83
|
+
emit "DataSourceFieldsAnalyzer initialize(**args) args: #{args}"
|
72
84
|
@args = args
|
73
|
-
# puts "@args: #{@args}"
|
74
85
|
@recordDir = !@args.nil? && @args[:recordDir] == true
|
75
86
|
@ttdocdir = @args[:ttdocdir]
|
76
87
|
@csvAdd = args[:csvMode] == :add
|
@@ -78,36 +89,28 @@ module DataSources
|
|
78
89
|
init
|
79
90
|
@funcdoc = {:class=>self.class, :blurb=>'Analyze Data Source Fields', :description=>'Documents Data Source fields, with (some) technical information.',}
|
80
91
|
#--
|
81
|
-
|
82
|
-
@csvFile
|
92
|
+
detailsFileName = docFile(@@fullFileName)
|
93
|
+
@csvFile = CSV.open( detailsFileName,@csvMode )
|
94
|
+
#--
|
95
|
+
colsFileName = docFile(@@colsFileName)
|
96
|
+
@colsFile = CSV.open( colsFileName, @csvMode )
|
97
|
+
|
98
|
+
#--
|
83
99
|
unless @csvAdd
|
84
100
|
if @recordDir
|
85
101
|
@csvFile << @@fullHeader + ['Workbook Dir']
|
102
|
+
@colsFile << @@colsHeader + ['Workbook Dir']
|
86
103
|
else
|
87
104
|
@csvFile << @@fullHeader
|
105
|
+
@colsFile << @@colsHeader
|
88
106
|
end
|
89
107
|
end
|
90
|
-
addDocFile @csvFile,
|
108
|
+
addDocFile @csvFile, detailsFileName, "Workbooks, Data Sources, and Fields' details (technical use)."
|
109
|
+
addDocFile @colsFile, colsFileName, "Workbooks, Data Sources, and Column Fields."
|
91
110
|
#--
|
92
|
-
# --
|
93
111
|
@dsCount = 0
|
94
112
|
@recNum = 0
|
95
|
-
|
96
|
-
|
97
|
-
def initializeX
|
98
|
-
# @csvFile = CSV.open(@@csvFileName,'w')
|
99
|
-
# @csvFile << @@csvHeader
|
100
|
-
# @csvRecords = Set.new
|
101
|
-
# puts "Opened: #{!@csvFile.nil?} - #{@@csvFileName}"
|
102
|
-
# --
|
103
|
-
@csvFileFull = CSV.open(@@fullFileName,'w')
|
104
|
-
@csvFileFull << @@fullHeader
|
105
|
-
puts "Opened: #{!@csvFileFull.nil?} - #{@@fullFileName} "
|
106
|
-
# --
|
107
|
-
# @csvFileTech = CSV.open(@@techFileName,'w')
|
108
|
-
# @csvFileTech << @@techHeader
|
109
|
-
# puts "Opened: #{!@csvFileTech.nil?} - #{@@techFileName}"
|
110
|
-
# --
|
113
|
+
@colFldNum = 0
|
111
114
|
end
|
112
115
|
|
113
116
|
def self.csvHeader
|
@@ -126,12 +129,15 @@ module DataSources
|
|
126
129
|
@twb = nil
|
127
130
|
@twb = twb if twb.instance_of? Twb::Workbook
|
128
131
|
@twb = Twb::Workbook.new(twb) if twb.instance_of? String
|
132
|
+
@twbName = @twb.name
|
133
|
+
@twbDir = @twb.dir
|
129
134
|
raise ArgumentError.new("ERROR in Workbok processing: '#{twb}' must be a Workbook (class) or the name of a Workbook (String), is a #{twb.class} \n ") unless @twb.is_a? Twb::Workbook
|
130
135
|
# --
|
131
136
|
dss = @twb.datasources
|
132
137
|
@fieldCnt = 0
|
133
138
|
dss.each do |ds|
|
134
139
|
@dsname = ds.uiname
|
140
|
+
processColumnFields ds
|
135
141
|
# puts "\n -- #{ds.uiname} "
|
136
142
|
# tables = Set.new
|
137
143
|
# fields = {}
|
@@ -178,6 +184,44 @@ module DataSources
|
|
178
184
|
end
|
179
185
|
end # def processTwb twb
|
180
186
|
|
187
|
+
def processColumnFields ds
|
188
|
+
deepFields = {}
|
189
|
+
recordedFields = {}
|
190
|
+
ds.columnFields.each do |field|
|
191
|
+
if field.pathbare.eql? '/workbook/datasources/datasource/column'
|
192
|
+
recordColField field
|
193
|
+
recordedFields[field.name] = field
|
194
|
+
# print '.'
|
195
|
+
else
|
196
|
+
deepFields[field.name] = field
|
197
|
+
# print '-'
|
198
|
+
end
|
199
|
+
end
|
200
|
+
deepFields.each do |name,field|
|
201
|
+
unless recordedFields.has_key? name
|
202
|
+
recordColField(field)
|
203
|
+
recordedFields[field.name] = field
|
204
|
+
# print ':'
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# @@colsHeader = ['Field #',
|
210
|
+
# 'Workbook',
|
211
|
+
# 'Data Source',
|
212
|
+
# 'Field - tech',
|
213
|
+
# 'caption',
|
214
|
+
# 'uiname',
|
215
|
+
# 'alias',
|
216
|
+
# 'datatype',
|
217
|
+
# 'datatype-customized',
|
218
|
+
# 'default-format',
|
219
|
+
# 'param-domain-type',
|
220
|
+
# 'role',
|
221
|
+
# 'type',
|
222
|
+
# 'value'
|
223
|
+
# ]
|
224
|
+
|
181
225
|
def metrics
|
182
226
|
{
|
183
227
|
'# of Data Sources' => @dsCount,
|
@@ -185,8 +229,47 @@ module DataSources
|
|
185
229
|
}
|
186
230
|
end
|
187
231
|
|
232
|
+
|
233
|
+
|
188
234
|
private
|
189
235
|
|
236
|
+
# def recordCSV record
|
237
|
+
# numberedRec = [@recNum+=1] + record
|
238
|
+
# if @recordDir
|
239
|
+
# @csvFile << numberedRec.push(@twb.dir)
|
240
|
+
# else
|
241
|
+
# @csvFile << numberedRec
|
242
|
+
# end
|
243
|
+
# end
|
244
|
+
|
245
|
+
def recordColField field
|
246
|
+
numberedRec = [
|
247
|
+
@colFldNum +=1,
|
248
|
+
@twbName,
|
249
|
+
@dsname,
|
250
|
+
field.name,
|
251
|
+
field.caption,
|
252
|
+
field.uiname,
|
253
|
+
field.alias,
|
254
|
+
field.dataType,
|
255
|
+
field.datatypeCustomized,
|
256
|
+
field.defaultFormat,
|
257
|
+
field.paramDomainType,
|
258
|
+
field.role,
|
259
|
+
field.type,
|
260
|
+
!field.calcField.nil?,
|
261
|
+
field.value,
|
262
|
+
field.hidden,
|
263
|
+
field.pathbare
|
264
|
+
]
|
265
|
+
#--
|
266
|
+
if @recordDir
|
267
|
+
@colsFile << numberedRec.push(@twb.dir)
|
268
|
+
else
|
269
|
+
@colsFile << numberedRec
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
190
273
|
def recordFieldFull field, source
|
191
274
|
# print field.properties.nil? ? '-' : ":#{field.properties.length}"
|
192
275
|
# puts field.properties
|
@@ -198,13 +281,14 @@ module DataSources
|
|
198
281
|
# @twb.dir,
|
199
282
|
@dsname,
|
200
283
|
field.uiname,
|
284
|
+
field.name,
|
201
285
|
source,
|
202
286
|
field.class,
|
203
287
|
field.node.path.to_s.gsub(/[0-9]/,'').gsub('[]',''),
|
204
288
|
name,
|
205
289
|
value
|
206
290
|
]
|
207
|
-
csvRec
|
291
|
+
csvRec << @twb.dir if @recordDir
|
208
292
|
@csvFile << csvRec
|
209
293
|
end
|
210
294
|
end
|
@@ -0,0 +1,103 @@
|
|
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
|
+
module DataSources
|
22
|
+
|
23
|
+
|
24
|
+
class DataSourcesEnumerator
|
25
|
+
|
26
|
+
include TabTool
|
27
|
+
|
28
|
+
attr_accessor :localEmit
|
29
|
+
|
30
|
+
def initialize(**args)
|
31
|
+
@args = args
|
32
|
+
@recordDir = !@args.nil? && @args[:recordDir] == true
|
33
|
+
@ttdocdir = @args[:ttdocdir]
|
34
|
+
@csvAdd = args[:csvMode] == :add
|
35
|
+
@csvMode = @csvAdd ? 'a' : 'w'
|
36
|
+
init
|
37
|
+
@funcdoc = {:class=>self.class, :blurb=>"Enumerate Workbooks' Data Sources", :description=>'Documents Data Sources, recoring their names.',}
|
38
|
+
#--
|
39
|
+
docFileName = docFile('DataSources.csv')
|
40
|
+
@sheetFieldsCSV = CSV.open( docFileName,@csvMode)
|
41
|
+
unless @csvAdd
|
42
|
+
if @recordDir
|
43
|
+
@sheetFieldsCSV << ['Rec #','Workbook','Data Source','Data Source (tech)','Class','Extract?','# Connections','# Fields','Published?', '# Tables', '# Aliases', '# Groups', 'Workbook Dir']
|
44
|
+
else
|
45
|
+
@sheetFieldsCSV << ['Rec #','Workbook','Data Source','Data Source (tech)','Class','Extract?','# Connections','# Fields','Published?', '# Tables', '# Aliases', '# Groups']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
addDocFile @sheetFieldsCSV, docFileName, "Workbooks and the Data Sources they contain."
|
49
|
+
#--
|
50
|
+
@dsCount = 0
|
51
|
+
@recNum = 0
|
52
|
+
end
|
53
|
+
|
54
|
+
def metrics
|
55
|
+
{
|
56
|
+
'# of Data Sources' => @dsCount,
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def processTWB twb
|
61
|
+
@twb = twb
|
62
|
+
@twbName = @twb.name
|
63
|
+
@twbDir = @twb.dir
|
64
|
+
emit " -- #{@twbName}"
|
65
|
+
parseDataSources
|
66
|
+
end
|
67
|
+
|
68
|
+
def parseDataSources
|
69
|
+
@dataSources = @twb.datasources
|
70
|
+
@dataSources.each do |ds|
|
71
|
+
@dsCount += 1
|
72
|
+
recordCSV [ @twbName,
|
73
|
+
ds.uiname,
|
74
|
+
ds.name,
|
75
|
+
ds.dsclass,
|
76
|
+
ds.isExtract,
|
77
|
+
ds.connections.length,
|
78
|
+
ds.allFields.length,
|
79
|
+
ds.isPublished,
|
80
|
+
ds.tables.length,
|
81
|
+
ds.aliases.length,
|
82
|
+
ds.groups.length
|
83
|
+
]
|
84
|
+
# [@twbName, @modTime, sheet.name, filter.type, filter.inexclude, filter.dataSource.uiname, filter.uiname, nil, nil, nil, filter.inexMode, filter.includeNull, filter.kind]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def recordCSV record
|
91
|
+
numberedRec = [@recNum+=1] + record
|
92
|
+
if @recordDir
|
93
|
+
@sheetFieldsCSV << numberedRec.push(@twbDir)
|
94
|
+
else
|
95
|
+
@sheetFieldsCSV << numberedRec
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end # class DataSourcesEnumeratorA
|
100
|
+
|
101
|
+
end # module DataSources
|
102
|
+
end # module Analysis
|
103
|
+
end # module Twb
|
@@ -38,9 +38,9 @@ module Analysis
|
|
38
38
|
@dashSheetsCSV = CSV.open(docFileName,@csvMode)
|
39
39
|
unless @csvAdd
|
40
40
|
if @recordDir
|
41
|
-
@dashSheetsCSV << ['Rec #','Workbook','
|
41
|
+
@dashSheetsCSV << ['Rec #','Workbook','Dashboard','Worksheet','Hidden','Visible', 'Workbook Dir']
|
42
42
|
else
|
43
|
-
@dashSheetsCSV << ['Rec #','Workbook','
|
43
|
+
@dashSheetsCSV << ['Rec #','Workbook','Dashboard','Worksheet','Hidden','Visible' ]
|
44
44
|
end
|
45
45
|
end
|
46
46
|
addDocFile @dashSheetsCSV, docFileName, "Workbooks, Dashboards, and their Worksheets"
|
@@ -53,7 +53,7 @@ module Analysis
|
|
53
53
|
|
54
54
|
def metrics
|
55
55
|
{
|
56
|
-
'# of Workbooks' => @twbCount,
|
56
|
+
# '# of Workbooks' => @twbCount,
|
57
57
|
'# of Dashboards' => @dashCount,
|
58
58
|
'# of Worksheets' => @sheetCount
|
59
59
|
}
|
@@ -77,8 +77,9 @@ module Analysis
|
|
77
77
|
@dashCount += 1
|
78
78
|
dash.worksheets.each do |sheet|
|
79
79
|
@sheetCount += 1
|
80
|
-
|
81
|
-
recordCSV [@twbName,
|
80
|
+
# puts "SHEET: #{sheet} -> #{sheet.name}"
|
81
|
+
recordCSV [@twbName, dash.name, sheet.name, sheet.hidden, sheet.visible ]
|
82
|
+
# recordCSV [@twbName, @modTime, dash.name, sheet.name, sheet.hidden, sheet.visible ]
|
82
83
|
end
|
83
84
|
end
|
84
85
|
end
|
@@ -47,6 +47,7 @@ module Analysis
|
|
47
47
|
@ttdocdir = @args[:ttdocdir]
|
48
48
|
@csvAdd = args[:csvMode] == :add
|
49
49
|
@csvMode = @csvAdd ? 'a' : 'w'
|
50
|
+
# @localEmit = true
|
50
51
|
init
|
51
52
|
@funcdoc = {:class=>self.class, :blurb=>'Analyze Worksheet Fields', :description=>nil,}
|
52
53
|
#--
|
@@ -69,7 +70,7 @@ module Analysis
|
|
69
70
|
|
70
71
|
def metrics
|
71
72
|
{
|
72
|
-
'# of Workbooks' => @twbcount,
|
73
|
+
# '# of Workbooks' => @twbcount,
|
73
74
|
'# of Worksheets' => @sheetCnt,
|
74
75
|
'# of Worksheet Fields' => @fieldsCnt
|
75
76
|
}
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# sheetfieldsanalyzer.rb Copyright (C) 2019 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 SheetsInTooltipAnalyzer
|
23
|
+
|
24
|
+
include TabTool
|
25
|
+
|
26
|
+
def initialize(**args)
|
27
|
+
@args = args
|
28
|
+
@recordDir = !@args.nil? && @args[:recordDir] == true
|
29
|
+
@ttdocdir = @args[:ttdocdir]
|
30
|
+
@csvAdd = !@args.nil? && args[:csvMode] == :add
|
31
|
+
@csvMode = @csvAdd ? 'a' : 'w'
|
32
|
+
init
|
33
|
+
@funcdoc = {:class=>self.class, :blurb=>'Tooltip Vizzes', :description=>'Identifies the vizzes used in tooltips.',}
|
34
|
+
#--
|
35
|
+
docFileName = docFile('SheetsInTooltips.csv')
|
36
|
+
@csvFile = CSV.open(docFileName,@csvMode)
|
37
|
+
unless @csvAdd
|
38
|
+
if @recordDir
|
39
|
+
@csvFile << ['Rec #','Workbook','Worksheet - Host','Worksheet - Toolip viz','Max Width','Max Height', 'Workbook Dir']
|
40
|
+
else
|
41
|
+
@csvFile << ['Rec #','Workbook','Worksheet - Host','Worksheet - Toolip viz','Max Height','Max Width']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
addDocFile @csvFile, docFileName, "Workbooks, Worksheets with Vizzes in their Tooltips"
|
45
|
+
#--
|
46
|
+
@twbCount = 0
|
47
|
+
@sheetCount = 0
|
48
|
+
@ttvizCount = 0
|
49
|
+
@recNum = 0
|
50
|
+
end
|
51
|
+
|
52
|
+
def metrics
|
53
|
+
{
|
54
|
+
# '# of Workbooks' => @twbCount,
|
55
|
+
'# of Worksheets' => @sheetCount,
|
56
|
+
'# of Worksheets in Tooltips' => @ttvizCount
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def processTWB twb
|
61
|
+
@twb = twb
|
62
|
+
@twbName = @twb.name
|
63
|
+
@twbDir = @twb.dir
|
64
|
+
@modTime = @twb.modtime
|
65
|
+
emit " -- #{@twbName}"
|
66
|
+
@twbCount += 1
|
67
|
+
parseSheets
|
68
|
+
finis
|
69
|
+
end
|
70
|
+
|
71
|
+
def parseSheets
|
72
|
+
@sheets = @twb.worksheets
|
73
|
+
puts " #Sheets: #{@sheets.length}"
|
74
|
+
@sheetCount += @sheets.length
|
75
|
+
@sheets.each do |sheet|
|
76
|
+
puts "SHEET:: #{sheet.name}"
|
77
|
+
# @dashCount += 1
|
78
|
+
ttip = sheet.tooltip
|
79
|
+
runs = ttip.xpath('.//run')
|
80
|
+
runs.each do |run|
|
81
|
+
# puts run.text
|
82
|
+
text = run.text
|
83
|
+
isViz = text =~ /.*<Sheet[ ]+name=.*>.*/
|
84
|
+
puts "\t #{text}"
|
85
|
+
if isViz
|
86
|
+
vizCode = text.gsub(/^[^<]*/,'').gsub(/[^>]*$/,'')
|
87
|
+
vizNode = Nokogiri::XML(vizCode).at_xpath('./Sheet')
|
88
|
+
puts "\t #{vizCode}"
|
89
|
+
puts "\t #{vizNode.class} \t #{vizNode}"
|
90
|
+
recordCSV [@twbName, sheet.name, vizNode['name'], vizNode['maxwidth'], vizNode['maxheight']]
|
91
|
+
end
|
92
|
+
|
93
|
+
# @sheetCount += 1
|
94
|
+
# recordCSV [@twbName, dash.name, sheet.name, sheet.hidden, sheet.visible ]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def recordCSV record
|
102
|
+
numberedRec = [@recNum+=1] + record
|
103
|
+
if @recordDir
|
104
|
+
@csvFile << numberedRec.push(@twbDir)
|
105
|
+
else
|
106
|
+
@csvFile << numberedRec
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
end #class SheetFieldsAnalyzer
|
112
|
+
|
113
|
+
end # module Analysis
|
114
|
+
end # module Twb
|
data/lib/twb/columnfield.rb
CHANGED
@@ -21,6 +21,16 @@ module Twb
|
|
21
21
|
|
22
22
|
include Comparable
|
23
23
|
|
24
|
+
@@dataTypeRoleMap = {
|
25
|
+
'date' => :dimension,
|
26
|
+
'datetime' => :dimension,
|
27
|
+
'boolean' => :dimension,
|
28
|
+
'integer' => :measure,
|
29
|
+
'float' => :measure,
|
30
|
+
'real' => :measure,
|
31
|
+
'string' => :dimension,
|
32
|
+
}
|
33
|
+
|
24
34
|
# XML Attributes known to exist in TWB xml
|
25
35
|
# ----------------------------------------
|
26
36
|
# aggregation
|
@@ -48,15 +58,15 @@ module Twb
|
|
48
58
|
# visual-totals
|
49
59
|
|
50
60
|
attr_reader :node, :properties
|
51
|
-
attr_reader :name, :caption, :uiname
|
61
|
+
attr_reader :name, :caption, :uiname, :alias
|
52
62
|
attr_reader :dataType, :defaultFormat, :paramDomainType
|
53
63
|
attr_reader :role, :type, :value
|
54
64
|
attr_reader :alias, :semanticRole, :aggregation
|
55
65
|
attr_reader :autoColumn, :hidden, :datatypeCustomized
|
56
66
|
attr_reader :calcField
|
67
|
+
attr_reader :path
|
57
68
|
attr_reader :aliases
|
58
69
|
|
59
|
-
|
60
70
|
def initialize(fieldNode, datasource=nil)
|
61
71
|
@datasource = datasource
|
62
72
|
@node = fieldNode
|
@@ -67,16 +77,14 @@ module Twb
|
|
67
77
|
@userDataType = load 'user-datatype'
|
68
78
|
@defaultFormat = load 'default-format'
|
69
79
|
@paramDomainType = load 'param-domain-type'
|
70
|
-
@role = load 'role'
|
71
80
|
@type = load 'type'
|
72
81
|
@value = load 'value'
|
73
82
|
@alias = load 'alias'
|
74
83
|
@semanticRole = load 'semantic-role'
|
75
84
|
@aggregation = load 'aggregation'
|
76
85
|
@autoColumn = load 'auto-column'
|
77
|
-
@hidden = load 'hidden'
|
78
86
|
@datatypeCustomized = load 'datatype-customized'
|
79
|
-
@calcField = loadCalcField
|
87
|
+
# @calcField = loadCalcField
|
80
88
|
end
|
81
89
|
|
82
90
|
def id
|
@@ -89,10 +97,26 @@ module Twb
|
|
89
97
|
return val
|
90
98
|
end
|
91
99
|
|
100
|
+
def role
|
101
|
+
@role ||= loadRole
|
102
|
+
end
|
103
|
+
|
104
|
+
def loadRole
|
105
|
+
role = @node['role']
|
106
|
+
if role.nil?
|
107
|
+
role = @@dataTypeRoleMap[@dataType].nil? ? @dataType : @@dataTypeRoleMap[@dataType]
|
108
|
+
end
|
109
|
+
return role
|
110
|
+
end
|
111
|
+
|
92
112
|
def calcField
|
93
113
|
@calcField ||= loadCalcField
|
94
114
|
end
|
95
115
|
|
116
|
+
def hidden
|
117
|
+
@hidden ||= load('hidden').nil? ? false : true
|
118
|
+
end
|
119
|
+
|
96
120
|
def loadCalcField
|
97
121
|
if !@node.at_xpath('./calculation').nil?
|
98
122
|
@calcField = Twb::CalculatedField.new @node, @datasource
|
@@ -101,6 +125,14 @@ module Twb
|
|
101
125
|
end
|
102
126
|
end
|
103
127
|
|
128
|
+
def path
|
129
|
+
@path ||= @node.path.gsub(/\[[0-9]+\]/,'[]')
|
130
|
+
end
|
131
|
+
|
132
|
+
def pathbare
|
133
|
+
@pathbare ||= path().gsub(/\[\]/,'')
|
134
|
+
end
|
135
|
+
|
104
136
|
def comment
|
105
137
|
@commentLines ||= loadComment
|
106
138
|
end
|
data/lib/twb/datasource.rb
CHANGED
@@ -69,7 +69,7 @@ module Twb
|
|
69
69
|
@caption = @node.attr('caption')
|
70
70
|
@uiname = @caption.nil? ? @name : @caption
|
71
71
|
# puts "DATASOURCE: #{@uiname}"
|
72
|
-
processConnection
|
72
|
+
# processConnection
|
73
73
|
# processFilters
|
74
74
|
loadTableFields
|
75
75
|
loadFieldUINames
|
@@ -111,41 +111,44 @@ module Twb
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def processConnections
|
114
|
-
|
115
|
-
processConnection
|
114
|
+
@connections = Array.new
|
115
|
+
processConnection './/connection'
|
116
|
+
processConnection './/named-connection'
|
117
|
+
return @connections
|
116
118
|
end
|
117
119
|
|
118
120
|
def processConnection path
|
119
121
|
conns = @node.xpath(path)
|
120
122
|
conns.each do |connNode|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
#
|
126
|
-
# puts
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
123
|
+
@connections << connNode
|
124
|
+
# connClass = @dsclass
|
125
|
+
# cpath = connNode.path
|
126
|
+
# connPath = cpath.gsub(/\[[0-9]+\]/,'')
|
127
|
+
# connPNum = /(\d+)/.match(cpath)
|
128
|
+
# # puts cpath, connPath
|
129
|
+
# # puts "CPATH: #{cpath}"
|
130
|
+
# conn.attributes.each do |name,value|
|
131
|
+
# # puts "\n\t\t - %-15s -> %-s" % [name, value]
|
132
|
+
# $csvFile << [ $recCount += 1,
|
133
|
+
# $twbName, $dir, $build, $version,
|
134
|
+
# $dsName, $dstype, $dsuiname, connClass,
|
135
|
+
# connPath, connPNum,
|
136
|
+
# name, value.value
|
137
|
+
# ]
|
138
|
+
# end
|
136
139
|
end
|
137
140
|
end
|
138
141
|
|
139
|
-
def processConnection
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
end
|
142
|
+
# def processConnection
|
143
|
+
# @connection = @node.at_xpath('./connection')
|
144
|
+
# @dsclass = @name # handles 'Parameters' data source, which has no connection element (& others if so)
|
145
|
+
# unless @connection.nil?
|
146
|
+
# @dsclass = @connection.attribute('class').text
|
147
|
+
# # note: must use "dsclass" as "class" would override Rubys ".class" Kernel method
|
148
|
+
# setConnectionHash
|
149
|
+
# loadTables @connection
|
150
|
+
# end
|
151
|
+
# end
|
149
152
|
|
150
153
|
# Notes:
|
151
154
|
# - TODO: need to determine which, if any, of the connection attributes should be
|
@@ -174,12 +177,19 @@ module Twb
|
|
174
177
|
@isPublished = !node.at_xpath('./repository-location').nil?
|
175
178
|
end
|
176
179
|
|
177
|
-
def
|
180
|
+
def tables
|
181
|
+
@tables ||= loadTables
|
182
|
+
end
|
183
|
+
|
184
|
+
def loadTables
|
178
185
|
@tables = {}
|
179
|
-
|
180
|
-
|
181
|
-
|
186
|
+
connections.each do |conn|
|
187
|
+
nodes = conn.xpath(".//relation[@type='table']")
|
188
|
+
nodes.each do |node|
|
189
|
+
@tables[node.attr('name')] = node.attr('table')
|
190
|
+
end
|
182
191
|
end
|
192
|
+
return @tables
|
183
193
|
end
|
184
194
|
|
185
195
|
def joinPairs
|
@@ -237,7 +247,7 @@ module Twb
|
|
237
247
|
def loadColumnFields
|
238
248
|
@columnFields = Set.new
|
239
249
|
@columnFieldsMap = {}
|
240
|
-
nodes = @node.xpath('
|
250
|
+
nodes = @node.xpath('.//column')
|
241
251
|
nodes.each do |n|
|
242
252
|
field = Twb::ColumnField.new n, self
|
243
253
|
@columnFields << field
|
@@ -467,7 +477,7 @@ module Twb
|
|
467
477
|
def loadAllFields
|
468
478
|
@allFields = SortedSet.new
|
469
479
|
dbf = dbFields
|
470
|
-
@allFields << dbf
|
480
|
+
@allFields << dbf
|
471
481
|
@allFields << calculatedFieldNames
|
472
482
|
end
|
473
483
|
|
data/lib/twb/fieldcalculation.rb
CHANGED
@@ -31,7 +31,7 @@ module Twb
|
|
31
31
|
attr_reader :formulaResolved
|
32
32
|
attr_reader :formulaFlat
|
33
33
|
attr_reader :formulaFlatResolved
|
34
|
-
attr_reader :formulaLines, :formulaResolvedLines
|
34
|
+
attr_reader :formulaLines, :formulaResolvedLines, :formulaLinesCount
|
35
35
|
attr_reader :is_tableCalc
|
36
36
|
attr_reader :is_lod, :lodCodePos
|
37
37
|
attr_reader :class, :scopeIsolation
|
@@ -176,6 +176,10 @@ module Twb
|
|
176
176
|
formulaResolved.split(/\n|\r\n/)
|
177
177
|
end
|
178
178
|
|
179
|
+
def formulaLinesCount
|
180
|
+
@formulaLinesCount ||= formulaLines.length
|
181
|
+
end
|
182
|
+
|
179
183
|
def enlineResolvedFormula
|
180
184
|
end
|
181
185
|
|
@@ -194,7 +198,7 @@ module Twb
|
|
194
198
|
# line.strip
|
195
199
|
formula += ' ' + line.gsub(/\/\/.*/, '') # unless line =~ /^[ ]*\/\//
|
196
200
|
end
|
197
|
-
return formula
|
201
|
+
return formula.strip
|
198
202
|
end
|
199
203
|
|
200
204
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Gerrard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: creek
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- lib/twb/analysis/datasources/datasourcefieldsanalyzer.rb
|
70
70
|
- lib/twb/analysis/datasources/datasourcefieldscsvemitter.rb
|
71
71
|
- lib/twb/analysis/datasources/datasourceoriginsanalyzer.rb
|
72
|
+
- lib/twb/analysis/datasources/datasourcesenumerator.rb
|
72
73
|
- lib/twb/analysis/datasources/datasourceslocationsanalyzer.rb
|
73
74
|
- lib/twb/analysis/datasources/datasourcetablefieldscsvemitter.rb
|
74
75
|
- lib/twb/analysis/datasources/googlesheetdatasourcesanalyzer.rb
|
@@ -80,6 +81,7 @@ files:
|
|
80
81
|
- lib/twb/analysis/sheets/sheetfieldsanalyzer.rb
|
81
82
|
- lib/twb/analysis/sheets/sheetfiltersanalyzer.rb
|
82
83
|
- lib/twb/analysis/sheets/sheetfiltersanalyzera.rb
|
84
|
+
- lib/twb/analysis/sheets/sheetsintooltipanalyzer.rb
|
83
85
|
- lib/twb/analysis/sheets/sheetsourcesanalyzer.rb
|
84
86
|
- lib/twb/analysis/sheets/worksheetdatastructurecsvemitter.rb
|
85
87
|
- lib/twb/analysis/workbooksummaryanalyzer.rb
|