datashift 0.2.1 → 0.2.2

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