twb 5.1.4 → 5.2.0
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e028806f0ddf3f1a48cda4ae9476f63f9302107cab1116673f1934f96143919
|
4
|
+
data.tar.gz: ec93e736c7833c5801cdc5fa7bd9344addd0de495d26d7e7d9af81633abbef52
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 620200b5922bff2813fc62503fcce0faa43a9f3daade99e87e003fe05a856aae102a7a8fef67fe7ddb4e847cdb8a0594fc66eea6377cf46955fd0708995cf8eb
|
7
|
+
data.tar.gz: a7db358530ed931716db2221206eae088ce2b82a61a638dca303863c738d88f7283e2b791dba8feefe321cf7478f1f0d44b233b3fdc21958778defdc365ad21d
|
data/lib/twb.rb
CHANGED
@@ -54,6 +54,7 @@ require_relative 'twb/analysis/documentedfieldsmarkdownemitter'
|
|
54
54
|
require_relative 'twb/analysis/annotatedfieldscsvemitter'
|
55
55
|
require_relative 'twb/analysis/workbooksummaryanalyzer'
|
56
56
|
require_relative 'twb/analysis/calculatedfields/calculatedfieldsanalyzer'
|
57
|
+
require_relative 'twb/analysis/calculatedfields/dotanalyzer'
|
57
58
|
require_relative 'twb/analysis/calculatedfields/groupfieldsanalyzer'
|
58
59
|
require_relative 'twb/analysis/calculatedfields/markdownemitter'
|
59
60
|
require_relative 'twb/analysis/calculatedfields/csvemitter'
|
@@ -80,5 +81,5 @@ require_relative 'twb/analysis/sheets/sheetsintooltipanalyzer'
|
|
80
81
|
# Represents Tableau Workbooks, their contents, and classes that analyze and manipulate them.
|
81
82
|
#
|
82
83
|
module Twb
|
83
|
-
VERSION = '5.
|
84
|
+
VERSION = '5.2.0'
|
84
85
|
end
|
@@ -78,12 +78,12 @@ module Analysis
|
|
78
78
|
@fieldTables = {}
|
79
79
|
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
# @@dotHeader = <<DOTHEADER
|
82
|
+
# digraph g {
|
83
|
+
# graph [rankdir="LR" splines=line];
|
84
|
+
# node [shape="box" width="2"];
|
85
85
|
|
86
|
-
DOTHEADER
|
86
|
+
# DOTHEADER
|
87
87
|
|
88
88
|
def initialize(**args)
|
89
89
|
emit "initialize CalculatedFieldsAnalyzer args #{args}"
|
@@ -141,7 +141,7 @@ DOTHEADER
|
|
141
141
|
# end
|
142
142
|
processDataSource ds
|
143
143
|
end
|
144
|
-
mapTwb
|
144
|
+
# mapTwb
|
145
145
|
emitGml
|
146
146
|
@twbCount += 1
|
147
147
|
finis
|
@@ -195,10 +195,10 @@ DOTHEADER
|
|
195
195
|
calculatedFields.add calcField.id
|
196
196
|
dsFields[calcField.uiname] = calcField
|
197
197
|
# if @doGraph
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
198
|
+
calcFieldNode = Twb::Util::Graphnode.new(name: calcField.uiname, id: calcField.id, type: calcField, properties: {:DataSource => ds.uiname})
|
199
|
+
@nodes.add calcFieldNode
|
200
|
+
dsFieldEdge = Twb::Util::Graphedge.new(from: dataSourceNode, to: calcFieldNode, relationship: 'contains')
|
201
|
+
@edges.add dsFieldEdge
|
202
202
|
# end
|
203
203
|
calculation = calcField.calculation
|
204
204
|
if calculation.has_formula
|
@@ -265,46 +265,10 @@ DOTHEADER
|
|
265
265
|
'', # rf.id,
|
266
266
|
'', #refFieldTable
|
267
267
|
]
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
# if @doGraph
|
273
|
-
# unless rf.uiname.nil?
|
274
|
-
# properties = {'DataSource' => ds.uiname, 'DataSourceReference' => 'local', :source => rf}
|
275
|
-
# refFieldNode = Twb::Util::Graphnode.new(name: rf.uiname, id: rf.id, type: rf.type, properties: properties)
|
276
|
-
# @nodes.add refFieldNode
|
277
|
-
# fieldFieldEdge = Twb::Util::Graphedge.new(from: calcFieldNode, to: refFieldNode, relationship: 'references')
|
278
|
-
# @edges.add fieldFieldEdge
|
279
|
-
# end
|
280
|
-
# # end
|
281
|
-
# referencedFields.add rf.id
|
282
|
-
# refFieldTable = ds.fieldTable(rf.name)
|
283
|
-
# emit "refFieldTable.nil? : #{refFieldTable.nil?}"
|
284
|
-
# unless refFieldTable.nil?
|
285
|
-
# tableID = refFieldTable + ':::' + ds.uiname
|
286
|
-
# tableName = "||#{refFieldTable}||"
|
287
|
-
# # if @doGraph
|
288
|
-
# tableNode = Twb::Util::Graphnode.new(name: tableName, id: tableID, type: :DBTable, properties: properties)
|
289
|
-
# @nodes.add tableNode
|
290
|
-
# fieldFieldEdge = Twb::Util::Graphedge.new(from: refFieldNode, to: tableNode, relationship: 'is a field in')
|
291
|
-
# @edges.add fieldFieldEdge
|
292
|
-
# # end
|
293
|
-
# # fldToDsNode = tableNode
|
294
|
-
# end
|
295
|
-
# @csvFormulaFields << [
|
296
|
-
# @referencedFieldsCount += 1,
|
297
|
-
# @twb.name,
|
298
|
-
# # @modTime,
|
299
|
-
# ds.uiname,
|
300
|
-
# calcField.uiname,
|
301
|
-
# calculation.formulaFlat,
|
302
|
-
# calculation.formulaFlatResolved,
|
303
|
-
# rf.name,
|
304
|
-
# rf.uiname,
|
305
|
-
# rf.id,
|
306
|
-
# refFieldTable
|
307
|
-
# ]
|
268
|
+
refFieldNode = Twb::Util::Graphnode.new(name: rf.uiname, id: rf.id, type: rf, properties: {:DataSource => ds.uiname})
|
269
|
+
@nodes.add refFieldNode
|
270
|
+
refFieldEdge = Twb::Util::Graphedge.new(from: calcFieldNode, to: refFieldNode , relationship: 'references')
|
271
|
+
@edges.add refFieldEdge
|
308
272
|
end # resolvedFields.each do
|
309
273
|
end # if calculation.has_formula
|
310
274
|
end # ds.calculatedFields.each
|
@@ -345,49 +309,49 @@ DOTHEADER
|
|
345
309
|
emit "\t formula:: #{calculation.formulaFlat}"
|
346
310
|
end
|
347
311
|
|
348
|
-
def mapTwb
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
end
|
312
|
+
# def mapTwb
|
313
|
+
# twb = @twb.name
|
314
|
+
# rootFields = @twbRootFields
|
315
|
+
# dotStuff = initDot twb
|
316
|
+
# dotFile = dotStuff[:file]
|
317
|
+
# dotFileName = dotStuff[:name]
|
318
|
+
# dotFile.puts "\n // subgraph cluster_1 {"
|
319
|
+
# dotFile.puts " // color= grey;"
|
320
|
+
# dotFile.puts ""
|
321
|
+
# edgesAsStrings = SortedSet.new
|
322
|
+
# # this two step process coalesces the edges into a unique set, avoiding duplicating the dot
|
323
|
+
# # file entries, and can be shrunk when graph edges expose the bits necessary for management by Set
|
324
|
+
# emit "\n========================\nLoading Edges\n========================\n From DC? Referenced? Edge \n %s %s %s" % ['--------', '-----------', '-'*45]
|
325
|
+
# @edges.each do |e|
|
326
|
+
# # don't want to emit edge which is from a Data Connection to a
|
327
|
+
# # Calculated Field which is also referenced by another calculated field
|
328
|
+
# isFromDC = e.from.type == :TwbDataConnection
|
329
|
+
# isRefField = @referencedFields.include?(e.to.id)
|
330
|
+
# edgesAsStrings.add(e.dot) unless isFromDC && isRefField
|
331
|
+
# # emit " ES #{e.dot}"
|
332
|
+
# # emit " ES from #{e.from}"
|
333
|
+
# # emit " ES to #{e.to}"
|
334
|
+
# end
|
335
|
+
# emit "------------------------\n "
|
336
|
+
# edgesAsStrings.each do |es|
|
337
|
+
# dotFile.puts " #{es}"
|
338
|
+
# end
|
339
|
+
# emit "========================\n "
|
340
|
+
# dotFile.puts ""
|
341
|
+
# dotFile.puts " // }"
|
342
|
+
# dotFile.puts "\n\n // 4 NODES --------------------------------------------------------------------"
|
343
|
+
# @nodes.each do |n|
|
344
|
+
# dotFile.puts n.dotLabel
|
345
|
+
# end
|
346
|
+
# dotFile.puts "\n\n // 5--------------------------------------------------------------------"
|
347
|
+
# emitTypes( dotFile )
|
348
|
+
# closeDot( dotFile, twb )
|
349
|
+
# emit "Rendering DOT file - #{twb}"
|
350
|
+
# renderDot(twb,dotFileName,'pdf')
|
351
|
+
# renderDot(twb,dotFileName,'png')
|
352
|
+
# renderDot(twb,dotFileName,'svg')
|
353
|
+
# # emitEdges
|
354
|
+
# end
|
391
355
|
|
392
356
|
def cypher twbName
|
393
357
|
if @doGraph
|
@@ -503,44 +467,44 @@ DOTHEADER
|
|
503
467
|
dotFile.puts ' }'
|
504
468
|
end
|
505
469
|
|
506
|
-
def initDot twb
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
end
|
470
|
+
# def initDot twb
|
471
|
+
# dotFileName = docFile("#{twb}#{@@processName}.dot")
|
472
|
+
# dotFile = File.open(dotFileName,'w')
|
473
|
+
# dotFile.puts @@dotHeader
|
474
|
+
# return {:file => dotFile, :name => dotFileName}
|
475
|
+
# end
|
512
476
|
|
513
|
-
def closeDot dotFile, twb
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
end
|
477
|
+
# def closeDot dotFile, twb
|
478
|
+
# dotFile.puts ' '
|
479
|
+
# dotFile.puts '// -------------------------------------------------------------'
|
480
|
+
# dotFile.puts ' '
|
481
|
+
# dotFile.puts ' subgraph cluster_1 {'
|
482
|
+
# # dotFile.puts ' color=white;'
|
483
|
+
# dotFile.puts ' style=invis;'
|
484
|
+
# # dotFile.puts ' border=0;'
|
485
|
+
# dotFile.puts ' node [border=blue];'
|
486
|
+
# dotFile.puts ' '
|
487
|
+
# dotFile.puts ' "" [style=invis]'
|
488
|
+
# dotFile.puts " \"Tableau Tools\\nCalculated Fields Map\\nWorkbook '#{twb}'\\n#{Time.new.ctime}\" [penwidth=0]"
|
489
|
+
# # dotFile.puts " \"Tableau Tools Workbook Calculated Fields Map\\n#{Time.new.ctime}\" -> \"\" [style=invis]"
|
490
|
+
# dotFile.puts ' '
|
491
|
+
# dotFile.puts ' }'
|
492
|
+
# dotFile.puts ' '
|
493
|
+
# dotFile.puts '}'
|
494
|
+
# dotFile.close
|
495
|
+
# end
|
532
496
|
|
533
497
|
|
534
|
-
def renderDot twb, dot, format
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
end
|
498
|
+
# def renderDot twb, dot, format
|
499
|
+
# imageType = '-T' + format
|
500
|
+
# imageFile = './ttdoc/' + twb + @@processName + 'Graph.' + format
|
501
|
+
# imageParam = '-o"' + imageFile + '"'
|
502
|
+
# emit "system #{@@gvDotLocation} #{imageType} #{imageParam} \"#{dot}\""
|
503
|
+
# system "#{@@gvDotLocation} #{imageType} #{imageParam} \"#{dot}\""
|
504
|
+
# emit " - #{imageFile}"
|
505
|
+
# @imageFiles << imageFile
|
506
|
+
# return imageFile
|
507
|
+
# end
|
544
508
|
|
545
509
|
end # class
|
546
510
|
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# dotanalyzer.rb - this Ruby script Copyright 2017, 2018 Christopher 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
|
+
|
17
|
+
module Twb
|
18
|
+
module Analysis
|
19
|
+
module CalculatedFields
|
20
|
+
|
21
|
+
class DotAnalyzer
|
22
|
+
include TabTool
|
23
|
+
|
24
|
+
attr_reader :docFileName
|
25
|
+
|
26
|
+
@@gvDotLocation = 'C:\\tech\\graphviz\\Graphviz2.38\\bin\\dot.exe'
|
27
|
+
@@imageTypes = ['pdf', 'png', 'svg']
|
28
|
+
|
29
|
+
def initialize(**args)
|
30
|
+
@args = args
|
31
|
+
init
|
32
|
+
@funcdoc = {:class=>self.class, :blurb=>'Create Dot files documenting Calculated Fields', :description=>'Analyze Calculated Fields - create Dot files' }
|
33
|
+
@metrics = {}
|
34
|
+
@imageFiles = Array.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def processTWB twb
|
38
|
+
# twb = File.basename(twb)
|
39
|
+
@twb = twb #Twb::Workbook.new twb
|
40
|
+
addDocFile @dotFile, @dotFileName, "Dot file of Calculated fields for Workbook '#{@twb.name}'"
|
41
|
+
@twb.datasources.each do |ds|
|
42
|
+
unless ds.calculatedFields.empty?
|
43
|
+
initDotFile ds.uiname
|
44
|
+
# @dotFile.puts "\n ## #{ds.uiname} \n "
|
45
|
+
# @dotFile.puts "__has #{ds.calculatedFields.length} calculated fields__\n "
|
46
|
+
@cfCnt = 0
|
47
|
+
calcFields = Set.new
|
48
|
+
refFields = Set.new
|
49
|
+
edges = Set.new
|
50
|
+
ds.calculatedFields.each do |cf|
|
51
|
+
@cfCnt += 1
|
52
|
+
calcFields << cf.uiname
|
53
|
+
edges << " \"#{ds.uiname}\" -> \"#{cf.uiname}\" [tailport=e, headport=w] "
|
54
|
+
cf.referencedFields.each do |rf|
|
55
|
+
refFields << rf.uiname
|
56
|
+
edges << " \"#{cf.uiname}\" -> \"#{rf.uiname}\" [tailport=e, headport=w] "
|
57
|
+
end
|
58
|
+
end # ds.calculatedFields.each
|
59
|
+
# "federated.17h7owt0rsacke17cql8o0w2ittk" -> "New AO Actuals Query in PP+ (AO Variance Data)::vs Prior Year [YTD]"
|
60
|
+
# "federated.01s5lca037ted31gxs9sg0t9mnnt" [label="Controls" ]
|
61
|
+
edges.each do |edge|
|
62
|
+
@dotFile.puts "\t #{edge.strip}"
|
63
|
+
end
|
64
|
+
@dotFile.puts " "
|
65
|
+
allFields = calcFields + refFields
|
66
|
+
allFields.each do |f|
|
67
|
+
@dotFile.puts "\t \"#{f}\" [label=\"#{f}\"]"
|
68
|
+
end
|
69
|
+
endPointFields = allFields - calcFields
|
70
|
+
rankSame(endPointFields) unless endPointFields.nil? || endPointFields.empty?
|
71
|
+
closeDotFile
|
72
|
+
@@imageTypes.each do |type|
|
73
|
+
renderDot type
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end # twb.datasources.each
|
77
|
+
finis
|
78
|
+
end # def processTwb twb
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def initDotFile dsName
|
83
|
+
@dotFileName = './ttdoc/' + @twb.name + '.' + dsName + '.CalculatedFields.dot'
|
84
|
+
@dotFile = File.open(@dotFileName,'w')
|
85
|
+
# @dotFile.puts @@dotHeader
|
86
|
+
@dotFile.puts ' digraph g {'
|
87
|
+
@dotFile.puts ' graph [rankdir="LR" splines=line];'
|
88
|
+
@dotFile.puts ' node [shape="box" width="2"];'
|
89
|
+
@dotFile.puts ' '
|
90
|
+
@dotFile.puts ' subgraph cluster_0 {'
|
91
|
+
end
|
92
|
+
|
93
|
+
def closeDotFile
|
94
|
+
# @dotFile.puts "\n # counted #{@cfCnt} calculated fields\n "
|
95
|
+
# @dotFile.puts "\n }"
|
96
|
+
@dotFile.puts ' }'
|
97
|
+
@dotFile.puts ' '
|
98
|
+
@dotFile.puts '// -------------------------------------------------------------'
|
99
|
+
@dotFile.puts ' '
|
100
|
+
@dotFile.puts ' subgraph cluster_1 {'
|
101
|
+
#@dotFile.puts ' color=white;'
|
102
|
+
@dotFile.puts ' style=invis;'
|
103
|
+
#@dotFile.puts ' border=0;'
|
104
|
+
@dotFile.puts ' node [border=blue];'
|
105
|
+
@dotFile.puts ' '
|
106
|
+
@dotFile.puts ' "" [style=invis]'
|
107
|
+
@dotFile.puts " \"Tableau Tools\\nCalculated Fields Map\\nWorkbook '#{@twb.name}'\\n#{Time.new.ctime}\" [penwidth=0]"
|
108
|
+
#@dotFile.puts " \"Tableau Tools Workbook Calculated Fields Map\\n#{Time.new.ctime}\" -> \"\" [style=invis]"
|
109
|
+
@dotFile.puts ' '
|
110
|
+
@dotFile.puts ' }'
|
111
|
+
@dotFile.puts ' '
|
112
|
+
@dotFile.puts '}'
|
113
|
+
@dotFile.close
|
114
|
+
end
|
115
|
+
|
116
|
+
def rankSame fields
|
117
|
+
@dotFile.puts "\n {rank=same "
|
118
|
+
fields.each do |f|
|
119
|
+
@dotFile.puts "\t \"#{f}\" "
|
120
|
+
end
|
121
|
+
@dotFile.puts " } "
|
122
|
+
end
|
123
|
+
|
124
|
+
def renderDot format
|
125
|
+
imageType = '-T' + format
|
126
|
+
imageFile = @dotFileName + '.Graph.' + format
|
127
|
+
imageParam = '-o"' + imageFile + '"'
|
128
|
+
emit "system #{@@gvDotLocation} #{imageType} #{imageParam} \"#{@dotFileName}\""
|
129
|
+
system "#{@@gvDotLocation} #{imageType} #{imageParam} \"#{@dotFileName}\""
|
130
|
+
# emit " - #{imageFile}"
|
131
|
+
@imageFiles << imageFile
|
132
|
+
return imageFile
|
133
|
+
end
|
134
|
+
|
135
|
+
end # class DotAnalyzer
|
136
|
+
|
137
|
+
end # nodule CalculatedFields
|
138
|
+
end # module Analysis
|
139
|
+
end # module Twb
|
@@ -52,10 +52,10 @@ module CalculatedFields
|
|
52
52
|
@docFile.puts "#{l.gsub('<<','[').gsub('>>',']')}"
|
53
53
|
end
|
54
54
|
@docFile.puts "```"
|
55
|
-
if cf.
|
55
|
+
if cf.referencedFields.length > 0
|
56
56
|
fieldsRefOrder = []
|
57
57
|
fieldsSortSet = SortedSet.new
|
58
|
-
cf.
|
58
|
+
cf.referencedFields.each do |field|
|
59
59
|
fieldsRefOrder.push field.uiname
|
60
60
|
fieldsSortSet << field.uiname
|
61
61
|
end
|
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: 5.
|
4
|
+
version: 5.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Gerrard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: creek
|
@@ -64,6 +64,7 @@ files:
|
|
64
64
|
- lib/twb/analysis/annotatedfieldscsvemitter.rb
|
65
65
|
- lib/twb/analysis/calculatedfields/calculatedfieldsanalyzer.rb
|
66
66
|
- lib/twb/analysis/calculatedfields/csvemitter.rb
|
67
|
+
- lib/twb/analysis/calculatedfields/dotanalyzer.rb
|
67
68
|
- lib/twb/analysis/calculatedfields/fieldsaliasesanalyzer.rb
|
68
69
|
- lib/twb/analysis/calculatedfields/groupfieldsanalyzer.rb
|
69
70
|
- lib/twb/analysis/calculatedfields/markdownemitter.rb
|