twb 1.0.1 → 1.0.4
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 +1 -1
- data/lib/twb/analysis/calculatedfieldsanalyzer.rb +2 -3
- data/lib/twb/datasource.rb +1 -1
- data/lib/twb/fieldcalculation.rb +102 -31
- data/lib/twb/util/dotFileRenderer.rb +7 -8
- data/lib/twb/util/graphnode.rb +4 -6
- data/lib/twb/util/twbDashSheetDataDotRenderer.rb +8 -8
- data/test/testDotFileRenderer.rb +3 -7
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4680a8b2c0894aed8b9da0a4d6ba3fd0070c3008
|
4
|
+
data.tar.gz: 07d8a00756ff29178a6a331fe129737a563f5c24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9451fdcfcbc401ec98eb42c978621c76db842a3b785bf1d896c3283b55cdd2d524f1f2d52ff1c416b9c042efa3c3de140eab4bdc727e1be604790a30646e7a86
|
7
|
+
data.tar.gz: 12376fde72224cee30b7c0ceb2141f017b1a7cbb8991ba70c4dc4ba05748990a3b135212e6409b4f810c56705e983c210d3a90f6b31c185ef336003efd0d4fdc
|
data/lib/twb.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# calculatedfieldsanalyzer.rb - this Ruby script Copyright 2017 Christopher 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
|
@@ -111,7 +111,6 @@ DOTHEADER
|
|
111
111
|
def processTWB twbWithDir
|
112
112
|
twb = File.basename(twbWithDir)
|
113
113
|
@twb = Twb::Workbook.new twbWithDir
|
114
|
-
puts " - #{twbWithDir}"
|
115
114
|
emit "- Workbook: #{twbWithDir}"
|
116
115
|
emit " version: #{@twb.version}"
|
117
116
|
return if twbWithDir.end_with? == "Tableau Calculated Fields Analyses.twb"
|
@@ -144,7 +143,7 @@ DOTHEADER
|
|
144
143
|
calculationNodes = ds.calculatedFields
|
145
144
|
emit "calculationNodes : nil? '#{calculationNodes.nil?}'" # - len '#{calculationNodes.length}'"
|
146
145
|
calculationNodes.each do |calcNode|
|
147
|
-
calculation = Twb::FieldCalculation.new
|
146
|
+
calculation = Twb::FieldCalculation.new(calcNode, ds)
|
148
147
|
emit "HANDLING CALCULATION NODE:"
|
149
148
|
emit calcNode.attributes
|
150
149
|
#-- field names --
|
data/lib/twb/datasource.rb
CHANGED
@@ -39,7 +39,7 @@ module Twb
|
|
39
39
|
attr_reader :name, :caption, :uiname, :dsclass
|
40
40
|
attr_reader :connection, :connHash
|
41
41
|
attr_reader :tables
|
42
|
-
attr_reader :localfields,
|
42
|
+
attr_reader :localfields, :metadatafields, :mappedFields, :fieldUINames, :calculatedFields
|
43
43
|
attr_reader :filters
|
44
44
|
attr_reader :node
|
45
45
|
|
data/lib/twb/fieldcalculation.rb
CHANGED
@@ -19,34 +19,66 @@ module Twb
|
|
19
19
|
|
20
20
|
class FieldCalculation
|
21
21
|
|
22
|
-
attr_reader :node, :fieldNode
|
23
|
-
attr_reader :formula, :formulaLines, :formulaFlat
|
24
|
-
attr_reader :uiname, :techname,
|
22
|
+
attr_reader :node, :fieldNode, :dataSource
|
23
|
+
attr_reader :formula, :resolvedFormula, :formulaLines, :formulaFlat
|
24
|
+
attr_reader :uiname, :techname, :caption
|
25
25
|
attr_reader :class, :scopeIsolation
|
26
|
-
attr_reader :fields, :resolvedFields
|
26
|
+
attr_reader :fields, :resolvedFields, :remoteFields
|
27
27
|
attr_reader :comments
|
28
28
|
|
29
|
-
def initialize
|
29
|
+
def initialize(calcNode, datasource=nil)
|
30
30
|
if calcNode
|
31
31
|
@node = calcNode
|
32
|
-
@fieldNode = @node.
|
33
|
-
|
34
|
-
@
|
35
|
-
@
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
@
|
43
|
-
@
|
32
|
+
@fieldNode = @node.at_xpath('..')
|
33
|
+
@dataSource = datasource
|
34
|
+
@class = attribText(@node, 'class')
|
35
|
+
@remoteFields = {}
|
36
|
+
if 'categorical-bin'.eql? @class
|
37
|
+
# puts calcNode
|
38
|
+
# <calculation class='categorical-bin'
|
39
|
+
# column='[Calculation_507569757376950272]'
|
40
|
+
# default='"Other"'
|
41
|
+
# new-bin='true'>
|
42
|
+
@techname = calcNode.attribute('column').text.gsub(/^\[|\]$/,'') # assumes the column attribute exists
|
43
|
+
@caption = attribText(@fieldNode, 'caption')
|
44
|
+
uiname = if datasource.nil?
|
45
|
+
@techName
|
46
|
+
else
|
47
|
+
datasource.fieldUIName(@techname)
|
48
|
+
end
|
49
|
+
@uiname = "#{uiname} (group)"
|
50
|
+
@formula = "grouped '#{uiname}' values"
|
51
|
+
@comments = [ ]
|
52
|
+
@resolvedFields = [ {:field => uiname, :fieldui => uiname, :source => nil} ]
|
53
|
+
else
|
54
|
+
# field names
|
55
|
+
# puts "\n****** "
|
56
|
+
# puts " fnode: #{@fieldNode}\n****** "
|
57
|
+
@caption = @fieldNode.attribute('caption').nil? ? nil : @fieldNode.attribute('caption').text
|
58
|
+
@techname = @fieldNode.attribute('name').text.gsub(/^\[|\]$/,'') # assumes the name attribute exists
|
59
|
+
@uiname = @caption.nil? ? @techname : @caption
|
60
|
+
# puts " caption: #{@caption}"
|
61
|
+
# puts "techname: #{@techname}"
|
62
|
+
# puts " uiname: #{@uiname}"
|
63
|
+
#--
|
64
|
+
@scopeIsolation = attribText(@node, 'scope-isolation')
|
65
|
+
#-- Formula --
|
66
|
+
@has_formula = @node.has_attribute?('formula')
|
67
|
+
if @has_formula
|
68
|
+
@formula = @node.attribute('formula').text
|
69
|
+
@formulaLines = formula.split(/\n|\r\n/)
|
70
|
+
@formulaFlat = flattenFormula(@formulaLines)
|
71
|
+
@comments = getComments(@formulaLines)
|
72
|
+
elsif @class.eql? 'categorical-bin'
|
73
|
+
@formula = '<<Group>>'
|
74
|
+
@formulaLines = [ @formula ]
|
75
|
+
@formulaFlat = [ @formula ]
|
76
|
+
@comments = [ ]
|
77
|
+
end
|
78
|
+
#-- Fields --
|
79
|
+
parseFormFields # establishes @ fields
|
80
|
+
resolveFields # establishes @ resolvedFields
|
44
81
|
end
|
45
|
-
@class = attribText(@node, 'class')
|
46
|
-
@scopeIsolation = attribText(@node, 'scope-isolation')
|
47
|
-
#-- Fields --
|
48
|
-
parseFormFields # establishes @ fields
|
49
|
-
resolveFields # establishes @ resolvedFields
|
50
82
|
end
|
51
83
|
end
|
52
84
|
|
@@ -54,11 +86,11 @@ module Twb
|
|
54
86
|
node.attribute(attribute).nil? ? nil : node.attribute(attribute).text
|
55
87
|
end
|
56
88
|
|
57
|
-
|
89
|
+
|
58
90
|
def parseFormFields
|
59
91
|
@fields = Set.new []
|
60
92
|
formula = formulaFlat
|
61
|
-
if formula.include?('[') && formula.include?(']')
|
93
|
+
if !formula.nil? && formula.include?('[') && formula.include?(']')
|
62
94
|
noSqLits = formula.gsub(/'[\[\.\]]+'/, ' ')
|
63
95
|
flatForm = noSqLits.gsub( /\n/, ' ')
|
64
96
|
stripFrt = flatForm.gsub( /^[^\[]*[\[]/ , '[' )
|
@@ -74,18 +106,55 @@ module Twb
|
|
74
106
|
|
75
107
|
def resolveFields
|
76
108
|
@resolvedFields = []
|
77
|
-
fields.each do |field|
|
109
|
+
@fields.each do |field|
|
78
110
|
rawField = field.gsub(/^\[|\]$/,'')
|
79
111
|
parts = rawField.split('].[')
|
80
112
|
if parts.length > 1
|
81
|
-
|
113
|
+
source = parts[0]
|
114
|
+
fieldCalc = parts[1]
|
115
|
+
fieldui = fieldCalc
|
82
116
|
else
|
83
|
-
|
117
|
+
source = nil
|
118
|
+
fieldCalc = parts[0]
|
119
|
+
fieldui = @dataSource.fieldUIName(fieldCalc)
|
120
|
+
end
|
121
|
+
hash = { :field => fieldCalc, :fieldui => fieldui, :source => source }
|
122
|
+
@resolvedFields << hash
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
def resolvedFormula
|
128
|
+
@resolvedFormula ||= resolveFormula
|
129
|
+
end
|
130
|
+
|
131
|
+
def resolveFormula
|
132
|
+
# puts "resolveFormula :\n-- formula --\n %s \n-- @dataSource.nil? : %s \n-- @resolvedFields.nil? : %s \n--" % [ @formula, @dataSource.nil?, @resolvedFields.nil? ]
|
133
|
+
formula = @formula
|
134
|
+
unless @dataSource.nil? || @formula.nil? || @resolvedFields.nil?
|
135
|
+
@resolvedFields.each do |rf|
|
136
|
+
field = rf[:field]
|
137
|
+
source = rf[:source]
|
138
|
+
# puts " field: #{field}"
|
139
|
+
# puts " src: #{source}"
|
140
|
+
if source.nil?
|
141
|
+
fieldInternal = field # "[#{rf[:field]}]"
|
142
|
+
fieldUI = @dataSource.fieldUIName(field)
|
143
|
+
# puts " f?: #{formula.include?(field)}"
|
144
|
+
# puts " fui: #{fieldUI}"
|
145
|
+
formula = formula.gsub(field,fieldUI) unless fieldUI.nil?
|
146
|
+
# puts "formula:\n--\n#{formula}"
|
147
|
+
else
|
148
|
+
@remoteFields[source] = SortedSet.new unless @remoteFields.include? source
|
149
|
+
@remoteFields[source].add field
|
150
|
+
end
|
84
151
|
end
|
85
|
-
@resolvedFields << hash unless hash.nil?
|
86
152
|
end
|
153
|
+
# puts "resolvedFormula:\n==\n#{formula}\n--"
|
154
|
+
return formula
|
87
155
|
end
|
88
156
|
|
157
|
+
|
89
158
|
def flattenFormula lines
|
90
159
|
formula = ''
|
91
160
|
lines.each do |line|
|
@@ -94,7 +163,8 @@ module Twb
|
|
94
163
|
end
|
95
164
|
return formula.strip
|
96
165
|
end
|
97
|
-
|
166
|
+
|
167
|
+
|
98
168
|
def getComments lines
|
99
169
|
comments = ''
|
100
170
|
lines.each do |line|
|
@@ -105,6 +175,7 @@ module Twb
|
|
105
175
|
return comments.strip
|
106
176
|
end
|
107
177
|
|
108
|
-
end
|
109
178
|
|
110
|
-
end
|
179
|
+
end # class FieldCalculation
|
180
|
+
|
181
|
+
end # module Twb
|
@@ -15,19 +15,19 @@
|
|
15
15
|
|
16
16
|
module Twb
|
17
17
|
module Util
|
18
|
-
|
18
|
+
|
19
19
|
class DotFileRenderer
|
20
20
|
@@gvDotLocation = 'C:\\tech\\graphviz\\Graphviz2.38\\bin\\dot.exe'
|
21
|
-
@@renderTypes = [
|
21
|
+
@@renderTypes = [:pdf, :png, :svg]
|
22
22
|
|
23
23
|
def initialize
|
24
24
|
@gvDotLocation = @@gvDotLocation
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def gvDotLocation=(location)
|
28
28
|
@gvDotLocation = location
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
def renderAll(dotFile)
|
32
32
|
renderedFiles = []
|
33
33
|
@@renderTypes.each do |type|
|
@@ -35,12 +35,11 @@ module Util
|
|
35
35
|
end
|
36
36
|
return renderedFiles
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def render(dotFile, type)
|
40
|
-
type = type.downcase
|
41
40
|
if @@renderTypes.include?(type)
|
42
|
-
typeParam = '-T' + type
|
43
|
-
renderedFile = dotFile.sub(/.dot$/,'') + '.' + type
|
41
|
+
typeParam = '-T' + type.to_s
|
42
|
+
renderedFile = dotFile.sub(/.dot$/,'') + '.' + type.to_s
|
44
43
|
renderedFileParam = '-o' + renderedFile
|
45
44
|
#puts "\t <render cmd> system #{@gvDotLocation}, #{typeParam}, #{renderedFileParam}, #{dotFile} "
|
46
45
|
system @gvDotLocation, typeParam, renderedFileParam, dotFile
|
data/lib/twb/util/graphnode.rb
CHANGED
@@ -30,17 +30,18 @@ module Util
|
|
30
30
|
@@typeColors = { :CalculatedField => 'fillcolor=lightskyblue3',
|
31
31
|
:DBTable => 'fillcolor=steelblue',
|
32
32
|
:TwbDataConnection => 'fillcolor=cornflowerblue',
|
33
|
-
:DatabaseField => 'fillcolor=
|
33
|
+
:DatabaseField => 'fillcolor=grey',
|
34
34
|
}
|
35
35
|
|
36
|
+
# not currently used
|
36
37
|
@@typeStyles = { :CalculatedField => 'style=filled',
|
37
38
|
:DBTable => 'style=filled',
|
38
39
|
:TwbDataConnection => 'style=filled',
|
39
40
|
:DatabaseField => 'style=filled'
|
40
41
|
}
|
41
42
|
|
43
|
+
# @id - the technical fully qualified identifier, distinquishes the node from similarly named nodes
|
42
44
|
# @name - the visible name
|
43
|
-
# @id - the technical identifier, used to distinquish the node from similarly named nodes
|
44
45
|
# @type - useful for categorizing the node
|
45
46
|
attr_reader :id, :type, :name
|
46
47
|
attr_reader :cypherID, :cypherCreate
|
@@ -73,10 +74,7 @@ module Util
|
|
73
74
|
|
74
75
|
|
75
76
|
def dotLabel
|
76
|
-
|
77
|
-
# style = @type =~ /Data Source|DB Table|Database Field/ ? "style=filled" : ''
|
78
|
-
# style = @type =~ /Data Source|DB Table|Database Field/ ? "style=filled" : ''
|
79
|
-
"\"%s\" [label=\"%s\" %s %s %s ]" % [id, name, @@typeShapes[@type], 'style=filled', @@typeColors[@type]]
|
77
|
+
"\"%s\" [label=\"%s\" %s %s ]" % [id, name.gsub('"','\"'), @@typeShapes[@type], @@typeColors[@type]]
|
80
78
|
end
|
81
79
|
|
82
80
|
|
@@ -15,33 +15,33 @@
|
|
15
15
|
|
16
16
|
module Twb
|
17
17
|
module Util
|
18
|
-
|
18
|
+
|
19
19
|
class DotFileRenderer
|
20
20
|
@@gvDotLocation = 'C:\\tech\\graphviz\\Graphviz2.38\\bin\\dot.exe'
|
21
|
-
@@renderTypes = [
|
21
|
+
@@renderTypes = [:pdf, :png, :svg]
|
22
22
|
def initialize
|
23
23
|
@gvDotLocation = @@gvDotLocation
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def gvDotLocation=(location)
|
27
27
|
@gvDotLocation = location
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def render dotFile
|
31
31
|
renderedFiles = []
|
32
32
|
@@renderTypes.each do |type|
|
33
33
|
renderedFiles << render(dotFile,type)
|
34
34
|
end
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def render(dotFile,type)
|
38
38
|
if @@renderTypes.includes(type)
|
39
|
-
renderedFile = dotFile + '.' + type
|
40
|
-
system "\"#{@gvDotLocation}\" -T#{type} -o\"#{renderedFile}\" \"#{dotFile}\""
|
39
|
+
renderedFile = dotFile + '.' + type.to_s
|
40
|
+
system "\"#{@gvDotLocation}\" -T#{type.to_s} -o\"#{renderedFile}\" \"#{dotFile}\""
|
41
41
|
end
|
42
42
|
return renderedFile
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
end
|
46
46
|
|
47
47
|
def renderDot(twb,dot)
|
data/test/testDotFileRenderer.rb
CHANGED
@@ -38,15 +38,11 @@ class TestDotFileRenderer < Test::Unit::TestCase
|
|
38
38
|
|
39
39
|
def test_SpecifyType
|
40
40
|
renderer = Twb::Util::DotFileRenderer.new
|
41
|
-
renderedFile = renderer.render(@@testDotFile
|
41
|
+
renderedFile = renderer.render(@@testDotFile,:pdf)
|
42
42
|
assert(!renderedFile.nil?)
|
43
|
-
renderedFile = renderer.render(@@testDotFile
|
43
|
+
renderedFile = renderer.render(@@testDotFile,:png)
|
44
44
|
assert(!renderedFile.nil?)
|
45
|
-
renderedFile = renderer.render(@@testDotFile
|
46
|
-
assert(!renderedFile.nil?)
|
47
|
-
renderedFile = renderer.render(@@testDotFile,'PNG') # should convert to 'png'
|
48
|
-
assert(!renderedFile.nil?)
|
49
|
-
renderedFile = renderer.render(@@testDotFile,'svg')
|
45
|
+
renderedFile = renderer.render(@@testDotFile,:svg)
|
50
46
|
assert(!renderedFile.nil?)
|
51
47
|
renderedFile = renderer.render(@@testDotFile,'xyz') # should fail
|
52
48
|
assert(renderedFile.nil?)
|