twb 3.7.5 → 3.9.3

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
  SHA1:
3
- metadata.gz: 183bcd3f582a9939faf1544d4b4ecfda0cd36f0a
4
- data.tar.gz: 3daae7542ce46541dd310e6f27d717530db9ac95
3
+ metadata.gz: 01e007d30bd02da639fec0bef95e94e39bf4c51b
4
+ data.tar.gz: 6f07a8ac8308a197494e6d89211f9569e1d31884
5
5
  SHA512:
6
- metadata.gz: aa968978835cf31847e3446bc02107662b00e3213e4d1345104ace41b6cddd3d2c6d7756bd6ec4ae9a6bcae22acb4904e53771658b210d1847852558f29d43f8
7
- data.tar.gz: c19335e986bbbf10166e96b97bb2e0064091fe2e71af00bbc0bc17c4e87b01fa51eefc019ea19e3e23149efaf341d2d95484ffdd06aa19b7e9122713439e639a
6
+ metadata.gz: 1c9413d8c790ebc4f11396b3c118a03e9bef3914b4a19b53384cf374572b24521f4f5de8cc6a7ebbc04d559d0ef13840263b68dd6da6566c22c69f39c3dacada
7
+ data.tar.gz: f0ebc5cbcf4e62b0b17021d97143d0b701b9af1ade87cee099c963d9af423b9cc5038c462239e9365460a69e4e0b0a144f8e52ca25c99f6852371d3e9547dd53
data/lib/twb.rb CHANGED
@@ -48,6 +48,7 @@ require_relative 'twb/util/docprep'
48
48
  require_relative 'twb/analysis/documentedfieldsmarkdownemitter'
49
49
  require_relative 'twb/analysis/annotatedfieldsCSVEmitter'
50
50
  require_relative 'twb/analysis/calculatedfields/calculatedfieldsanalyzer'
51
+ require_relative 'twb/analysis/calculatedfields/groupfieldsanalyzer'
51
52
  require_relative 'twb/analysis/calculatedfields/markdownemitter'
52
53
  require_relative 'twb/analysis/calculatedfields/csvemitter'
53
54
  require_relative 'twb/analysis/datasources/DataSourceFieldsCSVEmitter'
@@ -56,9 +57,11 @@ require_relative 'twb/analysis/datasources/googlesheetdatasourcesanalyzer'
56
57
  require_relative 'twb/analysis/Sheets/WorksheetDataStructureCSVEmitter'
57
58
  require_relative 'twb/analysis/Sheets/sheetfiltersanalyzer'
58
59
  require_relative 'twb/analysis/Sheets/sheetfieldsanalyzer'
60
+ require_relative 'twb/analysis/Sheets/dashsheetsanalyzer'
61
+
59
62
 
60
63
  # Represents Tableau Workbooks and their contents.
61
64
  #
62
65
  module Twb
63
- VERSION = '3.7.5'
66
+ VERSION = '3.9.3'
64
67
  end
@@ -26,7 +26,7 @@ module Analysis
26
26
  include TabTool
27
27
  include Graph
28
28
 
29
- attr_reader :calculatedFieldsCount, :formulaFieldsCount, :dataFiles
29
+ attr_reader :calculatedFieldsCount, :referencedFieldsCount, :metrics
30
30
  attr_accessor :ttdocdir
31
31
 
32
32
  @@ttlogfile = 'CalculatedFieldsAnalyzer.ttlog'
@@ -57,16 +57,16 @@ module Analysis
57
57
  'Formula', 'Formula Line #', 'Formula Line'
58
58
  ]
59
59
 
60
- @@formFieldsCSVFileName = 'TwbFormulaFields.csv'
60
+ @@formFieldsCSVFileName = 'TwbCalculatedFieldsReferenced.csv'
61
61
  @@formFieldsCSVFileHeader = ['Rec #',
62
62
  'Workbook', 'Workbook Dir',
63
63
  'Data Source',
64
64
  'Field - Calculated',
65
- 'Data Source - Formula (tech)',
66
- 'Data Source - Formula',
67
- 'Field - Formula (tech)',
68
- 'Field - Formula',
69
- 'Data Source + Field - Calculated',
65
+ 'Formula (tech)',
66
+ 'Formula',
67
+ 'Field - Referenced (tech)',
68
+ 'Field - Referenced',
69
+ 'Data Source + Field - Referenced',
70
70
  'Table'
71
71
  ]
72
72
 
@@ -82,7 +82,8 @@ module Analysis
82
82
  DOTHEADER
83
83
 
84
84
  def initialize
85
- @funcdoc = {:class=>self.class, :blurb=>'Analyzing Calculated Fields from Tableau Workbooks.', :description=>'Calculated fields can be complex, this tool provides robust coverage.',}
85
+ init
86
+ @funcdoc = {:class=>self.class, :blurb=>'Analyze Calculated Fields from Tableau Workbooks.', :description=>'Calculated fields can be complex, this tool provides robust coverage.',}
86
87
  #-- CSV records collectors
87
88
  @csvCalculatedFields = []
88
89
  @csvFormulaFields = []
@@ -90,7 +91,7 @@ DOTHEADER
90
91
  #-- Counters setup --
91
92
  @twbCount = 0
92
93
  @calculatedFieldsCount = 0
93
- @formulaFieldsCount = 0
94
+ @referencedFieldsCount = 0
94
95
  #--
95
96
  @referencedFields = SortedSet.new
96
97
  #--
@@ -127,342 +128,359 @@ DOTHEADER
127
128
  mapTwb
128
129
  emitGml
129
130
  @twbCount += 1
131
+ finis
130
132
  end
131
133
 
132
- def emitGml
133
- gml = Twb::Util::GML.new
134
- gml.fileName = @twb.name
135
- gml.nodes = @nodes
136
- gml.edges = @edges
137
- gml.render
138
- end
139
-
140
- def processDataSource ds
141
- emit "======= DATA SOURCE: #{ds.uiname} ====== "
142
- dsNodes = Set.new
143
- dsEdges = Set.new
144
- dsFields = {}
145
- @twbFields[ds.uiname] = dsFields
146
- calculatedFields = SortedSet.new
147
- fieldFormulaLines = []
148
- referencedFields = SortedSet.new
149
- dataSourceNode = Twb::Util::Graphnode.new(name: ds.uiname, id: ds.id, type: ds, properties: {workbook: @twb.name})
150
- @nodes.add dataSourceNode
151
- #-- process Calculatred Fields
152
- ds.calculatedFields.each do |calcField|
153
- calculatedFields.add calcField.id
154
- dsFields[calcField.uiname] = calcField
155
- calcFieldNode = Twb::Util::Graphnode.new(name: calcField.uiname, id: calcField.id, type: calcField, properties: {:DataSource => ds.uiname})
156
- @nodes.add calcFieldNode
157
- dsFieldEdge = Twb::Util::Graphedge.new(from: dataSourceNode, to: calcFieldNode, relationship: 'contains')
158
- @edges.add dsFieldEdge
159
- calculation = calcField.calculation
160
- if calculation.has_formula
161
- #-- collect field formulas as single lines
162
- @csvCalculatedFields.push [
163
- @calculatedFieldsCount += 1,
164
- @twb.name,
165
- @twbDir,
166
- ds.uiname,
167
- ds.caption,
168
- ds.name,
169
- calcField.uiname,
170
- calcField.caption,
171
- calcField.name,
172
- ds.name + '::' + calcField.name,
173
- calcField.datatype,
174
- calcField.role,
175
- calcField.type,
176
- calculation.class,
177
- calculation.scopeIsolation,
178
- calculation.formulaFlat.length,
179
- calculation.formulaResolved,
180
- calculation.formulaFlat,
181
- calculation.comments,
182
- calculation.is_lod
183
- ]
184
- #-- collect individual formula lines
185
- flnum = 0
186
- emit "@@ FL: #{calcField.uiname}"
187
- calculation.formulaResolvedLines.each do |fl|
188
- emit "@@ FL: => '#{fl}'"
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
- ]
202
- end
203
- #-- collect fields referenced in formula
204
- calculation.calcFields.each do |rf|
205
- emit " rf.name :'#{rf.name}'"
206
- emit " rf.uiname:'#{rf.uiname}'"
207
- properties = {'DataSource' => ds.uiname, 'DataSourceReference' => 'local', :source => rf}
208
- refFieldNode = Twb::Util::Graphnode.new(name: rf.uiname, id: rf.id, type: rf.type, properties: properties)
209
- @nodes.add refFieldNode
210
- fieldFieldEdge = Twb::Util::Graphedge.new(from: calcFieldNode, to: refFieldNode, relationship: 'references')
211
- @edges.add fieldFieldEdge
212
- referencedFields.add rf.id
213
- refFieldTable = ds.fieldTable(rf.name)
214
- emit "refFieldTable.nil? : #{refFieldTable.nil?}"
215
- unless refFieldTable.nil?
216
- tableID = refFieldTable + ':::' + ds.uiname
217
- tableName = "||#{refFieldTable}||"
218
- tableNode = Twb::Util::Graphnode.new(name: tableName, id: tableID, type: :DBTable, properties: properties)
219
- @nodes.add tableNode
220
- fieldFieldEdge = Twb::Util::Graphedge.new(from: refFieldNode, to: tableNode, relationship: 'is a field in')
221
- @edges.add fieldFieldEdge
222
- # fldToDsNode = tableNode
223
- end
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
- ]
237
- end # resolvedFields.each do
238
- end # if calculation.has_formula
239
- end # ds.calculatedFields.each
240
-
241
- dsRootFields = calculatedFields - referencedFields
242
- @referencedFields.merge referencedFields
243
- @twbRootFields.merge dsRootFields
244
- cypher @twb.name
245
- cypherPy @twb.name
246
- emit "#######################"
247
- #-- record calculated fields
248
- emit "@@ record calculated fields ds: #{ds.uiname}"
249
- @csvCalculatedFields.each do |r|
250
- @csvCF << r
251
- end
252
- #-- record individual formula lines
253
- emit "@@ individual formula lines ds: #{ds.uiname}"
254
- fieldFormulaLines.each do |ffl|
255
- @csvCFLs << ffl
256
- end
257
- #-- record formula-referenced fields
258
- emit "@@ formula-referenced fields ds: #{ds.uiname}"
259
- @csvFormulaFields.each do |r|
260
- @csvFF << r
261
- end
262
- #--
263
- return @imageFiles
264
- end # def processDataSource
265
-
266
- def emitCalcfield calcField
267
- emit "\t FIELD cap :: #{calcField.caption} "
268
- emit "\t tname:: #{calcField.name}"
269
- emit "\t uiname:: #{calcField.uiname}"
270
- emit "\t formula:: #{calculation.formulaFlat}"
271
- end
272
-
273
- def mapTwb
274
- twb = @twb.name
275
- rootFields = @twbRootFields
276
- dotStuff = initDot twb
277
- dotFile = dotStuff[:file]
278
- dotFileName = dotStuff[:name]
279
- dotFile.puts "\n // subgraph cluster_1 {"
280
- dotFile.puts " // color= grey;"
281
- dotFile.puts ""
282
- edgesAsStrings = SortedSet.new
283
- # this two step process coalesces the edges into a unique set, avoiding duplicating the dot
284
- # file entries, and can be shrunk when graph edges expose the bits necessary for management by Set
285
- emit "\n========================\nLoading Edges\n========================\n From DC? Referenced? Edge \n %s %s %s" % ['--------', '-----------', '-'*45]
286
- @edges.each do |e|
287
- # don't want to emit edge which is from a Data Connection to a
288
- # Calculated Field which is also referenced by another calculated field
289
- isFromDC = e.from.type == :TwbDataConnection
290
- isRefField = @referencedFields.include?(e.to.id)
291
- edgesAsStrings.add(e.dot) unless isFromDC && isRefField
292
- # emit " ES #{e.dot}"
293
- # emit " ES from #{e.from}"
294
- # emit " ES to #{e.to}"
295
- end
296
- emit "------------------------\n "
297
- edgesAsStrings.each do |es|
298
- dotFile.puts " #{es}"
299
- end
300
- emit "========================\n "
301
- dotFile.puts ""
302
- dotFile.puts " // }"
303
- dotFile.puts "\n\n // 4 NODES --------------------------------------------------------------------"
304
- @nodes.each do |n|
305
- dotFile.puts n.dotLabel
306
- end
307
- dotFile.puts "\n\n // 5--------------------------------------------------------------------"
308
- emitTypes( dotFile )
309
- closeDot( dotFile, twb )
310
- emit "Rendering DOT file - #{twb}"
311
- renderDot(twb,dotFileName,'pdf')
312
- renderDot(twb,dotFileName,'png')
313
- renderDot(twb,dotFileName,'svg')
314
- # emitEdges
315
- end
316
-
317
- def cypher twbName
318
- cypher = Twb::Util::Cypher.new
319
- cypher.fileName = "#{twbName}.calcFields"
320
- cypher.nodes = @nodes
321
- cypher.edges = @edges
322
- cypher.render
323
- end
324
-
325
- def cypherPy twbName
326
- cypher = Twb::Util::CypherPython.new
327
- cypher.fileName = "#{twbName}.calcFields"
328
- cypher.nodes = @nodes
329
- cypher.edges = @edges
330
- cypher.render
134
+ def metrics
135
+ @metrics ||= loadMetrics
331
136
  end
332
137
 
333
- # def graphEdges twb
334
- # # graphFile = File.new(twb + '.cypher', 'w')
335
- # # # graphFile.puts "OKEY DOKE, graphing away"
336
- # # cypherCode = Set.new
337
- # # @edges.each do |edge|
338
- # # cypherCode.add edge.from.cypherCreate
339
- # # cypherCode.add edge.to.cypherCreate
340
- # # cypherCode.add edge.cypherCreate
341
- # # end
342
- # # cypherCode.each do |cc|
343
- # # graphFile.puts cc
344
- # # end
345
- # # graphFile.puts "\nreturn *"
346
- # # graphFile.close unless graphFile.nil?
347
- # # @imageFiles << File.basename(graphFile)
348
- # end
349
-
350
- def emitEdges
351
- emit " %-15s %s" % ['type', 'Edge']
352
- emit " %-15s %s" % ['-'*15, '-'*35]
353
- @edges.each do |edge|
354
- emit " %-15s %s" % [edge.from.type, edge.from]
355
- emit " %-15s %s" % [edge.to.type, edge.to]
356
- emit "\n "
357
- end
138
+ def loadMetrics
139
+ @metrics = {
140
+ '# of Workbooks' => @twbCount,
141
+ '# of Calculated Fields' => @calculatedFieldsCount,
142
+ '# of Referenced Fields' => @referencedFieldsCount,
143
+ }
358
144
  end
359
145
 
360
- def emitTypes dotFile
361
- typedNodes = {}
362
- dotFile.puts "\n\n // 2--------------------------------------------------------------------"
363
- @edges.each do |edge|
364
- emit " EDGE :: #{edge}"
365
- loadNodeType typedNodes, edge.from
366
- loadNodeType typedNodes, edge.to
367
- end
368
- typedNodes.each do |type, nodes|
369
- # emit "+++++++++ typedNodes of '#{type}'' "
370
- # nodes.each do |node|
371
- # emit " -n- #{node}"
146
+ #-- private methods begin here, to end of class
147
+
148
+ private
149
+
150
+ def emitGml
151
+ gml = Twb::Util::GML.new
152
+ gml.fileName = @twb.name
153
+ gml.nodes = @nodes
154
+ gml.edges = @edges
155
+ gml.render
156
+ end
157
+
158
+ def processDataSource ds
159
+ emit "======= DATA SOURCE: #{ds.uiname} ====== "
160
+ dsNodes = Set.new
161
+ dsEdges = Set.new
162
+ dsFields = {}
163
+ @twbFields[ds.uiname] = dsFields
164
+ calculatedFields = SortedSet.new
165
+ fieldFormulaLines = []
166
+ referencedFields = SortedSet.new
167
+ dataSourceNode = Twb::Util::Graphnode.new(name: ds.uiname, id: ds.id, type: ds, properties: {workbook: @twb.name})
168
+ @nodes.add dataSourceNode
169
+ #-- process Calculatred Fields
170
+ ds.calculatedFields.each do |calcField|
171
+ calculatedFields.add calcField.id
172
+ dsFields[calcField.uiname] = calcField
173
+ calcFieldNode = Twb::Util::Graphnode.new(name: calcField.uiname, id: calcField.id, type: calcField, properties: {:DataSource => ds.uiname})
174
+ @nodes.add calcFieldNode
175
+ dsFieldEdge = Twb::Util::Graphedge.new(from: dataSourceNode, to: calcFieldNode, relationship: 'contains')
176
+ @edges.add dsFieldEdge
177
+ calculation = calcField.calculation
178
+ if calculation.has_formula
179
+ #-- collect field formulas as single lines
180
+ @csvCalculatedFields.push [
181
+ @calculatedFieldsCount += 1,
182
+ @twb.name,
183
+ @twbDir,
184
+ ds.uiname,
185
+ ds.caption,
186
+ ds.name,
187
+ calcField.uiname,
188
+ calcField.caption,
189
+ calcField.name,
190
+ ds.name + '::' + calcField.name,
191
+ calcField.datatype,
192
+ calcField.role,
193
+ calcField.type,
194
+ calculation.class,
195
+ calculation.scopeIsolation,
196
+ calculation.formulaFlat.length,
197
+ calculation.formulaFlatResolved,
198
+ calculation.formulaFlat,
199
+ calculation.comments,
200
+ calculation.is_lod
201
+ ]
202
+ #-- collect individual formula lines
203
+ flnum = 0
204
+ emit "@@ FL: #{calcField.uiname}"
205
+ calculation.formulaResolvedLines.each do |fl|
206
+ emit "@@ FL: => '#{fl}'"
207
+ fieldFormulaLines << [ @calculatedFieldsCount, # 'Calc Field #',
208
+ @twb.name, # 'Workbook',
209
+ @twbDir, # 'Workbook Dir',
210
+ ds.uiname, # 'Data Source',
211
+ ds.caption, # 'Data Source Caption',
212
+ ds.name, # 'Data Source Name (tech)',
213
+ calcField.uiname, # 'Field Name',
214
+ calcField.caption, # 'Field Caption',
215
+ calcField.name, # 'Field Name (tech)',
216
+ calcField.calculation.formulaFlatResolved, # 'Formula'
217
+ flnum += 1, # 'Formula Line #',
218
+ fl.start_with?(" ") ? "'#{fl}" : fl # 'Formula Line' - THIS IS A STUPID HACK NEEDED BECAUSE TABLEAU STRIPS LEADING BLANKS FROM CSV VALUES
219
+ ]
220
+ end
221
+ #-- collect fields referenced in formula
222
+ calculation.calcFields.each do |rf|
223
+ emit " rf.name :'#{rf.name}'"
224
+ emit " rf.uiname:'#{rf.uiname}'"
225
+ properties = {'DataSource' => ds.uiname, 'DataSourceReference' => 'local', :source => rf}
226
+ refFieldNode = Twb::Util::Graphnode.new(name: rf.uiname, id: rf.id, type: rf.type, properties: properties)
227
+ @nodes.add refFieldNode
228
+ fieldFieldEdge = Twb::Util::Graphedge.new(from: calcFieldNode, to: refFieldNode, relationship: 'references')
229
+ @edges.add fieldFieldEdge
230
+ referencedFields.add rf.id
231
+ refFieldTable = ds.fieldTable(rf.name)
232
+ emit "refFieldTable.nil? : #{refFieldTable.nil?}"
233
+ unless refFieldTable.nil?
234
+ tableID = refFieldTable + ':::' + ds.uiname
235
+ tableName = "||#{refFieldTable}||"
236
+ tableNode = Twb::Util::Graphnode.new(name: tableName, id: tableID, type: :DBTable, properties: properties)
237
+ @nodes.add tableNode
238
+ fieldFieldEdge = Twb::Util::Graphedge.new(from: refFieldNode, to: tableNode, relationship: 'is a field in')
239
+ @edges.add fieldFieldEdge
240
+ # fldToDsNode = tableNode
241
+ end
242
+ @csvFormulaFields << [
243
+ @referencedFieldsCount += 1,
244
+ @twb.name,
245
+ @twbDir,
246
+ ds.uiname,
247
+ calcField.uiname,
248
+ calculation.formulaFlat,
249
+ calculation.formulaFlatResolved,
250
+ rf.name,
251
+ rf.uiname,
252
+ rf.id,
253
+ refFieldTable
254
+ ]
255
+ end # resolvedFields.each do
256
+ end # if calculation.has_formula
257
+ end # ds.calculatedFields.each
258
+
259
+ dsRootFields = calculatedFields - referencedFields
260
+ @referencedFields.merge referencedFields
261
+ @twbRootFields.merge dsRootFields
262
+ cypher @twb.name
263
+ cypherPy @twb.name
264
+ emit "#######################"
265
+ #-- record calculated fields
266
+ emit "@@ record calculated fields ds: #{ds.uiname}"
267
+ @csvCalculatedFields.each do |r|
268
+ @csvCF << r
269
+ end
270
+ #-- record individual formula lines
271
+ emit "@@ individual formula lines ds: #{ds.uiname}"
272
+ fieldFormulaLines.each do |ffl|
273
+ @csvCFLs << ffl
274
+ end
275
+ #-- record formula-referenced fields
276
+ emit "@@ formula-referenced fields ds: #{ds.uiname}"
277
+ @csvFormulaFields.each do |r|
278
+ @csvFF << r
279
+ end
280
+ #--
281
+ return @imageFiles
282
+ end # def processDataSource
283
+
284
+ def emitCalcfield calcField
285
+ emit "\t FIELD cap :: #{calcField.caption} "
286
+ emit "\t tname:: #{calcField.name}"
287
+ emit "\t uiname:: #{calcField.uiname}"
288
+ emit "\t formula:: #{calculation.formulaFlat}"
289
+ end
290
+
291
+ def mapTwb
292
+ twb = @twb.name
293
+ rootFields = @twbRootFields
294
+ dotStuff = initDot twb
295
+ dotFile = dotStuff[:file]
296
+ dotFileName = dotStuff[:name]
297
+ dotFile.puts "\n // subgraph cluster_1 {"
298
+ dotFile.puts " // color= grey;"
299
+ dotFile.puts ""
300
+ edgesAsStrings = SortedSet.new
301
+ # this two step process coalesces the edges into a unique set, avoiding duplicating the dot
302
+ # file entries, and can be shrunk when graph edges expose the bits necessary for management by Set
303
+ emit "\n========================\nLoading Edges\n========================\n From DC? Referenced? Edge \n %s %s %s" % ['--------', '-----------', '-'*45]
304
+ @edges.each do |e|
305
+ # don't want to emit edge which is from a Data Connection to a
306
+ # Calculated Field which is also referenced by another calculated field
307
+ isFromDC = e.from.type == :TwbDataConnection
308
+ isRefField = @referencedFields.include?(e.to.id)
309
+ edgesAsStrings.add(e.dot) unless isFromDC && isRefField
310
+ # emit " ES #{e.dot}"
311
+ # emit " ES from #{e.from}"
312
+ # emit " ES to #{e.to}"
313
+ end
314
+ emit "------------------------\n "
315
+ edgesAsStrings.each do |es|
316
+ dotFile.puts " #{es}"
317
+ end
318
+ emit "========================\n "
319
+ dotFile.puts ""
320
+ dotFile.puts " // }"
321
+ dotFile.puts "\n\n // 4 NODES --------------------------------------------------------------------"
322
+ @nodes.each do |n|
323
+ dotFile.puts n.dotLabel
324
+ end
325
+ dotFile.puts "\n\n // 5--------------------------------------------------------------------"
326
+ emitTypes( dotFile )
327
+ closeDot( dotFile, twb )
328
+ emit "Rendering DOT file - #{twb}"
329
+ renderDot(twb,dotFileName,'pdf')
330
+ renderDot(twb,dotFileName,'png')
331
+ renderDot(twb,dotFileName,'svg')
332
+ # emitEdges
333
+ end
334
+
335
+ def cypher twbName
336
+ cypher = Twb::Util::Cypher.new
337
+ cypher.fileName = "#{twbName}.calcFields"
338
+ cypher.nodes = @nodes
339
+ cypher.edges = @edges
340
+ cypher.render
341
+ end
342
+
343
+ def cypherPy twbName
344
+ cypher = Twb::Util::CypherPython.new
345
+ cypher.fileName = "#{twbName}.calcFields"
346
+ cypher.nodes = @nodes
347
+ cypher.edges = @edges
348
+ cypher.render
349
+ end
350
+
351
+ # def graphEdges twb
352
+ # # graphFile = File.new(twb + '.cypher', 'w')
353
+ # # # graphFile.puts "OKEY DOKE, graphing away"
354
+ # # cypherCode = Set.new
355
+ # # @edges.each do |edge|
356
+ # # cypherCode.add edge.from.cypherCreate
357
+ # # cypherCode.add edge.to.cypherCreate
358
+ # # cypherCode.add edge.cypherCreate
359
+ # # end
360
+ # # cypherCode.each do |cc|
361
+ # # graphFile.puts cc
362
+ # # end
363
+ # # graphFile.puts "\nreturn *"
364
+ # # graphFile.close unless graphFile.nil?
365
+ # # @imageFiles << File.basename(graphFile)
372
366
  # end
373
- rankSame(dotFile, type, nodes) unless type.eql? 'CalculatedField' # == :CalculatedField
374
- end
375
- # labelTypes dotFile, edges
376
- end
377
367
 
378
- def loadNodeType set, node
379
- type = node.type
380
- set[type] = Set.new unless set.include? type
381
- set[type].add node
382
- end
383
-
384
- @@unrankedTypes = ['CalculationField']
385
- def rankSame dotFile, type, nodes
386
- return if @@unrankedTypes.include? type.to_s
387
- @lines = SortedSet.new
388
- nodes.each do |node|
389
- @lines << node.id
390
- end
391
- dotFile.puts "\n // '#{type}' --------------------------------------------------------------------"
392
- dotFile.puts "\n {rank=same "
393
- @lines.each do |line|
394
- dotFile.puts " \"#{line}\""
395
- end
396
- dotFile.puts " }"
397
- end
398
-
399
- def rankRootFields dotFile, dsRootFields
400
- dotFile.puts "\n // Unreferenced (root) Calculated Fields -----------------------------------------"
401
- dotFile.puts "\n {rank=same "
402
- dsRootFields.each do |rf|
403
- emit "ROOT FIELD: #{rf.class} :: #{rf}"
404
- dotFile.puts " \"#{rf}\""
405
- end
406
- dotFile.puts " }"
407
- end
408
-
409
-
410
- def labelTypes dotFile
411
- fromTos = Set.new
412
- @edges.each do |edge|
413
- # fromTos.add "\"Alien Data Source\" -> \"Alien Data Source\""
414
- fromTos.add "\"#{edge.from.type}\""
415
- fromTos.add "\"#{edge.to.type}\""
416
- end
417
- return if fromTos.empty?
418
- dotFile.puts "\n // 3--------------------------------------------------------------------"
419
- dotFile.puts ' subgraph cluster_0 {'
420
- dotFile.puts ' color=white;'
421
- dotFile.puts ' node [shape="box3d" style="filled" ];'
422
- fromTos.each do |ft|
423
- dotFile.puts " #{ft}"
424
- end
425
- dotFile.puts ' }'
426
- end
427
-
428
- def initDot twb
429
- dotFileName = docFile("#{twb}#{@@processName}.dot")
430
- dotFile = File.open(dotFileName,'w')
431
- dotFile.puts @@dotHeader
432
- return {:file => dotFile, :name => dotFileName}
433
- end
434
-
435
- def closeDot dotFile, twb
436
- dotFile.puts ' '
437
- dotFile.puts '// -------------------------------------------------------------'
438
- dotFile.puts ' '
439
- dotFile.puts ' subgraph cluster_1 {'
440
- # dotFile.puts ' color=white;'
441
- dotFile.puts ' style=invis;'
442
- # dotFile.puts ' border=0;'
443
- dotFile.puts ' node [border=blue];'
444
- dotFile.puts ' '
445
- dotFile.puts ' "" [style=invis]'
446
- dotFile.puts " \"Tableau Tools\\nCalculated Fields Map\\nWorkbook '#{twb}'\\n#{Time.new.ctime}\" [penwidth=0]"
447
- # dotFile.puts " \"Tableau Tools Workbook Calculated Fields Map\\n#{Time.new.ctime}\" -> \"\" [style=invis]"
448
- dotFile.puts ' '
449
- dotFile.puts ' }'
450
- dotFile.puts ' '
451
- dotFile.puts '}'
452
- dotFile.close
453
- end
454
-
455
-
456
- def renderDot twb, dot, format
457
- imageType = '-T' + format
458
- imageFile = './ttdoc/' + twb + @@processName + 'Graph.' + format
459
- imageParam = '-o"' + imageFile + '"'
460
- emit "system #{@@gvDotLocation} #{imageType} #{imageParam} \"#{dot}\""
461
- system "#{@@gvDotLocation} #{imageType} #{imageParam} \"#{dot}\""
462
- emit " - #{imageFile}"
463
- @imageFiles << imageFile
464
- return imageFile
465
- end
368
+ def emitEdges
369
+ emit " %-15s %s" % ['type', 'Edge']
370
+ emit " %-15s %s" % ['-'*15, '-'*35]
371
+ @edges.each do |edge|
372
+ emit " %-15s %s" % [edge.from.type, edge.from]
373
+ emit " %-15s %s" % [edge.to.type, edge.to]
374
+ emit "\n "
375
+ end
376
+ end
377
+
378
+ def emitTypes dotFile
379
+ typedNodes = {}
380
+ dotFile.puts "\n\n // 2--------------------------------------------------------------------"
381
+ @edges.each do |edge|
382
+ emit " EDGE :: #{edge}"
383
+ loadNodeType typedNodes, edge.from
384
+ loadNodeType typedNodes, edge.to
385
+ end
386
+ typedNodes.each do |type, nodes|
387
+ # emit "+++++++++ typedNodes of '#{type}'' "
388
+ # nodes.each do |node|
389
+ # emit " -n- #{node}"
390
+ # end
391
+ rankSame(dotFile, type, nodes) unless type.eql? 'CalculatedField' # == :CalculatedField
392
+ end
393
+ # labelTypes dotFile, edges
394
+ end
395
+
396
+ def loadNodeType set, node
397
+ type = node.type
398
+ set[type] = Set.new unless set.include? type
399
+ set[type].add node
400
+ end
401
+
402
+ @@unrankedTypes = ['CalculationField']
403
+ def rankSame dotFile, type, nodes
404
+ return if @@unrankedTypes.include? type.to_s
405
+ @lines = SortedSet.new
406
+ nodes.each do |node|
407
+ @lines << node.id
408
+ end
409
+ dotFile.puts "\n // '#{type}' --------------------------------------------------------------------"
410
+ dotFile.puts "\n {rank=same "
411
+ @lines.each do |line|
412
+ dotFile.puts " \"#{line}\""
413
+ end
414
+ dotFile.puts " }"
415
+ end
416
+
417
+ def rankRootFields dotFile, dsRootFields
418
+ dotFile.puts "\n // Unreferenced (root) Calculated Fields -----------------------------------------"
419
+ dotFile.puts "\n {rank=same "
420
+ dsRootFields.each do |rf|
421
+ emit "ROOT FIELD: #{rf.class} :: #{rf}"
422
+ dotFile.puts " \"#{rf}\""
423
+ end
424
+ dotFile.puts " }"
425
+ end
426
+
427
+
428
+ def labelTypes dotFile
429
+ fromTos = Set.new
430
+ @edges.each do |edge|
431
+ # fromTos.add "\"Alien Data Source\" -> \"Alien Data Source\""
432
+ fromTos.add "\"#{edge.from.type}\""
433
+ fromTos.add "\"#{edge.to.type}\""
434
+ end
435
+ return if fromTos.empty?
436
+ dotFile.puts "\n // 3--------------------------------------------------------------------"
437
+ dotFile.puts ' subgraph cluster_0 {'
438
+ dotFile.puts ' color=white;'
439
+ dotFile.puts ' node [shape="box3d" style="filled" ];'
440
+ fromTos.each do |ft|
441
+ dotFile.puts " #{ft}"
442
+ end
443
+ dotFile.puts ' }'
444
+ end
445
+
446
+ def initDot twb
447
+ dotFileName = docFile("#{twb}#{@@processName}.dot")
448
+ dotFile = File.open(dotFileName,'w')
449
+ dotFile.puts @@dotHeader
450
+ return {:file => dotFile, :name => dotFileName}
451
+ end
452
+
453
+ def closeDot dotFile, twb
454
+ dotFile.puts ' '
455
+ dotFile.puts '// -------------------------------------------------------------'
456
+ dotFile.puts ' '
457
+ dotFile.puts ' subgraph cluster_1 {'
458
+ # dotFile.puts ' color=white;'
459
+ dotFile.puts ' style=invis;'
460
+ # dotFile.puts ' border=0;'
461
+ dotFile.puts ' node [border=blue];'
462
+ dotFile.puts ' '
463
+ dotFile.puts ' "" [style=invis]'
464
+ dotFile.puts " \"Tableau Tools\\nCalculated Fields Map\\nWorkbook '#{twb}'\\n#{Time.new.ctime}\" [penwidth=0]"
465
+ # dotFile.puts " \"Tableau Tools Workbook Calculated Fields Map\\n#{Time.new.ctime}\" -> \"\" [style=invis]"
466
+ dotFile.puts ' '
467
+ dotFile.puts ' }'
468
+ dotFile.puts ' '
469
+ dotFile.puts '}'
470
+ dotFile.close
471
+ end
472
+
473
+
474
+ def renderDot twb, dot, format
475
+ imageType = '-T' + format
476
+ imageFile = './ttdoc/' + twb + @@processName + 'Graph.' + format
477
+ imageParam = '-o"' + imageFile + '"'
478
+ emit "system #{@@gvDotLocation} #{imageType} #{imageParam} \"#{dot}\""
479
+ system "#{@@gvDotLocation} #{imageType} #{imageParam} \"#{dot}\""
480
+ emit " - #{imageFile}"
481
+ @imageFiles << imageFile
482
+ return imageFile
483
+ end
466
484
 
467
485
  end # class
468
486