twb 1.0.1 → 1.0.4
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 +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?)
|