twb 1.9.1 → 2.2.1
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 +6 -2
- data/lib/twb/analysis/AnnotatedFieldsCSVEmitter.rb +182 -0
- data/lib/twb/analysis/CalculatedFields/MarkdownEmitter.rb +2 -1
- data/lib/twb/analysis/DataSources/DataSourceFieldsCSVEmitter.rb +250 -0
- data/lib/twb/analysis/DocumentedFieldsCSVEmitter.rb +208 -0
- data/lib/twb/analysis/DocumentedFieldsMarkdownEmitter.rb +77 -0
- data/lib/twb/calculatedfield.rb +36 -3
- data/lib/twb/columnfield.rb +70 -3
- data/lib/twb/datasource.rb +54 -108
- data/lib/twb/dbfield.rb +70 -0
- data/lib/twb/fieldcalculation.rb +9 -2
- data/lib/twb/localfield.rb +17 -23
- data/lib/twb/mappedfield.rb +62 -0
- data/lib/twb/metadatafield.rb +25 -6
- metadata +7 -2
- data/bin/twb.rb +0 -1
@@ -0,0 +1,208 @@
|
|
1
|
+
# DocumentedFieldsCSVEmitter.rb - this Ruby script Copyright 2017 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
|
+
require 'twb'
|
17
|
+
require 'csv'
|
18
|
+
|
19
|
+
module Twb
|
20
|
+
module Analysis
|
21
|
+
|
22
|
+
class DocumentedFieldsCSVEmitter
|
23
|
+
|
24
|
+
attr_reader :csvFileName, :csvRecords
|
25
|
+
attr_reader :dsCount, :fieldsCount
|
26
|
+
|
27
|
+
@@csvHeader = ['Record #',
|
28
|
+
'Workbook',
|
29
|
+
'Workbook Dir',
|
30
|
+
'Data Source',
|
31
|
+
'Field',
|
32
|
+
'Doc Type',
|
33
|
+
'Doc Line #'
|
34
|
+
'Doc Line'
|
35
|
+
]
|
36
|
+
@@csvFileType = 'TwbAnnotatedFields'
|
37
|
+
@@csvFileName = @@csvFileType + '.csv'
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
@csvFile = CSV.open(@@csvFileName,'w')
|
41
|
+
@csvFile << @@csvHeader
|
42
|
+
@csvRecords = Set.new
|
43
|
+
puts "Opened: #{!@csvFile.nil?} - #{@@csvFileName}"
|
44
|
+
# --
|
45
|
+
@recNum = 0
|
46
|
+
@dsCount = 0
|
47
|
+
@fieldsCount = 0
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.csvHeader
|
51
|
+
@@csvHeader
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.csvFileType
|
55
|
+
@@csvFileType
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.csvFileNaame
|
59
|
+
@@csvFileName
|
60
|
+
end
|
61
|
+
|
62
|
+
def processTwb twb
|
63
|
+
@twb = nil
|
64
|
+
@twb = twb if twb.instance_of? Twb::Workbook
|
65
|
+
@twb = Twb::Workbook.new(twb) if twb.instance_of? String
|
66
|
+
raise ArgumentError.new("ERROR in Workbok processing: '#{twb}' must be a Workbook (class) or the name of a Workbook (String), is a #{twb.class} \n ") unless @twb.is_a? Twb::Workbook
|
67
|
+
# --
|
68
|
+
dss = @twb.datasources
|
69
|
+
dss.each do |ds|
|
70
|
+
@dsname = ds.uiname
|
71
|
+
# puts "\n -- #{ds.uiname} "
|
72
|
+
# tables = Set.new
|
73
|
+
fields = {}
|
74
|
+
@dsCount += 1
|
75
|
+
fclasses = Set.new
|
76
|
+
ds.localFields.each do |field|
|
77
|
+
recordFieldFull field, :local
|
78
|
+
# recordField( fields, field, {:type=>:local,:columnField=>true,:hidden=>field.hidden} )
|
79
|
+
# emitTech( ds, field.uiname, 'Local', field.properties)
|
80
|
+
# recordTech( field, 'LocalA')
|
81
|
+
end
|
82
|
+
ds.columnFields.each do |field|
|
83
|
+
recordFieldFull field, :column
|
84
|
+
# recordField( fields, field, {:type=>'column',:columnField=>true,:hidden=>field.hidden} )
|
85
|
+
# emitTech( ds, field.uiname, 'Column', field.properties)
|
86
|
+
# recordTech( field, 'ColumnA')
|
87
|
+
end
|
88
|
+
ds.calculatedFields.each do |field|
|
89
|
+
recordFieldFull field, :calc
|
90
|
+
# puts "WWW #{field.class} :: #{field} ::prop:: #{field.properties.class} :: #{field.properties}"
|
91
|
+
# recordField( fields, field, {:type=>'calc',:calculatedField=>true,:hidden=>field.hidden} )
|
92
|
+
# emitTech( ds, field.uiname, 'Calculated', field.properties)
|
93
|
+
# recordTech( field, 'CalcA')
|
94
|
+
end
|
95
|
+
ds.metadataFields.each do |field|
|
96
|
+
recordFieldFull field, :metadata
|
97
|
+
# recordField( fields, field, {:type=>'metadata',:metadataField=>true} )
|
98
|
+
# emitTech( ds, field.uiname, 'MetaData', field.properties)
|
99
|
+
# recordTech( field, 'MetadataA')
|
100
|
+
end
|
101
|
+
ds.dbFields.each do |field|
|
102
|
+
recordFieldFull field, :db
|
103
|
+
# recordField( fields, field, {:type=>'database',:dbField=>true} )
|
104
|
+
# emitTech( ds, field.uiname, 'Db', field.properties)
|
105
|
+
# recordTech( field, 'DbA')
|
106
|
+
end
|
107
|
+
ds.mappedFields.each do |field|
|
108
|
+
recordFieldFull field, :mapped
|
109
|
+
# recordField( fields, field, {:type=>'mapped',:mappedField=>true} )
|
110
|
+
# emitTech( ds, field.uiname, 'Mapped', field.properties)
|
111
|
+
# recordTech( field, 'MappedA')
|
112
|
+
end
|
113
|
+
emitFields(fields)
|
114
|
+
end
|
115
|
+
end # def processTwb twb
|
116
|
+
|
117
|
+
def recordFieldFull field, source
|
118
|
+
# print field.properties.nil? ? '-' : ":#{field.properties.length}"
|
119
|
+
# puts field.properties
|
120
|
+
@recNum+=1
|
121
|
+
field.properties.each do |name,value|
|
122
|
+
# print name
|
123
|
+
@csvFileFull << [ @recNum,
|
124
|
+
@twb.name,
|
125
|
+
@twb.dir,
|
126
|
+
@dsname,
|
127
|
+
field.uiname,
|
128
|
+
source,
|
129
|
+
field.class,
|
130
|
+
field.node.path.to_s.gsub(/[0-9]/,'').gsub('[]',''),
|
131
|
+
name,
|
132
|
+
value
|
133
|
+
]
|
134
|
+
# @csvFileFull << csvRec
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def recordField fields, field, props
|
139
|
+
# puts "%-65s :: %s " % [fieldName,props]
|
140
|
+
return if field.uiname.nil?
|
141
|
+
if fields.has_key? field.uiname
|
142
|
+
fields[field.uiname].merge! field.properties
|
143
|
+
else
|
144
|
+
fields[field.uiname] = field.properties
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def emitFields fields
|
149
|
+
fields.each do |fieldName,props|
|
150
|
+
# puts "FIELD:: %-40s :: %s" % [fieldName,props.inspect]
|
151
|
+
# class = props[]
|
152
|
+
csvRec = [ @recNum+=1,
|
153
|
+
@twb.name,
|
154
|
+
@twb.dir,
|
155
|
+
@dsname,
|
156
|
+
fieldName,
|
157
|
+
props[:type],
|
158
|
+
props['hidden'],
|
159
|
+
props[:columnField],
|
160
|
+
props[:calculatedField],
|
161
|
+
props[:dbField],
|
162
|
+
props[:mappedField],
|
163
|
+
props[:metadataField]
|
164
|
+
]
|
165
|
+
@csvFile << csvRec
|
166
|
+
@csvRecords.add csvRec
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def recordTech field, type
|
171
|
+
@recNum+=1
|
172
|
+
field.properties.each do |name,value|
|
173
|
+
@csvFileTech << [ @recNum,
|
174
|
+
@twb.name,
|
175
|
+
@twb.dir,
|
176
|
+
@dsname,
|
177
|
+
field.uiname,
|
178
|
+
type,
|
179
|
+
name,
|
180
|
+
value
|
181
|
+
]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def emitTech dataSource, fieldName, type, properties
|
186
|
+
# puts "XX #{dataSource.uiname} :: #{fieldName} #{} :: #{type} :: #{properties}"
|
187
|
+
@recNum+=1
|
188
|
+
properties.each do |name,value|
|
189
|
+
@csvFileTech << [ @recNum,
|
190
|
+
@twb.name,
|
191
|
+
@twb.dir,
|
192
|
+
dataSource.uiname,
|
193
|
+
fieldName,
|
194
|
+
type,
|
195
|
+
name,
|
196
|
+
value
|
197
|
+
]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def cleanup
|
202
|
+
@csvFile.close unless @csvFile.nil?
|
203
|
+
end
|
204
|
+
|
205
|
+
end # class DocumentedFieldsCSVEmitter
|
206
|
+
|
207
|
+
end # module Analysis
|
208
|
+
end # module Twb
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# calculatedfieldsanalyzer.rb - this Ruby script Copyright 2017 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
|
+
require 'twb'
|
17
|
+
|
18
|
+
module Twb
|
19
|
+
module Analysis
|
20
|
+
|
21
|
+
class DocumentedFieldsMarkdownEmitter
|
22
|
+
|
23
|
+
attr_reader :docFileName
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
# puts ""
|
27
|
+
end
|
28
|
+
|
29
|
+
def processTwb twbName
|
30
|
+
@twb = Twb::Workbook.new twbName
|
31
|
+
@docFileName = @twb.name + '.DocumentedFields.md'
|
32
|
+
@docFile = File.open(@docFileName,'w')
|
33
|
+
@docFile.puts "## #{@twb.name}"
|
34
|
+
dss = @twb.datasources
|
35
|
+
@docFile.puts "#{dss.length} Data Source#{(dss.length>1) ? 's' : ''}"
|
36
|
+
@docFile.puts " "
|
37
|
+
dss.each do |ds|
|
38
|
+
@docFile.puts "- #{ds.uiname}"
|
39
|
+
end
|
40
|
+
@docFile.puts " "
|
41
|
+
dss.each do |ds|
|
42
|
+
@docFile.puts "### #{ds.uiname}"
|
43
|
+
columnFields = ds.columnFields #.sort_by { |fldName,calc| fldName }
|
44
|
+
columnFields.each do |field|
|
45
|
+
commentLines = field.comment
|
46
|
+
calculation = field.calcField
|
47
|
+
# puts "\n#{field.uiname}"
|
48
|
+
# puts "Comment? #{commentLines}"
|
49
|
+
# puts "Calc ? #{calculation}"
|
50
|
+
@docFile.puts "\n##### #{field.uiname} "
|
51
|
+
unless commentLines.nil?
|
52
|
+
commentLines.each do |line|
|
53
|
+
@docFile.puts "###### #{line}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
unless calculation.nil?
|
57
|
+
@docFile.puts "```"
|
58
|
+
@docFile.puts calculation.formulaResolved
|
59
|
+
@docFile.puts "\n -- Fields --" unless calculation.calcFields.empty?
|
60
|
+
refFields = SortedSet.new
|
61
|
+
calculation.calcFields.each do |cf|
|
62
|
+
fds = if cf.dataSourceRef == :remote then "<<#{cf.dataSource}>>" else '' end
|
63
|
+
refFields.add "#{cf.uiName} \t #{fds}"
|
64
|
+
end
|
65
|
+
refFields.each do |rf|
|
66
|
+
@docFile.puts " #{rf}"
|
67
|
+
end
|
68
|
+
@docFile.puts "```"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end # class MarkdownEmitter
|
75
|
+
|
76
|
+
end # module Analysis
|
77
|
+
end # module Twb
|
data/lib/twb/calculatedfield.rb
CHANGED
@@ -19,14 +19,16 @@ module Twb
|
|
19
19
|
|
20
20
|
class CalculatedField
|
21
21
|
|
22
|
-
attr_reader :
|
22
|
+
attr_reader :dataSource
|
23
|
+
attr_reader :node, :properties
|
23
24
|
attr_reader :caption, :name, :uiname
|
24
25
|
attr_reader :datatype, :role, :type
|
25
|
-
attr_reader :calculation
|
26
|
+
attr_reader :calculation, :calcFields
|
27
|
+
attr_reader :hidden
|
26
28
|
|
27
29
|
def initialize(calcNode, datasource=nil)
|
28
|
-
@dataSource = datasource
|
29
30
|
@node = calcNode
|
31
|
+
@dataSource = datasource
|
30
32
|
# --
|
31
33
|
@caption = calcNode.attribute('caption').text if calcNode.has_attribute?('caption')
|
32
34
|
@name = calcNode.attribute('name').text.gsub(/^\[/,'').gsub(/\]$/,'')
|
@@ -37,6 +39,37 @@ module Twb
|
|
37
39
|
@type = @node.attribute('type').text
|
38
40
|
# --
|
39
41
|
@calculation = Twb::FieldCalculation.new(self, datasource)
|
42
|
+
# --
|
43
|
+
@hidden = true if calcNode.has_attribute?('caption')
|
44
|
+
end
|
45
|
+
|
46
|
+
def properties
|
47
|
+
@properties ||= loadProperties
|
48
|
+
end
|
49
|
+
|
50
|
+
def calcLines
|
51
|
+
@calculation.calcFields
|
52
|
+
end
|
53
|
+
|
54
|
+
def formulaLines
|
55
|
+
@calculation.formulaLines
|
56
|
+
end
|
57
|
+
|
58
|
+
def formulaResolvedLines
|
59
|
+
@calculation.formulaResolvedLines
|
60
|
+
end
|
61
|
+
|
62
|
+
def formulaResolved
|
63
|
+
@calculation.formulaResolved
|
64
|
+
end
|
65
|
+
|
66
|
+
def loadProperties
|
67
|
+
@properties= {}
|
68
|
+
@node.attributes.each do |name,attr|
|
69
|
+
@properties[name] = attr.value
|
70
|
+
end
|
71
|
+
@properties[:uiname] = @uiname
|
72
|
+
return @properties
|
40
73
|
end
|
41
74
|
|
42
75
|
def to_s
|
data/lib/twb/columnfield.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014,
|
1
|
+
# Copyright (C) 2014, 2018 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
|
@@ -47,15 +47,18 @@ module Twb
|
|
47
47
|
# value
|
48
48
|
# visual-totals
|
49
49
|
|
50
|
-
attr_reader :node
|
50
|
+
attr_reader :node, :properties
|
51
51
|
attr_reader :name, :caption, :uiname
|
52
52
|
attr_reader :dataType, :defaultFormat, :paramDomainType
|
53
53
|
attr_reader :role, :type, :value
|
54
54
|
attr_reader :alias, :semanticRole, :aggregation
|
55
55
|
attr_reader :autoColumn, :hidden, :datatypeCustomized
|
56
|
+
attr_reader :calcField
|
56
57
|
|
57
58
|
|
58
|
-
def initialize
|
59
|
+
def initialize(fieldNode, datasource=nil)
|
60
|
+
|
61
|
+
@datasource = datasource
|
59
62
|
@node = fieldNode
|
60
63
|
@name = load 'name'
|
61
64
|
@caption = load 'caption'
|
@@ -73,6 +76,7 @@ module Twb
|
|
73
76
|
@autoColumn = load 'auto-column'
|
74
77
|
@hidden = load 'hidden'
|
75
78
|
@datatypeCustomized = load 'datatype-customized'
|
79
|
+
@calcField = loadCalcField
|
76
80
|
end
|
77
81
|
|
78
82
|
def load nodeName
|
@@ -81,6 +85,69 @@ module Twb
|
|
81
85
|
return val
|
82
86
|
end
|
83
87
|
|
88
|
+
def calcField
|
89
|
+
@calcField ||= loadCalcField
|
90
|
+
end
|
91
|
+
|
92
|
+
def loadCalcField
|
93
|
+
if !@node.at_xpath('./calculation').nil?
|
94
|
+
@calcField = Twb::CalculatedField.new @node, @datasource
|
95
|
+
else
|
96
|
+
@calcField = nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def comment
|
101
|
+
@commentLines ||= loadComment
|
102
|
+
end
|
103
|
+
|
104
|
+
# COMMENTED FIELDS
|
105
|
+
# --------------------------------------
|
106
|
+
# <column caption='Calculation1' datatype='real' name='[Calculation_821344020759588865]' role='measure' type='quantitative'>
|
107
|
+
# <calculation class='tableau' formula='[Formula Length] * 1.1' />
|
108
|
+
# <desc>
|
109
|
+
# <formatted-text>
|
110
|
+
# <run>THIS IS A COMMENT</run>
|
111
|
+
# </formatted-text>
|
112
|
+
# </desc>
|
113
|
+
# </column>
|
114
|
+
# <column datatype='string' name='[Data Source Name (tech) (CalculatedFieldsReferencedFields.csv)]' role='dimension' type='nominal'>
|
115
|
+
# <desc>
|
116
|
+
# <formatted-text>
|
117
|
+
# <run>THIS IS A COMMENT</run>
|
118
|
+
# </formatted-text>
|
119
|
+
# </desc>
|
120
|
+
# </column>
|
121
|
+
def loadComment
|
122
|
+
@commentLines = []
|
123
|
+
runs = @node.xpath('./desc/formatted-text/run')
|
124
|
+
runs.each do |run|
|
125
|
+
lines = run.text.split(/\n/)
|
126
|
+
lines.each do |line|
|
127
|
+
@commentLines << line
|
128
|
+
end
|
129
|
+
end
|
130
|
+
return @commentLines
|
131
|
+
end
|
132
|
+
|
133
|
+
def hasComment?
|
134
|
+
loadComment if @commentLines.nil?
|
135
|
+
@commentLines.empty?
|
136
|
+
end
|
137
|
+
|
138
|
+
def properties
|
139
|
+
@properties ||= loadProperties
|
140
|
+
end
|
141
|
+
|
142
|
+
def loadProperties
|
143
|
+
@properties= {}
|
144
|
+
@node.attributes.each do |name,attr|
|
145
|
+
@properties[name] = attr.value
|
146
|
+
end
|
147
|
+
@properties[:uiname] = @name
|
148
|
+
return @properties
|
149
|
+
end
|
150
|
+
|
84
151
|
def to_s
|
85
152
|
@name
|
86
153
|
end
|
data/lib/twb/datasource.rb
CHANGED
@@ -48,6 +48,7 @@ module Twb
|
|
48
48
|
attr_reader :metadataFields
|
49
49
|
attr_reader :dbFields
|
50
50
|
attr_reader :mappedFields
|
51
|
+
attr_reader :tableFieldsMap
|
51
52
|
attr_reader :fieldUINames
|
52
53
|
attr_reader :calculatedFields, :calculatedFieldNamesMap, :calculatedFieldNames, :calculatedField
|
53
54
|
attr_reader :allFields
|
@@ -59,7 +60,7 @@ module Twb
|
|
59
60
|
@workbook = workbook
|
60
61
|
@name = @node.attr('name')
|
61
62
|
@caption = @node.attr('caption')
|
62
|
-
@uiname =
|
63
|
+
@uiname = @caption.nil? ? @name : @caption
|
63
64
|
processConnection
|
64
65
|
processFilters
|
65
66
|
loadTableFields
|
@@ -150,7 +151,6 @@ module Twb
|
|
150
151
|
table = code.split('].[')[0][1..-1]
|
151
152
|
end
|
152
153
|
|
153
|
-
|
154
154
|
def Parameters?
|
155
155
|
'Parameters'.eql? @name
|
156
156
|
end
|
@@ -159,22 +159,30 @@ module Twb
|
|
159
159
|
@columnFields ||= loadColumnFields
|
160
160
|
end
|
161
161
|
|
162
|
+
def columnFieldsMap
|
163
|
+
loadColumnFields if @columnFieldsMap.nil?
|
164
|
+
return @columnFieldsMap
|
165
|
+
end
|
166
|
+
|
162
167
|
def loadColumnFields
|
163
|
-
@columnFields
|
168
|
+
@columnFields = Set.new
|
169
|
+
@columnFieldsMap = {}
|
164
170
|
nodes = @node.xpath('./column')
|
165
171
|
nodes.each do |n|
|
166
|
-
field = Twb::ColumnField.new n
|
172
|
+
field = Twb::ColumnField.new n, self
|
167
173
|
@columnFields << field
|
174
|
+
@columnFieldsMap[field.uiname] = field
|
168
175
|
end
|
169
176
|
return @columnFields
|
170
177
|
end
|
171
178
|
|
179
|
+
|
172
180
|
def localFields
|
173
181
|
@localFields ||= loadLocalFields
|
174
182
|
end
|
175
183
|
|
176
184
|
def loadLocalFields
|
177
|
-
@localFields =
|
185
|
+
@localFields = Set.new
|
178
186
|
unless @connection.nil? # Parameters has no connection node, & no local fields
|
179
187
|
connClass = @node.at_xpath('./connection').attribute('class').text
|
180
188
|
fxpath = case connClass
|
@@ -185,7 +193,7 @@ module Twb
|
|
185
193
|
nodes = @node.xpath(fxpath)
|
186
194
|
nodes.each do |node|
|
187
195
|
field = Twb::LocalField.new(node)
|
188
|
-
@localFields
|
196
|
+
@localFields << field
|
189
197
|
end
|
190
198
|
end
|
191
199
|
return @localFields
|
@@ -201,7 +209,7 @@ module Twb
|
|
201
209
|
# nodes = @node.xpath(".//metadata-record[@class='column']")
|
202
210
|
# # note: there are other nodes "<metadata-record class='capability'>" whose nature is unclear
|
203
211
|
# # these nodes have no value for their <name node, so are not loaded
|
204
|
-
nodes = @node.xpath(
|
212
|
+
nodes = @node.xpath("./connection//metadata-record[@class='column']")
|
205
213
|
nodes.each do |node|
|
206
214
|
field = Twb::MetadataField.new(node)
|
207
215
|
field.source = :db
|
@@ -234,34 +242,44 @@ module Twb
|
|
234
242
|
|
235
243
|
def loadFieldUINames
|
236
244
|
@fieldUINames = {}
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
245
|
+
metadataFields.each do |fld|
|
246
|
+
unless fld.localName.nil?
|
247
|
+
@fieldUINames[fld.localName] = fld.uiname
|
248
|
+
@fieldUINames[fld.uiname] = fld.uiname
|
249
|
+
end
|
250
|
+
end
|
251
|
+
calculatedFields.each do |fld|
|
252
|
+
@fieldUINames[fld.name] = fld.uiname
|
253
|
+
@fieldUINames[fld.uiname] = fld.uiname
|
244
254
|
end
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
@fieldUINames[dbName] = fname unless @fieldUINames.include?(dbName)
|
249
|
-
@fieldUINames[fname] = fname unless @fieldUINames.include?(fname)
|
255
|
+
localFields.each do |fld|
|
256
|
+
@fieldUINames[fld.name] = fld.uiname
|
257
|
+
@fieldUINames[fld.uiname] = fld.uiname
|
250
258
|
end
|
251
259
|
end
|
252
260
|
|
261
|
+
def tableFields
|
262
|
+
@tableFieldsMap.values
|
263
|
+
end
|
264
|
+
|
265
|
+
def dbFieldsMap
|
266
|
+
@tableFieldsMap
|
267
|
+
end
|
268
|
+
|
253
269
|
def dbFields
|
254
|
-
|
255
|
-
|
256
|
-
|
270
|
+
@tableFieldsMap.values
|
271
|
+
end
|
272
|
+
|
273
|
+
def mappedFieldsMap
|
274
|
+
loadTableFields if @tableFieldsMap.nil?
|
275
|
+
return @tableFieldsMap
|
257
276
|
end
|
258
277
|
|
259
278
|
def mappedFields
|
260
|
-
loadTableFields if @
|
261
|
-
return @
|
279
|
+
loadTableFields if @tableFieldsMap.nil?
|
280
|
+
return @tableFieldsMap.values
|
262
281
|
end
|
263
282
|
|
264
|
-
# NOTE: Calculated Fields are mapped by their UI name
|
265
283
|
def calculatedFieldsMap
|
266
284
|
@calculatedFieldsMap ||= loadCalculatedFields
|
267
285
|
end
|
@@ -296,48 +314,40 @@ module Twb
|
|
296
314
|
@allFields = SortedSet.new
|
297
315
|
dbf = dbFields
|
298
316
|
@allFields << dbf.keys
|
317
|
+
@allFields << calculatedFieldNames
|
299
318
|
end
|
300
319
|
|
301
320
|
def has_field? fieldName
|
302
|
-
|
321
|
+
dbFieldsMap.has_key? fieldName
|
303
322
|
end
|
304
323
|
|
305
324
|
def fieldTable fieldName
|
306
|
-
loadTableFields if @
|
307
|
-
|
308
|
-
return
|
325
|
+
loadTableFields if @tableFieldsMap.nil?
|
326
|
+
dbField = @tableFieldsMap[fieldName]
|
327
|
+
return dbField.nil? ? nil : dbField.dbtable
|
309
328
|
end
|
310
329
|
|
311
330
|
# fields are unique in the data source by UI name
|
312
331
|
def loadTableFields
|
313
332
|
# puts "DATA SOURCE FIELD TABLE LOAD"
|
314
|
-
@
|
333
|
+
@tableFieldsMap = {}
|
315
334
|
fieldNodes = @node.xpath('./connection/cols/map')
|
316
335
|
fieldNodes.each do |fn|
|
317
|
-
|
318
|
-
|
319
|
-
parts = fldRef.split('].[')
|
320
|
-
table = parts[0]
|
321
|
-
dbName = parts[1]
|
322
|
-
@mappedFields[uiName] = {'table' => table, 'dbName' => dbName}
|
323
|
-
# puts "=== #{uiName} :: T=#{@mappedFields[uiName]['table']} DBN=#{@mappedFields[uiName]['dbName']} "
|
336
|
+
dbField = Twb::DbField.new(@uiname, fn, :map)
|
337
|
+
@tableFieldsMap[dbField.uiname] = dbField
|
324
338
|
end
|
325
339
|
relTableNodes = @node.xpath('.//relation[@table]')
|
326
340
|
relTableNodes.each do |relNode|
|
327
341
|
table = relNode.attribute('name').text
|
328
|
-
cols
|
342
|
+
cols = relNode.xpath('./columns/column')
|
329
343
|
cols.each do |col|
|
344
|
+
dbField = Twb::DbField.new(@uiname, col, :tableColumn, table)
|
330
345
|
fldName = col.attribute('name')
|
331
|
-
@
|
346
|
+
@tableFieldsMap[dbField.uiname] = dbField
|
332
347
|
end
|
333
348
|
end
|
334
349
|
end
|
335
350
|
|
336
|
-
# =begin
|
337
|
-
# <filter class='categorical' column='[enforcement_type]' filter-group='2'>
|
338
|
-
# <groupfilter function='member' level='[enforcement_type]' member='"towing"' user:ui-domain='database' user:ui-enumeration='inclusive' user:ui-marker='enumerate' />
|
339
|
-
# </filter>
|
340
|
-
# end
|
341
351
|
def processFilters
|
342
352
|
if @filters.nil?
|
343
353
|
@filters = {}
|
@@ -360,70 +370,6 @@ module Twb
|
|
360
370
|
|
361
371
|
end # class DataSource
|
362
372
|
|
363
|
-
# DataSource Utilities
|
364
|
-
|
365
|
-
# # Generates and returns a set of graph node-edge-node triplets.
|
366
|
-
# # The intention is to create a graph that can be standalone used as a subgraph by the function's caller.
|
367
|
-
# # The initial implementation only considers a linear list of node names as input, resulting in
|
368
|
-
# # the creation of a single-path structure, e.g.
|
369
|
-
# # [inNode] -> node1 -> node2 -> ... -> nodeN
|
370
|
-
# # this may manifest as a tree when there are siblings at any level, e.g.
|
371
|
-
# # [inNode] -> node1 -> node11 -> ... -> nodeX
|
372
|
-
# # -> node22 -> ... -> nodeY
|
373
|
-
# # -> node2 -> node21 -> ... -> nodeZ
|
374
|
-
# # Params:
|
375
|
-
# # +inNodes+:: the node(s) to which this function's generated graph is to be linked, may be nil:
|
376
|
-
# # + , or multiple)
|
377
|
-
# # +nodesList+:: list of node types by name to be built and linked together, in the order named
|
378
|
-
# def graphNodes nodesList
|
379
|
-
# graph = Twb::Util::Graphedges.new()
|
380
|
-
# nodesList.each do |nName|
|
381
|
-
# end
|
382
|
-
# case @dsclass
|
383
|
-
# when 'federated'
|
384
|
-
# graph = processFederatedSource nodesList
|
385
|
-
# end
|
386
|
-
# return graph
|
387
|
-
# end
|
388
|
-
|
389
|
-
# def processFederatedSource nodesList
|
390
|
-
# emit false, " (federated) #{dataSource.uiname}"
|
391
|
-
# dsGNode = Twb::Util::Graphnode.new(name: @uiname, id: @name, type: 'Data Connection')
|
392
|
-
# graph = Twb::Util::Graphedges.new(dsGNode)
|
393
|
-
# edges = []
|
394
|
-
# dsNode = dataSource.node
|
395
|
-
# connections = dsNode.xpath('./connection/named-connections/named-connection/connection')
|
396
|
-
# connections.each do |conn|
|
397
|
-
# connClass = conn.attribute('class').text
|
398
|
-
# emit true, "CONN CLASS: #{connClass}"
|
399
|
-
# # -- Generating Source Node
|
400
|
-
# cgParams = @@cgNodeParams[connClass]
|
401
|
-
# # emit true, "cgparams : #{cgParams}"
|
402
|
-
# cLabel = buildConnGraphPart( conn, cgParams['label'])
|
403
|
-
# cID = buildConnGraphPart( conn, cgParams['id'])
|
404
|
-
# cType = cgParams['type']
|
405
|
-
# # emit true, " label : #{cLabel}"
|
406
|
-
# # emit true, " id : #{cID}"
|
407
|
-
# # emit true, " type : #{cType}"
|
408
|
-
# srcNode = Twb::Util::Graphnode.new(name: cLabel, id: cID, type: 'Data Source')
|
409
|
-
# graphEdge = Twb::Util::Graphedge.new(from: dsGNode, to: srcNode, relationship: 'is located at')
|
410
|
-
# graph.edges << graphEdge
|
411
|
-
# end
|
412
|
-
# return graph
|
413
|
-
# end
|
414
|
-
|
415
|
-
# def buildConnGraphPart connNode, attributes
|
416
|
-
# return connNode.attribute(attributes) if attributes.is_a? String
|
417
|
-
# emit false, "ATTRIBUTES :: #{attributes}"
|
418
|
-
# str = ''
|
419
|
-
# attributes.each do |attName|
|
420
|
-
# attrib = connNode.attribute(attName)
|
421
|
-
# emit false, " -#{attName}\t-> #{attrib}"
|
422
|
-
# str += attrib.text unless attrib.nil?
|
423
|
-
# end
|
424
|
-
# return str
|
425
|
-
# end
|
426
|
-
|
427
373
|
|
428
374
|
class JoinTablePair
|
429
375
|
attr_reader :from, :to
|