twb 1.0.5 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,71 @@
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
+ module CalculatedFields
21
+
22
+ class MarkdownEmitter
23
+
24
+ attr_reader :docFileName
25
+
26
+ def initialize
27
+ # puts ""
28
+ end
29
+
30
+ def processTwb twb
31
+ twb = File.basename(twb)
32
+ @twb = Twb::Workbook.new twb
33
+ @docFileName = twb + '.CalculatedFields.md'
34
+ @docFile = File.open(@docFileName,'w')
35
+ @docFile.puts "## #{twb}"
36
+ dsNames = @twb.datasourceUINames
37
+ @docFile.puts "#{dsNames.length} Data Sources"
38
+ dsNames.each do |dsn|
39
+ ds = @twb.datasource dsn
40
+ # puts " => #{ds.uiname} "
41
+ # end
42
+ # dss = @twb.datasources
43
+ # dss.each do |ds|
44
+ @docFile.puts "### #{ds.uiname}"
45
+ calcFields = ds.calculatedFieldsMap.sort_by { |fldName,calc| fldName }
46
+ calcFields.each do |fldname, field|
47
+ calculation = field.calculation
48
+ @docFile.puts "\n##### #{fldname} "
49
+ @docFile.puts "```"
50
+ if calculation.has_formula
51
+ @docFile.puts calculation.formulaResolved
52
+ @docFile.puts "\n -- Fields --" unless calculation.calcFields.empty?
53
+ refFields = SortedSet.new
54
+ calculation.calcFields.each do |cf|
55
+ fds = if cf.dataSourceRef == :remote then "<<#{cf.dataSource}>>" else '' end
56
+ refFields.add "#{cf.uiName} \t #{fds}"
57
+ end
58
+ refFields.each do |rf|
59
+ @docFile.puts " #{rf}"
60
+ end
61
+ @docFile.puts "```"
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ end # class MarkdownEmitter
68
+
69
+ end # nodule CalculatedFields
70
+ end # module Analysis
71
+ end # module Twb
@@ -0,0 +1,117 @@
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
+ module DataSources
21
+
22
+ class DataSourceTableFieldsCSVEmitter
23
+
24
+ attr_reader :csvFileName, :csvRecords
25
+ attr_reader :dsCount, :tablesCount, :fieldsCount
26
+
27
+ @@csvFileName = 'DataSourceTableFields.csv'
28
+ @csvFileDescription = 'Contains CSV records, each containing the details for an individual Field contained in a table in the Data Source. Data Sources are identified within their host Workbook. '
29
+
30
+ @@csvHeader = ['Record #',
31
+ 'Workbook',
32
+ 'Workbook Dir',
33
+ 'Data Source',
34
+ 'Data Source (tech)',
35
+ 'Table',
36
+ 'Field',
37
+ 'Field (code)',
38
+ 'Field (table)',
39
+ ]
40
+
41
+
42
+ def initialize
43
+ @csvFile = CSV.open(@@csvFileName, 'w')
44
+ @csvFile << @@csvHeader
45
+ @outputs = Set.new
46
+ @outputs << @@csvFileName
47
+ @dsCount = 0
48
+ @tablesCount = 0
49
+ @fieldsCount = 0
50
+ @csvRecords = Set.new
51
+ end
52
+
53
+ def doc
54
+ {
55
+ :filedoc => [
56
+ { :file => @@csvFileName,
57
+ :description => @csvFileDescription,
58
+ :header => @@csvHeader
59
+ }
60
+ ],
61
+ :outputs => @outputs
62
+ }
63
+ end
64
+
65
+ def self.csvHeader
66
+ @@csvHeader
67
+ end
68
+
69
+ def self.csvFileType
70
+ @@csvFileName
71
+ end
72
+
73
+ def outputs
74
+ @outputs
75
+ end
76
+
77
+ def processTwb twb
78
+ @recNum = 0
79
+ @twb = twb if twb.instance_of? Twb::Workbook
80
+ @twb = Twb::Workbook.new(twb) if twb.instance_of? String
81
+ # --
82
+ dss = @twb.datasources
83
+ dss.each do |ds|
84
+ tables = Set.new
85
+ @dsCount += 1
86
+ ds.node.xpath('./connection/cols/map').each do |cnode|
87
+ # puts cnode
88
+ key = cnode.attribute('key').text
89
+ codename = key.gsub(/^\[|\]$/,'')
90
+ fielduiname = ds.fieldUIName(codename)
91
+ value = cnode.attribute('value').text.gsub(/^\[|\]$/,'')
92
+ parts = value.split('].[')
93
+ # puts "%-30s %-45s %s \n " % [fielduiname, value, parts]
94
+ csvRecord = [ @fieldsCount += 1,
95
+ @twb.name,
96
+ @twb.dir,
97
+ ds.uiname,
98
+ ds.name,
99
+ parts[0],
100
+ fielduiname,
101
+ codename,
102
+ parts[1]
103
+ ]
104
+ tables.add parts[0]
105
+ @csvRecords.add csvRecord
106
+ @csvFile << csvRecord
107
+ end
108
+ @tablesCount += tables.length
109
+ end
110
+ end
111
+
112
+ end # class CSVEmitter
113
+
114
+
115
+ end # module DataSources
116
+ end # module Analysis
117
+ end # module Twb
@@ -0,0 +1,192 @@
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
+ require 'csv'
18
+
19
+ module Twb
20
+ module Analysis
21
+ module Sheets
22
+
23
+ class WorksheetDataStructureCSVEmitter
24
+
25
+ # attr_reader :csvFieldsFileName, :csvRecords
26
+ attr_reader :sheetNames, :sheetCount
27
+
28
+ @@csvSheetPanesFileType = 'WorksheetPanes'
29
+ @@csvSheetPanesHeader = ['Record #',
30
+ 'Workbook',
31
+ 'Workbook Dir',
32
+ 'Worksheet',
33
+ 'Pane #',
34
+ 'Pane #of',
35
+ 'Axis Type',
36
+ 'Axis Field Code',
37
+ 'Axis Field Data Source',
38
+ 'Axis Field Data Source (tech)',
39
+ 'Axis Field Prefix',
40
+ 'Axis Field Name',
41
+ 'Axis Field Suffix'
42
+ ]
43
+
44
+ @@csvSheetFieldsFileType = 'WorksheetFieldsStructure'
45
+ @@csvSheetFieldsHeader = ['Record #',
46
+ 'Workbook',
47
+ 'Workbook Dir',
48
+ 'Worksheet',
49
+ 'Pane #',
50
+ 'Data Source',
51
+ 'Data Source (tech)',
52
+ 'Function',
53
+ 'Field Code',
54
+ 'Field Name (tech)',
55
+ 'Field Prefix',
56
+ 'Field Suffix',
57
+ 'Position'
58
+ ]
59
+
60
+
61
+ def initialize
62
+ @csvPanesFileName = "Twb#{@@csvSheetPanesFileType}.csv"
63
+ @csvPanesFile = CSV.open(@csvPanesFileName,'w')
64
+ @csvPanesFile << @@csvSheetPanesHeader
65
+ # --
66
+ @csvFieldsFileName = "Twb#{@@csvSheetFieldsFileType}.csv"
67
+ @csvFieldsFile = CSV.open(@csvFieldsFileName,'w')
68
+ @csvFieldsFile << @@csvSheetFieldsHeader
69
+ # --
70
+ @twbsCnt = 0
71
+ @sheetsCnt = 0
72
+ @panesCnt = 0
73
+ @fieldsCnt = 0
74
+ end
75
+
76
+ # def self.csvHeader
77
+ # @@csvHeader
78
+ # end
79
+
80
+ # def self.csvFileType
81
+ # @@csvFieldsFileType
82
+ # end
83
+
84
+ def processTwb twb
85
+ @twb = case twb
86
+ when String then Twb::Workbook.new(twb)
87
+ when Twb::Workbook then twb
88
+ else raise ArgumentError.new("ERROR: #{twb} must be a String or Workbook, is a #{twb.class}")
89
+ end
90
+ # --
91
+ @sheetNames = SortedSet.new
92
+ # --
93
+ sheets = @twb.worksheets
94
+ sheets.each do |sheet|
95
+ @sheetNames << sheet.name
96
+ processPanes sheet
97
+ processRowsCols sheet
98
+ end
99
+ end
100
+
101
+ def processPanes sheet
102
+ pnodes = sheet.node.xpath('.//panes/pane')
103
+ panesCnt = pnodes.length
104
+ pnodes.each do |node|
105
+ id = node.has_attribute?('id') ? node.attribute('id').text : '0'
106
+ #--
107
+ if node.has_attribute?('x-axis-name')
108
+ axistype = 'x'
109
+ field = Twb::CodedField.new node.attribute('x-axis-name').text
110
+ elsif node.has_attribute?('y-axis-name')
111
+ axistype = 'y'
112
+ field = Twb::CodedField.new node.attribute('y-axis-name').text
113
+ else
114
+ axistype = nil
115
+ field = nil
116
+ end
117
+ #--
118
+ @csvPanesFile << [ @panesCnt+=1,
119
+ @twb.name, # 'Workbook',
120
+ @twb.dir, # 'Workbook Dir',
121
+ sheet.name, # 'Worksheet',
122
+ id, # 'Pane #',
123
+ panesCnt, # 'Pane #of',
124
+ axistype, # 'Axis Type',
125
+ field.nil? ? ' ' : field.code, # 'Axis Field Code',
126
+ field.nil? ? ' ' : field.dataSource, # 'Axis Field Code',
127
+ field.nil? ? ' ' : field.prefix, # 'Axis Field Prefix',
128
+ field.nil? ? ' ' : field.name, # 'Axis Field Name',
129
+ field.nil? ? ' ' : field.suffix, # 'Axis Field Suffix'
130
+ ]
131
+ processPaneFields sheet.name, node, id
132
+ end
133
+ end
134
+
135
+ def processPaneFields sheet, node, paneID
136
+ encodedFields = node.xpath('.//encodings/*')
137
+ # puts " flds : #{encodedFields.length} "
138
+ fieldPos = 0
139
+ encodedFields.each do |ef|
140
+ function = ef.name
141
+ field = Twb::CodedField.new(ef.attribute('column').text)
142
+ dsuiname = @twb.datasource(field.dataSource).uiname
143
+ # puts " ef : <<#{function}>> #{ef} -> #{field} "
144
+ @csvFieldsFile << [ @fieldsCnt+=1, # 'Record #',
145
+ @twb.name, # 'Workbook',
146
+ @twb.dir, # 'Workbook Dir',
147
+ sheet, # 'Worksheet',
148
+ paneID, # 'Pane #',
149
+ dsuiname, # 'Data Source',
150
+ field.dataSource, # 'Data Source (tech)',
151
+ function, # 'Function',
152
+ field.code, # 'Field Code',
153
+ field.name, # 'Field Name (tech)',
154
+ field.prefix, # 'Field Prefix',
155
+ field.suffix, # 'Field Suffix',
156
+ fieldPos+=1
157
+ ]
158
+ end
159
+
160
+ end
161
+
162
+ def processRowsCols sheet
163
+ emitRowColFields sheet, 'row', sheet.rowFields
164
+ emitRowColFields sheet, 'col', sheet.colFields
165
+ end
166
+
167
+ def emitRowColFields sheet, type, fields
168
+ fieldPos = 0
169
+ fields.each do |field|
170
+ dsuiname = @twb.datasource(field.dataSource).uiname
171
+ @csvFieldsFile << [ @fieldsCnt+=1, # 'Record #',
172
+ @twb.name, # 'Workbook',
173
+ @twb.dir, # 'Workbook Dir',
174
+ sheet.name, # 'Worksheet',
175
+ nil, # 'Pane #',
176
+ dsuiname, # 'Data Source',
177
+ field.dataSource, # 'Data Source (tech)',
178
+ type, # 'Function',
179
+ field.code, # 'Field Code',
180
+ field.name, # 'Field Name (tech)',
181
+ field.prefix, # 'Field Prefix',
182
+ field.suffix, # 'Field Suffix',
183
+ fieldPos+=1
184
+ ]
185
+ end
186
+ end
187
+
188
+ end # class WorksheetDataStructureCSVEmitter
189
+
190
+ end # module Sheets
191
+ end # module Analysis
192
+ end # module Twb
@@ -0,0 +1,47 @@
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
+
18
+ module Twb
19
+
20
+ class CalculatedField
21
+
22
+ attr_reader :node, :dataSource
23
+ attr_reader :caption, :name, :uiname
24
+ attr_reader :datatype, :role, :type
25
+ attr_reader :calculation
26
+
27
+ def initialize(calcNode, datasource=nil)
28
+ @dataSource = datasource
29
+ @node = calcNode
30
+ # --
31
+ @caption = calcNode.attribute('caption').text if calcNode.has_attribute?('caption')
32
+ @name = calcNode.attribute('name').text.gsub(/^\[/,'').gsub(/\]$/,'')
33
+ @uiname = caption.nil? ? @name : @caption
34
+ # --
35
+ @datatype = @node.attribute('datatype').text
36
+ @role = @node.attribute('role').text
37
+ @type = @node.attribute('type').text
38
+ # --
39
+ @calculation = Twb::FieldCalculation.new(self, datasource)
40
+ end
41
+
42
+ def to_s
43
+ "%s(%s) => %s" % [uiname, name, @calculation.formulaFlat]
44
+ end
45
+
46
+ end # class
47
+ end # module
@@ -0,0 +1,94 @@
1
+ # Copyright (C) 2014, 2015 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 ColumnField
21
+
22
+ include Comparable
23
+
24
+ # XML Attributes known to exist in TWB xml
25
+ # ----------------------------------------
26
+ # aggregation
27
+ # aggregation-param
28
+ # alias
29
+ # auto-column
30
+ # caption
31
+ # datatype
32
+ # datatype-customized
33
+ # default-format
34
+ # default-role
35
+ # default-type
36
+ # hidden
37
+ # layered
38
+ # name
39
+ # param-domain-type
40
+ # pivot
41
+ # role
42
+ # semantic-role
43
+ # SplitFieldIndex
44
+ # SplitFieldOrigin
45
+ # type
46
+ # user-datatype
47
+ # value
48
+ # visual-totals
49
+
50
+ attr_reader :node
51
+ attr_reader :name, :caption, :uiname
52
+ attr_reader :dataType, :defaultFormat, :paramDomainType
53
+ attr_reader :role, :type, :value
54
+ attr_reader :alias, :semanticRole, :aggregation
55
+ attr_reader :autoColumn, :hidden, :datatypeCustomized
56
+
57
+
58
+ def initialize fieldNode
59
+ @node = fieldNode
60
+ @name = load 'name'
61
+ @caption = load 'caption'
62
+ @uiname = @caption.nil? ? @name : @caption
63
+ @dataType = load 'datatype'
64
+ @userDataType = load 'user-datatype'
65
+ @defaultFormat = load 'default-format'
66
+ @paramDomainType = load 'param-domain-type'
67
+ @role = load 'role'
68
+ @type = load 'type'
69
+ @value = load 'value'
70
+ @alias = load 'alias'
71
+ @semanticRole = load 'semantic-role'
72
+ @aggregation = load 'aggregation'
73
+ @autoColumn = load 'auto-column'
74
+ @hidden = load 'hidden'
75
+ @datatypeCustomized = load 'datatype-customized'
76
+ end
77
+
78
+ def load nodeName
79
+ attr = @node.attribute(nodeName)
80
+ val = attr.nil? ? nil : attr.text.strip.gsub(/^\[|\]$/,'')
81
+ return val
82
+ end
83
+
84
+ def to_s
85
+ @name
86
+ end
87
+
88
+ def <=>(other)
89
+ @name <=> other.name
90
+ end
91
+
92
+ end # class ColumnField
93
+
94
+ end