twb 2.2.1 → 3.7.2
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 +13 -1
- data/lib/twb/action.rb +5 -1
- data/lib/twb/analysis/AnnotatedFieldsCSVEmitter.rb +3 -0
- data/lib/twb/analysis/CalculatedFields/CalculatedFieldsAnalyzer.rb +276 -287
- data/lib/twb/analysis/CalculatedFields/MarkdownEmitter.rb +48 -34
- data/lib/twb/analysis/DataSources/DataSourceFieldsCSVEmitter.rb +103 -103
- data/lib/twb/analysis/DataSources/googlesheetdatasourcesanalyzer.rb +79 -0
- data/lib/twb/analysis/DocumentedFieldsMarkdownEmitter.rb +1 -1
- data/lib/twb/analysis/Sheets/sheetfieldsanalyzer.rb +82 -0
- data/lib/twb/analysis/Sheets/sheetfiltersanalyzer.rb +214 -0
- data/lib/twb/calculatedfield.rb +20 -5
- data/lib/twb/codedfield.rb +87 -0
- data/lib/twb/columnfield.rb +21 -2
- data/lib/twb/connection.rb +33 -0
- data/lib/twb/dashboard.rb +5 -1
- data/lib/twb/datasource.rb +131 -20
- data/lib/twb/dbfield.rb +4 -0
- data/lib/twb/field.rb +5 -1
- data/lib/twb/fieldcalculation.rb +134 -78
- data/lib/twb/localfield.rb +5 -1
- data/lib/twb/mappedfield.rb +5 -1
- data/lib/twb/metadatafield.rb +5 -1
- data/lib/twb/storyboard.rb +5 -1
- data/lib/twb/tabclass.rb +71 -0
- data/lib/twb/tabtest.rb +31 -0
- data/lib/twb/tabtool.rb +63 -0
- data/lib/twb/twbcodedfield.rb +87 -0
- data/lib/twb/util/cypher.rb +112 -0
- data/lib/twb/util/cypherpython.rb +128 -0
- data/lib/twb/util/docprep.rb +46 -0
- data/lib/twb/util/fielddomainloader.rb +108 -0
- data/lib/twb/util/gml.rb +144 -0
- data/lib/twb/util/gmledge.rb +73 -0
- data/lib/twb/util/graph.rb +30 -0
- data/lib/twb/util/graphedge.rb +8 -9
- data/lib/twb/util/graphnode.rb +46 -29
- data/lib/twb/util/tabgraph.rb +30 -0
- data/lib/twb/window.rb +5 -1
- data/lib/twb/workbook.rb +18 -5
- data/lib/twb/worksheet.rb +5 -1
- data/test/fieldAliases.rb +10 -0
- data/test/testFieldAliases.rb +65 -0
- data/test/testFieldDomainLoaded.rb +14 -0
- data/test/testFieldDomainLoader.rb +131 -0
- metadata +22 -1
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright (C) 2014, 2015, 2016 Chris 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 'nokogiri'
|
17
|
+
|
18
|
+
module Twb
|
19
|
+
|
20
|
+
class Connection < TabClass
|
21
|
+
|
22
|
+
attr_reader :xmlType
|
23
|
+
|
24
|
+
# node: XML element
|
25
|
+
def initialize node
|
26
|
+
@xmlType = node.type
|
27
|
+
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end # class
|
32
|
+
|
33
|
+
end # module Twb
|
data/lib/twb/dashboard.rb
CHANGED
@@ -18,7 +18,7 @@ require 'digest/md5'
|
|
18
18
|
|
19
19
|
module Twb
|
20
20
|
|
21
|
-
class Dashboard
|
21
|
+
class Dashboard < TabClass
|
22
22
|
|
23
23
|
@@hasher = Digest::SHA256.new
|
24
24
|
|
@@ -39,6 +39,10 @@ module Twb
|
|
39
39
|
return self
|
40
40
|
end
|
41
41
|
|
42
|
+
def id
|
43
|
+
@id ||= @id = @name.hash
|
44
|
+
end
|
45
|
+
|
42
46
|
def loadSheets twbworksheets
|
43
47
|
@sheets = {}
|
44
48
|
dsheets = @node.xpath('.//zone[@name]').to_a
|
data/lib/twb/datasource.rb
CHANGED
@@ -19,7 +19,7 @@ require 'json'
|
|
19
19
|
|
20
20
|
module Twb
|
21
21
|
|
22
|
-
class DataSource
|
22
|
+
class DataSource < TabClass
|
23
23
|
|
24
24
|
@@hasher = Digest::SHA256.new
|
25
25
|
|
@@ -41,7 +41,7 @@ module Twb
|
|
41
41
|
attr_reader :workbook
|
42
42
|
attr_reader :name, :caption, :uiname
|
43
43
|
attr_reader :dsclass, :isExtract
|
44
|
-
attr_reader :connection, :connHash
|
44
|
+
attr_reader :connection, :connections, :connHash, :uuid
|
45
45
|
attr_reader :tables, :joinPairs
|
46
46
|
attr_reader :localFields, :localFieldNames, :localField, :hasField
|
47
47
|
attr_reader :columnFields
|
@@ -50,6 +50,7 @@ module Twb
|
|
50
50
|
attr_reader :mappedFields
|
51
51
|
attr_reader :tableFieldsMap
|
52
52
|
attr_reader :fieldUINames
|
53
|
+
attr_reader :aliases
|
53
54
|
attr_reader :calculatedFields, :calculatedFieldNamesMap, :calculatedFieldNames, :calculatedField
|
54
55
|
attr_reader :allFields
|
55
56
|
attr_reader :filters
|
@@ -61,12 +62,18 @@ module Twb
|
|
61
62
|
@name = @node.attr('name')
|
62
63
|
@caption = @node.attr('caption')
|
63
64
|
@uiname = @caption.nil? ? @name : @caption
|
65
|
+
# puts "DATASOURCE: #{@uiname}"
|
64
66
|
processConnection
|
65
67
|
processFilters
|
66
68
|
loadTableFields
|
69
|
+
loadFieldUINames
|
67
70
|
return self
|
68
71
|
end
|
69
72
|
|
73
|
+
def id
|
74
|
+
@id ||= @id = @name
|
75
|
+
end
|
76
|
+
|
70
77
|
def tableauVersion tabVersion
|
71
78
|
@tableauVersion = tabVersion
|
72
79
|
end
|
@@ -75,6 +82,36 @@ module Twb
|
|
75
82
|
@tableauVersion
|
76
83
|
end
|
77
84
|
|
85
|
+
def connections
|
86
|
+
@connections ||= @connections = processConnections
|
87
|
+
end
|
88
|
+
|
89
|
+
def processConnections
|
90
|
+
processConnection ds, './/connection'
|
91
|
+
processConnection ds, './/named-connection'
|
92
|
+
end
|
93
|
+
|
94
|
+
def processConnection path
|
95
|
+
conns = @node.xpath(path)
|
96
|
+
conns.each do |connNode|
|
97
|
+
connClass = @dsclass
|
98
|
+
cpath = conn.path
|
99
|
+
connPath = cpath.gsub(/\[[0-9]+\]/,'')
|
100
|
+
connPNum = /(\d+)/.match(cpath)
|
101
|
+
# puts cpath, connPath
|
102
|
+
# puts "CPATH: #{cpath}"
|
103
|
+
conn.attributes.each do |name,value|
|
104
|
+
# puts "\n\t\t - %-15s -> %-s" % [name, value]
|
105
|
+
$csvFile << [ $recCount += 1,
|
106
|
+
$twbName, $dir, $build, $version,
|
107
|
+
$dsName, $dstype, $dsuiname, connClass,
|
108
|
+
connPath, connPNum,
|
109
|
+
name, value.value
|
110
|
+
]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
78
115
|
def processConnection
|
79
116
|
@connection = @node.at_xpath('./connection')
|
80
117
|
@dsclass = @name # handles 'Parameters' data source, which has no connection element (& others if so)
|
@@ -98,6 +135,7 @@ module Twb
|
|
98
135
|
dsConnStr += attr.text
|
99
136
|
end
|
100
137
|
@connHash = Digest::MD5.hexdigest(dsConnStr)
|
138
|
+
@uuid = @connHash
|
101
139
|
end
|
102
140
|
|
103
141
|
def isExtract
|
@@ -133,16 +171,16 @@ module Twb
|
|
133
171
|
|
134
172
|
def loadJoinTree
|
135
173
|
loadJoinPairs if @joinPairs.nil?
|
136
|
-
# puts
|
137
|
-
# @joinPairs.each { |jp| puts "JP::#{jp}" }
|
174
|
+
# puts "LJT::#{@uiname}::joinPairs:: #{@joinPairs.inspect}"
|
175
|
+
# @joinPairs.each { |jp| # puts "JP::#{jp}" }
|
138
176
|
@joinTree = JoinTree.new(@name)
|
139
177
|
@joinPairs.each do |from,to|
|
140
|
-
# puts
|
178
|
+
# puts "from:#{from} -> to:#{to}"
|
141
179
|
tableFrom = JoinTable.new(from)
|
142
180
|
tableTo = JoinTable.new(to)
|
143
181
|
@joinTree.add(tableFrom, tableTo)
|
144
182
|
end
|
145
|
-
# puts
|
183
|
+
# puts '---'
|
146
184
|
return @joinTree
|
147
185
|
end
|
148
186
|
|
@@ -156,7 +194,7 @@ module Twb
|
|
156
194
|
end
|
157
195
|
|
158
196
|
def columnFields
|
159
|
-
@columnFields ||= loadColumnFields
|
197
|
+
@columnFields ||= @columnFields = loadColumnFields
|
160
198
|
end
|
161
199
|
|
162
200
|
def columnFieldsMap
|
@@ -172,10 +210,71 @@ module Twb
|
|
172
210
|
field = Twb::ColumnField.new n, self
|
173
211
|
@columnFields << field
|
174
212
|
@columnFieldsMap[field.uiname] = field
|
213
|
+
@columnFieldsMap[field.name] = field
|
175
214
|
end
|
176
215
|
return @columnFields
|
177
216
|
end
|
178
217
|
|
218
|
+
def aliases
|
219
|
+
@aliases ||= loadAliases
|
220
|
+
end
|
221
|
+
|
222
|
+
def loadAliases
|
223
|
+
@aliases = {}
|
224
|
+
# puts $node.xpath('.//column/aliases/..').length
|
225
|
+
@node.xpath('./column//aliases/..').each do |anode|
|
226
|
+
# puts "anode:: #{anode}"
|
227
|
+
# puts " path:: #{anode.path}"
|
228
|
+
aliasMap = {}
|
229
|
+
nameTech = anode.attribute('name').text.gsub(/^\[|\]$/,'')
|
230
|
+
name = fieldUIName nameTech
|
231
|
+
if ':Measure Names'.eql? name
|
232
|
+
# puts "processing Measure Names"
|
233
|
+
anode.xpath('.//alias').each do |vnode|
|
234
|
+
keyCode = vnode.attribute('key').text.gsub(/^[#"%]|[#"%]$/,'')
|
235
|
+
key = Twb::CodedField.new(keyCode).name
|
236
|
+
field = fieldUIName key
|
237
|
+
alia = vnode.attribute('value').text
|
238
|
+
# puts " keyCode:: #{key}"
|
239
|
+
# puts " key :: #{key}"
|
240
|
+
# puts " field :: #{field}"
|
241
|
+
# puts " alias :: #{alia}"
|
242
|
+
aliasMap[field] = alia
|
243
|
+
end
|
244
|
+
else
|
245
|
+
anode.xpath('.//alias').each do |vnode|
|
246
|
+
key = vnode.attribute('key').text.gsub(/^[#"]|[#"]$/,'')
|
247
|
+
alia = vnode.attribute('value').text
|
248
|
+
# puts " key :: #{key}"
|
249
|
+
# puts "alias:: #{alia}"
|
250
|
+
aliasMap[key] = alia
|
251
|
+
end
|
252
|
+
end
|
253
|
+
@aliases[name] = aliasMap
|
254
|
+
end
|
255
|
+
return @aliases
|
256
|
+
end
|
257
|
+
|
258
|
+
def fieldHasAliases fieldName
|
259
|
+
!aliases[fieldName].nil?
|
260
|
+
end
|
261
|
+
|
262
|
+
def fieldAliases fieldName
|
263
|
+
aliases[fieldName]
|
264
|
+
end
|
265
|
+
|
266
|
+
def fieldAlias fieldName, value
|
267
|
+
return value unless fieldHasAliases(fieldName)
|
268
|
+
fldAlias = aliases[fieldName][value]
|
269
|
+
fldAlias.nil? ? value : fldAlias
|
270
|
+
end
|
271
|
+
|
272
|
+
def deAlias fieldName, fAlias
|
273
|
+
fldAliases = aliases[fieldName]
|
274
|
+
return fAlias if fldAliases.nil?
|
275
|
+
dbFieldValue = fldAliases.key(fAlias)
|
276
|
+
dbFieldValue.nil? ? fAlias : dbFieldValue
|
277
|
+
end
|
179
278
|
|
180
279
|
def localFields
|
181
280
|
@localFields ||= loadLocalFields
|
@@ -241,21 +340,33 @@ module Twb
|
|
241
340
|
end
|
242
341
|
|
243
342
|
def loadFieldUINames
|
343
|
+
# puts 'loadFieldUINames'
|
244
344
|
@fieldUINames = {}
|
345
|
+
# puts 'metadataFields'
|
245
346
|
metadataFields.each do |fld|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
347
|
+
# puts " - name:%-45s | uiname:%-45s | localName:%-45s " % [ "'#{fld.name}'", "'#{fld.uiname}'", "'#{fld.localName}'"]
|
348
|
+
@fieldUINames[fld.uiname] = fld.uiname
|
349
|
+
@fieldUINames[fld.localName] = fld.uiname unless fld.localName.nil?
|
350
|
+
@fieldUINames[fld.name] = fld.uiname unless fld.name.nil?
|
250
351
|
end
|
352
|
+
# puts 'calculatedFields'
|
251
353
|
calculatedFields.each do |fld|
|
354
|
+
# puts " - name:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.uiname}'"]
|
252
355
|
@fieldUINames[fld.name] = fld.uiname
|
253
356
|
@fieldUINames[fld.uiname] = fld.uiname
|
254
357
|
end
|
255
|
-
|
358
|
+
# puts 'localFields'
|
359
|
+
localFields.each do |fld|
|
360
|
+
# puts " - name:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.uiname}'"]
|
256
361
|
@fieldUINames[fld.name] = fld.uiname
|
257
362
|
@fieldUINames[fld.uiname] = fld.uiname
|
258
363
|
end
|
364
|
+
# puts "columnFields: #{columnFields.length}"
|
365
|
+
columnFields.each do |fld|
|
366
|
+
@fieldUINames[fld.name] = fld.uiname
|
367
|
+
@fieldUINames[fld.uiname] = fld.uiname
|
368
|
+
end
|
369
|
+
return @fieldUINames
|
259
370
|
end
|
260
371
|
|
261
372
|
def tableFields
|
@@ -329,7 +440,7 @@ module Twb
|
|
329
440
|
|
330
441
|
# fields are unique in the data source by UI name
|
331
442
|
def loadTableFields
|
332
|
-
# puts
|
443
|
+
# puts "DATA SOURCE FIELD TABLE LOAD"
|
333
444
|
@tableFieldsMap = {}
|
334
445
|
fieldNodes = @node.xpath('./connection/cols/map')
|
335
446
|
fieldNodes.each do |fn|
|
@@ -410,10 +521,10 @@ module Twb
|
|
410
521
|
end
|
411
522
|
|
412
523
|
def addChild child
|
413
|
-
# puts
|
414
|
-
# puts
|
524
|
+
# puts "#{@name}.addChild(#{child.name})"
|
525
|
+
# puts "children: #{@children}"
|
415
526
|
@children[child.name] = child if @children[child.name].nil?
|
416
|
-
# puts
|
527
|
+
# puts "children: #{@children}"
|
417
528
|
end
|
418
529
|
|
419
530
|
def child name
|
@@ -435,8 +546,8 @@ module Twb
|
|
435
546
|
end
|
436
547
|
|
437
548
|
def add host, dest
|
438
|
-
# puts
|
439
|
-
# puts
|
549
|
+
# puts "\nJT add() host: #{host}"
|
550
|
+
# puts " dest: #{dest}"
|
440
551
|
from = @tables[host.name].nil? ? host : @tables[host.name]
|
441
552
|
to = @tables[dest.name].nil? ? dest : @tables[dest.name]
|
442
553
|
from.addChild(to)
|
@@ -453,7 +564,7 @@ module Twb
|
|
453
564
|
end
|
454
565
|
|
455
566
|
def setDepth table, depth
|
456
|
-
# puts
|
567
|
+
# puts "-- setDepth(#{table.class}[#{table.name}] \t, #{depth})"
|
457
568
|
@tables[table.name].depth = depth
|
458
569
|
childrenDepth = depth+1
|
459
570
|
table.children.each { |n,c| setDepth(c,childrenDepth)}
|
@@ -464,7 +575,7 @@ module Twb
|
|
464
575
|
end
|
465
576
|
|
466
577
|
def disp table
|
467
|
-
puts
|
578
|
+
# puts "%s %s %s (%d)" % [' ' * table.depth, '-' * table.depth, table.name, table.depth]
|
468
579
|
table.children.each { |n,c| disp c}
|
469
580
|
end
|
470
581
|
|
data/lib/twb/dbfield.rb
CHANGED
data/lib/twb/field.rb
CHANGED
@@ -17,7 +17,7 @@ require 'nokogiri'
|
|
17
17
|
|
18
18
|
module Twb
|
19
19
|
|
20
|
-
class Field
|
20
|
+
class Field < TabClass
|
21
21
|
|
22
22
|
attr_reader :node, :name, :datatype, :role, :type, :hidden, :caption, :aggregation, :uiname
|
23
23
|
|
@@ -35,6 +35,10 @@ module Twb
|
|
35
35
|
return self
|
36
36
|
end
|
37
37
|
|
38
|
+
def id
|
39
|
+
@id ||= @id = @name.hash
|
40
|
+
end
|
41
|
+
|
38
42
|
end
|
39
43
|
|
40
44
|
end
|
data/lib/twb/fieldcalculation.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014, 2015, 2017 Chris Gerrard
|
1
|
+
# Copyright (C) 2014, 2015, 2017 Chris 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
|
@@ -14,13 +14,17 @@
|
|
14
14
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
15
|
|
16
16
|
require 'nokogiri'
|
17
|
+
require 'digest/md5'
|
18
|
+
require 'csv'
|
17
19
|
|
18
20
|
module Twb
|
19
21
|
|
20
|
-
class FieldCalculation
|
22
|
+
class FieldCalculation < TabClass
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
+
include TabTool
|
25
|
+
|
26
|
+
attr_reader :node, :field, :fieldNode, :dataSource
|
27
|
+
attr_reader :name, :caption, :uiname
|
24
28
|
attr_reader :has_formula
|
25
29
|
attr_reader :formula
|
26
30
|
attr_reader :formulaUC
|
@@ -29,10 +33,12 @@ module Twb
|
|
29
33
|
attr_reader :formulaFlatResolved
|
30
34
|
attr_reader :formulaLines, :formulaResolvedLines
|
31
35
|
attr_reader :is_tableCalc
|
32
|
-
attr_reader :is_lod,
|
33
|
-
attr_reader :class,
|
34
|
-
attr_reader :fields,
|
35
|
-
attr_reader :comments
|
36
|
+
attr_reader :is_lod, :lodCodePos
|
37
|
+
attr_reader :class, :scopeIsolation
|
38
|
+
attr_reader :fields, :remoteFields, :calcFields
|
39
|
+
attr_reader :comments, :uuid
|
40
|
+
|
41
|
+
attr_accessor :ttlogfile
|
36
42
|
|
37
43
|
@@tableCalcs = [ 'FIRST', 'INDEX', 'LAST', 'SIZE',
|
38
44
|
'LOOKUP', 'PREVIOUS_VALUE',
|
@@ -47,11 +53,15 @@ module Twb
|
|
47
53
|
'WINDOW_VAR', 'WINDOW_VARP'
|
48
54
|
]
|
49
55
|
|
56
|
+
@ttlogfile =
|
57
|
+
|
50
58
|
def initialize(calcField, datasource=nil)
|
51
59
|
raise ArgumentError.new("FieldCalculation must be initialized with a CalculatedField, has been provided with a #{calcField.class}") if calcField.class != Twb::CalculatedField
|
52
|
-
|
53
|
-
|
54
|
-
|
60
|
+
# initLogger
|
61
|
+
@field = calcField
|
62
|
+
calcNode = calcField.node
|
63
|
+
@istableCalc = false
|
64
|
+
# @localEmit = false
|
55
65
|
# puts "FieldCalculation calcNode.nil? :: #{calcNode.nil?} "
|
56
66
|
unless calcNode.nil?
|
57
67
|
@node = calcNode.at_xpath('./calculation')
|
@@ -60,27 +70,23 @@ module Twb
|
|
60
70
|
@class = attribText(@node, 'class')
|
61
71
|
@remoteFields = {}
|
62
72
|
if 'categorical-bin'.eql? @class
|
63
|
-
|
64
|
-
# column='[Calculation_507569757376950272]'
|
65
|
-
# default='"Other"'
|
66
|
-
# new-bin='true'>
|
67
|
-
@techname = @node.attribute('column').text.gsub(/^\[|\]$/,'') # assumes the column attribute exists
|
73
|
+
@name = @node.attribute('column').text.gsub(/^\[|\]$/,'') # assumes the column attribute exists
|
68
74
|
@caption = attribText(@fieldNode, 'caption')
|
69
75
|
uiname = if datasource.nil?
|
70
|
-
@
|
76
|
+
@name
|
71
77
|
else
|
72
|
-
datasource.fieldUIName(@
|
78
|
+
datasource.fieldUIName(@name)
|
73
79
|
end
|
74
|
-
@uiname
|
80
|
+
@uiname = "#{uiname} <<group>>"
|
75
81
|
@has_formula = true
|
76
|
-
@formula = "grouped
|
82
|
+
@formula = "grouped <<[#{uiname}]>> values"
|
77
83
|
@formulaLines = [ @formula ]
|
78
84
|
@formulaFlat = @formula
|
79
85
|
@comments = [ ]
|
80
86
|
@is_lod = false
|
81
87
|
else
|
82
88
|
@caption = calcField.caption
|
83
|
-
@
|
89
|
+
@name = calcField.name
|
84
90
|
@uiname = calcField.uiname
|
85
91
|
#--
|
86
92
|
@scopeIsolation = attribText(@node, 'scope-isolation')
|
@@ -88,6 +94,7 @@ module Twb
|
|
88
94
|
@has_formula = @node.has_attribute?('formula')
|
89
95
|
if @has_formula
|
90
96
|
@formula = @node.attribute('formula').text.gsub(/\r\n/,"\n")
|
97
|
+
# puts "\n-- init: #{@formula}"
|
91
98
|
@formulaUC = @formula.upcase
|
92
99
|
@formulaLines = formula.split(/\n|\r\n/)
|
93
100
|
@formulaFlat = flattenFormula(@formulaLines)
|
@@ -101,6 +108,20 @@ module Twb
|
|
101
108
|
end
|
102
109
|
end
|
103
110
|
|
111
|
+
# def initLogger(logfile=nil)
|
112
|
+
# logfilename = docFile(logfile.nil? ? @ttlogfile : logfile)
|
113
|
+
# @logger = Logger.new(logfilename)
|
114
|
+
# @logger.level = Logger::DEBUG
|
115
|
+
# end
|
116
|
+
|
117
|
+
def id
|
118
|
+
@id ||= @id = @formulaFlat.hash + calcField.hash
|
119
|
+
end
|
120
|
+
|
121
|
+
def uuid
|
122
|
+
@uuid ||= @uuid = Digest::MD5.hexdigest(@formula)
|
123
|
+
end
|
124
|
+
|
104
125
|
# def assessTableCalc formula
|
105
126
|
# @@tableCalcs.any? { |tc| string.include?(tc) }
|
106
127
|
# end
|
@@ -109,36 +130,14 @@ module Twb
|
|
109
130
|
node.attribute(attribute).nil? ? nil : node.attribute(attribute).text
|
110
131
|
end
|
111
132
|
|
112
|
-
|
113
|
-
def parseFormFields
|
114
|
-
@fields = Set.new []
|
115
|
-
@calcFields = SortedSet.new []
|
116
|
-
formula = @formulaFlat
|
117
|
-
if !formula.nil? && formula.include?('[') && formula.include?(']')
|
118
|
-
noSqLits = formula.gsub( /'[\[\.\]]+'/, ' ')
|
119
|
-
flatForm = noSqLits.gsub( /\n/, ' ')
|
120
|
-
stripFrt = flatForm.gsub( /^[^\[]*[\[]/ , '[' )
|
121
|
-
stripBck = stripFrt.gsub( /\][^\]]+$/ , ']' )
|
122
|
-
stripMid = stripBck.gsub( /\][^\]]{2,}\[/ , ']]..[[' )
|
123
|
-
stripCom = stripMid.gsub( /\][ ]*,[ ]*\[/ , ']]..[[' )
|
124
|
-
stripFns = stripMid.gsub( /\][ ]*[\*\/+\-><,=][ ]*\[/ , ']]..[[' )
|
125
|
-
fields = stripFns.split(']..[')
|
126
|
-
fields.each { |field| @fields.add field.gsub(/^\[|\]$/, '')}
|
127
|
-
fields.each do |field|
|
128
|
-
cf = CalculationField.new( field.gsub(/^\[|\]$/, ''), @dataSource, @dataSource.workbook )
|
129
|
-
@calcFields.add cf
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
133
|
def formulaResolved
|
135
|
-
@formulaResolved ||= resolveFormula
|
134
|
+
@formulaResolved ||= @formulaResolved = resolveFormula
|
136
135
|
end
|
137
136
|
|
138
137
|
def resolveFormula
|
138
|
+
# puts "\ndef resolveFormula:\n--\n#{@formula}"
|
139
139
|
formula = @formula
|
140
140
|
parseFormFields # - extracts the fields from the formula; as persisted they're the internal names
|
141
|
-
# puts "::++ #{formula}"
|
142
141
|
@calcFields.each do |calcField|
|
143
142
|
if calcField.techUIdiff
|
144
143
|
# puts ":::: #{calcField.techCode} // #{calcField.uiCode}"
|
@@ -149,7 +148,42 @@ module Twb
|
|
149
148
|
return formula
|
150
149
|
end
|
151
150
|
|
151
|
+
def calcFields
|
152
|
+
@calcFields ||= parseFormFields
|
153
|
+
end
|
154
|
+
|
155
|
+
def parseFormFields
|
156
|
+
# puts "--parseFormFields"
|
157
|
+
@fields = Set.new
|
158
|
+
@calcFields = Set.new
|
159
|
+
formula = @formulaFlat
|
160
|
+
if !formula.nil? && formula.include?('[') && formula.include?(']')
|
161
|
+
fields = Set.new
|
162
|
+
# noSqLits = formula.gsub( /'[\[\.\]]+'/, ' ')
|
163
|
+
quotes = formula.gsub('"',"'")
|
164
|
+
noSqLits = quotes.gsub( /'[\[\.\]]+'/, ' ')
|
165
|
+
flatForm = noSqLits.gsub( /\n/, ' ')
|
166
|
+
stripFrt = flatForm.gsub( /^[^\[]*[\[]/ , '[' )
|
167
|
+
stripBck = stripFrt.gsub( /\][^\]]+$/ , ']' )
|
168
|
+
stripMid = stripBck.gsub( /\][^\]]{2,}\[/ , ']]..[[' )
|
169
|
+
stripCom = stripMid.gsub( /\][ ]*,[ ]*\[/ , ']]..[[' )
|
170
|
+
stripFns = stripMid.gsub( /\][ ]*[\*\/+\-><,=][ ]*\[/ , ']]..[[' )
|
171
|
+
fields = stripFns.split(']..[')
|
172
|
+
emit "::self::: #{self} :: #{__LINE__} :: fields:'#{fields.inspect}'"
|
173
|
+
fields.each do |field|
|
174
|
+
emit "::self::: #{self} :: #{__LINE__} :: field:'#{field}'"
|
175
|
+
cf = CalculationField.new( field.gsub(/^\[|\]$/, ''), @dataSource )
|
176
|
+
@calcFields.add cf
|
177
|
+
@fields.add field.gsub(/^\[|\]$/, '')
|
178
|
+
end
|
179
|
+
end
|
180
|
+
return @calcFields
|
181
|
+
end
|
182
|
+
|
152
183
|
def formulaResolvedLines
|
184
|
+
# puts "\ndef formulaResolvedLines\n--\n#{formulaResolved}"
|
185
|
+
# puts "--\n#{formulaResolved.split(/\n|\r\n/)}"
|
186
|
+
# puts "--\n#{formulaResolved.split(/\n|\r\n/)}"
|
153
187
|
formulaResolved.split(/\n|\r\n/)
|
154
188
|
end
|
155
189
|
|
@@ -162,16 +196,16 @@ module Twb
|
|
162
196
|
|
163
197
|
def flattenResolvedFormula
|
164
198
|
formula = formulaResolved
|
165
|
-
formula.gsub(/\n/, '
|
199
|
+
formula.gsub(/\n/, ' ')
|
166
200
|
end
|
167
201
|
|
168
202
|
def flattenFormula lines
|
169
203
|
formula = ''
|
170
204
|
lines.each do |line|
|
171
|
-
line.strip
|
205
|
+
# line.strip
|
172
206
|
formula += ' ' + line.gsub(/\/\/.*/, '') # unless line =~ /^[ ]*\/\//
|
173
207
|
end
|
174
|
-
return formula.strip
|
208
|
+
return formula # .strip
|
175
209
|
end
|
176
210
|
|
177
211
|
|
@@ -190,71 +224,93 @@ module Twb
|
|
190
224
|
|
191
225
|
|
192
226
|
class CalculationField
|
193
|
-
# is a field used in a calculation, resolved into
|
227
|
+
# is a field used in a calculation, resolved into its human-meaningful form
|
194
228
|
|
195
229
|
include Comparable
|
230
|
+
include TabTool
|
196
231
|
|
197
|
-
attr_reader :
|
198
|
-
attr_reader :
|
199
|
-
attr_reader :dataSource,
|
200
|
-
attr_reader :fqName
|
232
|
+
attr_reader :name, :techCode
|
233
|
+
attr_reader :uiname, :uiCode
|
234
|
+
attr_reader :dataSource, :dataSourceName, :dataSourceRef, :dataSourceExists
|
235
|
+
attr_reader :fqName, :type
|
201
236
|
attr_reader :techUIdiff
|
202
237
|
|
203
|
-
def initialize code, datasource
|
204
|
-
|
205
|
-
@dataSource = datasource
|
238
|
+
def initialize code, datasource
|
239
|
+
# puts "\n\nCalculationField :: %-25s | %s " % [datasource.uiname, code]
|
240
|
+
@dataSource = datasource
|
241
|
+
@dataSourceName = datasource.uiname
|
206
242
|
@dataSourceRef = :local
|
207
243
|
@dataSourceExists = true
|
208
244
|
@techUIdiff = false
|
209
|
-
@
|
245
|
+
@uiname = ''
|
210
246
|
rawCode = code.gsub(/^\[|\]$/,'')
|
211
247
|
parts = rawCode.split('].[')
|
212
|
-
#
|
248
|
+
#puts "Field: #{code} \t parts: #{parts.length} - #{parts.inspect}"
|
213
249
|
if parts.length == 1
|
214
|
-
@
|
250
|
+
@name = parts[0]
|
215
251
|
@techCode = "[#{parts[0]}]"
|
252
|
+
#puts "@name: #{@name}"
|
216
253
|
if datasource.nil?
|
217
|
-
|
254
|
+
# puts 'a'
|
255
|
+
@uiname = @name
|
218
256
|
@uiCode = @techCode
|
219
257
|
@techUIdiff = false
|
220
258
|
else # !datasource.nil?
|
221
|
-
# puts
|
222
|
-
@
|
223
|
-
@
|
259
|
+
# puts 'b'
|
260
|
+
#puts "b - found uiname for '#{@name}'?: #{!datasource.fieldUIName(@name).nil?} \t is:#{datasource.fieldUIName(@name)} "
|
261
|
+
@uiname = datasource.fieldUIName(@name).nil? ? @name : datasource.fieldUIName(@name)
|
262
|
+
@uiCode = @uiname.nil? ? @techCode : "[#{@uiname}]"
|
224
263
|
@techUIdiff = !@techCode.eql?(@uiCode)
|
264
|
+
# puts ":b #{datasource.fieldUIName(@name).nil?} ... #{@name} ... #{@uiname}"
|
265
|
+
# puts "CalculationField :: uin: %-25s | @name:%-s" % [@uiname,@name]
|
225
266
|
end
|
226
267
|
else # parts.length <> 1
|
268
|
+
# puts 'c'
|
227
269
|
rdstech = parts[0]
|
228
270
|
calcField = parts[1]
|
229
|
-
@
|
230
|
-
@
|
231
|
-
@dataSourceRef
|
271
|
+
@uiname = calcField
|
272
|
+
@dataSourceName = rdstech
|
273
|
+
@dataSourceRef = :remote
|
232
274
|
@techCode = "[#{rdstech}].[#{calcField}]"
|
233
275
|
workbook = datasource.workbook
|
234
|
-
|
276
|
+
@dataSource = workbook.nil? ? nil : workbook.datasource(rdstech)
|
235
277
|
# puts "\t twb: #{workbook.class} / remoteds: #{remoteds.class} : #{remoteds.nil? ? "<<NOT FOUND:#{rdstech}:>>" : remoteds.uiname} "
|
236
278
|
#--
|
237
|
-
if
|
238
|
-
|
279
|
+
if @dataSource.nil? || @dataSource.fieldUIName(calcField).nil?
|
280
|
+
# puts 'd'
|
281
|
+
@uiname = calcField
|
239
282
|
@uiCode = "[<<NOT FOUND>>#{rdstech}].[#{calcField}]"
|
240
283
|
@techUIdiff = true
|
241
284
|
@dataSourceExists = false
|
242
|
-
else !remoteds.nil?
|
243
|
-
|
244
|
-
@
|
245
|
-
@
|
246
|
-
@
|
285
|
+
else # !remoteds.nil?
|
286
|
+
# puts 'e'
|
287
|
+
@dataSourceName = @dataSource.uiname
|
288
|
+
@uiname = @dataSource.fieldUIName(calcField)
|
289
|
+
@uiCode = "[#{@dataSourceName}].[#{@uiname}]"
|
290
|
+
@techUIdiff = !@techCode.eql?(@uiCode)
|
247
291
|
@dataSourceExists = true
|
248
292
|
end
|
249
293
|
end
|
250
|
-
|
294
|
+
# puts "\t dsName: #{@dataSourceName}"
|
295
|
+
# puts "\t @name: #{@name}"
|
296
|
+
# puts "\t uiname: #{@uiname}"
|
297
|
+
@fqName = "#{@dataSourceName}::#{@uiname}"
|
298
|
+
@type = if @dataSource.nil?
|
299
|
+
:CalculatedField
|
300
|
+
else
|
301
|
+
@dataSource.calculatedField(@uiname).nil? ? :DatabaseField : :CalculatedField
|
302
|
+
end
|
251
303
|
end # initialize
|
252
304
|
|
305
|
+
def id
|
306
|
+
@id ||= @id = "#{@dataSourceName}::#{@uiname}"
|
307
|
+
end
|
308
|
+
|
253
309
|
def <=>(other)
|
254
|
-
# myName = @
|
310
|
+
# myName = @uiname.nil? ? '' : @uiname
|
255
311
|
# otherName = other.uiName.nil? ? "" : other.uiName
|
256
|
-
# # puts "#{@
|
257
|
-
# # puts "#{@
|
312
|
+
# # puts "#{@uiname} / #{myName} <=> #{otherName} / #{other.uiName}"
|
313
|
+
# # puts "#{@uiname.nil?} // #{other.uiName.nil?}"
|
258
314
|
# myName <=> otherName
|
259
315
|
@fqName <=> other.fqName
|
260
316
|
end
|