twb 2.2.1 → 3.7.2

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