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