twb 4.6.1 → 4.6.2
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 +1 -1
- data/lib/twb/analysis/calculatedfields/calculatedfieldsanalyzer.rb +488 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24b17be9e4300e5a4b463cd41aac99d2a1802973d0ff6f348588dfc9004c8624
|
4
|
+
data.tar.gz: 74a3828145f626709a332b7e678444d7a1063f2c14e77cd799d19fbfaa49a9d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 767720b5f5ec250225eac1e5ad72c3b913251341f210ef2e663ba442962d17bd675335f12b07b93ac1343a589b2b807b944ff498985f687508a7567bb5230e06
|
7
|
+
data.tar.gz: f70f2834c01269f5b2ebde7aa363e6536db5db413781b39e9881b4ba2a1953883b6f147e31e4c3af92586cb5f01b08e6d75f57636982da0f9c6c2d32a1f113f0
|
data/lib/twb.rb
CHANGED
@@ -13,29 +13,512 @@
|
|
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
|
-
require '
|
16
|
+
# require 'nokogiri'
|
17
|
+
# require 'twb'
|
18
|
+
# require 'set'
|
19
|
+
require 'csv'
|
17
20
|
|
18
21
|
module Twb
|
19
22
|
module Analysis
|
20
23
|
|
21
|
-
|
22
24
|
class CalculatedFieldsAnalyzer
|
23
25
|
|
24
|
-
include TabTool
|
26
|
+
include TabTool
|
27
|
+
include Graph
|
28
|
+
|
29
|
+
attr_reader :calculatedFieldsCount, :referencedFieldsCount, :metrics
|
30
|
+
attr_accessor :ttdocdir
|
31
|
+
|
32
|
+
@@ttlogfile = 'CalculatedFieldsAnalyzer.ttlog'
|
33
|
+
@@gvDotLocation = 'C:\\tech\\graphviz\\Graphviz2.38\\bin\\dot.exe'
|
34
|
+
@@processName = '.CalculatedFields'
|
35
|
+
|
36
|
+
@@calcFieldsCSVFileName = 'CalculatedFields.csv'
|
37
|
+
@@calcFieldsCSVFileHeader = ['Record #',
|
38
|
+
'Workbook',
|
39
|
+
# 'Workbook Modified',
|
40
|
+
'Data Source', 'Data Source Caption', 'Data Source Name (tech)',
|
41
|
+
'Field Name', 'Field Caption', 'Field Name (tech)',
|
42
|
+
'Data Source + Field Name (tech)',
|
43
|
+
'Data Type', 'Role', 'Type',
|
44
|
+
'Class',
|
45
|
+
'Scope Isolation',
|
46
|
+
'Formula Length',
|
47
|
+
'Formula',
|
48
|
+
'Formula (tech)',
|
49
|
+
'Formula Comments',
|
50
|
+
'Formula LOD?'
|
51
|
+
]
|
52
|
+
|
53
|
+
@@calcLinesCSVFileName = 'CalculatedFieldsFormulaLines.csv'
|
54
|
+
@@calcLinesCSVFileHeader = ['Record #',
|
55
|
+
'Workbook',
|
56
|
+
# 'Workbook Modified',
|
57
|
+
'Data Source', 'Data Source Caption', 'Data Source Name (tech)',
|
58
|
+
'Field Name', 'Field Caption', 'Field Name (tech)',
|
59
|
+
'Formula', 'Formula Line #', 'Formula Line'
|
60
|
+
]
|
61
|
+
|
62
|
+
@@formFieldsCSVFileName = 'CalculatedFieldsReferenced.csv'
|
63
|
+
@@formFieldsCSVFileHeader = ['Record #',
|
64
|
+
'Workbook',
|
65
|
+
# 'Workbook Modified',
|
66
|
+
'Data Source',
|
67
|
+
'Field - Calculated',
|
68
|
+
'Formula (tech)',
|
69
|
+
'Formula',
|
70
|
+
'Field - Referenced (tech)',
|
71
|
+
'Field - Referenced',
|
72
|
+
'Data Source + Field - Referenced',
|
73
|
+
'Table'
|
74
|
+
]
|
75
|
+
|
76
|
+
@techUINames = {}
|
77
|
+
@fieldTables = {}
|
78
|
+
|
79
|
+
|
80
|
+
@@dotHeader = <<DOTHEADER
|
81
|
+
digraph g {
|
82
|
+
graph [rankdir="LR" splines=line];
|
83
|
+
node [shape="box" width="2"];
|
84
|
+
|
85
|
+
DOTHEADER
|
25
86
|
|
26
87
|
def initialize(**args)
|
88
|
+
emit "initialize CalculatedFieldsAnalyzer args #{args}"
|
89
|
+
@args = args
|
90
|
+
@recordDir = !@args.nil? && @args[:recordDir] == true
|
91
|
+
@ttdocdir = @args[:ttdocdir]
|
92
|
+
@csvAdd = @args[:csvMode] == :add
|
93
|
+
@csvMode = @csvAdd ? 'a' : 'w'
|
94
|
+
init
|
95
|
+
@funcdoc = {:class=>self.class, :blurb=>'Analyze Calculated Fields', :description=>'Calculated fields can be complex, this tool provides robust coverage.',}
|
96
|
+
#-- CSV records collectors
|
97
|
+
@csvCalculatedFields = Array.new
|
98
|
+
@csvFormulaFields = Array.new
|
99
|
+
@csvFormulaLines = Array.new
|
100
|
+
#-- Counters setup --
|
101
|
+
@twbCount = 0
|
102
|
+
@dataSourcesCount = 0
|
103
|
+
@calculatedFieldsCount = 0
|
104
|
+
@referencedFieldsCount = 0
|
105
|
+
#--
|
106
|
+
@referencedFields = SortedSet.new
|
107
|
+
#--
|
108
|
+
twbdirLabel = @recordDir.nil? ? nil : 'Workbook Dir'
|
109
|
+
@csvCF = initCSV(@@calcFieldsCSVFileName, 'Calculated fields and their formulas.', @@calcFieldsCSVFileHeader )
|
110
|
+
@csvCFLs = initCSV(@@calcLinesCSVFileName, "Calculated fields and their formulas' individual lines.", @@calcLinesCSVFileHeader )
|
111
|
+
@csvFF = initCSV(@@formFieldsCSVFileName, 'Calculated fields and the fields their formulas reference.', @@formFieldsCSVFileHeader )
|
112
|
+
# TODO migrate addition of 'Workbook Dir' to CSV header to TabTool
|
113
|
+
#--
|
114
|
+
@localEmit = false
|
115
|
+
@imageFiles = Array.new
|
116
|
+
#--
|
117
|
+
@doGraph = config(:dograph)
|
27
118
|
end
|
28
119
|
|
29
120
|
def processTWB workbook
|
121
|
+
@twb = workbook.is_a?(String) ? Twb::Workbook.new(workbook) : workbook
|
122
|
+
throw Exception unless @twb.is_a? Twb::Workbook
|
123
|
+
emit "- Workbook: #{workbook}"
|
124
|
+
emit " version: #{@twb.version}"
|
125
|
+
@twbDir = @twb.dir #File.dirname(File.expand_path(workbook))
|
126
|
+
@modTime = @twb.modtime
|
127
|
+
@edges = Set.new
|
128
|
+
#-- processing
|
129
|
+
dss = @twb.datasources
|
130
|
+
# puts " # data sources: #{dss.length}"
|
131
|
+
@twbRootFields = Set.new
|
132
|
+
@twbFields = Hash.new { |h,k| h[k] = [] }
|
133
|
+
@nodes = Set.new
|
134
|
+
dss.each do |ds|
|
135
|
+
@dataSourcesCount += 1
|
136
|
+
# puts "\t\t - #{ds.uiname} \t\t #{ds.calculatedFields.length}"
|
137
|
+
next if ds.Parameters? # don't process the Parameters data source - Parameters' fields aren't Calculated fields for our purposes
|
138
|
+
# dataSourceNode = Twb::Util::Graphnode.new(name: ds.uiname, id: ds.id, type: ds, properties: {workbook: workbook})
|
139
|
+
# @nodes.add dataSourceNode
|
140
|
+
# ds.calculatedFields.each do |calcField|
|
141
|
+
# end
|
142
|
+
processDataSource ds
|
143
|
+
end
|
144
|
+
mapTwb
|
145
|
+
emitGml
|
146
|
+
@twbCount += 1
|
147
|
+
finis
|
30
148
|
end
|
31
149
|
|
32
150
|
def metrics
|
33
|
-
|
151
|
+
@metrics ||= loadMetrics
|
152
|
+
end
|
153
|
+
|
154
|
+
def loadMetrics
|
155
|
+
@metrics = {
|
156
|
+
'# of Workbooks' => @twbCount,
|
157
|
+
'# of Data Sources' => @dataSourcesCount,
|
158
|
+
'# of Calculated Fields' => @calculatedFieldsCount,
|
159
|
+
'# of Referenced Fields' => @referencedFieldsCount,
|
160
|
+
}
|
34
161
|
end
|
35
162
|
|
36
163
|
#-- private methods begin here, to end of class
|
37
164
|
|
38
|
-
|
165
|
+
private
|
166
|
+
|
167
|
+
def emitGml
|
168
|
+
gml = Twb::Util::GML.new
|
169
|
+
gml.fileName = @twb.name
|
170
|
+
gml.nodes = @nodes
|
171
|
+
gml.edges = @edges
|
172
|
+
gml.render
|
173
|
+
end
|
174
|
+
|
175
|
+
def processDataSource ds
|
176
|
+
emit "======= DATA SOURCE: #{ds.uiname} ====== "
|
177
|
+
dsNodes = Set.new
|
178
|
+
dsEdges = Set.new
|
179
|
+
dsFields = {}
|
180
|
+
@twbFields[ds.uiname] = dsFields
|
181
|
+
calculatedFields = SortedSet.new
|
182
|
+
fieldFormulaLines = Array.new
|
183
|
+
referencedFields = SortedSet.new
|
184
|
+
# if @doGraph
|
185
|
+
dataSourceNode = Twb::Util::Graphnode.new(name: ds.uiname, id: ds.id, type: ds, properties: {workbook: @twb.name})
|
186
|
+
@nodes.add dataSourceNode
|
187
|
+
# end
|
188
|
+
#-- process Calculatred Fields
|
189
|
+
ds.calculatedFields.each do |calcField|
|
190
|
+
emit "Calculated Field: #{calcField}"
|
191
|
+
calculatedFields.add calcField.id
|
192
|
+
dsFields[calcField.uiname] = calcField
|
193
|
+
# if @doGraph
|
194
|
+
calcFieldNode = Twb::Util::Graphnode.new(name: calcField.uiname, id: calcField.id, type: calcField, properties: {:DataSource => ds.uiname})
|
195
|
+
@nodes.add calcFieldNode
|
196
|
+
dsFieldEdge = Twb::Util::Graphedge.new(from: dataSourceNode, to: calcFieldNode, relationship: 'contains')
|
197
|
+
@edges.add dsFieldEdge
|
198
|
+
# end
|
199
|
+
calculation = calcField.calculation
|
200
|
+
if calculation.has_formula
|
201
|
+
#-- collect field formulas as single lines
|
202
|
+
@csvCalculatedFields.push [
|
203
|
+
@calculatedFieldsCount += 1,
|
204
|
+
@twb.name,
|
205
|
+
# @modTime,
|
206
|
+
ds.uiname,
|
207
|
+
ds.caption,
|
208
|
+
ds.name,
|
209
|
+
calcField.uiname,
|
210
|
+
calcField.caption,
|
211
|
+
calcField.name,
|
212
|
+
ds.name + '::' + calcField.name,
|
213
|
+
calcField.datatype,
|
214
|
+
calcField.role,
|
215
|
+
calcField.type,
|
216
|
+
calculation.class,
|
217
|
+
calculation.scopeIsolation,
|
218
|
+
calculation.formulaFlat.length,
|
219
|
+
calculation.formulaFlatResolved,
|
220
|
+
calculation.formulaFlat,
|
221
|
+
calculation.comments,
|
222
|
+
calculation.is_lod
|
223
|
+
]
|
224
|
+
#-- collect individual formula lines
|
225
|
+
flnum = 0
|
226
|
+
emit "@@ calcField.uiname: #{calcField.uiname}"
|
227
|
+
calculation.formulaResolvedLines.each do |fl|
|
228
|
+
emit "@@ resolved line:: => '#{fl}'"
|
229
|
+
fieldFormulaLines << [ @calculatedFieldsCount, # 'Calc Field #',
|
230
|
+
@twb.name, # 'Workbook',
|
231
|
+
# @modTime,
|
232
|
+
ds.uiname, # 'Data Source',
|
233
|
+
ds.caption, # 'Data Source Caption',
|
234
|
+
ds.name, # 'Data Source Name (tech)',
|
235
|
+
calcField.uiname, # 'Field Name',
|
236
|
+
calcField.caption, # 'Field Caption',
|
237
|
+
calcField.name, # 'Field Name (tech)',
|
238
|
+
calcField.calculation.formulaFlatResolved, # 'Formula'
|
239
|
+
flnum += 1, # 'Formula Line #',
|
240
|
+
fl.start_with?(" ") ? "'#{fl}" : fl # 'Formula Line' - THIS IS A STUPID HACK NEEDED BECAUSE TABLEAU STRIPS LEADING BLANKS FROM CSV VALUES
|
241
|
+
]
|
242
|
+
end
|
243
|
+
#-- collect fields referenced in formula
|
244
|
+
emit "# Calculated Fields: #{calculation.calcFields.length}"
|
245
|
+
calculation.calcFields.each do |rf|
|
246
|
+
emit " referenced field ::'#{rf}'"
|
247
|
+
emit " referenced field.name ::'#{rf.name.nil?}' :: '#{rf.name}'"
|
248
|
+
emit " referenced field.uiname::'#{rf.uiname}'"
|
249
|
+
# if @doGraph
|
250
|
+
unless rf.uiname.nil?
|
251
|
+
properties = {'DataSource' => ds.uiname, 'DataSourceReference' => 'local', :source => rf}
|
252
|
+
refFieldNode = Twb::Util::Graphnode.new(name: rf.uiname, id: rf.id, type: rf.type, properties: properties)
|
253
|
+
@nodes.add refFieldNode
|
254
|
+
fieldFieldEdge = Twb::Util::Graphedge.new(from: calcFieldNode, to: refFieldNode, relationship: 'references')
|
255
|
+
@edges.add fieldFieldEdge
|
256
|
+
end
|
257
|
+
# end
|
258
|
+
referencedFields.add rf.id
|
259
|
+
refFieldTable = ds.fieldTable(rf.name)
|
260
|
+
emit "refFieldTable.nil? : #{refFieldTable.nil?}"
|
261
|
+
unless refFieldTable.nil?
|
262
|
+
tableID = refFieldTable + ':::' + ds.uiname
|
263
|
+
tableName = "||#{refFieldTable}||"
|
264
|
+
# if @doGraph
|
265
|
+
tableNode = Twb::Util::Graphnode.new(name: tableName, id: tableID, type: :DBTable, properties: properties)
|
266
|
+
@nodes.add tableNode
|
267
|
+
fieldFieldEdge = Twb::Util::Graphedge.new(from: refFieldNode, to: tableNode, relationship: 'is a field in')
|
268
|
+
@edges.add fieldFieldEdge
|
269
|
+
# end
|
270
|
+
# fldToDsNode = tableNode
|
271
|
+
end
|
272
|
+
@csvFormulaFields << [
|
273
|
+
@referencedFieldsCount += 1,
|
274
|
+
@twb.name,
|
275
|
+
# @modTime,
|
276
|
+
ds.uiname,
|
277
|
+
calcField.uiname,
|
278
|
+
calculation.formulaFlat,
|
279
|
+
calculation.formulaFlatResolved,
|
280
|
+
rf.name,
|
281
|
+
rf.uiname,
|
282
|
+
rf.id,
|
283
|
+
refFieldTable
|
284
|
+
]
|
285
|
+
end # resolvedFields.each do
|
286
|
+
end # if calculation.has_formula
|
287
|
+
end # ds.calculatedFields.each
|
288
|
+
|
289
|
+
dsRootFields = calculatedFields - referencedFields
|
290
|
+
@referencedFields.merge referencedFields
|
291
|
+
@twbRootFields.merge dsRootFields
|
292
|
+
if @doGraph
|
293
|
+
cypher @twb.name
|
294
|
+
cypherPy @twb.name
|
295
|
+
end
|
296
|
+
emit "#######################"
|
297
|
+
#--
|
298
|
+
#-- record calculated fields
|
299
|
+
twbDirCSV = @recordDir.nil? ? nil : @twbDir
|
300
|
+
emit "@@ record calculated fields ds: #{ds.uiname}"
|
301
|
+
@csvCalculatedFields.each do |r|
|
302
|
+
@csvCF << r
|
303
|
+
end
|
304
|
+
#-- record individual formula lines
|
305
|
+
emit "@@ individual formula lines ds: #{ds.uiname}"
|
306
|
+
fieldFormulaLines.each do |ffl|
|
307
|
+
@csvCFLs << ffl
|
308
|
+
end
|
309
|
+
#-- record formula-referenced fields
|
310
|
+
emit "@@ formula-referenced fields ds: #{ds.uiname}"
|
311
|
+
@csvFormulaFields.each do |r|
|
312
|
+
@csvFF << r
|
313
|
+
end
|
314
|
+
#--
|
315
|
+
return @imageFiles
|
316
|
+
end # def processDataSource
|
317
|
+
|
318
|
+
def emitCalcfield calcField
|
319
|
+
emit "\t FIELD cap :: #{calcField.caption} "
|
320
|
+
emit "\t tname:: #{calcField.name}"
|
321
|
+
emit "\t uiname:: #{calcField.uiname}"
|
322
|
+
emit "\t formula:: #{calculation.formulaFlat}"
|
323
|
+
end
|
324
|
+
|
325
|
+
def mapTwb
|
326
|
+
twb = @twb.name
|
327
|
+
rootFields = @twbRootFields
|
328
|
+
dotStuff = initDot twb
|
329
|
+
dotFile = dotStuff[:file]
|
330
|
+
dotFileName = dotStuff[:name]
|
331
|
+
dotFile.puts "\n // subgraph cluster_1 {"
|
332
|
+
dotFile.puts " // color= grey;"
|
333
|
+
dotFile.puts ""
|
334
|
+
edgesAsStrings = SortedSet.new
|
335
|
+
# this two step process coalesces the edges into a unique set, avoiding duplicating the dot
|
336
|
+
# file entries, and can be shrunk when graph edges expose the bits necessary for management by Set
|
337
|
+
emit "\n========================\nLoading Edges\n========================\n From DC? Referenced? Edge \n %s %s %s" % ['--------', '-----------', '-'*45]
|
338
|
+
@edges.each do |e|
|
339
|
+
# don't want to emit edge which is from a Data Connection to a
|
340
|
+
# Calculated Field which is also referenced by another calculated field
|
341
|
+
isFromDC = e.from.type == :TwbDataConnection
|
342
|
+
isRefField = @referencedFields.include?(e.to.id)
|
343
|
+
edgesAsStrings.add(e.dot) unless isFromDC && isRefField
|
344
|
+
# emit " ES #{e.dot}"
|
345
|
+
# emit " ES from #{e.from}"
|
346
|
+
# emit " ES to #{e.to}"
|
347
|
+
end
|
348
|
+
emit "------------------------\n "
|
349
|
+
edgesAsStrings.each do |es|
|
350
|
+
dotFile.puts " #{es}"
|
351
|
+
end
|
352
|
+
emit "========================\n "
|
353
|
+
dotFile.puts ""
|
354
|
+
dotFile.puts " // }"
|
355
|
+
dotFile.puts "\n\n // 4 NODES --------------------------------------------------------------------"
|
356
|
+
@nodes.each do |n|
|
357
|
+
dotFile.puts n.dotLabel
|
358
|
+
end
|
359
|
+
dotFile.puts "\n\n // 5--------------------------------------------------------------------"
|
360
|
+
emitTypes( dotFile )
|
361
|
+
closeDot( dotFile, twb )
|
362
|
+
emit "Rendering DOT file - #{twb}"
|
363
|
+
renderDot(twb,dotFileName,'pdf')
|
364
|
+
renderDot(twb,dotFileName,'png')
|
365
|
+
renderDot(twb,dotFileName,'svg')
|
366
|
+
# emitEdges
|
367
|
+
end
|
368
|
+
|
369
|
+
def cypher twbName
|
370
|
+
if @doGraph
|
371
|
+
cypher = Twb::Util::Cypher.new
|
372
|
+
cypher.fileName = "#{twbName}.calcFields"
|
373
|
+
cypher.nodes = @nodes
|
374
|
+
cypher.edges = @edges
|
375
|
+
cypher.render
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def cypherPy twbName
|
380
|
+
if @doGraph
|
381
|
+
cypher = Twb::Util::CypherPython.new
|
382
|
+
cypher.fileName = "#{twbName}.calcFields"
|
383
|
+
cypher.nodes = @nodes
|
384
|
+
cypher.edges = @edges
|
385
|
+
cypher.render
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
# def graphEdges twb
|
390
|
+
# # graphFile = File.new(twb + '.cypher', 'w')
|
391
|
+
# # # graphFile.puts "OKEY DOKE, graphing away"
|
392
|
+
# # cypherCode = Set.new
|
393
|
+
# # @edges.each do |edge|
|
394
|
+
# # cypherCode.add edge.from.cypherCreate
|
395
|
+
# # cypherCode.add edge.to.cypherCreate
|
396
|
+
# # cypherCode.add edge.cypherCreate
|
397
|
+
# # end
|
398
|
+
# # cypherCode.each do |cc|
|
399
|
+
# # graphFile.puts cc
|
400
|
+
# # end
|
401
|
+
# # graphFile.puts "\nreturn *"
|
402
|
+
# # graphFile.close unless graphFile.nil?
|
403
|
+
# # @imageFiles << File.basename(graphFile)
|
404
|
+
# end
|
405
|
+
|
406
|
+
def emitEdges
|
407
|
+
emit " %-15s %s" % ['type', 'Edge']
|
408
|
+
emit " %-15s %s" % ['-'*15, '-'*35]
|
409
|
+
@edges.each do |edge|
|
410
|
+
emit " %-15s %s" % [edge.from.type, edge.from]
|
411
|
+
emit " %-15s %s" % [edge.to.type, edge.to]
|
412
|
+
emit "\n "
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
def emitTypes dotFile
|
417
|
+
typedNodes = {}
|
418
|
+
dotFile.puts "\n\n // 2--------------------------------------------------------------------"
|
419
|
+
@edges.each do |edge|
|
420
|
+
emit " EDGE :: #{edge}"
|
421
|
+
loadNodeType typedNodes, edge.from
|
422
|
+
loadNodeType typedNodes, edge.to
|
423
|
+
end
|
424
|
+
typedNodes.each do |type, nodes|
|
425
|
+
# emit "+++++++++ typedNodes of '#{type}'' "
|
426
|
+
# nodes.each do |node|
|
427
|
+
# emit " -n- #{node}"
|
428
|
+
# end
|
429
|
+
rankSame(dotFile, type, nodes) unless type.eql? 'CalculatedField' # == :CalculatedField
|
430
|
+
end
|
431
|
+
# labelTypes dotFile, edges
|
432
|
+
end
|
433
|
+
|
434
|
+
def loadNodeType set, node
|
435
|
+
type = node.type
|
436
|
+
set[type] = Set.new unless set.include? type
|
437
|
+
set[type].add node
|
438
|
+
end
|
439
|
+
|
440
|
+
@@unrankedTypes = ['CalculationField']
|
441
|
+
def rankSame dotFile, type, nodes
|
442
|
+
return if @@unrankedTypes.include? type.to_s
|
443
|
+
@lines = SortedSet.new
|
444
|
+
nodes.each do |node|
|
445
|
+
@lines << node.id
|
446
|
+
end
|
447
|
+
dotFile.puts "\n // '#{type}' --------------------------------------------------------------------"
|
448
|
+
dotFile.puts "\n {rank=same "
|
449
|
+
@lines.each do |line|
|
450
|
+
dotFile.puts " \"#{line}\""
|
451
|
+
end
|
452
|
+
dotFile.puts " }"
|
453
|
+
end
|
454
|
+
|
455
|
+
def rankRootFields dotFile, dsRootFields
|
456
|
+
dotFile.puts "\n // Unreferenced (root) Calculated Fields -----------------------------------------"
|
457
|
+
dotFile.puts "\n {rank=same "
|
458
|
+
dsRootFields.each do |rf|
|
459
|
+
emit "ROOT FIELD: #{rf.class} :: #{rf}"
|
460
|
+
dotFile.puts " \"#{rf}\""
|
461
|
+
end
|
462
|
+
dotFile.puts " }"
|
463
|
+
end
|
464
|
+
|
465
|
+
def labelTypes dotFile
|
466
|
+
fromTos = Set.new
|
467
|
+
@edges.each do |edge|
|
468
|
+
# fromTos.add "\"Alien Data Source\" -> \"Alien Data Source\""
|
469
|
+
fromTos.add "\"#{edge.from.type}\""
|
470
|
+
fromTos.add "\"#{edge.to.type}\""
|
471
|
+
end
|
472
|
+
return if fromTos.empty?
|
473
|
+
dotFile.puts "\n // 3--------------------------------------------------------------------"
|
474
|
+
dotFile.puts ' subgraph cluster_0 {'
|
475
|
+
dotFile.puts ' color=white;'
|
476
|
+
dotFile.puts ' node [shape="box3d" style="filled" ];'
|
477
|
+
fromTos.each do |ft|
|
478
|
+
dotFile.puts " #{ft}"
|
479
|
+
end
|
480
|
+
dotFile.puts ' }'
|
481
|
+
end
|
482
|
+
|
483
|
+
def initDot twb
|
484
|
+
dotFileName = docFile("#{twb}#{@@processName}.dot")
|
485
|
+
dotFile = File.open(dotFileName,'w')
|
486
|
+
dotFile.puts @@dotHeader
|
487
|
+
return {:file => dotFile, :name => dotFileName}
|
488
|
+
end
|
489
|
+
|
490
|
+
def closeDot dotFile, twb
|
491
|
+
dotFile.puts ' '
|
492
|
+
dotFile.puts '// -------------------------------------------------------------'
|
493
|
+
dotFile.puts ' '
|
494
|
+
dotFile.puts ' subgraph cluster_1 {'
|
495
|
+
# dotFile.puts ' color=white;'
|
496
|
+
dotFile.puts ' style=invis;'
|
497
|
+
# dotFile.puts ' border=0;'
|
498
|
+
dotFile.puts ' node [border=blue];'
|
499
|
+
dotFile.puts ' '
|
500
|
+
dotFile.puts ' "" [style=invis]'
|
501
|
+
dotFile.puts " \"Tableau Tools\\nCalculated Fields Map\\nWorkbook '#{twb}'\\n#{Time.new.ctime}\" [penwidth=0]"
|
502
|
+
# dotFile.puts " \"Tableau Tools Workbook Calculated Fields Map\\n#{Time.new.ctime}\" -> \"\" [style=invis]"
|
503
|
+
dotFile.puts ' '
|
504
|
+
dotFile.puts ' }'
|
505
|
+
dotFile.puts ' '
|
506
|
+
dotFile.puts '}'
|
507
|
+
dotFile.close
|
508
|
+
end
|
509
|
+
|
510
|
+
|
511
|
+
def renderDot twb, dot, format
|
512
|
+
imageType = '-T' + format
|
513
|
+
imageFile = './ttdoc/' + twb + @@processName + 'Graph.' + format
|
514
|
+
imageParam = '-o"' + imageFile + '"'
|
515
|
+
emit "system #{@@gvDotLocation} #{imageType} #{imageParam} \"#{dot}\""
|
516
|
+
system "#{@@gvDotLocation} #{imageType} #{imageParam} \"#{dot}\""
|
517
|
+
emit " - #{imageFile}"
|
518
|
+
@imageFiles << imageFile
|
519
|
+
return imageFile
|
520
|
+
end
|
521
|
+
|
39
522
|
end # class
|
40
523
|
|
41
524
|
end # module Analysis
|
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.6.
|
4
|
+
version: 4.6.2
|
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-02-
|
11
|
+
date: 2019-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: creek
|