ar_loader 0.0.4

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.
@@ -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
+