ar_loader 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,103 @@
1
+ # Copyright:: (c) Autotelik Media Ltd 2011
2
+ # Author :: Tom Statter
3
+ # Date :: Feb 2011
4
+ # License:: MIT. Free, Open Source.
5
+ #
6
+ # Usage from rake : rake image_load input=path_to_images
7
+ #
8
+ # => rake image_load input=vendor\extensions\autotelik\fixtures\
9
+ # => rake image_load input="C:\images\photos large' dummy=true
10
+ # => rake image_load input="C:\images\taxon_icons" skip_if_no_assoc=true klass=Taxon
11
+
12
+ namespace :autotelik do
13
+
14
+ namespace :spree do
15
+
16
+ desc "Populate the DB with images.\nDefault location db/image_seeds, or specify :input=<path> or dir under db/image_seeds with :folder"
17
+ # :dummy => dummy run without actual saving to DB
18
+ task :image_load, :input, :folder, :dummy, :sku, :skip_if_no_assoc, :skip_if_loaded, :klass, :needs => :environment do |t, args|
19
+
20
+ require 'image_loader'
21
+
22
+ raise "USAGE: Please specify one of :input or :folder" if(args[:input] && args[:folder])
23
+ puts "SKU not specified " if(args[:input] && args[:folder])
24
+
25
+ if args[:input]
26
+ @image_cache = args[:input]
27
+ else
28
+ @image_cache = File.join(Rails.root, "db", "image_seeds")
29
+ @image_cache = File.join(@image_cache, args[:folder]) if(args[:folder])
30
+ end
31
+
32
+ klazz = args[:klass] ? Kernal.const_get(args[:klass]) : Product
33
+
34
+ image_loader = ImageLoader.new
35
+
36
+ if(File.exists? @image_cache )
37
+ puts "Loading images from #{@image_cache}"
38
+
39
+ missing_records = []
40
+ Dir.glob("#{@image_cache}/*.{jpg,png,gif}") do |image_name|
41
+
42
+ puts "Processing #{image_name} : #{File.exists?(image_name)}"
43
+ base_name = File.basename(image_name, '.*')
44
+
45
+ record = nil
46
+ if(klazz == Product && args[:sku])
47
+ sku = base_name.slice!(/\w+/)
48
+ sku.strip!
49
+ base_name.strip!
50
+
51
+ puts "Looking fo SKU #{sku}"
52
+ record = Variant.find_by_sku(sku)
53
+ if record
54
+ record = record.product # SKU stored on Variant but we want it's master Product
55
+ else
56
+ puts "Looking for NAME [#{base_name}]"
57
+ record = klazz.find_by_name(base_name)
58
+ end
59
+ else
60
+ puts "Looking for #{klazz.name} with NAME [#{base_name}]"
61
+ record = klazz.find_by_name(base_name)
62
+ end
63
+
64
+ if(record)
65
+ puts "FOUND: #{record.inspect}"
66
+ puts "FOUND: #{record.images.collect(&:attachment_file_name).inspect}"
67
+ exists = record.images.detect {|i| puts "COMPARE #{i.attachment_file_name} => #{image_name}"; i.attachment_file_name == image_name }
68
+ puts "Check for existing attachment [#{exists}]"
69
+ if(args[:skip_if_loaded] && exists)
70
+ puts "Skipping - Image #{image_name} already loaded for #{klazz}"
71
+ next
72
+ end
73
+ else
74
+ missing_records << image_name
75
+ end
76
+
77
+ # Now do actual upload to DB unless we are doing a dummy run,
78
+ # or the Image must have an associated record
79
+ unless(args[:dummy] == 'true' || (args[:skip_if_no_assoc] && record.nil?))
80
+ image_loader.refresh()
81
+ puts "Process Image"
82
+ image_loader.process( image_name, record )
83
+ end
84
+
85
+ end
86
+
87
+ unless missing_records.empty?
88
+ FileUtils.mkdir_p('MissingRecords') unless File.directory?('MissingRecords')
89
+
90
+ puts '\nMISSING Records Report>>'
91
+ missing_records.each do |i|
92
+ puts "Copy #{i} to MissingRecords folder"
93
+ FileUtils.cp( i, 'MissingRecords') unless(args[:dummy] == 'true')
94
+ end
95
+ end
96
+
97
+ else
98
+ puts "ERROR: Supplied Path #{@image_cache} not accesible"
99
+ exit(-1)
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,107 @@
1
+ # Copyright:: (c) Autotelik Media Ltd 2011
2
+ # Author :: Tom Statter
3
+ # Date :: Feb 2011
4
+ # License:: MIT. Free, Open Source.
5
+ #
6
+ # REQUIRES: JRuby access to Java
7
+ #
8
+ # Usage from rake : jruby -S rake excel_loader input=<file.xls>
9
+ #
10
+ # e.g. => jruby -S rake excel_load input=vendor\extensions\autotelik\fixtures\ExampleInfoWeb.xls
11
+ # => jruby -S rake excel_load input=C:\MyProducts.xls verbose=true
12
+ #
13
+ namespace :autotelik do
14
+
15
+ desc "Populate a Spree database with Product/Varient data stored in Excel"
16
+ task :product_load, :input, :verbose, :sku_prefix, :needs => :environment do |t, args|
17
+
18
+ raise "USAGE: jruby -S rake product_load input=excel_file.xls" unless args[:input]
19
+ raise "ERROR: Could not find file #{args[:input]}" unless File.exists?(args[:input])
20
+
21
+ require 'product_loader'
22
+ require 'method_mapper_excel'
23
+
24
+ @method_mapper = MethodMapperExcel.new(args[:input], Product)
25
+
26
+ @excel = @method_mapper.excel
27
+
28
+ if(args[:verbose])
29
+ puts "Loading from Excel file: #{args[:input]}"
30
+ puts "Processing #{@excel.num_rows} rows"
31
+ end
32
+
33
+ # REQUIRED 'set' methods on Product i.e will not validate/save without these
34
+ required_methods = ['sku', 'name', 'price']
35
+
36
+ @method_mapper.check_mandatory( required_methods )
37
+
38
+ # COLUMNS WITH DEFAULTS - TODO create YAML configuration file to drive defaults etc
39
+
40
+ MethodDetail.set_default_value('available_on', Time.now.to_s(:db) )
41
+ MethodDetail.set_default_value('cost_price', 0.0 )
42
+
43
+ MethodDetail.set_prefix('sku', args[:sku_prefix] ) if args[:sku_prefix]
44
+
45
+ # Process spreadsheet and create Products
46
+ method_names = @method_mapper.method_names
47
+
48
+ sku_index = method_names.index('sku')
49
+
50
+ Product.transaction do
51
+ @products = []
52
+
53
+ (1..@excel.num_rows).collect do |row|
54
+
55
+ product_data_row = @excel.sheet.getRow(row)
56
+ break if product_data_row.nil?
57
+
58
+ # Excel num_rows seems to return all 'visible' rows so,
59
+ # we have to manually detect when actual data ends and all the empty rows start
60
+ contains_data = required_methods.find { |mthd| ! product_data_row.getCell(method_names.index(mthd)).to_s.empty? }
61
+ break unless contains_data
62
+
63
+ @assoc_classes = {}
64
+
65
+ loader = ProductLoader.new()
66
+
67
+ # TODO - Smart sorting of column processing order ....
68
+ # Does not currently ensure mandatory columns (for valid?) processed first but Product needs saving
69
+ # before associations can be processed so user should ensure SKU, name, price columns are among first columns
70
+
71
+ @method_mapper.methods.each_with_index do |method_map, col|
72
+ product_data_row.getCell(col).setCellType(JExcelFile::HSSFCell::CELL_TYPE_STRING) if(col == sku_index)
73
+ loader.process(method_map, @excel.value(product_data_row, col))
74
+ begin
75
+ prod = loader.load_object
76
+ if( prod.valid? && prod.new_record? )
77
+ prod.save
78
+ end
79
+ rescue
80
+ raise "Error processing Product"
81
+ end
82
+ end
83
+
84
+ product = loader.load_object
85
+
86
+ product.available_on ||= Time.now.to_s(:db)
87
+
88
+ # TODO - handle when it's not valid ?
89
+ # Process rest and dump out an exception list of Products
90
+ #unless(product.valid?)
91
+ #end
92
+
93
+ puts "SAVING ROW #{row} : #{product.inspect}" if args[:verbose]
94
+
95
+ unless(product.save)
96
+ puts product.errors.inspect
97
+ puts product.errors.full_messages.inspect
98
+ raise "Error Saving Product: #{product.sku} :#{product.name}"
99
+ else
100
+ @products << product
101
+ end
102
+ end
103
+ end # TRANSACTION
104
+
105
+ end
106
+
107
+ end
@@ -0,0 +1,13 @@
1
+ # Copyright:: (c) Autotelik Media Ltd 2011
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2010
4
+ #
5
+ # License:: Free, OpenSource... MIT ?
6
+
7
+ # This is the best effort I've found so far to reduce the amount of MS cruft
8
+ # to absolute minimum
9
+ # ... but unfortunately these tags will NOT BE REMOVED completely - manual cleanup still required
10
+
11
+ # TODO - add another ruby parse layer to strip these out completely
12
+
13
+ new-empty-tags: o:smarttagtype, st1:placename, st1:place, st1:placetype
@@ -0,0 +1,167 @@
1
+ # Copyright:: (c) Autotelik Media Ltd 2011
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2010
4
+ #
5
+ # License:: Free, OpenSource... MIT ?
6
+ #
7
+ # About:: Rake tasks to read Word documents, containing product descriptions,
8
+ # convert to HTML, tidy the HTML and then create seed_fu ready fixtures,
9
+ # from a template, with product description supplied by the HTML
10
+ #
11
+ # Note cleanest HTML is produced by this combination : saving with WdFormatHTML
12
+ # not WdFormatFilteredHTML and using the '--word-2000', 'y' option to tidy
13
+ # (don't use the '--bare' option)
14
+ #
15
+ # Not currently available for JRuby due to Win32Ole requirement
16
+ #
17
+ # Requires local exes available in PATH for :
18
+ # Microsoft Word
19
+ # HTML Tidy - http://tidy.sourceforge.net (Free)
20
+ #
21
+ require 'erb'
22
+
23
+ namespace :autotelik do
24
+
25
+ desc "Convert MS Word to HTML and seed_fu fixtures. help=true for detailed usage."
26
+
27
+ task :word2html, :help, :needs => [:environment] do |t, args|
28
+ x =<<-EOS
29
+
30
+ USAGE::
31
+ Convert MS Word docs to HTML and seed_fu fixtures, by default searches for docs
32
+ in RAILS_ROOT/doc/copy
33
+
34
+ You can change the directory where Word document files are located
35
+ with the COPY_PATH environment variable.
36
+
37
+ Examples:
38
+ # default, to convert all Word files for the current environment
39
+ rake autotelik:word2seedfu
40
+
41
+ # to load seed files matching orders or customers
42
+ rake db:seed SEED=orders,customers
43
+
44
+ # to load files from RAILS_ROOT/features/fixtures
45
+ rake db:seed FIXTURE_PATH=features/fixtures
46
+ EOS
47
+
48
+ if(args[:help])
49
+ puts x
50
+ exit(0)
51
+ end
52
+
53
+ site_extension_lib = File.join(SiteExtension.root, 'lib')
54
+
55
+ require File.join(site_extension_lib, 'word')
56
+
57
+ copy_path = ENV["COPY_PATH"] ? ENV["COPY_PATH"] : File.join(RAILS_ROOT, "doc", "copy")
58
+ fixtures_path = ENV["FIXTURES_PATH"] ? ENV["FIXTURES_PATH"] : File.join(RAILS_ROOT, "db", "fixtures")
59
+
60
+ copy_files = Dir[File.join(copy_path, '*.doc')]
61
+
62
+ copy_files.each do |file|
63
+
64
+ name = File.basename(file, '.doc')
65
+
66
+ puts "\n== Generate raw HTML from #{name}.doc =="
67
+
68
+ @word = Word.new(true)
69
+
70
+ @word.open( file )
71
+
72
+ html_file = File.join(copy_path, "#{name}.ms.html")
73
+
74
+ @word.save_as_html( html_file )
75
+
76
+ tidy_file = File.join(copy_path, "#{name}.html")
77
+
78
+ tidy_config = File.join(site_extension_lib, 'tasks', 'tidy_config.txt')
79
+
80
+ puts "tidy cmd line:", "tidy -config #{tidy_config} -clean --show-body-only y --word-2000 y --indent-spaces 2 -output #{tidy_file} #{html_file}"
81
+
82
+ result = system("tidy", '-config', "#{tidy_config}", '-clean', '--show-body-only', 'y', '--word-2000', 'y', '--indent-spaces', '2', '-output', "#{tidy_file}", "#{html_file}")
83
+
84
+ # TODO maybe report on result, $?
85
+
86
+ File.open( tidy_file ) do |f|
87
+ puts f.read
88
+ end
89
+
90
+ @word.quit
91
+ end
92
+ end
93
+
94
+ desc "Convert MS Word to HTML and seed_fu fixtures. help=true for detailed usage."
95
+ task :word2seedfu => :environment do
96
+ site_extension_lib = File.join(SiteExtension.root, 'lib')
97
+
98
+ require File.join(site_extension_lib, 'word')
99
+
100
+ sku_id = ENV["INITIAL_SKU_ID"] ? ENV["INITIAL_SKU_ID"] : 0
101
+ sku_prefix = ENV["SKU_PREFIX"] ? ENV["SKU_PREFIX"] : File.basename( RAILS_ROOT )
102
+
103
+ seedfu_template = File.join(site_extension_lib, 'tasks', 'seed_fu_product_template.erb')
104
+
105
+ begin
106
+ File.open( seedfu_template ) do |f|
107
+ @template = ERB.new(f.read)
108
+ end
109
+ rescue => e
110
+ puts "ERROR: #{e.inspect}"
111
+ puts "Cannot open or read template #{seedfu_template}"
112
+ raise e
113
+ end
114
+
115
+ copy_path = ENV["COPY_PATH"] ? ENV["COPY_PATH"] : File.join(RAILS_ROOT, "doc", "copy")
116
+ fixtures_path = ENV["FIXTURES_PATH"] ? ENV["FIXTURES_PATH"] : File.join(RAILS_ROOT, "db", "fixtures")
117
+
118
+ copy_files = Dir[File.join(copy_path, '*.doc')]
119
+
120
+ copy_files.each do |file|
121
+
122
+ name = File.basename(file, '.doc')
123
+
124
+ puts "\n== Generate raw HTML from #{name}.doc =="
125
+
126
+ @word = Word.new(true)
127
+
128
+ @word.open( file )
129
+
130
+ html_file = File.join(copy_path, "#{name}.ms.html")
131
+
132
+ @word.save_as_html( html_file )
133
+
134
+ tidy_file = File.join(copy_path, "#{name}.html")
135
+
136
+ tidy_config = File.join(site_extension_lib, 'tasks', 'tidy_config.txt')
137
+
138
+ puts "tidy cmd line:", "tidy -config #{tidy_config} -clean --show-body-only y --word-2000 y --indent-spaces 2 -output #{tidy_file} #{html_file}"
139
+
140
+ result = system("tidy", '-config', "#{tidy_config}", '-clean', '--show-body-only', 'y', '--word-2000', 'y', '--indent-spaces', '2', '-output', "#{tidy_file}", "#{html_file}")
141
+
142
+ # TODO maybe report on result, $?
143
+
144
+ File.open( tidy_file ) do |f|
145
+ @description = f.read
146
+ end
147
+
148
+ sku_id_str = "%03d" % sku_id
149
+
150
+ seed_file = "#{sku_id_str}_#{name.gsub(' ', '_')}.rb"
151
+ puts "\n== Generate seed fu file #{seed_file} =="
152
+
153
+ @sku = "#{sku_prefix}_#{sku_id_str}"
154
+ @name = 'TODO'
155
+
156
+ File.open( File.join(fixtures_path, seed_file), 'w' ) do |f|
157
+ f.write @template.result(binding)
158
+ puts "\nFile created: #{File.join(fixtures_path, seed_file)}"
159
+ end
160
+
161
+ sku_id += 1
162
+
163
+ @word.quit
164
+ end
165
+
166
+ end
167
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ar_loader
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.4
6
+ platform: ruby
7
+ authors:
8
+ - thomas statter
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-03-24 00:00:00 +00:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: A file based loader for Active Record models. Seed database directly from Excel/CSV. Includes rake support for Spree
18
+ email: rubygems@autotelik.co.uk
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - README.markdown
25
+ - LICENSE
26
+ files:
27
+ - LICENSE
28
+ - README.markdown
29
+ - Rakefile
30
+ - lib/ar_loader.rb
31
+ - lib/to_b.rb
32
+ - lib/VERSION
33
+ - lib/engine/file_definitions.rb
34
+ - lib/engine/mapping_file_definitions.rb
35
+ - lib/engine/method_detail.rb
36
+ - lib/engine/method_mapper.rb
37
+ - lib/engine/method_mapper_csv.rb
38
+ - lib/engine/word.rb
39
+ - lib/engine/jruby/jexcel_file.rb
40
+ - lib/engine/jruby/method_mapper_excel.rb
41
+ - lib/java/poi-3.2-FINAL-20081019.jar
42
+ - lib/java/poi-3.6.jar
43
+ - lib/java/poi-contrib-3.6-20091214.jar
44
+ - lib/java/poi-examples-3.6-20091214.jar
45
+ - lib/java/poi-ooxml-3.6-20091214.jar
46
+ - lib/java/poi-ooxml-schemas-3.6-20091214.jar
47
+ - lib/java/poi-scratchpad-3.6-20091214.jar
48
+ - lib/loaders/loader_base.rb
49
+ - lib/loaders/spree/image_loader.rb
50
+ - lib/loaders/spree/product_loader.rb
51
+ - spec/excel_loader_spec.rb
52
+ - spec/spec_helper.rb
53
+ - tasks/db_tasks.rake
54
+ - tasks/excel_loader.rake
55
+ - tasks/file_tasks.rake
56
+ - tasks/seed_fu_product_template.erb
57
+ - tasks/tidy_config.txt
58
+ - tasks/word_to_seedfu.rake
59
+ - tasks/spree/image_load.rake
60
+ - tasks/spree/product_loader.rake
61
+ has_rdoc: true
62
+ homepage: http://www.autotelik.co.uk
63
+ licenses: []
64
+
65
+ post_install_message:
66
+ rdoc_options: []
67
+
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project:
85
+ rubygems_version: 1.5.1
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: File based loader for Active Record models
89
+ test_files: []
90
+