twb 4.9.3 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.calcFields.length > 0
55
+ if cf.referencedFields.length > 0
56
56
  fieldsRefOrder = []
57
57
  fieldsSortSet = SortedSet.new
58
- cf.calcFields.each do |field|
58
+ cf.referencedFields.each do |field|
59
59
  fieldsRefOrder.push field.uiname
60
60
  fieldsSortSet << field.uiname
61
61
  end
@@ -0,0 +1,6 @@
1
+ require 'twb'
2
+
3
+ twb = Twb::Workbook.new 'MS.twb'
4
+ ds = twb.datasources.to_a.last
5
+ cfs = ds.calculatedFields
6
+ cfs.each { |cf| puts "\n\n--\n#{cf.calculation.formula}\n--"; cf.calcFields.each { |f| puts " %-12s %s " % [f[:ds], f[:field] ] } }
@@ -14,6 +14,7 @@
14
14
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
15
 
16
16
  require 'csv'
17
+ require 'pry'
17
18
 
18
19
  module Twb
19
20
  module Analysis
@@ -32,6 +33,7 @@ module DataSources
32
33
  'Data Source',
33
34
  'File Name',
34
35
  'File Dir',
36
+ 'File Exists?',
35
37
  'File Modified',
36
38
  'File Size'
37
39
  ]
@@ -55,7 +57,7 @@ module DataSources
55
57
 
56
58
  def processTWB twb
57
59
  @twbName = twb.name
58
- @twDir = twb.dir
60
+ @twbDir = twb.dir
59
61
  twb.datasources.each do |ds|
60
62
  processFiles ds
61
63
  end
@@ -69,9 +71,10 @@ module DataSources
69
71
  fqFileName = twbFileDir.nil? ? twbFileName : twbFileDir + '/' + twbFileName
70
72
  fileName = File.basename fqFileName
71
73
  fileDir = File.dirname fqFileName
72
- modtime = File.mtime fqFileName
73
- size = File.size fqFileName
74
- data = [$recNum+=1, twbName, twb.dir, ds.uiname, fileName, fileDir, modtime, size]
74
+ exists = File.exist? fqFileName
75
+ modtime = exists ? File.mtime(fqFileName) : nil
76
+ size = exists ? File.size(fqFileName) : nil
77
+ data = [@recNum+=1, @twbName, @twbDir, ds.uiname, fileName, fileDir, exists, modtime, size]
75
78
  @csvFile << data
76
79
  end
77
80
  end
@@ -0,0 +1,94 @@
1
+ # sheetfieldsanalyzer.rb Copyright (C) 2018 Chris 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
+ require 'twb'
17
+ require 'csv'
18
+
19
+ module Twb
20
+ module Analysis
21
+
22
+ class DashboardsSummarizer
23
+
24
+ include TabTool
25
+
26
+ attr_accessor :localEmit
27
+
28
+ def initialize(**args)
29
+ @args = args
30
+ @recordDir = !@args.nil? && @args[:recordDir] == true
31
+ @ttdocdir = @args[:ttdocdir]
32
+ @csvAdd = !@args.nil? && args[:csvMode] == :add
33
+ @csvMode = @csvAdd ? 'a' : 'w'
34
+ init
35
+ @funcdoc = {:class=>self.class, :blurb=>'Analyze Dashboard Worksheets', :description=>'Identifies the Worksheets present in Dashboards.',}
36
+ #--
37
+ docFileName = docFile('DashboardSummaries.csv')
38
+ @dashboardsCSV = CSV.open(docFileName,@csvMode)
39
+ unless @csvAdd
40
+ if @recordDir
41
+ @dashboardsCSV << ['Rec #','Workbook','Dashboard','# Worksheets','Workbook Dir']
42
+ else
43
+ @dashboardsCSV << ['Rec #','Workbook','Dashboard','# Worksheets' ]
44
+ end
45
+ end
46
+ addDocFile @dashboardsCSV, docFileName, "Workbooks and their Dashboards' summaries"
47
+ #--
48
+ @twbCount = 0
49
+ @dashCount = 0
50
+ @recNum = 0
51
+ end
52
+
53
+ def metrics
54
+ {
55
+ # '# of Workbooks' => @twbCount,
56
+ '# of Dashboards' => @dashCount,
57
+ }
58
+ end
59
+
60
+ def processTWB twb
61
+ @twb = twb
62
+ @twbName = @twb.name
63
+ @twbDir = @twb.dir
64
+ @modTime = @twb.modtime
65
+ emit " -- #{@twbName}"
66
+ @twbCount += 1
67
+ parseDashes
68
+ finis
69
+ end
70
+
71
+ def parseDashes
72
+ @dashboards = @twb.dashboards
73
+ @dashboards.each do |dash|
74
+ emit "DASH:: #{dash.name}"
75
+ @dashCount += 1
76
+ recordCSV [@twbName, dash.name, dash.worksheets.length]
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def recordCSV record
83
+ numberedRec = [@recNum+=1] + record
84
+ if @recordDir
85
+ @dashboardsCSV << numberedRec.push(@twbDir)
86
+ else
87
+ @dashboardsCSV << numberedRec
88
+ end
89
+ end
90
+
91
+ end #class SheetFieldsAnalyzer
92
+
93
+ end # module Analysis
94
+ end # module Twb
@@ -37,7 +37,7 @@ module Analysis
37
37
  docFileName = docFile('WorkbookSummary.csv')
38
38
  @csvFile = CSV.open(docFileName,@csvMode)
39
39
  unless @csvAdd
40
- csvHeader = ['Rec #', 'Workbook','Type','Version','Build','Platform','Modified','# Data Sources','# Dashboards','# Worksheets']
40
+ csvHeader = ['Rec #', 'Workbook','Directory','Type','Version','Build','Platform','Modified','# Data Sources','# Dashboards','# Worksheets']
41
41
  if @recordDir
42
42
  csvHeader.push('Workbook Directory')
43
43
  end
@@ -65,6 +65,7 @@ module Analysis
65
65
  @twb = twb
66
66
  emit " -- #{@twbName}"
67
67
  recordCSV [ @twb.name,
68
+ @twb.dir,
68
69
  @twb.type,
69
70
  @twb.version,
70
71
  @twb.build,
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014, 2015, 2017 Chris Gerrard
1
+ # Copyright (C) 2014, 2015, 2020 Chris Gerrard
2
2
  #
3
3
  # This program is free software: you can redistribute it and/or modify
4
4
  # it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@ module Twb
26
26
  attr_reader :node, :properties
27
27
  attr_reader :caption, :name, :uiname
28
28
  attr_reader :datatype, :role, :propType
29
- attr_reader :calculation, :calcFields
29
+ attr_reader :calculation, :referencedFields
30
30
  attr_reader :isGroup, :groupMembers
31
31
  attr_reader :hidden
32
32
 
@@ -52,8 +52,8 @@ module Twb
52
52
  @properties ||= loadProperties
53
53
  end
54
54
 
55
- def calcFields
56
- @calculation.calcFields
55
+ def referencedFields
56
+ @calculation.referencedFields
57
57
  end
58
58
 
59
59
  def formulaLines
@@ -14,6 +14,7 @@
14
14
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
15
 
16
16
  require 'nokogiri'
17
+ require 'pry'
17
18
 
18
19
  module Twb
19
20
 
@@ -85,7 +86,6 @@ module Twb
85
86
  @aggregation = load 'aggregation'
86
87
  @autoColumn = load 'auto-column'
87
88
  @datatypeCustomized = load 'datatype-customized'
88
- # @calcField = loadCalcField
89
89
  end
90
90
 
91
91
  def id
@@ -94,7 +94,7 @@ module Twb
94
94
 
95
95
  def load nodeName
96
96
  attr = @node.attribute(nodeName)
97
- val = attr.nil? ? nil : attr.text.strip.gsub(/^\[|\]$/,'')
97
+ val = attr.nil? ? nil : attr.text.strip
98
98
  return val
99
99
  end
100
100
 
@@ -56,7 +56,7 @@ module Twb
56
56
  attr_reader :tableFieldsMap
57
57
  attr_reader :fieldUINames
58
58
  attr_reader :aliases
59
- attr_reader :calculatedFields, :calculatedFieldNamesMap, :calculatedFieldNames, :calculatedField
59
+ attr_reader :calculatedFields, :calculatedFieldsMap, :calculatedFieldNames, :calculatedField
60
60
  attr_reader :allFields
61
61
  attr_reader :groups
62
62
  attr_reader :filters
@@ -391,40 +391,6 @@ module Twb
391
391
  return @fieldUINames
392
392
  end
393
393
 
394
- def loadFieldUINames
395
- # puts 'loadFieldUINames'
396
- @fieldUINames = {}
397
- # puts 'metadataFields'
398
- metadataFields.each do |fld|
399
- # puts " - name:%-45s | uiname:%-45s | localName:%-45s " % [ "'#{fld.name}'", "'#{fld.uiname}'", "'#{fld.localName}'"]
400
- @fieldUINames[fld.uiname] = fld.uiname
401
- @fieldUINames[fld.localName] = fld.uiname unless fld.localName.nil?
402
- @fieldUINames[fld.name] = fld.uiname unless fld.name.nil?
403
- end
404
- # puts 'calculatedFields'
405
- calculatedFields.each do |fld|
406
- # puts " - name:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.uiname}'"]
407
- @fieldUINames[fld.name] = fld.uiname
408
- @fieldUINames[fld.uiname] = fld.uiname
409
- end
410
- # puts 'localFields'
411
- localFields.each do |fld|
412
- # puts " - name:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.uiname}'"]
413
- @fieldUINames[fld.name] = fld.uiname
414
- @fieldUINames[fld.uiname] = fld.uiname
415
- end
416
- # puts "columnFields: #{columnFields.length}"
417
- columnFields.each do |fld|
418
- @fieldUINames[fld.name] = fld.uiname
419
- @fieldUINames[fld.uiname] = fld.uiname
420
- end
421
- groups.each do |fld|
422
- @fieldUINames[fld.name] = fld.uiname
423
- @fieldUINames[fld.uiname] = fld.uiname
424
- end
425
- return @fieldUINames
426
- end
427
-
428
394
  def tableFields
429
395
  @tableFieldsMap.values
430
396
  end
@@ -482,8 +448,7 @@ module Twb
482
448
 
483
449
  def loadAllFields
484
450
  @allFields = SortedSet.new
485
- dbf = dbFields
486
- @allFields << dbf
451
+ @allFields << dbFields
487
452
  @allFields << calculatedFieldNames
488
453
  end
489
454
 
@@ -532,6 +497,53 @@ module Twb
532
497
 
533
498
  private
534
499
 
500
+ def loadFieldUINames
501
+ @fieldUINames = {}
502
+ # puts"metadataFields: #{metadataFields.length}"
503
+ metadataFields.each do |fld|
504
+ # puts" - name:%-45s | caption:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.caption}'", "'#{fld.uiname}'"]
505
+ unless fld.name.nil?
506
+ @fieldUINames[fld.uiname] = fld.uiname
507
+ @fieldUINames[fld.localName] = fld.uiname unless fld.localName.nil?
508
+ @fieldUINames[fld.name] = fld.uiname
509
+ end
510
+ end
511
+ # puts"localFields: #{localFields.length}"
512
+ localFields.each do |fld|
513
+ # puts" - name:%-45s | caption:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.caption}'", "'#{fld.uiname}'"]
514
+ unless fld.caption.nil?
515
+ @fieldUINames[fld.name] = fld.caption
516
+ @fieldUINames[fld.uiname] = fld.caption
517
+ end
518
+ end
519
+ # puts"groups: #{groups.length}"
520
+ groups.each do |fld|
521
+ # puts" - name:%-45s | caption:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.caption}'", "'#{fld.uiname}'"]
522
+ unless fld.caption.nil?
523
+ @fieldUINames[fld.name] = fld.caption
524
+ @fieldUINames[fld.uiname] = fld.caption
525
+ end
526
+ end
527
+ # puts"calculatedFields: #{calculatedFields.length}"
528
+ calculatedFields.each do |fld|
529
+ # puts" - name:%-45s | caption:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.caption}'", "'#{fld.uiname}'"]
530
+ unless fld.caption.nil?
531
+ @fieldUINames[fld.name] = fld.caption
532
+ @fieldUINames[fld.uiname] = fld.caption
533
+ end
534
+ end
535
+ # puts"columnFields: #{columnFields.length}"
536
+ columnFields.each do |fld|
537
+ # puts" - name:%-45s | caption:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.caption}'", "'#{fld.uiname}'"]
538
+ unless fld.caption.nil?
539
+ # @fieldUINames[fld.name] = fld.caption
540
+ @fieldUINames[fld.name.gsub(/^[\[]|[\]]$/,'')] = fld.caption
541
+ @fieldUINames[fld.uiname] = fld.caption
542
+ end
543
+ end
544
+ return @fieldUINames
545
+ end
546
+
535
547
  def loadGroups
536
548
  @groups = []
537
549
  groupNodes = @node.xpath('.//group')