twb 4.6.1 → 4.6.2
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 +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
|