twb 4.9.10 → 5.2.3
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 +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
|