jcangas-datagateway 1.2.2 → 1.4.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.
- data/VERSION.yml +2 -2
- data/lib/datagateway/conduits/csv_conduit.rb +0 -0
- data/lib/datagateway/conduits/yaml_conduit.rb +0 -0
- data/lib/datagateway/data_gateway.rb +27 -514
- data/lib/datagateway/dbchange_point.rb +0 -0
- data/lib/datagateway/encoder.rb +27 -0
- data/lib/datagateway/folder_writer.rb +0 -0
- data/lib/datagateway/job.rb +223 -0
- data/lib/datagateway/jobs/nexus_export.rb +7 -6
- data/lib/datagateway/jobs/nexus_import.rb +1 -4
- data/lib/datagateway/jobs/shopin_export.rb +9 -6
- data/lib/datagateway/jobs/shopin_import.rb +9 -8
- data/lib/datagateway/mapper.rb +226 -0
- data/lib/datagateway/nexus/nax_ar.rb +0 -0
- data/lib/datagateway/nexus/nax_tlb.rb +0 -0
- data/lib/datagateway/nexus/nexus_ar.rb +0 -0
- data/lib/datagateway/ssh_transfer.rb +0 -0
- data/lib/datagateway.rb +111 -77
- data/lib/version.rb +0 -0
- data/template-prj/{README → nexus/README} +0 -0
- data/template-prj/{config → nexus/config}/README +0 -0
- data/template-prj/nexus/config/database.yml +17 -0
- data/template-prj/{config → nexus/config}/settings.yml +0 -2
- data/template-prj/{data → nexus/data}/donebox/README +0 -0
- data/template-prj/{data → nexus/data}/inbox/README +0 -0
- data/template-prj/{data → nexus/data}/outbox/README +0 -0
- data/template-prj/nexus/export-data.rb +9 -0
- data/template-prj/nexus/import-data.rb +14 -0
- data/template-prj/{log → nexus/log}/README +0 -0
- data/template-prj/{ruby.exe → nexus/ruby.exe} +0 -0
- data/template-prj/nexus/sistema.ini +4 -0
- data/template-prj/shopin/README +2 -0
- data/template-prj/shopin/config/README +4 -0
- data/template-prj/shopin/config/database.yml +8 -0
- data/template-prj/shopin/config/settings.yml +13 -0
- data/template-prj/shopin/data/donebox/README +1 -0
- data/template-prj/shopin/data/inbox/README +1 -0
- data/template-prj/shopin/data/outbox/README +1 -0
- data/template-prj/shopin/export-data.rb +6 -0
- data/template-prj/shopin/import-data.rb +6 -0
- data/template-prj/shopin/log/README +2 -0
- data/test/datagateway_test.rb +0 -0
- data/test/test_helper.rb +0 -0
- metadata +41 -20
- data/template-prj/config/database.yml +0 -32
- data/template-prj/dgw-job.rb +0 -25
- data/template-prj/run.bat +0 -1
- data/template-prj/sistema.ini +0 -3
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
require 'encoder'
|
|
2
|
+
require 'mapper'
|
|
3
|
+
|
|
4
|
+
module DataGateway
|
|
5
|
+
class Job
|
|
6
|
+
attr_accessor :format_id
|
|
7
|
+
attr :name
|
|
8
|
+
|
|
9
|
+
def initialize(name, &block)
|
|
10
|
+
@name = name
|
|
11
|
+
@use_resources = DataGateway.use_resources
|
|
12
|
+
@encoder = NullEncoder.new
|
|
13
|
+
instance_eval(&block) if block_given?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def run
|
|
17
|
+
DataGateway.logger.info "running job #{name}"
|
|
18
|
+
do_run
|
|
19
|
+
DataGateway.logger.info "job #{name} finished"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
def use_resources(bool)
|
|
24
|
+
@use_resources = bool
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def encode(options)
|
|
28
|
+
@encoder = Encoder.new(options)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def pack(data)
|
|
32
|
+
DataGateway.conduit_for(self.format_id).pack(data)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def unpack(data)
|
|
36
|
+
DataGateway.conduit_for(format_id).unpack(data)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def dbchange_point
|
|
40
|
+
@dbchange_point ||= DBChangePoint.class_for(ActiveRecord::Base.connection).new
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Import < Job
|
|
45
|
+
def initialize(name, &block)
|
|
46
|
+
@mappers = {} #import mappers
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def do_run
|
|
51
|
+
new_last_time = Time.now
|
|
52
|
+
Dir.glob(File.join(DataGateway::inbox, '*.zip')).sort.each do |filename|
|
|
53
|
+
DataGateway.logger.info "Importing #{filename}"
|
|
54
|
+
import_file(filename)
|
|
55
|
+
FileUtils.mv(filename, DataGateway::donebox)
|
|
56
|
+
DataGateway.logger.info "Import done for #{filename}"
|
|
57
|
+
end
|
|
58
|
+
DataGateway.last_time['import'] = new_last_time
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def donebox
|
|
62
|
+
Dir.glob(File.join(DataGateway::donebox, '*.zip')).each do |filename|
|
|
63
|
+
FileUtils.rm(filename) if File.mtime(filename) < 7.days.ago
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def get_mapper(table)
|
|
68
|
+
@mappers[table] ||= TableMapper.new(self, table)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
public # DSL
|
|
72
|
+
|
|
73
|
+
def importing(table, &block)
|
|
74
|
+
get_mapper(table).instance_eval(&block) if block_given?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def import_file(file_name)
|
|
78
|
+
self.format_id = file_name
|
|
79
|
+
clear_fixups
|
|
80
|
+
begin
|
|
81
|
+
Zip::ZipInputStream::open(file_name) { |io|
|
|
82
|
+
while (entry = io.get_next_entry)
|
|
83
|
+
content = io.read
|
|
84
|
+
fname = entry.name
|
|
85
|
+
if fname =~ /^attach\//
|
|
86
|
+
fname = fname.gsub(/^attach\//, '')
|
|
87
|
+
import_attach(fname, content)
|
|
88
|
+
else
|
|
89
|
+
class_name = File.basename(fname, '.*').downcase.camelize.to_sym
|
|
90
|
+
import_class(class_name, content)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
}
|
|
94
|
+
ensure
|
|
95
|
+
solve_fixups
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def clear_fixups
|
|
100
|
+
@mappers.each_value {|mapper| mapper.clear_fixups }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def solve_fixups
|
|
104
|
+
@mappers.each_value {|mapper| mapper.solve_fixups }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def import_class(class_name, data)
|
|
108
|
+
DataGateway.logger.info "Importing data for #{class_name}"
|
|
109
|
+
mapper = get_mapper(class_name)
|
|
110
|
+
if mapper.ignore?
|
|
111
|
+
DataGateway.logger.info "Ignored"
|
|
112
|
+
return
|
|
113
|
+
end
|
|
114
|
+
mapper.update_all unpack(data)
|
|
115
|
+
#p dbchange_point.capture_for(klass)
|
|
116
|
+
DataGateway.logger.info "done for #{class_name}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def import_attach(fname, content)
|
|
120
|
+
return unless @use_resources
|
|
121
|
+
DataGateway.logger.info "importing attach #{fname}"
|
|
122
|
+
target_file = File.expand_path(fname, DataGateway.resource_path)
|
|
123
|
+
FileUtils.mkdir_p(File.dirname(target_file))
|
|
124
|
+
File.open(target_file, "wb") {|f| f.write content}
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
class Export < Job
|
|
129
|
+
def initialize(name, &block)
|
|
130
|
+
@exports = {}
|
|
131
|
+
@exports_order = []
|
|
132
|
+
export_to :csv #default format
|
|
133
|
+
super
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def each
|
|
137
|
+
tables = @exports_order
|
|
138
|
+
tables.each { |cname|
|
|
139
|
+
yield cname, export_class(cname)
|
|
140
|
+
}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def export_folder
|
|
144
|
+
@export_folder ||= create_folder
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def close_export_folder
|
|
148
|
+
@export_folder.close if @export_folder
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def do_run
|
|
152
|
+
new_last_time = Time.now
|
|
153
|
+
begin
|
|
154
|
+
self.each { |cname, records|
|
|
155
|
+
file_name = "#{cname}.#{self.format_id}"
|
|
156
|
+
DataGateway.logger.debug "Exporting archive #{file_name}"
|
|
157
|
+
write_file(file_name, records)
|
|
158
|
+
}
|
|
159
|
+
ensure
|
|
160
|
+
close_export_folder
|
|
161
|
+
end
|
|
162
|
+
DataGateway.last_time['export'] = new_last_time
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private # DSL
|
|
166
|
+
|
|
167
|
+
def exporting(table_name, options = {})
|
|
168
|
+
@exports_order << table_name
|
|
169
|
+
@exports[table_name] = options
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def export_to(format)
|
|
173
|
+
self.format_id = format
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
private
|
|
177
|
+
def create_folder(outbox = DataGateway::outbox)
|
|
178
|
+
FileUtils.mkpath(outbox)
|
|
179
|
+
stamp = Time.now.strftime("%Y%m%d%H%M%S")
|
|
180
|
+
folder_name = File.join(outbox, "#{stamp}.#{self.format_id}")
|
|
181
|
+
#(APP_ENV == 'development') ? FolderWriter.new(folder_name) :
|
|
182
|
+
ZipFolderWriter.new(folder_name)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def export_class(cname)
|
|
186
|
+
result = []
|
|
187
|
+
bm = Benchmark.measure {
|
|
188
|
+
klass = DataGateway.model_for(cname.to_s.downcase.camelize)
|
|
189
|
+
DataGateway.logger.info "Exporting #{klass} (#{self.format_id})"
|
|
190
|
+
records = klass.find(:all, @exports[cname])
|
|
191
|
+
records.each { |r| export_attachments(r) } if @use_resources
|
|
192
|
+
result = pack(records) unless records.empty?
|
|
193
|
+
}
|
|
194
|
+
DataGateway.logger.info "data exported in #{bm}"
|
|
195
|
+
result
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def export_attachments(record)
|
|
199
|
+
record.attributes.keys.select { |key| key =~ /^img/i }.each { |key|
|
|
200
|
+
file = record.attributes[key]
|
|
201
|
+
next if file.blank?
|
|
202
|
+
path = File.expand_path(file, DataGateway.resource_path)
|
|
203
|
+
#TODO: Si el fichero ya se exporto solo en este vuelta, no repetir!
|
|
204
|
+
next unless File.exist?(path)
|
|
205
|
+
modified = File.mtime(path) > DataGateway.last_time['export']
|
|
206
|
+
next unless modified
|
|
207
|
+
DataGateway.logger.info "exporting attach #{path}"
|
|
208
|
+
attach_data = File.open(path, 'rb') { |f| f.read }
|
|
209
|
+
write_file(File.join('attach', file), attach_data, 'b')
|
|
210
|
+
}
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def write_file(file_name, data, options = '')
|
|
214
|
+
op = StringIO.new("", "w")
|
|
215
|
+
op.puts data
|
|
216
|
+
if (options == 'b')
|
|
217
|
+
export_folder.open(file_name, "w" + options){|f| f.puts op.string}
|
|
218
|
+
else
|
|
219
|
+
export_folder.open(file_name, "w" + options){|f| f.puts @encoder.pack(op.string)}
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
@@ -5,19 +5,20 @@ db_connection AppConfig.connections[:nexusdb]
|
|
|
5
5
|
export :nexus_export do
|
|
6
6
|
encode( :to => "UTF-8" , :from => "WINDOWS-1252")
|
|
7
7
|
export_to :csv
|
|
8
|
-
exporting :user,
|
|
9
|
-
|
|
8
|
+
exporting :user,
|
|
9
|
+
:select => 'USERID AS LOGIN, CODCLI AS CODE, EMAIL',
|
|
10
|
+
:conditions => 'CODCLI IS NOT NULL'
|
|
11
|
+
exporting :category
|
|
10
12
|
exporting :family
|
|
11
13
|
|
|
12
14
|
exporting :news,
|
|
13
|
-
:select => 'DESCRIPTION AS TITLE, FROMDATE AS PUBLISHED_AT, TODATE AS EXPIRES_AT, PRIORITY, NEWSTEXT BODY'
|
|
14
|
-
exporting :product,
|
|
15
|
+
:select => 'ID AS CODE, DESCRIPTION AS TITLE, FROMDATE AS PUBLISHED_AT, TODATE AS EXPIRES_AT, PRIORITY, NEWSTEXT BODY'
|
|
16
|
+
exporting :product,
|
|
15
17
|
:select => 'CATEGORY_ID, FAMILY_ID, CODE, IMG_DETAIL, IMG_MINI, DETAIL, da.DESCART AS LABEL, PRCVENTA',
|
|
16
18
|
:joins => 'as p left outer join ARTICULO as ar on CODE = ar.CODART left outer join DESCRIPA as da on CODE = da.CODART',
|
|
17
19
|
:conditions => "da.codidioma = 'CAS'"
|
|
18
|
-
|
|
20
|
+
|
|
19
21
|
exporting :tarifa_venta,
|
|
20
22
|
:select => 'IDTARIFAV, CODART, FECMAX, PRECIO, TARIFA, UNIDADES',
|
|
21
23
|
:joins => 'as tarf inner join mmproducts p on code = tarf.codart'
|
|
22
24
|
end
|
|
23
|
-
|
|
@@ -7,10 +7,7 @@ import(:nexus_import) do
|
|
|
7
7
|
importing :User do
|
|
8
8
|
force_upcase
|
|
9
9
|
identity_by :USERID
|
|
10
|
-
columns :full_name => none
|
|
11
|
-
#:userid => write_to(:USERID),
|
|
12
|
-
#:email => :EMAIL,
|
|
13
|
-
|
|
10
|
+
columns :full_name => none
|
|
14
11
|
end
|
|
15
12
|
|
|
16
13
|
##FIX: no puede ser LineItem pq hay varios documentos -> LineOrder
|
|
@@ -3,14 +3,17 @@ db_connection AppConfig.connections[:shopindb]
|
|
|
3
3
|
|
|
4
4
|
export 'shopin_export' do
|
|
5
5
|
exporting :user,
|
|
6
|
-
:select =>
|
|
6
|
+
:select => "login as userid, email, ' 1' as codcli",
|
|
7
7
|
:conditions => 'activated_at is not null'
|
|
8
8
|
|
|
9
9
|
exporting :order,
|
|
10
10
|
:select => 'number, reference, special_instructions, login as userid',
|
|
11
|
-
:joins => 'as ord left outer join users as usr on ord.user_id = usr.id'
|
|
12
|
-
|
|
11
|
+
:joins => 'as ord left outer join users as usr on ord.user_id = usr.id',
|
|
12
|
+
:conditions => "(ord.updated_at > '#{last_time(:export)}')"
|
|
13
|
+
|
|
13
14
|
exporting :line_items,
|
|
14
|
-
|
|
15
|
-
:joins => 'as line left outer join orders as ord on line.order_id = ord.id'
|
|
16
|
-
|
|
15
|
+
:select => 'line.id as id, number as order_id, price, product_id, quantity',
|
|
16
|
+
:joins => 'as line left outer join orders as ord on line.order_id = ord.id',
|
|
17
|
+
:conditions => "(ord.updated_at > '#{last_time(:export)}')"
|
|
18
|
+
|
|
19
|
+
end
|
|
@@ -18,16 +18,17 @@ import 'shopin_import' do
|
|
|
18
18
|
columns :DESCRIPTION => :name,
|
|
19
19
|
:DBREV => none
|
|
20
20
|
end
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
importing :News do
|
|
23
|
+
identity_by :code
|
|
24
|
+
force_downcase
|
|
23
25
|
target :Post
|
|
24
|
-
force_downcase
|
|
25
26
|
end
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
importing :User do
|
|
28
|
-
|
|
29
|
+
identity_by :login
|
|
29
30
|
end
|
|
30
|
-
|
|
31
|
+
|
|
31
32
|
importing :Product do
|
|
32
33
|
identity_by :code
|
|
33
34
|
force_downcase
|
|
@@ -35,7 +36,7 @@ import 'shopin_import' do
|
|
|
35
36
|
:DETAIL => :description,
|
|
36
37
|
:PRCVENTA => :price
|
|
37
38
|
end
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
importing :TarifaVenta do
|
|
40
41
|
target :PriceList
|
|
41
42
|
identity_by :code
|
|
@@ -45,6 +46,6 @@ import 'shopin_import' do
|
|
|
45
46
|
:FECMAX => :expires_at,
|
|
46
47
|
:PRECIO => :price,
|
|
47
48
|
:TARIFA => :group,
|
|
48
|
-
:UNIDADES => :quantity
|
|
49
|
+
:UNIDADES => :quantity
|
|
49
50
|
end
|
|
50
|
-
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
module DataGateway
|
|
2
|
+
class ColumnMapper
|
|
3
|
+
attr :options
|
|
4
|
+
def initialize(table_mapper, options = {})
|
|
5
|
+
@table_mapper = table_mapper
|
|
6
|
+
@options = options
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def apply_to(record, value)
|
|
10
|
+
DataGateway.logger.debug "apply_to(#{record.inspect}, #{value}) options #{@options.inspect}"
|
|
11
|
+
send(@options[:method], record, value)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# value mappers
|
|
15
|
+
def none(record, value)
|
|
16
|
+
# do nothing
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def write_to(record, value)
|
|
20
|
+
record[@options[:write_to]] = value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def lookup(record, value)
|
|
24
|
+
|
|
25
|
+
result_status = {}
|
|
26
|
+
new_value = lookup_map(value, result_status)
|
|
27
|
+
if result_status[:found]
|
|
28
|
+
write_to(record, new_value)
|
|
29
|
+
else
|
|
30
|
+
@table_mapper.register_fixup(value, self)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
def lookup_map(value, result_status)
|
|
36
|
+
DataGateway.logger.debug "lookup_map(#{value}, #{result_status}) options #{@options.inspect}"
|
|
37
|
+
lookup_model = DataGateway.model_for(@options[:table])
|
|
38
|
+
finder = "find_or_create_by_#{@options[:key]}"
|
|
39
|
+
lookup_rec = lookup_model.send(finder, value)
|
|
40
|
+
result_status[:found] = !(lookup_rec.nil?)
|
|
41
|
+
if result_status[:found]
|
|
42
|
+
lookup_rec.send(@options[:value]).to_s # valor mapeado
|
|
43
|
+
else
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def solve_fixup(record, value)
|
|
49
|
+
DataGateway.logger.debug "mapper #{@map_method} opt. #{@options.inspect}"
|
|
50
|
+
result_status = {}
|
|
51
|
+
DataGateway.logger.debug "lookup #{record.inspect} -> <#{value}>"
|
|
52
|
+
value = lookup_map(value, result_status)
|
|
53
|
+
if result_status[:found]
|
|
54
|
+
DataGateway.logger.debug "FOUND: #{value}"
|
|
55
|
+
write_to(record, value)
|
|
56
|
+
else
|
|
57
|
+
DataGateway.logger.debug "NOT FOUND!"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class TableMapper
|
|
63
|
+
attr :source
|
|
64
|
+
attr :identity
|
|
65
|
+
|
|
66
|
+
def initialize(job, table)
|
|
67
|
+
@job = job
|
|
68
|
+
@source = table
|
|
69
|
+
@target = table
|
|
70
|
+
@column_map = {}
|
|
71
|
+
@identity = [:id]
|
|
72
|
+
@ar_fixups = []
|
|
73
|
+
@force_case = false
|
|
74
|
+
clear_fixups
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def ignore!
|
|
78
|
+
@target = nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def ignore?
|
|
82
|
+
@target == nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def target(table_name = nil)
|
|
86
|
+
@target = table_name if table_name
|
|
87
|
+
@target
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def force_downcase
|
|
91
|
+
@force_case = :downcase
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def force_upcase
|
|
95
|
+
@force_case = :upcase
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def target_model
|
|
99
|
+
DataGateway.model_for(@target)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# DSL syntax sugar for declare column mappers
|
|
103
|
+
def columns(column_map)
|
|
104
|
+
column_map.each_pair do |key, value|
|
|
105
|
+
case value
|
|
106
|
+
when Symbol, String: column_map[key] = write_to(value.to_sym)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
@column_map.merge! column_map
|
|
110
|
+
|
|
111
|
+
@column_map.each_pair do |key, value|
|
|
112
|
+
defaults = column_mapper_defaults(key)
|
|
113
|
+
@column_map[key].options.replace(defaults.merge!(@column_map[key].options))
|
|
114
|
+
#puts "map for #{key} => #{@column_map[key].options.inspect}"
|
|
115
|
+
end
|
|
116
|
+
@column_map
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def none
|
|
120
|
+
create_column_mapper({:method => :none})
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def write_to(column)
|
|
124
|
+
create_column_mapper({:method => :write_to, :write_to => column})
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def lookup(options)
|
|
128
|
+
options[:method] = :lookup
|
|
129
|
+
create_column_mapper(options)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def identity_by(*cols)
|
|
133
|
+
@identity = cols
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# utilities
|
|
137
|
+
def column_mapper_for(column_name)
|
|
138
|
+
@column_map[column_name] ||= create_column_mapper(column_mapper_defaults(column_name))
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def column_mapper_defaults(column_name)
|
|
142
|
+
column_name = column_name.to_s.send(@force_case).to_sym if @force_case
|
|
143
|
+
case column_name.to_s
|
|
144
|
+
when /(.*)_id$/i: { :method => :lookup,
|
|
145
|
+
:write_to => column_name,
|
|
146
|
+
:table => $1.downcase.camelize.to_sym,
|
|
147
|
+
:key => @job.get_mapper($1.downcase.camelize.to_sym).identity,
|
|
148
|
+
:value => :id
|
|
149
|
+
}
|
|
150
|
+
else { :method => :write_to,
|
|
151
|
+
:write_to => column_name
|
|
152
|
+
}
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def update_all(records)
|
|
158
|
+
DataGateway.logger.debug "importing #{records.size} recods into table #{source}"
|
|
159
|
+
klass = target_model
|
|
160
|
+
DataGateway.logger.debug "target class #{klass} (PK: #{klass.primary_key})"
|
|
161
|
+
records.each { |rec|
|
|
162
|
+
rec = map(rec)
|
|
163
|
+
update_rec(rec) unless current_fixups(rec)
|
|
164
|
+
}
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def clear_fixups
|
|
168
|
+
@fixups = {}
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def register_fixup(value, col_mapper)
|
|
172
|
+
@ar_fixups << [value, col_mapper]
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def current_fixups(rec)
|
|
176
|
+
DataGateway.logger.debug "current fixups for: #{rec.inspect}"
|
|
177
|
+
DataGateway.logger.debug @ar_fixups.inspect
|
|
178
|
+
return false if @ar_fixups.empty?
|
|
179
|
+
@fixups[rec] ||= []
|
|
180
|
+
@fixups[rec].concat @ar_fixups
|
|
181
|
+
@ar_fixups = []
|
|
182
|
+
true
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def solve_fixups
|
|
186
|
+
DataGateway.logger.debug "start solve_fixups #{@fixups.size}"
|
|
187
|
+
@fixups.each_pair { |record, data|
|
|
188
|
+
data.each{|item|
|
|
189
|
+
value = item[0]
|
|
190
|
+
col_mapper = item[1]
|
|
191
|
+
col_mapper.solve_fixup(record, value)
|
|
192
|
+
}
|
|
193
|
+
update_rec(record)
|
|
194
|
+
}
|
|
195
|
+
DataGateway.logger.debug "end solve_fixups"
|
|
196
|
+
ensure
|
|
197
|
+
clear_fixups
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def update_rec(rec)
|
|
201
|
+
DataGateway.logger.debug "update rec class #{target_model} '->' #{rec.inspect}"
|
|
202
|
+
conditions = identity.map{|key| key.to_s}
|
|
203
|
+
values = conditions.map{|key| rec.delete(key.to_sym)}
|
|
204
|
+
finder = 'find_or_create_by_' + conditions.join('_')
|
|
205
|
+
DataGateway.logger.debug "finder: #{finder}(#{values.join(',')})"
|
|
206
|
+
|
|
207
|
+
ar = target_model.send(finder, *values)
|
|
208
|
+
DataGateway.logger.debug "get: #{ar.inspect}"
|
|
209
|
+
DataGateway.logger.debug "upd atrt: #{rec.inspect}"
|
|
210
|
+
ar.update_attributes(rec)
|
|
211
|
+
return ar[:id]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def map(ar)
|
|
215
|
+
new_ar = {}
|
|
216
|
+
ar.each_pair do |key, value|
|
|
217
|
+
column_mapper_for(key).apply_to(new_ar, value)
|
|
218
|
+
end
|
|
219
|
+
new_ar
|
|
220
|
+
end
|
|
221
|
+
private
|
|
222
|
+
def create_column_mapper( options)
|
|
223
|
+
ColumnMapper.new(self, options)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|