twb 0.5.3 → 1.0
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 +7 -0
- data/lib/twb/action.rb +121 -0
- data/lib/twb/countNodes.rb +98 -0
- data/lib/twb/dashboard.rb +5 -1
- data/lib/twb/datasource.rb +196 -6
- data/lib/twb/fieldcalculation.rb +54 -18
- data/lib/twb/findDSFields.rb +153 -0
- data/lib/twb/graph.rb +53 -0
- data/lib/twb/graphedges.rb +36 -0
- data/lib/twb/graphnode.rb +53 -0
- data/lib/twb/identifyFields.rb +103 -0
- data/lib/twb/localfield.rb +7 -6
- data/lib/twb/util/graphedge.rb +69 -0
- data/lib/twb/util/graphedges.rb +38 -0
- data/lib/twb/util/graphnode.rb +94 -0
- data/lib/twb/workbook.rb +60 -40
- data/lib/twb/worksheet.rb +69 -1
- data/lib/twb.rb +6 -1
- metadata +25 -17
data/lib/twb/workbook.rb
CHANGED
@@ -19,14 +19,18 @@ require 'zip'
|
|
19
19
|
module Twb
|
20
20
|
|
21
21
|
|
22
|
-
#
|
22
|
+
# A Tableau Workbook and its parts.
|
23
23
|
#
|
24
24
|
class Workbook
|
25
25
|
|
26
|
-
attr_reader :workbooknode
|
27
|
-
attr_reader :
|
26
|
+
attr_reader :workbooknode
|
27
|
+
attr_reader :name, :dir
|
28
|
+
attr_reader :modtime, :version, :build
|
29
|
+
attr_reader :datasources, :datasourceNames, :datasourceUINames
|
30
|
+
attr_reader :dashboards, :storyboards, :worksheets, :actions
|
31
|
+
attr_reader :valid, :ndoc
|
28
32
|
|
29
|
-
# Creates a Workbook
|
33
|
+
# Creates a Workbook from its file name.
|
30
34
|
#
|
31
35
|
# == Parameters:
|
32
36
|
# twbWithDir
|
@@ -67,17 +71,29 @@ module Twb
|
|
67
71
|
loadDashboards
|
68
72
|
loadStoryboards
|
69
73
|
loadWindows
|
74
|
+
loadActions
|
70
75
|
@valid = true
|
71
76
|
end
|
72
77
|
|
73
78
|
def loaddatasources
|
74
|
-
|
75
|
-
@
|
76
|
-
@
|
77
|
-
@
|
79
|
+
# puts "LOAD DATA SOURCES"
|
80
|
+
# @dataSourcesNode = @ndoc.at_xpath('//workbook/datasources')
|
81
|
+
@datasources = Set.new
|
82
|
+
@datasourceNames = Set.new
|
83
|
+
@datasourceUINames = Set.new
|
84
|
+
@dataSourceNamesMap = {}
|
85
|
+
datasourceNodes = @ndoc.xpath('//workbook/datasources/datasource')
|
86
|
+
# puts "DATASOURCENODES : #{@datasourceNodes.length}"
|
87
|
+
datasourceNodes.each do |node|
|
78
88
|
datasource = Twb::DataSource.new(node)
|
79
|
-
@datasources
|
89
|
+
@datasources << datasource
|
90
|
+
@datasourceNames << datasource.name
|
91
|
+
@datasourceNames << datasource.uiname
|
92
|
+
@datasourceUINames << datasource.uiname
|
93
|
+
@dataSourceNamesMap[datasource.name] = datasource
|
94
|
+
@dataSourceNamesMap[datasource.uiname] = datasource
|
80
95
|
end
|
96
|
+
# puts "DATASOURCES : #{@datasources.length}"
|
81
97
|
end
|
82
98
|
|
83
99
|
def loadWorksheets
|
@@ -92,7 +108,7 @@ module Twb
|
|
92
108
|
def loadDashboards
|
93
109
|
@dashesNode = @ndoc.at_xpath('//workbook/dashboards')
|
94
110
|
@dashboards = {}
|
95
|
-
dashes = @ndoc.xpath('//workbook/dashboards/dashboard'
|
111
|
+
dashes = @ndoc.xpath('//workbook/dashboards/dashboard').to_a
|
96
112
|
dashes.each do |node|
|
97
113
|
unless node.attr('type') == 'storyboard' then
|
98
114
|
dashboard = Twb::Dashboard.new(node, @worksheets)
|
@@ -120,10 +136,28 @@ module Twb
|
|
120
136
|
end
|
121
137
|
end
|
122
138
|
|
123
|
-
def
|
124
|
-
@
|
139
|
+
def loadActions
|
140
|
+
@actions = {}
|
141
|
+
actionNodes = @ndoc.xpath("//workbook/actions/action")
|
142
|
+
actionNodes.each do |anode|
|
143
|
+
action = Twb::Action.new(anode, @workbooknode)
|
144
|
+
@actions[action.uiname] = action
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def actions
|
149
|
+
@actions.values
|
150
|
+
end
|
151
|
+
|
152
|
+
def actionNames
|
153
|
+
@actions.keys
|
125
154
|
end
|
126
155
|
|
156
|
+
# def datasources
|
157
|
+
# # puts "ACCESSING DATASOURCES - #{@datasources.class} - #{@datasources.length} - #{@datasources.values.length}"
|
158
|
+
# return @datasources.values
|
159
|
+
# end
|
160
|
+
|
127
161
|
def dashboards
|
128
162
|
@dashboards.values
|
129
163
|
end
|
@@ -132,6 +166,10 @@ module Twb
|
|
132
166
|
@dashboards.keys
|
133
167
|
end
|
134
168
|
|
169
|
+
def dashboard name
|
170
|
+
@dashboards[name]
|
171
|
+
end
|
172
|
+
|
135
173
|
def storyboards
|
136
174
|
@storyboards.values
|
137
175
|
end
|
@@ -140,20 +178,6 @@ module Twb
|
|
140
178
|
@worksheets.values
|
141
179
|
end
|
142
180
|
|
143
|
-
def datasourceNames
|
144
|
-
@datasources.keys
|
145
|
-
end
|
146
|
-
|
147
|
-
def datasourceUINames
|
148
|
-
uiNames = []
|
149
|
-
@datasources.values.each {|ds| uiNames << ds.uiname}
|
150
|
-
return uiNames
|
151
|
-
end
|
152
|
-
|
153
|
-
def dashboardNames
|
154
|
-
@dashboards.keys
|
155
|
-
end
|
156
|
-
|
157
181
|
def storyboardNames
|
158
182
|
@storyboards.keys
|
159
183
|
end
|
@@ -163,11 +187,7 @@ module Twb
|
|
163
187
|
end
|
164
188
|
|
165
189
|
def datasource name
|
166
|
-
@
|
167
|
-
end
|
168
|
-
|
169
|
-
def dashboard name
|
170
|
-
@dashboards[name]
|
190
|
+
@dataSourceNamesMap[name]
|
171
191
|
end
|
172
192
|
|
173
193
|
def storyboard name
|
@@ -178,15 +198,6 @@ module Twb
|
|
178
198
|
@worksheets[name]
|
179
199
|
end
|
180
200
|
|
181
|
-
# Make sure that the TWB has a <dashboards> node.
|
182
|
-
# It's possible for a TWB to have no dashboards, and therefore no <dashboards> node.
|
183
|
-
def ensureDashboardsNodeExists
|
184
|
-
if @dashesNode.nil?
|
185
|
-
@dashesNode = Nokogiri::XML::Node.new "dashboards", @ndoc
|
186
|
-
# TODO fix this @dataSourcesNode.add_next_sibling(@dashesNode)
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
201
|
def ensureWindowsNodeExists
|
191
202
|
if @windowsnode.nil?
|
192
203
|
@windowsnode = Nokogiri::XML::Node.new "windows", @ndoc
|
@@ -207,6 +218,15 @@ module Twb
|
|
207
218
|
@windowsnode.add_child(docDashboard.winnode)
|
208
219
|
end
|
209
220
|
|
221
|
+
# Make sure that the TWB has a <dashboards> node.
|
222
|
+
# It's possible for a TWB to have no dashboards, and therefore no <dashboards> node.
|
223
|
+
def ensureDashboardsNodeExists
|
224
|
+
if @dashesNode.nil?
|
225
|
+
@dashesNode = Nokogiri::XML::Node.new "dashboards", @ndoc
|
226
|
+
# TODO fix this @dataSourcesNode.add_next_sibling(@dashesNode)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
210
230
|
def getNewDashboardTitle(t)
|
211
231
|
title = t
|
212
232
|
if @datasources.include?(title)
|
data/lib/twb/worksheet.rb
CHANGED
@@ -32,12 +32,14 @@ module Twb
|
|
32
32
|
|
33
33
|
@@hasher = Digest::SHA256.new
|
34
34
|
|
35
|
-
attr_reader :node,
|
35
|
+
attr_reader :node, :name, :datasourcenames, :datasources
|
36
|
+
attr_reader :fields, :rowFields, :colFields, :datasourceFields
|
36
37
|
|
37
38
|
def initialize sheetNode
|
38
39
|
@node = sheetNode
|
39
40
|
@name = @node.attr('name')
|
40
41
|
loadDataSourceNames
|
42
|
+
loadFields
|
41
43
|
return self
|
42
44
|
end
|
43
45
|
|
@@ -58,6 +60,72 @@ module Twb
|
|
58
60
|
@datasources[name]
|
59
61
|
end
|
60
62
|
|
63
|
+
def datasourceFields
|
64
|
+
@datasourceFields.nil? ? loadFields : @datasourceFields
|
65
|
+
end
|
66
|
+
|
67
|
+
def fields
|
68
|
+
@fields.nil? ? loadFields : @fields
|
69
|
+
end
|
70
|
+
|
71
|
+
def loadFields
|
72
|
+
# puts "WORKSHEET loadFields"
|
73
|
+
@datasourceFields = {}
|
74
|
+
dsNodes = @node.xpath('.//datasource-dependencies')
|
75
|
+
dsNodes.each do |dsn|
|
76
|
+
dsName = dsn.attribute('datasource').text
|
77
|
+
dsfs = @datasourceFields[dsName] = []
|
78
|
+
fieldNodes = dsn.xpath('./column')
|
79
|
+
fieldNodes.each do |fn|
|
80
|
+
fname = fn.attribute('name')
|
81
|
+
dsfs.push fname.text.gsub(/^\[/,'').gsub(/\]$/,'') unless fname.nil?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
# <rows>([DATA Connection (copy 2)].[none:JIRA HARVEST Correspondence Name:nk] / [DATA Connection (copy 2)].[none:CreatedDate (Issue) (DAY):ok])</rows>
|
85
|
+
# <cols>[DATA Connection (copy 2)].[:Measure Names]</cols>
|
86
|
+
@fields = {}
|
87
|
+
@rowFields = pullRowColFields './/rows' # returns map of data source => (Set of) field names
|
88
|
+
@fields[:rows] = @rowFields
|
89
|
+
@colFields = pullRowColFields './/cols' # returns map of data source => (Set of) field names
|
90
|
+
@fields[:cols] = @colFields
|
91
|
+
end
|
92
|
+
|
93
|
+
def addDSFields fields, usage
|
94
|
+
fields.each do
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def pullRowColFields xpath
|
99
|
+
dsFields = {} # data source => (Set of) fields by names
|
100
|
+
node = @node.xpath(xpath) # expect only one each of <rows> & <cols> node per <worksheet node
|
101
|
+
code = node.text
|
102
|
+
# puts "\n CODE: '#{code}'"
|
103
|
+
return dsFields if code.nil?
|
104
|
+
codeFields = code.gsub(/^[(]|[)]$/,'').split(/] [\/+*] [(]?\[/)
|
105
|
+
return dsFields if codeFields.nil? # - commented out, left here for logic clarity
|
106
|
+
codeFields.each do |dsf|
|
107
|
+
parts = dsf.split('].[')
|
108
|
+
if parts.length == 2
|
109
|
+
ds = parts[0].gsub(/^\[|\]$/,'')
|
110
|
+
|
111
|
+
# remove prefix(es), if any
|
112
|
+
f1 = parts[1].gsub(/^pcto:|^fVal:/,'').gsub(/^[a-z]+:/,'')
|
113
|
+
# /^pcto:|^fVal:/ - special handling based upon actual codings
|
114
|
+
|
115
|
+
# remove terminating )s
|
116
|
+
f2 = f1.gsub(/[\)]+$/,'')
|
117
|
+
|
118
|
+
# remove suffix(es), if any
|
119
|
+
f3 = f2.gsub(/:[0-9]+[\]]?$/,'').gsub(/:[a-z]+[\]]?$|[\]]?$/,'')
|
120
|
+
# /:[0-9]+[\]]?$/ - special handling based upon actual codings
|
121
|
+
|
122
|
+
dsFields[ds] = Set.new unless dsFields.include? ds
|
123
|
+
dsFields[ds].add(f3)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
return dsFields
|
127
|
+
end
|
128
|
+
|
61
129
|
def datasourcenames
|
62
130
|
@datasources.keys
|
63
131
|
end
|
data/lib/twb.rb
CHANGED
@@ -23,15 +23,20 @@ require_relative 'twb/storyboard'
|
|
23
23
|
require_relative 'twb/window'
|
24
24
|
require_relative 'twb/workbook'
|
25
25
|
require_relative 'twb/worksheet'
|
26
|
+
require_relative 'twb/action'
|
27
|
+
require_relative 'twb/fieldcalculation'
|
26
28
|
require_relative 'twb/docdashboardimagevert.rb'
|
27
29
|
require_relative 'twb/docdashboardwebvert.rb'
|
28
30
|
require_relative 'twb/util/twbDashSheetDataDotBuilder'
|
29
31
|
require_relative 'twb/util/dotFileRenderer'
|
30
32
|
require_relative 'twb/util/htmllistcollapsible'
|
31
33
|
require_relative 'twb/util/xraydashboards'
|
34
|
+
require_relative 'twb/util/graphnode'
|
35
|
+
require_relative 'twb/util/graphedge'
|
36
|
+
require_relative 'twb/util/graphedges'
|
32
37
|
|
33
38
|
# Represents Tableau Workbooks and their contents.
|
34
39
|
#
|
35
40
|
module Twb
|
36
|
-
VERSION = '0
|
41
|
+
VERSION = '1.0'
|
37
42
|
end
|
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0
|
5
|
-
prerelease:
|
4
|
+
version: '1.0'
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Chris Gerrard
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2016-08-31 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
13
|
description: A sollection of Ruby classes designed for accessing and recording information
|
15
14
|
about, and for manipulating, Tableau Workbooks and their contents.
|
@@ -18,33 +17,47 @@ executables: []
|
|
18
17
|
extensions: []
|
19
18
|
extra_rdoc_files: []
|
20
19
|
files:
|
20
|
+
- LICENSE.md
|
21
|
+
- LICENSE.txt
|
22
|
+
- README.md
|
21
23
|
- bin/twb.rb
|
24
|
+
- lib/twb.rb
|
25
|
+
- lib/twb/TwbTest.rb
|
26
|
+
- lib/twb/action.rb
|
22
27
|
- lib/twb/apps/X-Ray Dashboards.rb
|
28
|
+
- lib/twb/countNodes.rb
|
23
29
|
- lib/twb/dashboard.rb
|
30
|
+
- lib/twb/dashboard.txt
|
24
31
|
- lib/twb/datasource.rb
|
25
32
|
- lib/twb/docdashboard.rb
|
26
33
|
- lib/twb/docdashboardimagevert.rb
|
27
34
|
- lib/twb/docdashboardwebvert.rb
|
28
35
|
- lib/twb/field.rb
|
29
36
|
- lib/twb/fieldcalculation.rb
|
37
|
+
- lib/twb/findDSFields.rb
|
38
|
+
- lib/twb/graph.rb
|
39
|
+
- lib/twb/graphedges.rb
|
40
|
+
- lib/twb/graphnode.rb
|
30
41
|
- lib/twb/hashtohtml.rb
|
31
42
|
- lib/twb/htmllistcollapsible.rb
|
43
|
+
- lib/twb/identifyFields.rb
|
32
44
|
- lib/twb/localfield.rb
|
33
45
|
- lib/twb/metadatafield.rb
|
34
46
|
- lib/twb/storyboard.rb
|
35
47
|
- lib/twb/there.rb
|
36
|
-
- lib/twb/TwbTest.rb
|
37
48
|
- lib/twb/util/dotFileRenderer.rb
|
49
|
+
- lib/twb/util/graphedge.rb
|
50
|
+
- lib/twb/util/graphedges.rb
|
51
|
+
- lib/twb/util/graphnode.rb
|
38
52
|
- lib/twb/util/hashtohtml.rb
|
39
53
|
- lib/twb/util/htmllistcollapsible.rb
|
40
|
-
- lib/twb/util/twbDashesSheetDataDotBuilder.rb
|
41
54
|
- lib/twb/util/twbDashSheetDataDotBuilder.rb
|
42
55
|
- lib/twb/util/twbDashSheetDataDotRenderer.rb
|
56
|
+
- lib/twb/util/twbDashesSheetDataDotBuilder.rb
|
43
57
|
- lib/twb/util/xraydashboards.rb
|
44
58
|
- lib/twb/window.rb
|
45
59
|
- lib/twb/workbook.rb
|
46
60
|
- lib/twb/worksheet.rb
|
47
|
-
- lib/twb.rb
|
48
61
|
- test/testDashboardXRay.rb
|
49
62
|
- test/testDocDashboard.rb
|
50
63
|
- test/testDocDashboardCreate.rb
|
@@ -56,33 +69,28 @@ files:
|
|
56
69
|
- test/testTwbDashSheetDataDotBuilder.rb
|
57
70
|
- test/testTwbGem.rb
|
58
71
|
- test/testTwbWrite.rb
|
59
|
-
- LICENSE.md
|
60
|
-
- README.md
|
61
|
-
- lib/twb/dashboard.txt
|
62
|
-
- LICENSE.txt
|
63
72
|
homepage: http://rubygems.org/gems/twb
|
64
73
|
licenses:
|
65
|
-
-
|
74
|
+
- GPL-3.0
|
75
|
+
metadata: {}
|
66
76
|
post_install_message:
|
67
77
|
rdoc_options: []
|
68
78
|
require_paths:
|
69
79
|
- lib
|
70
80
|
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
-
none: false
|
72
81
|
requirements:
|
73
|
-
- -
|
82
|
+
- - ">="
|
74
83
|
- !ruby/object:Gem::Version
|
75
84
|
version: '0'
|
76
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
-
none: false
|
78
86
|
requirements:
|
79
|
-
- -
|
87
|
+
- - ">="
|
80
88
|
- !ruby/object:Gem::Version
|
81
89
|
version: '0'
|
82
90
|
requirements: []
|
83
91
|
rubyforge_project:
|
84
|
-
rubygems_version:
|
92
|
+
rubygems_version: 2.6.11
|
85
93
|
signing_key:
|
86
|
-
specification_version:
|
94
|
+
specification_version: 4
|
87
95
|
summary: Classes for accessing Tableau Workbooks and their contents.
|
88
96
|
test_files: []
|