datashift 0.2.1 → 0.2.2

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.
Files changed (84) hide show
  1. data/.document +5 -5
  2. data/LICENSE.txt +26 -26
  3. data/README.markdown +326 -305
  4. data/README.rdoc +19 -19
  5. data/Rakefile +86 -93
  6. data/VERSION +1 -1
  7. data/datashift.gemspec +163 -152
  8. data/lib/applications/jruby/jexcel_file.rb +410 -408
  9. data/lib/applications/jruby/word.rb +79 -79
  10. data/lib/datashift.rb +183 -152
  11. data/lib/datashift/exceptions.rb +11 -11
  12. data/lib/datashift/file_definitions.rb +353 -353
  13. data/lib/datashift/mapping_file_definitions.rb +87 -87
  14. data/lib/datashift/method_detail.rb +293 -275
  15. data/lib/datashift/method_dictionary.rb +208 -209
  16. data/lib/datashift/method_mapper.rb +90 -90
  17. data/lib/datashift/model_mapper.rb +27 -0
  18. data/lib/exporters/csv_exporter.rb +36 -0
  19. data/lib/exporters/excel_exporter.rb +116 -0
  20. data/lib/exporters/exporter_base.rb +15 -0
  21. data/lib/generators/csv_generator.rb +36 -36
  22. data/lib/generators/excel_generator.rb +106 -122
  23. data/lib/generators/generator_base.rb +13 -13
  24. data/lib/helpers/core_ext/to_b.rb +24 -24
  25. data/lib/helpers/rake_utils.rb +42 -0
  26. data/lib/helpers/spree_helper.rb +194 -153
  27. data/lib/java/poi-3.7/LICENSE +507 -507
  28. data/lib/java/poi-3.7/NOTICE +21 -21
  29. data/lib/java/poi-3.7/RELEASE_NOTES.txt +115 -115
  30. data/lib/loaders/csv_loader.rb +98 -98
  31. data/lib/loaders/excel_loader.rb +155 -155
  32. data/lib/loaders/loader_base.rb +420 -420
  33. data/lib/loaders/spreadsheet_loader.rb +136 -136
  34. data/lib/loaders/spree/image_loader.rb +67 -63
  35. data/lib/loaders/spree/product_loader.rb +289 -248
  36. data/lib/thor/generate_excel.thor +54 -0
  37. data/sandbox/app/controllers/application_controller.rb +3 -0
  38. data/sandbox/config/application.rb +43 -0
  39. data/sandbox/config/database.yml +34 -0
  40. data/sandbox/config/environment.rb +7 -0
  41. data/sandbox/config/environments/development.rb +30 -0
  42. data/spec/csv_loader_spec.rb +30 -30
  43. data/spec/datashift_spec.rb +26 -26
  44. data/spec/db/migrate/20110803201325_create_test_bed.rb +85 -85
  45. data/spec/excel_exporter_spec.rb +78 -78
  46. data/spec/excel_generator_spec.rb +78 -78
  47. data/spec/excel_loader_spec.rb +223 -223
  48. data/spec/file_definitions.rb +141 -141
  49. data/spec/fixtures/ProjectsDefaults.yml +29 -29
  50. data/spec/fixtures/config/database.yml +27 -27
  51. data/spec/fixtures/datashift_Spree_db.sqlite +0 -0
  52. data/spec/fixtures/datashift_test_models_db.sqlite +0 -0
  53. data/spec/fixtures/negative/SpreeProdMiss1Mandatory.csv +4 -4
  54. data/spec/fixtures/negative/SpreeProdMissManyMandatory.csv +4 -4
  55. data/spec/fixtures/spree/SpreeProducts.csv +4 -4
  56. data/spec/fixtures/spree/SpreeProducts.xls +0 -0
  57. data/spec/fixtures/spree/SpreeProductsMultiColumn.csv +4 -4
  58. data/spec/fixtures/spree/SpreeProductsMultiColumn.xls +0 -0
  59. data/spec/fixtures/spree/SpreeProductsSimple.csv +4 -4
  60. data/spec/fixtures/spree/SpreeProductsWithImages.csv +4 -4
  61. data/spec/fixtures/spree/SpreeZoneExample.csv +5 -5
  62. data/spec/fixtures/test_model_defs.rb +57 -57
  63. data/spec/loader_spec.rb +120 -120
  64. data/spec/method_dictionary_spec.rb +242 -242
  65. data/spec/method_mapper_spec.rb +41 -41
  66. data/spec/spec_helper.rb +154 -116
  67. data/spec/spree_exporter_spec.rb +67 -0
  68. data/spec/spree_generator_spec.rb +77 -64
  69. data/spec/spree_loader_spec.rb +363 -324
  70. data/spec/spree_method_mapping_spec.rb +218 -214
  71. data/tasks/config/seed_fu_product_template.erb +15 -15
  72. data/tasks/config/tidy_config.txt +12 -12
  73. data/tasks/{excel_generator.rake → export/excel_generator.rake} +101 -78
  74. data/tasks/file_tasks.rake +36 -36
  75. data/tasks/import/csv.rake +50 -49
  76. data/tasks/import/excel.rake +74 -71
  77. data/tasks/spree/image_load.rake +108 -108
  78. data/tasks/spree/product_loader.rake +43 -43
  79. data/tasks/word_to_seedfu.rake +166 -166
  80. data/test/helper.rb +18 -18
  81. data/test/test_interact.rb +7 -7
  82. metadata +16 -8
  83. data/datashift-0.1.0.gem +0 -0
  84. data/tasks/db_tasks.rake +0 -66
@@ -1,409 +1,411 @@
1
- # Copyright:: (c) Autotelik Media Ltd 2011
2
- # Author :: Tom Statter
3
- # Date :: Aug 2010
4
- # License:: MIT
5
- #
6
- # An Excel file helper. Create and populate XSL files
7
- #
8
- # The maximum number of columns and rows in an Excel file is fixed at 256 Columns and 65536 Rows
9
- #
10
- # POI jar location needs to be added to class path.
11
- #
12
- # TODO - Check out http://poi.apache.org/poi-ruby.html
13
- #
14
- if(DataShift::Guards::jruby?)
15
-
16
- require "poi-3.7-20101029.jar"
17
-
18
- class JExcelFile
19
-
20
- java_import org.apache.poi.poifs.filesystem.POIFSFileSystem
21
-
22
- include_class 'org.apache.poi.hssf.usermodel.HSSFCell'
23
- include_class 'org.apache.poi.hssf.usermodel.HSSFWorkbook'
24
- include_class 'org.apache.poi.hssf.usermodel.HSSFCellStyle'
25
- include_class 'org.apache.poi.hssf.usermodel.HSSFDataFormat'
26
- include_class 'org.apache.poi.hssf.usermodel.HSSFClientAnchor'
27
- include_class 'org.apache.poi.hssf.usermodel.HSSFRichTextString'
28
-
29
- include_class 'java.io.ByteArrayOutputStream'
30
- include_class 'java.util.Date'
31
- include_class 'java.io.FileInputStream'
32
- include_class 'java.io.FileOutputStream'
33
-
34
- attr_accessor :book, :row, :date_style
35
- attr_reader :sheet
36
-
37
- MAX_COLUMNS = 256.freeze
38
-
39
- def self.date_format
40
- HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm")
41
- end
42
-
43
- # NOTE: this is the POI 3.7 HSSF maximum rows
44
- def self.maxrows
45
- return 65535
46
- end
47
-
48
- # The HSSFWorkbook uses 0 based indexes, whilst our companion jexcel_win32 class
49
- # uses 1 based indexes. So they can be used interchangeably we bring indexes
50
- # inline with JExcel usage in this class, as 1 based maps more intuitively for the user
51
- #
52
- # i.e Row 1 passed to this class, internally means Row 0
53
-
54
- def initialize()
55
- @book = nil
56
- # The @patriarchs hash is a workaround because HSSFSheet.getDrawingPatriarch()
57
- # causes a lot of issues (if it doesn't throw an exception!)
58
- @patriarchs = Hash.new
59
-
60
- @date_style = nil
61
- end
62
-
63
- def open(filename)
64
- inp = FileInputStream.new(filename)
65
-
66
- @book = HSSFWorkbook.new(inp)
67
-
68
- @date_style = @book.createCellStyle
69
- @date_style.setDataFormat( JExcelFile::date_format )
70
-
71
- @current_sheet = 0
72
- sheet(@current_sheet)
73
- end
74
-
75
- # EXCEL ITEMS
76
-
77
- def create(sheet_name)
78
- @book = HSSFWorkbook.new() if @book.nil?
79
-
80
- # Double check sheet doesn't already exist
81
- if(@book.getSheetIndex(sheet_name) < 0)
82
- sheet = @book.createSheet(sheet_name.gsub(" ", ''))
83
-
84
- @patriarchs.store(sheet_name, sheet.createDrawingPatriarch())
85
- end
86
- @current_sheet = @book.getSheetIndex(sheet_name)
87
-
88
- @date_style = @book.createCellStyle
89
- @date_style.setDataFormat( JExcelFile::date_format )
90
-
91
- self.sheet()
92
- end
93
-
94
- alias_method(:create_sheet, :create)
95
-
96
- # Return the current or specified HSSFSheet
97
- def sheet(i = nil)
98
- @current_sheet = i if i
99
- @sheet = @book.getSheetAt(@current_sheet)
100
- end
101
-
102
- def activate_sheet(sheet)
103
- active_sheet = @current_sheet
104
- if(@book)
105
- i = sheet if sheet.kind_of?(Integer)
106
- i = @book.getSheetIndex(sheet) if sheet.kind_of?(String)
107
-
108
- if( i >= 0 )
109
- @book.setActiveSheet(i) unless @book.nil?
110
- active_sheet = @book.getSheetAt(i)
111
- active_sheet.setActive(true)
112
- end unless i.nil?
113
- end
114
- return active_sheet
115
- end
116
-
117
- def num_rows
118
- @sheet.getPhysicalNumberOfRows
119
- end
120
-
121
- # Process each row. (type is org.apache.poi.hssf.usermodel.HSSFRow)
122
-
123
- def each_row
124
- @sheet.rowIterator.each { |row| yield row }
125
- end
126
-
127
- # Create new row, bring index in line with POI usage (our 1 is their 0)
128
- def create_row(index)
129
- return if @sheet.nil?
130
- raise "BAD INDEX: Row indexing starts at 1" if(index == 0)
131
- @row = @sheet.createRow(index - 1)
132
- @row
133
- end
134
-
135
- #############################
136
- # INSERTING DATA INTO EXCEL #
137
- #############################
138
-
139
- # Populate a single cell with data
140
- #
141
- def set_cell(row, column, datum)
142
- @row = @sheet.getRow(row - 1) || create_row(row)
143
- @row.createCell(column - 1, excel_cell_type(datum)).setCellValue(datum)
144
- end
145
-
146
- # Convert array into a header row
147
- def set_headers(headers)
148
- create_row(1)
149
- return if headers.empty?
150
-
151
- set_row(1, 1, headers)
152
- end
153
-
154
- # Populate a row of cells with data in an array
155
- # where the co-ordinates relate to row/column start position
156
- #
157
- def set_row( row, col, data, sheet_num = nil)
158
-
159
- sheet(sheet_num)
160
-
161
- create_row(row)
162
-
163
- column = col
164
- data.each do |datum|
165
- set_cell(row, column, datum)
166
- column += 1
167
- end
168
- end
169
-
170
- # Return a mapping from Ruby type to type for HSSFCell
171
- def excel_cell_type(data)
172
-
173
- if(data.kind_of?(Numeric))
174
- HSSFCell::CELL_TYPE_NUMERIC
175
- elsif(data.nil?)
176
- HSSFCell::CELL_TYPE_BLANK
177
- elsif(data.is_a?(TrueClass) || data.is_a?(FalseClass))
178
- HSSFCell::CELL_TYPE_BOOLEAN
179
- else
180
- HSSFCell::CELL_TYPE_STRING
181
- end
182
- # HSSFCell::CELL_TYPE_FORMULA
183
- end
184
-
185
- # TODO - Move into an ActiveRecord helper module of it's own
186
- def ar_to_headers( records )
187
- return if( !records.first.is_a?(ActiveRecord::Base) || records.empty?)
188
-
189
- headers = records.first.class.columns.collect( &:name )
190
- set_headers( headers )
191
- end
192
-
193
- # Pass a set of AR records
194
- def ar_to_xls(records, options = {})
195
- return if( ! records.first.is_a?(ActiveRecord::Base) || records.empty?)
196
-
197
- row_index =
198
- if(options[:no_headers])
199
- 1
200
- else
201
- ar_to_headers( records )
202
- 2
203
- end
204
-
205
- records.each do |record|
206
- create_row(row_index)
207
-
208
- ar_to_xls_row(1, record)
209
-
210
- row_index += 1
211
- end
212
- end
213
-
214
- # Save data from an AR record to the current row, based on the record's columns [c1,c2,c3]
215
- # Returns the number of the final column written to
216
- def ar_to_xls_row(start_column, record)
217
- return unless( record.is_a?(ActiveRecord::Base))
218
-
219
- column = start_column
220
- record.class.columns.each do |connection_column|
221
- ar_to_xls_cell(column, record, connection_column)
222
- column += 1
223
- end
224
- column
225
- end
226
-
227
- def ar_to_xls_cell(column, record, connection_column)
228
- begin
229
- datum = record.send(connection_column.name)
230
-
231
- if(connection_column.sql_type =~ /date/)
232
- @row.createCell(column - 1, HSSFCell::CELL_TYPE_STRING).setCellValue(datum.to_s)
233
-
234
- elsif(connection_column.type == :boolean || connection_column.sql_type =~ /tinyint/)
235
- @row.createCell(column - 1, HSSFCell::CELL_TYPE_BOOLEAN).setCellValue(datum)
236
-
237
- elsif(connection_column.sql_type =~ /int/)
238
- @row.createCell(column - 1, HSSFCell::CELL_TYPE_NUMERIC).setCellValue(datum.to_i)
239
- else
240
- @row.createCell(column - 1, HSSFCell::CELL_TYPE_STRING).setCellValue( datum.to_s )
241
- end
242
-
243
- rescue => e
244
- puts "Failed to export #{datum} from #{connection_column.inspect} to column #{column}"
245
- puts e
246
- end
247
- end
248
-
249
- ##############################
250
- # RETRIEVING DATA FROM EXCEL #
251
- ##############################
252
-
253
- # Return the raw data of the requested cell by row/column
254
- def get_cell_value(row, column)
255
- raise TypeError, "Expect row argument of type HSSFRow" unless row.is_a?(Java::OrgApachePoiHssfUsermodel::HSSFRow)
256
- cell_value( row.getCell(column) )
257
- end
258
-
259
- # Return the raw data of an HSSFCell
260
- def cell_value(cell)
261
- return unless cell
262
- #puts "DEBUG CELL TYPE : #{cell} => #{cell.getCellType().inspect}"
263
- case (cell.getCellType())
264
- when HSSFCell::CELL_TYPE_FORMULA then return cell.getCellFormula()
265
- when HSSFCell::CELL_TYPE_NUMERIC then return cell.getNumericCellValue()
266
- when HSSFCell::CELL_TYPE_STRING then return cell.getStringCellValue()
267
- when HSSFCell::CELL_TYPE_BOOLEAN then return cell.getBooleanCellValue()
268
- when HSSFCell::CELL_TYPE_BLANK then return ""
269
- end
270
- end
271
-
272
- def save( filename = nil )
273
- filename.nil? ? file = @filepath : file = filename
274
- out = FileOutputStream.new(file)
275
- @book.write(out) unless @book.nil?
276
- out.close
277
- end
278
-
279
- def save_to_text( filename )
280
- File.open( filename, 'w') {|f| f.write(to_s) }
281
- end
282
-
283
-
284
- def add_comment( cell, text )
285
- raise "Please supply valid HSSFCell" unless cell.respond_to?('setCellComment')
286
- return if @sheet.nil?
287
-
288
- patriarch = @patriarchs[@sheet.getSheetName()]
289
-
290
- anchor = HSSFClientAnchor.new(100, 50, 100, 50, cell.getColumnIndex(), cell.getRowIndex(), cell.getColumnIndex()+3, cell.getRowIndex()+4)
291
- comment = patriarch.createCellComment(anchor)
292
-
293
- comment_text = HSSFRichTextString.new(text)
294
- comment.setString(comment_text)
295
- comment.setAuthor("Mapping")
296
-
297
- cell.setCellComment(comment)
298
- end
299
-
300
- # The internal representation of a Excel File
301
-
302
- # Get a percentage style
303
- def getPercentStyle()
304
- if (@percentCellStyle.nil? && @book)
305
- @percentCellStyle = @book.createCellStyle();
306
- @percentCellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00%"));
307
- end
308
- return @percentCellStyle
309
- end
310
-
311
- # Auto size either the given column index or all columns
312
- def autosize(column = nil)
313
- return if @sheet.nil?
314
- if (column.kind_of? Integer)
315
- @sheet.autoSizeColumn(column)
316
- else
317
- @sheet.getRow(0).cellIterator.each{|c| @sheet.autoSizeColumn(c.getColumnIndex)}
318
- end
319
- end
320
-
321
- def to_s
322
- return "" unless @book
323
-
324
- outs = ByteArrayOutputStream.new
325
- @book.write(outs);
326
- outs.close();
327
- String.from_java_bytes(outs.toByteArray)
328
- end
329
-
330
- def createFreezePane(row=1, column=0)
331
- return if @sheet.nil?
332
- @sheet.createFreezePane(row, column)
333
- end
334
-
335
- # Use execute to run sql query provided
336
- # and write to a csv file (path required)
337
- # header row is optional but default is on
338
- # Auto mapping of specified columns is optional
339
- # @mappings is a hash{column => map} of columns to a map{old_value => new_value}
340
- def results_to_sheet( results, sheet, mappings=nil, header=true)
341
- numrows = results.length
342
- sheet_name = sheet
343
-
344
- if (numrows == 0)
345
- log :info, "WARNING - results are empty nothing written to sheet: #{sheet}"
346
- return
347
- end
348
-
349
- #Check if we need to split the results into seperate sheets
350
- if (numrows > @@maxrows )
351
- startrow = 0
352
- while (numrows > 0)
353
- # Split the results and write to a new sheet
354
- next_results = results.slice(startrow, @@maxrows > numrows ? numrows : @@maxrows)
355
- self.results_to_sheet(next_results, "#{sheet_name}", mappings, header) if next_results
356
-
357
- # Increase counters
358
- numrows -= next_results.length
359
- startrow += next_results.length
360
- sheet_name += 'I'
361
- end
362
- else
363
- # Create required sheet
364
- self.create(sheet)
365
-
366
- row_index = self.num_rows
367
- # write header line
368
- if (header && row_index==0 )
369
- header_row = @sheet.createRow(row_index)
370
- cell_index = 0
371
- results[0].keys.each{ |h|
372
- header_row.createCell(cell_index).setCellValue("#{h}")
373
- @sheet.setDefaultColumnStyle(cell_index, self.getPercentStyle) if "#{h}".include? '%'
374
- cell_index += 1
375
- }
376
- # Freeze the header row
377
- @sheet.createFreezePane( 0, 1, 0, 1 )
378
- row_index += 1
379
- end
380
-
381
- # write_results
382
- results.each{ |row|
383
- sheet_row = @sheet.createRow(row_index)
384
- cell_index = 0
385
- row.each{|k,v|
386
- celltype = v.kind_of?(Numeric) ? HSSFCell::CELL_TYPE_NUMERIC : HSSFCell::CELL_TYPE_STRING
387
- cell = sheet_row.createCell(cell_index, celltype)
388
-
389
- v.nil? ? value = "<NIL>" : value = v
390
-
391
- cell.setCellValue(value)
392
-
393
- cell_index +=1
394
- }
395
- #puts "#{sheet}: written row #{row_index}"
396
- row_index +=1
397
- }
398
- end
399
-
400
- end
401
-
402
- end # END JExcelFile
403
- else
404
- class JExcelFile
405
- def initialize
406
- raise DataShift::BadRuby, "Please install and use JRuby for working with .xls files"
407
- end
408
- end
1
+ # Copyright:: (c) Autotelik Media Ltd 2011
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2010
4
+ # License:: MIT
5
+ #
6
+ # An Excel file helper. Create and populate XSL files
7
+ #
8
+ # The maximum number of columns and rows in an Excel file is fixed at 256 Columns and 65536 Rows
9
+ #
10
+ # POI jar location needs to be added to class path.
11
+ #
12
+ # TODO - Check out http://poi.apache.org/poi-ruby.html
13
+ #
14
+ if(DataShift::Guards::jruby?)
15
+
16
+ require "poi-3.7-20101029.jar"
17
+
18
+ class JExcelFile
19
+
20
+ java_import org.apache.poi.poifs.filesystem.POIFSFileSystem
21
+
22
+ include_class 'org.apache.poi.hssf.usermodel.HSSFCell'
23
+ include_class 'org.apache.poi.hssf.usermodel.HSSFWorkbook'
24
+ include_class 'org.apache.poi.hssf.usermodel.HSSFCellStyle'
25
+ include_class 'org.apache.poi.hssf.usermodel.HSSFDataFormat'
26
+ include_class 'org.apache.poi.hssf.usermodel.HSSFClientAnchor'
27
+ include_class 'org.apache.poi.hssf.usermodel.HSSFRichTextString'
28
+
29
+ include_class 'java.io.ByteArrayOutputStream'
30
+ include_class 'java.util.Date'
31
+ include_class 'java.io.FileInputStream'
32
+ include_class 'java.io.FileOutputStream'
33
+
34
+ attr_accessor :book, :row, :date_style
35
+ attr_reader :sheet
36
+
37
+ MAX_COLUMNS = 256.freeze unless defined?(MAX_COLUMNS)
38
+
39
+ def self.date_format
40
+ HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm")
41
+ end
42
+
43
+ # NOTE: this is the POI 3.7 HSSF maximum rows
44
+ def self.maxrows
45
+ return 65535
46
+ end
47
+
48
+ # The HSSFWorkbook uses 0 based indexes, whilst our companion jexcel_win32 class
49
+ # uses 1 based indexes. So they can be used interchangeably we bring indexes
50
+ # inline with JExcel usage in this class, as 1 based maps more intuitively for the user
51
+ #
52
+ # i.e Row 1 passed to this class, internally means Row 0
53
+
54
+ def initialize()
55
+ @book = nil
56
+ # The @patriarchs hash is a workaround because HSSFSheet.getDrawingPatriarch()
57
+ # causes a lot of issues (if it doesn't throw an exception!)
58
+ @patriarchs = Hash.new
59
+
60
+ @date_style = nil
61
+ end
62
+
63
+ def open(filename)
64
+ inp = FileInputStream.new(filename)
65
+
66
+ @book = HSSFWorkbook.new(inp)
67
+
68
+ @date_style = @book.createCellStyle
69
+ @date_style.setDataFormat( JExcelFile::date_format )
70
+
71
+ @current_sheet = 0
72
+ sheet(@current_sheet)
73
+ end
74
+
75
+ # EXCEL ITEMS
76
+
77
+ def create(sheet_name)
78
+ @book = HSSFWorkbook.new() if @book.nil?
79
+
80
+ acceptable_name = sheet_name.gsub(':', '').gsub(" ", '')
81
+
82
+ # Double check sheet doesn't already exist
83
+ if(@book.getSheetIndex(acceptable_name) < 0)
84
+ sheet = @book.createSheet(acceptable_name.gsub(" ", ''))
85
+
86
+ @patriarchs.store(acceptable_name, sheet.createDrawingPatriarch())
87
+ end
88
+ @current_sheet = @book.getSheetIndex(acceptable_name)
89
+
90
+ @date_style = @book.createCellStyle
91
+ @date_style.setDataFormat( JExcelFile::date_format )
92
+
93
+ self.sheet()
94
+ end
95
+
96
+ alias_method(:create_sheet, :create)
97
+
98
+ # Return the current or specified HSSFSheet
99
+ def sheet(i = nil)
100
+ @current_sheet = i if i
101
+ @sheet = @book.getSheetAt(@current_sheet)
102
+ end
103
+
104
+ def activate_sheet(sheet)
105
+ active_sheet = @current_sheet
106
+ if(@book)
107
+ i = sheet if sheet.kind_of?(Integer)
108
+ i = @book.getSheetIndex(sheet) if sheet.kind_of?(String)
109
+
110
+ if( i >= 0 )
111
+ @book.setActiveSheet(i) unless @book.nil?
112
+ active_sheet = @book.getSheetAt(i)
113
+ active_sheet.setActive(true)
114
+ end unless i.nil?
115
+ end
116
+ return active_sheet
117
+ end
118
+
119
+ def num_rows
120
+ @sheet.getPhysicalNumberOfRows
121
+ end
122
+
123
+ # Process each row. (type is org.apache.poi.hssf.usermodel.HSSFRow)
124
+
125
+ def each_row
126
+ @sheet.rowIterator.each { |row| yield row }
127
+ end
128
+
129
+ # Create new row, bring index in line with POI usage (our 1 is their 0)
130
+ def create_row(index)
131
+ return if @sheet.nil?
132
+ raise "BAD INDEX: Row indexing starts at 1" if(index == 0)
133
+ @row = @sheet.createRow(index - 1)
134
+ @row
135
+ end
136
+
137
+ #############################
138
+ # INSERTING DATA INTO EXCEL #
139
+ #############################
140
+
141
+ # Populate a single cell with data
142
+ #
143
+ def set_cell(row, column, datum)
144
+ @row = @sheet.getRow(row - 1) || create_row(row)
145
+ @row.createCell(column - 1, excel_cell_type(datum)).setCellValue(datum)
146
+ end
147
+
148
+ # Convert array into a header row
149
+ def set_headers(headers)
150
+ create_row(1)
151
+ return if headers.empty?
152
+
153
+ set_row(1, 1, headers)
154
+ end
155
+
156
+ # Populate a row of cells with data in an array
157
+ # where the co-ordinates relate to row/column start position
158
+ #
159
+ def set_row( row, col, data, sheet_num = nil)
160
+
161
+ sheet(sheet_num)
162
+
163
+ create_row(row)
164
+
165
+ column = col
166
+ data.each do |datum|
167
+ set_cell(row, column, datum)
168
+ column += 1
169
+ end
170
+ end
171
+
172
+ # Return a mapping from Ruby type to type for HSSFCell
173
+ def excel_cell_type(data)
174
+
175
+ if(data.kind_of?(Numeric))
176
+ HSSFCell::CELL_TYPE_NUMERIC
177
+ elsif(data.nil?)
178
+ HSSFCell::CELL_TYPE_BLANK
179
+ elsif(data.is_a?(TrueClass) || data.is_a?(FalseClass))
180
+ HSSFCell::CELL_TYPE_BOOLEAN
181
+ else
182
+ HSSFCell::CELL_TYPE_STRING
183
+ end
184
+ # HSSFCell::CELL_TYPE_FORMULA
185
+ end
186
+
187
+ # TODO - Move into an ActiveRecord helper module of it's own
188
+ def ar_to_headers( records )
189
+ return if( !records.first.is_a?(ActiveRecord::Base) || records.empty?)
190
+
191
+ headers = records.first.class.columns.collect( &:name )
192
+ set_headers( headers )
193
+ end
194
+
195
+ # Pass a set of AR records
196
+ def ar_to_xls(records, options = {})
197
+ return if( ! records.first.is_a?(ActiveRecord::Base) || records.empty?)
198
+
199
+ row_index =
200
+ if(options[:no_headers])
201
+ 1
202
+ else
203
+ ar_to_headers( records )
204
+ 2
205
+ end
206
+
207
+ records.each do |record|
208
+ create_row(row_index)
209
+
210
+ ar_to_xls_row(1, record)
211
+
212
+ row_index += 1
213
+ end
214
+ end
215
+
216
+ # Save data from an AR record to the current row, based on the record's columns [c1,c2,c3]
217
+ # Returns the number of the final column written to
218
+ def ar_to_xls_row(start_column, record)
219
+ return unless( record.is_a?(ActiveRecord::Base))
220
+
221
+ column = start_column
222
+ record.class.columns.each do |connection_column|
223
+ ar_to_xls_cell(column, record, connection_column)
224
+ column += 1
225
+ end
226
+ column
227
+ end
228
+
229
+ def ar_to_xls_cell(column, record, connection_column)
230
+ begin
231
+ datum = record.send(connection_column.name)
232
+
233
+ if(connection_column.sql_type =~ /date/)
234
+ @row.createCell(column - 1, HSSFCell::CELL_TYPE_STRING).setCellValue(datum.to_s)
235
+
236
+ elsif(connection_column.type == :boolean || connection_column.sql_type =~ /tinyint/)
237
+ @row.createCell(column - 1, HSSFCell::CELL_TYPE_BOOLEAN).setCellValue(datum)
238
+
239
+ elsif(connection_column.sql_type =~ /int/)
240
+ @row.createCell(column - 1, HSSFCell::CELL_TYPE_NUMERIC).setCellValue(datum.to_i)
241
+ else
242
+ @row.createCell(column - 1, HSSFCell::CELL_TYPE_STRING).setCellValue( datum.to_s )
243
+ end
244
+
245
+ rescue => e
246
+ puts "Failed to export #{datum} from #{connection_column.inspect} to column #{column}"
247
+ puts e
248
+ end
249
+ end
250
+
251
+ ##############################
252
+ # RETRIEVING DATA FROM EXCEL #
253
+ ##############################
254
+
255
+ # Return the raw data of the requested cell by row/column
256
+ def get_cell_value(row, column)
257
+ raise TypeError, "Expect row argument of type HSSFRow" unless row.is_a?(Java::OrgApachePoiHssfUsermodel::HSSFRow)
258
+ cell_value( row.getCell(column) )
259
+ end
260
+
261
+ # Return the raw data of an HSSFCell
262
+ def cell_value(cell)
263
+ return unless cell
264
+ #puts "DEBUG CELL TYPE : #{cell} => #{cell.getCellType().inspect}"
265
+ case (cell.getCellType())
266
+ when HSSFCell::CELL_TYPE_FORMULA then return cell.getCellFormula()
267
+ when HSSFCell::CELL_TYPE_NUMERIC then return cell.getNumericCellValue()
268
+ when HSSFCell::CELL_TYPE_STRING then return cell.getStringCellValue()
269
+ when HSSFCell::CELL_TYPE_BOOLEAN then return cell.getBooleanCellValue()
270
+ when HSSFCell::CELL_TYPE_BLANK then return ""
271
+ end
272
+ end
273
+
274
+ def save( filename = nil )
275
+ filename.nil? ? file = @filepath : file = filename
276
+ out = FileOutputStream.new(file)
277
+ @book.write(out) unless @book.nil?
278
+ out.close
279
+ end
280
+
281
+ def save_to_text( filename )
282
+ File.open( filename, 'w') {|f| f.write(to_s) }
283
+ end
284
+
285
+
286
+ def add_comment( cell, text )
287
+ raise "Please supply valid HSSFCell" unless cell.respond_to?('setCellComment')
288
+ return if @sheet.nil?
289
+
290
+ patriarch = @patriarchs[@sheet.getSheetName()]
291
+
292
+ anchor = HSSFClientAnchor.new(100, 50, 100, 50, cell.getColumnIndex(), cell.getRowIndex(), cell.getColumnIndex()+3, cell.getRowIndex()+4)
293
+ comment = patriarch.createCellComment(anchor)
294
+
295
+ comment_text = HSSFRichTextString.new(text)
296
+ comment.setString(comment_text)
297
+ comment.setAuthor("Mapping")
298
+
299
+ cell.setCellComment(comment)
300
+ end
301
+
302
+ # The internal representation of a Excel File
303
+
304
+ # Get a percentage style
305
+ def getPercentStyle()
306
+ if (@percentCellStyle.nil? && @book)
307
+ @percentCellStyle = @book.createCellStyle();
308
+ @percentCellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00%"));
309
+ end
310
+ return @percentCellStyle
311
+ end
312
+
313
+ # Auto size either the given column index or all columns
314
+ def autosize(column = nil)
315
+ return if @sheet.nil?
316
+ if (column.kind_of? Integer)
317
+ @sheet.autoSizeColumn(column)
318
+ else
319
+ @sheet.getRow(0).cellIterator.each{|c| @sheet.autoSizeColumn(c.getColumnIndex)}
320
+ end
321
+ end
322
+
323
+ def to_s
324
+ return "" unless @book
325
+
326
+ outs = ByteArrayOutputStream.new
327
+ @book.write(outs);
328
+ outs.close();
329
+ String.from_java_bytes(outs.toByteArray)
330
+ end
331
+
332
+ def createFreezePane(row=1, column=0)
333
+ return if @sheet.nil?
334
+ @sheet.createFreezePane(row, column)
335
+ end
336
+
337
+ # Use execute to run sql query provided
338
+ # and write to a csv file (path required)
339
+ # header row is optional but default is on
340
+ # Auto mapping of specified columns is optional
341
+ # @mappings is a hash{column => map} of columns to a map{old_value => new_value}
342
+ def results_to_sheet( results, sheet, mappings=nil, header=true)
343
+ numrows = results.length
344
+ sheet_name = sheet
345
+
346
+ if (numrows == 0)
347
+ log :info, "WARNING - results are empty nothing written to sheet: #{sheet}"
348
+ return
349
+ end
350
+
351
+ #Check if we need to split the results into seperate sheets
352
+ if (numrows > @@maxrows )
353
+ startrow = 0
354
+ while (numrows > 0)
355
+ # Split the results and write to a new sheet
356
+ next_results = results.slice(startrow, @@maxrows > numrows ? numrows : @@maxrows)
357
+ self.results_to_sheet(next_results, "#{sheet_name}", mappings, header) if next_results
358
+
359
+ # Increase counters
360
+ numrows -= next_results.length
361
+ startrow += next_results.length
362
+ sheet_name += 'I'
363
+ end
364
+ else
365
+ # Create required sheet
366
+ self.create(sheet)
367
+
368
+ row_index = self.num_rows
369
+ # write header line
370
+ if (header && row_index==0 )
371
+ header_row = @sheet.createRow(row_index)
372
+ cell_index = 0
373
+ results[0].keys.each{ |h|
374
+ header_row.createCell(cell_index).setCellValue("#{h}")
375
+ @sheet.setDefaultColumnStyle(cell_index, self.getPercentStyle) if "#{h}".include? '%'
376
+ cell_index += 1
377
+ }
378
+ # Freeze the header row
379
+ @sheet.createFreezePane( 0, 1, 0, 1 )
380
+ row_index += 1
381
+ end
382
+
383
+ # write_results
384
+ results.each{ |row|
385
+ sheet_row = @sheet.createRow(row_index)
386
+ cell_index = 0
387
+ row.each{|k,v|
388
+ celltype = v.kind_of?(Numeric) ? HSSFCell::CELL_TYPE_NUMERIC : HSSFCell::CELL_TYPE_STRING
389
+ cell = sheet_row.createCell(cell_index, celltype)
390
+
391
+ v.nil? ? value = "<NIL>" : value = v
392
+
393
+ cell.setCellValue(value)
394
+
395
+ cell_index +=1
396
+ }
397
+ #puts "#{sheet}: written row #{row_index}"
398
+ row_index +=1
399
+ }
400
+ end
401
+
402
+ end
403
+
404
+ end # END JExcelFile
405
+ else
406
+ class JExcelFile
407
+ def initialize
408
+ raise DataShift::BadRuby, "Please install and use JRuby for working with .xls files"
409
+ end
410
+ end
409
411
  end