ar_loader 0.0.4 → 0.0.6
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/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
|