twb 0.5.3 → 1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|