twb 3.7.2 → 3.7.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/twb.rb +1 -1
- data/lib/twb/analysis/CalculatedFields/CalculatedFieldsAnalyzer.rb +36 -82
- data/lib/twb/analysis/CalculatedFields/MarkdownEmitter.rb +2 -2
- data/lib/twb/analysis/DataSources/googlesheetdatasourcesanalyzer.rb +10 -3
- data/lib/twb/analysis/Sheets/sheetfieldsanalyzer.rb +1 -3
- data/lib/twb/tabtool.rb +78 -7
- data/lib/twb/util/graph.rb +4 -6
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 183bcd3f582a9939faf1544d4b4ecfda0cd36f0a
|
4
|
+
data.tar.gz: 3daae7542ce46541dd310e6f27d717530db9ac95
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa968978835cf31847e3446bc02107662b00e3213e4d1345104ace41b6cddd3d2c6d7756bd6ec4ae9a6bcae22acb4904e53771658b210d1847852558f29d43f8
|
7
|
+
data.tar.gz: c19335e986bbbf10166e96b97bb2e0064091fe2e71af00bbc0bc17c4e87b01fa51eefc019ea19e3e23149efaf341d2d95484ffdd06aa19b7e9122713439e639a
|
data/lib/twb.rb
CHANGED
@@ -21,9 +21,10 @@ require 'csv'
|
|
21
21
|
module Twb
|
22
22
|
module Analysis
|
23
23
|
|
24
|
-
class CalculatedFieldsAnalyzer < Twb::Util::Graph
|
24
|
+
class CalculatedFieldsAnalyzer # < Twb::Util::Graph
|
25
25
|
|
26
26
|
include TabTool
|
27
|
+
include Graph
|
27
28
|
|
28
29
|
attr_reader :calculatedFieldsCount, :formulaFieldsCount, :dataFiles
|
29
30
|
attr_accessor :ttdocdir
|
@@ -81,12 +82,7 @@ module Analysis
|
|
81
82
|
DOTHEADER
|
82
83
|
|
83
84
|
def initialize
|
84
|
-
|
85
|
-
@ttdocdir = 'ttdoc'
|
86
|
-
# logfile = docFile(@@ttlogfile)
|
87
|
-
# @logger = Logger.new(logfile)
|
88
|
-
# @logger.level = Logger::DEBUG
|
89
|
-
emit ' Initializing CalculatedFieldsAnalyzer'
|
85
|
+
@funcdoc = {:class=>self.class, :blurb=>'Analyzing Calculated Fields from Tableau Workbooks.', :description=>'Calculated fields can be complex, this tool provides robust coverage.',}
|
90
86
|
#-- CSV records collectors
|
91
87
|
@csvCalculatedFields = []
|
92
88
|
@csvFormulaFields = []
|
@@ -98,43 +94,12 @@ DOTHEADER
|
|
98
94
|
#--
|
99
95
|
@referencedFields = SortedSet.new
|
100
96
|
#--
|
101
|
-
|
102
|
-
@
|
103
|
-
@
|
104
|
-
# @csvCF.close
|
105
|
-
#--
|
106
|
-
emit "init CSV #{@@calcLinesCSVFileName}"
|
107
|
-
@csvCFLs = File.open(docFile(@@calcLinesCSVFileName), 'w')
|
108
|
-
@csvCFLs.puts @@calcLinesCSVFileHeader.to_csv
|
109
|
-
# @csvCFLs.close
|
110
|
-
#--
|
111
|
-
emit "init CSV #{@@formFieldsCSVFileName}"
|
112
|
-
@csvFF = File.open(docFile(@@formFieldsCSVFileName), 'w')
|
113
|
-
@csvFF.puts @@formFieldsCSVFileHeader.to_csv
|
114
|
-
# @csvFF.close
|
115
|
-
#--
|
116
|
-
@dataFiles = { @@calcFieldsCSVFileName => 'Calculated Fields & their Formulas',
|
117
|
-
@@calcLinesCSVFileName => 'Calculated Fields & individual Formula Lines',
|
118
|
-
@@formFieldsCSVFileName => 'Fields referenced in Formulas'
|
119
|
-
}
|
97
|
+
@csvCF = initCSV(@@calcFieldsCSVFileName, 'Calculated fields and their formulas.', @@calcFieldsCSVFileHeader)
|
98
|
+
@csvCFLs = initCSV(@@calcLinesCSVFileName, "Calculated fields and their formulas' individual lines.", @@calcLinesCSVFileHeader)
|
99
|
+
@csvFF = initCSV(@@formFieldsCSVFileName, 'Calculated fields and the fields their formulas reference.', @@formFieldsCSVFileHeader)
|
120
100
|
#--
|
121
101
|
@localEmit = false
|
122
|
-
@imageFiles
|
123
|
-
end
|
124
|
-
|
125
|
-
def initDocDir
|
126
|
-
return if $ttdocdir.nil?
|
127
|
-
return if ''.eql? $ttdocdir
|
128
|
-
return if Dir.exists?($ttdocdir)
|
129
|
-
if File.exists? $ttdocdir
|
130
|
-
$ttdocdir = nil
|
131
|
-
return
|
132
|
-
end
|
133
|
-
Dir.mkdir $ttdocdir
|
134
|
-
end
|
135
|
-
|
136
|
-
def docFile name
|
137
|
-
@ttdocdir.nil? ? name : "#{@ttdocdir}\\#{name}"
|
102
|
+
@imageFiles = []
|
138
103
|
end
|
139
104
|
|
140
105
|
def processTWB workbook
|
@@ -221,19 +186,19 @@ DOTHEADER
|
|
221
186
|
emit "@@ FL: #{calcField.uiname}"
|
222
187
|
calculation.formulaResolvedLines.each do |fl|
|
223
188
|
emit "@@ FL: => '#{fl}'"
|
224
|
-
fieldFormulaLines
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
189
|
+
fieldFormulaLines << [ @calculatedFieldsCount, # 'Calc Field #',
|
190
|
+
@twb.name, # 'Workbook',
|
191
|
+
@twbDir, # 'Workbook Dir',
|
192
|
+
ds.uiname, # 'Data Source',
|
193
|
+
ds.caption, # 'Data Source Caption',
|
194
|
+
ds.name, # 'Data Source Name (tech)',
|
195
|
+
calcField.uiname, # 'Field Name',
|
196
|
+
calcField.caption, # 'Field Caption',
|
197
|
+
calcField.name, # 'Field Name (tech)',
|
198
|
+
calcField.calculation.formulaFlatResolved, # 'Formula'
|
199
|
+
flnum += 1, # 'Formula Line #',
|
200
|
+
fl.start_with?(" ") ? "'#{fl}" : fl # 'Formula Line' - THIS IS A STUPID HACK NEEDED BECAUSE TABLEAU STRIPS LEADING BLANKS FROM CSV VALUES
|
201
|
+
]
|
237
202
|
end
|
238
203
|
#-- collect fields referenced in formula
|
239
204
|
calculation.calcFields.each do |rf|
|
@@ -256,19 +221,19 @@ DOTHEADER
|
|
256
221
|
@edges.add fieldFieldEdge
|
257
222
|
# fldToDsNode = tableNode
|
258
223
|
end
|
259
|
-
@csvFormulaFields
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
224
|
+
@csvFormulaFields << [
|
225
|
+
@formulaFieldsCount += 1,
|
226
|
+
@twb.name,
|
227
|
+
@twbDir,
|
228
|
+
ds.uiname,
|
229
|
+
calcField.uiname,
|
230
|
+
calculation.formulaFlat,
|
231
|
+
calculation.formulaResolved,
|
232
|
+
rf.name,
|
233
|
+
rf.uiname,
|
234
|
+
rf.id,
|
235
|
+
refFieldTable
|
236
|
+
]
|
272
237
|
end # resolvedFields.each do
|
273
238
|
end # if calculation.has_formula
|
274
239
|
end # ds.calculatedFields.each
|
@@ -280,35 +245,24 @@ DOTHEADER
|
|
280
245
|
cypherPy @twb.name
|
281
246
|
emit "#######################"
|
282
247
|
#-- record calculated fields
|
283
|
-
# @csvCF = File.open(docFile(@@calcFieldsCSVFileName), 'a')
|
284
|
-
# @csvCF.puts @@calcFieldsCSVFileHeader.to_csv
|
285
248
|
emit "@@ record calculated fields ds: #{ds.uiname}"
|
286
|
-
# @csvCF.open('a')
|
287
249
|
@csvCalculatedFields.each do |r|
|
288
|
-
@csvCF
|
250
|
+
@csvCF << r
|
289
251
|
end
|
290
252
|
#-- record individual formula lines
|
291
|
-
# @csvCFLs = File.open(docFile(@@calcLinesCSVFileName), 'a')
|
292
|
-
# @csvCFLs.puts @@calcLinesCSVFileHeader.to_csv
|
293
253
|
emit "@@ individual formula lines ds: #{ds.uiname}"
|
294
254
|
fieldFormulaLines.each do |ffl|
|
295
|
-
|
296
|
-
emit "@@@@ FFLB: #{ffl.to_csv}"
|
297
|
-
emit "@@@@ FFLc: "
|
298
|
-
@csvCFLs.puts ffl.to_csv
|
255
|
+
@csvCFLs << ffl
|
299
256
|
end
|
300
257
|
#-- record formula-referenced fields
|
301
|
-
# @csvFF = File.open(docFile(@@formFieldsCSVFileName), 'a')
|
302
|
-
# @csvFF.puts @@formFieldsCSVFileHeader.to_csv
|
303
258
|
emit "@@ formula-referenced fields ds: #{ds.uiname}"
|
304
259
|
@csvFormulaFields.each do |r|
|
305
|
-
@csvFF
|
260
|
+
@csvFF << r
|
306
261
|
end
|
307
262
|
#--
|
308
263
|
return @imageFiles
|
309
264
|
end # def processDataSource
|
310
265
|
|
311
|
-
|
312
266
|
def emitCalcfield calcField
|
313
267
|
emit "\t FIELD cap :: #{calcField.caption} "
|
314
268
|
emit "\t tname:: #{calcField.name}"
|
@@ -32,13 +32,13 @@ module CalculatedFields
|
|
32
32
|
@twb = twb #Twb::Workbook.new twb
|
33
33
|
@docFileName = './ttdoc/' + @twb.name + '.CalculatedFields.md'
|
34
34
|
@docFile = File.open(@docFileName,'w')
|
35
|
-
@docFile.puts "
|
35
|
+
@docFile.puts "# #{twb.name}"
|
36
36
|
dsNames = @twb.datasourceUINames
|
37
37
|
@docFile.puts "#{dsNames.length} Data Sources"
|
38
38
|
|
39
39
|
|
40
40
|
@twb.datasources.each do |ds|
|
41
|
-
@docFile.puts "
|
41
|
+
@docFile.puts "## #{ds.uiname}"
|
42
42
|
@docFile.puts "__has #{ds.calculatedFields.length} calculated fields__\n "
|
43
43
|
cnt = 0
|
44
44
|
ds.calculatedFields.each do |cf|
|
@@ -28,14 +28,21 @@ module Analysis
|
|
28
28
|
attr_reader :twbname, :twbcount
|
29
29
|
|
30
30
|
def initialize
|
31
|
+
#-- set up metrics
|
31
32
|
@twbcount = 0
|
32
|
-
@
|
33
|
-
@
|
33
|
+
@dscount = 0
|
34
|
+
@sheetcount = 0
|
35
|
+
@metrics = {
|
36
|
+
'# of Workbooks' => @twbcount ,
|
37
|
+
'# of Worksheets' => @dscount ,
|
38
|
+
'# of Worksheets' => @sheetcount
|
39
|
+
}
|
40
|
+
#--
|
34
41
|
@funcdoc = {:class=>self.class, :blurb=>'Analyzing Google Sheet Data Sources from Tableau Workbooks.', :description=>nil,}
|
35
42
|
docFileName = docFile('TWBGoogleSheetDataSources.csv')
|
36
43
|
@csv = CSV.open(docFileName, 'w')
|
37
44
|
@csv << ["Workbook",'Data Source','Connection','File Name','Type','Table Name','Field']
|
38
|
-
@docfiles = [{:name=>docFileName,:
|
45
|
+
@docfiles = [{:name=>docFileName,:description=>"CSV File containing the data relating Google Sheet-based Data Sources."}]
|
39
46
|
end
|
40
47
|
|
41
48
|
def processTWB twb
|
@@ -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
|
@@ -34,7 +33,7 @@ module Analysis
|
|
34
33
|
docFileName = docFile('TWBWorksheetFields.csv')
|
35
34
|
@sheetFieldsCSV = CSV.open(docFileName,'w')
|
36
35
|
@sheetFieldsCSV << ['Workbook','Worksheet','Data Source','Data Source (tech)','Field','Field (tech)']
|
37
|
-
|
36
|
+
addDocFile docFileName, "CSV File containing the data relating Workbooks,Worksheets, and the sheets' Data Sources and Fields"
|
38
37
|
end
|
39
38
|
|
40
39
|
def processTWB twb
|
@@ -45,7 +44,6 @@ module Analysis
|
|
45
44
|
parseSheets
|
46
45
|
end
|
47
46
|
|
48
|
-
|
49
47
|
def parseSheets
|
50
48
|
@worksheets = @twb.worksheets
|
51
49
|
@worksheets.each do |sheet|
|
data/lib/twb/tabtool.rb
CHANGED
@@ -17,29 +17,75 @@ require 'logger'
|
|
17
17
|
|
18
18
|
module TabTool
|
19
19
|
|
20
|
-
attr_accessor :ttdocdir, :logger,
|
21
|
-
attr_reader :funcdoc, :docfiles
|
20
|
+
attr_accessor :ttdocdir, :logger, :loglevel, :logfilename
|
21
|
+
attr_reader :funcdoc, :docfiles, :metrics
|
22
22
|
|
23
23
|
TTDOCDIR = './ttdoc'
|
24
24
|
|
25
25
|
@funcdoc = {:class=>nil, :blurb=>nil, :description=>nil,}
|
26
|
-
@docfiles = {}
|
26
|
+
@docfiles = [] # should be of form [{:name=>'docFileName',:description=>'doc file description'}]
|
27
27
|
|
28
28
|
@ttdocdir = './ttdoc'
|
29
29
|
@logfilename = 'TabTools.ttlog'
|
30
30
|
@loglevel = Logger::DEBUG
|
31
31
|
@localEmit = false
|
32
|
+
@docDirSet = false
|
32
33
|
|
33
34
|
def funcdoc
|
34
35
|
@funcdoc.nil? ? {:class=>'n/a', :blurb=>'generic TabTool blurb', :description=>'A useful Tableau Tool.'} : @funcdoc
|
35
36
|
end
|
36
37
|
|
37
38
|
def docFile name
|
38
|
-
|
39
|
-
|
39
|
+
initDocDir unless @docDirSet
|
40
|
+
@docDir.nil? ? name : "#{@docDir}/#{name}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def docfiles
|
44
|
+
@docfiles ||= @docfiles = []
|
45
|
+
end
|
46
|
+
|
47
|
+
def addDocFile name, description
|
48
|
+
@docfiles = [] if @docfiles.nil?
|
49
|
+
@docfiles << {:name=>name,:description=>description}
|
50
|
+
end
|
51
|
+
|
52
|
+
def docFileMaxNameLen
|
53
|
+
maxlen = 0
|
54
|
+
docfiles.each do |f|
|
55
|
+
nameLen = f[:name].class == String ? [maxlen, f[:name].length].max : 0
|
56
|
+
maxlen = nameLen.nil? ? maxlen : [maxlen,nameLen].max
|
57
|
+
end
|
58
|
+
return maxlen
|
59
|
+
end
|
60
|
+
|
61
|
+
def docfilesdoc
|
62
|
+
lines = SortedSet.new
|
63
|
+
unless @docfiles.nil? || @docfiles.empty?
|
64
|
+
nameLen = docFileMaxNameLen
|
65
|
+
docfiles.each do |dfi|
|
66
|
+
lines << " - %-#{nameLen}s %-s " % [ dfi[:name], dfi[:description] ]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
docLines = lines.empty? ? [] : [' ','For documentation and generated data see the following:',' ']
|
70
|
+
lines.each do |l|
|
71
|
+
docLines << l
|
72
|
+
end
|
73
|
+
return docLines
|
74
|
+
end
|
75
|
+
|
76
|
+
def initCSV(fileName, desc=nil, header=nil)
|
77
|
+
# puts 'initCSV'
|
78
|
+
# puts " @docDirSet: #{@docDirSet} "
|
79
|
+
csvName = docFile(fileName)
|
80
|
+
emit "init CSV #{csvName}"
|
81
|
+
csvFile = CSV.open(csvName, 'w')
|
82
|
+
csvFile << header unless header.nil?
|
83
|
+
addDocFile csvName,desc
|
84
|
+
return csvFile
|
40
85
|
end
|
41
86
|
|
42
87
|
def emit(local=@localEmit, stuff)
|
88
|
+
initDocDir if @docDirSet.nil? || !@docDirSet
|
43
89
|
initLogger if @logger.nil?
|
44
90
|
if stuff.is_a? String then
|
45
91
|
lines = stuff.split(/\n/)
|
@@ -54,10 +100,35 @@ module TabTool
|
|
54
100
|
end
|
55
101
|
|
56
102
|
def initLogger
|
57
|
-
|
58
|
-
|
103
|
+
initDocDir unless @docDirSet
|
104
|
+
initDocDir
|
105
|
+
logFileName = docFile("#{self.class.to_s.split('::').last}.ttlog")
|
106
|
+
@logger = Logger.new(logFileName)
|
59
107
|
@logger.level = Logger::DEBUG
|
60
108
|
end
|
61
109
|
|
110
|
+
def initDocDir
|
111
|
+
# puts 'initDocDir - begin'
|
112
|
+
# puts " @docDirSet: #{@docDirSet} "
|
113
|
+
# puts " TTDOCDIR : #{TTDOCDIR} "
|
114
|
+
# puts " $ttdocdir : #{$ttdocdir} "
|
115
|
+
# puts " @docDir : #{@docDir} "
|
116
|
+
return if @docDirSet
|
117
|
+
return if TTDOCDIR.nil? && $ttdocdir.nil?
|
118
|
+
return if ''.eql?($ttdocdir) && ''.eql?(TTDOCDIR)
|
119
|
+
@docDir = $ttdocdir.nil? ? TTDOCDIR : $ttdocdir
|
120
|
+
return if Dir.exists?(@docDir)
|
121
|
+
if File.exists? @docDir
|
122
|
+
@docDir = nil
|
123
|
+
return
|
124
|
+
end
|
125
|
+
Dir.mkdir @docDir
|
126
|
+
@docDirSet = true
|
127
|
+
# puts " @docDirSet: #{@docDirSet} "
|
128
|
+
# puts " @docDir : #{@docDir} "
|
129
|
+
# puts 'initDocDir - end'
|
130
|
+
end
|
131
|
+
|
132
|
+
|
62
133
|
end # module TabTool
|
63
134
|
|
data/lib/twb/util/graph.rb
CHANGED
@@ -13,18 +13,16 @@
|
|
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
|
-
module
|
17
|
-
module Util
|
16
|
+
module Graph
|
18
17
|
|
19
|
-
class Graph
|
20
18
|
attr_accessor :nodes, :edges
|
19
|
+
|
21
20
|
def nodes
|
22
21
|
@nodes ||= @nodes = []
|
23
22
|
end
|
23
|
+
|
24
24
|
def edges
|
25
25
|
@edges ||= @edges = []
|
26
26
|
end
|
27
|
-
end
|
28
27
|
|
29
|
-
end # module
|
30
|
-
end # module Twb
|
28
|
+
end # module Graph
|