twb 2.2.1 → 3.7.2

Sign up to get free protection for your applications and to get access to all the features.
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