twb 1.9.1 → 2.2.1
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 +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
|