twb 1.0.5 → 1.9.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.
@@ -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