caboose-rets 0.1.18 → 0.1.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/caboose_rets/admin_media.js +7 -7
  3. data/app/assets/javascripts/caboose_rets/caboose_rets.js +8 -8
  4. data/app/controllers/caboose_rets/agents_controller.rb +67 -75
  5. data/app/controllers/caboose_rets/offices_controller.rb +4 -4
  6. data/app/controllers/caboose_rets/open_houses_controller.rb +3 -3
  7. data/app/controllers/caboose_rets/properties_controller.rb +160 -0
  8. data/app/controllers/caboose_rets/rets_controller.rb +2 -2
  9. data/app/controllers/caboose_rets/rets_media_controller.rb +13 -13
  10. data/app/controllers/caboose_rets/saved_properties_controller.rb +35 -35
  11. data/app/models/caboose_rets/agent.rb +42 -55
  12. data/app/models/caboose_rets/agent_meta.rb +7 -7
  13. data/app/models/caboose_rets/media.rb +63 -57
  14. data/app/models/caboose_rets/office.rb +35 -21
  15. data/app/models/caboose_rets/open_house.rb +15 -15
  16. data/app/models/caboose_rets/property.rb +203 -0
  17. data/app/models/caboose_rets/rets_importer.rb +193 -233
  18. data/app/models/caboose_rets/rets_importer_bak.rb +77 -77
  19. data/app/models/caboose_rets/rets_importer_old.rb +625 -0
  20. data/app/models/caboose_rets/saved_property.rb +4 -4
  21. data/app/models/caboose_rets/saved_search.rb +36 -33
  22. data/app/models/caboose_rets/schema.rb +324 -855
  23. data/app/models/caboose_rets/schema_past.rb +918 -0
  24. data/app/models/caboose_rets/search_option.rb +6 -6
  25. data/app/views/caboose/blocks/_layout_rets.html.erb +9 -6
  26. data/app/views/caboose/blocks/_rets_agent_listings.html.erb +3 -3
  27. data/app/views/caboose_rets/agents/index.html.erb +23 -40
  28. data/app/views/caboose_rets/media/admin_property_media.html.erb +2 -2
  29. data/app/views/caboose_rets/{residential → properties}/_search_form.html.erb +42 -42
  30. data/app/views/caboose_rets/{residential → properties}/admin_edit.html.erb +7 -7
  31. data/app/views/caboose_rets/{land → properties}/admin_index.html.erb +11 -11
  32. data/app/views/caboose_rets/properties/details.html.erb +127 -0
  33. data/app/views/caboose_rets/{land → properties}/index.html.erb +0 -0
  34. data/app/views/caboose_rets/{residential/residential_not_exists.html.erb → properties/property_not_exists.html.erb} +4 -5
  35. data/app/views/caboose_rets/{residential → properties}/test_form.html.erb +1 -1
  36. data/app/views/caboose_rets/rets/admin_import_form.html.erb +1 -1
  37. data/config/routes.rb +70 -55
  38. data/lib/caboose-rets.rb +1 -1
  39. data/lib/caboose_rets/engine.rb +3 -3
  40. data/lib/caboose_rets/version.rb +1 -1
  41. data/lib/rets/base/core.rb +1 -1
  42. data/lib/rets/base/sax_search.rb +1 -1
  43. data/lib/tasks/caboose_rets.rake +106 -17
  44. metadata +16 -50
  45. data/app/controllers/caboose_rets/commercial_controller.rb +0 -349
  46. data/app/controllers/caboose_rets/land_controller.rb +0 -144
  47. data/app/controllers/caboose_rets/multi_family_controller.rb +0 -107
  48. data/app/controllers/caboose_rets/residential_controller.rb +0 -163
  49. data/app/models/caboose_rets/commercial_property.rb +0 -214
  50. data/app/models/caboose_rets/land_property.rb +0 -144
  51. data/app/models/caboose_rets/multi_family_property.rb +0 -199
  52. data/app/models/caboose_rets/residential_property.rb +0 -236
  53. data/app/views/caboose/blocks/_rets_commercial_details.html.erb +0 -192
  54. data/app/views/caboose/blocks/_rets_commercial_headers.html.erb +0 -17
  55. data/app/views/caboose/blocks/_rets_commercial_index.html.erb +0 -71
  56. data/app/views/caboose/blocks/_rets_commercial_row.html.erb +0 -39
  57. data/app/views/caboose/blocks/_rets_commercial_search_form.html.erb +0 -201
  58. data/app/views/caboose/blocks/_rets_land_details.html.erb +0 -165
  59. data/app/views/caboose/blocks/_rets_land_index.html.erb +0 -63
  60. data/app/views/caboose/blocks/_rets_land_row.html.erb +0 -58
  61. data/app/views/caboose/blocks/_rets_land_search_form.html.erb +0 -194
  62. data/app/views/caboose/blocks/_rets_multifamily_details.html.erb +0 -161
  63. data/app/views/caboose/blocks/_rets_multifamily_index.html.erb +0 -63
  64. data/app/views/caboose/blocks/_rets_multifamily_row.html.erb +0 -56
  65. data/app/views/caboose/blocks/_rets_multifamily_search_form.html.erb +0 -273
  66. data/app/views/caboose/blocks/_rets_openhouse_details.html.erb +0 -12
  67. data/app/views/caboose/blocks/_rets_openhouses_index.html.erb +0 -79
  68. data/app/views/caboose/blocks/_rets_residential_details.html.erb +0 -243
  69. data/app/views/caboose/blocks/_rets_residential_index.html.erb +0 -65
  70. data/app/views/caboose/blocks/_rets_residential_row.html.erb +0 -59
  71. data/app/views/caboose/blocks/_rets_residential_search_form.html.erb +0 -304
  72. data/app/views/caboose_rets/commercial/admin_edit.html.erb +0 -269
  73. data/app/views/caboose_rets/commercial/admin_index.html.erb +0 -51
  74. data/app/views/caboose_rets/commercial/admin_new.html.erb +0 -57
  75. data/app/views/caboose_rets/commercial/details.html.erb +0 -0
  76. data/app/views/caboose_rets/commercial/index.html.erb +0 -87
  77. data/app/views/caboose_rets/land/admin_edit.html.erb +0 -156
  78. data/app/views/caboose_rets/land/details.html.erb +0 -0
  79. data/app/views/caboose_rets/multi_family/admin_edit.html.erb +0 -211
  80. data/app/views/caboose_rets/multi_family/admin_index.html.erb +0 -46
  81. data/app/views/caboose_rets/residential/admin_index.html.erb +0 -46
  82. data/app/views/caboose_rets/residential/details.html.erb +0 -0
  83. data/app/views/caboose_rets/residential/index.html.erb +0 -0
@@ -51,10 +51,10 @@
51
51
  #
52
52
  # def self.meta(class_type)
53
53
  # case class_type
54
- # when 'RES' then Caboose::StdClass.new({ :search_type => 'Property' , :remote_key_field => 'MLS_ACCT' , :local_key_field => 'mls_acct' , :local_table => 'rets_residential' , :date_modified_field => 'DATE_MODIFIED' })
55
- # when 'COM' then Caboose::StdClass.new({ :search_type => 'Property' , :remote_key_field => 'MLS_ACCT' , :local_key_field => 'mls_acct' , :local_table => 'rets_commercial' , :date_modified_field => 'DATE_MODIFIED' })
56
- # when 'LND' then Caboose::StdClass.new({ :search_type => 'Property' , :remote_key_field => 'MLS_ACCT' , :local_key_field => 'mls_acct' , :local_table => 'rets_land' , :date_modified_field => 'DATE_MODIFIED' })
57
- # when 'MUL' then Caboose::StdClass.new({ :search_type => 'Property' , :remote_key_field => 'MLS_ACCT' , :local_key_field => 'mls_acct' , :local_table => 'rets_multi_family' , :date_modified_field => 'DATE_MODIFIED' })
54
+ # when 'RES' then Caboose::StdClass.new({ :search_type => 'Property' , :remote_key_field => 'MLS' , :local_key_field => 'mls' , :local_table => 'rets_residential' , :date_modified_field => 'DATE_MODIFIED' })
55
+ # when 'COM' then Caboose::StdClass.new({ :search_type => 'Property' , :remote_key_field => 'MLS' , :local_key_field => 'mls' , :local_table => 'rets_commercial' , :date_modified_field => 'DATE_MODIFIED' })
56
+ # when 'LND' then Caboose::StdClass.new({ :search_type => 'Property' , :remote_key_field => 'MLS' , :local_key_field => 'mls' , :local_table => 'rets_land' , :date_modified_field => 'DATE_MODIFIED' })
57
+ # when 'MUL' then Caboose::StdClass.new({ :search_type => 'Property' , :remote_key_field => 'MLS' , :local_key_field => 'mls' , :local_table => 'rets_multi_family' , :date_modified_field => 'DATE_MODIFIED' })
58
58
  # when 'OFF' then Caboose::StdClass.new({ :search_type => 'Office' , :remote_key_field => 'LO_LO_CODE' , :local_key_field => 'lo_code' , :local_table => 'rets_offices' , :date_modified_field => 'LO_DATE_MODIFIED' })
59
59
  # when 'AGT' then Caboose::StdClass.new({ :search_type => 'Agent' , :remote_key_field => 'LA_LA_CODE' , :local_key_field => 'la_code' , :local_table => 'rets_agents' , :date_modified_field => 'LA_DATE_MODIFIED' })
60
60
  # when 'OPH' then Caboose::StdClass.new({ :search_type => 'OpenHouse' , :remote_key_field => 'ID' , :local_key_field => 'id' , :local_table => 'rets_open_houses' , :date_modified_field => 'DATE_MODIFIED' })
@@ -66,7 +66,7 @@
66
66
  # # Import method
67
67
  # #=============================================================================
68
68
  #
69
- # def self.import(class_type, query)
69
+ # def self.import(class_type, query)
70
70
  # m = self.meta(class_type)
71
71
  # self.log("Importing #{m.search_type}:#{class_type} with query #{query}...")
72
72
  # self.get_config if @@config.nil? || @@config['url'].nil?
@@ -78,7 +78,7 @@
78
78
  # :timeout => -1
79
79
  # }
80
80
  # obj = nil
81
- # begin
81
+ # begin
82
82
  # self.client.search(params) do |data|
83
83
  # obj = self.get_instance_with_id(class_type, data)
84
84
  # if obj.nil?
@@ -93,7 +93,7 @@
93
93
  # self.log "Import error for #{class_type}: #{query}"
94
94
  # self.log err.message
95
95
  # end
96
- #
96
+ #
97
97
  # end
98
98
  #
99
99
  # def self.get_instance_with_id(class_type, data)
@@ -111,10 +111,10 @@
111
111
  # obj = case class_type
112
112
  # when 'OPH' then m.where(:id => data['ID'].to_i ).exists? ? m.where(:id => data['ID'].to_i ).first : m.new(:id => data['ID'].to_i )
113
113
  # when 'GFX' then m.where(:media_id => data['MEDIA_ID'] ).exists? ? m.where(:media_id => data['MEDIA_ID'] ).first : m.new(:media_id => data['MEDIA_ID'] )
114
- # when 'COM' then m.where(:id => data['MLS_ACCT'].to_i ).exists? ? m.where(:id => data['MLS_ACCT'].to_i ).first : m.new(:id => data['MLS_ACCT'].to_i )
115
- # when 'LND' then m.where(:id => data['MLS_ACCT'].to_i ).exists? ? m.where(:id => data['MLS_ACCT'].to_i ).first : m.new(:id => data['MLS_ACCT'].to_i )
116
- # when 'MUL' then m.where(:id => data['MLS_ACCT'].to_i ).exists? ? m.where(:id => data['MLS_ACCT'].to_i ).first : m.new(:id => data['MLS_ACCT'].to_i )
117
- # when 'RES' then m.where(:id => data['MLS_ACCT'].to_i ).exists? ? m.where(:id => data['MLS_ACCT'].to_i ).first : m.new(:id => data['MLS_ACCT'].to_i )
114
+ # when 'COM' then m.where(:id => data['MLS'].to_i ).exists? ? m.where(:id => data['MLS'].to_i ).first : m.new(:id => data['MLS'].to_i )
115
+ # when 'LND' then m.where(:id => data['MLS'].to_i ).exists? ? m.where(:id => data['MLS'].to_i ).first : m.new(:id => data['MLS'].to_i )
116
+ # when 'MUL' then m.where(:id => data['MLS'].to_i ).exists? ? m.where(:id => data['MLS'].to_i ).first : m.new(:id => data['MLS'].to_i )
117
+ # when 'RES' then m.where(:id => data['MLS'].to_i ).exists? ? m.where(:id => data['MLS'].to_i ).first : m.new(:id => data['MLS'].to_i )
118
118
  # when 'AGT' then m.where(:la_code => data['LA_LA_CODE'] ).exists? ? m.where(:la_code => data['LA_LA_CODE'] ).first : m.new(:la_code => data['LA_LA_CODE'] )
119
119
  # when 'OFF' then m.where(:lo_code => data['LO_LO_CODE'] ).exists? ? m.where(:lo_code => data['LO_LO_CODE'] ).first : m.new(:lo_code => data['LO_LO_CODE'] )
120
120
  # end
@@ -163,18 +163,18 @@
163
163
  # # Single model import methods (called from a worker dyno)
164
164
  # #=============================================================================
165
165
  #
166
- # def self.import_property(mls_acct, save_images = true)
167
- # self.import('RES', "(MLS_ACCT=*#{mls_acct}*)")
168
- # p = CabooseRets::ResidentialProperty.where(:id => mls_acct.to_i).first
166
+ # def self.import_property(mls, save_images = true)
167
+ # self.import('RES', "(MLS=*#{mls}*)")
168
+ # p = CabooseRets::ResidentialProperty.where(:id => mls.to_i).first
169
169
  # if p.nil?
170
- # self.import('COM', "(MLS_ACCT=*#{mls_acct}*)")
171
- # p = CabooseRets::CommercialProperty.where(:id => mls_acct.to_i).first
170
+ # self.import('COM', "(MLS=*#{mls}*)")
171
+ # p = CabooseRets::CommercialProperty.where(:id => mls.to_i).first
172
172
  # if p.nil?
173
- # self.import('LND', "(MLS_ACCT=*#{mls_acct}*)")
174
- # p = CabooseRets::LandProperty.where(:id => mls_acct.to_i).first
173
+ # self.import('LND', "(MLS=*#{mls}*)")
174
+ # p = CabooseRets::LandProperty.where(:id => mls.to_i).first
175
175
  # if p.nil?
176
- # self.import('MUL', "(MLS_ACCT=*#{mls_acct}*)")
177
- # p = CabooseRets::MultiFamilyProperty.where(:id => mls_acct.to_i).first
176
+ # self.import('MUL', "(MLS=*#{mls}*)")
177
+ # p = CabooseRets::MultiFamilyProperty.where(:id => mls.to_i).first
178
178
  # return if p.nil?
179
179
  # end
180
180
  # end
@@ -182,35 +182,35 @@
182
182
  # self.download_property_images(p, save_images)
183
183
  # end
184
184
  #
185
- # def self.import_residential_property(mls_acct, save_images = true)
186
- # self.import('RES', "(MLS_ACCT=*#{mls_acct}*)")
187
- # p = CabooseRets::ResidentialProperty.where(:id => mls_acct.to_i).first
185
+ # def self.import_residential_property(mls, save_images = true)
186
+ # self.import('RES', "(MLS=*#{mls}*)")
187
+ # p = CabooseRets::ResidentialProperty.where(:id => mls.to_i).first
188
188
  # self.download_property_images(p, save_images)
189
189
  # self.update_coords(p)
190
190
  # end
191
191
  #
192
- # def self.import_commercial_property(mls_acct, save_images = true)
193
- # self.import('COM', "(MLS_ACCT=*#{mls_acct}*)")
194
- # p = CabooseRets::CommercialProperty.where(:id => mls_acct.to_i).first
192
+ # def self.import_commercial_property(mls, save_images = true)
193
+ # self.import('COM', "(MLS=*#{mls}*)")
194
+ # p = CabooseRets::CommercialProperty.where(:id => mls.to_i).first
195
195
  # self.download_property_images(p, save_images)
196
196
  # self.update_coords(p)
197
197
  # end
198
198
  #
199
- # def self.import_land_property(mls_acct, save_images = true)
200
- # self.import('LND', "(MLS_ACCT=*#{mls_acct}*)")
201
- # p = CabooseRets::LandProperty.where(:id => mls_acct.to_i).first
199
+ # def self.import_land_property(mls, save_images = true)
200
+ # self.import('LND', "(MLS=*#{mls}*)")
201
+ # p = CabooseRets::LandProperty.where(:id => mls.to_i).first
202
202
  # self.download_property_images(p, save_images)
203
203
  # self.update_coords(p)
204
204
  # end
205
205
  #
206
- # def self.import_multi_family_property(mls_acct, save_images = true)
207
- # self.import('MUL', "(MLS_ACCT=*#{mls_acct}*)")
208
- # p = CabooseRets::MultiFamilyProperty.where(:id => mls_acct.to_i).first
206
+ # def self.import_multi_family_property(mls, save_images = true)
207
+ # self.import('MUL', "(MLS=*#{mls}*)")
208
+ # p = CabooseRets::MultiFamilyProperty.where(:id => mls.to_i).first
209
209
  # self.download_property_images(p, save_images)
210
210
  # self.update_coords(p)
211
211
  # end
212
212
  #
213
- # def self.import_office(lo_code, save_images = true)
213
+ # def self.import_office(lo_code, save_images = true)
214
214
  # self.import('OFF', "(LO_LO_CODE=*#{lo_code}*)")
215
215
  # office = CabooseRets::Office.where(:lo_code => lo_code.to_s).first
216
216
  # self.download_office_image(office) if save_images == true
@@ -232,35 +232,35 @@
232
232
  # #if m.nil?
233
233
  # # self.log("Can't find media record for #{id}.")
234
234
  # # return
235
- # #end
236
- # #self.client.get_object(:resource => :Property, :type => :Photo, :location => true, :id => m.mls_acct) do |headers, content|
235
+ # #end
236
+ # #self.client.get_object(:resource => :Property, :type => :Photo, :location => true, :id => m.mls) do |headers, content|
237
237
  # # if headers['object-id'].to_i == m.media_order
238
238
  # # m.url = headers['location']
239
239
  # # #m.image = URI.parse(headers['location'])
240
240
  # # end
241
241
  # #end
242
- # #m.save
242
+ # #m.save
243
243
  # end
244
244
  #
245
245
  # #=============================================================================
246
246
  # # Images
247
247
  # #=============================================================================
248
- #
248
+ #
249
249
  # def self.download_property_images(p, save_images = true)
250
250
  # return if save_images == false
251
- # self.refresh_property_media(p)
251
+ # self.refresh_property_media(p)
252
252
  #
253
- # #self.log("-- Downloading images and resizing for #{p.mls_acct}")
253
+ # #self.log("-- Downloading images and resizing for #{p.mls}")
254
254
  # #media = []
255
255
  # #self.client.get_object(:resource => :Property, :type => :Photo, :location => true, :id => p.id) do |headers, content|
256
256
  # #
257
257
  # # # Find the associated media record for the image
258
258
  # # #filename = File.basename(headers['location'])
259
- # # #m = CabooseRets::Media.where(:mls_acct => p.mls_acct, :file_name => filename).first
260
- # # m = CabooseRets::Media.where(:mls_acct => p.mls_acct, :media_order => headers['object-id'].to_i).first
259
+ # # #m = CabooseRets::Media.where(:mls => p.mls, :file_name => filename).first
260
+ # # m = CabooseRets::Media.where(:mls => p.mls, :media_order => headers['object-id'].to_i).first
261
261
  # #
262
262
  # # if m.nil?
263
- # # self.log("Can't find media record for #{p.mls_acct} #{filename}.")
263
+ # # self.log("Can't find media record for #{p.mls} #{filename}.")
264
264
  # # else
265
265
  # # m.url = headers['location']
266
266
  # # #m.image = URI.parse(headers['location'])
@@ -269,35 +269,35 @@
269
269
  # # end
270
270
  # #end
271
271
  # #
272
- # #self.log("-- Uploading images to S3 for #{p.mls_acct}")
272
+ # #self.log("-- Uploading images to S3 for #{p.mls}")
273
273
  # #media.each do |m|
274
274
  # # m.save
275
275
  # #end
276
276
  # end
277
277
  #
278
278
  # def self.refresh_property_media(p)
279
- # #self.log("-- Deleting images and metadata for #{p.mls_acct}...")
280
- # #CabooseRets::Media.where(:mls_acct => p.mls_acct, :media_type => 'Photo').destroy_all
281
- # #CabooseRets::Media.where(:mls_acct => p.mls_acct).destroy_all
279
+ # #self.log("-- Deleting images and metadata for #{p.mls}...")
280
+ # #CabooseRets::Media.where(:mls => p.mls, :media_type => 'Photo').destroy_all
281
+ # #CabooseRets::Media.where(:mls => p.mls).destroy_all
282
282
  #
283
- # self.log("-- Downloading GFX records for #{p.mls_acct}...")
283
+ # self.log("-- Downloading GFX records for #{p.mls}...")
284
284
  # params = {
285
285
  # :search_type => 'Media',
286
- # :class => 'GFX',
287
- # :query => "(MLS_ACCT=*#{p.id}*)",
286
+ # :class => 'GFX',
287
+ # :query => "(MLS=*#{p.id}*)",
288
288
  # :timeout => -1
289
289
  # }
290
290
  # ids = []
291
291
  # self.client.search(params) do |data|
292
- # ids << data['MEDIA_ID']
292
+ # ids << data['MEDIA_ID']
293
293
  # m = CabooseRets::Media.new
294
- # m.parse(data)
294
+ # m.parse(data)
295
295
  # m.save
296
296
  # end
297
- #
297
+ #
298
298
  # # Delete any records in the local database that shouldn't be there
299
- # puts "- Finding GFX records for MLS ##{p.mls_acct} in the local database that are not in the remote database..."
300
- # query = "select media_id from rets_media where mls_acct = '#{p.mls_acct}'"
299
+ # puts "- Finding GFX records for MLS ##{p.mls} in the local database that are not in the remote database..."
300
+ # query = "select media_id from rets_media where mls = '#{p.mls}'"
301
301
  # rows = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.send(:sanitize_sql_array, query))
302
302
  # local_ids = rows.collect{ |row| row[k] }
303
303
  # ids_to_remove = local_ids - ids
@@ -305,7 +305,7 @@
305
305
  # query = ["delete from rets_media where media_id not in (?)", ids_to_remove]
306
306
  # ActiveRecord::Base.connection.execute(ActiveRecord::Base.send(:sanitize_sql_array, query))
307
307
  # end
308
- #
308
+ #
309
309
  # end
310
310
  #
311
311
  # #def self.download_agent_image(agent)
@@ -344,7 +344,7 @@
344
344
  # i = 0
345
345
  # models.each do |model|
346
346
  # self.log "Updating coords #{names[i]} properties..."
347
- # model.where(:latitude => nil).reorder(:mls_acct).each do |p|
347
+ # model.where(:latitude => nil).reorder(:mls).each do |p|
348
348
  # self.update_coords(p)
349
349
  # end
350
350
  # i = i + 1
@@ -352,7 +352,7 @@
352
352
  # return
353
353
  # end
354
354
  #
355
- # self.log "Getting coords for mls_acct #{p.mls_acct}..."
355
+ # self.log "Getting coords for mls #{p.mls}..."
356
356
  # coords = self.coords_from_address(CGI::escape "#{p.street_num} #{p.street_name}, #{p.city}, #{p.state} #{p.zip}")
357
357
  # return if coords.nil? || coords == false
358
358
  #
@@ -404,7 +404,7 @@
404
404
  # m = self.meta(class_type)
405
405
  #
406
406
  # puts "Purging #{class_type}..."
407
- #
407
+ #
408
408
  # # Get the total number of records
409
409
  # puts "- Getting total number of records for #{class_type}..."
410
410
  # params = {
@@ -417,33 +417,33 @@
417
417
  # self.client.search(params.merge({ :count_mode => :only }))
418
418
  # count = self.client.rets_data[:code] == "20201" ? 0 : self.client.rets_data[:count]
419
419
  # batch_count = (count.to_f/5000.0).ceil
420
- #
420
+ #
421
421
  # ids = []
422
422
  # k = m.remote_key_field
423
423
  # (0...batch_count).each do |i|
424
424
  # puts "- Getting ids for #{class_type} (batch #{i+1} of #{batch_count})..."
425
425
  # self.client.search(params.merge({ :select => [k], :limit => 5000, :offset => 5000*i })) do |data|
426
426
  # ids << case class_type
427
- # when 'RES' then data[k]
428
- # when 'COM' then data[k]
429
- # when 'LND' then data[k]
430
- # when 'MUL' then data[k]
431
- # when 'OFF' then data[k]
427
+ # when 'RES' then data[k]
428
+ # when 'COM' then data[k]
429
+ # when 'LND' then data[k]
430
+ # when 'MUL' then data[k]
431
+ # when 'OFF' then data[k]
432
432
  # when 'AGT' then data[k]
433
433
  # when 'OPH' then data[k].to_i
434
434
  # when 'GFX' then data[k]
435
- # end
435
+ # end
436
436
  # end
437
437
  # end
438
438
  #
439
439
  # # Delete any records in the local database that shouldn't be there
440
- # puts "- Finding #{class_type} records in the local database that are not in the remote database..."
440
+ # puts "- Finding #{class_type} records in the local database that are not in the remote database..."
441
441
  # t = m.local_table
442
- # k = m.local_key_field
442
+ # k = m.local_key_field
443
443
  # query = "select distinct #{k} from #{t}"
444
444
  # rows = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.send(:sanitize_sql_array, query))
445
445
  # local_ids = rows.collect{ |row| row[k] }
446
- # ids_to_remove = local_ids - ids
446
+ # ids_to_remove = local_ids - ids
447
447
  # puts "- Found #{ids_to_remove.count} #{class_type} records in the local database that are not in the remote database."
448
448
  # puts "- Deleting #{class_type} records in the local database that shouldn't be there..."
449
449
  # query = ["delete from #{t} where #{k} not in (?)", ids_to_remove]
@@ -454,7 +454,7 @@
454
454
  # query = "select distinct #{k} from #{t}"
455
455
  # rows = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.send(:sanitize_sql_array, query))
456
456
  # local_ids = rows.collect{ |row| row[k] }
457
- # ids_to_add = ids - local_ids
457
+ # ids_to_add = ids - local_ids
458
458
  # puts "- Found #{ids_to_add.count} #{class_type} records in the remote database that we need to add to the local database."
459
459
  # ids_to_add.each do |id|
460
460
  # puts "- Importing #{id}..."
@@ -471,7 +471,7 @@
471
471
  # end
472
472
  #
473
473
  # end
474
- #
474
+ #
475
475
  # def self.get_media_urls
476
476
  # m = self.meta(class_type)
477
477
  #
@@ -492,15 +492,15 @@
492
492
  # (0...batch_count).each do |i|
493
493
  # self.client.search(params.merge({ :select => [k], :limit => 5000, :offset => 5000*i })) do |data|
494
494
  # ids << case class_type
495
- # when 'RES' then data[k]
496
- # when 'COM' then data[k]
497
- # when 'LND' then data[k]
498
- # when 'MUL' then data[k]
499
- # when 'OFF' then data[k]
495
+ # when 'RES' then data[k]
496
+ # when 'COM' then data[k]
497
+ # when 'LND' then data[k]
498
+ # when 'MUL' then data[k]
499
+ # when 'OFF' then data[k]
500
500
  # when 'AGT' then data[k]
501
501
  # when 'OPH' then data[k].to_i
502
502
  # when 'GFX' then data[k]
503
- # end
503
+ # end
504
504
  # end
505
505
  # end
506
506
  #
@@ -618,4 +618,4 @@
618
618
  # self.unlock_task if setting && d.strftime('%F %T') == setting.value
619
619
  # end
620
620
  #
621
- #end
621
+ #end
@@ -0,0 +1,625 @@
1
+ #require 'ruby-rets'
2
+ require "rets/version"
3
+ require "rets/exceptions"
4
+ require "rets/client"
5
+ require "rets/http"
6
+ require "rets/stream_http"
7
+ require "rets/base/core"
8
+ require "rets/base/sax_search"
9
+ require "rets/base/sax_metadata"
10
+
11
+ require 'httparty'
12
+ require 'json'
13
+
14
+ # http://rets.solidearth.com/ClientHome.aspx
15
+
16
+ class CabooseRets::RetsImporter # < ActiveRecord::Base
17
+
18
+ @@rets_client = nil
19
+ @@config = nil
20
+
21
+ def self.config
22
+ return @@config
23
+ end
24
+
25
+ def self.get_config
26
+ @@config = {
27
+ 'url' => nil, # URL to the RETS login
28
+ 'username' => nil,
29
+ 'password' => nil,
30
+ 'temp_path' => nil,
31
+ 'log_file' => nil,
32
+ 'media_base_url' => nil
33
+ }
34
+ config = YAML::load(File.open("#{Rails.root}/config/rets_importer.yml"))
35
+ config = config[Rails.env]
36
+ config.each { |key,val| @@config[key] = val }
37
+ end
38
+
39
+ def self.client
40
+ self.get_config if @@config.nil? || @@config['url'].nil?
41
+
42
+ if @@rets_client.nil?
43
+ @@rets_client = RETS::Client.login(
44
+ :url => 'http://rets.wamls.mlsmatrix.com/rets/Login.ashx', #@@config['url'],
45
+ :username => 'RETS_6', #@@config['username'],
46
+ :password => 'ellis' #@@config['password']
47
+ )
48
+ end
49
+ return @@rets_client
50
+ end
51
+
52
+ def self.meta(class_type)
53
+ Caboose::StdClass.new({ :search_type => 'Property' , :remote_key_field => 'MLS' , :local_key_field => 'mls' , :local_table => 'rets_properties' , :date_modified_field => 'DATE_MODIFIED'})
54
+ # case class_type
55
+ # when 'RES' then Caboose::StdClass.new({ :search_type => 'Property' , :remote_key_field => 'MLS' , :local_key_field => 'mls' , :local_table => 'rets_properties' , :date_modified_field => 'DATE_MODIFIED' })
56
+ # when 'OFF' then Caboose::StdClass.new({ :search_type => 'Office' , :remote_key_field => 'LO_LO_CODE' , :local_key_field => 'lo_code' , :local_table => 'rets_offices' , :date_modified_field => 'LO_DATE_MODIFIED' })
57
+ # when 'AGT' then Caboose::StdClass.new({ :search_type => 'Agent' , :remote_key_field => 'LA_LA_CODE' , :local_key_field => 'la_code' , :local_table => 'rets_agents' , :date_modified_field => 'LA_DATE_MODIFIED' })
58
+ # when 'OPH' then Caboose::StdClass.new({ :search_type => 'OpenHouse' , :remote_key_field => 'ID' , :local_key_field => 'id' , :local_table => 'rets_open_houses' , :date_modified_field => 'DATE_MODIFIED' })
59
+ # when 'GFX' then Caboose::StdClass.new({ :search_type => 'Media' , :remote_key_field => 'MEDIA_ID' , :local_key_field => 'media_id' , :local_table => 'rets_media' , :date_modified_field => 'DATE_MODIFIED' })
60
+ # end
61
+ end
62
+
63
+ #=============================================================================
64
+ # Import method
65
+ #=============================================================================
66
+
67
+ def self.import(class_type, query)
68
+ m = self.meta(class_type)
69
+ #self.log("Importing #{m.search_type}:#{class_type} with query #{query}...")
70
+ self.get_config if @@config.nil? || @@config['url'].nil?
71
+ params = {
72
+ :search_type => m.search_type,
73
+ :class => class_type,
74
+ :query => query,
75
+ :limit => -1,
76
+ :timeout => -1
77
+ }
78
+ obj = nil
79
+ begin
80
+ self.client.search(params) do |data|
81
+ obj = self.get_instance_with_id(class_type, data)
82
+ if obj.nil?
83
+ self.log("Error: object is nil")
84
+ self.log(data.inspect)
85
+ next
86
+ end
87
+ obj.parse(data)
88
+ obj.save
89
+ end
90
+ rescue RETS::HTTPError => err
91
+ self.log "Import error for #{class_type}: #{query}"
92
+ self.log err.message
93
+ end
94
+
95
+ end
96
+
97
+ def self.get_instance_with_id(class_type, data)
98
+ obj = nil
99
+ m = CabooseRets::Property
100
+ # m = case class_type
101
+ # when 'OPH' then CabooseRets::OpenHouse
102
+ # when 'GFX' then CabooseRets::Media
103
+ # when 'COM' then CabooseRets::CommercialProperty
104
+ # when 'LND' then CabooseRets::LandProperty
105
+ # when 'MUL' then CabooseRets::MultiFamilyProperty
106
+ # when 'RES' then CabooseRets::ResidentialProperty
107
+ # when 'AGT' then CabooseRets::Agent
108
+ # when 'OFF' then CabooseRets::Office
109
+ # end
110
+ obj = m.where(:id => data['ID'].to_i).exists? ? m.where(:id => data['ID'].to_i).first : m.new(:id=> data['ID'].to_i)
111
+ # obj = case class_type
112
+ # when 'OPH' then m.where(:id => data['ID'].to_i ).exists? ? m.where(:id => data['ID'].to_i ).first : m.new(:id => data['ID'].to_i )
113
+ # when 'GFX' then m.where(:media_id => data['MEDIA_ID'] ).exists? ? m.where(:media_id => data['MEDIA_ID'] ).first : m.new(:media_id => data['MEDIA_ID'] )
114
+ # when 'COM' then m.where(:id => data['MLS'].to_i ).exists? ? m.where(:id => data['MLS'].to_i ).first : m.new(:id => data['MLS'].to_i )
115
+ # when 'LND' then m.where(:id => data['MLS'].to_i ).exists? ? m.where(:id => data['MLS'].to_i ).first : m.new(:id => data['MLS'].to_i )
116
+ # when 'MUL' then m.where(:id => data['MLS'].to_i ).exists? ? m.where(:id => data['MLS'].to_i ).first : m.new(:id => data['MLS'].to_i )
117
+ # when 'RES' then m.where(:id => data['MLS'].to_i ).exists? ? m.where(:id => data['MLS'].to_i ).first : m.new(:id => data['MLS'].to_i )
118
+ # when 'AGT' then m.where(:la_code => data['LA_LA_CODE'] ).exists? ? m.where(:la_code => data['LA_LA_CODE'] ).first : m.new(:la_code => data['LA_LA_CODE'] )
119
+ # when 'OFF' then m.where(:lo_code => data['LO_LO_CODE'] ).exists? ? m.where(:lo_code => data['LO_LO_CODE'] ).first : m.new(:lo_code => data['LO_LO_CODE'] )
120
+ # end
121
+ return obj
122
+ end
123
+
124
+ #=============================================================================
125
+ # Main updater
126
+ #=============================================================================
127
+
128
+ def self.update_after(date_modified, save_images = true)
129
+ self.delay(:queue => 'rets').update_helper('PRO', date_modified, save_images)
130
+ # self.delay(:queue => 'rets').update_helper('RES', date_modified, save_images)
131
+ # self.delay(:queue => 'rets').update_helper('COM', date_modified, save_images)
132
+ # self.delay(:queue => 'rets').update_helper('LND', date_modified, save_images)
133
+ # self.delay(:queue => 'rets').update_helper('MUL', date_modified, save_images)
134
+ # self.delay(:queue => 'rets').update_helper('OFF', date_modified, save_images)
135
+ # self.delay(:queue => 'rets').update_helper('AGT', date_modified, save_images)
136
+ # self.delay(:queue => 'rets').update_helper('OPH', date_modified, save_images)
137
+ end
138
+
139
+ def self.update_helper(class_type, date_modified, save_images = true)
140
+ m = self.meta(class_type)
141
+ k = m.remote_key_field
142
+ d = date_modified.in_time_zone(CabooseRets::timezone).strftime("%FT%T")
143
+ params = {
144
+ :search_type => m.search_type,
145
+ :class => class_type,
146
+ :select => [m.remote_key_field],
147
+ :query => "(#{m.date_modified_field}=#{d}+)",
148
+ :standard_names_only => true,
149
+ :timeout => -1
150
+ }
151
+ self.client.search(params) do |data|
152
+ self.delay(:priority => 10, :queue => 'rets').import_properties( data[k], save_images)
153
+ # case class_type
154
+ # when 'RES' then self.delay(:priority => 10, :queue => 'rets').import_residential_property( data[k], save_images)
155
+ # when 'COM' then self.delay(:priority => 10, :queue => 'rets').import_commercial_property( data[k], save_images)
156
+ # when 'LND' then self.delay(:priority => 10, :queue => 'rets').import_land_property( data[k], save_images)
157
+ # when 'MUL' then self.delay(:priority => 10, :queue => 'rets').import_multi_family_property( data[k], save_images)
158
+ # when 'OFF' then self.delay(:priority => 10, :queue => 'rets').import_office( data[k], save_images)
159
+ # when 'AGT' then self.delay(:priority => 10, :queue => 'rets').import_agent( data[k], save_images)
160
+ # when 'OPH' then self.delay(:priority => 10, :queue => 'rets').import_open_house( data[k], save_images)
161
+ # end
162
+ end
163
+ end
164
+
165
+ #=============================================================================
166
+ # Single model import methods (called from a worker dyno)
167
+ #=============================================================================
168
+
169
+ def self.import_property(mls, save_images = true)
170
+ self.import('RES', "(MLS=*#{mls}*)")
171
+ p = CabooseRets::Property.where(:id => mls.to_i).first
172
+ # p = CabooseRets::ResidentialProperty.where(:id => mls.to_i).first
173
+ # if p.nil?
174
+ # self.import('COM', "(MLS=*#{mls}*)")
175
+ # p = CabooseRets::CommercialProperty.where(:id => mls.to_i).first
176
+ # if p.nil?
177
+ # self.import('LND', "(MLS=*#{mls}*)")
178
+ # p = CabooseRets::LandProperty.where(:id => mls.to_i).first
179
+ # if p.nil?
180
+ # self.import('MUL', "(MLS=*#{mls}*)")
181
+ # p = CabooseRets::MultiFamilyProperty.where(:id => mls.to_i).first
182
+ # return if p.nil?
183
+ # end
184
+ # end
185
+ # end
186
+ self.download_property_images(p, save_images)
187
+ end
188
+
189
+ def self.import_properties(mls, save_images = true)
190
+ self.import('PRO', "(MLS=*#{mls}*)")
191
+ p = CabooseRets::Property.where(:id => mls.to_i).first
192
+ self.download_property_images(p, save_images)
193
+ self.update_coords(p)
194
+ end
195
+
196
+ # def self.import_residential_property(mls, save_images = true)
197
+ # self.import('RES', "(MLS=*#{mls}*)")
198
+ # p = CabooseRets::ResidentialProperty.where(:id => mls.to_i).first
199
+ # self.download_property_images(p, save_images)
200
+ # self.update_coords(p)
201
+ # end
202
+ #
203
+ # def self.import_commercial_property(mls, save_images = true)
204
+ # self.import('COM', "(MLS=*#{mls}*)")
205
+ # p = CabooseRets::CommercialProperty.where(:id => mls.to_i).first
206
+ # self.download_property_images(p, save_images)
207
+ # self.update_coords(p)
208
+ # end
209
+ #
210
+ # def self.import_land_property(mls, save_images = true)
211
+ # self.import('LND', "(MLS=*#{mls}*)")
212
+ # p = CabooseRets::LandProperty.where(:id => mls.to_i).first
213
+ # self.download_property_images(p, save_images)
214
+ # self.update_coords(p)
215
+ # end
216
+ #
217
+ # def self.import_multi_family_property(mls, save_images = true)
218
+ # self.import('MUL', "(MLS=*#{mls}*)")
219
+ # p = CabooseRets::MultiFamilyProperty.where(:id => mls.to_i).first
220
+ # self.download_property_images(p, save_images)
221
+ # self.update_coords(p)
222
+ # end
223
+ #
224
+ # def self.import_office(lo_code, save_images = true)
225
+ # self.import('OFF', "(LO_LO_CODE=*#{lo_code}*)")
226
+ # office = CabooseRets::Office.where(:lo_code => lo_code.to_s).first
227
+ # self.download_office_image(office) if save_images == true
228
+ # end
229
+ #
230
+ # def self.import_agent(la_code, save_images = true)
231
+ # self.import('AGT', "(LA_LA_CODE=*#{la_code}*)")
232
+ # a = CabooseRets::Agent.where(:la_code => la_code.to_s).first
233
+ # self.download_agent_image(a) #if save_images == true
234
+ # end
235
+ #
236
+ # def self.import_open_house(id, save_images = true)
237
+ # self.import('OPH', "((ID=#{id}+),(ID=#{id}-))")
238
+ # end
239
+
240
+ def self.import_media(id, save_images = true)
241
+ self.import('GFX', "((MEDIA_ID=#{id}+),(MEDIA_ID=#{id}-))")
242
+ end
243
+
244
+ #=============================================================================
245
+ # Images
246
+ #=============================================================================
247
+
248
+ def self.download_property_images(p, save_images = true)
249
+ return if save_images == false
250
+
251
+ self.log("- Downloading GFX records for #{p.mls}...")
252
+ params = {
253
+ :search_type => 'Media',
254
+ :class => 'GFX',
255
+ :query => "(MLS=*#{p.mls}*)",
256
+ :timeout => -1
257
+ }
258
+ ids = []
259
+ self.client.search(params) do |data|
260
+ ids << data['MEDIA_ID']
261
+ m = CabooseRets::Media.where(:media_id => data['MEDIA_ID']).first
262
+ m = CabooseRets::Media.new if m.nil?
263
+ m.parse(data)
264
+ m.save
265
+ end
266
+
267
+ if ids.count > 0
268
+ # Delete any records in the local database that shouldn't be there
269
+ self.log("- Deleting GFX records for MLS ##{p.mls} in the local database that are not in the remote database...")
270
+ query = "select media_id from rets_media where mls = '#{p.mls}'"
271
+ rows = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.send(:sanitize_sql_array, query))
272
+ local_ids = rows.collect{ |row| row['media_id'] }
273
+ ids_to_remove = local_ids - ids
274
+ if ids_to_remove && ids_to_remove.count > 0
275
+ query = ["delete from rets_media where media_id in (?)", ids_to_remove]
276
+ ActiveRecord::Base.connection.execute(ActiveRecord::Base.send(:sanitize_sql_array, query))
277
+ end
278
+ end
279
+
280
+ end
281
+
282
+ def self.download_agent_image(agent)
283
+ self.log "Saving image for #{agent.first_name} #{agent.last_name}..."
284
+ begin
285
+ self.client.get_object(:resource => :Agent, :type => :Photo, :location => true, :id => agent.la_code) do |headers, content|
286
+ agent.verify_meta_exists
287
+ agent.meta.image_location = headers['location']
288
+ agent.meta.save
289
+ end
290
+ rescue RETS::APIError => err
291
+ self.log "No image for #{agent.first_name} #{agent.last_name}."
292
+ self.log err
293
+ end
294
+ end
295
+
296
+ def self.download_office_image(office)
297
+ #self.log "Saving image for #{agent.first_name} #{agent.last_name}..."
298
+ #begin
299
+ # self.client.get_object(:resource => :Agent, :type => :Photo, :location => true, :id => agent.la_code) do |headers, content|
300
+ # agent.verify_meta_exists
301
+ # agent.meta.image_location = headers['location']
302
+ # agent.meta.save
303
+ # end
304
+ #rescue RETS::APIError => err
305
+ # self.log "No image for #{agent.first_name} #{agent.last_name}."
306
+ # self.log err
307
+ #end
308
+ end
309
+
310
+ #=============================================================================
311
+ # GPS
312
+ #=============================================================================
313
+
314
+ def self.update_coords(p = nil)
315
+ if p.nil?
316
+ models = CabooseRets::Property #[CabooseRets::CommercialProperty, CabooseRets::LandProperty, CabooseRets::MultiFamilyProperty, CabooseRets::ResidentialProperty]
317
+ names = ["commercial", "land", "multi-family", "residential"]
318
+ i = 0
319
+ models.each do |model|
320
+ self.log "Updating coords #{names[i]} properties..."
321
+ model.where(:latitude => nil).reorder(:mls).each do |p|
322
+ self.delay(:queue => 'rets').update_coords(p)
323
+ end
324
+ i = i + 1
325
+ end
326
+ return
327
+ end
328
+
329
+ self.log "Getting coords for mls #{p.mls}..."
330
+ coords = self.coords_from_address(CGI::escape "#{p.street_num} #{p.street_name}, #{p.city}, #{p.state} #{p.zip}")
331
+ if coords.nil? || coords == false
332
+ self.log "Can't set coords for mls acct #{p.mls}..."
333
+ return
334
+ end
335
+
336
+ p.latitude = coords['lat']
337
+ p.longitude = coords['lng']
338
+ p.save
339
+ end
340
+
341
+ def self.coords_from_address(address)
342
+ #return false
343
+ begin
344
+ uri = "https://maps.googleapis.com/maps/api/geocode/json?address=#{address}&sensor=false"
345
+ uri.gsub!(" ", "+")
346
+ resp = HTTParty.get(uri)
347
+ json = JSON.parse(resp.body)
348
+ return json['results'][0]['geometry']['location']
349
+ rescue
350
+ self.log "Error: #{uri}"
351
+ sleep(2)
352
+ return false
353
+ end
354
+ end
355
+
356
+ #=============================================================================
357
+ # Purging
358
+ #=============================================================================
359
+
360
+ def self.purge
361
+ self.purge_properties
362
+ self.purge_residential
363
+ self.purge_commercial
364
+ self.purge_land
365
+ self.purge_multi_family
366
+ self.purge_offices
367
+ #self.purge_agents
368
+ self.purge_open_houses
369
+ self.purge_media
370
+ end
371
+
372
+ def self.purge_properties() self.delay(:queue => 'rets').purge_helper('PRO', '2012-01-01') end
373
+ def self.purge_residential() self.delay(:queue => 'rets').purge_helper('RES', '2012-01-01') end
374
+ def self.purge_commercial() self.delay(:queue => 'rets').purge_helper('COM', '2012-01-01') end
375
+ def self.purge_land() self.delay(:queue => 'rets').purge_helper('LND', '2012-01-01') end
376
+ def self.purge_multi_family() self.delay(:queue => 'rets').purge_helper('MUL', '2012-01-01') end
377
+ def self.purge_offices() self.delay(:queue => 'rets').purge_helper('OFF', '2012-01-01') end
378
+ def self.purge_agents() self.delay(:queue => 'rets').purge_helper('AGT', '2012-01-01') end
379
+ def self.purge_open_houses() self.delay(:queue => 'rets').purge_helper('OPH', '2012-01-01') end
380
+ def self.purge_media() self.delay(:queue => 'rets').purge_helper('GFX', '2012-01-01') end
381
+
382
+ def self.purge_helper(class_type, date_modified)
383
+ m = self.meta(class_type)
384
+
385
+ self.log("Purging #{class_type}...")
386
+
387
+ # Get the total number of records
388
+ self.log("- Getting total number of records for #{class_type}...")
389
+ params = {
390
+ :search_type => m.search_type,
391
+ :class => class_type,
392
+ :query => "(#{m.date_modified_field}=#{date_modified}T00:00:01+)",
393
+ :standard_names_only => true,
394
+ :timeout => -1
395
+ }
396
+ self.client.search(params.merge({ :count_mode => :only }))
397
+ count = self.client.rets_data[:code] == "20201" ? 0 : self.client.rets_data[:count]
398
+ batch_count = (count.to_f/5000.0).ceil
399
+
400
+ ids = []
401
+ k = m.remote_key_field
402
+ (0...batch_count).each do |i|
403
+ self.log("- Getting ids for #{class_type} (batch #{i+1} of #{batch_count})...")
404
+ self.client.search(params.merge({ :select => [k], :limit => 5000, :offset => 5000*i })) do |data|
405
+ ids << (class_type == 'OPH' ? data[k].to_i : data[k])
406
+ end
407
+ end
408
+
409
+ # Only do stuff if we got a real response from the server
410
+ if ids.count > 0
411
+
412
+ # Delete any records in the local database that shouldn't be there
413
+ self.log("- Finding #{class_type} records in the local database that are not in the remote database...")
414
+ t = m.local_table
415
+ k = m.local_key_field
416
+ query = "select distinct #{k} from #{t}"
417
+ rows = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.send(:sanitize_sql_array, query))
418
+ local_ids = rows.collect{ |row| row[k] }
419
+ ids_to_remove = local_ids - ids
420
+ self.log("- Found #{ids_to_remove.count} #{class_type} records in the local database that are not in the remote database.")
421
+ self.log("- Deleting #{class_type} records in the local database that shouldn't be there...")
422
+ query = ["delete from #{t} where #{k} in (?)", ids_to_remove]
423
+ ActiveRecord::Base.connection.execute(ActiveRecord::Base.send(:sanitize_sql_array, query))
424
+
425
+ # Find any ids in the remote database that should be in the local database
426
+ self.log("- Finding #{class_type} records in the remote database that should be in the local database...")
427
+ query = "select distinct #{k} from #{t}"
428
+ rows = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.send(:sanitize_sql_array, query))
429
+ local_ids = rows.collect{ |row| row[k] }
430
+ ids_to_add = ids - local_ids
431
+ ids_to_add = ids_to_add.sort.reverse
432
+ self.log("- Found #{ids_to_add.count} #{class_type} records in the remote database that we need to add to the local database.")
433
+ ids_to_add.each do |id|
434
+ self.log("- Importing #{id}...")
435
+ self.delay(:queue => 'rets').import_properties(id, false)
436
+ # case class_type
437
+ # when 'RES' then self.delay(:queue => 'rets').import_residential_property(id, false)
438
+ # when 'COM' then self.delay(:queue => 'rets').import_commercial_property(id, false)
439
+ # when 'LND' then self.delay(:queue => 'rets').import_land_property(id, false)
440
+ # when 'MUL' then self.delay(:queue => 'rets').import_multi_family_property(id, false)
441
+ # when 'OFF' then self.delay(:queue => 'rets').import_office(id, false)
442
+ # when 'AGT' then self.delay(:queue => 'rets').import_agent(id, false)
443
+ # when 'OPH' then self.delay(:queue => 'rets').import_open_house(id, false)
444
+ # when 'GFX' then self.delay(:queue => 'rets').import_media(id, false)
445
+ # end
446
+ end
447
+
448
+ end
449
+
450
+ end
451
+
452
+ def self.get_media_urls
453
+ m = self.meta(class_type)
454
+
455
+ # Get the total number of records
456
+ params = {
457
+ :search_type => m.search_type,
458
+ :class => class_type,
459
+ :query => "(#{m.date_modified_field}=#{date_modified}T00:00:01+)",
460
+ :standard_names_only => true,
461
+ :timeout => -1
462
+ }
463
+ self.client.search(params.merge({ :count_mode => :only }))
464
+ count = self.client.rets_data[:code] == "20201" ? 0 : self.client.rets_data[:count]
465
+ batch_count = (count.to_f/5000.0).ceil
466
+
467
+ ids = []
468
+ k = m.remote_key_field
469
+ (0...batch_count).each do |i|
470
+ self.client.search(params.merge({ :select => [k], :limit => 5000, :offset => 5000*i })) do |data|
471
+ ids << data[k]
472
+ # ids << case class_type
473
+ # when 'RES' then data[k]
474
+ # when 'COM' then data[k]
475
+ # when 'LND' then data[k]
476
+ # when 'MUL' then data[k]
477
+ # when 'OFF' then data[k]
478
+ # when 'AGT' then data[k]
479
+ # when 'OPH' then data[k].to_i
480
+ # when 'GFX' then data[k]
481
+ # end
482
+ end
483
+ end
484
+
485
+ if ids.count > 0
486
+ # Delete any records in the local database that shouldn't be there
487
+ t = m.local_table
488
+ k = m.local_key_field
489
+ query = ["delete from #{t} where #{k} not in (?)", ids]
490
+ ActiveRecord::Base.connection.execute(ActiveRecord::Base.send(:sanitize_sql_array, query))
491
+
492
+ # Find any ids in the remote database that should be in the local database
493
+ query = "select distinct #{k} from #{t}"
494
+ rows = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.send(:sanitize_sql_array, query))
495
+ local_ids = rows.collect{ |row| row[k] }
496
+ ids_to_add = ids - local_ids
497
+ ids_to_add.each do |id|
498
+ self.log("Importing #{id}...")
499
+ self.delay(:queue => 'rets').import_properties(id, false)
500
+ # case class_type
501
+ # when 'RES' then self.delay(:queue => 'rets').import_residential_property(id, false)
502
+ # when 'COM' then self.delay(:queue => 'rets').import_commercial_property(id, false)
503
+ # when 'LND' then self.delay(:queue => 'rets').import_land_property(id, false)
504
+ # when 'MUL' then self.delay(:queue => 'rets').import_multi_family_property(id, false)
505
+ # when 'OFF' then self.delay(:queue => 'rets').import_office(id, false)
506
+ # when 'AGT' then self.delay(:queue => 'rets').import_agent(id, false)
507
+ # when 'OPH' then self.delay(:queue => 'rets').import_open_house(id, false)
508
+ # when 'GFX' then self.delay(:queue => 'rets').import_media(id)
509
+ # end
510
+ end
511
+ end
512
+
513
+ end
514
+
515
+ #=============================================================================
516
+ # Logging
517
+ #=============================================================================
518
+
519
+ def self.log(msg)
520
+ puts "[rets_importer] #{msg}"
521
+ #Rails.logger.info("[rets_importer] #{msg}")
522
+ end
523
+
524
+ def self.log2(msg)
525
+ puts "======================================================================"
526
+ puts "[rets_importer] #{msg}"
527
+ puts "======================================================================"
528
+ #Rails.logger.info("[rets_importer] #{msg}")
529
+ end
530
+
531
+ #=============================================================================
532
+ # Locking update task
533
+ #=============================================================================
534
+
535
+ def self.update_rets
536
+ self.log2("Updating rets...")
537
+ if self.task_is_locked
538
+ self.log2("Task is locked, aborting.")
539
+ return
540
+ end
541
+ self.log2("Locking task...")
542
+ task_started = self.lock_task
543
+
544
+ begin
545
+ overlap = 30.seconds
546
+ if (DateTime.now - self.last_purged).to_i >= 1
547
+ self.purge
548
+ self.save_last_purged(task_started)
549
+ # Keep this in here to make sure all updates are caught
550
+ #overlap = 1.month
551
+ end
552
+
553
+ self.log2("Updating after #{self.last_updated.strftime("%FT%T%:z")}...")
554
+ self.update_after(self.last_updated - overlap)
555
+
556
+ self.log2("Saving the timestamp for when we updated...")
557
+ self.save_last_updated(task_started)
558
+
559
+ self.log2("Unlocking the task...")
560
+ self.unlock_task
561
+ rescue Exception => err
562
+ puts err
563
+ raise
564
+ ensure
565
+ self.log2("Unlocking task if last updated...")
566
+ self.unlock_task_if_last_updated(task_started)
567
+ end
568
+
569
+ # Start the same update process in five minutes
570
+ self.log2("Adding the update rets task for 5 minutes from now...")
571
+ q = "handler like '%update_rets%'"
572
+ count = Delayed::Job.where(q).count
573
+ if count == 0 || (count == 1 && Delayed::Job.where(q).first.locked_at)
574
+ self.delay(:run_at => 5.minutes.from_now, :queue => 'rets').update_rets
575
+ end
576
+ end
577
+
578
+ def self.last_updated
579
+ if !Caboose::Setting.exists?(:name => 'rets_last_updated')
580
+ Caboose::Setting.create(:name => 'rets_last_updated', :value => '2013-08-06T00:00:01')
581
+ end
582
+ s = Caboose::Setting.where(:name => 'rets_last_updated').first
583
+ return DateTime.parse(s.value)
584
+ end
585
+
586
+ def self.last_purged
587
+ if !Caboose::Setting.exists?(:name => 'rets_last_purged')
588
+ Caboose::Setting.create(:name => 'rets_last_purged', :value => '2013-08-06T00:00:01')
589
+ end
590
+ s = Caboose::Setting.where(:name => 'rets_last_purged').first
591
+ return DateTime.parse(s.value)
592
+ end
593
+
594
+ def self.save_last_updated(d)
595
+ s = Caboose::Setting.where(:name => 'rets_last_updated').first
596
+ s.value = d.in_time_zone(CabooseRets::timezone).strftime("%FT%T%:z")
597
+ s.save
598
+ end
599
+
600
+ def self.save_last_purged(d)
601
+ s = Caboose::Setting.where(:name => 'rets_last_purged').first
602
+ s.value = d.in_time_zone(CabooseRets::timezone).strftime("%FT%T%:z")
603
+ s.save
604
+ end
605
+
606
+ def self.task_is_locked
607
+ return Caboose::Setting.exists?(:name => 'rets_update_running')
608
+ end
609
+
610
+ def self.lock_task
611
+ d = DateTime.now.utc.in_time_zone(CabooseRets::timezone)
612
+ Caboose::Setting.create(:name => 'rets_update_running', :value => d.strftime("%FT%T%:z"))
613
+ return d
614
+ end
615
+
616
+ def self.unlock_task
617
+ Caboose::Setting.where(:name => 'rets_update_running').first.destroy
618
+ end
619
+
620
+ def self.unlock_task_if_last_updated(d)
621
+ setting = Caboose::Setting.where(:name => 'rets_update_running').first
622
+ self.unlock_task if setting && d.in_time_zone.strftime("%FT%T%:z") == setting.value
623
+ end
624
+
625
+ end