twb 1.9.1 → 2.2.1
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 +6 -2
- data/lib/twb/analysis/AnnotatedFieldsCSVEmitter.rb +182 -0
- data/lib/twb/analysis/CalculatedFields/MarkdownEmitter.rb +2 -1
- data/lib/twb/analysis/DataSources/DataSourceFieldsCSVEmitter.rb +250 -0
- data/lib/twb/analysis/DocumentedFieldsCSVEmitter.rb +208 -0
- data/lib/twb/analysis/DocumentedFieldsMarkdownEmitter.rb +77 -0
- data/lib/twb/calculatedfield.rb +36 -3
- data/lib/twb/columnfield.rb +70 -3
- data/lib/twb/datasource.rb +54 -108
- data/lib/twb/dbfield.rb +70 -0
- data/lib/twb/fieldcalculation.rb +9 -2
- data/lib/twb/localfield.rb +17 -23
- data/lib/twb/mappedfield.rb +62 -0
- data/lib/twb/metadatafield.rb +25 -6
- metadata +7 -2
- data/bin/twb.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ac4493d38cdcff16c2d5cb96873ba8356e80271
|
4
|
+
data.tar.gz: 8f77ffab827d962ec69aae1061d72978a65111ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f146956037bfdbd37161ee9a631dc86827afa4af34470199beb2553b152cdeb95201994b088a2028ac89cb5d54e728ff70af4c794e1f9d7a1a903d3af9ab5a2
|
7
|
+
data.tar.gz: ef4a7e6e3435b0e3bfa4d9ba096f8f055ec40ffc865196f0923b04cbd9818a9047c62799fb0f505ae1d940269719c71446e0227c44d79f15285fef058770bed8
|
data/lib/twb.rb
CHANGED
@@ -16,6 +16,7 @@
|
|
16
16
|
require_relative 'twb/dashboard'
|
17
17
|
require_relative 'twb/datasource'
|
18
18
|
require_relative 'twb/docdashboard'
|
19
|
+
require_relative 'twb/dbfield'
|
19
20
|
require_relative 'twb/localfield'
|
20
21
|
require_relative 'twb/metadatafield'
|
21
22
|
require_relative 'twb/storyboard'
|
@@ -35,14 +36,17 @@ require_relative 'twb/util/xraydashboards'
|
|
35
36
|
require_relative 'twb/util/graphnode'
|
36
37
|
require_relative 'twb/util/graphedge'
|
37
38
|
require_relative 'twb/util/graphedges'
|
39
|
+
require_relative 'twb/analysis/documentedfieldsmarkdownemitter'
|
40
|
+
require_relative 'twb/analysis/annotatedfieldsCSVEmitter'
|
38
41
|
require_relative 'twb/analysis/calculatedfields/calculatedfieldsanalyzer'
|
39
42
|
require_relative 'twb/analysis/calculatedfields/markdownemitter'
|
40
43
|
require_relative 'twb/analysis/calculatedfields/csvemitter'
|
44
|
+
require_relative 'twb/analysis/datasources/DataSourceFieldsCSVEmitter'
|
41
45
|
require_relative 'twb/analysis/datasources/DataSourceTableFieldsCSVEmitter'
|
42
|
-
require_relative 'twb/analysis/Sheets/WorksheetDataStructureCSVEmitter
|
46
|
+
require_relative 'twb/analysis/Sheets/WorksheetDataStructureCSVEmitter'
|
43
47
|
|
44
48
|
# Represents Tableau Workbooks and their contents.
|
45
49
|
#
|
46
50
|
module Twb
|
47
|
-
VERSION = '
|
51
|
+
VERSION = '2.2.1'
|
48
52
|
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# DocumentedFieldsCSVEmitter.rb - this Ruby script Copyright 2017 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
|
+
require 'twb'
|
17
|
+
require 'csv'
|
18
|
+
|
19
|
+
module Twb
|
20
|
+
module Analysis
|
21
|
+
|
22
|
+
class AnnotatedFieldsCSVEmitter
|
23
|
+
|
24
|
+
attr_reader :csvFileName, :csvRecords
|
25
|
+
attr_reader :dsCount, :fieldsCount
|
26
|
+
|
27
|
+
@@csvHeader = ['Record #',
|
28
|
+
'Workbook',
|
29
|
+
'Workbook Dir',
|
30
|
+
'Data Source',
|
31
|
+
'Field',
|
32
|
+
'Doc Type',
|
33
|
+
'Doc Line #',
|
34
|
+
'Doc Line'
|
35
|
+
]
|
36
|
+
@@csvFileType = 'TwbAnnotatedFields'
|
37
|
+
@@csvFileName = @@csvFileType + '.csv'
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
@csvFile = CSV.open(@@csvFileName,'w')
|
41
|
+
@csvFile << @@csvHeader
|
42
|
+
@csvRecords = Set.new
|
43
|
+
# --
|
44
|
+
@recNum = 0
|
45
|
+
@dsCount = 0
|
46
|
+
@fieldNum = 0
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.csvHeader
|
50
|
+
@@csvHeader
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.csvFileType
|
54
|
+
@@csvFileType
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.csvFileNaame
|
58
|
+
@@csvFileName
|
59
|
+
end
|
60
|
+
|
61
|
+
def processTwb twb
|
62
|
+
@twb = nil
|
63
|
+
@twb = twb if twb.instance_of? Twb::Workbook
|
64
|
+
@twb = Twb::Workbook.new(twb) if twb.instance_of? String
|
65
|
+
raise ArgumentError.new("ERROR in Workbok processing: '#{twb}' must be a Workbook (class) or the name of a Workbook (String), is a #{twb.class} \n ") unless @twb.is_a? Twb::Workbook
|
66
|
+
# --
|
67
|
+
dss = @twb.datasources
|
68
|
+
dss.each do |ds|
|
69
|
+
@dsname = ds.uiname
|
70
|
+
# puts "\n -- #{ds.uiname} "
|
71
|
+
# tables = Set.new
|
72
|
+
fields = {}
|
73
|
+
@dsCount += 1
|
74
|
+
ds.columnFields.each do |field|
|
75
|
+
recordField field
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end # def processTwb twb
|
79
|
+
|
80
|
+
def recordField field
|
81
|
+
@fieldNum += 1
|
82
|
+
@lineNum = 0
|
83
|
+
field.comment.each do |line|
|
84
|
+
# dispLine = if ''.eql?(line) the ? '' : line
|
85
|
+
@csvFile << [ @recNum+=1,
|
86
|
+
@twb.name,
|
87
|
+
@twb.dir,
|
88
|
+
@dsname,
|
89
|
+
field.uiname,
|
90
|
+
'Comment',
|
91
|
+
@lineNum+=1,
|
92
|
+
# ''.eql?(line) ? " " : line
|
93
|
+
line
|
94
|
+
]
|
95
|
+
end
|
96
|
+
unless field.calcField.nil?
|
97
|
+
@lineNum = 0
|
98
|
+
field.calcField.formulaResolvedLines.each do |line|
|
99
|
+
@csvFile << [ @recNum+=1,
|
100
|
+
@twb.name,
|
101
|
+
@twb.dir,
|
102
|
+
@dsname,
|
103
|
+
field.uiname,
|
104
|
+
'Formula',
|
105
|
+
@lineNum+=1,
|
106
|
+
line
|
107
|
+
]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# def recordField fields, field, props
|
113
|
+
# # puts "%-65s :: %s " % [fieldName,props]
|
114
|
+
# return if field.uiname.nil?
|
115
|
+
# if fields.has_key? field.uiname
|
116
|
+
# fields[field.uiname].merge! field.properties
|
117
|
+
# else
|
118
|
+
# fields[field.uiname] = field.properties
|
119
|
+
# end
|
120
|
+
# end
|
121
|
+
|
122
|
+
def emitFields fields
|
123
|
+
fields.each do |fieldName,props|
|
124
|
+
# puts "FIELD:: %-40s :: %s" % [fieldName,props.inspect]
|
125
|
+
# class = props[]
|
126
|
+
csvRec = [ @recNum+=1,
|
127
|
+
@twb.name,
|
128
|
+
@twb.dir,
|
129
|
+
@dsname,
|
130
|
+
fieldName,
|
131
|
+
props[:type],
|
132
|
+
props['hidden'],
|
133
|
+
props[:columnField],
|
134
|
+
props[:calculatedField],
|
135
|
+
props[:dbField],
|
136
|
+
props[:mappedField],
|
137
|
+
props[:metadataField]
|
138
|
+
]
|
139
|
+
@csvFile << csvRec
|
140
|
+
@csvRecords.add csvRec
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def recordTech field, type
|
145
|
+
@recNum+=1
|
146
|
+
field.properties.each do |name,value|
|
147
|
+
@csvFileTech << [ @recNum,
|
148
|
+
@twb.name,
|
149
|
+
@twb.dir,
|
150
|
+
@dsname,
|
151
|
+
field.uiname,
|
152
|
+
type,
|
153
|
+
name,
|
154
|
+
value
|
155
|
+
]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def emitTech dataSource, fieldName, type, properties
|
160
|
+
# puts "XX #{dataSource.uiname} :: #{fieldName} #{} :: #{type} :: #{properties}"
|
161
|
+
@recNum+=1
|
162
|
+
properties.each do |name,value|
|
163
|
+
@csvFileTech << [ @recNum,
|
164
|
+
@twb.name,
|
165
|
+
@twb.dir,
|
166
|
+
dataSource.uiname,
|
167
|
+
fieldName,
|
168
|
+
type,
|
169
|
+
name,
|
170
|
+
value
|
171
|
+
]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def cleanup
|
176
|
+
@csvFile.close unless @csvFile.nil?
|
177
|
+
end
|
178
|
+
|
179
|
+
end # class DocumentedFieldsCSVEmitter
|
180
|
+
|
181
|
+
end # module Analysis
|
182
|
+
end # module Twb
|
@@ -28,7 +28,7 @@ module CalculatedFields
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def processTwb twb
|
31
|
-
|
31
|
+
twb = File.basename(twb)
|
32
32
|
@twb = Twb::Workbook.new twb
|
33
33
|
@docFileName = twb + '.CalculatedFields.md'
|
34
34
|
@docFile = File.open(@docFileName,'w')
|
@@ -46,6 +46,7 @@ module CalculatedFields
|
|
46
46
|
calcFields.each do |fldname, field|
|
47
47
|
calculation = field.calculation
|
48
48
|
@docFile.puts "\n##### #{fldname} "
|
49
|
+
@docFile.puts "###### ...description..."
|
49
50
|
@docFile.puts "```"
|
50
51
|
if calculation.has_formula
|
51
52
|
@docFile.puts calculation.formulaResolved
|
@@ -0,0 +1,250 @@
|
|
1
|
+
# calculatedfieldsanalyzer.rb - this Ruby script Copyright 2017 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
|
+
require 'twb'
|
17
|
+
require 'csv'
|
18
|
+
|
19
|
+
module Twb
|
20
|
+
module Analysis
|
21
|
+
module DataSources
|
22
|
+
|
23
|
+
class DataSourceFieldsCSVEmitter
|
24
|
+
|
25
|
+
attr_reader :csvFileName, :csvRecords
|
26
|
+
attr_reader :dsCount, :fieldsCount
|
27
|
+
|
28
|
+
@@csvHeader = ['Record #',
|
29
|
+
'Workbook',
|
30
|
+
'Workbook Dir',
|
31
|
+
'Data Source',
|
32
|
+
'Field',
|
33
|
+
'Twb Type',
|
34
|
+
'Hidden', # props[:hidden],
|
35
|
+
'Type - Column', # props[:columnField],
|
36
|
+
'Type - Calculated', # props[:calculatedField],
|
37
|
+
'Type - Db', # props[:dbField],
|
38
|
+
'Type - Mapped', # props[:mappedField]
|
39
|
+
'Type - MetaData', # props[:mappedField]
|
40
|
+
]
|
41
|
+
@@csvFileType = 'TwbDataSourceFields'
|
42
|
+
@@csvFileName = @@csvFileType + '.csv'
|
43
|
+
|
44
|
+
@@tallHeader = ['Field #',
|
45
|
+
'Workbook',
|
46
|
+
'Workbook Dir',
|
47
|
+
'Data Source',
|
48
|
+
'Field',
|
49
|
+
'Field Type',
|
50
|
+
'Property - Name',
|
51
|
+
'Property - Value'
|
52
|
+
]
|
53
|
+
@@fullFileType = 'TwbDataSourceFieldsDetails'
|
54
|
+
@@fullFileName = @@fullFileType + '.csv'
|
55
|
+
|
56
|
+
|
57
|
+
@@fullHeader = ['Field #',
|
58
|
+
'Workbook',
|
59
|
+
'Workbook Dir',
|
60
|
+
'Data Source',
|
61
|
+
'Field',
|
62
|
+
'Field - source',
|
63
|
+
'Field - class',
|
64
|
+
'Field - path',
|
65
|
+
'Property - Name',
|
66
|
+
'Property - Value'
|
67
|
+
]
|
68
|
+
@@tallFileType = 'TwbDataSourceFieldsTech'
|
69
|
+
@@tallFileName = @@tallFileType + '.csv'
|
70
|
+
|
71
|
+
def initialize
|
72
|
+
@csvFile = CSV.open(@@csvFileName,'w')
|
73
|
+
@csvFile << @@csvHeader
|
74
|
+
@csvRecords = Set.new
|
75
|
+
puts "Opened: #{!@csvFile.nil?} - #{@@csvFileName}"
|
76
|
+
# --
|
77
|
+
@csvFileFull = CSV.open(@@fullFileName,'w')
|
78
|
+
@csvFileFull << @@fullHeader
|
79
|
+
puts "Opened: #{!@csvFileFull.nil?} - #{@@fullFileName} "
|
80
|
+
# --
|
81
|
+
@csvFileTech = CSV.open(@@tallFileName,'w')
|
82
|
+
@csvFileTech << @@tallHeader
|
83
|
+
puts "Opened: #{!@csvFileTech.nil?} - #{@@tallFileName}"
|
84
|
+
# --
|
85
|
+
@recNum = 0
|
86
|
+
@dsCount = 0
|
87
|
+
@fieldsCount = 0
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.csvHeader
|
91
|
+
@@csvHeader
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.csvFileType
|
95
|
+
@@csvFileType
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.csvFileNaame
|
99
|
+
@@csvFileName
|
100
|
+
end
|
101
|
+
|
102
|
+
def processTwb twb
|
103
|
+
@twb = nil
|
104
|
+
@twb = twb if twb.instance_of? Twb::Workbook
|
105
|
+
@twb = Twb::Workbook.new(twb) if twb.instance_of? String
|
106
|
+
raise ArgumentError.new("ERROR in Workbok processing: '#{twb}' must be a Workbook (class) or the name of a Workbook (String), is a #{twb.class} \n ") unless @twb.is_a? Twb::Workbook
|
107
|
+
# --
|
108
|
+
dss = @twb.datasources
|
109
|
+
dss.each do |ds|
|
110
|
+
@dsname = ds.uiname
|
111
|
+
# puts "\n -- #{ds.uiname} "
|
112
|
+
# tables = Set.new
|
113
|
+
fields = {}
|
114
|
+
@dsCount += 1
|
115
|
+
fclasses = Set.new
|
116
|
+
ds.localFields.each do |field|
|
117
|
+
recordFieldFull field, :local
|
118
|
+
# recordField( fields, field, {:type=>:local,:columnField=>true,:hidden=>field.hidden} )
|
119
|
+
# emitTech( ds, field.uiname, 'Local', field.properties)
|
120
|
+
# recordTech( field, 'LocalA')
|
121
|
+
end
|
122
|
+
ds.columnFields.each do |field|
|
123
|
+
recordFieldFull field, :column
|
124
|
+
# recordField( fields, field, {:type=>'column',:columnField=>true,:hidden=>field.hidden} )
|
125
|
+
# emitTech( ds, field.uiname, 'Column', field.properties)
|
126
|
+
# recordTech( field, 'ColumnA')
|
127
|
+
end
|
128
|
+
ds.calculatedFields.each do |field|
|
129
|
+
recordFieldFull field, :calc
|
130
|
+
# puts "WWW #{field.class} :: #{field} ::prop:: #{field.properties.class} :: #{field.properties}"
|
131
|
+
# recordField( fields, field, {:type=>'calc',:calculatedField=>true,:hidden=>field.hidden} )
|
132
|
+
# emitTech( ds, field.uiname, 'Calculated', field.properties)
|
133
|
+
# recordTech( field, 'CalcA')
|
134
|
+
end
|
135
|
+
ds.metadataFields.each do |field|
|
136
|
+
recordFieldFull field, :metadata
|
137
|
+
# recordField( fields, field, {:type=>'metadata',:metadataField=>true} )
|
138
|
+
# emitTech( ds, field.uiname, 'MetaData', field.properties)
|
139
|
+
# recordTech( field, 'MetadataA')
|
140
|
+
end
|
141
|
+
ds.dbFields.each do |field|
|
142
|
+
recordFieldFull field, :db
|
143
|
+
# recordField( fields, field, {:type=>'database',:dbField=>true} )
|
144
|
+
# emitTech( ds, field.uiname, 'Db', field.properties)
|
145
|
+
# recordTech( field, 'DbA')
|
146
|
+
end
|
147
|
+
ds.mappedFields.each do |field|
|
148
|
+
recordFieldFull field, :mapped
|
149
|
+
# recordField( fields, field, {:type=>'mapped',:mappedField=>true} )
|
150
|
+
# emitTech( ds, field.uiname, 'Mapped', field.properties)
|
151
|
+
# recordTech( field, 'MappedA')
|
152
|
+
end
|
153
|
+
emitFields(fields)
|
154
|
+
end
|
155
|
+
end # def processTwb twb
|
156
|
+
|
157
|
+
def recordFieldFull field, source
|
158
|
+
# print field.properties.nil? ? '-' : ":#{field.properties.length}"
|
159
|
+
# puts field.properties
|
160
|
+
@recNum+=1
|
161
|
+
field.properties.each do |name,value|
|
162
|
+
# print name
|
163
|
+
@csvFileFull << [ @recNum,
|
164
|
+
@twb.name,
|
165
|
+
@twb.dir,
|
166
|
+
@dsname,
|
167
|
+
field.uiname,
|
168
|
+
source,
|
169
|
+
field.class,
|
170
|
+
field.node.path.to_s.gsub(/[0-9]/,'').gsub('[]',''),
|
171
|
+
name,
|
172
|
+
value
|
173
|
+
]
|
174
|
+
# @csvFileFull << csvRec
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def recordField fields, field, props
|
179
|
+
# puts "%-65s :: %s " % [fieldName,props]
|
180
|
+
return if field.uiname.nil?
|
181
|
+
if fields.has_key? field.uiname
|
182
|
+
fields[field.uiname].merge! field.properties
|
183
|
+
else
|
184
|
+
fields[field.uiname] = field.properties
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def emitFields fields
|
189
|
+
fields.each do |fieldName,props|
|
190
|
+
# puts "FIELD:: %-40s :: %s" % [fieldName,props.inspect]
|
191
|
+
# class = props[]
|
192
|
+
csvRec = [ @recNum+=1,
|
193
|
+
@twb.name,
|
194
|
+
@twb.dir,
|
195
|
+
@dsname,
|
196
|
+
fieldName,
|
197
|
+
props[:type],
|
198
|
+
props['hidden'],
|
199
|
+
props[:columnField],
|
200
|
+
props[:calculatedField],
|
201
|
+
props[:dbField],
|
202
|
+
props[:mappedField],
|
203
|
+
props[:metadataField]
|
204
|
+
]
|
205
|
+
@csvFile << csvRec
|
206
|
+
@csvRecords.add csvRec
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def recordTech field, type
|
211
|
+
@recNum+=1
|
212
|
+
field.properties.each do |name,value|
|
213
|
+
@csvFileTech << [ @recNum,
|
214
|
+
@twb.name,
|
215
|
+
@twb.dir,
|
216
|
+
@dsname,
|
217
|
+
field.uiname,
|
218
|
+
type,
|
219
|
+
name,
|
220
|
+
value
|
221
|
+
]
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def emitTech dataSource, fieldName, type, properties
|
226
|
+
# puts "XX #{dataSource.uiname} :: #{fieldName} #{} :: #{type} :: #{properties}"
|
227
|
+
@recNum+=1
|
228
|
+
properties.each do |name,value|
|
229
|
+
@csvFileTech << [ @recNum,
|
230
|
+
@twb.name,
|
231
|
+
@twb.dir,
|
232
|
+
dataSource.uiname,
|
233
|
+
fieldName,
|
234
|
+
type,
|
235
|
+
name,
|
236
|
+
value
|
237
|
+
]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def cleanup
|
242
|
+
@csvFile.close unless @csvFile.nil?
|
243
|
+
end
|
244
|
+
|
245
|
+
end # class DataSourceFieldsCSVEmitter
|
246
|
+
|
247
|
+
|
248
|
+
end # module DataSources
|
249
|
+
end # module Analysis
|
250
|
+
end # module Twb
|