twb 4.9.4 → 5.2.1
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/tfl/Flow.rb +311 -0
- data/lib/twb.rb +3 -2
- data/lib/twb/analysis/calculatedfields/calculatedfieldsanalyzer.rb +101 -122
- data/lib/twb/analysis/calculatedfields/dotanalyzer.rb +139 -0
- data/lib/twb/analysis/calculatedfields/markdownemitter.rb +2 -2
- data/lib/twb/analysis/calculatedfields/t.rb +6 -0
- data/lib/twb/analysis/datasources/datasourcefilesanalyzer.rb +7 -4
- data/lib/twb/analysis/sheets/dashboardsummarizer.rb +94 -0
- data/lib/twb/analysis/workbooksummaryanalyzer.rb +2 -1
- data/lib/twb/calculatedfield.rb +4 -4
- data/lib/twb/columnfield.rb +2 -2
- data/lib/twb/datasource.rb +49 -37
- data/lib/twb/fieldcalculation.rb +178 -101
- data/lib/twb/util/cypher.rb +1 -1
- data/lib/twb/util/gml.rb +1 -1
- data/test/testFieldCalculation.rb +10 -6
- metadata +6 -2
data/lib/twb/fieldcalculation.rb
CHANGED
@@ -16,6 +16,7 @@
|
|
16
16
|
require 'nokogiri'
|
17
17
|
require 'digest/md5'
|
18
18
|
require 'csv'
|
19
|
+
require 'pry'
|
19
20
|
|
20
21
|
module Twb
|
21
22
|
|
@@ -35,7 +36,7 @@ module Twb
|
|
35
36
|
attr_reader :is_tableCalc
|
36
37
|
attr_reader :is_lod, :lodCodePos
|
37
38
|
attr_reader :class, :scopeIsolation
|
38
|
-
attr_reader :fields, :remoteFields,
|
39
|
+
attr_reader :fields, :remoteFields, :referencedFields
|
39
40
|
attr_reader :comments, :uuid
|
40
41
|
|
41
42
|
# attr_accessor :ttlogfile
|
@@ -120,59 +121,33 @@ module Twb
|
|
120
121
|
end
|
121
122
|
|
122
123
|
def formulaResolved
|
123
|
-
@formulaResolved ||=
|
124
|
+
@formulaResolved ||= resolveFormula
|
124
125
|
end
|
125
126
|
|
126
127
|
def resolveFormula
|
127
|
-
#
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
128
|
+
# emit "\ndef resolveFormula:\n--\n#{@formula}"
|
129
|
+
resolved = @formula
|
130
|
+
# emit "\t formula:#{resolved}:"
|
131
|
+
# parseFormFields # - extracts the fields from the formula; as persisted they're the internal names
|
132
|
+
referencedFields.each do |refField|
|
133
|
+
# emit "\t refField: "
|
134
|
+
resolved.gsub!(refField.techCode,refField.uiCode)
|
135
|
+
# if calcField.techUIdiff
|
136
|
+
# # puts ":::: #{calcField.techCode} // #{calcField.uiCode}"
|
137
|
+
# formula = formula.gsub(calcField.techCode,calcField.uiCode)
|
138
|
+
# # puts ":--: #{formula}"
|
139
|
+
# end
|
136
140
|
end
|
137
|
-
|
141
|
+
# emit "\t formula:#{resolved}:"
|
142
|
+
return resolved
|
138
143
|
end
|
139
144
|
|
140
|
-
def
|
141
|
-
@
|
145
|
+
def referencedFields
|
146
|
+
@referencedFields ||= parseFormFields
|
142
147
|
end
|
143
148
|
|
144
|
-
def parseFormFields
|
145
|
-
# puts "--parseFormFields"
|
146
|
-
@fields = Set.new
|
147
|
-
@calcFields = Set.new
|
148
|
-
formula = @formulaFlat
|
149
|
-
if !formula.nil? && formula.include?('[') && formula.include?(']')
|
150
|
-
fields = Set.new
|
151
|
-
# noSqLits = formula.gsub( /'[\[\.\]]+'/, ' ')
|
152
|
-
quotes = formula.gsub('"',"'")
|
153
|
-
noSqLits = quotes.gsub( /'[\[\.\]]+'/, ' ')
|
154
|
-
flatForm = noSqLits.gsub( /\n/, ' ')
|
155
|
-
stripFrt = flatForm.gsub( /^[^\[]*[\[]/ , '[' )
|
156
|
-
stripBck = stripFrt.gsub( /\][^\]]+$/ , ']' )
|
157
|
-
stripMid = stripBck.gsub( /\][^\]]{2,}\[/ , ']]..[[' )
|
158
|
-
stripCom = stripMid.gsub( /\][ ]*,[ ]*\[/ , ']]..[[' )
|
159
|
-
stripFns = stripMid.gsub( /\][ ]*[\*\/+\-><,=][ ]*\[/ , ']]..[[' )
|
160
|
-
fields = stripFns.split(']..[')
|
161
|
-
emit "::self::: #{self} :: #{__LINE__} :: fields:'#{fields.inspect}'"
|
162
|
-
fields.each do |field|
|
163
|
-
emit "::self::: #{self} :: #{__LINE__} :: field:'#{field}'"
|
164
|
-
cf = CalculationField.new( field.gsub(/^\[|\]$/, ''), @dataSource )
|
165
|
-
@calcFields.add cf
|
166
|
-
@fields.add field.gsub(/^\[|\]$/, '')
|
167
|
-
end
|
168
|
-
end
|
169
|
-
return @calcFields
|
170
|
-
end
|
171
149
|
|
172
150
|
def formulaResolvedLines
|
173
|
-
# puts "\ndef formulaResolvedLines\n--\n#{formulaResolved}"
|
174
|
-
# puts "--\n#{formulaResolved.split(/\n|\r\n/)}"
|
175
|
-
# puts "--\n#{formulaResolved.split(/\n|\r\n/)}"
|
176
151
|
formulaResolved.split(/\n|\r\n/)
|
177
152
|
end
|
178
153
|
|
@@ -212,11 +187,149 @@ module Twb
|
|
212
187
|
return comments.strip
|
213
188
|
end
|
214
189
|
|
215
|
-
|
190
|
+
private
|
216
191
|
|
217
192
|
|
193
|
+
def pullString chars
|
194
|
+
delim1 = chars.shift
|
195
|
+
delim2 = delim1+delim1
|
196
|
+
field = delim1
|
197
|
+
done = false
|
198
|
+
until done | chars.empty?
|
199
|
+
s01 = chars[0..1].join
|
200
|
+
if !delim1.eql? chars[0]
|
201
|
+
field += chars.shift
|
202
|
+
else
|
203
|
+
case s01
|
204
|
+
when delim2
|
205
|
+
field += delim2
|
206
|
+
chars.shift(2)
|
207
|
+
when delim1
|
208
|
+
field += delim1
|
209
|
+
chars.shift
|
210
|
+
done = true
|
211
|
+
else
|
212
|
+
field += delim1
|
213
|
+
chars.shift
|
214
|
+
done = true
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def pullField chars
|
221
|
+
# chars = str.split ''
|
222
|
+
done = false
|
223
|
+
ds = ''
|
224
|
+
field = ''
|
225
|
+
until done
|
226
|
+
s01 = chars[0..1].join
|
227
|
+
s02 = chars[0..2].join
|
228
|
+
if ']'.eql? chars[0]
|
229
|
+
case s01
|
230
|
+
when ']]'
|
231
|
+
field += ']]'
|
232
|
+
chars.shift(2)
|
233
|
+
when ']'
|
234
|
+
field += chars.shift
|
235
|
+
done = true
|
236
|
+
else
|
237
|
+
if '].['.eql?(s02)
|
238
|
+
ds = field + ']'
|
239
|
+
chars.shift(2)
|
240
|
+
# fldstr = chars.join
|
241
|
+
field = pullField(chars)[:field]
|
242
|
+
done = true
|
243
|
+
else
|
244
|
+
field += ']'
|
245
|
+
chars.shift
|
246
|
+
done = true
|
247
|
+
end
|
248
|
+
end
|
249
|
+
else
|
250
|
+
field += chars[0]
|
251
|
+
chars.shift
|
252
|
+
end
|
253
|
+
end
|
254
|
+
# puts "field: '#{field}' \t\t ds: #{ds}"
|
255
|
+
return {:field => field.sub(/\[/,'').sub(/\]$/,''), :ds => ds.sub(/\[/,'').sub(/\]$/,'') }
|
256
|
+
end
|
218
257
|
|
219
|
-
|
258
|
+
def parseFormFields # formula
|
259
|
+
@referencedFields = Array.new
|
260
|
+
rawFields = Array.new
|
261
|
+
if !@formula.nil? && @formula.include?('[') && @formula.include?(']')
|
262
|
+
chars = formula.split('')
|
263
|
+
until chars.empty?
|
264
|
+
char0 = chars[0]
|
265
|
+
case char0
|
266
|
+
when '"', "'"
|
267
|
+
pullString(chars)
|
268
|
+
when '['
|
269
|
+
rawFields << pullField(chars)
|
270
|
+
else
|
271
|
+
unless chars.nil? | chars.empty?
|
272
|
+
chars.shift
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
rawFields.each do |rf|
|
277
|
+
ds = rf[:ds]
|
278
|
+
dataSource = if ''.eql? ds
|
279
|
+
@dataSource
|
280
|
+
else
|
281
|
+
@dataSource.workbook.datasource(ds)
|
282
|
+
end
|
283
|
+
# fieldUIName = dataSource.fieldUIName(rf[:field])
|
284
|
+
refField = ReferencedField.new(rf[:field], dataSource)
|
285
|
+
@referencedFields << refField
|
286
|
+
end
|
287
|
+
end
|
288
|
+
return @referencedFields
|
289
|
+
end
|
290
|
+
|
291
|
+
def parseFormFieldsx # formula
|
292
|
+
rawFields = Set.new
|
293
|
+
if !@formula.nil? && @formula.include?('[') && @formula.include?(']')
|
294
|
+
noComms = @formula.gsub(/\/\/.*\r\n/,' ')
|
295
|
+
formBase = noComms.gsub(/\r\n/,' ')
|
296
|
+
formLen = formBase.length
|
297
|
+
formChars = formBase.split ''
|
298
|
+
until formChars.empty?
|
299
|
+
c = formChars.shift
|
300
|
+
case c
|
301
|
+
when '"', "'"
|
302
|
+
pullString(formChars, c)
|
303
|
+
when '['
|
304
|
+
rawFields << pullField(formChars, ']', @referencedFields)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
@referencedFields = Set.new
|
309
|
+
rawFields.each do |rf|
|
310
|
+
# @referencedFields << rf
|
311
|
+
dataSource = if ''.eql? rf[:ds]
|
312
|
+
@dataSource
|
313
|
+
else
|
314
|
+
@dataSource.workbook.datasource(rf[:ds])
|
315
|
+
end
|
316
|
+
# if dataSource.nil?
|
317
|
+
# binding.pry
|
318
|
+
# end
|
319
|
+
fieldUIName = dataSource.fieldUIName(rf[:field])
|
320
|
+
# binding.pry
|
321
|
+
refField = ReferencedField.new(rf[:field], dataSource)
|
322
|
+
# binding.pry
|
323
|
+
@referencedFields << refField
|
324
|
+
end
|
325
|
+
return @referencedFields
|
326
|
+
end
|
327
|
+
|
328
|
+
end # class FieldCalculation
|
329
|
+
|
330
|
+
|
331
|
+
# class CalculationField
|
332
|
+
class ReferencedField
|
220
333
|
# is a field used in a calculation, resolved into its human-meaningful form
|
221
334
|
|
222
335
|
include Comparable
|
@@ -228,65 +341,29 @@ module Twb
|
|
228
341
|
attr_reader :fqName, :type
|
229
342
|
attr_reader :techUIdiff
|
230
343
|
|
231
|
-
def initialize
|
232
|
-
# puts "\n\
|
344
|
+
def initialize name, datasource
|
345
|
+
# puts "\n\nReferencedField :: ds: %-25s | n: %s " % [datasource, name]
|
346
|
+
@name = name
|
233
347
|
@dataSource = datasource
|
234
|
-
@dataSourceName = datasource.uiname
|
348
|
+
@dataSourceName = datasource.nil? ? nil : datasource.uiname
|
235
349
|
@dataSourceRef = :local
|
236
350
|
@dataSourceExists = true
|
351
|
+
@techCode = "[#{name}]"
|
237
352
|
@techUIdiff = false
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
#puts "@name
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
else # !datasource.nil?
|
252
|
-
# puts 'b'
|
253
|
-
#puts "b - found uiname for '#{@name}'?: #{!datasource.fieldUIName(@name).nil?} \t is:#{datasource.fieldUIName(@name)} "
|
254
|
-
@uiname = datasource.fieldUIName(@name).nil? ? @name : datasource.fieldUIName(@name)
|
255
|
-
@uiCode = @uiname.nil? ? @techCode : "[#{@uiname}]"
|
256
|
-
@techUIdiff = !@techCode.eql?(@uiCode)
|
257
|
-
# puts ":b #{datasource.fieldUIName(@name).nil?} ... #{@name} ... #{@uiname}"
|
258
|
-
# puts "CalculationField :: uin: %-25s | @name:%-s" % [@uiname,@name]
|
259
|
-
end
|
260
|
-
else # parts.length <> 1
|
261
|
-
# puts 'c'
|
262
|
-
rdstech = parts[0]
|
263
|
-
calcField = parts[1]
|
264
|
-
@uiname = calcField
|
265
|
-
@dataSourceName = rdstech
|
266
|
-
@dataSourceRef = :remote
|
267
|
-
@techCode = "[#{rdstech}].[#{calcField}]"
|
268
|
-
workbook = datasource.workbook
|
269
|
-
@dataSource = workbook.nil? ? nil : workbook.datasource(rdstech)
|
270
|
-
# puts "\t twb: #{workbook.class} / remoteds: #{remoteds.class} : #{remoteds.nil? ? "<<NOT FOUND:#{rdstech}:>>" : remoteds.uiname} "
|
271
|
-
#--
|
272
|
-
if @dataSource.nil? || @dataSource.fieldUIName(calcField).nil?
|
273
|
-
# puts 'd'
|
274
|
-
@uiname = calcField
|
275
|
-
@uiCode = "[<<NOT FOUND>>#{rdstech}].[#{calcField}]"
|
276
|
-
@techUIdiff = true
|
277
|
-
@dataSourceExists = false
|
278
|
-
else # !remoteds.nil?
|
279
|
-
# puts 'e'
|
280
|
-
@dataSourceName = @dataSource.uiname
|
281
|
-
@uiname = @dataSource.fieldUIName(calcField)
|
282
|
-
@uiCode = "[#{@dataSourceName}].[#{@uiname}]"
|
283
|
-
@techUIdiff = !@techCode.eql?(@uiCode)
|
284
|
-
@dataSourceExists = true
|
285
|
-
end
|
353
|
+
if dataSource.nil?
|
354
|
+
# puts 'a'
|
355
|
+
@uiname = @name
|
356
|
+
@uiCode = @techCode
|
357
|
+
@techUIdiff = false
|
358
|
+
else # !datasource.nil?
|
359
|
+
# puts 'b'
|
360
|
+
# puts "b - found uiname for '#{@name}'?: #{!datasource.fieldUIName(@name).nil?} \t is:#{datasource.fieldUIName(@name)} "
|
361
|
+
@uiname = datasource.fieldUIName(@name).nil? ? @name : datasource.fieldUIName(@name)
|
362
|
+
@uiCode = @uiname.nil? ? @techCode : "[#{@uiname}]"
|
363
|
+
@techUIdiff = !@techCode.eql?(@uiCode)
|
364
|
+
# puts ":b #{datasource.fieldUIName(@name).nil?} ... #{@name} ... #{@uiname}"
|
365
|
+
# puts "CalculationField :: uin: %-25s | @name:%-s" % [@uiname,@name]
|
286
366
|
end
|
287
|
-
# puts "\t dsName: #{@dataSourceName}"
|
288
|
-
# puts "\t @name: #{@name}"
|
289
|
-
# puts "\t uiname: #{@uiname}"
|
290
367
|
@fqName = "#{@dataSourceName}::#{@uiname}"
|
291
368
|
@type = if @dataSource.nil?
|
292
369
|
:CalculatedField
|
@@ -308,6 +385,6 @@ module Twb
|
|
308
385
|
@fqName <=> other.fqName
|
309
386
|
end
|
310
387
|
|
311
|
-
end # class
|
388
|
+
end # class ReferencedField
|
312
389
|
|
313
390
|
end # module Twb
|
data/lib/twb/util/cypher.rb
CHANGED
data/lib/twb/util/gml.rb
CHANGED
@@ -16,7 +16,9 @@
|
|
16
16
|
require 'nokogiri'
|
17
17
|
|
18
18
|
#require 'twb'
|
19
|
-
require 'C:\tech\Tableau\tools\Ruby\gems\twb\lib\twb.rb'
|
19
|
+
# require 'C:\tech\Tableau\tools\Ruby\gems\twb\lib\twb.rb'
|
20
|
+
# require 'C:\tech\Tableau\Tableau Tools\Ruby\gems\twb\lib\twb.rb'
|
21
|
+
require 'twb'
|
20
22
|
require "test/unit"
|
21
23
|
|
22
24
|
system "cls"
|
@@ -25,10 +27,11 @@ class TestFieldCalculation < Test::Unit::TestCase
|
|
25
27
|
|
26
28
|
def test_fragment1
|
27
29
|
doc = Nokogiri::XML::Document.parse <<-EOHTML
|
28
|
-
<calculation class='tableau' formula='abc' />
|
30
|
+
<calculation class='tableau' name='I am a formular field' formula='abc' datatype='datatype' role='' type='' class='calcfield' />
|
29
31
|
EOHTML
|
30
|
-
calcNode
|
31
|
-
|
32
|
+
calcNode = doc.at_xpath('./calculation')
|
33
|
+
calcField = Twb::CalculatedField.new calcNode
|
34
|
+
calc = Twb::FieldCalculation.new(calcField)
|
32
35
|
assert(!calc.nil?)
|
33
36
|
#puts "node: #{calcNode}"
|
34
37
|
#puts "formula: #{calc.formula}"
|
@@ -41,10 +44,11 @@ EOHTML
|
|
41
44
|
|
42
45
|
def test_fragment2
|
43
46
|
doc = Nokogiri::XML::Document.parse <<-EOHTML
|
44
|
-
<calculation class='tableau' formula='// this is the number of days between the order and shipment datediff('day',[Order Date] , [other].[Ship Date])' />
|
47
|
+
<calculation class='tableau' name='Another formula fied' formula='// this is the number of days between the order and shipment datediff('day',[Order Date] , [other].[Ship Date])' />
|
45
48
|
EOHTML
|
46
49
|
calcNode = doc.at_xpath('./calculation')
|
47
|
-
|
50
|
+
calcField = Twb::CalculatedField calcNode
|
51
|
+
calc = Twb::FieldCalculation.new(calcField)
|
48
52
|
assert(!calc.nil?)
|
49
53
|
#puts "node: #{calcNode}"
|
50
54
|
#puts "formula: #{calc.formula}"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Gerrard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: creek
|
@@ -58,14 +58,17 @@ extensions: []
|
|
58
58
|
extra_rdoc_files: []
|
59
59
|
files:
|
60
60
|
- lib/t.rb
|
61
|
+
- lib/tfl/Flow.rb
|
61
62
|
- lib/twb.rb
|
62
63
|
- lib/twb/action.rb
|
63
64
|
- lib/twb/analysis/annotatedfieldscsvemitter.rb
|
64
65
|
- lib/twb/analysis/calculatedfields/calculatedfieldsanalyzer.rb
|
65
66
|
- lib/twb/analysis/calculatedfields/csvemitter.rb
|
67
|
+
- lib/twb/analysis/calculatedfields/dotanalyzer.rb
|
66
68
|
- lib/twb/analysis/calculatedfields/fieldsaliasesanalyzer.rb
|
67
69
|
- lib/twb/analysis/calculatedfields/groupfieldsanalyzer.rb
|
68
70
|
- lib/twb/analysis/calculatedfields/markdownemitter.rb
|
71
|
+
- lib/twb/analysis/calculatedfields/t.rb
|
69
72
|
- lib/twb/analysis/datasources/categoricalcolorcodinganalyzer.rb
|
70
73
|
- lib/twb/analysis/datasources/datasourcefieldsanalyzer.rb
|
71
74
|
- lib/twb/analysis/datasources/datasourcefieldscsvemitter.rb
|
@@ -82,6 +85,7 @@ files:
|
|
82
85
|
- lib/twb/analysis/documentedfieldscsvemitter.rb
|
83
86
|
- lib/twb/analysis/documentedfieldsmarkdownemitter.rb
|
84
87
|
- lib/twb/analysis/sheets/analyzedashboardsheets.rb
|
88
|
+
- lib/twb/analysis/sheets/dashboardsummarizer.rb
|
85
89
|
- lib/twb/analysis/sheets/dashsheetsanalyzer.rb
|
86
90
|
- lib/twb/analysis/sheets/sheetfieldsanalyzer.rb
|
87
91
|
- lib/twb/analysis/sheets/sheetfiltersanalyzer.rb
|