twb 4.9.10 → 5.2.3
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 +2 -1
- data/lib/twb/analysis/calculatedfields/calculatedfieldsanalyzer.rb +99 -120
- 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/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/quickfilter.rb +1 -1
- data/test/testFieldCalculation.rb +10 -6
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebe8daee637317b5fb8024b346e2eed50883e00839a0d21a031672a55e116f44
|
4
|
+
data.tar.gz: 509985403a45d56d8b3fc3690d3d14c934700fe6879d356de9579cd3df6792bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da4d761cceb4011cdd9442ef2ce959a3d74e9b2d1cdc004ef484c6418bfa7bc6d48e1848950a11d73d3af403de7ded1c8a3b4b07b8262b881ac9f2cab0c3c05e
|
7
|
+
data.tar.gz: 50fab8909339cb928ed2d717409b66a965c1588b96c47100842e8f667c7a85bdf9c680f66482ca6244ccd23437998b623c4885f2970a8e5a00539a2254560d1d
|
data/lib/twb.rb
CHANGED
@@ -54,6 +54,7 @@ require_relative 'twb/analysis/documentedfieldsmarkdownemitter'
|
|
54
54
|
require_relative 'twb/analysis/annotatedfieldscsvemitter'
|
55
55
|
require_relative 'twb/analysis/workbooksummaryanalyzer'
|
56
56
|
require_relative 'twb/analysis/calculatedfields/calculatedfieldsanalyzer'
|
57
|
+
require_relative 'twb/analysis/calculatedfields/dotanalyzer'
|
57
58
|
require_relative 'twb/analysis/calculatedfields/groupfieldsanalyzer'
|
58
59
|
require_relative 'twb/analysis/calculatedfields/markdownemitter'
|
59
60
|
require_relative 'twb/analysis/calculatedfields/csvemitter'
|
@@ -80,5 +81,5 @@ require_relative 'twb/analysis/sheets/sheetsintooltipanalyzer'
|
|
80
81
|
# Represents Tableau Workbooks, their contents, and classes that analyze and manipulate them.
|
81
82
|
#
|
82
83
|
module Twb
|
83
|
-
VERSION = '
|
84
|
+
VERSION = '5.2.3'
|
84
85
|
end
|
@@ -78,12 +78,12 @@ module Analysis
|
|
78
78
|
@fieldTables = {}
|
79
79
|
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
# @@dotHeader = <<DOTHEADER
|
82
|
+
# digraph g {
|
83
|
+
# graph [rankdir="LR" splines=line];
|
84
|
+
# node [shape="box" width="2"];
|
85
85
|
|
86
|
-
DOTHEADER
|
86
|
+
# DOTHEADER
|
87
87
|
|
88
88
|
def initialize(**args)
|
89
89
|
emit "initialize CalculatedFieldsAnalyzer args #{args}"
|
@@ -141,7 +141,7 @@ DOTHEADER
|
|
141
141
|
# end
|
142
142
|
processDataSource ds
|
143
143
|
end
|
144
|
-
mapTwb
|
144
|
+
# mapTwb
|
145
145
|
emitGml
|
146
146
|
@twbCount += 1
|
147
147
|
finis
|
@@ -195,10 +195,10 @@ DOTHEADER
|
|
195
195
|
calculatedFields.add calcField.id
|
196
196
|
dsFields[calcField.uiname] = calcField
|
197
197
|
# if @doGraph
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
198
|
+
calcFieldNode = Twb::Util::Graphnode.new(name: calcField.uiname, id: calcField.id, type: calcField, properties: {:DataSource => ds.uiname})
|
199
|
+
@nodes.add calcFieldNode
|
200
|
+
dsFieldEdge = Twb::Util::Graphedge.new(from: dataSourceNode, to: calcFieldNode, relationship: 'contains')
|
201
|
+
@edges.add dsFieldEdge
|
202
202
|
# end
|
203
203
|
calculation = calcField.calculation
|
204
204
|
if calculation.has_formula
|
@@ -249,47 +249,26 @@ DOTHEADER
|
|
249
249
|
]
|
250
250
|
end
|
251
251
|
#-- collect fields referenced in formula
|
252
|
-
emit "# Calculated Fields: #{calculation.
|
253
|
-
calculation.
|
254
|
-
emit " referenced field ::
|
255
|
-
emit " referenced field.name ::'#{rf.name.nil?}' :: '#{rf.name}'"
|
256
|
-
emit " referenced field.uiname::'#{rf.uiname}'"
|
257
|
-
# if @doGraph
|
258
|
-
unless rf.uiname.nil?
|
259
|
-
properties = {'DataSource' => ds.uiname, 'DataSourceReference' => 'local', :source => rf}
|
260
|
-
refFieldNode = Twb::Util::Graphnode.new(name: rf.uiname, id: rf.id, type: rf.type, properties: properties)
|
261
|
-
@nodes.add refFieldNode
|
262
|
-
fieldFieldEdge = Twb::Util::Graphedge.new(from: calcFieldNode, to: refFieldNode, relationship: 'references')
|
263
|
-
@edges.add fieldFieldEdge
|
264
|
-
end
|
265
|
-
# end
|
266
|
-
referencedFields.add rf.id
|
267
|
-
refFieldTable = ds.fieldTable(rf.name)
|
268
|
-
emit "refFieldTable.nil? : #{refFieldTable.nil?}"
|
269
|
-
unless refFieldTable.nil?
|
270
|
-
tableID = refFieldTable + ':::' + ds.uiname
|
271
|
-
tableName = "||#{refFieldTable}||"
|
272
|
-
# if @doGraph
|
273
|
-
tableNode = Twb::Util::Graphnode.new(name: tableName, id: tableID, type: :DBTable, properties: properties)
|
274
|
-
@nodes.add tableNode
|
275
|
-
fieldFieldEdge = Twb::Util::Graphedge.new(from: refFieldNode, to: tableNode, relationship: 'is a field in')
|
276
|
-
@edges.add fieldFieldEdge
|
277
|
-
# end
|
278
|
-
# fldToDsNode = tableNode
|
279
|
-
end
|
252
|
+
emit "# Calculated Fields: #{calculation.referencedFields.length}"
|
253
|
+
calculation.referencedFields.each do |rf|
|
254
|
+
emit " referenced field :: %12s %s " % [ rf.dataSourceName, rf.uiname ]
|
280
255
|
@csvFormulaFields << [
|
281
256
|
@referencedFieldsCount += 1,
|
282
257
|
@twb.name,
|
283
258
|
# @modTime,
|
284
|
-
ds.uiname,
|
259
|
+
rf.dataSourceName, # ds.uiname,
|
285
260
|
calcField.uiname,
|
286
261
|
calculation.formulaFlat,
|
287
262
|
calculation.formulaFlatResolved,
|
288
263
|
rf.name,
|
289
|
-
rf.uiname,
|
290
|
-
rf.id,
|
291
|
-
refFieldTable
|
264
|
+
rf.uiname, #.uiname,
|
265
|
+
'', # rf.id,
|
266
|
+
'', #refFieldTable
|
292
267
|
]
|
268
|
+
refFieldNode = Twb::Util::Graphnode.new(name: rf.uiname, id: rf.id, type: rf, properties: {:DataSource => ds.uiname})
|
269
|
+
@nodes.add refFieldNode
|
270
|
+
refFieldEdge = Twb::Util::Graphedge.new(from: calcFieldNode, to: refFieldNode , relationship: 'references')
|
271
|
+
@edges.add refFieldEdge
|
293
272
|
end # resolvedFields.each do
|
294
273
|
end # if calculation.has_formula
|
295
274
|
end # ds.calculatedFields.each
|
@@ -330,49 +309,49 @@ DOTHEADER
|
|
330
309
|
emit "\t formula:: #{calculation.formulaFlat}"
|
331
310
|
end
|
332
311
|
|
333
|
-
def mapTwb
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
end
|
312
|
+
# def mapTwb
|
313
|
+
# twb = @twb.name
|
314
|
+
# rootFields = @twbRootFields
|
315
|
+
# dotStuff = initDot twb
|
316
|
+
# dotFile = dotStuff[:file]
|
317
|
+
# dotFileName = dotStuff[:name]
|
318
|
+
# dotFile.puts "\n // subgraph cluster_1 {"
|
319
|
+
# dotFile.puts " // color= grey;"
|
320
|
+
# dotFile.puts ""
|
321
|
+
# edgesAsStrings = SortedSet.new
|
322
|
+
# # this two step process coalesces the edges into a unique set, avoiding duplicating the dot
|
323
|
+
# # file entries, and can be shrunk when graph edges expose the bits necessary for management by Set
|
324
|
+
# emit "\n========================\nLoading Edges\n========================\n From DC? Referenced? Edge \n %s %s %s" % ['--------', '-----------', '-'*45]
|
325
|
+
# @edges.each do |e|
|
326
|
+
# # don't want to emit edge which is from a Data Connection to a
|
327
|
+
# # Calculated Field which is also referenced by another calculated field
|
328
|
+
# isFromDC = e.from.type == :TwbDataConnection
|
329
|
+
# isRefField = @referencedFields.include?(e.to.id)
|
330
|
+
# edgesAsStrings.add(e.dot) unless isFromDC && isRefField
|
331
|
+
# # emit " ES #{e.dot}"
|
332
|
+
# # emit " ES from #{e.from}"
|
333
|
+
# # emit " ES to #{e.to}"
|
334
|
+
# end
|
335
|
+
# emit "------------------------\n "
|
336
|
+
# edgesAsStrings.each do |es|
|
337
|
+
# dotFile.puts " #{es}"
|
338
|
+
# end
|
339
|
+
# emit "========================\n "
|
340
|
+
# dotFile.puts ""
|
341
|
+
# dotFile.puts " // }"
|
342
|
+
# dotFile.puts "\n\n // 4 NODES --------------------------------------------------------------------"
|
343
|
+
# @nodes.each do |n|
|
344
|
+
# dotFile.puts n.dotLabel
|
345
|
+
# end
|
346
|
+
# dotFile.puts "\n\n // 5--------------------------------------------------------------------"
|
347
|
+
# emitTypes( dotFile )
|
348
|
+
# closeDot( dotFile, twb )
|
349
|
+
# emit "Rendering DOT file - #{twb}"
|
350
|
+
# renderDot(twb,dotFileName,'pdf')
|
351
|
+
# renderDot(twb,dotFileName,'png')
|
352
|
+
# renderDot(twb,dotFileName,'svg')
|
353
|
+
# # emitEdges
|
354
|
+
# end
|
376
355
|
|
377
356
|
def cypher twbName
|
378
357
|
if @doGraph
|
@@ -488,44 +467,44 @@ DOTHEADER
|
|
488
467
|
dotFile.puts ' }'
|
489
468
|
end
|
490
469
|
|
491
|
-
def initDot twb
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
end
|
470
|
+
# def initDot twb
|
471
|
+
# dotFileName = docFile("#{twb}#{@@processName}.dot")
|
472
|
+
# dotFile = File.open(dotFileName,'w')
|
473
|
+
# dotFile.puts @@dotHeader
|
474
|
+
# return {:file => dotFile, :name => dotFileName}
|
475
|
+
# end
|
497
476
|
|
498
|
-
def closeDot dotFile, twb
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
end
|
477
|
+
# def closeDot dotFile, twb
|
478
|
+
# dotFile.puts ' '
|
479
|
+
# dotFile.puts '// -------------------------------------------------------------'
|
480
|
+
# dotFile.puts ' '
|
481
|
+
# dotFile.puts ' subgraph cluster_1 {'
|
482
|
+
# # dotFile.puts ' color=white;'
|
483
|
+
# dotFile.puts ' style=invis;'
|
484
|
+
# # dotFile.puts ' border=0;'
|
485
|
+
# dotFile.puts ' node [border=blue];'
|
486
|
+
# dotFile.puts ' '
|
487
|
+
# dotFile.puts ' "" [style=invis]'
|
488
|
+
# dotFile.puts " \"Tableau Tools\\nCalculated Fields Map\\nWorkbook '#{twb}'\\n#{Time.new.ctime}\" [penwidth=0]"
|
489
|
+
# # dotFile.puts " \"Tableau Tools Workbook Calculated Fields Map\\n#{Time.new.ctime}\" -> \"\" [style=invis]"
|
490
|
+
# dotFile.puts ' '
|
491
|
+
# dotFile.puts ' }'
|
492
|
+
# dotFile.puts ' '
|
493
|
+
# dotFile.puts '}'
|
494
|
+
# dotFile.close
|
495
|
+
# end
|
517
496
|
|
518
497
|
|
519
|
-
def renderDot twb, dot, format
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
end
|
498
|
+
# def renderDot twb, dot, format
|
499
|
+
# imageType = '-T' + format
|
500
|
+
# imageFile = './ttdoc/' + twb + @@processName + 'Graph.' + format
|
501
|
+
# imageParam = '-o"' + imageFile + '"'
|
502
|
+
# emit "system #{@@gvDotLocation} #{imageType} #{imageParam} \"#{dot}\""
|
503
|
+
# system "#{@@gvDotLocation} #{imageType} #{imageParam} \"#{dot}\""
|
504
|
+
# emit " - #{imageFile}"
|
505
|
+
# @imageFiles << imageFile
|
506
|
+
# return imageFile
|
507
|
+
# end
|
529
508
|
|
530
509
|
end # class
|
531
510
|
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# dotanalyzer.rb - this Ruby script Copyright 2017, 2018 Christopher 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
|
+
|
17
|
+
module Twb
|
18
|
+
module Analysis
|
19
|
+
module CalculatedFields
|
20
|
+
|
21
|
+
class DotAnalyzer
|
22
|
+
include TabTool
|
23
|
+
|
24
|
+
attr_reader :docFileName
|
25
|
+
|
26
|
+
@@gvDotLocation = 'C:\\tech\\graphviz\\Graphviz2.38\\bin\\dot.exe'
|
27
|
+
@@imageTypes = ['pdf', 'png', 'svg']
|
28
|
+
|
29
|
+
def initialize(**args)
|
30
|
+
@args = args
|
31
|
+
init
|
32
|
+
@funcdoc = {:class=>self.class, :blurb=>'Create Dot files documenting Calculated Fields', :description=>'Analyze Calculated Fields - create Dot files' }
|
33
|
+
@metrics = {}
|
34
|
+
@imageFiles = Array.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def processTWB twb
|
38
|
+
# twb = File.basename(twb)
|
39
|
+
@twb = twb #Twb::Workbook.new twb
|
40
|
+
addDocFile @dotFile, @dotFileName, "Dot file of Calculated fields for Workbook '#{@twb.name}'"
|
41
|
+
@twb.datasources.each do |ds|
|
42
|
+
unless ds.calculatedFields.empty?
|
43
|
+
initDotFile ds.uiname
|
44
|
+
# @dotFile.puts "\n ## #{ds.uiname} \n "
|
45
|
+
# @dotFile.puts "__has #{ds.calculatedFields.length} calculated fields__\n "
|
46
|
+
@cfCnt = 0
|
47
|
+
calcFields = Set.new
|
48
|
+
refFields = Set.new
|
49
|
+
edges = Set.new
|
50
|
+
ds.calculatedFields.each do |cf|
|
51
|
+
@cfCnt += 1
|
52
|
+
calcFields << cf.uiname
|
53
|
+
edges << " \"#{ds.uiname}\" -> \"#{cf.uiname}\" [tailport=e, headport=w] "
|
54
|
+
cf.referencedFields.each do |rf|
|
55
|
+
refFields << rf.uiname
|
56
|
+
edges << " \"#{cf.uiname}\" -> \"#{rf.uiname}\" [tailport=e, headport=w] "
|
57
|
+
end
|
58
|
+
end # ds.calculatedFields.each
|
59
|
+
# "federated.17h7owt0rsacke17cql8o0w2ittk" -> "New AO Actuals Query in PP+ (AO Variance Data)::vs Prior Year [YTD]"
|
60
|
+
# "federated.01s5lca037ted31gxs9sg0t9mnnt" [label="Controls" ]
|
61
|
+
edges.each do |edge|
|
62
|
+
@dotFile.puts "\t #{edge.strip}"
|
63
|
+
end
|
64
|
+
@dotFile.puts " "
|
65
|
+
allFields = calcFields + refFields
|
66
|
+
allFields.each do |f|
|
67
|
+
@dotFile.puts "\t \"#{f}\" [label=\"#{f}\"]"
|
68
|
+
end
|
69
|
+
endPointFields = allFields - calcFields
|
70
|
+
rankSame(endPointFields) unless endPointFields.nil? || endPointFields.empty?
|
71
|
+
closeDotFile
|
72
|
+
@@imageTypes.each do |type|
|
73
|
+
renderDot type
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end # twb.datasources.each
|
77
|
+
finis
|
78
|
+
end # def processTwb twb
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def initDotFile dsName
|
83
|
+
@dotFileName = './ttdoc/' + @twb.name + '.' + dsName + '.CalculatedFields.dot'
|
84
|
+
@dotFile = File.open(@dotFileName,'w')
|
85
|
+
# @dotFile.puts @@dotHeader
|
86
|
+
@dotFile.puts ' digraph g {'
|
87
|
+
@dotFile.puts ' graph [rankdir="LR" splines=line];'
|
88
|
+
@dotFile.puts ' node [shape="box" width="2"];'
|
89
|
+
@dotFile.puts ' '
|
90
|
+
@dotFile.puts ' subgraph cluster_0 {'
|
91
|
+
end
|
92
|
+
|
93
|
+
def closeDotFile
|
94
|
+
# @dotFile.puts "\n # counted #{@cfCnt} calculated fields\n "
|
95
|
+
# @dotFile.puts "\n }"
|
96
|
+
@dotFile.puts ' }'
|
97
|
+
@dotFile.puts ' '
|
98
|
+
@dotFile.puts '// -------------------------------------------------------------'
|
99
|
+
@dotFile.puts ' '
|
100
|
+
@dotFile.puts ' subgraph cluster_1 {'
|
101
|
+
#@dotFile.puts ' color=white;'
|
102
|
+
@dotFile.puts ' style=invis;'
|
103
|
+
#@dotFile.puts ' border=0;'
|
104
|
+
@dotFile.puts ' node [border=blue];'
|
105
|
+
@dotFile.puts ' '
|
106
|
+
@dotFile.puts ' "" [style=invis]'
|
107
|
+
@dotFile.puts " \"Tableau Tools\\nCalculated Fields Map\\nWorkbook '#{@twb.name}'\\n#{Time.new.ctime}\" [penwidth=0]"
|
108
|
+
#@dotFile.puts " \"Tableau Tools Workbook Calculated Fields Map\\n#{Time.new.ctime}\" -> \"\" [style=invis]"
|
109
|
+
@dotFile.puts ' '
|
110
|
+
@dotFile.puts ' }'
|
111
|
+
@dotFile.puts ' '
|
112
|
+
@dotFile.puts '}'
|
113
|
+
@dotFile.close
|
114
|
+
end
|
115
|
+
|
116
|
+
def rankSame fields
|
117
|
+
@dotFile.puts "\n {rank=same "
|
118
|
+
fields.each do |f|
|
119
|
+
@dotFile.puts "\t \"#{f}\" "
|
120
|
+
end
|
121
|
+
@dotFile.puts " } "
|
122
|
+
end
|
123
|
+
|
124
|
+
def renderDot format
|
125
|
+
imageType = '-T' + format
|
126
|
+
imageFile = @dotFileName + '.Graph.' + format
|
127
|
+
imageParam = '-o"' + imageFile + '"'
|
128
|
+
emit "system #{@@gvDotLocation} #{imageType} #{imageParam} \"#{@dotFileName}\""
|
129
|
+
system "#{@@gvDotLocation} #{imageType} #{imageParam} \"#{@dotFileName}\""
|
130
|
+
# emit " - #{imageFile}"
|
131
|
+
@imageFiles << imageFile
|
132
|
+
return imageFile
|
133
|
+
end
|
134
|
+
|
135
|
+
end # class DotAnalyzer
|
136
|
+
|
137
|
+
end # nodule CalculatedFields
|
138
|
+
end # module Analysis
|
139
|
+
end # module Twb
|
@@ -52,10 +52,10 @@ module CalculatedFields
|
|
52
52
|
@docFile.puts "#{l.gsub('<<','[').gsub('>>',']')}"
|
53
53
|
end
|
54
54
|
@docFile.puts "```"
|
55
|
-
if cf.
|
55
|
+
if cf.referencedFields.length > 0
|
56
56
|
fieldsRefOrder = []
|
57
57
|
fieldsSortSet = SortedSet.new
|
58
|
-
cf.
|
58
|
+
cf.referencedFields.each do |field|
|
59
59
|
fieldsRefOrder.push field.uiname
|
60
60
|
fieldsSortSet << field.uiname
|
61
61
|
end
|
data/lib/twb/calculatedfield.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014, 2015,
|
1
|
+
# Copyright (C) 2014, 2015, 2020 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
|
@@ -26,7 +26,7 @@ module Twb
|
|
26
26
|
attr_reader :node, :properties
|
27
27
|
attr_reader :caption, :name, :uiname
|
28
28
|
attr_reader :datatype, :role, :propType
|
29
|
-
attr_reader :calculation, :
|
29
|
+
attr_reader :calculation, :referencedFields
|
30
30
|
attr_reader :isGroup, :groupMembers
|
31
31
|
attr_reader :hidden
|
32
32
|
|
@@ -52,8 +52,8 @@ module Twb
|
|
52
52
|
@properties ||= loadProperties
|
53
53
|
end
|
54
54
|
|
55
|
-
def
|
56
|
-
@calculation.
|
55
|
+
def referencedFields
|
56
|
+
@calculation.referencedFields
|
57
57
|
end
|
58
58
|
|
59
59
|
def formulaLines
|
data/lib/twb/columnfield.rb
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
15
|
|
16
16
|
require 'nokogiri'
|
17
|
+
require 'pry'
|
17
18
|
|
18
19
|
module Twb
|
19
20
|
|
@@ -85,7 +86,6 @@ module Twb
|
|
85
86
|
@aggregation = load 'aggregation'
|
86
87
|
@autoColumn = load 'auto-column'
|
87
88
|
@datatypeCustomized = load 'datatype-customized'
|
88
|
-
# @calcField = loadCalcField
|
89
89
|
end
|
90
90
|
|
91
91
|
def id
|
@@ -94,7 +94,7 @@ module Twb
|
|
94
94
|
|
95
95
|
def load nodeName
|
96
96
|
attr = @node.attribute(nodeName)
|
97
|
-
val = attr.nil? ? nil : attr.text.strip
|
97
|
+
val = attr.nil? ? nil : attr.text.strip
|
98
98
|
return val
|
99
99
|
end
|
100
100
|
|
data/lib/twb/datasource.rb
CHANGED
@@ -56,7 +56,7 @@ module Twb
|
|
56
56
|
attr_reader :tableFieldsMap
|
57
57
|
attr_reader :fieldUINames
|
58
58
|
attr_reader :aliases
|
59
|
-
attr_reader :calculatedFields, :
|
59
|
+
attr_reader :calculatedFields, :calculatedFieldsMap, :calculatedFieldNames, :calculatedField
|
60
60
|
attr_reader :allFields
|
61
61
|
attr_reader :groups
|
62
62
|
attr_reader :filters
|
@@ -391,40 +391,6 @@ module Twb
|
|
391
391
|
return @fieldUINames
|
392
392
|
end
|
393
393
|
|
394
|
-
def loadFieldUINames
|
395
|
-
# puts 'loadFieldUINames'
|
396
|
-
@fieldUINames = {}
|
397
|
-
# puts 'metadataFields'
|
398
|
-
metadataFields.each do |fld|
|
399
|
-
# puts " - name:%-45s | uiname:%-45s | localName:%-45s " % [ "'#{fld.name}'", "'#{fld.uiname}'", "'#{fld.localName}'"]
|
400
|
-
@fieldUINames[fld.uiname] = fld.uiname
|
401
|
-
@fieldUINames[fld.localName] = fld.uiname unless fld.localName.nil?
|
402
|
-
@fieldUINames[fld.name] = fld.uiname unless fld.name.nil?
|
403
|
-
end
|
404
|
-
# puts 'calculatedFields'
|
405
|
-
calculatedFields.each do |fld|
|
406
|
-
# puts " - name:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.uiname}'"]
|
407
|
-
@fieldUINames[fld.name] = fld.uiname
|
408
|
-
@fieldUINames[fld.uiname] = fld.uiname
|
409
|
-
end
|
410
|
-
# puts 'localFields'
|
411
|
-
localFields.each do |fld|
|
412
|
-
# puts " - name:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.uiname}'"]
|
413
|
-
@fieldUINames[fld.name] = fld.uiname
|
414
|
-
@fieldUINames[fld.uiname] = fld.uiname
|
415
|
-
end
|
416
|
-
# puts "columnFields: #{columnFields.length}"
|
417
|
-
columnFields.each do |fld|
|
418
|
-
@fieldUINames[fld.name] = fld.uiname
|
419
|
-
@fieldUINames[fld.uiname] = fld.uiname
|
420
|
-
end
|
421
|
-
groups.each do |fld|
|
422
|
-
@fieldUINames[fld.name] = fld.uiname
|
423
|
-
@fieldUINames[fld.uiname] = fld.uiname
|
424
|
-
end
|
425
|
-
return @fieldUINames
|
426
|
-
end
|
427
|
-
|
428
394
|
def tableFields
|
429
395
|
@tableFieldsMap.values
|
430
396
|
end
|
@@ -482,8 +448,7 @@ module Twb
|
|
482
448
|
|
483
449
|
def loadAllFields
|
484
450
|
@allFields = SortedSet.new
|
485
|
-
|
486
|
-
@allFields << dbf
|
451
|
+
@allFields << dbFields
|
487
452
|
@allFields << calculatedFieldNames
|
488
453
|
end
|
489
454
|
|
@@ -532,6 +497,53 @@ module Twb
|
|
532
497
|
|
533
498
|
private
|
534
499
|
|
500
|
+
def loadFieldUINames
|
501
|
+
@fieldUINames = {}
|
502
|
+
# puts"metadataFields: #{metadataFields.length}"
|
503
|
+
metadataFields.each do |fld|
|
504
|
+
# puts" - name:%-45s | caption:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.caption}'", "'#{fld.uiname}'"]
|
505
|
+
unless fld.name.nil?
|
506
|
+
@fieldUINames[fld.uiname] = fld.uiname
|
507
|
+
@fieldUINames[fld.localName] = fld.uiname unless fld.localName.nil?
|
508
|
+
@fieldUINames[fld.name] = fld.uiname
|
509
|
+
end
|
510
|
+
end
|
511
|
+
# puts"localFields: #{localFields.length}"
|
512
|
+
localFields.each do |fld|
|
513
|
+
# puts" - name:%-45s | caption:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.caption}'", "'#{fld.uiname}'"]
|
514
|
+
unless fld.caption.nil?
|
515
|
+
@fieldUINames[fld.name] = fld.caption
|
516
|
+
@fieldUINames[fld.uiname] = fld.caption
|
517
|
+
end
|
518
|
+
end
|
519
|
+
# puts"groups: #{groups.length}"
|
520
|
+
groups.each do |fld|
|
521
|
+
# puts" - name:%-45s | caption:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.caption}'", "'#{fld.uiname}'"]
|
522
|
+
unless fld.caption.nil?
|
523
|
+
@fieldUINames[fld.name] = fld.caption
|
524
|
+
@fieldUINames[fld.uiname] = fld.caption
|
525
|
+
end
|
526
|
+
end
|
527
|
+
# puts"calculatedFields: #{calculatedFields.length}"
|
528
|
+
calculatedFields.each do |fld|
|
529
|
+
# puts" - name:%-45s | caption:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.caption}'", "'#{fld.uiname}'"]
|
530
|
+
unless fld.caption.nil?
|
531
|
+
@fieldUINames[fld.name] = fld.caption
|
532
|
+
@fieldUINames[fld.uiname] = fld.caption
|
533
|
+
end
|
534
|
+
end
|
535
|
+
# puts"columnFields: #{columnFields.length}"
|
536
|
+
columnFields.each do |fld|
|
537
|
+
# puts" - name:%-45s | caption:%-45s | uiname:%-45s " % [ "'#{fld.name}'", "'#{fld.caption}'", "'#{fld.uiname}'"]
|
538
|
+
unless fld.caption.nil?
|
539
|
+
# @fieldUINames[fld.name] = fld.caption
|
540
|
+
@fieldUINames[fld.name.gsub(/^[\[]|[\]]$/,'')] = fld.caption
|
541
|
+
@fieldUINames[fld.uiname] = fld.caption
|
542
|
+
end
|
543
|
+
end
|
544
|
+
return @fieldUINames
|
545
|
+
end
|
546
|
+
|
535
547
|
def loadGroups
|
536
548
|
@groups = []
|
537
549
|
groupNodes = @node.xpath('.//group')
|
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/quickfilter.rb
CHANGED
@@ -120,7 +120,7 @@ module Twb
|
|
120
120
|
# filter class='relative-date' column='[Sample - Superstore].[none:Order Date:qk]' first-period='1' include-future='true' include-null='false' last-period='1' period-type='year' />
|
121
121
|
def resolveRelativeDate
|
122
122
|
emit "resolveRelativeDate"
|
123
|
-
periodType = @node['period-type']
|
123
|
+
periodType = @node['period-type'].nil? ? '' : @node['period-type']
|
124
124
|
inclFuture = @node['include-future'] == 'true'
|
125
125
|
firstPeriod = @node['first-period'].to_i
|
126
126
|
lastPeriod = @node['last-period'].to_i
|
@@ -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.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Gerrard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: creek
|
@@ -64,9 +64,11 @@ files:
|
|
64
64
|
- lib/twb/analysis/annotatedfieldscsvemitter.rb
|
65
65
|
- lib/twb/analysis/calculatedfields/calculatedfieldsanalyzer.rb
|
66
66
|
- lib/twb/analysis/calculatedfields/csvemitter.rb
|
67
|
+
- lib/twb/analysis/calculatedfields/dotanalyzer.rb
|
67
68
|
- lib/twb/analysis/calculatedfields/fieldsaliasesanalyzer.rb
|
68
69
|
- lib/twb/analysis/calculatedfields/groupfieldsanalyzer.rb
|
69
70
|
- lib/twb/analysis/calculatedfields/markdownemitter.rb
|
71
|
+
- lib/twb/analysis/calculatedfields/t.rb
|
70
72
|
- lib/twb/analysis/datasources/categoricalcolorcodinganalyzer.rb
|
71
73
|
- lib/twb/analysis/datasources/datasourcefieldsanalyzer.rb
|
72
74
|
- lib/twb/analysis/datasources/datasourcefieldscsvemitter.rb
|