ar_loader 0.0.4 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +9 -9
- data/README.markdown +221 -210
- data/Rakefile +76 -76
- data/lib/VERSION +1 -1
- data/lib/ar_loader.rb +66 -53
- data/lib/engine/file_definitions.rb +353 -353
- data/lib/engine/jruby/jexcel_file.rb +181 -181
- data/lib/engine/jruby/method_mapper_excel.rb +43 -43
- data/lib/engine/mapping_file_definitions.rb +87 -87
- data/lib/engine/method_detail.rb +140 -139
- data/lib/engine/method_mapper.rb +156 -156
- data/lib/engine/method_mapper_csv.rb +27 -27
- data/lib/engine/word.rb +70 -70
- data/lib/loaders/loader_base.rb +73 -60
- data/lib/loaders/spree/image_loader.rb +41 -46
- data/lib/loaders/spree/product_loader.rb +91 -92
- data/lib/to_b.rb +24 -24
- data/spec/database.yml +6 -0
- data/spec/db/migrate/20110803201325_create_testbed.rb +25 -0
- data/spec/excel_loader_spec.rb +98 -137
- data/spec/spec_helper.rb +18 -36
- data/spec/spree_loader_spec.rb +158 -0
- data/tasks/{seed_fu_product_template.erb → config/seed_fu_product_template.erb} +15 -15
- data/tasks/{tidy_config.txt → config/tidy_config.txt} +12 -12
- data/tasks/db_tasks.rake +64 -64
- data/tasks/excel_loader.rake +113 -100
- data/tasks/file_tasks.rake +37 -37
- data/tasks/spree/image_load.rake +107 -102
- data/tasks/spree/product_loader.rake +107 -106
- data/tasks/word_to_seedfu.rake +166 -166
- metadata +61 -47
data/tasks/excel_loader.rake
CHANGED
@@ -1,101 +1,114 @@
|
|
1
|
-
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
-
# Author :: Tom Statter
|
3
|
-
# Date :: Feb 2011
|
4
|
-
# License:: TBD. Free, Open Source. MIT ?
|
5
|
-
#
|
6
|
-
# REQUIRES: JRuby
|
7
|
-
#
|
8
|
-
# Usage
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
raise "
|
19
|
-
raise "ERROR:
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
1
|
+
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
+
# Author :: Tom Statter
|
3
|
+
# Date :: Feb 2011
|
4
|
+
# License:: TBD. Free, Open Source. MIT ?
|
5
|
+
#
|
6
|
+
# REQUIRES: JRuby
|
7
|
+
#
|
8
|
+
# Usage::
|
9
|
+
# => jruby -S rake autotelik:excel_load model=<active record class> input=<file.xls>
|
10
|
+
# => jruby -S rake autotelik:excel_load model=<active record class> input=C:\MyProducts.xlsverbose=true
|
11
|
+
#
|
12
|
+
namespace :autotelik do
|
13
|
+
|
14
|
+
desc "Populate a model's table in db with data from .xls (Excel) file"
|
15
|
+
task :excel_load, :model, :loader, :input, :verbose, :needs => :environment do |t, args|
|
16
|
+
|
17
|
+
raise "USAGE: jruby -S rake excel_load input=excel_file.xls" unless args[:input]
|
18
|
+
raise "ERROR: Cannot process without AR Model - please supply model=<Class>" unless args[:model]
|
19
|
+
raise "ERROR: Could not find file #{args[:input]}" unless File.exists?(args[:input])
|
20
|
+
|
21
|
+
begin
|
22
|
+
klass = Kernel.const_get(args[:model])
|
23
|
+
rescue NameError
|
24
|
+
raise "ERROR: No such AR Model found - please check model=<Class>"
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
require "#{args[:model]}_loader"
|
29
|
+
|
30
|
+
loader_klass = Kernel.const_get("#{args[:model]}Loader")
|
31
|
+
|
32
|
+
loader = loader_klass.new(klass)
|
33
|
+
rescue
|
34
|
+
puts "INFO: No specific #{args[:model]}Loader found using generic loader"
|
35
|
+
loader = LoaderBase.new(klass)
|
36
|
+
end
|
37
|
+
|
38
|
+
require 'method_mapper_excel'
|
39
|
+
|
40
|
+
@method_mapper = MethodMapperExcel.new(args[:input], klass)
|
41
|
+
|
42
|
+
@excel = @method_mapper.excel
|
43
|
+
|
44
|
+
if(args[:verbose])
|
45
|
+
puts "Loading from Excel file: #{args[:input]}"
|
46
|
+
puts "Processing #{@excel.num_rows} rows"
|
47
|
+
end
|
48
|
+
|
49
|
+
# Process spreadsheet and create model instances
|
50
|
+
|
51
|
+
klass.transaction do
|
52
|
+
@loaded_objects = []
|
53
|
+
|
54
|
+
(1..@excel.num_rows).collect do |row|
|
55
|
+
|
56
|
+
data_row = @excel.sheet.getRow(row)
|
57
|
+
break if data_row.nil?
|
58
|
+
|
59
|
+
@assoc_classes = {}
|
60
|
+
|
61
|
+
# TODO - Smart sorting of column processing order ....
|
62
|
+
# Does not currently ensure mandatory columns (for valid?) processed first but model needs saving
|
63
|
+
# before associations can be processed so user should ensure mandatory columns are prior to associations
|
64
|
+
|
65
|
+
contains_data = false
|
66
|
+
|
67
|
+
# Iterate over the columns method_mapper found in Excel,
|
68
|
+
# pulling data out of associated column
|
69
|
+
@method_mapper.methods.each_with_index do |method_map, col|
|
70
|
+
|
71
|
+
value = @excel.value(data_row, col)
|
72
|
+
|
73
|
+
# Excel num_rows seems to return all 'visible' rows so,
|
74
|
+
# we have to manually detect when actual data ends, this isn't very smart but
|
75
|
+
# currently got no better idea than ending once we hit the first completely empty row
|
76
|
+
|
77
|
+
contains_data = true if(value.to_s.empty?)
|
78
|
+
|
79
|
+
puts "VALUE #{value.class}"
|
80
|
+
puts "VALUE #{value} #{value.inspect}"
|
81
|
+
|
82
|
+
loader.process(method_map, @excel.value(data_row, col))
|
83
|
+
|
84
|
+
begin
|
85
|
+
loader.load_object.save if( loader.load_object.valid? && loader.load_object.new_record? )
|
86
|
+
rescue
|
87
|
+
raise "Error processing row"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
break unless contains_data
|
92
|
+
|
93
|
+
loaded_object = loader.load_object
|
94
|
+
|
95
|
+
# TODO - handle when it's not valid ?
|
96
|
+
# Process rest and dump out an exception list of Products
|
97
|
+
#unless(product.valid?)
|
98
|
+
#end
|
99
|
+
|
100
|
+
puts "SAVING ROW #{row} : #{loaded_object.inspect}" if args[:verbose]
|
101
|
+
|
102
|
+
unless(loaded_object.save)
|
103
|
+
puts loaded_object.errors.inspect
|
104
|
+
puts loaded_object.errors.full_messages.inspect
|
105
|
+
raise "Error Saving : #{loaded_object.inspect}"
|
106
|
+
else
|
107
|
+
@loaded_objects << loaded_object
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end # TRANSACTION
|
111
|
+
|
112
|
+
end
|
113
|
+
|
101
114
|
end
|
data/tasks/file_tasks.rake
CHANGED
@@ -1,38 +1,38 @@
|
|
1
|
-
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
-
# Author :: Tom Statter
|
3
|
-
# Date :: Feb 2011
|
4
|
-
# License:: MIT. Free, Open Source.
|
5
|
-
#
|
6
|
-
# Usage:: rake autotelik:file_rename input=/blah image_load input=path_to_images
|
7
|
-
#
|
8
|
-
|
9
|
-
namespace :autotelik do
|
10
|
-
|
11
|
-
desc "copy or mv a folder of files, consistently renaming in the process"
|
12
|
-
task :file_rename, :input, :offset, :prefix, :width, :commit, :mv do |t, args|
|
13
|
-
raise "USAGE: rake file_rename input='C:\blah' [offset=n prefix='str' width=n]" unless args[:input] && File.exists?(args[:input])
|
14
|
-
width = args[:width] || 2
|
15
|
-
|
16
|
-
action = args[:mv] ? 'mv' : 'cp'
|
17
|
-
|
18
|
-
cache = args[:input]
|
19
|
-
|
20
|
-
if(File.exists?(cache) )
|
21
|
-
puts "Renaming files from #{cache}"
|
22
|
-
Dir.glob(File.join(cache, "*")) do |name|
|
23
|
-
path, base_name = File.split(name)
|
24
|
-
id = base_name.slice!(/\w+/)
|
25
|
-
|
26
|
-
id = id.to_i + args[:offset].to_i if(args[:offset])
|
27
|
-
id = "%0#{width}d" % id.to_i if(args[:width])
|
28
|
-
id = args[:prefix] + id.to_s if(args[:prefix])
|
29
|
-
|
30
|
-
destination = File.join(path, "#{id}#{base_name}")
|
31
|
-
puts "ACTION: #{action} #{name} #{destination}"
|
32
|
-
|
33
|
-
File.send( action, name, destination) if args[:commit]
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
1
|
+
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
+
# Author :: Tom Statter
|
3
|
+
# Date :: Feb 2011
|
4
|
+
# License:: MIT. Free, Open Source.
|
5
|
+
#
|
6
|
+
# Usage:: rake autotelik:file_rename input=/blah image_load input=path_to_images
|
7
|
+
#
|
8
|
+
|
9
|
+
namespace :autotelik do
|
10
|
+
|
11
|
+
desc "copy or mv a folder of files, consistently renaming in the process"
|
12
|
+
task :file_rename, :input, :offset, :prefix, :width, :commit, :mv do |t, args|
|
13
|
+
raise "USAGE: rake file_rename input='C:\blah' [offset=n prefix='str' width=n]" unless args[:input] && File.exists?(args[:input])
|
14
|
+
width = args[:width] || 2
|
15
|
+
|
16
|
+
action = args[:mv] ? 'mv' : 'cp'
|
17
|
+
|
18
|
+
cache = args[:input]
|
19
|
+
|
20
|
+
if(File.exists?(cache) )
|
21
|
+
puts "Renaming files from #{cache}"
|
22
|
+
Dir.glob(File.join(cache, "*")) do |name|
|
23
|
+
path, base_name = File.split(name)
|
24
|
+
id = base_name.slice!(/\w+/)
|
25
|
+
|
26
|
+
id = id.to_i + args[:offset].to_i if(args[:offset])
|
27
|
+
id = "%0#{width}d" % id.to_i if(args[:width])
|
28
|
+
id = args[:prefix] + id.to_s if(args[:prefix])
|
29
|
+
|
30
|
+
destination = File.join(path, "#{id}#{base_name}")
|
31
|
+
puts "ACTION: #{action} #{name} #{destination}"
|
32
|
+
|
33
|
+
File.send( action, name, destination) if args[:commit]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
38
|
end
|
data/tasks/spree/image_load.rake
CHANGED
@@ -1,103 +1,108 @@
|
|
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, :
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
puts "
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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, :model, :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
|
+
attachment_klazz = Product
|
33
|
+
|
34
|
+
begin
|
35
|
+
attachment_klazz = Kernel.const_get(args[:model]) if(args[:model])
|
36
|
+
rescue NameError
|
37
|
+
attachment_klazz = Product
|
38
|
+
end
|
39
|
+
|
40
|
+
image_loader = ImageLoader.new
|
41
|
+
|
42
|
+
if(File.exists? @image_cache )
|
43
|
+
puts "Loading images from #{@image_cache}"
|
44
|
+
|
45
|
+
missing_records = []
|
46
|
+
Dir.glob("#{@image_cache}/*.{jpg,png,gif}") do |image_name|
|
47
|
+
|
48
|
+
puts "Processing #{image_name} : #{File.exists?(image_name)}"
|
49
|
+
base_name = File.basename(image_name, '.*')
|
50
|
+
|
51
|
+
record = nil
|
52
|
+
if(attachment_klazz == Product && args[:sku])
|
53
|
+
sku = base_name.slice!(/\w+/)
|
54
|
+
sku.strip!
|
55
|
+
base_name.strip!
|
56
|
+
|
57
|
+
puts "Looking fo SKU #{sku}"
|
58
|
+
record = Variant.find_by_sku(sku)
|
59
|
+
if record
|
60
|
+
record = record.product # SKU stored on Variant but we want it's master Product
|
61
|
+
else
|
62
|
+
puts "Looking for NAME [#{base_name}]"
|
63
|
+
record = attachment_klazz.find_by_name(base_name)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
puts "Looking for #{attachment_klazz.name} with NAME [#{base_name}]"
|
67
|
+
record = attachment_klazz.find_by_name(base_name)
|
68
|
+
end
|
69
|
+
|
70
|
+
if(record)
|
71
|
+
puts "Found record for attachment : #{record.inspect}"
|
72
|
+
exists = record.images.detect {|i| puts "COMPARE #{i.attachment_file_name} => #{image_name}"; i.attachment_file_name == image_name }
|
73
|
+
puts "Found existing attachments [#{exists}]" unless(exists.nil?)
|
74
|
+
if(args[:skip_if_loaded] && !exists.nil?)
|
75
|
+
puts "Skipping - Image #{image_name} already loaded for #{attachment_klazz}"
|
76
|
+
next
|
77
|
+
end
|
78
|
+
else
|
79
|
+
missing_records << image_name
|
80
|
+
end
|
81
|
+
|
82
|
+
# Now do actual upload to DB unless we are doing a dummy run,
|
83
|
+
# or the Image must have an associated record
|
84
|
+
unless(args[:dummy] == 'true' || (args[:skip_if_no_assoc] && record.nil?))
|
85
|
+
image_loader.reset()
|
86
|
+
puts "Process Image"
|
87
|
+
image_loader.process( image_name, record )
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
unless missing_records.empty?
|
93
|
+
FileUtils.mkdir_p('MissingRecords') unless File.directory?('MissingRecords')
|
94
|
+
|
95
|
+
puts '\nMISSING Records Report>>'
|
96
|
+
missing_records.each do |i|
|
97
|
+
puts "Copy #{i} to MissingRecords folder"
|
98
|
+
FileUtils.cp( i, 'MissingRecords') unless(args[:dummy] == 'true')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
else
|
103
|
+
puts "ERROR: Supplied Path #{@image_cache} not accesible"
|
104
|
+
exit(-1)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
103
108
|
end
|