sp-excel-loader 0.3.40
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 +7 -0
- data/Gemfile +4 -0
- data/LICENSE +661 -0
- data/README.md +8 -0
- data/lib/sp-excel-loader.rb +6 -0
- data/lib/sp/excel/loader.rb +61 -0
- data/lib/sp/excel/loader/jrxml/band.rb +80 -0
- data/lib/sp/excel/loader/jrxml/band_container.rb +229 -0
- data/lib/sp/excel/loader/jrxml/box.rb +75 -0
- data/lib/sp/excel/loader/jrxml/casper_checkbox.rb +97 -0
- data/lib/sp/excel/loader/jrxml/casper_combo.rb +86 -0
- data/lib/sp/excel/loader/jrxml/casper_date.rb +54 -0
- data/lib/sp/excel/loader/jrxml/casper_radio_button.rb +48 -0
- data/lib/sp/excel/loader/jrxml/casper_text_field.rb +157 -0
- data/lib/sp/excel/loader/jrxml/client_combo_text_field.rb +72 -0
- data/lib/sp/excel/loader/jrxml/excel_to_jrxml.rb +1183 -0
- data/lib/sp/excel/loader/jrxml/extensions.rb +330 -0
- data/lib/sp/excel/loader/jrxml/field.rb +65 -0
- data/lib/sp/excel/loader/jrxml/group.rb +71 -0
- data/lib/sp/excel/loader/jrxml/image.rb +63 -0
- data/lib/sp/excel/loader/jrxml/jasper.rb +228 -0
- data/lib/sp/excel/loader/jrxml/parameter.rb +73 -0
- data/lib/sp/excel/loader/jrxml/pen.rb +97 -0
- data/lib/sp/excel/loader/jrxml/property.rb +52 -0
- data/lib/sp/excel/loader/jrxml/property_expression.rb +52 -0
- data/lib/sp/excel/loader/jrxml/report_element.rb +92 -0
- data/lib/sp/excel/loader/jrxml/static_text.rb +59 -0
- data/lib/sp/excel/loader/jrxml/style.rb +99 -0
- data/lib/sp/excel/loader/jrxml/text_field.rb +83 -0
- data/lib/sp/excel/loader/jrxml/variable.rb +77 -0
- data/lib/sp/excel/loader/json_to_xlsx.rb +159 -0
- data/lib/sp/excel/loader/model_exporter.rb +249 -0
- data/lib/sp/excel/loader/payrollexporter.rb +168 -0
- data/lib/sp/excel/loader/rubyxl_table_patch.rb +91 -0
- data/lib/sp/excel/loader/version.rb +26 -0
- data/lib/sp/excel/loader/workbookloader.rb +480 -0
- data/spec/calc_spec.rb +87 -0
- data/spec/model.xls +0 -0
- metadata +151 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright (c) 2011-2016 Cloudware S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
# This file is part of sp-excel-loader.
|
6
|
+
#
|
7
|
+
# sp-excel-loader is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Affero General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# sp-excel-loader is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Affero General Public License
|
18
|
+
# along with sp-excel-loader. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'rubyXL'
|
22
|
+
require 'rubyXL/objects/ooxml_object'
|
23
|
+
|
24
|
+
# Monkey patch to RubyXL
|
25
|
+
module RubyXL
|
26
|
+
|
27
|
+
class SortCondition < OOXMLObject
|
28
|
+
define_attribute(:ref , :string, :required => true)
|
29
|
+
define_element_name 'sortCondition'
|
30
|
+
end
|
31
|
+
|
32
|
+
class SortState < OOXMLObject
|
33
|
+
define_attribute(:ref , :string, :required => true)
|
34
|
+
define_element_name 'sortState'
|
35
|
+
define_child_node(RubyXL::SortCondition)
|
36
|
+
end
|
37
|
+
|
38
|
+
class TableColumn < OOXMLObject
|
39
|
+
define_attribute(:id , :int , :required => true)
|
40
|
+
define_attribute(:name , :string, :required => true)
|
41
|
+
define_attribute(:totalsRowShown, :int , :default => 0 )
|
42
|
+
define_element_name 'tableColumn'
|
43
|
+
end
|
44
|
+
|
45
|
+
class TableColumns < OOXMLContainerObject
|
46
|
+
define_attribute(:count, :int, :default => 0)
|
47
|
+
define_child_node(RubyXL::TableColumn, :collection => true)
|
48
|
+
define_element_name 'tableColumns'
|
49
|
+
end
|
50
|
+
|
51
|
+
class TableStyleInfo < OOXMLObject
|
52
|
+
define_attribute(:name , :string , :required => true)
|
53
|
+
define_attribute(:showColumnStripes , :string , :default => 0)
|
54
|
+
define_attribute(:showFirstColumn , :string , :default => 0)
|
55
|
+
define_attribute(:showLastColumn , :string , :default => 0)
|
56
|
+
define_attribute(:showRowStripes , :string , :default => 0)
|
57
|
+
define_element_name 'tableStyleInfo'
|
58
|
+
end
|
59
|
+
|
60
|
+
class Table < OOXMLTopLevelObject
|
61
|
+
CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml'
|
62
|
+
REL_TYPE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/table'
|
63
|
+
|
64
|
+
define_attribute(:id , :int , :required => true)
|
65
|
+
define_attribute(:name , :string, :required => true)
|
66
|
+
define_attribute(:ref , :string, :required => true)
|
67
|
+
define_attribute(:displayName , :string, :required => true)
|
68
|
+
define_child_node(RubyXL::AutoFilter)
|
69
|
+
define_child_node(RubyXL::SortState)
|
70
|
+
define_child_node(RubyXL::TableColumns)
|
71
|
+
define_child_node(RubyXL::TableStyleInfo)
|
72
|
+
define_element_name 'table'
|
73
|
+
set_namespaces('http://schemas.openxmlformats.org/spreadsheetml/2006/main' => '',
|
74
|
+
'http://schemas.openxmlformats.org/officeDocument/2006/relationships' => 'r')
|
75
|
+
|
76
|
+
def xlsx_path
|
77
|
+
ROOT.join('xl', 'tables', "table#{file_index}.xml")
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
class Tables < OOXMLContainerObject
|
83
|
+
define_child_node(RubyXL::Table, :collection => true)
|
84
|
+
define_element_name 'tables'
|
85
|
+
end
|
86
|
+
|
87
|
+
class Worksheet
|
88
|
+
define_relationship(RubyXL::Table)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright (c) 2011-2016 Cloudware S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
# This file is part of sp-excel-loader.
|
6
|
+
#
|
7
|
+
# sp-excel-loader is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Affero General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# sp-excel-loader is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Affero General Public License
|
18
|
+
# along with sp-excel-loader. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
module Sp
|
21
|
+
module Excel
|
22
|
+
module Loader
|
23
|
+
VERSION = File.read(File.expand_path('../../../../../VERSION', __FILE__)).strip
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,480 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright (c) 2011-2016 Cloudware S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
# This file is part of sp-excel-loader.
|
6
|
+
#
|
7
|
+
# sp-excel-loader is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Affero General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# sp-excel-loader is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Affero General Public License
|
18
|
+
# along with sp-excel-loader. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'bigdecimal'
|
22
|
+
require 'date'
|
23
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'rubyxl_table_patch'))
|
24
|
+
|
25
|
+
module Sp
|
26
|
+
module Excel
|
27
|
+
module Loader
|
28
|
+
|
29
|
+
class TableRow
|
30
|
+
def self.factory(klass_name = nil)
|
31
|
+
klass = Class.new(self)
|
32
|
+
Object.const_set(klass_name.capitalize, klass) unless klass_name.nil?
|
33
|
+
klass
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_attr(a_name, a_value)
|
37
|
+
a_name = a_name.tr(' ', '_')
|
38
|
+
self.class.send(:attr_accessor, a_name)
|
39
|
+
instance_variable_set("@#{a_name}", a_value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class WorkbookLoader < TableRow
|
44
|
+
|
45
|
+
attr_accessor :workbook
|
46
|
+
|
47
|
+
def initialize (a_file)
|
48
|
+
@workbook = RubyXL::Parser.parse(a_file)
|
49
|
+
@cellnames = Hash.new
|
50
|
+
@shared_formulas = Hash.new
|
51
|
+
@table_names = []
|
52
|
+
end
|
53
|
+
|
54
|
+
def read_all_tables ()
|
55
|
+
@workbook.worksheets.each do |ws|
|
56
|
+
ws.generic_storage.each do |tbl|
|
57
|
+
next unless tbl.is_a? RubyXL::Table
|
58
|
+
read_typed_table(ws, tbl, tbl.name)
|
59
|
+
@table_names << tbl.name
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def write_typed_table (a_records, a_table_name, a_style_filter = nil, a_keep_formulas = false)
|
65
|
+
ws, tbl, ref = find_table(a_table_name)
|
66
|
+
|
67
|
+
header_row = ref.row_range.begin()
|
68
|
+
dst_row = header_row + 1
|
69
|
+
type_row = header_row - 1
|
70
|
+
|
71
|
+
a_records.each do |record|
|
72
|
+
|
73
|
+
ref.col_range.each do |col|
|
74
|
+
|
75
|
+
datatype, value = type_n_value_toxls(ws[type_row][col].value, record.send(ws[header_row][col].value))
|
76
|
+
|
77
|
+
# Add or create cell
|
78
|
+
if ws[dst_row].nil? || ws[dst_row][col].nil?
|
79
|
+
ws.add_cell(dst_row, col, value)
|
80
|
+
else
|
81
|
+
if a_keep_formulas then
|
82
|
+
ws[dst_row][col].change_contents(value)
|
83
|
+
else
|
84
|
+
style_index = ws[dst_row][col].style_index
|
85
|
+
ws.delete_cell(dst_row, col)
|
86
|
+
ws.add_cell(dst_row, col, value)
|
87
|
+
ws[dst_row][col].style_index = style_index
|
88
|
+
end
|
89
|
+
end
|
90
|
+
# Only change datatype for number, other values make excel bark ...
|
91
|
+
if [RubyXL::DataType::NUMBER].include? datatype
|
92
|
+
ws[dst_row][col].datatype = datatype
|
93
|
+
end
|
94
|
+
|
95
|
+
# Call formater hook
|
96
|
+
unless a_style_filter.nil?
|
97
|
+
a_style_filter.call(record, ws, dst_row, col)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
dst_row += 1
|
102
|
+
end
|
103
|
+
|
104
|
+
# Adjust the table size
|
105
|
+
previous_last_row = ref.col_range.end()
|
106
|
+
tbl.ref = RubyXL::Reference.ind2ref(ref.row_range.begin(),
|
107
|
+
ref.col_range.begin()) + ":" +
|
108
|
+
RubyXL::Reference.ind2ref(ref.row_range.begin() + a_records.size(),
|
109
|
+
ref.col_range.end())
|
110
|
+
for row in dst_row..previous_last_row
|
111
|
+
ws.delete_row(row)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# @brief Convert the database value to the Excel cell type and value
|
117
|
+
#
|
118
|
+
# @param a_type The value in the types header row
|
119
|
+
# @param a_value Value from the database
|
120
|
+
#
|
121
|
+
# @return XLS type and value
|
122
|
+
#
|
123
|
+
def type_n_value_toxls (a_type, a_value)
|
124
|
+
|
125
|
+
if a_value.nil?
|
126
|
+
datatype = RubyXL::DataType::RAW_STRING
|
127
|
+
else
|
128
|
+
case a_type
|
129
|
+
when 'INTEGER', 'INTEGER_NULLABLE'
|
130
|
+
datatype = RubyXL::DataType::NUMBER
|
131
|
+
a_value = a_value.to_i
|
132
|
+
when 'MONEY', 'MONEY_NULLABLE', 'DECIMAL', 'DECIMAL_NULLABLE'
|
133
|
+
datatype = RubyXL::DataType::NUMBER
|
134
|
+
a_value = a_value.to_f
|
135
|
+
when 'TEXT', 'TEXT_NULLABLE'
|
136
|
+
datatype = RubyXL::DataType::RAW_STRING
|
137
|
+
when 'BOOLEAN', 'BOOLEAN_NULLABLE'
|
138
|
+
datatype = RubyXL::DataType::BOOLEAN
|
139
|
+
unless a_value.nil?
|
140
|
+
a_value = a_value ? 'TRUE' : 'FALSE'
|
141
|
+
end
|
142
|
+
when 'DATE', 'DATE_NULLABLE'
|
143
|
+
datatype = RubyXL::DataType::DATE
|
144
|
+
a_value = @workbook.date_to_num(Date.iso8601(a_value))
|
145
|
+
when 'DATETIME', 'DATETIME_NULLABLE'
|
146
|
+
datatype = RubyXL::DataType::DATE
|
147
|
+
a_value = @workbook.date_to_num(DateTime.parse(a_value))
|
148
|
+
else
|
149
|
+
datatype = RubyXL::DataType::RAW_STRING
|
150
|
+
end
|
151
|
+
end
|
152
|
+
return datatype, a_value
|
153
|
+
end
|
154
|
+
|
155
|
+
def read_typed_table (a_worksheet, a_table, a_table_name)
|
156
|
+
ref = RubyXL::Reference.new(a_table.ref)
|
157
|
+
header_row = ref.row_range.begin()
|
158
|
+
type_row = header_row - 1
|
159
|
+
records = Array.new
|
160
|
+
|
161
|
+
klass = TableRow.factory a_table_name
|
162
|
+
|
163
|
+
for row in ref.row_range.begin()+1..ref.row_range.end()
|
164
|
+
record = klass.new
|
165
|
+
|
166
|
+
ref.col_range.each do |col|
|
167
|
+
cell = a_worksheet[row][col] unless a_worksheet[row].nil?
|
168
|
+
unless cell.nil?
|
169
|
+
unless a_worksheet[type_row].nil? || a_worksheet[type_row][col].nil? || a_worksheet[type_row][col].value.nil?
|
170
|
+
type = a_worksheet[type_row][col].value
|
171
|
+
else
|
172
|
+
type = 'TEXT'
|
173
|
+
end
|
174
|
+
case type
|
175
|
+
when 'TEXT', 'TEXT_NULLABLE'
|
176
|
+
value = cell.value.to_s
|
177
|
+
when 'SQL', 'SQL_NULLABLE'
|
178
|
+
value = cell.value.to_s
|
179
|
+
when 'INTEGER', 'INTEGER_NULLABLE'
|
180
|
+
value = cell.value.to_i
|
181
|
+
when 'DECIMAL', 'MONEY', 'DECIMAL_NULLABLE', 'MONEY_NULLABLE'
|
182
|
+
value = BigDecimal.new(cell.value.to_s)
|
183
|
+
when 'BOOLEAN', 'BOOLEAN_NULLABLE'
|
184
|
+
value = cell.value.to_i == 0 ? false : true
|
185
|
+
when 'DATE', 'DATE_NULLABLE'
|
186
|
+
begin
|
187
|
+
if cell.is_date?
|
188
|
+
value = cell.value
|
189
|
+
else
|
190
|
+
value = Date.parse cell.value
|
191
|
+
end
|
192
|
+
rescue => e
|
193
|
+
if type == 'DATE_NULLABLE'
|
194
|
+
value = nil
|
195
|
+
else
|
196
|
+
puts "Error in #{a_worksheet.sheet_name}!#{RubyXL::Reference.ind2ref(row,col)} #{e.message}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
when 'DATETIME', 'DATETIME_NULLABLE'
|
200
|
+
begin
|
201
|
+
if cell.is_date?
|
202
|
+
value = cell.value
|
203
|
+
else
|
204
|
+
value = Date.parse cell.value
|
205
|
+
end
|
206
|
+
rescue => e
|
207
|
+
if type == 'DATETIME_NULLABLE'
|
208
|
+
value = nil
|
209
|
+
else
|
210
|
+
puts "Error in #{a_worksheet.sheet_name}!#{RubyXL::Reference.ind2ref(row,col)} #{e.message}"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
else
|
214
|
+
value = cell.value.to_s
|
215
|
+
end
|
216
|
+
else
|
217
|
+
value = nil
|
218
|
+
end
|
219
|
+
if value.kind_of?(String)
|
220
|
+
value.gsub!(/\A\u00A0+/, '')
|
221
|
+
value.gsub!(/\u00A0+\z/, '')
|
222
|
+
value.strip!
|
223
|
+
end
|
224
|
+
begin
|
225
|
+
record.add_attr(a_worksheet[header_row][col].value.strip, value)
|
226
|
+
rescue => e
|
227
|
+
puts "Error in #{a_worksheet.sheet_name}!#{RubyXL::Reference.ind2ref(header_row,col)} #{e.message}"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
records << record
|
231
|
+
end
|
232
|
+
add_attr(a_table_name, records)
|
233
|
+
end
|
234
|
+
|
235
|
+
def find_table (a_table_name)
|
236
|
+
@workbook.worksheets.each do |ws|
|
237
|
+
ws.generic_storage.each do |tbl|
|
238
|
+
next unless tbl.is_a? RubyXL::Table
|
239
|
+
next unless tbl.name == a_table_name
|
240
|
+
|
241
|
+
return ws, tbl, RubyXL::Reference.new(tbl.ref)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
raise "Table '#{a_table_name}' not found in the workbook"
|
245
|
+
end
|
246
|
+
|
247
|
+
def read_named_cells (a_sheet_name)
|
248
|
+
cellnames = Hash.new
|
249
|
+
ref_regexp = a_sheet_name + '!\$*([A-Z]+)\$*(\d+)'
|
250
|
+
@workbook.defined_names.each do |dn|
|
251
|
+
next unless dn.local_sheet_id.nil?
|
252
|
+
match = dn.reference.match(ref_regexp)
|
253
|
+
if match and match.size == 3
|
254
|
+
matched_name = match[1].to_s + match[2].to_s
|
255
|
+
if cellnames[matched_name]
|
256
|
+
raise "**** Fatal error:\n duplicate cellname for #{matched_name}: #{@cellnames[matched_name]} and #{dn.name}"
|
257
|
+
end
|
258
|
+
cellnames[dn.name] = matched_name
|
259
|
+
end
|
260
|
+
end
|
261
|
+
cellnames
|
262
|
+
end
|
263
|
+
|
264
|
+
####################################################################################################################
|
265
|
+
# #
|
266
|
+
# Methods that write XLS table into to the database #
|
267
|
+
# #
|
268
|
+
####################################################################################################################
|
269
|
+
|
270
|
+
def export_table_to_pg (a_conn, a_schema, a_prefix, a_table_name)
|
271
|
+
export_table_to_pg_with_othername(a_conn, a_schema, a_prefix, a_table_name, a_table_name)
|
272
|
+
end
|
273
|
+
|
274
|
+
def export_table_to_pg_with_othername (a_conn, a_schema, a_prefix, a_table_name, a_xls_table_name)
|
275
|
+
table = a_schema
|
276
|
+
table ||= 'public'
|
277
|
+
table += '.'
|
278
|
+
table += a_prefix unless a_prefix.nil?
|
279
|
+
table += a_table_name
|
280
|
+
export_table_to_pg_other(a_conn, table, a_xls_table_name)
|
281
|
+
end
|
282
|
+
|
283
|
+
def export_table_to_pg_other (a_conn, a_db_table_name, a_xls_table_name)
|
284
|
+
|
285
|
+
ws, tbl, ref = find_table(a_xls_table_name)
|
286
|
+
|
287
|
+
header_row = ref.row_range.begin()
|
288
|
+
dst_row = header_row + 1
|
289
|
+
type_row = header_row - 1
|
290
|
+
|
291
|
+
column_names = Array.new
|
292
|
+
column_type = Hash.new
|
293
|
+
columns = Array.new
|
294
|
+
ref.col_range.each do |col|
|
295
|
+
|
296
|
+
next if ws[type_row].nil?
|
297
|
+
next if ws[type_row][col].nil?
|
298
|
+
next if ws[type_row][col].value == 'VOID'
|
299
|
+
|
300
|
+
column_type[col] = ws[type_row][col].value
|
301
|
+
columns << col
|
302
|
+
column_names << ws[header_row][col].value
|
303
|
+
end
|
304
|
+
|
305
|
+
rows = Array.new
|
306
|
+
for row in ref.row_range.begin()+1..ref.row_range.end()
|
307
|
+
|
308
|
+
next if ws[row].nil?
|
309
|
+
row_values = Array.new
|
310
|
+
columns.each do |col|
|
311
|
+
|
312
|
+
cell = ws[row][col]
|
313
|
+
if cell.nil?
|
314
|
+
value = 'NULL'
|
315
|
+
elsif column_type[col] =~ /_NULLABLE$/ && 0 == cell.value.to_s.strip.size
|
316
|
+
value = 'NULL'
|
317
|
+
else
|
318
|
+
case column_type[col]
|
319
|
+
when 'TEXT', 'TEXT_NULLABLE'
|
320
|
+
value = '\'' + a_conn.escape_string(cell.value.to_s.strip) + '\''
|
321
|
+
when 'SQL', 'SQL_NULLABLE'
|
322
|
+
value = cell.value.to_s.strip
|
323
|
+
when 'INTEGER', 'INTEGER_NULLABLE'
|
324
|
+
value = cell.value.to_i
|
325
|
+
when 'DECIMAL', 'DECIMAL_NULLABLE', 'MONEY', 'MONEY_NULLABLE'
|
326
|
+
value = BigDecimal.new(cell.value.to_s.strip)
|
327
|
+
when 'BOOLEAN', 'BOOLEAN_NULLABLE'
|
328
|
+
value = cell.value.to_i == 0 ? 'false' : 'true'
|
329
|
+
when 'DATE', 'DATE_NULLABLE'
|
330
|
+
begin
|
331
|
+
value = '\'' + cell.value.strftime('%Y-%m-%d') + '\''
|
332
|
+
rescue => e
|
333
|
+
puts "Error in table #{a_xls_table_name} #{RubyXL::Reference.ind2ref(row,col)} #{e.message} value=#{cell.value.to_s}"
|
334
|
+
end
|
335
|
+
when 'DATETIME', 'DATETIME_NULLABLE'
|
336
|
+
begin
|
337
|
+
value = '\'' + cell.value.strftime('%Y-%m-%d %H:%M:%S') + '\''
|
338
|
+
rescue => e
|
339
|
+
puts "Error in table #{a_xls_table_name} #{RubyXL::Reference.ind2ref(row,col)} #{e.message} value=#{cell.value.to_s}"
|
340
|
+
end
|
341
|
+
else
|
342
|
+
value = '\'' + a_conn.escape_string(cell.value.to_s.strip) + '\''
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
row_values << value
|
347
|
+
end
|
348
|
+
rows << '(' + row_values.join(',') + ')'
|
349
|
+
end
|
350
|
+
a_conn.exec("INSERT INTO #{a_db_table_name} (#{column_names.join(',')}) VALUES #{rows.join(",\n")}")
|
351
|
+
end
|
352
|
+
|
353
|
+
def write (a_filename)
|
354
|
+
@workbook.calculation_chain = nil
|
355
|
+
@workbook.calc_pr.full_calc_on_load = true
|
356
|
+
@workbook.write(a_filename)
|
357
|
+
end
|
358
|
+
|
359
|
+
####################################################################################################################
|
360
|
+
# #
|
361
|
+
# Methods related to cloning of calculation tables that use templates #
|
362
|
+
# #
|
363
|
+
####################################################################################################################
|
364
|
+
|
365
|
+
|
366
|
+
#
|
367
|
+
# @brief This method takes a set of parameters read from the database and writes each parameter to excel cell on
|
368
|
+
# the specified sheet. The target cell must be named the column's name and have *no* formula
|
369
|
+
#
|
370
|
+
# @param a_sheet_name Name of the sheet where the replacements should be made
|
371
|
+
# @param a_scalars Hash with the values read from the database
|
372
|
+
#
|
373
|
+
def replace_scalars_in_sheet(a_sheet_name, a_scalars)
|
374
|
+
|
375
|
+
ws = @workbook[a_sheet_name]
|
376
|
+
cellnames = read_named_cells(a_sheet_name)
|
377
|
+
|
378
|
+
a_scalars.each do |name, value|
|
379
|
+
cell_id = cellnames[name]
|
380
|
+
unless cell_id.nil?
|
381
|
+
col, row = RubyXL::Reference.ref2ind(cell_id)
|
382
|
+
cell = ws[col][row]
|
383
|
+
if cell.formula.nil?
|
384
|
+
if cell.is_date?
|
385
|
+
if value.nil? or value.length == 0
|
386
|
+
value = nil
|
387
|
+
else
|
388
|
+
value = @workbook.date_to_num(DateTime.iso8601(value))
|
389
|
+
end
|
390
|
+
elsif (cell.datatype.nil? or cell.datatype == RubyXL::DataType::NUMBER)
|
391
|
+
begin
|
392
|
+
value = value.to_f
|
393
|
+
rescue
|
394
|
+
# Not a number? let it pass as it is
|
395
|
+
end
|
396
|
+
end
|
397
|
+
cell.change_contents(value)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def parse_shared_formulas (a_worksheet)
|
404
|
+
@shared_formulas = Hash.new
|
405
|
+
ref = a_worksheet.dimension.ref
|
406
|
+
ref.row_range.each do |row|
|
407
|
+
ref.col_range.each do |col|
|
408
|
+
next if a_worksheet[row].nil?
|
409
|
+
cell = a_worksheet[row][col]
|
410
|
+
next if cell.nil?
|
411
|
+
formula = cell.formula
|
412
|
+
next if formula.nil?
|
413
|
+
|
414
|
+
if formula.t == 'shared' and not formula.expression.nil? and formula.expression.length != 0
|
415
|
+
@shared_formulas[formula.si] = formula.expression
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
def read_formula_expression (a_cell)
|
422
|
+
return nil if (a_cell.formula.nil?)
|
423
|
+
return @shared_formulas[a_cell.formula.si] if a_cell.formula.t == 'shared'
|
424
|
+
return a_cell.formula.expression
|
425
|
+
end
|
426
|
+
|
427
|
+
def clone_lines_table (a_records, a_table_name, a_lines, a_template_column, a_closed_column = nil)
|
428
|
+
ws, tbl, ref = find_table(a_table_name)
|
429
|
+
|
430
|
+
header_row = ref.row_range.begin()
|
431
|
+
style_row = header_row + a_records.size
|
432
|
+
dst_row = style_row + 1
|
433
|
+
type_row = header_row - 1
|
434
|
+
|
435
|
+
template_index = Hash.new
|
436
|
+
a_records.each_with_index do |record, index|
|
437
|
+
template = record.send(a_template_column.to_sym)
|
438
|
+
template_index[template] = header_row + index + 1
|
439
|
+
end
|
440
|
+
|
441
|
+
parse_shared_formulas(ws)
|
442
|
+
|
443
|
+
a_lines.each_with_index do |line, index|
|
444
|
+
if template_index.has_key?(line[a_template_column]) == false
|
445
|
+
puts "Template #{line[a_template_column]} of line #{index+1} does exist in the model"
|
446
|
+
next
|
447
|
+
end
|
448
|
+
|
449
|
+
src_row = template_index[line[a_template_column]]
|
450
|
+
closed = line[a_closed_column] == 't'
|
451
|
+
|
452
|
+
ref.col_range.each do |col|
|
453
|
+
|
454
|
+
datatype, value = type_n_value_toxls(ws[type_row][col].value, line[ws[header_row][col].value])
|
455
|
+
|
456
|
+
# Copy formula if the line is open
|
457
|
+
expression = read_formula_expression(ws[src_row][col])
|
458
|
+
if closed == false and not expression.nil? and expression.length != 0
|
459
|
+
ws.add_cell(dst_row, col, '', expression)
|
460
|
+
else
|
461
|
+
ws.add_cell(dst_row, col, value)
|
462
|
+
end
|
463
|
+
ws[dst_row][col].style_index = ws[style_row][col].style_index
|
464
|
+
end
|
465
|
+
ws.change_row_height(dst_row, ws.get_row_height(src_row))
|
466
|
+
dst_row += 1
|
467
|
+
end
|
468
|
+
|
469
|
+
# Adjust the table size
|
470
|
+
previous_last_row = ref.col_range.end()
|
471
|
+
tbl.ref = RubyXL::Reference.ind2ref(ref.row_range.begin(),
|
472
|
+
ref.col_range.begin()) + ":" +
|
473
|
+
RubyXL::Reference.ind2ref(ref.row_range.begin() + a_records.size() + a_lines.ntuples,
|
474
|
+
ref.col_range.end())
|
475
|
+
end
|
476
|
+
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|