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.
@@ -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 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 AR model's table with data stored in Excel"
16
- task :excel_load, :klass, :input, :verbose, :sku_prefix, :needs => :environment do |t, args|
17
-
18
- raise "USAGE: jruby -S rake excel_load input=excel_file.xls" unless args[:input]
19
- raise "ERROR: Cannot process without AR Model - please supply model=<Class>" unless args[:class]
20
- raise "ERROR: Could not find file #{args[:input]}" unless File.exists?(args[:input])
21
-
22
- klass = Kernal.const_get(args[:model])
23
- raise "ERROR: No such AR Model found - please check model=<Class>" unless(klass)
24
-
25
- require 'product_loader'
26
- require 'method_mapper_excel'
27
-
28
- args[:class]
29
-
30
- @method_mapper = MethodMapperExcel.new(args[:input], Product)
31
-
32
- @excel = @method_mapper.excel
33
-
34
- if(args[:verbose])
35
- puts "Loading from Excel file: #{args[:input]}"
36
- puts "Processing #{@excel.num_rows} rows"
37
- end
38
-
39
- # TODO create YAML configuration file to drive mandatory columns
40
- #
41
- # TODO create YAML configuration file to drive defaults etc
42
-
43
- # Process spreadsheet and create model instances
44
-
45
- method_names = @method_mapper.method_names
46
-
47
- Product.transaction do
48
- @products = []
49
-
50
- (1..@excel.num_rows).collect do |row|
51
-
52
- product_data_row = @excel.sheet.getRow(row)
53
- break if product_data_row.nil?
54
-
55
- # Excel num_rows seems to return all 'visible' rows so,
56
- # we have to manually detect when actual data ends and all the empty rows start
57
- contains_data = required_methods.find { |mthd| ! product_data_row.getCell(method_names.index(mthd)).to_s.empty? }
58
- break unless contains_data
59
-
60
- @assoc_classes = {}
61
-
62
- loader = ProductLoader.new()
63
-
64
- # TODO - Smart sorting of column processing order ....
65
- # Does not currently ensure mandatory columns (for valid?) processed first but model needs saving
66
- # before associations can be processed so user should ensure mandatory columns are prior to associations
67
-
68
- @method_mapper.methods.each_with_index do |method_map, col|
69
-
70
- loader.process(method_map, @excel.value(product_data_row, col))
71
- begin
72
- loader.load_object.save if( loader.load_object.valid? && loader.load_object.new_record? )
73
- rescue
74
- raise "Error processing Product"
75
- end
76
- end
77
-
78
- product = loader.load_object
79
-
80
- product.available_on ||= Time.now.to_s(:db)
81
-
82
- # TODO - handle when it's not valid ?
83
- # Process rest and dump out an exception list of Products
84
- #unless(product.valid?)
85
- #end
86
-
87
- puts "SAVING ROW #{row} : #{product.inspect}" if args[:verbose]
88
-
89
- unless(product.save)
90
- puts product.errors.inspect
91
- puts product.errors.full_messages.inspect
92
- raise "Error Saving Product: #{product.sku} :#{product.name}"
93
- else
94
- @products << product
95
- end
96
- end
97
- end # TRANSACTION
98
-
99
- end
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
@@ -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
@@ -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, :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
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