caboose-rets 0.0.62 → 0.0.63

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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NWU0NDk2NmI3ZjJhNjA0M2RlYzdjMTZmNmMyZjkzN2Q2OTVlNzdlMA==
4
+ MDA2YWIxNjM3YWYyYmYwYjNjNGNkZDQ1YThmMGI3MDU4ZDFjYzYzZQ==
5
5
  data.tar.gz: !binary |-
6
- MDhjOWMwODBiNDg5OTUwOWUyNTg1NGY5ZmQ5NWVkNGQzMTk0ZmQxZQ==
6
+ NmMyNTFkYWE1NjBlYWZkNzNlNDk2ZDg0MGQyOGYxYmEwYmVlYTIzYg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MmNjYzBiZWIxYzM3ZjlhMzBjNDAyYmZkOTA2NzA1MThhMDkxZWJkMjNlMjdi
10
- ZWYzNjM4ZjU4MjJmZDRlMTNmZjljODFmYWY4NjAxOTE4M2Y3OGVhMWQ4Nzg5
11
- MGRiY2Y0OTMyYjA0ZjI4OTdiY2U5NGQ3Yzk2NTJhMGVkODQ4NGY=
9
+ ODhkYWYyOWJhZmQyNzczNzQ1N2UzZTAwZTVkZGJmNDY4MjlmZGM3ODY0ZjI3
10
+ ZGMwNTkxODk5NWNiZWM5MWE2Y2VhYTc1OTBhYmUyN2Q1Y2IzYjVlYjNmYmI5
11
+ MWY5YTY0YmJlNWFjOTMwMjc3ZWY0MjIyYWE3MWViMGQxYTRjOGQ=
12
12
  data.tar.gz: !binary |-
13
- OTkxYzMyMmUxODZhNWYxZDk4NGM3ZDM0M2E2MDM5MWQ3NGRiNmE5NmY3NjJj
14
- ODQxMjRhMzllNmJiMmQzOWI2OWI0ZTk4NTMwYTU2ZTc0YWUxMzI4OTVkZjgz
15
- NDJhMjA0ZmQxY2NkZDRmYjQ1YzllNjJkOWMyMzY0YWRkNmM5MjM=
13
+ NmE1YTVkMWI2OTU2Nzg2MDhiMTM0ZDgyYTIwOTNmM2RlZDM0MmI1MTZmMDc4
14
+ ZTc1YWVmNDhiYWZkNmViNmI0MzQ1YzA5NmY0NWVjNzVkZjkyNmJkMTYxNWEy
15
+ NWQ0OTVmZGUxY2NlNGI4MzBkZWIwNGE2MDYzZGM2MjI0MmJlN2E=
@@ -14,14 +14,14 @@ require 'json'
14
14
  # http://rets.solidearth.com/ClientHome.aspx
15
15
 
16
16
  class CabooseRets::RetsImporter # < ActiveRecord::Base
17
-
17
+
18
18
  @@rets_client = nil
19
19
  @@config = nil
20
-
20
+
21
21
  def self.config
22
22
  return @@config
23
23
  end
24
-
24
+
25
25
  def self.get_config
26
26
  @@config = {
27
27
  'url' => nil, # URL to the RETS login
@@ -35,11 +35,11 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
35
35
  config = config[Rails.env]
36
36
  config.each { |key,val| @@config[key] = val }
37
37
  end
38
-
38
+
39
39
  def self.client
40
40
  self.get_config if @@config.nil? || @@config['url'].nil?
41
-
42
- if (@@rets_client.nil?)
41
+
42
+ if @@rets_client.nil?
43
43
  @@rets_client = RETS::Client.login(
44
44
  :url => @@config['url'],
45
45
  :username => @@config['username'],
@@ -48,35 +48,49 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
48
48
  end
49
49
  return @@rets_client
50
50
  end
51
-
51
+
52
+ def self.meta(class_type)
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' })
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
+ 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
+ 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' })
61
+ 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' })
62
+ end
63
+ end
64
+
52
65
  #=============================================================================
53
66
  # Import method
54
67
  #=============================================================================
55
-
56
- def self.import(query, search_type, class_type)
57
- self.log("Importing #{search_type}:#{class_type} with query #{query}...")
68
+
69
+ def self.import(class_type, query)
70
+ m = self.meta(class_type)
71
+ self.log("Importing #{m.search_type}:#{class_type} with query #{query}...")
58
72
  self.get_config if @@config.nil? || @@config['url'].nil?
59
73
  params = {
60
- :search_type => search_type,
74
+ :search_type => m.search_type,
61
75
  :class => class_type,
62
76
  :query => query,
63
- :limit => -1,
77
+ :limit => -1,
64
78
  :timeout => -1
65
79
  }
66
80
  obj = nil
67
81
  self.client.search(params) do |data|
68
82
  obj = self.get_instance_with_id(class_type, data)
69
83
  if obj.nil?
70
- self.log("Error: object is nil")
84
+ self.log("Error: object is nil")
71
85
  self.log(data.inspect)
72
86
  next
73
87
  end
74
- obj.parse(data)
75
- obj.save
76
- end
88
+ obj.parse(data)
89
+ obj.save
90
+ end
77
91
  end
78
-
79
- def self.get_instance_with_id(class_type, data)
92
+
93
+ def self.get_instance_with_id(class_type, data)
80
94
  obj = nil
81
95
  m = case class_type
82
96
  when 'OPH' then CabooseRets::OpenHouse
@@ -87,183 +101,182 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
87
101
  when 'RES' then CabooseRets::ResidentialProperty
88
102
  when 'AGT' then CabooseRets::Agent
89
103
  when 'OFF' then CabooseRets::Office
90
- end
104
+ end
91
105
  obj = case class_type
92
106
  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 )
93
107
  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'] )
94
108
  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 )
95
- 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 )
96
- 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 )
97
- 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 )
109
+ 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 )
110
+ 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 )
111
+ 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 )
98
112
  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'] )
99
113
  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'] )
100
114
  end
101
- return obj
102
- end
103
-
104
- #def self.delete_old_properties
105
- # self.log("Deleting old residential properties...")
106
- # self.get_config if @@config.nil? || @@config['url'].nil?
107
- #
108
- # d = DateTime.new(2000, 1, 1)
109
- # now = DateTime.now
110
- # while d < now
111
- # d2 = d + 3.months
112
- # puts "- Getting ids for #{d.strftime("%Y-%m-%d")} - #{d2.strftime("%Y-%m-%d")}..."
113
- # q = "((DATE_CREATED=#{d.strftime("%Y-%m-%d")}+),(DATE_CREATED=#{d2.strftime("%Y-%m-%d")}-))",
114
- # params = { :search_type => 'Property', :class => 'RES', :query => q, :limit => -1, :timeout => -1, :select => 'MLS_ACCT' }
115
- # ids = []
116
- # self.client.search(params) do |data|
117
- # ids << data['MLS_ACCT'].to_i
118
- # end
119
- # d = d + 3.months
120
- # end
121
- #
122
- # puts "Deleting properties..."
123
- # #CabooseRets::ResidentialProperty.where("id not in (?)", ids).destroy_all
124
- #end
125
-
115
+ return obj
116
+ end
117
+
126
118
  #=============================================================================
127
119
  # Main updater
128
120
  #=============================================================================
129
121
 
130
122
  def self.update_after(date_modified, save_images = true)
131
- self.update_residential_properties_modified_after(date_modified, save_images)
132
- self.update_commercial_properties_modified_after(date_modified, save_images)
133
- self.update_land_properties_modified_after(date_modified, save_images)
134
- self.update_multi_family_properties_modified_after(date_modified, save_images)
135
- self.update_offices_modified_after(date_modified, save_images)
136
- self.update_agents_modified_after(date_modified, save_images)
137
- self.update_open_houses_modified_after(date_modified, save_images)
138
- end
139
- def self.update_residential_properties_modified_after(date_modified, save_images = true) self.client.search({ :search_type => 'Property' , :class => 'RES', :select => ['MLS_ACCT'] , :query => "(DATE_MODIFIED=#{date_modified.strftime("%FT%T")}+)" , :standard_names_only => true, :timeout => -1 }) { |data| self.delay(:priority => 10).import_residential_property( data['MLS_ACCT' ], save_images) } end
140
- def self.update_commercial_properties_modified_after(date_modified, save_images = true) self.client.search({ :search_type => 'Property' , :class => 'COM', :select => ['MLS_ACCT'] , :query => "(DATE_MODIFIED=#{date_modified.strftime("%FT%T")}+)" , :standard_names_only => true, :timeout => -1 }) { |data| self.delay(:priority => 10).import_commercial_property( data['MLS_ACCT' ], save_images) } end
141
- def self.update_land_properties_modified_after(date_modified, save_images = true) self.client.search({ :search_type => 'Property' , :class => 'LND', :select => ['MLS_ACCT'] , :query => "(DATE_MODIFIED=#{date_modified.strftime("%FT%T")}+)" , :standard_names_only => true, :timeout => -1 }) { |data| self.delay(:priority => 10).import_land_property( data['MLS_ACCT' ], save_images) } end
142
- def self.update_multi_family_properties_modified_after(date_modified, save_images = true) self.client.search({ :search_type => 'Property' , :class => 'MUL', :select => ['MLS_ACCT'] , :query => "(DATE_MODIFIED=#{date_modified.strftime("%FT%T")}+)" , :standard_names_only => true, :timeout => -1 }) { |data| self.delay(:priority => 10).import_multi_family_property( data['MLS_ACCT' ], save_images) } end
143
- def self.update_offices_modified_after(date_modified, save_images = true) self.client.search({ :search_type => 'Office' , :class => 'OFF', :select => ['LO_LO_CODE'] , :query => "(LO_DATE_MODIFIED=#{date_modified.strftime("%FT%T")}+)", :standard_names_only => true, :timeout => -1 }) { |data| self.delay(:priority => 10).import_office( data['LO_LO_CODE'], save_images) } end
144
- def self.update_agents_modified_after(date_modified, save_images = true) self.client.search({ :search_type => 'Agent' , :class => 'AGT', :select => ['LA_LA_CODE'] , :query => "(LA_DATE_MODIFIED=#{date_modified.strftime("%FT%T")}+)", :standard_names_only => true, :timeout => -1 }) { |data| self.delay(:priority => 10).import_agent( data['LA_LA_CODE'], save_images) } end
145
- def self.update_open_houses_modified_after(date_modified, save_images = true) self.client.search({ :search_type => 'OpenHouse', :class => 'OPH', :select => ['ID'] , :query => "(DATE_MODIFIED=#{date_modified.strftime("%FT%T")}+)" , :standard_names_only => true, :timeout => -1 }) { |data| self.delay(:priority => 10).import_open_house( data['ID' ], save_images) } end
146
-
123
+ self.update_helper('RES', date_modified, save_images)
124
+ self.update_helper('COM', date_modified, save_images)
125
+ self.update_helper('LND', date_modified, save_images)
126
+ self.update_helper('MUL', date_modified, save_images)
127
+ self.update_helper('OFF', date_modified, save_images)
128
+ self.update_helper('AGT', date_modified, save_images)
129
+ self.update_helper('OPH', date_modified, save_images)
130
+ end
131
+
132
+ def self.update_helper(class_type, date_modified, save_images = true)
133
+ m = self.meta(class_type)
134
+ k = m.remote_key_field
135
+ params = {
136
+ :search_type => m.search_type,
137
+ :class => class_type,
138
+ :select => [m.remote_key_field],
139
+ :query => "(#{m.date_modified_field}=#{date_modified.strftime("%FT%T")}+)",
140
+ :standard_names_only => true,
141
+ :timeout => -1
142
+ }
143
+ self.client.search(params) do |data|
144
+ case class_type
145
+ when 'RES' then self.delay(:priority => 10).import_residential_property( data[k], save_images)
146
+ when 'COM' then self.delay(:priority => 10).import_commercial_property( data[k], save_images)
147
+ when 'LND' then self.delay(:priority => 10).import_land_property( data[k], save_images)
148
+ when 'MUL' then self.delay(:priority => 10).import_multi_family_property( data[k], save_images)
149
+ when 'OFF' then self.delay(:priority => 10).import_office( data[k], save_images)
150
+ when 'AGT' then self.delay(:priority => 10).import_agent( data[k], save_images)
151
+ when 'OPH' then self.delay(:priority => 10).import_open_house( data[k], save_images)
152
+ end
153
+ end
154
+ end
155
+
147
156
  #=============================================================================
148
157
  # Single model import methods (called from a worker dyno)
149
158
  #=============================================================================
150
-
151
- def self.import_property(mls_acct)
152
- self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'RES')
159
+
160
+ def self.import_property(mls_acct, save_images = true)
161
+ self.import('RES', "(MLS_ACCT=*#{mls_acct}*)")
153
162
  p = CabooseRets::ResidentialProperty.where(:id => mls_acct.to_i).first
154
163
  if p.nil?
155
- self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'COM')
156
- p = CabooseRets::CommercialProperty.where(:id => mls_acct.to_i).first
164
+ self.import('COM', "(MLS_ACCT=*#{mls_acct}*)")
165
+ p = CabooseRets::CommercialProperty.where(:id => mls_acct.to_i).first
157
166
  if p.nil?
158
- self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'LND')
167
+ self.import('LND', "(MLS_ACCT=*#{mls_acct}*)")
159
168
  p = CabooseRets::LandProperty.where(:id => mls_acct.to_i).first
160
169
  if p.nil?
161
- self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'MUL')
170
+ self.import('MUL', "(MLS_ACCT=*#{mls_acct}*)")
162
171
  p = CabooseRets::MultiFamilyProperty.where(:id => mls_acct.to_i).first
163
172
  return if p.nil?
164
173
  end
165
174
  end
166
175
  end
167
- self.download_property_images(p)
168
- end
169
-
170
- def self.import_residential_property(mls_acct, save_images = true)
171
- self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'RES')
172
- p = CabooseRets::ResidentialProperty.where(:id => mls_acct.to_i).first
173
- self.download_property_images(p, save_images)
174
- self.update_coords(p)
175
- end
176
-
177
- def self.import_commercial_property(mls_acct, save_images = true)
178
- self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'COM')
176
+ self.download_property_images(p, save_images)
177
+ end
178
+
179
+ def self.import_residential_property(mls_acct, save_images = true)
180
+ self.import('RES', "(MLS_ACCT=*#{mls_acct}*)")
181
+ p = CabooseRets::ResidentialProperty.where(:id => mls_acct.to_i).first
182
+ self.download_property_images(p, save_images)
183
+ self.update_coords(p)
184
+ end
185
+
186
+ def self.import_commercial_property(mls_acct, save_images = true)
187
+ self.import('COM', "(MLS_ACCT=*#{mls_acct}*)")
179
188
  p = CabooseRets::CommercialProperty.where(:id => mls_acct.to_i).first
180
189
  self.download_property_images(p, save_images)
181
190
  self.update_coords(p)
182
191
  end
183
-
184
- def self.import_land_property(mls_acct, save_images = true)
185
- self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'LND')
186
- p = CabooseRets::LandProperty.where(:id => mls_acct.to_i).first
192
+
193
+ def self.import_land_property(mls_acct, save_images = true)
194
+ self.import('LND', "(MLS_ACCT=*#{mls_acct}*)")
195
+ p = CabooseRets::LandProperty.where(:id => mls_acct.to_i).first
187
196
  self.download_property_images(p, save_images)
188
197
  self.update_coords(p)
189
198
  end
190
-
191
- def self.import_multi_family_property(mls_acct, save_images = true)
192
- self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'MUL')
199
+
200
+ def self.import_multi_family_property(mls_acct, save_images = true)
201
+ self.import('MUL', "(MLS_ACCT=*#{mls_acct}*)")
193
202
  p = CabooseRets::MultiFamilyProperty.where(:id => mls_acct.to_i).first
194
203
  self.download_property_images(p, save_images)
195
204
  self.update_coords(p)
196
205
  end
197
-
206
+
198
207
  def self.import_office(lo_code, save_images = true)
199
- self.import("(LO_LO_CODE=*#{lo_code}*)", 'Office', 'OFF')
208
+ self.import('OFF', "(LO_LO_CODE=*#{lo_code}*)")
200
209
  office = CabooseRets::Office.where(:lo_code => lo_code.to_s).first
201
210
  self.download_office_image(office) if save_images == true
202
211
  end
203
-
212
+
204
213
  def self.import_agent(la_code, save_images = true)
205
- self.import("(LA_LA_CODE=*#{la_code}*)", 'Agent', 'AGT')
214
+ self.import('AGT', "(LA_LA_CODE=*#{la_code}*)")
206
215
  a = CabooseRets::Agent.where(:la_code => la_code.to_s).first
207
216
  self.download_agent_image(a) if save_images == true
208
217
  end
209
-
218
+
210
219
  def self.import_open_house(id, save_images = true)
211
- self.import("(ID=*#{id}*)", 'OpenHouse', 'OPH')
220
+ self.import('OPH', "(ID=*#{id}*)")
221
+ end
222
+
223
+ def self.import_media(id, save_images = true)
224
+ self.import('GFX', "(MEDIA_ID=*#{id}*)")
212
225
  end
213
226
 
214
227
  #=============================================================================
215
228
  # Images
216
229
  #=============================================================================
217
-
218
- def self.download_property_images(p, save_images = true)
230
+
231
+ def self.download_property_images(p, save_images = true)
219
232
  self.refresh_property_media(p)
220
233
  return if save_images == false
221
-
234
+
222
235
  self.log("-- Downloading images and resizing for #{p.mls_acct}")
223
236
  media = []
224
237
  self.client.get_object(:resource => :Property, :type => :Photo, :location => true, :id => p.id) do |headers, content|
225
-
238
+
226
239
  # Find the associated media record for the image
227
240
  filename = File.basename(headers['location'])
228
241
  m = CabooseRets::Media.where(:mls_acct => p.mls_acct, :file_name => filename).first
229
-
242
+
230
243
  if m.nil?
231
244
  self.log("Can't find media record for #{p.mls_acct} #{filename}.")
232
- else
245
+ else
233
246
  m.image = URI.parse(headers['location'])
234
247
  media << m
235
248
  #m.save
236
- end
249
+ end
237
250
  end
238
-
251
+
239
252
  self.log("-- Uploading images to S3 for #{p.mls_acct}")
240
- media.each do |m|
253
+ media.each do |m|
241
254
  m.save
242
- end
255
+ end
243
256
  end
244
-
257
+
245
258
  def self.refresh_property_media(p)
246
- self.log("-- Deleting images and metadata for #{p.mls_acct}...")
259
+ self.log("-- Deleting images and metadata for #{p.mls_acct}...")
247
260
  #CabooseRets::Media.where(:mls_acct => p.mls_acct, :media_type => 'Photo').destroy_all
248
261
  CabooseRets::Media.where(:mls_acct => p.mls_acct).destroy_all
249
-
250
- self.log("-- Downloading image metadata for #{p.mls_acct}...")
262
+
263
+ self.log("-- Downloading image metadata for #{p.mls_acct}...")
251
264
  params = {
252
265
  :search_type => 'Media',
253
266
  :class => 'GFX',
254
267
  #:query => "(MLS_ACCT=*#{p.id}*),(MEDIA_TYPE=|I)",
255
268
  :query => "(MLS_ACCT=*#{p.id}*)",
256
269
  :timeout => -1
257
- }
258
- self.client.search(params) do |data|
270
+ }
271
+ self.client.search(params) do |data|
259
272
  m = CabooseRets::Media.new
260
273
  m.parse(data)
261
274
  #m.id = m.media_id
262
275
  m.save
263
276
  end
264
277
  end
265
-
266
- def self.download_agent_image(agent)
278
+
279
+ def self.download_agent_image(agent)
267
280
  self.log "Saving image for #{agent.first_name} #{agent.last_name}..."
268
281
  begin
269
282
  self.client.get_object(:resource => :Agent, :type => :Photo, :location => true, :id => agent.la_code) do |headers, content|
@@ -272,9 +285,9 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
272
285
  end
273
286
  rescue RETS::APIError => err
274
287
  self.log "No image for #{agent.first_name} #{agent.last_name}."
275
- end
288
+ end
276
289
  end
277
-
290
+
278
291
  def self.download_office_image(office)
279
292
  self.log "Saving image for #{office.lo_name}..."
280
293
  begin
@@ -282,58 +295,58 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
282
295
  office.image = URI.parse(headers['location'])
283
296
  office.save
284
297
  end
285
- rescue RETS::APIError => err
298
+ rescue RETS::APIError => err
286
299
  self.log "No image for #{office.lo_name}."
287
- end
300
+ end
288
301
  end
289
302
 
290
303
  #=============================================================================
291
304
  # GPS
292
305
  #=============================================================================
293
-
294
- def self.update_coords(p = nil)
306
+
307
+ def self.update_coords(p = nil)
295
308
  if p.nil?
296
309
  models = [CabooseRets::CommercialProperty, CabooseRets::LandProperty, CabooseRets::MultiFamilyProperty, CabooseRets::ResidentialProperty]
297
310
  names = ["commercial", "land", "multi-family", "residential"]
298
311
  i = 0
299
- models.each do |model|
312
+ models.each do |model|
300
313
  self.log "Updating coords #{names[i]} properties..."
301
314
  model.where(:latitude => nil).reorder(:mls_acct).each do |p|
302
- self.update_coords(p)
315
+ self.update_coords(p)
303
316
  end
304
317
  i = i + 1
305
318
  end
306
319
  return
307
320
  end
308
-
321
+
309
322
  self.log "Getting coords for mls_acct #{p.mls_acct}..."
310
323
  coords = self.coords_from_address(CGI::escape "#{p.street_num} #{p.street_name}, #{p.city}, #{p.state} #{p.zip}")
311
324
  return if coords.nil? || coords == false
312
-
325
+
313
326
  p.latitude = coords['lat']
314
327
  p.longitude = coords['lng']
315
- p.save
328
+ p.save
316
329
  end
317
-
318
- def self.coords_from_address(address)
330
+
331
+ def self.coords_from_address(address)
319
332
  begin
320
333
  uri = "https://maps.googleapis.com/maps/api/geocode/json?address=#{address}&sensor=false"
321
- uri.gsub!(" ", "+")
334
+ uri.gsub!(" ", "+")
322
335
  resp = HTTParty.get(uri)
323
336
  json = JSON.parse(resp.body)
324
- return json['results'][0]['geometry']['location']
337
+ return json['results'][0]['geometry']['location']
325
338
  rescue
326
339
  self.log "Error: #{uri}"
327
340
  sleep(2)
328
- return false
341
+ return false
329
342
  end
330
343
  end
331
-
344
+
332
345
  #=============================================================================
333
346
  # Purging
334
347
  #=============================================================================
335
-
336
- def self.purge
348
+
349
+ def self.purge
337
350
  self.purge_residential
338
351
  self.purge_commercial
339
352
  self.purge_land
@@ -343,56 +356,89 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
343
356
  self.purge_open_houses
344
357
  self.purge_media
345
358
  end
346
-
347
- def self.purge_residential() self.purge_helper('Property' , 'RES', 'MLS_ACCT' , 'DATE_MODIFIED' , '2012-01-01', "delete from rets_residential where mls_acct not in (?)") end
348
- def self.purge_commercial() self.purge_helper('Property' , 'COM', 'MLS_ACCT' , 'DATE_MODIFIED' , '2012-01-01', "delete from rets_commercial where mls_acct not in (?)") end
349
- def self.purge_land() self.purge_helper('Property' , 'LND', 'MLS_ACCT' , 'DATE_MODIFIED' , '2012-01-01', "delete from rets_land where mls_acct not in (?)") end
350
- def self.purge_multi_family() self.purge_helper('Property' , 'MUL', 'MLS_ACCT' , 'DATE_MODIFIED' , '2012-01-01', "delete from rets_multi_family where mls_acct not in (?)") end
351
- def self.purge_offices() self.purge_helper('Office' , 'OFF', 'LO_LO_CODE' , 'LO_DATE_MODIFIED' , '2012-01-01', "delete from rets_offices where lo_code not in (?)") end
352
- def self.purge_agents() self.purge_helper('Agent' , 'AGT', 'LA_LA_CODE' , 'LA_DATE_MODIFIED' , '2012-01-01', "delete from rets_agents where la_code not in (?)") end
353
- def self.purge_open_houses() self.purge_helper('OpenHouse' , 'OPH', 'ID' , 'DATE_MODIFIED' , '2012-01-01', "delete from rets_open_houses where id not in (?)") end
354
- def self.purge_media() self.purge_helper('Media' , 'GFX', 'MEDIA_ID' , 'DATE_MODIFIED' , '2012-01-01', "delete from rets_media where media_id not in (?)") end
355
-
356
- def self.purge_helper(search_type, class_type, key_field, date_modified_field, date_modified, delete_query)
357
-
359
+
360
+ def self.purge_residential() self.purge_helper('RES', '2012-01-01') end
361
+ def self.purge_commercial() self.purge_helper('COM', '2012-01-01') end
362
+ def self.purge_land() self.purge_helper('LND', '2012-01-01') end
363
+ def self.purge_multi_family() self.purge_helper('MUL', '2012-01-01') end
364
+ def self.purge_offices() self.purge_helper('OFF', '2012-01-01') end
365
+ def self.purge_agents() self.purge_helper('AGT', '2012-01-01') end
366
+ def self.purge_open_houses() self.purge_helper('OPH', '2012-01-01') end
367
+ def self.purge_media() self.purge_helper('GFX', '2012-01-01') end
368
+
369
+ def self.purge_helper(class_type, date_modified)
370
+ m = self.meta(class_type)
371
+
358
372
  # Get the total number of records
359
- params = { :search_type => search_type, :class => class_type, :query => "(#{date_modified_field}=#{date_modified}T00:00:01+)", :standard_names_only => true, :timeout => -1 }
373
+ params = {
374
+ :search_type => m.search_type,
375
+ :class => class_type,
376
+ :query => "(#{m.date_modified_field}=#{date_modified}T00:00:01+)",
377
+ :standard_names_only => true,
378
+ :timeout => -1
379
+ }
360
380
  self.client.search(params.merge({ :count_mode => :only }))
361
- count = self.client.rets_data[:code] == "20201" ? 0 : self.client.rets_data[:count]
381
+ count = self.client.rets_data[:code] == "20201" ? 0 : self.client.rets_data[:count]
362
382
  batch_count = (count.to_f/5000.0).ceil
363
383
 
364
384
  ids = []
365
- (0...batch_count).each do |i|
366
- self.client.search(params.merge({ :select => [key_field], :limit => 5000, :offset => 5000*i })){ |data| ids << data[key_field] }
367
- end
368
- ActiveRecord::Base.connection.execute(ActiveRecord::Base.send(:sanitize_sql_array, [delete_query, ids]))
385
+ k = m.remote_key_field
386
+ (0...batch_count).each do |i|
387
+ self.client.search(params.merge({ :select => [k], :limit => 5000, :offset => 5000*i })){ |data| ids << data[k] }
388
+ end
389
+
390
+ # Delete any records in the local database that shouldn't be there
391
+ t = m.local_table
392
+ k = m.local_key_field
393
+ query = ["delete from #{t} where #{k} not in (?)", ids]
394
+ ActiveRecord::Base.connection.execute(ActiveRecord::Base.send(:sanitize_sql_array, query))
395
+
396
+ # Find any ids in the remote database that should be in the local database
397
+ query = "select distinct #{k} from #{t}"
398
+ rows = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.send(:sanitize_sql_array, query))
399
+ local_ids = rows.collect{ |row| row[k] }
400
+ ids_to_add = ids - local_ids
401
+ ids_to_add.each do |id|
402
+ puts "Importing #{id}..."
403
+ case class_type
404
+ when 'RES' then self.delay.import_residential_property(id)
405
+ when 'COM' then self.delay.import_commercial_property(id)
406
+ when 'LND' then self.delay.import_land_property(id)
407
+ when 'MUL' then self.delay.import_multi_family_property(id)
408
+ when 'OFF' then self.delay.import_office(id)
409
+ when 'AGT' then self.delay.import_agent(id)
410
+ when 'OPH' then self.delay.import_open_house(id)
411
+ when 'GFX' then self.delay.import_media(id)
412
+ end
413
+ end
414
+
369
415
  end
370
416
 
371
417
  #=============================================================================
372
418
  # Logging
373
419
  #=============================================================================
374
-
420
+
375
421
  def self.log(msg)
376
422
  #puts "[rets_importer] #{msg}"
377
423
  Rails.logger.info("[rets_importer] #{msg}")
378
- end
379
-
424
+ end
425
+
380
426
  #=============================================================================
381
427
  # Locking update task
382
428
  #=============================================================================
383
-
384
- def self.update_rets
429
+
430
+ def self.update_rets
385
431
  return if self.task_is_locked
386
432
  task_started = self.lock_task
387
-
433
+
388
434
  begin
389
- overlap = 30.seconds
390
- if (DateTime.now - self.last_purged).to_i > 1
435
+ overlap = 30.seconds
436
+ if (DateTime.now - self.last_purged).to_i > 1
391
437
  self.purge
392
438
  self.save_last_purged(task_started)
393
439
  overlap = 1.month
394
- end
395
- self.update_after(self.last_updated - overlap)
440
+ end
441
+ self.update_after(self.last_updated - overlap)
396
442
  self.save_last_updated(task_started)
397
443
  self.unlock_task
398
444
  rescue
@@ -400,11 +446,11 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
400
446
  ensure
401
447
  self.unlock_task_if_last_updated(task_started)
402
448
  end
403
-
449
+
404
450
  # Start the same update process in five minutes
405
- self.delay(:run_at => 1.minutes.from_now).update_rets
451
+ self.delay(:run_at => 1.minutes.from_now).update_rets
406
452
  end
407
-
453
+
408
454
  def self.last_updated
409
455
  if !Caboose::Setting.exists?(:name => 'rets_last_updated')
410
456
  Caboose::Setting.create(:name => 'rets_last_updated', :value => '2013-08-06T00:00:01')
@@ -412,7 +458,7 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
412
458
  s = Caboose::Setting.where(:name => 'rets_last_updated').first
413
459
  return DateTime.parse(s.value)
414
460
  end
415
-
461
+
416
462
  def self.last_purged
417
463
  if !Caboose::Setting.exists?(:name => 'rets_last_purged')
418
464
  Caboose::Setting.create(:name => 'rets_last_purged', :value => '2013-08-06T00:00:01')
@@ -420,36 +466,36 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
420
466
  s = Caboose::Setting.where(:name => 'rets_last_purged').first
421
467
  return DateTime.parse(s.value)
422
468
  end
423
-
469
+
424
470
  def self.save_last_updated(d)
425
471
  s = Caboose::Setting.where(:name => 'rets_last_updated').first
426
472
  s.value = d.strftime('%FT%T')
427
473
  s.save
428
474
  end
429
-
475
+
430
476
  def self.save_last_purged(d)
431
477
  s = Caboose::Setting.where(:name => 'rets_last_purged').first
432
478
  s.value = d.strftime('%FT%T')
433
479
  s.save
434
480
  end
435
-
481
+
436
482
  def self.task_is_locked
437
483
  return Caboose::Setting.exists?(:name => 'rets_update_running')
438
484
  end
439
-
485
+
440
486
  def self.lock_task
441
487
  d = DateTime.now.utc - 5.hours
442
488
  Caboose::Setting.create(:name => 'rets_update_running', :value => d.strftime('%F %T'))
443
489
  return d
444
490
  end
445
-
491
+
446
492
  def self.unlock_task
447
493
  Caboose::Setting.where(:name => 'rets_update_running').first.destroy
448
494
  end
449
-
495
+
450
496
  def self.unlock_task_if_last_updated(d)
451
497
  setting = Caboose::Setting.where(:name => 'rets_update_running').first
452
498
  self.unlock_task if setting && d.strftime('%F %T') == setting.value
453
499
  end
454
-
500
+
455
501
  end
@@ -1,3 +1,3 @@
1
1
  module CabooseRets
2
- VERSION = '0.0.62'
2
+ VERSION = '0.0.63'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caboose-rets
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.62
4
+ version: 0.0.63
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Barry