sp-excel-loader 0.3.40
Sign up to get free protection for your applications and to get access to all the features.
- 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
|