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.
- checksums.yaml +4 -4
- data/lib/twb.rb +13 -1
- data/lib/twb/action.rb +5 -1
- data/lib/twb/analysis/AnnotatedFieldsCSVEmitter.rb +3 -0
- data/lib/twb/analysis/CalculatedFields/CalculatedFieldsAnalyzer.rb +276 -287
- data/lib/twb/analysis/CalculatedFields/MarkdownEmitter.rb +48 -34
- data/lib/twb/analysis/DataSources/DataSourceFieldsCSVEmitter.rb +103 -103
- data/lib/twb/analysis/DataSources/googlesheetdatasourcesanalyzer.rb +79 -0
- data/lib/twb/analysis/DocumentedFieldsMarkdownEmitter.rb +1 -1
- data/lib/twb/analysis/Sheets/sheetfieldsanalyzer.rb +82 -0
- data/lib/twb/analysis/Sheets/sheetfiltersanalyzer.rb +214 -0
- data/lib/twb/calculatedfield.rb +20 -5
- data/lib/twb/codedfield.rb +87 -0
- data/lib/twb/columnfield.rb +21 -2
- data/lib/twb/connection.rb +33 -0
- data/lib/twb/dashboard.rb +5 -1
- data/lib/twb/datasource.rb +131 -20
- data/lib/twb/dbfield.rb +4 -0
- data/lib/twb/field.rb +5 -1
- data/lib/twb/fieldcalculation.rb +134 -78
- data/lib/twb/localfield.rb +5 -1
- data/lib/twb/mappedfield.rb +5 -1
- data/lib/twb/metadatafield.rb +5 -1
- data/lib/twb/storyboard.rb +5 -1
- data/lib/twb/tabclass.rb +71 -0
- data/lib/twb/tabtest.rb +31 -0
- data/lib/twb/tabtool.rb +63 -0
- data/lib/twb/twbcodedfield.rb +87 -0
- data/lib/twb/util/cypher.rb +112 -0
- data/lib/twb/util/cypherpython.rb +128 -0
- data/lib/twb/util/docprep.rb +46 -0
- data/lib/twb/util/fielddomainloader.rb +108 -0
- data/lib/twb/util/gml.rb +144 -0
- data/lib/twb/util/gmledge.rb +73 -0
- data/lib/twb/util/graph.rb +30 -0
- data/lib/twb/util/graphedge.rb +8 -9
- data/lib/twb/util/graphnode.rb +46 -29
- data/lib/twb/util/tabgraph.rb +30 -0
- data/lib/twb/window.rb +5 -1
- data/lib/twb/workbook.rb +18 -5
- data/lib/twb/worksheet.rb +5 -1
- data/test/fieldAliases.rb +10 -0
- data/test/testFieldAliases.rb +65 -0
- data/test/testFieldDomainLoaded.rb +14 -0
- data/test/testFieldDomainLoader.rb +131 -0
- metadata +22 -1
@@ -0,0 +1,82 @@
|
|
1
|
+
# sheetfieldsanalyzer.rb Copyright (C) 2018 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 'twb'
|
17
|
+
require 'csv'
|
18
|
+
require 'set'
|
19
|
+
|
20
|
+
module Twb
|
21
|
+
module Analysis
|
22
|
+
|
23
|
+
class SheetFieldsAnalyzer
|
24
|
+
|
25
|
+
include TabTool
|
26
|
+
|
27
|
+
attr_accessor :localEmit
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@twbCnt = 0
|
31
|
+
@sheetCnt = 0
|
32
|
+
@filterCnt = 0
|
33
|
+
@funcdoc = {:class=>self.class, :blurb=>'Analyzing Sheet Fields from Tableau Workbooks.', :description=>nil,}
|
34
|
+
docFileName = docFile('TWBWorksheetFields.csv')
|
35
|
+
@sheetFieldsCSV = CSV.open(docFileName,'w')
|
36
|
+
@sheetFieldsCSV << ['Workbook','Worksheet','Data Source','Data Source (tech)','Field','Field (tech)']
|
37
|
+
@docfiles = [{:name=>docFileName,:desc=>"CSV File containing the data relating Workbooks,Worksheets, and the sheets' Data Sources and Fields"}]
|
38
|
+
end
|
39
|
+
|
40
|
+
def processTWB twb
|
41
|
+
@twb = twb
|
42
|
+
emit " -- #{@twb.name}"
|
43
|
+
@twbCnt += 1
|
44
|
+
@twbDomainsLoaded = false
|
45
|
+
parseSheets
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def parseSheets
|
50
|
+
@worksheets = @twb.worksheets
|
51
|
+
@worksheets.each do |sheet|
|
52
|
+
emit "SHEET: #{sheet.name}"
|
53
|
+
showFields sheet unless sheet.datasourceFields.nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def showFields sheet
|
58
|
+
fields = sheet.datasourceFields
|
59
|
+
emit " #FIELDS: #{fields.length}"
|
60
|
+
if fields.nil?
|
61
|
+
@sheetFieldsCSV << [@twb.name, sheet.name, nil, nil, nil, nil]
|
62
|
+
end
|
63
|
+
fields.each do |dsName, dsfields|
|
64
|
+
ds = @twb.datasource dsName
|
65
|
+
emit " ds: #{dsName}"
|
66
|
+
emit " - #{ds.uiname}"
|
67
|
+
emit " : #{ds.class}"
|
68
|
+
dsfields.each do |sheetField|
|
69
|
+
emit " f: #{sheetField}"
|
70
|
+
emit " c: #{sheetField.class}"
|
71
|
+
fuiName = ds.fieldUIName sheetField #Fields[sheetField]
|
72
|
+
@sheetFieldsCSV << [@twb.name, sheet.name, ds.uiname, dsName, sheetField.uiname, sheetField.name]
|
73
|
+
# emit true, " : #{dsFields[field].class}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
end #class SheetFieldsAnalyzer
|
80
|
+
|
81
|
+
end # module Analysis
|
82
|
+
end # module Twb
|
@@ -0,0 +1,214 @@
|
|
1
|
+
# WorksheetFields.rb Copyright (C) 2017, 2018 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 'twb'
|
17
|
+
require 'csv'
|
18
|
+
require 'set'
|
19
|
+
|
20
|
+
module Twb
|
21
|
+
module Analysis
|
22
|
+
|
23
|
+
class SheetFiltersAnalyzer
|
24
|
+
|
25
|
+
include TabTool
|
26
|
+
|
27
|
+
attr_accessor :localEmit
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@twbCnt = 0
|
31
|
+
@sheetCnt = 0
|
32
|
+
@filterCnt = 0
|
33
|
+
# @ttlogfile = File.new('./ttdoc/SheetFiltersAnalyzer.ttlog','w')
|
34
|
+
$sheetFieldsCSV = CSV.open(docFile('TWBWorksheetFilters.csv') ,'w')
|
35
|
+
$sheetFieldsCSV << ['Workbook','Worksheet','Filter Type','Data Source','Field','Value','Alias', 'Alias?']
|
36
|
+
end
|
37
|
+
|
38
|
+
def processTWB twb
|
39
|
+
@twb = twb
|
40
|
+
emit " -- #{@twb.name}"
|
41
|
+
@twbCnt += 1
|
42
|
+
@twbDomainsLoaded = false
|
43
|
+
parseFilters
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def parseFilters
|
48
|
+
@worksheets = @twb.worksheets
|
49
|
+
@worksheets.each do |sheet|
|
50
|
+
emit "\n\nSHEET: #{sheet.name}"
|
51
|
+
@sheetCnt += 1
|
52
|
+
filters = sheet.node.xpath('.//filter[@column]')
|
53
|
+
filters.each do |filter|
|
54
|
+
emit "\nFILTER:\n#{filter}\n====="
|
55
|
+
fieldCode = filter.attribute('column').text
|
56
|
+
codedField = Twb::CodedField.new(fieldCode)
|
57
|
+
fieldTech = codedField.name
|
58
|
+
srcTech = codedField.dataSource
|
59
|
+
dataSource = @twb.datasource(srcTech)
|
60
|
+
@dsAliases = dataSource.aliases
|
61
|
+
field = dataSource.fieldUIName fieldTech
|
62
|
+
emit "\n:: FIELD :: #{field} -- #{fieldTech} -- #{codedField.rawCode}"
|
63
|
+
filterValues = collectFilterValues sheet, dataSource, field, filter
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def collectFilterValues sheet, dataSource, field, node
|
69
|
+
emit "collectFilterValues"
|
70
|
+
emit " sheet: #{sheet.name}"
|
71
|
+
emit " ds: #{dataSource.uiname}"
|
72
|
+
emit " field: #{field}"
|
73
|
+
emit " node: #{node}"
|
74
|
+
emit " "
|
75
|
+
firstChild = node.at_xpath('./groupfilter')
|
76
|
+
results = {}
|
77
|
+
unless firstChild.nil?
|
78
|
+
function = firstChild.attribute('function').text
|
79
|
+
emit "function: #{function}"
|
80
|
+
emit node.to_s
|
81
|
+
#-- single element filter
|
82
|
+
if 'member'.eql? function
|
83
|
+
emit "HANDLING SINGLE MEMBER FILTER"
|
84
|
+
value = firstChild.attribute('member').text.gsub(/^"|"$/,'')
|
85
|
+
alia = dataSource.deAlias(field,value)
|
86
|
+
emit "value :%-25s => alias: %-s" % [value, alia]
|
87
|
+
$sheetFieldsCSV << [ @twb.name, sheet.name, 'single', dataSource.uiname, field, value, alia ]
|
88
|
+
return {value => alia}
|
89
|
+
end
|
90
|
+
#-- otherwise filter contains multiple elements
|
91
|
+
#-- handle individual member elements
|
92
|
+
elements = firstChild.xpath('.//groupfilter')
|
93
|
+
elements.each do |element|
|
94
|
+
function = element.attribute('function').text
|
95
|
+
emit "firstCHild\nfunction: #{function}\n node:\n#{element}"
|
96
|
+
if 'member'.eql? function
|
97
|
+
name = element.attribute('member').text.gsub(/^"|"$/,'')
|
98
|
+
emit "%%%% NAME:: #{name}"
|
99
|
+
alia = dataSource.fieldAlias(field,name) # $TableNameAliases[name]
|
100
|
+
results[name] = alia
|
101
|
+
end
|
102
|
+
if 'range'.eql? function
|
103
|
+
t = filtersFromRangeNode(dataSource, field, element)
|
104
|
+
t.each do |name,alia|
|
105
|
+
emit "RANGE ELEMENT Name: %-20s ALIAS: %-s " % [name, alia]
|
106
|
+
results[name] = dataSource.fieldAlias(field,name)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
results.each do |name,alia|
|
111
|
+
$sheetFieldsCSV << [ @twb.name, sheet.name, 'single', dataSource.uiname, field, name, alia, !name.eql?(alia) ]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
return results
|
115
|
+
end
|
116
|
+
|
117
|
+
def filtersFromRangeNode dataSource, field, node
|
118
|
+
unless @twbDomainsLoaded
|
119
|
+
loadDomains
|
120
|
+
end
|
121
|
+
emit "filtersFromRangeNode"
|
122
|
+
emit " from: #{node.attribute('from')}"
|
123
|
+
emit " to. : #{node.attribute('to')}"
|
124
|
+
from = node.attribute('from').text.gsub(/^"|"$/,'')
|
125
|
+
to = node.attribute( 'to').text.gsub(/^"|"$/,'')
|
126
|
+
emit " from: #{from}"
|
127
|
+
emit " to. : #{to}"
|
128
|
+
filtersInRange dataSource, field, from, to
|
129
|
+
end
|
130
|
+
|
131
|
+
def filtersInRange dataSource, field, from, to
|
132
|
+
emit "filtersInRange"
|
133
|
+
results = {}
|
134
|
+
dsFields = @twbFielddomains[dataSource.uiname]
|
135
|
+
emit "dsFields : #{dsFields}"
|
136
|
+
if dsFields.nil? || dsFields.empty?
|
137
|
+
emit "#### ALERT - FIELD DOMAIN VALUES FOR DATASOURCE #{dataSource.uiname} NOT LOADED ####"
|
138
|
+
emit @twbFieldDomains
|
139
|
+
emit "==========="
|
140
|
+
emit dsFields
|
141
|
+
emit "==========="
|
142
|
+
else
|
143
|
+
fieldVals = dsFields[field].to_a
|
144
|
+
if dataSource.fieldHasAliases field
|
145
|
+
#-- resolve aliases
|
146
|
+
dbValues = SortedSet.new
|
147
|
+
aliases = dataSource.fieldAliases field
|
148
|
+
fieldVals.each do |fv|
|
149
|
+
fvAliased = aliases.has_value? fv
|
150
|
+
if fvAliased
|
151
|
+
dbValues << aliases.key(fv)
|
152
|
+
else
|
153
|
+
dbValues << fv
|
154
|
+
end
|
155
|
+
end
|
156
|
+
$fieldVals = dbValues.to_a
|
157
|
+
else
|
158
|
+
#-- use domain values as returned
|
159
|
+
$fieldVals = dsFields[field].to_a
|
160
|
+
end
|
161
|
+
# domainVals = dsFields[field].to_a
|
162
|
+
# #-- need to dealias (unalias?) the field domain values
|
163
|
+
# dbVals = SortedSet.new
|
164
|
+
# domainVals.each do |dv|
|
165
|
+
emit "field values: #{$fieldVals}"
|
166
|
+
unless $fieldVals.nil? || $fieldVals.empty?
|
167
|
+
values = $fieldVals
|
168
|
+
f_i = values.index from
|
169
|
+
t_i = values.index to
|
170
|
+
emit " from: #{f_i} :: '#{from}'"
|
171
|
+
emit " to: #{t_i} :: '#{to}'"
|
172
|
+
badRange = f_i.nil? || t_i.nil?
|
173
|
+
emit "badRange: #{badRange}"
|
174
|
+
if badRange
|
175
|
+
emit "BAD RANGE"
|
176
|
+
else
|
177
|
+
tnames = values[f_i..t_i]
|
178
|
+
tnames.each do |tname|
|
179
|
+
results[tname] = dataSource.deAlias(field,tname) # $TableNameAliases[tname]
|
180
|
+
end
|
181
|
+
emit " filtersInRange f:%-35s t:%-s " % ["'#{from}:#{f_i}'", "'#{to}:#{t_i}'"]
|
182
|
+
emit " names: #{results.keys}"
|
183
|
+
emit " aliases: #{results.values}"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
return results
|
188
|
+
end
|
189
|
+
|
190
|
+
def loadDomains
|
191
|
+
emit "def loadDomains\n=="
|
192
|
+
loader = Twb::Util::FieldDomainLoader.new
|
193
|
+
@twbFielddomains = loader.loadWorkbook @twb
|
194
|
+
emit "#{@twbFielddomains}\n=="
|
195
|
+
@twbDomainsLoaded = true
|
196
|
+
end
|
197
|
+
|
198
|
+
# def emit(local=@localEmit, stuff)
|
199
|
+
# if stuff.is_a? String then
|
200
|
+
# lines = stuff.split(/\n/)
|
201
|
+
# lines.each do |line|
|
202
|
+
# @ttlogfile.puts "#{$emitPrefix}#{line}"
|
203
|
+
# puts "#{$emitPrefix}#{line}" if local
|
204
|
+
# end
|
205
|
+
# else
|
206
|
+
# @ttlogfile.puts "#{$emitPrefix}#{stuff}"
|
207
|
+
# puts "#{$emitPrefix}#{stuff}" if local
|
208
|
+
# end
|
209
|
+
# end
|
210
|
+
|
211
|
+
end # class
|
212
|
+
|
213
|
+
end # module Twb
|
214
|
+
end # module Analysis
|
data/lib/twb/calculatedfield.rb
CHANGED
@@ -14,15 +14,16 @@
|
|
14
14
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
15
|
|
16
16
|
require 'nokogiri'
|
17
|
+
require 'digest/md5'
|
17
18
|
|
18
19
|
module Twb
|
19
20
|
|
20
|
-
class CalculatedField
|
21
|
+
class CalculatedField < TabClass
|
21
22
|
|
22
23
|
attr_reader :dataSource
|
23
24
|
attr_reader :node, :properties
|
24
|
-
attr_reader :caption, :name, :uiname
|
25
|
-
attr_reader :datatype, :role, :
|
25
|
+
attr_reader :caption, :name, :uiname
|
26
|
+
attr_reader :datatype, :role, :propType
|
26
27
|
attr_reader :calculation, :calcFields
|
27
28
|
attr_reader :hidden
|
28
29
|
|
@@ -36,7 +37,7 @@ module Twb
|
|
36
37
|
# --
|
37
38
|
@datatype = @node.attribute('datatype').text
|
38
39
|
@role = @node.attribute('role').text
|
39
|
-
@
|
40
|
+
@propType = @node.attribute('type').text # n.b. 'type' is used as a proxy for class
|
40
41
|
# --
|
41
42
|
@calculation = Twb::FieldCalculation.new(self, datasource)
|
42
43
|
# --
|
@@ -47,7 +48,7 @@ module Twb
|
|
47
48
|
@properties ||= loadProperties
|
48
49
|
end
|
49
50
|
|
50
|
-
def
|
51
|
+
def calcFields
|
51
52
|
@calculation.calcFields
|
52
53
|
end
|
53
54
|
|
@@ -69,9 +70,23 @@ module Twb
|
|
69
70
|
@properties[name] = attr.value
|
70
71
|
end
|
71
72
|
@properties[:uiname] = @uiname
|
73
|
+
@properties[:uuid] = uuid
|
72
74
|
return @properties
|
73
75
|
end
|
74
76
|
|
77
|
+
def id
|
78
|
+
@id ||= @id = "#{@dataSource.uiname}::#{@uiname}"
|
79
|
+
end
|
80
|
+
|
81
|
+
# def uuid
|
82
|
+
# @uuid ||= loadUUID
|
83
|
+
# end
|
84
|
+
|
85
|
+
# def loadUUID
|
86
|
+
# dsn = @dataSource.nil? ? 'NO DATASOURCE' : dataSource.uuid
|
87
|
+
# @uuid = Digest::MD5.hexdigest("#{dsn}::@name").hash
|
88
|
+
# end
|
89
|
+
|
75
90
|
def to_s
|
76
91
|
"%s(%s) => %s" % [uiname, name, @calculation.formulaFlat]
|
77
92
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Copyright (C) 2014, 2015, 2017 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
|
+
require 'digest/md5'
|
18
|
+
require 'csv'
|
19
|
+
|
20
|
+
module Twb
|
21
|
+
|
22
|
+
class CodedField
|
23
|
+
|
24
|
+
include Comparable
|
25
|
+
|
26
|
+
attr_reader :dataSource
|
27
|
+
attr_reader :name, :techCode, :rawCode
|
28
|
+
|
29
|
+
def initialize code
|
30
|
+
#puts "\n\nCodedField :: #{code}"
|
31
|
+
@rawCode = code
|
32
|
+
trimmed = code.gsub(/^"|"$/,'').gsub(/^\[|\]$/,'')
|
33
|
+
parts = trimmed.split('].[')
|
34
|
+
#puts "Field: #{code} parts: #{parts.length} - #{parts.inspect}"
|
35
|
+
#puts " p1: #{parts[0]}"
|
36
|
+
#puts " p2: #{parts[1]}"
|
37
|
+
if parts.length == 1
|
38
|
+
#puts '==1'
|
39
|
+
@name = parts[0]
|
40
|
+
@techCode = "[#{@name}]"
|
41
|
+
else # parts.length <> 1
|
42
|
+
#puts '<>1'
|
43
|
+
#puts "p[0]: #{parts[0]}"
|
44
|
+
#puts "p[1]: #{parts[1]}"
|
45
|
+
@dataSource = parts[0]
|
46
|
+
fldName = parts[1]
|
47
|
+
if fldName.start_with?(':') && fldName.count(':') == 1
|
48
|
+
@name = fldName
|
49
|
+
else
|
50
|
+
parseField fldName
|
51
|
+
end
|
52
|
+
@techCode = "[#{@dataSource}].[#{@name}]"
|
53
|
+
end
|
54
|
+
end # initialize
|
55
|
+
|
56
|
+
def parseField str
|
57
|
+
# puts "parseField: #{str}"
|
58
|
+
parts = str.split(':')
|
59
|
+
# puts "parseField: #{str}"
|
60
|
+
# puts " parts : #{parts}"
|
61
|
+
# puts " partsl: #{parts.length}"
|
62
|
+
# puts " p[0]: #{parts[0]}"
|
63
|
+
# puts " p[1]: #{parts[1]}"
|
64
|
+
case parts.length
|
65
|
+
when 1
|
66
|
+
@name = parts[0]
|
67
|
+
else
|
68
|
+
@name = parts[1]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def id
|
73
|
+
@id ||= @id = "#{@dataSourceName}::#{@uiname}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def <=>(other)
|
77
|
+
# myName = @uiname.nil? ? '' : @uiname
|
78
|
+
# otherName = other.uiName.nil? ? "" : other.uiName
|
79
|
+
# ##puts "#{@uiname} / #{myName} <=> #{otherName} / #{other.uiName}"
|
80
|
+
# ##puts "#{@uiname.nil?} // #{other.uiName.nil?}"
|
81
|
+
# myName <=> otherName
|
82
|
+
@fqName <=> other.techCode
|
83
|
+
end
|
84
|
+
|
85
|
+
end # class CalculationField
|
86
|
+
|
87
|
+
end # module Twb
|
data/lib/twb/columnfield.rb
CHANGED
@@ -17,7 +17,7 @@ require 'nokogiri'
|
|
17
17
|
|
18
18
|
module Twb
|
19
19
|
|
20
|
-
class ColumnField
|
20
|
+
class ColumnField < TabClass
|
21
21
|
|
22
22
|
include Comparable
|
23
23
|
|
@@ -54,10 +54,10 @@ module Twb
|
|
54
54
|
attr_reader :alias, :semanticRole, :aggregation
|
55
55
|
attr_reader :autoColumn, :hidden, :datatypeCustomized
|
56
56
|
attr_reader :calcField
|
57
|
+
attr_reader :aliases
|
57
58
|
|
58
59
|
|
59
60
|
def initialize(fieldNode, datasource=nil)
|
60
|
-
|
61
61
|
@datasource = datasource
|
62
62
|
@node = fieldNode
|
63
63
|
@name = load 'name'
|
@@ -79,6 +79,10 @@ module Twb
|
|
79
79
|
@calcField = loadCalcField
|
80
80
|
end
|
81
81
|
|
82
|
+
def id
|
83
|
+
@id ||= @id = @name.hash
|
84
|
+
end
|
85
|
+
|
82
86
|
def load nodeName
|
83
87
|
attr = @node.attribute(nodeName)
|
84
88
|
val = attr.nil? ? nil : attr.text.strip.gsub(/^\[|\]$/,'')
|
@@ -135,6 +139,21 @@ module Twb
|
|
135
139
|
@commentLines.empty?
|
136
140
|
end
|
137
141
|
|
142
|
+
def aliases
|
143
|
+
@aliases ||= loadAliases
|
144
|
+
end
|
145
|
+
|
146
|
+
def loadAliases
|
147
|
+
@aliases = {}
|
148
|
+
aliasNodes = @node.xpath('.//alias')
|
149
|
+
aliasNodes.each do |node|
|
150
|
+
key = node.attribute('key').text.gsub(/^"|"$/,'')
|
151
|
+
value = node.attribute('value').text.gsub(/^"|"$/,'')
|
152
|
+
@aliases[key] = value
|
153
|
+
end
|
154
|
+
return @aliases
|
155
|
+
end
|
156
|
+
|
138
157
|
def properties
|
139
158
|
@properties ||= loadProperties
|
140
159
|
end
|