datashift 0.9.0 → 0.10.0
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.
- data/README.markdown +63 -64
- data/Rakefile +4 -7
- data/VERSION +1 -1
- data/datashift.gemspec +92 -62
- data/lib/applications/apache_poi_extensions.rb +62 -0
- data/lib/applications/excel.rb +78 -0
- data/lib/applications/excel_base.rb +65 -0
- data/lib/applications/jexcel_file.rb +222 -0
- data/lib/applications/jexcel_file_extensions.rb +244 -0
- data/lib/applications/jruby/{jexcel_file.rb → old_pre_proxy_jexcel_file.rb} +0 -0
- data/lib/applications/ruby_poi_translations.rb +64 -0
- data/lib/applications/spreadsheet_extensions.rb +31 -0
- data/lib/datashift/method_details_manager.rb +4 -0
- data/lib/exporters/csv_exporter.rb +3 -1
- data/lib/exporters/excel_exporter.rb +59 -74
- data/lib/generators/excel_generator.rb +70 -74
- data/lib/guards.rb +57 -0
- data/lib/loaders/excel_loader.rb +105 -105
- data/lib/loaders/loader_base.rb +43 -21
- data/lib/loaders/paperclip/attachment_loader.rb +104 -0
- data/lib/loaders/paperclip/datashift_paperclip.rb +78 -0
- data/lib/loaders/paperclip/{image_loader.rb → image_loading.rb} +2 -18
- data/lib/thor/{generate_excel.thor → generate.thor} +48 -0
- data/lib/thor/paperclip.thor +85 -0
- data/lib/thor/tools.thor +23 -2
- data/spec/Gemfile +1 -7
- data/spec/csv_exporter_spec.rb +4 -4
- data/spec/csv_loader_spec.rb +1 -1
- data/spec/excel_exporter_spec.rb +43 -45
- data/spec/excel_generator_spec.rb +132 -60
- data/spec/excel_loader_spec.rb +134 -140
- data/spec/excel_spec.rb +179 -0
- data/spec/fixtures/ProjectsMultiCategoriesHeaderLookup.xls +0 -0
- data/spec/fixtures/config/database.yml +2 -2
- data/spec/fixtures/db/datashift_test_models_db.sqlite +0 -0
- data/spec/{db → fixtures/db}/migrate/20110803201325_create_test_bed.rb +0 -0
- data/spec/fixtures/load_datashift.thor +3 -0
- data/spec/fixtures/models/category.rb +7 -0
- data/spec/fixtures/models/empty.rb +2 -0
- data/spec/fixtures/models/loader_release.rb +10 -0
- data/spec/fixtures/models/long_and_complex_table_linked_to_version.rb +6 -0
- data/spec/fixtures/models/milestone.rb +8 -0
- data/spec/fixtures/models/owner.rb +5 -0
- data/spec/fixtures/models/project.rb +26 -0
- data/spec/fixtures/models/test_model_defs.rb +67 -0
- data/spec/fixtures/models/version.rb +7 -0
- data/spec/loader_spec.rb +4 -3
- data/spec/method_dictionary_spec.rb +7 -6
- data/spec/method_mapper_spec.rb +3 -2
- data/spec/rails_sandbox/.gitignore +15 -0
- data/spec/rails_sandbox/Gemfile +40 -0
- data/spec/rails_sandbox/README.rdoc +261 -0
- data/spec/rails_sandbox/Rakefile +7 -0
- data/spec/rails_sandbox/app/assets/images/rails.png +0 -0
- data/spec/rails_sandbox/app/assets/javascripts/application.js +15 -0
- data/spec/rails_sandbox/app/assets/stylesheets/application.css +13 -0
- data/spec/rails_sandbox/app/controllers/application_controller.rb +3 -0
- data/spec/rails_sandbox/app/helpers/application_helper.rb +2 -0
- data/spec/rails_sandbox/app/mailers/.gitkeep +0 -0
- data/spec/rails_sandbox/app/models/.gitkeep +0 -0
- data/spec/rails_sandbox/app/models/category.rb +7 -0
- data/spec/rails_sandbox/app/models/empty.rb +2 -0
- data/spec/rails_sandbox/app/models/loader_release.rb +10 -0
- data/spec/rails_sandbox/app/models/long_and_complex_table_linked_to_version.rb +6 -0
- data/spec/rails_sandbox/app/models/milestone.rb +8 -0
- data/spec/rails_sandbox/app/models/owner.rb +5 -0
- data/spec/rails_sandbox/app/models/project.rb +26 -0
- data/spec/rails_sandbox/app/models/test_model_defs.rb +67 -0
- data/spec/rails_sandbox/app/models/version.rb +7 -0
- data/spec/rails_sandbox/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_sandbox/config.ru +4 -0
- data/spec/rails_sandbox/config/application.rb +62 -0
- data/spec/rails_sandbox/config/boot.rb +6 -0
- data/spec/rails_sandbox/config/database.yml +20 -0
- data/spec/rails_sandbox/config/environment.rb +5 -0
- data/spec/rails_sandbox/config/environments/development.rb +37 -0
- data/spec/rails_sandbox/config/environments/production.rb +67 -0
- data/spec/rails_sandbox/config/environments/test.rb +37 -0
- data/spec/rails_sandbox/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_sandbox/config/initializers/inflections.rb +15 -0
- data/spec/rails_sandbox/config/initializers/mime_types.rb +5 -0
- data/spec/rails_sandbox/config/initializers/secret_token.rb +7 -0
- data/spec/rails_sandbox/config/initializers/session_store.rb +8 -0
- data/spec/rails_sandbox/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_sandbox/config/locales/en.yml +5 -0
- data/spec/rails_sandbox/config/routes.rb +58 -0
- data/spec/rails_sandbox/db/migrate/20110803201325_create_test_bed.rb +96 -0
- data/spec/rails_sandbox/db/schema.rb +81 -0
- data/spec/rails_sandbox/db/seeds.rb +7 -0
- data/spec/rails_sandbox/lib/assets/.gitkeep +0 -0
- data/spec/rails_sandbox/lib/tasks/.gitkeep +0 -0
- data/spec/rails_sandbox/log/.gitkeep +0 -0
- data/spec/rails_sandbox/public/404.html +26 -0
- data/spec/rails_sandbox/public/422.html +26 -0
- data/spec/rails_sandbox/public/500.html +25 -0
- data/spec/rails_sandbox/public/favicon.ico +0 -0
- data/spec/rails_sandbox/public/index.html +241 -0
- data/spec/rails_sandbox/public/robots.txt +5 -0
- data/spec/rails_sandbox/script/rails +6 -0
- data/spec/rails_sandbox/test/fixtures/.gitkeep +0 -0
- data/spec/rails_sandbox/test/functional/.gitkeep +0 -0
- data/spec/rails_sandbox/test/integration/.gitkeep +0 -0
- data/spec/rails_sandbox/test/performance/browsing_test.rb +12 -0
- data/spec/rails_sandbox/test/test_helper.rb +13 -0
- data/spec/rails_sandbox/test/unit/.gitkeep +0 -0
- data/spec/rails_sandbox/vendor/assets/javascripts/.gitkeep +0 -0
- data/spec/rails_sandbox/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/rails_sandbox/vendor/plugins/.gitkeep +0 -0
- data/spec/spec_helper.rb +144 -121
- data/spec/thor_spec.rb +34 -14
- metadata +207 -194
- data/lib/helpers/spree_helper.rb +0 -213
- data/lib/loaders/spreadsheet_loader.rb +0 -144
- data/lib/loaders/spree/image_loader.rb +0 -90
- data/lib/loaders/spree/product_loader.rb +0 -354
- data/lib/thor/spree/bootstrap_cleanup.thor +0 -61
- data/lib/thor/spree/products_images.thor +0 -252
- data/lib/thor/spree/reports.thor +0 -56
- data/public/spree/products/large/DEMO_001_ror_bag.jpeg +0 -0
- data/public/spree/products/large/DEMO_002_Powerstation.jpg +0 -0
- data/public/spree/products/large/DEMO_003_ror_mug.jpeg +0 -0
- data/public/spree/products/mini/DEMO_001_ror_bag.jpeg +0 -0
- data/public/spree/products/mini/DEMO_002_Powerstation.jpg +0 -0
- data/public/spree/products/mini/DEMO_003_ror_mug.jpeg +0 -0
- data/public/spree/products/original/DEMO_001_ror_bag.jpeg +0 -0
- data/public/spree/products/original/DEMO_002_Powerstation.jpg +0 -0
- data/public/spree/products/original/DEMO_003_ror_mug.jpeg +0 -0
- data/public/spree/products/product/DEMO_001_ror_bag.jpeg +0 -0
- data/public/spree/products/product/DEMO_002_Powerstation.jpg +0 -0
- data/public/spree/products/product/DEMO_003_ror_mug.jpeg +0 -0
- data/public/spree/products/small/DEMO_001_ror_bag.jpeg +0 -0
- data/public/spree/products/small/DEMO_002_Powerstation.jpg +0 -0
- data/public/spree/products/small/DEMO_003_ror_mug.jpeg +0 -0
- data/spec/fixtures/datashift_Spree_db.sqlite +0 -0
- data/spec/fixtures/datashift_test_models_db.sqlite +0 -0
- data/spec/fixtures/negative/SpreeProdMiss1Mandatory.csv +0 -4
- data/spec/fixtures/negative/SpreeProdMiss1Mandatory.xls +0 -0
- data/spec/fixtures/negative/SpreeProdMissManyMandatory.csv +0 -4
- data/spec/fixtures/negative/SpreeProdMissManyMandatory.xls +0 -0
- data/spec/fixtures/spree/SpreeImages.xls +0 -0
- data/spec/fixtures/spree/SpreeMultiVariant.csv +0 -4
- data/spec/fixtures/spree/SpreeProducts.csv +0 -4
- data/spec/fixtures/spree/SpreeProducts.xls +0 -0
- data/spec/fixtures/spree/SpreeProductsDefaults.yml +0 -15
- data/spec/fixtures/spree/SpreeProductsMandatoryOnly.xls +0 -0
- data/spec/fixtures/spree/SpreeProductsMultiColumn.csv +0 -4
- data/spec/fixtures/spree/SpreeProductsMultiColumn.xls +0 -0
- data/spec/fixtures/spree/SpreeProductsSimple.csv +0 -4
- data/spec/fixtures/spree/SpreeProductsSimple.xls +0 -0
- data/spec/fixtures/spree/SpreeProductsWithImages.csv +0 -4
- data/spec/fixtures/spree/SpreeProductsWithImages.xls +0 -0
- data/spec/fixtures/spree/SpreeZoneExample.csv +0 -5
- data/spec/fixtures/spree/SpreeZoneExample.xls +0 -0
- data/spec/spree_exporter_spec.rb +0 -72
- data/spec/spree_generator_spec.rb +0 -96
- data/spec/spree_images_loader_spec.rb +0 -107
- data/spec/spree_loader_spec.rb +0 -375
- data/spec/spree_method_mapping_spec.rb +0 -226
- data/spec/spree_variants_loader_spec.rb +0 -189
- data/tasks/export/excel_generator.rake +0 -102
- data/tasks/import/excel.rake +0 -75
- data/test/helper.rb +0 -18
- data/test/test_interact.rb +0 -7
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Copyright:: Autotelik Media Ltd
|
|
2
|
+
# Author :: Tom Statter
|
|
3
|
+
# Date :: July 2010
|
|
4
|
+
# License::
|
|
5
|
+
#
|
|
6
|
+
#
|
|
7
|
+
if(DataShift::Guards::jruby?)
|
|
8
|
+
|
|
9
|
+
require 'java'
|
|
10
|
+
require "poi-3.7-20101029.jar"
|
|
11
|
+
|
|
12
|
+
# Extend the Poi classes with some syntactic sugar
|
|
13
|
+
|
|
14
|
+
class Java::OrgApachePoiHssfUsermodel::HSSFSheet
|
|
15
|
+
def name()
|
|
16
|
+
getSheetName
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def num_rows
|
|
20
|
+
getPhysicalNumberOfRows
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Java::OrgApachePoiHssfUsermodel::HSSFRow
|
|
26
|
+
|
|
27
|
+
include RubyPoiTranslations
|
|
28
|
+
|
|
29
|
+
include Enumerable
|
|
30
|
+
|
|
31
|
+
def []( column)
|
|
32
|
+
cell_value( get_or_create_cell( column ) )
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def []=( column, value )
|
|
36
|
+
get_or_create_cell(column, value).setCellValue((value.to_s || ""))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def get_or_create_cell( column, value = nil )
|
|
40
|
+
if(value)
|
|
41
|
+
java_send(:getCell, [Java::int], column) || createCell(column, poi_cell_type(value))
|
|
42
|
+
else
|
|
43
|
+
java_send(:getCell, [Java::int], column) || java_send(:createCell, [Java::int], column)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def idx
|
|
48
|
+
getRowNum()
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Iterate over each column in the row and yield on the cell
|
|
52
|
+
def each(&block)
|
|
53
|
+
cellIterator.each {|c| yield cell_value(c) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# TODO
|
|
57
|
+
# for min, max and sort from enumerable need <=>
|
|
58
|
+
# def <=> end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Copyright:: Autotelik Media Ltd
|
|
2
|
+
# Author :: Tom Statter
|
|
3
|
+
# Date :: July 2010
|
|
4
|
+
# License::
|
|
5
|
+
#
|
|
6
|
+
# Details:: A wrapper around creating and directly manipulating Excel files.
|
|
7
|
+
# Acts as proxy over main Ruby gem spreadsheet and our own JRuby only implementation using Apache POI
|
|
8
|
+
# Aim is to make it seamless to switch between any Excel implementation
|
|
9
|
+
#
|
|
10
|
+
# http://spreadsheet.rubyforge.org/GUIDE_txt.html
|
|
11
|
+
#
|
|
12
|
+
require 'guards'
|
|
13
|
+
|
|
14
|
+
module ExcelProxy
|
|
15
|
+
# Returns the current proxy class
|
|
16
|
+
def self.proxy_class
|
|
17
|
+
if(DataShift::Guards.jruby?)
|
|
18
|
+
require 'jexcel_file'
|
|
19
|
+
JExcelFile
|
|
20
|
+
else
|
|
21
|
+
require 'spreadsheet'
|
|
22
|
+
require 'spreadsheet_extensions'
|
|
23
|
+
Spreadsheet
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.proxy_object
|
|
28
|
+
if(DataShift::Guards.jruby?)
|
|
29
|
+
ExcelProxy::proxy_class.new
|
|
30
|
+
else
|
|
31
|
+
ExcelProxy::proxy_class::Workbook.new
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Excel #< BasicObject
|
|
37
|
+
|
|
38
|
+
def initialize()
|
|
39
|
+
@excel_class = ExcelProxy::proxy_class
|
|
40
|
+
@excel = ExcelProxy::proxy_object
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Forward all undefined methods to the wrapped Excel object.
|
|
44
|
+
def method_missing(method, *args, &block)
|
|
45
|
+
#puts @excel.class, method, args.inspect
|
|
46
|
+
|
|
47
|
+
if(@excel.respond_to?(method))
|
|
48
|
+
@excel.send(method, *args, &block)
|
|
49
|
+
elsif(@excel.worksheets.last.respond_to?(method)) # active_worksheet doesn't work so use the latest
|
|
50
|
+
@excel.worksheets.last.send(method, *args, &block)
|
|
51
|
+
elsif(@excel_class.respond_to?(method))
|
|
52
|
+
if(method == :open || method == 'open')
|
|
53
|
+
@excel = @excel_class.send(method, *args, &block)
|
|
54
|
+
else
|
|
55
|
+
@excel_class.send(method, *args, &block)
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.method_missing(method, *args, &block)
|
|
63
|
+
@excel_class.send(method, *args, &block)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns +true+ if _obj_ responds to the given method. Private methods are included in the search
|
|
67
|
+
# only if the optional second parameter evaluates to +true+.
|
|
68
|
+
def respond_to?(method, include_private=false)
|
|
69
|
+
super || @excel.respond_to?(method, include_private)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# without this can't get at any defined modules etc
|
|
73
|
+
#
|
|
74
|
+
def self.const_missing(name)
|
|
75
|
+
::Object.const_get(name)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# To change this template, choose Tools | Templates
|
|
2
|
+
# and open the template in the editor.
|
|
3
|
+
|
|
4
|
+
module ExcelBase
|
|
5
|
+
|
|
6
|
+
def sanitize_sheet_name( name )
|
|
7
|
+
name.gsub(/[\[\]:\*\/\\\?]/, '')
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Helpers for dealing with Active Record models and collections
|
|
11
|
+
|
|
12
|
+
def ar_to_headers( records )
|
|
13
|
+
return if( !records.first.is_a?(ActiveRecord::Base) || records.empty?)
|
|
14
|
+
|
|
15
|
+
headers = records.first.class.columns.collect( &:name )
|
|
16
|
+
set_headers( headers )
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Pass a set of AR records
|
|
21
|
+
def ar_to_xls(records, options = {})
|
|
22
|
+
return if( ! records.first.is_a?(ActiveRecord::Base) || records.empty?)
|
|
23
|
+
|
|
24
|
+
row_index =
|
|
25
|
+
if(options[:no_headers])
|
|
26
|
+
0
|
|
27
|
+
else
|
|
28
|
+
ar_to_headers( records )
|
|
29
|
+
1
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
records.each do |record|
|
|
33
|
+
create_row(row_index)
|
|
34
|
+
|
|
35
|
+
ar_to_xls_row(0, record)
|
|
36
|
+
|
|
37
|
+
row_index += 1
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# Save data from an AR record to the current row, based on the record's columns [c1,c2,c3]
|
|
43
|
+
# Returns the number of the final column written to
|
|
44
|
+
def ar_to_xls_row(row, start_column, record)
|
|
45
|
+
return unless( record.is_a?(ActiveRecord::Base))
|
|
46
|
+
|
|
47
|
+
column = start_column
|
|
48
|
+
record.class.columns.each do |connection_column|
|
|
49
|
+
ar_to_xls_cell(row, column, record, connection_column)
|
|
50
|
+
column += 1
|
|
51
|
+
end
|
|
52
|
+
column
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def ar_to_xls_cell(row, column, record, connection_column)
|
|
56
|
+
begin
|
|
57
|
+
datum = record.send(connection_column.name)
|
|
58
|
+
|
|
59
|
+
self[row, column] = datum
|
|
60
|
+
rescue => e
|
|
61
|
+
puts "Failed to export #{datum} from #{connection_column.inspect} to column #{column}"
|
|
62
|
+
puts e, e.backtrace
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# Copyright:: Autotelik Media Ltd
|
|
2
|
+
# Author :: Tom Statter
|
|
3
|
+
# Date :: July 2010
|
|
4
|
+
# License::
|
|
5
|
+
#
|
|
6
|
+
# A wrapper around creating and directly manipulating Excel files.
|
|
7
|
+
#
|
|
8
|
+
# i.e Create and populate XSL files
|
|
9
|
+
#
|
|
10
|
+
# jar added to class path in manifest - 'poi-3.5-beta4-20081128.jar'
|
|
11
|
+
#
|
|
12
|
+
if(DataShift::Guards::jruby?)
|
|
13
|
+
|
|
14
|
+
require 'java'
|
|
15
|
+
|
|
16
|
+
require 'excel_base'
|
|
17
|
+
require 'ruby_poi_translations'
|
|
18
|
+
|
|
19
|
+
class JExcelFile
|
|
20
|
+
|
|
21
|
+
include ExcelBase
|
|
22
|
+
|
|
23
|
+
include RubyPoiTranslations
|
|
24
|
+
extend RubyPoiTranslations
|
|
25
|
+
|
|
26
|
+
include Enumerable
|
|
27
|
+
|
|
28
|
+
include_class 'org.apache.poi.hssf.util.HSSFColor'
|
|
29
|
+
java_import 'org.apache.poi.poifs.filesystem.POIFSFileSystem'
|
|
30
|
+
|
|
31
|
+
java_import 'org.apache.poi.hssf.usermodel.HSSFCell'
|
|
32
|
+
java_import 'org.apache.poi.hssf.usermodel.HSSFWorkbook'
|
|
33
|
+
java_import 'org.apache.poi.hssf.usermodel.HSSFCellStyle'
|
|
34
|
+
java_import 'org.apache.poi.hssf.usermodel.HSSFDataFormat'
|
|
35
|
+
java_import 'org.apache.poi.hssf.usermodel.HSSFClientAnchor'
|
|
36
|
+
java_import 'org.apache.poi.hssf.usermodel.HSSFRichTextString'
|
|
37
|
+
|
|
38
|
+
attr_accessor :workbook, :row, :date_style
|
|
39
|
+
attr_reader :sheet, :current_sheet_index
|
|
40
|
+
|
|
41
|
+
# NOTE: this is the POI 3.7 HSSF maximum rows
|
|
42
|
+
@@maxrows = 65535
|
|
43
|
+
|
|
44
|
+
def self.maxrows
|
|
45
|
+
return @@maxrows
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.date_format
|
|
49
|
+
HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.open(filename)
|
|
53
|
+
HSSFWorkbook.new(FileInputStream.new(filename))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# NOTES :
|
|
57
|
+
# The HSSFWorkbook uses 0 based indexes
|
|
58
|
+
|
|
59
|
+
def initialize()
|
|
60
|
+
@workbook = HSSFWorkbook.new
|
|
61
|
+
|
|
62
|
+
@sheet = nil
|
|
63
|
+
@current_sheet_index = 0
|
|
64
|
+
|
|
65
|
+
# The @patriarchs hash is a workaround because HSSFSheet.getDrawingPatriarch()
|
|
66
|
+
# causes a lot of issues (if it doesn't throw an exception!)
|
|
67
|
+
@patriarchs = Hash.new
|
|
68
|
+
|
|
69
|
+
@date_style = nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def open(filename)
|
|
73
|
+
@workbook = JExcelFile.open(filename)
|
|
74
|
+
|
|
75
|
+
@date_style = @workbook.createCellStyle
|
|
76
|
+
@date_style.setDataFormat( JExcelFile::date_format )
|
|
77
|
+
|
|
78
|
+
activate_sheet(0)
|
|
79
|
+
@workbook
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Create and return a new worksheet.
|
|
83
|
+
# Not set to the active worksheet
|
|
84
|
+
|
|
85
|
+
def create_worksheet( options = {} )
|
|
86
|
+
sheet_name = options[:name]
|
|
87
|
+
|
|
88
|
+
@workbook = HSSFWorkbook.new() if @workbook.nil?
|
|
89
|
+
|
|
90
|
+
unless(sheet_name)
|
|
91
|
+
i = 0
|
|
92
|
+
begin
|
|
93
|
+
sheet_name = "Worksheet#{i += 1}"
|
|
94
|
+
end while(@workbook.getSheetIndex(sheet_name) >= 0) # there is no hard limit to no of sheets in Excel but at some point you will run out of memory!
|
|
95
|
+
|
|
96
|
+
return create_sheet_and_set_styles( sheet_name )
|
|
97
|
+
else
|
|
98
|
+
if (@workbook.getSheetIndex(sheet_name) < 0) #Check sheet doesn't already exist
|
|
99
|
+
return create_sheet_and_set_styles( sheet_name )
|
|
100
|
+
else
|
|
101
|
+
activate_sheet(sheet_name)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Set the supplied sheet index or name, as the active sheet and return it.
|
|
107
|
+
# If no such sheet return current sheet
|
|
108
|
+
def activate_sheet(term)
|
|
109
|
+
|
|
110
|
+
if(@workbook)
|
|
111
|
+
x = term.is_a?(String) ? @workbook.getSheetIndex(term.to_java(java.lang.String)) : term
|
|
112
|
+
@sheet = worksheet(x)
|
|
113
|
+
|
|
114
|
+
if( @sheet )
|
|
115
|
+
@current_sheet_index = x
|
|
116
|
+
@workbook.setActiveSheet(@current_sheet_index)
|
|
117
|
+
@sheet = @workbook.getSheetAt(@current_sheet_index)
|
|
118
|
+
@sheet.setActive(true)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
@sheet
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Return a sheet by index
|
|
125
|
+
def worksheet( index )
|
|
126
|
+
if(@workbook)
|
|
127
|
+
x = index.is_a?(String) ? @workbook.getSheetIndex(index.to_java(java.lang.String)) : index
|
|
128
|
+
return @workbook.getSheetAt(x)
|
|
129
|
+
end
|
|
130
|
+
nil
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def worksheets
|
|
134
|
+
(0...@workbook.getNumberOfSheets).collect { |i| @workbook.getSheetAt(i) }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Create new row (indexing in line with POI usage, start 0)
|
|
138
|
+
def create_row(index)
|
|
139
|
+
return nil if @sheet.nil?
|
|
140
|
+
raise "BAD INDEX: Row indexing starts at 0" if(index < 0)
|
|
141
|
+
@row = @sheet.createRow(index)
|
|
142
|
+
@row
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def num_rows
|
|
146
|
+
@sheet.getPhysicalNumberOfRows
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Process each row. Row type is org.apache.poi.hssf.usermodel.HSSFRow
|
|
150
|
+
|
|
151
|
+
# Currently ignores skip argument - TODO - this is how spreadsheet gem works
|
|
152
|
+
# #each iterates over all used Rows (from the first used Row until but omitting the first unused Row, see also #dimensions)
|
|
153
|
+
# If the argument skip is given,
|
|
154
|
+
# #each iterates from that row until but omitting the first unused Row, effectively skipping the first skip Rows from the top of the Worksheet.
|
|
155
|
+
|
|
156
|
+
def each(skip = nil, &block)
|
|
157
|
+
@sheet.rowIterator.each(&block)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def row( index )
|
|
161
|
+
@sheet.getRow(index) || create_row(index)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Get the enriched value of the Cell at row, column.
|
|
165
|
+
def cell(row, column)
|
|
166
|
+
row = row(row)
|
|
167
|
+
cell_value( row.get_or_create_cell( column ) )
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Get the enriched value of the Cell at row, column.
|
|
171
|
+
def [](row, column)
|
|
172
|
+
cell(row, column)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def []=(row, column, value)
|
|
176
|
+
set_cell(row, column, value)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def set_cell(row, column, value)
|
|
180
|
+
@row = row(row)
|
|
181
|
+
|
|
182
|
+
@row.get_or_create_cell(column, value).setCellValue((value || ""))
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def write( filename = nil )
|
|
186
|
+
filename.nil? ? file = @filepath : file = filename
|
|
187
|
+
out = FileOutputStream.new(file)
|
|
188
|
+
@workbook.write(out) unless @workbook.nil?
|
|
189
|
+
out.close
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
alias_method :save, :write
|
|
193
|
+
|
|
194
|
+
def save_to_text( filename )
|
|
195
|
+
File.open( filename, 'w') {|f| f.write(to_s) }
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def to_s
|
|
199
|
+
outs = ByteArrayOutputStream.new
|
|
200
|
+
@workbook.write(outs);
|
|
201
|
+
outs.close();
|
|
202
|
+
String.from_java_bytes(outs.toByteArray)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
private
|
|
206
|
+
|
|
207
|
+
def create_sheet_and_set_styles( sheet_name )
|
|
208
|
+
@sheet = @workbook.createSheet( sanitize_sheet_name(sheet_name) )
|
|
209
|
+
|
|
210
|
+
@patriarchs.store(sheet_name, @sheet.createDrawingPatriarch())
|
|
211
|
+
|
|
212
|
+
@date_style = @workbook.createCellStyle
|
|
213
|
+
@date_style.setDataFormat( JExcelFile::date_format )
|
|
214
|
+
@sheet
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
require 'jexcel_file_extensions'
|
|
220
|
+
require 'apache_poi_extensions'
|
|
221
|
+
|
|
222
|
+
end
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Copyright:: Autotelik Media Ltd
|
|
2
|
+
# Author :: Tom Statter
|
|
3
|
+
# Date :: July 2010
|
|
4
|
+
# License::
|
|
5
|
+
#
|
|
6
|
+
# Details:: A helper module providing shortcuts for manipulating Excel files.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
if(DataShift::Guards::jruby?)
|
|
10
|
+
|
|
11
|
+
JExcelFile.class_eval do
|
|
12
|
+
|
|
13
|
+
include_class 'org.apache.poi.hssf.util.HSSFColor'
|
|
14
|
+
|
|
15
|
+
# Return the current active sheet
|
|
16
|
+
# If term supplied find sheet and set active
|
|
17
|
+
#
|
|
18
|
+
def active_worksheet(term = nil)
|
|
19
|
+
if( term.nil? )
|
|
20
|
+
@sheet ||= @workbook.getSheetAt(@current_sheet_index)
|
|
21
|
+
else
|
|
22
|
+
activate_sheet(term)
|
|
23
|
+
end if(@workbook)
|
|
24
|
+
@sheet
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Populate a row of cells with data in an array
|
|
28
|
+
# where the co-ordinates relate to row/column start position
|
|
29
|
+
#
|
|
30
|
+
def set_row( row, col, data, sheet_num = nil)
|
|
31
|
+
|
|
32
|
+
sheet(sheet_num)
|
|
33
|
+
|
|
34
|
+
create_row(row)
|
|
35
|
+
|
|
36
|
+
column = col
|
|
37
|
+
data.each do |datum|
|
|
38
|
+
set_cell(row, column, datum)
|
|
39
|
+
column += 1
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def headers=( headers )
|
|
44
|
+
headers.each_with_index do |header, column|
|
|
45
|
+
self[0, column] = (header || "")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Convert array into a header row
|
|
50
|
+
def set_headers(headers, apply_style = nil)
|
|
51
|
+
create_row(0)
|
|
52
|
+
return if headers.empty?
|
|
53
|
+
|
|
54
|
+
style = apply_style || header_style()
|
|
55
|
+
|
|
56
|
+
headers.each_with_index do |datum, i|
|
|
57
|
+
c = @row.createCell(i, poi_cell_type(datum))
|
|
58
|
+
c.setCellValue(datum)
|
|
59
|
+
c.setCellStyle(style)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
attr_writer :header_background_colour
|
|
64
|
+
|
|
65
|
+
def header_background_colour
|
|
66
|
+
@header_background_colour ||= org.apache.poi.hssf.util.HSSFColor::LIGHT_CORNFLOWER_BLUE.index
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def header_style
|
|
70
|
+
return @header_style if @header_style
|
|
71
|
+
@header_style = @workbook.createCellStyle();
|
|
72
|
+
@header_style.setBorderTop(6) # double lines border
|
|
73
|
+
@header_style.setBorderBottom(1) # single line border
|
|
74
|
+
@header_style.setFillBackgroundColor(header_background_colour)
|
|
75
|
+
|
|
76
|
+
@header_style
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def add_comment( cell, text )
|
|
80
|
+
raise "Please supply valid HSSFCell" unless cell.respond_to?('setCellComment')
|
|
81
|
+
return if @sheet.nil?
|
|
82
|
+
|
|
83
|
+
patriarch = @patriarchs[@sheet.getSheetName()]
|
|
84
|
+
|
|
85
|
+
anchor = HSSFClientAnchor.new(100, 50, 100, 50, cell.getColumnIndex(), cell.getRowIndex(), cell.getColumnIndex()+3, cell.getRowIndex()+4)
|
|
86
|
+
comment = patriarch.createCellComment(anchor)
|
|
87
|
+
|
|
88
|
+
comment_text = HSSFRichTextString.new(text)
|
|
89
|
+
comment.setString(comment_text)
|
|
90
|
+
comment.setAuthor("Mapping")
|
|
91
|
+
|
|
92
|
+
cell.setCellComment(comment)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Get a percentage style
|
|
96
|
+
def getPercentStyle()
|
|
97
|
+
if (@percentCellStyle.nil? && @workbook)
|
|
98
|
+
@percentCellStyle = @workbook.createCellStyle();
|
|
99
|
+
@percentCellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00%"));
|
|
100
|
+
end
|
|
101
|
+
return @percentCellStyle
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Auto size either the given column index or all columns
|
|
105
|
+
def autosize(column = nil)
|
|
106
|
+
return if @sheet.nil?
|
|
107
|
+
if (column.kind_of? Integer)
|
|
108
|
+
@sheet.autoSizeColumn(column)
|
|
109
|
+
else
|
|
110
|
+
@sheet.getRow(0).cellIterator.each{|c| @sheet.autoSizeColumn(c.getColumnIndex)}
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def create_freeze_pane(row=1, column=0)
|
|
115
|
+
return if @sheet.nil?
|
|
116
|
+
@sheet.createFreezePane(row, column)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# Use execute to run sql query provided
|
|
121
|
+
# and write to a csv file (path required)
|
|
122
|
+
# header row is optional but default is on
|
|
123
|
+
# Auto mapping of specified columns is optional
|
|
124
|
+
# @mappings is a hash{column => map} of columns to a map{old_value => new_value}
|
|
125
|
+
def results_to_sheet( results, sheet, mappings=nil, header=true)
|
|
126
|
+
numrows = results.length
|
|
127
|
+
sheet_name = sheet
|
|
128
|
+
|
|
129
|
+
if (numrows == 0)
|
|
130
|
+
log :info, "WARNING - results are empty nothing written to sheet: #{sheet}"
|
|
131
|
+
return
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
#Check if we need to split the results into seperate sheets
|
|
135
|
+
if (numrows > @@maxrows )
|
|
136
|
+
startrow = 0
|
|
137
|
+
while (numrows > 0)
|
|
138
|
+
# Split the results and write to a new sheet
|
|
139
|
+
next_results = results.slice(startrow, @@maxrows > numrows ? numrows : @@maxrows)
|
|
140
|
+
self.results_to_sheet(next_results, "#{sheet_name}", mappings, header) if next_results
|
|
141
|
+
|
|
142
|
+
# Increase counters
|
|
143
|
+
numrows -= next_results.length
|
|
144
|
+
startrow += next_results.length
|
|
145
|
+
sheet_name += 'I'
|
|
146
|
+
end
|
|
147
|
+
else
|
|
148
|
+
log :info, "Writting #{numrows} rows to : #{sheet_name}"
|
|
149
|
+
# Create required sheet
|
|
150
|
+
self.create(sheet)
|
|
151
|
+
|
|
152
|
+
row_index = self.num_rows
|
|
153
|
+
# write header line
|
|
154
|
+
if (header && row_index==0 )
|
|
155
|
+
header_row = @sheet.createRow(row_index)
|
|
156
|
+
cell_index = 0
|
|
157
|
+
results[0].keys.each{ |h|
|
|
158
|
+
header_row.createCell(cell_index).setCellValue("#{h}")
|
|
159
|
+
@sheet.setDefaultColumnStyle(cell_index, self.getPercentStyle) if "#{h}".include? '%'
|
|
160
|
+
cell_index += 1
|
|
161
|
+
}
|
|
162
|
+
# Freeze the header row
|
|
163
|
+
@sheet.createFreezePane( 0, 1, 0, 1 )
|
|
164
|
+
row_index += 1
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# write_results
|
|
168
|
+
results.each{ |row|
|
|
169
|
+
sheet_row = @sheet.createRow(row_index)
|
|
170
|
+
cell_index = 0
|
|
171
|
+
row.each{|k,v|
|
|
172
|
+
celltype = v.kind_of?(Numeric) ? HSSFCell::CELL_TYPE_NUMERIC : HSSFCell::CELL_TYPE_STRING
|
|
173
|
+
cell = sheet_row.createCell(cell_index, celltype)
|
|
174
|
+
|
|
175
|
+
v.nil? ? value = "<NIL>" : value = v
|
|
176
|
+
|
|
177
|
+
cell.setCellValue(value)
|
|
178
|
+
|
|
179
|
+
cell_index +=1
|
|
180
|
+
}
|
|
181
|
+
#puts "#{sheet}: written row #{row_index}"
|
|
182
|
+
row_index +=1
|
|
183
|
+
}
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
module ExcelHelper
|
|
188
|
+
require 'java'
|
|
189
|
+
|
|
190
|
+
java_import 'org.apache.poi.poifs.filesystem.POIFSFileSystem'
|
|
191
|
+
java_import 'org.apache.poi.hssf.usermodel.HSSFCell'
|
|
192
|
+
java_import 'org.apache.poi.hssf.usermodel.HSSFWorkbook'
|
|
193
|
+
java_import 'org.apache.poi.hssf.usermodel.HSSFCellStyle'
|
|
194
|
+
java_import 'org.apache.poi.hssf.usermodel.HSSFDataFormat'
|
|
195
|
+
java_import 'java.io.ByteArrayOutputStream'
|
|
196
|
+
java_import 'java.util.Date'
|
|
197
|
+
|
|
198
|
+
# ActiveRecord Helper - Export model data to XLS file format
|
|
199
|
+
#
|
|
200
|
+
def to_xls(items=[])
|
|
201
|
+
|
|
202
|
+
@excel = ExcelFile.new(items[0].class.name)
|
|
203
|
+
|
|
204
|
+
@excel.create_row(0)
|
|
205
|
+
|
|
206
|
+
sheet = @excel.sheet
|
|
207
|
+
|
|
208
|
+
# header row
|
|
209
|
+
if !items.empty?
|
|
210
|
+
row = sheet.createRow(0)
|
|
211
|
+
cell_index = 0
|
|
212
|
+
items[0].class.columns.each do |column|
|
|
213
|
+
row.createCell(cell_index).setCellValue(column.name)
|
|
214
|
+
cell_index += 1
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# value rows
|
|
219
|
+
row_index = 1
|
|
220
|
+
items.each do |item|
|
|
221
|
+
row = sheet.createRow(row_index);
|
|
222
|
+
|
|
223
|
+
cell_index = 0
|
|
224
|
+
item.class.columns.each do |column|
|
|
225
|
+
cell = row.createCell(cell_index)
|
|
226
|
+
if column.sql_type =~ /date/ then
|
|
227
|
+
millis = item.send(column.name).to_f * 1000
|
|
228
|
+
cell.setCellValue(Date.new(millis))
|
|
229
|
+
cell.setCellStyle(dateStyle);
|
|
230
|
+
elsif column.sql_type =~ /int/ then
|
|
231
|
+
cell.setCellValue(item.send(column.name).to_i)
|
|
232
|
+
else
|
|
233
|
+
value = item.send(column.name)
|
|
234
|
+
cell.setCellValue(item.send(column.name)) unless value.nil?
|
|
235
|
+
end
|
|
236
|
+
cell_index += 1
|
|
237
|
+
end
|
|
238
|
+
row_index += 1
|
|
239
|
+
end
|
|
240
|
+
@excel.to_s
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|