caboose-rets 0.0.52 → 0.0.53
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 +8 -8
- data/app/controllers/caboose_rets/residential_controller.rb +2 -4
- data/app/controllers/caboose_rets/rets_controller.rb +2 -2
- data/app/models/caboose_rets/agent.rb +10 -0
- data/app/models/caboose_rets/commercial_property.rb +3 -8
- data/app/models/caboose_rets/land_property.rb +2 -7
- data/app/models/caboose_rets/multi_family_property.rb +1 -6
- data/app/models/caboose_rets/office.rb +7 -0
- data/app/models/caboose_rets/residential_property.rb +2 -7
- data/app/models/caboose_rets/rets_importer.rb +123 -289
- data/app/models/caboose_rets/rets_importer_bak.rb +512 -0
- data/lib/caboose_rets/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MTBjMzMwZDYzNDkzMTQ3M2U3NTdkMjQ4MDc4OTZmMzk4YjlhM2Q2Yg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NmYyMWM1YmRjYjUwZjg0NWMxODk1NGVmNDUwMzQyMjBjZGJlNTFmZg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZDQ4NWYwYjQ3MGU5NTFlZWRlM2ZmMWEwZjhkODllN2QwZDRjN2FiMmNlZjBk
|
10
|
+
MGIxNDNhOGY3YmRjYzUwMzY4MTAzZDQzNDE0ZTE0ZjVhZjgxNWI2NmY5YTU2
|
11
|
+
MjI1MDVhNmVmNTVkMGZjMGZiMzM0ZWNkNDgwZWQxZWFhODQwODg=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
OGJmNTRiZjE3ZjUzYjE0ODFmNTg4ODY1YTllZGQ2MzQyYTVkMmQ3MjFjNGZl
|
14
|
+
ZGFhYzAyYWVjNGNkM2UxY2NiYjIyN2Y4OTRjZDZlODkxZWYwZmRiZWY3Nzkz
|
15
|
+
ZmIwNjJmZmY5MzFlOTg2YTc0OTRhYjRiODcyOGM1MGIzYTczNWY=
|
@@ -110,11 +110,9 @@ module CabooseRets
|
|
110
110
|
# GET /admin/residential/:mls_acct/refresh
|
111
111
|
def admin_refresh
|
112
112
|
return if !user_is_allowed('properties', 'edit')
|
113
|
-
|
113
|
+
|
114
114
|
p = ResidentialProperty.find(params[:mls_acct])
|
115
|
-
p.delay.refresh_from_mls
|
116
|
-
#RetsImporter.import("(MLS_ACCT=#{p.mls_acct})", 'Property', 'RES')
|
117
|
-
#RetsImporter.download_property_images(p)
|
115
|
+
p.delay.refresh_from_mls
|
118
116
|
|
119
117
|
resp = Caboose::StdClass.new
|
120
118
|
resp.success = "The property's info is being updated from MLS. This may take a few minutes depending on how many images it has."
|
@@ -12,8 +12,8 @@ module CabooseRets
|
|
12
12
|
def admin_import
|
13
13
|
return if !user_is_allowed('properties', 'edit')
|
14
14
|
|
15
|
-
mls_acct = params[:mls_acct].to_i
|
16
|
-
|
15
|
+
mls_acct = params[:mls_acct].to_i
|
16
|
+
CabooseRets::RetsImporter.delay.import_property(mls_acct)
|
17
17
|
|
18
18
|
resp = Caboose::StdClass.new
|
19
19
|
resp.success = "The property is being imported from MLS. This may take a few minutes depending on how many images it has."
|
@@ -30,6 +30,16 @@ class CabooseRets::Agent < ActiveRecord::Base
|
|
30
30
|
self.last_name[2] = self.last_name[2].upcase
|
31
31
|
end
|
32
32
|
end
|
33
|
+
|
34
|
+
def refresh_from_mls
|
35
|
+
CabooseRets::RetsImporter.import("(MLS_ACCT=#{self.mls_acct})", 'Property', 'RES')
|
36
|
+
CabooseRets::RetsImporter.download_property_images(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.refresh_from_mls(la_code)
|
40
|
+
CabooseRets::RetsImporter.import("(LA_LA_CODE=#{la_code})", 'Agent', 'AGT')
|
41
|
+
CabooseRets::RetsImporter.import_property(mls_acct)
|
42
|
+
end
|
33
43
|
|
34
44
|
def parse(data)
|
35
45
|
self.beeper_phone = data['LA_BEEPER_PHONE']
|
@@ -14,16 +14,11 @@ class CabooseRets::CommercialProperty < ActiveRecord::Base
|
|
14
14
|
return media.url
|
15
15
|
end
|
16
16
|
def self.geolocatable() all(conditions: "latitude IS NOT NULL AND longitude IS NOT NULL") end
|
17
|
-
|
18
|
-
def refresh_from_mls
|
19
|
-
CabooseRets::RetsImporter.import("(MLS_ACCT=#{self.mls_acct})", 'Property', 'COM')
|
20
|
-
CabooseRets::RetsImporter.download_property_images(self)
|
21
|
-
end
|
22
17
|
|
23
|
-
def
|
24
|
-
CabooseRets::RetsImporter.
|
18
|
+
def refresh_from_mls
|
19
|
+
CabooseRets::RetsImporter.import_commercial_property(self.mls_acct)
|
25
20
|
end
|
26
|
-
|
21
|
+
|
27
22
|
def parse(data)
|
28
23
|
self.acreage = data['ACREAGE']
|
29
24
|
self.adjoining_land_use = data['ADJOINING_LAND_USE']
|
@@ -15,13 +15,8 @@ class CabooseRets::LandProperty < ActiveRecord::Base
|
|
15
15
|
end
|
16
16
|
def self.geolocatable() all(conditions: "latitude IS NOT NULL AND longitude IS NOT NULL") end
|
17
17
|
|
18
|
-
def refresh_from_mls
|
19
|
-
CabooseRets::RetsImporter.
|
20
|
-
CabooseRets::RetsImporter.download_property_images(self)
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.import_from_mls(mls_acct)
|
24
|
-
CabooseRets::RetsImporter.import_property(mls_acct)
|
18
|
+
def refresh_from_mls
|
19
|
+
CabooseRets::RetsImporter.import_land_property(self.mls_acct)
|
25
20
|
end
|
26
21
|
|
27
22
|
def parse(data)
|
@@ -16,12 +16,7 @@ class CabooseRets::MultiFamilyProperty < ActiveRecord::Base
|
|
16
16
|
def self.geolocatable() all(conditions: "latitude IS NOT NULL AND longitude IS NOT NULL") end
|
17
17
|
|
18
18
|
def refresh_from_mls
|
19
|
-
CabooseRets::RetsImporter.
|
20
|
-
CabooseRets::RetsImporter.download_property_images(self)
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.import_from_mls(mls_acct)
|
24
|
-
CabooseRets::RetsImporter.import_property(mls_acct)
|
19
|
+
CabooseRets::RetsImporter.import_multi_family_property(self.mls_acct)
|
25
20
|
end
|
26
21
|
|
27
22
|
def parse(data)
|
@@ -1,6 +1,13 @@
|
|
1
1
|
|
2
2
|
class CabooseRets::Office < ActiveRecord::Base
|
3
3
|
self.table_name = "rets_offices"
|
4
|
+
has_attached_file :image,
|
5
|
+
:path => 'rets/offices/:lo_code_:style.:extension',
|
6
|
+
:styles => {
|
7
|
+
:thumb => '100x150>',
|
8
|
+
:large => '200x300>'
|
9
|
+
}
|
10
|
+
do_not_validate_attachment_file_type :image
|
4
11
|
attr_accessible :id, :name, :lo_code
|
5
12
|
|
6
13
|
def parse(data)
|
@@ -16,14 +16,9 @@ class CabooseRets::ResidentialProperty < ActiveRecord::Base
|
|
16
16
|
def self.geolocatable() all(conditions: "latitude IS NOT NULL AND longitude IS NOT NULL") end
|
17
17
|
|
18
18
|
def refresh_from_mls
|
19
|
-
CabooseRets::RetsImporter.
|
20
|
-
CabooseRets::RetsImporter.download_property_images(self)
|
19
|
+
CabooseRets::RetsImporter.import_residential_property(self.mls_acct)
|
21
20
|
end
|
22
|
-
|
23
|
-
def self.import_from_mls(mls_acct)
|
24
|
-
CabooseRets::RetsImporter.import_property(mls_acct)
|
25
|
-
end
|
26
|
-
|
21
|
+
|
27
22
|
#=============================================================================
|
28
23
|
|
29
24
|
# Assume this is running in a worker dyno
|
@@ -8,41 +8,7 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
|
|
8
8
|
|
9
9
|
@@rets_client = nil
|
10
10
|
@@config = nil
|
11
|
-
|
12
|
-
'OpenHouse' => ['OPH'],
|
13
|
-
'Media' => ['GFX'],
|
14
|
-
'Property' => ['COM', 'LND', 'MUL', 'RES'],
|
15
|
-
'Agent' => ['AGT'],
|
16
|
-
'Office' => ['OFF']
|
17
|
-
}
|
18
|
-
@@key_fields = {
|
19
|
-
'OpenHouse' => 'ID',
|
20
|
-
'Media' => 'MEDIA_ID',
|
21
|
-
'Property' => 'MLS_ACCT',
|
22
|
-
'Agent' => 'LA_LA_CODE',
|
23
|
-
'Office' => 'LO_LO_CODE'
|
24
|
-
}
|
25
|
-
@@models = {
|
26
|
-
'OPH' => 'CabooseRets::OpenHouse',
|
27
|
-
'GFX' => 'CabooseRets::Media',
|
28
|
-
'COM' => 'CabooseRets::CommercialProperty',
|
29
|
-
'LND' => 'CabooseRets::LandProperty',
|
30
|
-
'MUL' => 'CabooseRets::MultiFamilyProperty',
|
31
|
-
'RES' => 'CabooseRets::ResidentialProperty',
|
32
|
-
'AGT' => 'CabooseRets::Agent',
|
33
|
-
'OFF' => 'CabooseRets::Office'
|
34
|
-
}
|
35
|
-
@@date_modified_fields = {
|
36
|
-
'OPH' => 'DATE_MODIFIED',
|
37
|
-
'GFX' => 'DATE_MODIFIED',
|
38
|
-
'COM' => 'DATE_MODIFIED',
|
39
|
-
'LND' => 'DATE_MODIFIED',
|
40
|
-
'MUL' => 'DATE_MODIFIED',
|
41
|
-
'RES' => 'DATE_MODIFIED',
|
42
|
-
'AGT' => 'LA_DATE_MODIFIED',
|
43
|
-
'OFF' => 'LO_DATE_MODIFIED'
|
44
|
-
}
|
45
|
-
|
11
|
+
|
46
12
|
def self.config
|
47
13
|
return @@config
|
48
14
|
end
|
@@ -52,8 +18,6 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
|
|
52
18
|
'url' => nil, # URL to the RETS login
|
53
19
|
'username' => nil,
|
54
20
|
'password' => nil,
|
55
|
-
'limit' => nil, # How many records to limit per request
|
56
|
-
'days_per_batch' => nil, # When performing a large property import, how many days to search on per batch
|
57
21
|
'temp_path' => nil,
|
58
22
|
'log_file' => nil,
|
59
23
|
'media_base_url' => nil
|
@@ -77,37 +41,83 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
|
|
77
41
|
end
|
78
42
|
|
79
43
|
#=============================================================================
|
80
|
-
#
|
44
|
+
# Import method
|
81
45
|
#=============================================================================
|
82
46
|
|
83
|
-
def self.
|
84
|
-
self.
|
85
|
-
self.
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
self.
|
95
|
-
|
96
|
-
|
47
|
+
def self.import(query, search_type, class_type)
|
48
|
+
self.log("Importing #{search_type}:#{class_type} with query #{query}...")
|
49
|
+
self.get_config if @@config.nil? || @@config['url'].nil?
|
50
|
+
params = {
|
51
|
+
:search_type => search_type,
|
52
|
+
:class => class_type,
|
53
|
+
:query => query,
|
54
|
+
:limit => -1,
|
55
|
+
:timeout => -1
|
56
|
+
}
|
57
|
+
obj = nil
|
58
|
+
self.client.search(params) do |data|
|
59
|
+
obj = self.get_instance_with_id(class_type, data)
|
60
|
+
if obj.nil?
|
61
|
+
self.log("Error: object is nil")
|
62
|
+
self.log(data.inspect)
|
63
|
+
next
|
64
|
+
end
|
65
|
+
obj.parse(data)
|
66
|
+
obj.save
|
67
|
+
end
|
97
68
|
end
|
98
69
|
|
99
|
-
def self.
|
100
|
-
|
101
|
-
|
102
|
-
|
70
|
+
def self.get_instance_with_id(class_type, data)
|
71
|
+
obj = nil
|
72
|
+
m = case class_type
|
73
|
+
when 'OPH' then CabooseRets::OpenHouse
|
74
|
+
when 'GFX' then CabooseRets::Media
|
75
|
+
when 'COM' then CabooseRets::CommercialProperty
|
76
|
+
when 'LND' then CabooseRets::LandProperty
|
77
|
+
when 'MUL' then CabooseRets::MultiFamilyProperty
|
78
|
+
when 'RES' then CabooseRets::ResidentialProperty
|
79
|
+
when 'AGT' then CabooseRets::Agent
|
80
|
+
when 'OFF' then CabooseRets::Office
|
81
|
+
end
|
82
|
+
obj = case class_type
|
83
|
+
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 )
|
84
|
+
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'] )
|
85
|
+
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 )
|
86
|
+
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 )
|
87
|
+
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 )
|
88
|
+
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 )
|
89
|
+
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'] )
|
90
|
+
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'] )
|
91
|
+
end
|
92
|
+
return obj
|
103
93
|
end
|
104
94
|
|
105
95
|
#=============================================================================
|
106
|
-
#
|
96
|
+
# Main updater
|
107
97
|
#=============================================================================
|
108
|
-
|
109
|
-
def self.
|
98
|
+
|
99
|
+
def self.update_after(date_modified)
|
100
|
+
self.update_residential_properties_modified_after(date_modified)
|
101
|
+
self.update_commercial_properties_modified_after(date_modified)
|
102
|
+
self.update_land_properties_modified_after(date_modified)
|
103
|
+
self.update_multi_family_properties_modified_after(date_modified)
|
104
|
+
self.update_offices_modified_after(date_modified)
|
105
|
+
self.update_agents_modified_after(date_modified)
|
106
|
+
self.update_open_houses_modified_after(date_modified)
|
107
|
+
end
|
108
|
+
def self.update_residential_properties_modified_after(date_modified) 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.import_residential_property data['MLS_ACCT' ] } end
|
109
|
+
def self.update_commercial_properties_modified_after(date_modified) 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.import_commercial_property data['MLS_ACCT' ] } end
|
110
|
+
def self.update_land_properties_modified_after(date_modified) 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.import_land_property data['MLS_ACCT' ] } end
|
111
|
+
def self.update_multi_family_properties_modified_after(date_modified) 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.import_multi_family_property data['MLS_ACCT' ] } end
|
112
|
+
def self.update_offices_modified_after(date_modified) 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.import_office data['LO_LO_CODE'] } end
|
113
|
+
def self.update_agents_modified_after(date_modified) 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.import_agent data['LA_LA_CODE'] } end
|
114
|
+
def self.update_open_houses_modified_after(date_modified) 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.import_open_house data['ID' ] } end
|
110
115
|
|
116
|
+
#=============================================================================
|
117
|
+
# Single model import methods (called from a worker dyno)
|
118
|
+
#=============================================================================
|
119
|
+
|
120
|
+
def self.import_property(mls_acct)
|
111
121
|
self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'RES')
|
112
122
|
p = CabooseRets::ResidentialProperty.where(:id => mls_acct.to_i).first
|
113
123
|
if p.nil?
|
@@ -126,143 +136,53 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
|
|
126
136
|
self.download_property_images(p)
|
127
137
|
end
|
128
138
|
|
129
|
-
def self.
|
130
|
-
self.
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
while d.strftime('%FT%T') <= DateTime.now.strftime('%FT%T') do
|
136
|
-
break if d.nil?
|
137
|
-
|
138
|
-
d2 = d.strftime('%FT%T')
|
139
|
-
d2 << "-"
|
140
|
-
d2 << (d+@@config['days_per_batch']).strftime('%FT%T')
|
141
|
-
|
142
|
-
query = "(#{date_modified_field}=#{d2})"
|
143
|
-
self.import(query, search_type, class_type)
|
144
|
-
|
145
|
-
d = d + @@config['days_per_batch']
|
146
|
-
end
|
139
|
+
def self.import_residential_property(mls_acct)
|
140
|
+
self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'RES')
|
141
|
+
p = CabooseRets::ResidentialProperty.where(:id => mls_acct.to_i).first
|
142
|
+
self.download_property_images(p)
|
143
|
+
self.update_coords(p)
|
147
144
|
end
|
148
145
|
|
149
|
-
def self.
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
:query => query,
|
155
|
-
:count_mode => :only,
|
156
|
-
:timeout => -1,
|
157
|
-
)
|
158
|
-
# Return if no records found
|
159
|
-
if (self.client.rets_data[:code] == "20201")
|
160
|
-
self.log "No #{search_type}:#{class_type} records found for query: #{query}"
|
161
|
-
return
|
162
|
-
else
|
163
|
-
count = self.client.rets_data[:count]
|
164
|
-
self.log "Importing #{count} #{search_type}:#{class_type} record" + (count == 1 ? "" : "s") + "..."
|
165
|
-
end
|
166
|
-
|
167
|
-
count = self.client.rets_data[:count]
|
168
|
-
batch_count = (count.to_f/@@config['limit'].to_f).ceil
|
169
|
-
|
170
|
-
(0...batch_count).each do |i|
|
171
|
-
params = {
|
172
|
-
:search_type => search_type,
|
173
|
-
:class => class_type,
|
174
|
-
:query => query,
|
175
|
-
:limit => @@config['limit'],
|
176
|
-
:offset => @@config['limit'] * i,
|
177
|
-
:timeout => -1
|
178
|
-
}
|
179
|
-
obj = nil
|
180
|
-
self.client.search(params) do |data|
|
181
|
-
m = @@models[class_type]
|
182
|
-
#key_field = @@key_fields[search_type]
|
183
|
-
#id = data[key_field].to_i
|
184
|
-
#obj = m.exists?(id) ? m.find(id) : m.new
|
185
|
-
obj = self.get_instance_with_id(m, data)
|
186
|
-
if obj.nil?
|
187
|
-
puts "Error: object is nil"
|
188
|
-
puts m.inspect
|
189
|
-
puts data.inspect
|
190
|
-
next
|
191
|
-
end
|
192
|
-
obj.parse(data)
|
193
|
-
#obj.id = id
|
194
|
-
obj.save
|
195
|
-
end
|
196
|
-
|
197
|
-
case obj
|
198
|
-
when CabooseRets::CommercialProperty, CabooseRets::LandProperty, CabooseRets::MultiFamilyProperty, CabooseRets::ResidentialProperty
|
199
|
-
self.update_coords(obj)
|
200
|
-
end
|
201
|
-
end
|
146
|
+
def self.import_commercial_property(mls_acct)
|
147
|
+
self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'COM')
|
148
|
+
p = CabooseRets::CommercialProperty.where(:id => mls_acct.to_i).first
|
149
|
+
self.download_property_images(p)
|
150
|
+
self.update_coords(p)
|
202
151
|
end
|
203
152
|
|
204
|
-
def self.
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
when 'CabooseRets::Media' then obj = m.where(:media_id => data['MEDIA_ID'] ).exists? ? m.where(:media_id => data['MEDIA_ID'] ).first : m.new(:media_id => data['MEDIA_ID'] )
|
210
|
-
when 'CabooseRets::CommercialProperty' then obj = 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 )
|
211
|
-
when 'CabooseRets::LandProperty' then obj = 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 )
|
212
|
-
when 'CabooseRets::MultiFamilyProperty' then obj = 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 )
|
213
|
-
when 'CabooseRets::ResidentialProperty' then obj = 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 )
|
214
|
-
when 'CabooseRets::Agent' then obj = 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'] )
|
215
|
-
when 'CabooseRets::Office' then obj = 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'] )
|
216
|
-
end
|
217
|
-
return obj
|
153
|
+
def self.import_land_property(mls_acct)
|
154
|
+
self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'LND')
|
155
|
+
p = CabooseRets::LandProperty.where(:id => mls_acct.to_i).first
|
156
|
+
self.download_property_images(p)
|
157
|
+
self.update_coords(p)
|
218
158
|
end
|
219
159
|
|
220
|
-
|
221
|
-
|
222
|
-
|
160
|
+
def self.import_multi_family_property(mls_acct)
|
161
|
+
self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'MUL')
|
162
|
+
p = CabooseRets::MultiFamilyProperty.where(:id => mls_acct.to_i).first
|
163
|
+
self.download_property_images(p)
|
164
|
+
self.update_coords(p)
|
165
|
+
end
|
223
166
|
|
224
|
-
def self.
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
end
|
229
|
-
return
|
230
|
-
end
|
231
|
-
self.download_agent_images(agent)
|
167
|
+
def self.import_office(lo_code)
|
168
|
+
self.import("(LO_LO_CODE=*#{lo_code}*)", 'Office', 'OFF')
|
169
|
+
office = CabooseRets::Office.where(:lo_code => lo_code.to_s).first
|
170
|
+
self.download_office_image(office)
|
232
171
|
end
|
233
172
|
|
234
|
-
def self.
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
self.client.get_object(:resource => :Agent, :type => :Photo, :location => true, :id => a.la_code) do |headers, content|
|
239
|
-
a.image = URI.parse(headers['location'])
|
240
|
-
a.save
|
241
|
-
end
|
242
|
-
rescue RETS::APIError => err
|
243
|
-
self.log "No image for #{a.first_name} #{a.last_name}."
|
244
|
-
end
|
173
|
+
def self.import_agent(la_code)
|
174
|
+
self.import("(LA_LA_CODE=*#{la_code}*)", 'Agent', 'AGT')
|
175
|
+
a = CabooseRets::Agent.where(:la_code => la_code.to_s).first
|
176
|
+
self.download_agent_image(a)
|
245
177
|
end
|
246
178
|
|
179
|
+
def self.import_open_house(id)
|
180
|
+
self.import("(ID=*#{id}*)", 'OpenHouse', 'OPH')
|
181
|
+
end
|
182
|
+
|
247
183
|
#=============================================================================
|
248
|
-
#
|
184
|
+
# Images
|
249
185
|
#=============================================================================
|
250
|
-
|
251
|
-
def self.download_property_images_modified_after(date_modified)
|
252
|
-
models = [CabooseRets::CommercialProperty, CabooseRets::LandProperty, CabooseRets::MultiFamilyProperty, CabooseRets::ResidentialProperty]
|
253
|
-
names = ["commercial", "land", "multi-family", "residential"]
|
254
|
-
i = 0
|
255
|
-
models.each do |model|
|
256
|
-
count = model.where("photo_date_modified > ?", date_modified.strftime('%FT%T')).count
|
257
|
-
j = 1
|
258
|
-
model.where("photo_date_modified > ?", date_modified.strftime('%FT%T')).reorder(:mls_acct).each do |p|
|
259
|
-
self.log("Downloading images for #{j} of #{count} #{names[i]} properties...")
|
260
|
-
self.download_property_images(p)
|
261
|
-
j = j + 1
|
262
|
-
end
|
263
|
-
i = i + 1
|
264
|
-
end
|
265
|
-
end
|
266
186
|
|
267
187
|
def self.download_property_images(p)
|
268
188
|
self.refresh_property_media(p)
|
@@ -311,115 +231,29 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
|
|
311
231
|
end
|
312
232
|
end
|
313
233
|
|
314
|
-
def self.
|
315
|
-
|
316
|
-
|
317
|
-
:
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
if (self.client.rets_data[:code] == "20201")
|
325
|
-
self.log "No virtual tours found."
|
326
|
-
return
|
327
|
-
else
|
328
|
-
count = self.client.rets_data[:count]
|
329
|
-
self.log "Importing #{count} virtual tours..."
|
330
|
-
end
|
331
|
-
|
332
|
-
count = self.client.rets_data[:count]
|
333
|
-
batch_count = (count.to_f/@@config['limit'].to_f).ceil
|
334
|
-
|
335
|
-
(0...batch_count).each do |i|
|
336
|
-
params = {
|
337
|
-
:search_type => 'Media',
|
338
|
-
:class => 'GFX',
|
339
|
-
:query => "(MEDIA_TYPE=|V)",
|
340
|
-
:limit => @@config['limit'],
|
341
|
-
:offset => @@config['limit'] * i,
|
342
|
-
:timeout => -1
|
343
|
-
}
|
344
|
-
obj = nil
|
345
|
-
self.client.search(params) do |data|
|
346
|
-
mls_acct = data['MLS_ACCT'].to_i
|
347
|
-
m = CabooseRets::Media.exists?("mls_acct = '#{mls_acct}' and media_type = 'Virtual Tour'") ? CabooseRets::Media.where("mls_acct = '#{mls_acct}' and media_type = 'Virtual Tour'").first : CabooseRets::Media.new
|
348
|
-
m.parse(data)
|
349
|
-
m.save
|
350
|
-
end
|
351
|
-
end
|
234
|
+
def self.download_agent_image(agent)
|
235
|
+
self.log "Saving image for #{agent.first_name} #{agent.last_name}..."
|
236
|
+
begin
|
237
|
+
self.client.get_object(:resource => :Agent, :type => :Photo, :location => true, :id => agent.la_code) do |headers, content|
|
238
|
+
agent.image = URI.parse(headers['location'])
|
239
|
+
agent.save
|
240
|
+
end
|
241
|
+
rescue RETS::APIError => err
|
242
|
+
self.log "No image for #{agent.first_name} #{agent.last_name}."
|
243
|
+
end
|
352
244
|
end
|
353
245
|
|
354
|
-
def self.
|
355
|
-
self.log
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
:timeout => -1
|
365
|
-
}
|
366
|
-
self.client.search(params) do |data|
|
367
|
-
m = CabooseRets::Media.new
|
368
|
-
m.parse(data)
|
369
|
-
#m.id = m.media_id
|
370
|
-
m.save
|
371
|
-
end
|
246
|
+
def self.download_office_image(office)
|
247
|
+
self.log "Saving image for #{office.lo_name}..."
|
248
|
+
begin
|
249
|
+
self.client.get_object(:resource => :Office, :type => :Photo, :location => true, :id => office.lo_code) do |headers, content|
|
250
|
+
office.image = URI.parse(headers['location'])
|
251
|
+
office.save
|
252
|
+
end
|
253
|
+
rescue RETS::APIError => err
|
254
|
+
self.log "No image for #{office.lo_name}."
|
255
|
+
end
|
372
256
|
end
|
373
|
-
|
374
|
-
#def self.download_property_images(p)
|
375
|
-
#
|
376
|
-
# self.log("-- Deleting images and metadata for #{p.mls_acct}...")
|
377
|
-
# CabooseRets::Media.where(:mls_acct => p.mls_acct, :media_type => 'Photo').destroy_all
|
378
|
-
#
|
379
|
-
# self.log("-- Downloading image metadata for #{p.mls_acct}...")
|
380
|
-
# params = {
|
381
|
-
# :search_type => 'Media',
|
382
|
-
# :class => 'GFX',
|
383
|
-
# :query => "(MLS_ACCT=*#{p.id}*),(MEDIA_TYPE=|I)",
|
384
|
-
# :timeout => -1
|
385
|
-
# }
|
386
|
-
# puts "Before search #{p.id}"
|
387
|
-
# self.client.search(params) do |data|
|
388
|
-
# puts "download_property_images self.client.search #{p.id}"
|
389
|
-
# #m = CabooseRets::Media.new
|
390
|
-
# #m.parse(data)
|
391
|
-
# #m.id = m.media_id
|
392
|
-
# #m.save
|
393
|
-
# self.download_property_images2(p)
|
394
|
-
# end
|
395
|
-
# puts "After search #{p.id}"
|
396
|
-
#end
|
397
|
-
#
|
398
|
-
#def self.download_property_images2(p)
|
399
|
-
# puts "download_property_images2 #{p.id}"
|
400
|
-
# sleep(1)
|
401
|
-
#
|
402
|
-
# #self.log("-- Downloading images and resizing for #{p.mls_acct}")
|
403
|
-
# #media = []
|
404
|
-
# #self.client.get_object(:resource => :Property, :type => :Photo, :location => true, :id => p.id) do |headers, content|
|
405
|
-
# #
|
406
|
-
# ## Find the associated media record for the image
|
407
|
-
# #filename = File.basename(headers['location'])
|
408
|
-
# #m = CabooseRets::Media.where(:mls_acct => p.mls_acct, :file_name => filename).first
|
409
|
-
# #
|
410
|
-
# #if m.nil?
|
411
|
-
# # self.log("Can't find media record for #{p.mls_acct} #{filename}.")
|
412
|
-
# #else
|
413
|
-
# # m.image = URI.parse(headers['location'])
|
414
|
-
# # media << m
|
415
|
-
# # #m.save
|
416
|
-
# #end
|
417
|
-
# #
|
418
|
-
# #self.log("-- Uploading images to S3 for #{p.mls_acct}")
|
419
|
-
# #media.each do |m|
|
420
|
-
# # m.save
|
421
|
-
# #end
|
422
|
-
#end
|
423
257
|
|
424
258
|
#=============================================================================
|
425
259
|
# GPS
|
@@ -0,0 +1,512 @@
|
|
1
|
+
require 'ruby-rets'
|
2
|
+
require 'httparty'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
# http://rets.solidearth.com/ClientHome.aspx
|
6
|
+
|
7
|
+
class CabooseRets::RetsImporter # < ActiveRecord::Base
|
8
|
+
|
9
|
+
@@rets_client = nil
|
10
|
+
@@config = nil
|
11
|
+
@@object_types = {
|
12
|
+
'OpenHouse' => ['OPH'],
|
13
|
+
'Media' => ['GFX'],
|
14
|
+
'Property' => ['COM', 'LND', 'MUL', 'RES'],
|
15
|
+
'Agent' => ['AGT'],
|
16
|
+
'Office' => ['OFF']
|
17
|
+
}
|
18
|
+
@@key_fields = {
|
19
|
+
'OpenHouse' => 'ID',
|
20
|
+
'Media' => 'MEDIA_ID',
|
21
|
+
'Property' => 'MLS_ACCT',
|
22
|
+
'Agent' => 'LA_LA_CODE',
|
23
|
+
'Office' => 'LO_LO_CODE'
|
24
|
+
}
|
25
|
+
@@models = {
|
26
|
+
'OPH' => 'CabooseRets::OpenHouse',
|
27
|
+
'GFX' => 'CabooseRets::Media',
|
28
|
+
'COM' => 'CabooseRets::CommercialProperty',
|
29
|
+
'LND' => 'CabooseRets::LandProperty',
|
30
|
+
'MUL' => 'CabooseRets::MultiFamilyProperty',
|
31
|
+
'RES' => 'CabooseRets::ResidentialProperty',
|
32
|
+
'AGT' => 'CabooseRets::Agent',
|
33
|
+
'OFF' => 'CabooseRets::Office'
|
34
|
+
}
|
35
|
+
@@date_modified_fields = {
|
36
|
+
'OPH' => 'DATE_MODIFIED',
|
37
|
+
'GFX' => 'DATE_MODIFIED',
|
38
|
+
'COM' => 'DATE_MODIFIED',
|
39
|
+
'LND' => 'DATE_MODIFIED',
|
40
|
+
'MUL' => 'DATE_MODIFIED',
|
41
|
+
'RES' => 'DATE_MODIFIED',
|
42
|
+
'AGT' => 'LA_DATE_MODIFIED',
|
43
|
+
'OFF' => 'LO_DATE_MODIFIED'
|
44
|
+
}
|
45
|
+
|
46
|
+
def self.config
|
47
|
+
return @@config
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.get_config
|
51
|
+
@@config = {
|
52
|
+
'url' => nil, # URL to the RETS login
|
53
|
+
'username' => nil,
|
54
|
+
'password' => nil,
|
55
|
+
'limit' => nil, # How many records to limit per request
|
56
|
+
'days_per_batch' => nil, # When performing a large property import, how many days to search on per batch
|
57
|
+
'temp_path' => nil,
|
58
|
+
'log_file' => nil,
|
59
|
+
'media_base_url' => nil
|
60
|
+
}
|
61
|
+
config = YAML::load(File.open("#{Rails.root}/config/rets_importer.yml"))
|
62
|
+
config = config[Rails.env]
|
63
|
+
config.each { |key,val| @@config[key] = val }
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.client
|
67
|
+
self.get_config if @@config.nil? || @@config['url'].nil?
|
68
|
+
|
69
|
+
if (@@rets_client.nil?)
|
70
|
+
@@rets_client = RETS::Client.login(
|
71
|
+
:url => @@config['url'],
|
72
|
+
:username => @@config['username'],
|
73
|
+
:password => @@config['password']
|
74
|
+
)
|
75
|
+
end
|
76
|
+
return @@rets_client
|
77
|
+
end
|
78
|
+
|
79
|
+
#=============================================================================
|
80
|
+
# Main updater
|
81
|
+
#=============================================================================
|
82
|
+
|
83
|
+
def self.update_after(date_modified)
|
84
|
+
self.update_data_after(date_modified)
|
85
|
+
self.update_images_after(date_modified)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.update_data_after(date_modified)
|
89
|
+
self.get_config if @@config.nil? || @@config['url'].nil?
|
90
|
+
self.import_modified_after(date_modified, 'Agent' , 'AGT')
|
91
|
+
self.import_modified_after(date_modified, 'Office' , 'OFF')
|
92
|
+
self.import_modified_after(date_modified, 'OpenHouse' , 'OPH')
|
93
|
+
#self.import_modified_after(date_modified, 'Property' , 'COM')
|
94
|
+
self.import_modified_after(date_modified, 'Property' , 'LND')
|
95
|
+
self.import_modified_after(date_modified, 'Property' , 'MUL')
|
96
|
+
self.import_modified_after(date_modified, 'Property' , 'RES')
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.update_images_after(date_modified)
|
100
|
+
self.get_config if @@config.nil? || @@config['url'].nil?
|
101
|
+
self.download_agent_images_modified_after(date_modified)
|
102
|
+
self.download_property_images_modified_after(date_modified)
|
103
|
+
end
|
104
|
+
|
105
|
+
#=============================================================================
|
106
|
+
# Data
|
107
|
+
#=============================================================================
|
108
|
+
|
109
|
+
def self.import_property(mls_acct)
|
110
|
+
|
111
|
+
self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'RES')
|
112
|
+
p = CabooseRets::ResidentialProperty.where(:id => mls_acct.to_i).first
|
113
|
+
if p.nil?
|
114
|
+
self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'COM')
|
115
|
+
p = CabooseRets::CommercialProperty.where(:id => mls_acct.to_i).first
|
116
|
+
if p.nil?
|
117
|
+
self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'LND')
|
118
|
+
p = CabooseRets::LandProperty.where(:id => mls_acct.to_i).first
|
119
|
+
if p.nil?
|
120
|
+
self.import("(MLS_ACCT=*#{mls_acct}*)", 'Property', 'MUL')
|
121
|
+
p = CabooseRets::MultiFamilyProperty.where(:id => mls_acct.to_i).first
|
122
|
+
return if p.nil?
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
self.download_property_images(p)
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.import_modified_after(date_modified, search_type = nil, class_type = nil)
|
130
|
+
self.get_config if @@config.nil? || @@config['url'].nil?
|
131
|
+
|
132
|
+
d = date_modified
|
133
|
+
date_modified_field = @@date_modified_fields[class_type]
|
134
|
+
|
135
|
+
while d.strftime('%FT%T') <= DateTime.now.strftime('%FT%T') do
|
136
|
+
break if d.nil?
|
137
|
+
|
138
|
+
d2 = d.strftime('%FT%T')
|
139
|
+
d2 << "-"
|
140
|
+
d2 << (d+@@config['days_per_batch']).strftime('%FT%T')
|
141
|
+
|
142
|
+
query = "(#{date_modified_field}=#{d2})"
|
143
|
+
self.import(query, search_type, class_type)
|
144
|
+
|
145
|
+
d = d + @@config['days_per_batch']
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.import(query, search_type, class_type)
|
150
|
+
# See how many records we have
|
151
|
+
self.client.search(
|
152
|
+
:search_type => search_type,
|
153
|
+
:class => class_type,
|
154
|
+
:query => query,
|
155
|
+
:count_mode => :only,
|
156
|
+
:timeout => -1,
|
157
|
+
)
|
158
|
+
# Return if no records found
|
159
|
+
if (self.client.rets_data[:code] == "20201")
|
160
|
+
self.log "No #{search_type}:#{class_type} records found for query: #{query}"
|
161
|
+
return
|
162
|
+
else
|
163
|
+
count = self.client.rets_data[:count]
|
164
|
+
self.log "Importing #{count} #{search_type}:#{class_type} record" + (count == 1 ? "" : "s") + "..."
|
165
|
+
end
|
166
|
+
|
167
|
+
count = self.client.rets_data[:count]
|
168
|
+
batch_count = (count.to_f/@@config['limit'].to_f).ceil
|
169
|
+
|
170
|
+
(0...batch_count).each do |i|
|
171
|
+
params = {
|
172
|
+
:search_type => search_type,
|
173
|
+
:class => class_type,
|
174
|
+
:query => query,
|
175
|
+
:limit => @@config['limit'],
|
176
|
+
:offset => @@config['limit'] * i,
|
177
|
+
:timeout => -1
|
178
|
+
}
|
179
|
+
obj = nil
|
180
|
+
self.client.search(params) do |data|
|
181
|
+
m = @@models[class_type]
|
182
|
+
#key_field = @@key_fields[search_type]
|
183
|
+
#id = data[key_field].to_i
|
184
|
+
#obj = m.exists?(id) ? m.find(id) : m.new
|
185
|
+
obj = self.get_instance_with_id(m, data)
|
186
|
+
if obj.nil?
|
187
|
+
puts "Error: object is nil"
|
188
|
+
puts m.inspect
|
189
|
+
puts data.inspect
|
190
|
+
next
|
191
|
+
end
|
192
|
+
obj.parse(data)
|
193
|
+
#obj.id = id
|
194
|
+
obj.save
|
195
|
+
end
|
196
|
+
|
197
|
+
case obj
|
198
|
+
when CabooseRets::CommercialProperty, CabooseRets::LandProperty, CabooseRets::MultiFamilyProperty, CabooseRets::ResidentialProperty
|
199
|
+
self.update_coords(obj)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.get_instance_with_id(model, data)
|
205
|
+
obj = nil
|
206
|
+
m = model.constantize
|
207
|
+
case model
|
208
|
+
when 'CabooseRets::OpenHouse' then obj = m.where(:id => data['ID'].to_i ).exists? ? m.where(:id => data['ID'].to_i ).first : m.new(:id => data['ID'].to_i )
|
209
|
+
when 'CabooseRets::Media' then obj = m.where(:media_id => data['MEDIA_ID'] ).exists? ? m.where(:media_id => data['MEDIA_ID'] ).first : m.new(:media_id => data['MEDIA_ID'] )
|
210
|
+
when 'CabooseRets::CommercialProperty' then obj = 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 )
|
211
|
+
when 'CabooseRets::LandProperty' then obj = 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 )
|
212
|
+
when 'CabooseRets::MultiFamilyProperty' then obj = 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 )
|
213
|
+
when 'CabooseRets::ResidentialProperty' then obj = 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 )
|
214
|
+
when 'CabooseRets::Agent' then obj = 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'] )
|
215
|
+
when 'CabooseRets::Office' then obj = 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'] )
|
216
|
+
end
|
217
|
+
return obj
|
218
|
+
end
|
219
|
+
|
220
|
+
#=============================================================================
|
221
|
+
# Agent Images
|
222
|
+
#=============================================================================
|
223
|
+
|
224
|
+
def self.download_agent_images_modified_after(date_modified, agent = nil)
|
225
|
+
if agent.nil?
|
226
|
+
CabooseRets::Agent.where('photo_date_modified > ?', date_modified.strftime('%FT%T')).reorder('last_name, first_name').all.each do |a|
|
227
|
+
self.download_agent_images_modified_after(date_modified, a)
|
228
|
+
end
|
229
|
+
return
|
230
|
+
end
|
231
|
+
self.download_agent_images(agent)
|
232
|
+
end
|
233
|
+
|
234
|
+
def self.download_agent_images(agent)
|
235
|
+
a = agent
|
236
|
+
self.log "Saving image for #{a.first_name} #{a.last_name}..."
|
237
|
+
begin
|
238
|
+
self.client.get_object(:resource => :Agent, :type => :Photo, :location => true, :id => a.la_code) do |headers, content|
|
239
|
+
a.image = URI.parse(headers['location'])
|
240
|
+
a.save
|
241
|
+
end
|
242
|
+
rescue RETS::APIError => err
|
243
|
+
self.log "No image for #{a.first_name} #{a.last_name}."
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
#=============================================================================
|
248
|
+
# Property Images
|
249
|
+
#=============================================================================
|
250
|
+
|
251
|
+
def self.download_property_images_modified_after(date_modified)
|
252
|
+
models = [CabooseRets::CommercialProperty, CabooseRets::LandProperty, CabooseRets::MultiFamilyProperty, CabooseRets::ResidentialProperty]
|
253
|
+
names = ["commercial", "land", "multi-family", "residential"]
|
254
|
+
i = 0
|
255
|
+
models.each do |model|
|
256
|
+
count = model.where("photo_date_modified > ?", date_modified.strftime('%FT%T')).count
|
257
|
+
j = 1
|
258
|
+
model.where("photo_date_modified > ?", date_modified.strftime('%FT%T')).reorder(:mls_acct).each do |p|
|
259
|
+
self.log("Downloading images for #{j} of #{count} #{names[i]} properties...")
|
260
|
+
self.download_property_images(p)
|
261
|
+
j = j + 1
|
262
|
+
end
|
263
|
+
i = i + 1
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def self.download_property_images(p)
|
268
|
+
self.refresh_property_media(p)
|
269
|
+
|
270
|
+
self.log("-- Downloading images and resizing for #{p.mls_acct}")
|
271
|
+
media = []
|
272
|
+
self.client.get_object(:resource => :Property, :type => :Photo, :location => true, :id => p.id) do |headers, content|
|
273
|
+
|
274
|
+
# Find the associated media record for the image
|
275
|
+
filename = File.basename(headers['location'])
|
276
|
+
m = CabooseRets::Media.where(:mls_acct => p.mls_acct, :file_name => filename).first
|
277
|
+
|
278
|
+
if m.nil?
|
279
|
+
self.log("Can't find media record for #{p.mls_acct} #{filename}.")
|
280
|
+
else
|
281
|
+
m.image = URI.parse(headers['location'])
|
282
|
+
media << m
|
283
|
+
#m.save
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
self.log("-- Uploading images to S3 for #{p.mls_acct}")
|
288
|
+
media.each do |m|
|
289
|
+
m.save
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def self.refresh_property_media(p)
|
294
|
+
self.log("-- Deleting images and metadata for #{p.mls_acct}...")
|
295
|
+
#CabooseRets::Media.where(:mls_acct => p.mls_acct, :media_type => 'Photo').destroy_all
|
296
|
+
CabooseRets::Media.where(:mls_acct => p.mls_acct).destroy_all
|
297
|
+
|
298
|
+
self.log("-- Downloading image metadata for #{p.mls_acct}...")
|
299
|
+
params = {
|
300
|
+
:search_type => 'Media',
|
301
|
+
:class => 'GFX',
|
302
|
+
#:query => "(MLS_ACCT=*#{p.id}*),(MEDIA_TYPE=|I)",
|
303
|
+
:query => "(MLS_ACCT=*#{p.id}*)",
|
304
|
+
:timeout => -1
|
305
|
+
}
|
306
|
+
self.client.search(params) do |data|
|
307
|
+
m = CabooseRets::Media.new
|
308
|
+
m.parse(data)
|
309
|
+
#m.id = m.media_id
|
310
|
+
m.save
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def self.refresh_all_virtual_tours
|
315
|
+
# See how many records we have
|
316
|
+
self.client.search(
|
317
|
+
:search_type => 'Media',
|
318
|
+
:class => 'GFX',
|
319
|
+
:query => "(MEDIA_TYPE=|V)",
|
320
|
+
:count_mode => :only,
|
321
|
+
:timeout => -1
|
322
|
+
)
|
323
|
+
# Return if no records found
|
324
|
+
if (self.client.rets_data[:code] == "20201")
|
325
|
+
self.log "No virtual tours found."
|
326
|
+
return
|
327
|
+
else
|
328
|
+
count = self.client.rets_data[:count]
|
329
|
+
self.log "Importing #{count} virtual tours..."
|
330
|
+
end
|
331
|
+
|
332
|
+
count = self.client.rets_data[:count]
|
333
|
+
batch_count = (count.to_f/@@config['limit'].to_f).ceil
|
334
|
+
|
335
|
+
(0...batch_count).each do |i|
|
336
|
+
params = {
|
337
|
+
:search_type => 'Media',
|
338
|
+
:class => 'GFX',
|
339
|
+
:query => "(MEDIA_TYPE=|V)",
|
340
|
+
:limit => @@config['limit'],
|
341
|
+
:offset => @@config['limit'] * i,
|
342
|
+
:timeout => -1
|
343
|
+
}
|
344
|
+
obj = nil
|
345
|
+
self.client.search(params) do |data|
|
346
|
+
mls_acct = data['MLS_ACCT'].to_i
|
347
|
+
m = CabooseRets::Media.exists?("mls_acct = '#{mls_acct}' and media_type = 'Virtual Tour'") ? CabooseRets::Media.where("mls_acct = '#{mls_acct}' and media_type = 'Virtual Tour'").first : CabooseRets::Media.new
|
348
|
+
m.parse(data)
|
349
|
+
m.save
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def self.refresh_virtual_tours(p)
|
355
|
+
self.log("-- Deleting images and metadata for #{p.mls_acct}...")
|
356
|
+
CabooseRets::Media.where(:mls_acct => p.mls_acct, :media_type => 'Photo').destroy_all
|
357
|
+
|
358
|
+
self.log("-- Downloading image metadata for #{p.mls_acct}...")
|
359
|
+
params = {
|
360
|
+
:search_type => 'Media',
|
361
|
+
:class => 'GFX',
|
362
|
+
#:query => "(MLS_ACCT=*#{p.id}*),(MEDIA_TYPE=|I)",
|
363
|
+
:query => "(MLS_ACCT=*#{p.id}*)",
|
364
|
+
:timeout => -1
|
365
|
+
}
|
366
|
+
self.client.search(params) do |data|
|
367
|
+
m = CabooseRets::Media.new
|
368
|
+
m.parse(data)
|
369
|
+
#m.id = m.media_id
|
370
|
+
m.save
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
#def self.download_property_images(p)
|
375
|
+
#
|
376
|
+
# self.log("-- Deleting images and metadata for #{p.mls_acct}...")
|
377
|
+
# CabooseRets::Media.where(:mls_acct => p.mls_acct, :media_type => 'Photo').destroy_all
|
378
|
+
#
|
379
|
+
# self.log("-- Downloading image metadata for #{p.mls_acct}...")
|
380
|
+
# params = {
|
381
|
+
# :search_type => 'Media',
|
382
|
+
# :class => 'GFX',
|
383
|
+
# :query => "(MLS_ACCT=*#{p.id}*),(MEDIA_TYPE=|I)",
|
384
|
+
# :timeout => -1
|
385
|
+
# }
|
386
|
+
# puts "Before search #{p.id}"
|
387
|
+
# self.client.search(params) do |data|
|
388
|
+
# puts "download_property_images self.client.search #{p.id}"
|
389
|
+
# #m = CabooseRets::Media.new
|
390
|
+
# #m.parse(data)
|
391
|
+
# #m.id = m.media_id
|
392
|
+
# #m.save
|
393
|
+
# self.download_property_images2(p)
|
394
|
+
# end
|
395
|
+
# puts "After search #{p.id}"
|
396
|
+
#end
|
397
|
+
#
|
398
|
+
#def self.download_property_images2(p)
|
399
|
+
# puts "download_property_images2 #{p.id}"
|
400
|
+
# sleep(1)
|
401
|
+
#
|
402
|
+
# #self.log("-- Downloading images and resizing for #{p.mls_acct}")
|
403
|
+
# #media = []
|
404
|
+
# #self.client.get_object(:resource => :Property, :type => :Photo, :location => true, :id => p.id) do |headers, content|
|
405
|
+
# #
|
406
|
+
# ## Find the associated media record for the image
|
407
|
+
# #filename = File.basename(headers['location'])
|
408
|
+
# #m = CabooseRets::Media.where(:mls_acct => p.mls_acct, :file_name => filename).first
|
409
|
+
# #
|
410
|
+
# #if m.nil?
|
411
|
+
# # self.log("Can't find media record for #{p.mls_acct} #{filename}.")
|
412
|
+
# #else
|
413
|
+
# # m.image = URI.parse(headers['location'])
|
414
|
+
# # media << m
|
415
|
+
# # #m.save
|
416
|
+
# #end
|
417
|
+
# #
|
418
|
+
# #self.log("-- Uploading images to S3 for #{p.mls_acct}")
|
419
|
+
# #media.each do |m|
|
420
|
+
# # m.save
|
421
|
+
# #end
|
422
|
+
#end
|
423
|
+
|
424
|
+
#=============================================================================
|
425
|
+
# GPS
|
426
|
+
#=============================================================================
|
427
|
+
|
428
|
+
def self.update_coords(p = nil)
|
429
|
+
if p.nil?
|
430
|
+
models = [CabooseRets::CommercialProperty, CabooseRets::LandProperty, CabooseRets::MultiFamilyProperty, CabooseRets::ResidentialProperty]
|
431
|
+
names = ["commercial", "land", "multi-family", "residential"]
|
432
|
+
i = 0
|
433
|
+
models.each do |model|
|
434
|
+
self.log "Updating coords #{names[i]} properties..."
|
435
|
+
model.where(:latitude => nil).reorder(:mls_acct).each do |p|
|
436
|
+
self.update_coords(p)
|
437
|
+
end
|
438
|
+
i = i + 1
|
439
|
+
end
|
440
|
+
return
|
441
|
+
end
|
442
|
+
|
443
|
+
self.log "Getting coords for mls_acct #{p.mls_acct}..."
|
444
|
+
coords = self.coords_from_address(CGI::escape "#{p.street_num} #{p.street_name}, #{p.city}, #{p.state} #{p.zip}")
|
445
|
+
return if coords.nil? || coords == false
|
446
|
+
|
447
|
+
p.latitude = coords['lat']
|
448
|
+
p.longitude = coords['lng']
|
449
|
+
p.save
|
450
|
+
end
|
451
|
+
|
452
|
+
def self.coords_from_address(address)
|
453
|
+
begin
|
454
|
+
uri = "https://maps.googleapis.com/maps/api/geocode/json?address=#{address}&sensor=false"
|
455
|
+
uri.gsub!(" ", "+")
|
456
|
+
resp = HTTParty.get(uri)
|
457
|
+
json = JSON.parse(resp.body)
|
458
|
+
return json['results'][0]['geometry']['location']
|
459
|
+
rescue
|
460
|
+
self.log "Error: #{uri}"
|
461
|
+
sleep(2)
|
462
|
+
return false
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
#=============================================================================
|
467
|
+
# Logging
|
468
|
+
#=============================================================================
|
469
|
+
|
470
|
+
def self.log(msg)
|
471
|
+
#puts "[rets_importer] #{msg}"
|
472
|
+
Rails.logger.info("[rets_importer] #{msg}")
|
473
|
+
end
|
474
|
+
|
475
|
+
#=============================================================================
|
476
|
+
# Locking update task
|
477
|
+
#=============================================================================
|
478
|
+
|
479
|
+
def self.last_updated
|
480
|
+
if !Caboose::Setting.exists?(:name => 'rets_last_updated')
|
481
|
+
Caboose::Setting.create(:name => 'rets_last_updated', :value => '2013-08-06T00:00:01')
|
482
|
+
end
|
483
|
+
s = Caboose::Setting.where(:name => 'rets_last_updated').first
|
484
|
+
return DateTime.parse(s.value)
|
485
|
+
end
|
486
|
+
|
487
|
+
def self.save_last_updated(d)
|
488
|
+
s = Caboose::Setting.where(:name => 'rets_last_updated').first
|
489
|
+
s.value = d.strftime('%FT%T')
|
490
|
+
s.save
|
491
|
+
end
|
492
|
+
|
493
|
+
def self.task_is_locked
|
494
|
+
return Caboose::Setting.exists?(:name => 'rets_update_running')
|
495
|
+
end
|
496
|
+
|
497
|
+
def self.lock_task
|
498
|
+
d = DateTime.now.utc - 5.hours
|
499
|
+
Caboose::Setting.create(:name => 'rets_update_running', :value => d.strftime('%F %T'))
|
500
|
+
return d
|
501
|
+
end
|
502
|
+
|
503
|
+
def self.unlock_task
|
504
|
+
Caboose::Setting.where(:name => 'rets_update_running').first.destroy
|
505
|
+
end
|
506
|
+
|
507
|
+
def self.unlock_task_if_last_updated(d)
|
508
|
+
setting = Caboose::Setting.where(:name => 'rets_update_running').first
|
509
|
+
self.unlock_task if setting && d.strftime('%F %T') == setting.value
|
510
|
+
end
|
511
|
+
|
512
|
+
end
|
data/lib/caboose_rets/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: caboose-rets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.53
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Barry
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: caboose-cms
|
@@ -55,6 +55,7 @@ files:
|
|
55
55
|
- app/models/caboose_rets/open_house.rb
|
56
56
|
- app/models/caboose_rets/residential_property.rb
|
57
57
|
- app/models/caboose_rets/rets_importer.rb
|
58
|
+
- app/models/caboose_rets/rets_importer_bak.rb
|
58
59
|
- app/models/caboose_rets/rets_plugin.rb
|
59
60
|
- app/models/caboose_rets/saved_property.rb
|
60
61
|
- app/models/caboose_rets/saved_search.rb
|