caboose-rets 0.1.180 → 0.1.184

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b2fe2e36e342c415299f45cc81d83b301ab00b4c9d7878211c8836626726a43
4
- data.tar.gz: 4c481cd54cfcb3f5c96d58f25948fb1d99ead965380a29d413e8382ddf837cbc
3
+ metadata.gz: 84ef101eafb83b5891a32f9557f355e5f46de43e3d1e544aad19e5a533e78b6e
4
+ data.tar.gz: 1ddefc2dbff138dcb82eb3e4c879f9b19956726d2dd20ca134ec7106a45ce99e
5
5
  SHA512:
6
- metadata.gz: 005a9205c08fc9e64a09a4e9e1fad9c975cdde0f038ab20d7466dc029fd6804e433d2eb022d7bb5e4f3546fd0167cf5802a03ce1e511aefe6c2cb152efc6ed1c
7
- data.tar.gz: e120530752e605448eb07b0dc5c8e66ebd30f8b203091f85234bf8d42ade2da2a9878a6452188b550fd4c302804460056fbb25034a085656e4577e42c32f04a7
6
+ metadata.gz: 754a940a4a13708921c8e4b7bd05c311f04f14b448ac8cb6d6938c752f3f20960a7954465fa84530b3a9b0c8bc6b3cde2394414a6aa07981e5029e5b66aa5ce7
7
+ data.tar.gz: 80eace42b684e7bb8a25b4c7e88263c596fd1cdb2bd41e6fe709b260ec8401675c6fad0c26e56fc341eb247a103d30f69c3550d20a969b097a19df7b7bcd5605
@@ -23,6 +23,29 @@ module CabooseRets
23
23
  @agent = Agent.where(:slug => params[:slug]).where("office_mls_id ILIKE ?", @site.rets_office_id).first if !params[:slug].blank?
24
24
  end
25
25
 
26
+ # @route GET /rets-unsubscribe
27
+ def user_unsubscribe
28
+ user_id = params[:token].blank? ? nil : params[:token].strip.gsub("7b8v9j","").gsub("9b6h0c2n","")
29
+ @user = user_id ? Caboose::User.where(:id => user_id, :site_id => @site.id).first : nil
30
+ @token = params[:token]
31
+ render :file => "caboose/extras/error404", :layout => "caboose/application", :status => 404 and return if @user.nil? || @token.blank?
32
+ @page.meta_robots = "noindex, nofollow"
33
+ @page.uri = "rets-unsubscribe"
34
+ @page.seo_title = "Confirm Unsubscription | #{@site.description}"
35
+ end
36
+
37
+ # @route GET /rets-unsubscribe/confirm
38
+ def user_unsubscribe_confirm
39
+ user_id = params[:token].blank? ? nil : params[:token].strip.gsub("7b8v9j","").gsub("9b6h0c2n","")
40
+ @user = user_id ? Caboose::User.where(:id => user_id, :site_id => @site.id).first : nil
41
+ render :file => "caboose/extras/error404", :layout => "caboose/application", :status => 404 and return if @user.nil?
42
+ @page.meta_robots = "noindex, nofollow"
43
+ @page.uri = "rets-unsubscribe/confirm"
44
+ @page.seo_title = "Unsubscribed | #{@site.description}"
45
+ @user.tax_exempt = true # this means unsubscribed
46
+ @user.save
47
+ end
48
+
26
49
  #=============================================================================
27
50
  # Admin functions
28
51
  #=============================================================================
@@ -11,7 +11,8 @@ module CabooseRets
11
11
  # GET /admin/users/:id/mls
12
12
  def rets_info
13
13
  @edituser = Caboose::User.where(:id => params[:id], :site_id => @site.id).first
14
- @saved = SavedProperty.where(:user_id => @edituser.id).order('date_saved desc').all
14
+ @saved = SavedProperty.where(:user_id => @edituser.id).order('date_saved desc').limit(100)
15
+ @emails = Notification.where(:user_id => @edituser.id).order('date_sent desc').limit(100)
15
16
  render :layout => 'caboose/admin'
16
17
  end
17
18
 
@@ -1,8 +1,13 @@
1
1
  class CabooseRets::RetsMailer < ActionMailer::Base
2
2
 
3
- default from: "noreply <noreply@caboosecms.com>"
3
+ def from_address(site_id, from_name)
4
+ settings = Caboose::SmtpConfig.where(:site_id => site_id).first
5
+ fn = from_name.blank? ? (settings ? settings.site.description : "Caboose CMS") : from_name
6
+ fn = fn.gsub(",","").gsub("<","").gsub(">","").gsub("@","").gsub(":","").truncate(30)
7
+ return settings ? "#{fn} <#{settings.from_address}>" : "#{fn} <noreply@caboosecms.com>"
8
+ end
4
9
 
5
- def new_user(agent, user, from_address)
10
+ def new_user(agent, user)
6
11
  @agent = agent
7
12
  @user = user
8
13
  @site = user.site
@@ -11,10 +16,116 @@ class CabooseRets::RetsMailer < ActionMailer::Base
11
16
  mail(
12
17
  :to => to_address,
13
18
  :bcc => bcc_address,
14
- :from => from_address,
19
+ :from => from_address(@site.id, nil),
15
20
  :subject => "New User Registration",
16
21
  :reply_to => bcc_address
17
22
  )
18
23
  end
19
24
 
25
+ def user_welcome(agent, user)
26
+ @agent = agent
27
+ @user = user
28
+ @site = user.site
29
+ to_address = user.email
30
+ bcc_address = Rails.env.development? ? 'billyswifty@gmail.com' : @site.contact_email
31
+ reply_to = @site.contact_email.blank? ? 'noreply@caboosecms.com' : @site.contact_email
32
+
33
+ setting = Caboose::Setting.where(:site_id => @site.id, :name => "welcome_email_subject").first
34
+ subject = setting && !setting.value.blank? ? setting.value : "Welcome to #{@site.description}"
35
+
36
+ setting2 = Caboose::Setting.where(:site_id => @site.id, :name => "welcome_email_body").first
37
+ @body = setting2 && !setting2.value.blank? ? setting2.value : "We're excited to see that you've registered for #{@site.description}. As a registered user, we'll notify you when there's a price drop or pending sale on your favorite properties. We'll also notify you of new listings and similar properties you may be interested in. Your assigned REALTOR® is |agent_name| and will be in touch with you shortly. Thank you for choosing #{@site.description}!"
38
+ @body = @body.gsub("|agent_name|", "#{@agent.first_name} #{@agent.last_name}")
39
+
40
+ @color = @site.theme ? @site.theme.color_main : @site.theme_color
41
+
42
+ @url = "https://#{@site.primary_domain.domain}"
43
+ @url += "/real-estate" if @site.id == 541
44
+
45
+ @logo_url = @site.logo.url(:large)
46
+ @logo_url = "https:#{@logo_url}" if !@logo_url.include?('http')
47
+
48
+ @unsubscribe_url = "https://#{@site.primary_domain.domain}/rets-unsubscribe?token=7b8v9j#{@user.id}9b6h0c2n"
49
+
50
+ mail(
51
+ :to => to_address,
52
+ :bcc => bcc_address,
53
+ :from => from_address(@site.id, nil),
54
+ :subject => subject,
55
+ :reply_to => reply_to
56
+ )
57
+ end
58
+
59
+ def daily_report(user, new_listings, related_listings)
60
+ @user = user
61
+ @new_listings = new_listings
62
+ @related_listings = related_listings
63
+ @site = user.site
64
+ to_address = user.email
65
+ reply_to = @site.contact_email.blank? ? 'noreply@caboosecms.com' : @site.contact_email
66
+ @color = @site.theme ? @site.theme.color_main : @site.theme_color
67
+ @domain = "https://#{@site.primary_domain.domain}"
68
+ @domain = "http://dev.pmre.com:3000" if Rails.env.development?
69
+ @url = @domain
70
+ @url += "/real-estate" if @site.id == 541
71
+ @logo_url = @site.logo.url(:large)
72
+ subject = @site.id == 541 ? "New and Suggested Listings from Pritchett-Moore Real Estate" : "New and Suggested Listings from #{@site.description}"
73
+ @logo_url = "https:#{@logo_url}" if !@logo_url.include?('http')
74
+ @unsubscribe_url = "https://#{@site.primary_domain.domain}/rets-unsubscribe?token=7b8v9j#{@user.id}9b6h0c2n"
75
+ mail(
76
+ :to => to_address,
77
+ :from => from_address(@site.id, nil),
78
+ :subject => subject,
79
+ :reply_to => reply_to
80
+ )
81
+ end
82
+
83
+ def property_status_change(user, property, old_status)
84
+ @user = user
85
+ @property = property
86
+ @old_status = old_status
87
+ @site = user.site
88
+ to_address = user.email
89
+ reply_to = @site.contact_email.blank? ? 'noreply@caboosecms.com' : @site.contact_email
90
+ @color = @site.theme ? @site.theme.color_main : @site.theme_color
91
+ @domain = "https://#{@site.primary_domain.domain}"
92
+ @domain = "http://dev.pmre.com:3000" if Rails.env.development?
93
+ @url = @domain
94
+ @url += "/real-estate" if @site.id == 541
95
+ @logo_url = @site.logo.url(:large)
96
+ subject = "Status Change for Listing MLS ##{@property.mls_number}"
97
+ @logo_url = "https:#{@logo_url}" if !@logo_url.include?('http')
98
+ @unsubscribe_url = "https://#{@site.primary_domain.domain}/rets-unsubscribe?token=7b8v9j#{@user.id}9b6h0c2n"
99
+ mail(
100
+ :to => to_address,
101
+ :from => from_address(@site.id, nil),
102
+ :subject => subject,
103
+ :reply_to => reply_to
104
+ )
105
+ end
106
+
107
+ def property_price_change(user, property, old_price)
108
+ @user = user
109
+ @property = property
110
+ @old_price = old_price
111
+ @site = user.site
112
+ to_address = user.email
113
+ reply_to = @site.contact_email.blank? ? 'noreply@caboosecms.com' : @site.contact_email
114
+ @color = @site.theme ? @site.theme.color_main : @site.theme_color
115
+ @domain = "https://#{@site.primary_domain.domain}"
116
+ @domain = "http://dev.pmre.com:3000" if Rails.env.development?
117
+ @url = @domain
118
+ @url += "/real-estate" if @site.id == 541
119
+ @logo_url = @site.logo.url(:large)
120
+ subject = "Price Change for Listing MLS ##{@property.mls_number}"
121
+ @logo_url = "https:#{@logo_url}" if !@logo_url.include?('http')
122
+ @unsubscribe_url = "https://#{@site.primary_domain.domain}/rets-unsubscribe?token=7b8v9j#{@user.id}9b6h0c2n"
123
+ mail(
124
+ :to => to_address,
125
+ :from => from_address(@site.id, nil),
126
+ :subject => subject,
127
+ :reply_to => reply_to
128
+ )
129
+ end
130
+
20
131
  end
@@ -40,23 +40,26 @@ class CabooseRets::Agent < ActiveRecord::Base
40
40
  CabooseRets::RetsImporter.import_agent(self.mls_id)
41
41
  end
42
42
 
43
- def send_new_user_email(user)
44
- if !self.email.blank? && user && user.site
45
- settings = Caboose::SmtpConfig.where(:site_id => user.site_id).first
46
- if settings
47
- delivery_options = {
48
- user_name: settings.user_name,
49
- password: settings.password,
50
- address: settings.address,
51
- port: settings.port,
52
- domain: settings.domain,
53
- authentication: settings.authentication,
54
- enable_starttls_auto: settings.enable_starttls_auto
55
- }
56
- from_address = "#{settings.site.description} <#{settings.from_address}>"
57
- msg = CabooseRets::RetsMailer.send("new_user", self, user, from_address)
58
- msg.delivery_method.settings.merge!(delivery_options)
59
- msg.deliver_now
43
+ # Sends a SMS to the agent (using Twilio) notifying them that a new user has registered and been assigned to them
44
+ def send_text(message, site_id)
45
+ s1 = Caboose::Setting.where(:site_id => site_id, :name => "twilio_account_sid").first
46
+ account_sid = s1 ? s1.value : nil
47
+ s2 = Caboose::Setting.where(:site_id => site_id, :name => "twilio_auth_token").first
48
+ auth_token = s2 ? s2.value : nil
49
+ s3 = Caboose::Setting.where(:site_id => site_id, :name => "twilio_from_number").first
50
+ twilio_number = s3 ? s3.value : nil
51
+ send_to = self.cell_phone.blank? ? (self.direct_work_phone.blank? ? self.other_phone : self.direct_work_phone) : self.cell_phone
52
+ send_to = '205-657-0937' if Rails.env.development? # Billy's cell
53
+ if account_sid && auth_token && twilio_number && !send_to.blank? && !message.blank?
54
+ @client = Twilio::REST::Client.new account_sid, auth_token
55
+ begin
56
+ message = @client.messages.create(
57
+ body: message,
58
+ to: "+1#{send_to.gsub(/[(\- )]/, '')}",
59
+ from: "#{twilio_number}"
60
+ )
61
+ rescue
62
+ Caboose.log("invalid phone number")
60
63
  end
61
64
  end
62
65
  end
@@ -78,7 +81,21 @@ class CabooseRets::Agent < ActiveRecord::Base
78
81
  user.rets_agent_mls_id = agent.mls_id
79
82
  last_agent_mls_id.value = agent.mls_id
80
83
  user.save
81
- Rails.env.production? ? agent.delay(:queue => 'rets').send_new_user_email(user) : agent.send_new_user_email(user)
84
+
85
+ s1 = Caboose::Setting.where(:site_id => user.site_id, :name => "agent_text_message").first
86
+ message = s1 ? s1.value : nil
87
+
88
+ if !message.blank?
89
+ message = message.gsub("|user_first_name|", user.first_name)
90
+ message = message.gsub("|user_last_name|", user.last_name)
91
+ message = message.gsub("|user_email|", user.email)
92
+ message = message.gsub("|user_phone|", user.phone)
93
+ agent.delay(:queue => 'rets', :priority => 0).send_text(message, user.site_id)
94
+ end
95
+
96
+ CabooseRets::RetsMailer.configure_for_site(user.site_id).new_user(agent, user).deliver_later
97
+ CabooseRets::RetsMailer.configure_for_site(user.site_id).user_welcome(agent, user).deliver_later
98
+
82
99
  last_agent_mls_id.save
83
100
  role = Caboose::Role.where(:name => 'RETS Visitor', :site_id => user.site_id).exists? ? Caboose::Role.where(:name => 'RETS Visitor', :site_id => user.site_id).first : Caboose::Role.create(:name => 'RETS Visitor', :site_id => user.site_id)
84
101
  Caboose::RoleMembership.create(:user_id => user.id, :role_id => role.id)
@@ -0,0 +1,206 @@
1
+ class CabooseRets::Notification < ActiveRecord::Base
2
+
3
+ self.table_name = "rets_notifications"
4
+
5
+ belongs_to :user, :class_name => "Caboose::User"
6
+ belongs_to :site, :class_name => "Caboose::Site"
7
+
8
+ def self.property_status_changed(property, old_status)
9
+ return if property.nil?
10
+
11
+ us = Caboose::Setting.where(:site_id => 541, :name => "test_usernames").first
12
+ allowed_users = us ? us.value : ""
13
+ allowed_users = 'billyswifty' if Rails.env.development?
14
+ uwhere = allowed_users.blank? ? "" : "('#{allowed_users}' ILIKE '%' || username || '%')"
15
+
16
+ user_ids = CabooseRets::SavedProperty.where(:mls_number => property.mls_number).pluck(:user_id)
17
+ if user_ids.count > 0
18
+ user_ids.each do |user_id|
19
+ user = Caboose::User.where(uwhere).where(:id => user_id, :tax_exempt => false).first
20
+ d1 = DateTime.now - 24.hours
21
+ if user
22
+ # Check if a similar notification has already been sent
23
+ n = CabooseRets::Notification.where(
24
+ :user_id => user.id,
25
+ :site_id => user.site_id,
26
+ :kind => "Property Status Changed",
27
+ :sent_to => user.email,
28
+ :object_kind => "Property",
29
+ :object_id => property.mls_number,
30
+ :old_value => old_status,
31
+ :new_value => property.status
32
+ ).where("date_sent >= ?", d1).first
33
+ if n.nil?
34
+ n = CabooseRets::Notification.create(
35
+ :user_id => user.id,
36
+ :site_id => user.site_id,
37
+ :date_sent => DateTime.now,
38
+ :kind => "Property Status Changed",
39
+ :sent_to => user.email,
40
+ :object_kind => "Property",
41
+ :object_id => property.mls_number,
42
+ :old_value => old_status,
43
+ :new_value => property.status
44
+ )
45
+ CabooseRets::RetsMailer.configure_for_site(user.site_id).property_status_change(user, property, old_status).deliver_later
46
+ else
47
+ puts "Found a duplicate notification, not sending..."
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def self.property_price_changed(property, old_price)
55
+ return if property.nil? || property.status != 'Active'
56
+
57
+ us = Caboose::Setting.where(:site_id => 541, :name => "test_usernames").first
58
+ allowed_users = us ? us.value : ""
59
+ allowed_users = 'billyswifty' if Rails.env.development?
60
+ uwhere = allowed_users.blank? ? "" : "('#{allowed_users}' ILIKE '%' || username || '%')"
61
+
62
+ user_ids = CabooseRets::SavedProperty.where(:mls_number => property.mls_number).pluck(:user_id)
63
+ if user_ids.count > 0
64
+ user_ids.each do |user_id|
65
+ user = Caboose::User.where(uwhere).where(:id => user_id, :tax_exempt => false).first
66
+ d1 = DateTime.now - 24.hours
67
+ if user
68
+ # Check if a similar notification has already been sent
69
+ n = CabooseRets::Notification.where(
70
+ :user_id => user.id,
71
+ :site_id => user.site_id,
72
+ :kind => "Property Price Changed",
73
+ :sent_to => user.email,
74
+ :object_kind => "Property",
75
+ :object_id => property.mls_number,
76
+ :old_value => old_price,
77
+ :new_value => property.list_price
78
+ ).where("date_sent >= ?", d1).first
79
+ if n.nil?
80
+ n = CabooseRets::Notification.create(
81
+ :user_id => user.id,
82
+ :site_id => user.site_id,
83
+ :date_sent => DateTime.now,
84
+ :kind => "Property Price Changed",
85
+ :sent_to => user.email,
86
+ :object_kind => "Property",
87
+ :object_id => property.mls_number,
88
+ :old_value => old_price,
89
+ :new_value => property.list_price
90
+ )
91
+ CabooseRets::RetsMailer.configure_for_site(user.site_id).property_price_change(user, property, old_price).deliver_later
92
+ else
93
+ puts "Found a duplicate notification, not sending..."
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def self.send_new_suggested_emails
101
+ roles = Caboose::Role.where(:name => "RETS Visitor").order(:id).all
102
+ if roles.count > 0
103
+ role_ids = roles.map{|r| r.id}
104
+
105
+ us = Caboose::Setting.where(:site_id => 541, :name => "test_usernames").first
106
+ allowed_users = us ? us.value : ""
107
+ allowed_users = 'billyswifty' if Rails.env.development?
108
+ uwhere = allowed_users.blank? ? "" : "('#{allowed_users}' ILIKE '%' || username || '%')"
109
+
110
+ users = Caboose::User.joins(:role_memberships).where(uwhere).where("role_memberships.role_id in (?)", role_ids).where(:tax_exempt => false).order(:id).all
111
+
112
+ if users.count > 0
113
+
114
+ new_listings = CabooseRets::Property.where(:status => 'Active', :property_type => 'Residential').order('original_entry_timestamp desc').take(3)
115
+
116
+ users.each do |user|
117
+
118
+ puts "Gathering data for user: #{user.username}" if Rails.env.development?
119
+
120
+ saved_mls = CabooseRets::SavedProperty.where(:user_id => user.id).pluck(:mls_number)
121
+ saved_properties = saved_mls && saved_mls.count > 0 ? CabooseRets::Property.where(:status => 'Active', :property_type => 'Residential').where(:mls_number => saved_mls).limit(100) : []
122
+
123
+ if saved_properties.count > 0
124
+
125
+ puts "Saved listings: #{saved_properties.count}" if Rails.env.development?
126
+
127
+ price_where = "list_price is not null and (list_price >= ? AND list_price <= ?)"
128
+ beds_where = "beds_total is not null and (beds_total >= ? AND beds_total <= ?)"
129
+
130
+ all_prices = saved_properties.map{|p| p.list_price}
131
+ price_min = all_prices.count > 0 ? (all_prices.min * 0.8) : 150000
132
+ price_max = all_prices.count > 0 ? (all_prices.max * 1.2) : 350000
133
+
134
+ puts "Price range: #{price_min} - #{price_max}" if Rails.env.development?
135
+
136
+ all_beds = saved_properties.map{|p| p.beds_total}
137
+ beds_min = all_beds.count > 0 ? (all_beds.min - 1) : 2
138
+ beds_max = all_beds.count > 0 ? (all_beds.max + 1) : 5
139
+
140
+ puts "Beds range: #{beds_min} - #{beds_max}" if Rails.env.development?
141
+
142
+ property_subtypes = []
143
+ property_areas = []
144
+
145
+ saved_properties.each do |sp|
146
+ property_subtypes << sp.property_subtype if !property_subtypes.include?(sp.property_subtype)
147
+ property_areas << sp.area if !property_areas.include?(sp.area)
148
+ end
149
+
150
+ puts "Property subtypes: #{property_subtypes}" if Rails.env.development?
151
+ puts "Property areas: #{property_areas}" if Rails.env.development?
152
+
153
+ related_listings = CabooseRets::Property.where(:property_type => 'Residential', :status => 'Active', :property_subtype => property_subtypes, :area => property_areas).where("mls_number not in (?)", saved_mls).where(price_where,price_min,price_max).where(beds_where,beds_min,beds_max).order('original_entry_timestamp desc').take(3)
154
+
155
+ else
156
+
157
+ related_listings = []
158
+
159
+ end
160
+
161
+ if new_listings.count > 0 || related_listings.count > 0
162
+
163
+ msg = ""
164
+
165
+ if new_listings.count > 0
166
+ mls1 = new_listings.map{ |l| l.mls_number }
167
+ msg += "New Listings: #{mls1.join(', ')}"
168
+ end
169
+
170
+ if related_listings.count > 0
171
+ mls2 = related_listings.map{ |l| l.mls_number }
172
+ msg += "\n" if new_listings.count > 0
173
+ msg += "Related Listings: #{mls2.join(', ')}"
174
+ end
175
+
176
+ d = DateTime.now - 7.days
177
+ n = CabooseRets::Notification.where(
178
+ :user_id => user.id,
179
+ :site_id => user.site_id,
180
+ :kind => "New and Suggested Listings",
181
+ :sent_to => user.email,
182
+ :message => msg
183
+ ).where("date_sent >= ?", d).first
184
+
185
+ if n.nil?
186
+ n = CabooseRets::Notification.create(
187
+ :user_id => user.id,
188
+ :site_id => user.site_id,
189
+ :date_sent => DateTime.now,
190
+ :kind => "New and Suggested Listings",
191
+ :sent_to => user.email,
192
+ :message => msg
193
+ )
194
+ CabooseRets::RetsMailer.configure_for_site(user.site_id).daily_report(user, new_listings, related_listings).deliver_later
195
+ else
196
+ puts "Found a duplicate notification, not sending..."
197
+ end
198
+
199
+ end
200
+
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ end
@@ -24,10 +24,12 @@ class CabooseRets::Property <ActiveRecord::Base
24
24
  m = Caboose::Media.where(:id => img.media_id).first
25
25
  if m && m.image && m.image.url(:large)
26
26
  img_url = m.image.url(:large)
27
+ img_url = "https:#{img_url}" if !img_url.include?('http')
27
28
  return img_url
28
29
  break
29
30
  end
30
31
  end
32
+ img_url = "https:#{img_url}" if !img_url.include?('http')
31
33
  return img_url
32
34
  end
33
35
 
@@ -55,6 +57,10 @@ class CabooseRets::Property <ActiveRecord::Base
55
57
  end
56
58
 
57
59
  def parse(data)
60
+
61
+ old_price = self.list_price
62
+ old_status = self.status
63
+
58
64
  # puts(data.to_s)
59
65
  # self.access = nil
60
66
  self.acreage = data['LotSizeAcres'].blank? ? nil : data['LotSizeAcres'].to_f
@@ -249,5 +255,14 @@ class CabooseRets::Property <ActiveRecord::Base
249
255
  self.zoning = data['Zoning']
250
256
  # self.zoning_northport = data['ZoningNorthPort']
251
257
  # self.zoning_tusc = data['ZoningTusc']
258
+
259
+
260
+ if old_status != self.status # status was changed
261
+ CabooseRets::Notification.delay(:queue => "rets").property_status_changed(self, old_status)
262
+ elsif old_price != self.list_price && self.status == 'Active' # price was changed
263
+ CabooseRets::Notification.delay(:queue => "rets").property_price_changed(self, old_price)
264
+ end
265
+
266
+
252
267
  end
253
268
  end
@@ -70,7 +70,6 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
70
70
  :search_type => m.search_type,
71
71
  :class => class_type,
72
72
  :query => query,
73
- # :limit => 3,
74
73
  :timeout => -1
75
74
  }
76
75
  obj = nil
@@ -135,7 +134,7 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
135
134
 
136
135
  statusquery = ""
137
136
  case class_type
138
- when 'Property' then statusquery = "MlsStatus=Active"
137
+ when 'Property' then statusquery = "OriginatingSystemName=WESTAL"
139
138
  when 'Office' then statusquery = "OfficeStatus=Active"
140
139
  when 'Member' then statusquery = "MemberStatus=Active"
141
140
  when 'OpenHouse' then statusquery = "OpenHouseKeyNumeric=0+"
@@ -164,7 +163,7 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
164
163
  end
165
164
 
166
165
  # Check for changed images
167
- if class_type == 'Property'
166
+ if class_type == 'Property' && Rails.env.production?
168
167
  self.log3("Property",nil,"Checking for modified images on Properties...")
169
168
  d1 = (self.last_updated - 1.hours).in_time_zone(CabooseRets::timezone).strftime("%FT%T")
170
169
  params = {
@@ -191,17 +190,6 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
191
190
  # Single model import methods (called from a worker dyno)
192
191
  #=============================================================================
193
192
 
194
- # def self.import_property(mui, save_images = true)
195
- # self.import('Listing', "(Matrix_Unique_ID=#{mui})")
196
- # p = CabooseRets::Property.where(:matrix_unique_id => mui.to_s).first
197
- # if p != nil
198
- # self.download_property_images(p)
199
- # self.update_coords(p)
200
- # else
201
- # self.log("No Property associated with #{mui}")
202
- # end
203
- # end
204
-
205
193
  def self.import_properties(mls_id, save_images = true)
206
194
  si = save_images ? 'saving images' : 'not saving images'
207
195
  self.log3('Property',mls_id,"Importing Property #{mls_id} and #{si}...")
@@ -209,12 +197,12 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
209
197
  self.import('Property', "(ListingId=#{mls_id})")
210
198
  p = CabooseRets::Property.where(:mls_number => mls_id.to_s).first
211
199
  if p != nil && p.status == 'Active'
212
- self.download_property_images(p) if save_images == true
200
+ self.download_property_images(p) if save_images == true && Rails.env.production?
213
201
  if p.latitude.blank? || p.latitude == '0.0' || p.longitude.blank? || p.longitude == '0.0'
214
202
  self.update_coords(p)
215
203
  end
216
204
  else
217
- self.log3(nil,nil,"No Property associated with #{mls_id}")
205
+ self.log3(nil,nil,"No Active Property associated with #{mls_id}, not downloading images")
218
206
  end
219
207
  end
220
208
 
@@ -265,43 +253,8 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
265
253
  # Images go here
266
254
  #=============================================================================
267
255
 
268
- # def self.download_property_images(p, save_images = true)
269
- # return if save_images == false
270
- # self.log("- Downloading GFX records for #{p.matrix_unique_id}...")
271
- # params = {
272
- # :search_type => 'Media',
273
- # :class => 'Photo',
274
- # :type => 'Photo',
275
- # :resource => 'Property',
276
- # :query => "(ID=*#{p.matrix_unique_id}:1*)",
277
- # :limit => 1000,
278
- # :timeout => -1
279
- # }
280
- # ids = []
281
- # self.client.search(params) do |data|
282
- # puts data
283
- # ids << data['MEDIA_ID']
284
- # m = CabooseRets::Media.where(:media_id => data['MEDIA_ID']).first
285
- # m = CabooseRets::Media.new if m.nil?
286
- # data.MEDIA_MUI = p.matrix_unique_id
287
- # m.parse(data)
288
- # m.save
289
- # end
290
- # if ids.count > 0
291
- # # Delete any records in the local database that shouldn't be there
292
- # self.log("- Deleting GFX records for MLS ##{p.matrix_unique_id} in the local database that are not in the remote database...")
293
- # query = "select media_id from rets_media where matrix_unique_id = '#{p.matrix_unique_id}'"
294
- # rows = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.send(:sanitize_sql_array, query))
295
- # local_ids = rows.collect{ |row| row['media_id'] }
296
- # ids_to_remove = local_ids - ids
297
- # if ids_to_remove && ids_to_remove.count > 0
298
- # query = ["delete from rets_media where media_id in (?)", ids_to_remove]
299
- # ActiveRecord::Base.connection.execute(ActiveRecord::Base.send(:sanitize_sql_array, query))
300
- # end
301
- # end
302
- # end
303
-
304
256
  def self.download_property_images(p)
257
+ return if Rails.env.development?
305
258
  self.log3('Property',p.mls_number,"Downloading images for #{p.mls_number}...")
306
259
  ids_to_keep = []
307
260
  begin
@@ -369,93 +322,21 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
369
322
  end
370
323
  end
371
324
 
372
- # # Get first image
373
- # self.client.get_object(:resource => 'Property', :type => 'Photo', :location=> false, :id => "#{p.matrix_unique_id}:0") do |headers, content|
374
- # self.log3('Media',p.mls_number,"Downloading first photo with content-id #{headers['content-id']}, orderhint #{headers['orderhint']}, object-id #{headers['object-id']}")
375
- # m = CabooseRets::Media.where(:media_mui => headers['content-id'], :media_order => 0).first
376
- # m = CabooseRets::Media.new if m.nil?
377
- # tmp_path = "#{Rails.root}/tmp/rets_media_#{headers['content-id']}:0.jpeg"
378
- # File.open(tmp_path, "wb") do |f|
379
- # f.write(content)
380
- # end
381
- # m.media_mui = headers['content-id']
382
- # m.media_order = 0
383
- # m.media_type = 'Photo'
384
- # cm = Caboose::Media.new
385
- # cm.image = File.open(tmp_path)
386
- # cm.name = "rets_media_#{headers['content-id']}_0"
387
- # cm.original_name = "rets_media_#{headers['content-id']}_0.jpeg"
388
- # cm.processed = true
389
- # cm.save
390
- # m.media_id = cm.id
391
- # m.save
392
- # self.log3("Media",p.mls_number,"Created new RetsMedia object #{m.id}, CabooseMedia object #{cm.id}")
393
- # `rm #{tmp_path}`
394
- # self.log3("Media",p.mls_number,"Image rets_media_#{headers['content-id']}_0 saved")
395
- # end
396
- # # Get rest of images
397
- # self.client.get_object(:resource => 'Property', :type => 'Photo', :location=> false, :id => "#{p.matrix_unique_id}:*") do |headers, content|
398
- # self.log3('Media',p.mls_number,"Downloading subsequent photo with content-id #{headers['content-id']}, orderhint #{headers['orderhint']}, object-id #{headers['object-id']}")
399
- # m = CabooseRets::Media.where(:media_mui => headers['content-id'], :media_order => headers['orderhint']).first
400
- # m = CabooseRets::Media.new if m.nil?
401
- # tmp_path = "#{Rails.root}/tmp/rets_media_#{headers['content-id']}:#{headers['object-id']}.jpeg"
402
- # File.open(tmp_path, "wb") do |f|
403
- # f.write(content)
404
- # end
405
- # m.media_mui = headers['content-id']
406
- # m.media_order = headers['orderhint']
407
- # m.media_type = 'Photo'
408
- # cm = Caboose::Media.new
409
- # cm.image = File.open(tmp_path)
410
- # cm.name = "rets_media_#{headers['content-id']}_#{headers['object-id']}"
411
- # cm.original_name = "rets_media_#{headers['content-id']}_#{headers['object-id']}.jpeg"
412
- # cm.processed = true
413
- # cm.save
414
- # m.media_id = cm.id
415
- # m.save
416
- # self.log3("Media",p.mls_number,"Created new RetsMedia object #{m.id}, CabooseMedia object #{cm.id}")
417
- # `rm #{tmp_path}`
418
- # self.log3("Media",p.mls_number,"Image rets_media_#{headers['content-id']}_#{headers['object-id']} saved")
419
- # end
420
- # rescue RETS::APIError => err
421
- # self.log "No image for #{p.mls_number}."
422
- # self.log err
423
- # end
424
325
  end
425
326
 
426
327
  def self.download_missing_images
427
328
  self.log3("Property",nil,"Downloading all missing images...")
428
- CabooseRets::Property.where("photo_count = ? OR photo_count is null", '').all.each do |p|
329
+ CabooseRets::Property.where("photo_count = ? OR photo_count is null", '').where(:status => "Active").all.each do |p|
429
330
  self.delay(:priority => 10, :queue => 'rets').import_properties(p.mls_number, true)
430
331
  end
431
332
  end
432
333
 
433
334
  def self.download_agent_image(agent)
434
- # self.log "Saving image for #{agent.first_name} #{agent.last_name}..."
435
- # begin
436
- # self.client.get_object(:resource => :Member, :type => :Photo, :location => true, :id => property.list_agent_mls_id) do |headers, content|
437
- # agent.verify_meta_exists
438
- # agent.meta.image_location = headers['location']
439
- # agent.meta.save
440
- # end
441
- # rescue RETS::APIError => err
442
- # self.log "No image for #{agent.first_name} #{agent.last_name}."
443
- # self.log err
444
- # end
335
+
445
336
  end
446
337
 
447
338
  def self.download_office_image(office)
448
- #self.log "Saving image for #{agent.first_name} #{agent.last_name}..."
449
- #begin
450
- # self.client.get_object(:resource => :Agent, :type => :Photo, :location => true, :id => agent.la_code) do |headers, content|
451
- # agent.verify_meta_exists
452
- # agent.meta.image_location = headers['location']
453
- # agent.meta.save
454
- # end
455
- #rescue RETS::APIError => err
456
- # self.log "No image for #{agent.first_name} #{agent.last_name}."
457
- # self.log err
458
- #end
339
+
459
340
  end
460
341
 
461
342
  #=============================================================================
@@ -517,6 +398,7 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
517
398
  def self.purge_open_houses() self.delay(:priority => 10, :queue => 'rets').purge_helper('OpenHouse', '2012-01-01') end
518
399
 
519
400
 
401
+ # Adds/removes records in the database
520
402
  def self.purge_helper(class_type, date_modified)
521
403
  m = self.meta(class_type)
522
404
  self.log(m.search_type)
@@ -573,12 +455,12 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
573
455
  ids_to_remove = local_ids - ids
574
456
  self.log3(class_type,nil,"Found #{ids_to_remove.count} #{class_type} records in the local database that are not in the remote database.")
575
457
 
576
- # Delete all RetsMedia and CabooseMedia for the deleted property listings
458
+ # Delete all RetsMedia and CabooseMedia for the deleted property listings (except keep the first image)
577
459
  if class_type == 'Property' && ids_to_remove && ids_to_remove.count > 0
578
460
  self.log3(class_type,nil,"Deleting Media objects that shouldn't be there...")
579
461
  muis = CabooseRets::Property.where("#{k} in (?)", ids_to_remove).pluck(:matrix_unique_id)
580
- if muis && muis.count > 0
581
- CabooseRets::Media.where("media_mui in (?)", muis).each do |med|
462
+ if muis && muis.count > 0 && Rails.env.production?
463
+ CabooseRets::Media.where("media_mui in (?)", muis).where("media_order != ?", 1).each do |med|
582
464
  self.log3("Media",med.id,"Deleting old RetsMedia #{med.id} and CabooseMedia #{med.media_id}...")
583
465
  m = Caboose::Media.where(:id => med.media_id).where("name ILIKE ?","rets_media%").first
584
466
  m.destroy if m
@@ -587,9 +469,11 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
587
469
  end
588
470
  end
589
471
 
590
- self.log3(class_type,nil,"Deleting #{class_type} records in the local database that shouldn't be there...")
591
- query = ["delete from #{t} where #{k} in (?)", ids_to_remove]
592
- ActiveRecord::Base.connection.execute(ActiveRecord::Base.send(:sanitize_sql_array, query))
472
+ if class_type != 'Property' # keep all properties in the DB
473
+ self.log3(class_type,nil,"Deleting #{class_type} records in the local database that shouldn't be there...")
474
+ query = ["delete from #{t} where #{k} in (?)", ids_to_remove]
475
+ ActiveRecord::Base.connection.execute(ActiveRecord::Base.send(:sanitize_sql_array, query))
476
+ end
593
477
 
594
478
  # Find any ids in the remote database that should be in the local database
595
479
  self.log3(class_type,nil,"Finding #{class_type} records in the remote database that should be in the local database...")
@@ -612,54 +496,6 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
612
496
  end
613
497
  end
614
498
 
615
- # def self.get_media_urls
616
- # m = self.meta(class_type)
617
-
618
- # # Get the total number of records
619
- # params = {
620
- # :search_type => m.search_type,
621
- # :class => class_type,
622
- # :query => "(#{m.matrix_modified_dt}=#{date_modified}T00:00:01+)",
623
- # :standard_names_only => true,
624
- # :timeout => -1
625
- # }
626
- # self.client.search(params.merge({ :count => 1 }))
627
- # count = self.client.rets_data[:code] == "20201" ? 0 : self.client.rets_data[:count]
628
- # batch_count = (count.to_f/5000.0).ceil
629
-
630
- # ids = []
631
- # k = m.remote_key_field
632
- # (0...batch_count).each do |i|
633
- # self.client.search(params.merge({ :select => [k], :limit => 5000, :offset => 5000*i })) do |data|
634
- # ids << data[k]
635
- # end
636
- # end
637
-
638
- # if ids.count > 0
639
- # # Delete any records in the local database that shouldn't be there
640
- # t = m.local_table
641
- # k = m.local_key_field
642
- # query = ["delete from #{t} where #{k} not in (?)", ids]
643
- # ActiveRecord::Base.connection.execute(ActiveRecord::Base.send(:sanitize_sql_array, query))
644
-
645
- # # Find any ids in the remote database that should be in the local database
646
- # query = "select distinct #{k} from #{t}"
647
- # rows = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.send(:sanitize_sql_array, query))
648
- # local_ids = rows.collect{ |row| row[k] }
649
- # ids_to_add = ids - local_ids
650
- # ids_to_add.each do |id|
651
- # self.log("Importing #{id}...")
652
- # case class_type
653
- # when "Property" then self.delay(:priority => 10, :queue => 'rets').import_properties(id, true)
654
- # when "Office" then self.delay(:priority => 10, :queue => 'rets').import_office(id, false)
655
- # when "Member" then self.delay(:priority => 10, :queue => 'rets').import_agent(id, false)
656
- # when "OpenHouse" then self.delay(:priority => 10, :queue => 'rets').import_open_house(id, false)
657
- # end
658
- # end
659
- # end
660
-
661
- # end
662
-
663
499
  #=============================================================================
664
500
  # Logging
665
501
  #=============================================================================
@@ -704,7 +540,7 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
704
540
  overlap = 1.week
705
541
  end
706
542
  self.update_after((self.last_updated - overlap), false)
707
- self.download_missing_images
543
+ self.download_missing_images if Rails.env.production?
708
544
  self.log3(nil,nil,"Saving the timestamp for when we updated to #{task_started.to_s}...")
709
545
  self.save_last_updated(task_started)
710
546
  self.log2("Unlocking the task...")
@@ -725,7 +561,7 @@ class CabooseRets::RetsImporter # < ActiveRecord::Base
725
561
  end
726
562
 
727
563
  # Delete RETS logs over 7 days old
728
- dt = DateTime.now - 7.days
564
+ dt = DateTime.now - 5.days
729
565
  sql = "delete from rets_logs where timestamp < '#{dt}';"
730
566
  ActiveRecord::Base.connection.select_all(sql)
731
567
 
@@ -347,6 +347,18 @@ class CabooseRets::Schema < Caboose::Utilities::Schema
347
347
  Caboose::User => [
348
348
  [ :rets_agent_mls_id , :string ]
349
349
  ],
350
+ CabooseRets::Notification => [
351
+ [ :user_id, :integer ],
352
+ [ :date_sent, :datetime ],
353
+ [ :site_id, :integer ],
354
+ [ :kind, :string ],
355
+ [ :sent_to, :string ],
356
+ [ :object_kind, :string ],
357
+ [ :object_id, :string ],
358
+ [ :old_value, :string ],
359
+ [ :new_value, :string ],
360
+ [ :message, :text ]
361
+ ],
350
362
  CabooseRets::RetsConfig => [
351
363
  [ :site_id, :integer ],
352
364
  [ :office_mls, :string ],
@@ -0,0 +1,5 @@
1
+ <div class="constrain richtext" style="text-align:center;font-size:18px;padding-top:50px;padding-bottom:50px;">
2
+ <h4>Are you sure you want to unsubscribe?</h4>
3
+ <p>By unsubscribing, you will no longer receive email updates for saved properties or suggested listings.</p>
4
+ <p><a style="margin-top:20px;display:inline-block;" href="/rets-unsubscribe/confirm?token=<%= @token %>">Yes, unsubscribe from all property updates and suggested listings.</a></p>
5
+ </div>
@@ -0,0 +1,3 @@
1
+ <div class="constrain richtext" style="text-align:center;font-size:18px;padding-top:30px;padding-bottom:30px;">
2
+ <p>You have successfully unsubscribed.</p>
3
+ </div>
@@ -0,0 +1,31 @@
1
+ <table border="0" cellpadding="0" cellspacing="0" width="800" id="templateColumns" style="width:100%;margin:0 0 20px 0;text-align:left;">
2
+ <tr>
3
+ <td align="left" valign="top" width="25%" class="templateColumnContainer" style="width:25%;padding:0;">
4
+ <a style="display:block;text-decoration:none;" href="<%= url %>/properties/<%= p.mls_number %>/details">
5
+ <% if p.featured_photo_url %>
6
+ <img style="display:block;max-width:200px;height:auto;" width="200" alt="MLS # <%= p.mls_number %>" src="<%= p.featured_photo_url %>" />
7
+ <% end %>
8
+ </a>
9
+ </td>
10
+ <td align="left" valign="top" width="37.5%" class="templateColumnContainer" style="width:37.5%;padding:5px 5px 5px 10px;">
11
+ <p style="margin:0 0 5px 0;font-size:14px;line-height:16px;"><strong>MLS#: <%= p.mls_number %></strong></p>
12
+ <address style="margin:0 0 5px 0;font-size:15px;line-height:17px;"><%== p.full_address_city %></address>
13
+ <p style="margin:0 0 5px 0;font-size:15px;line-height:17px;">
14
+ <% if !p.subdivision.blank? %><span><%= p.subdivision.titleize %></span> | <% end %>
15
+ <% if !p.city.blank? && p.city.upcase != "NONE" %><span><%= p.city.titleize %></span><% end %>
16
+ </p>
17
+ </td>
18
+ <td align="left" valign="top" width="37.5%" class="templateColumnContainer" style="width:37.5%;padding:5px 0 5px 10px;">
19
+ <p style="margin:0 0 5px 0;font-size:16px;line-height:18px;"><strong><%= number_to_currency(p.list_price, :precision => 0) %></strong></p>
20
+ <p style="margin:0 0 5px 0;font-size:15px;line-height:17px;">
21
+ <% if !p.beds_total.blank? %><span><strong><%= p.beds_total %></strong> beds</span><br /><% end %>
22
+ <% if !p.baths_full.blank? %><span><strong><%= p.baths_full %></strong> baths</span><br /><% end %>
23
+ <% if !p.baths_half.blank? && p.baths_half > 0 %><span><strong><%= p.baths_half %></strong> ½ baths</span><br /><% end %>
24
+ <% if !p.sqft_total.blank? %><span><strong><%= number_with_delimiter(p.sqft_total.to_i, :delimiter => ',') %></strong> sqft</span><% end %>
25
+ </p>
26
+ <% if !p.original_entry_timestamp.blank? %>
27
+ <p style="margin:0;font-size:13px;line-height:15px;">Listed <%= time_ago_in_words(p.original_entry_timestamp) %> ago</p>
28
+ <% end %>
29
+ </td>
30
+ </tr>
31
+ </table>
@@ -0,0 +1,76 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5
+ <title></title>
6
+ <style>
7
+ @media only screen and (max-width: 480px) {
8
+ #templateColumns{
9
+ width: 100% !important;
10
+ }
11
+ .templateColumnContainer{
12
+ display: block !important;
13
+ width: 100% !important;
14
+ padding: 10px !important;
15
+ }
16
+ }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <table border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
21
+ <tr>
22
+ <td align="center" valign="top">
23
+ <table border="0" cellpadding="20" cellspacing="0" width="800" id="emailContainer">
24
+ <tr>
25
+ <td align="center" valign="top">
26
+
27
+
28
+
29
+ <div style="text-align:center;margin-bottom:20px;padding:20px;background-color:<%= @color %>;">
30
+ <a style="display:inline-block;text-decoration:none;" href="<%= @url %>" target="_blank">
31
+ <img style="display:block;margin:0;" src="<%= @logo_url %>" alt="<%= @site.description %>" width="220" />
32
+ </a>
33
+ </div>
34
+
35
+ <div style="text-align:center;padding:20px;">
36
+ <h2>New and Suggested Listings</h2>
37
+
38
+ <% if @new_listings.count > 0 %>
39
+ <h3>New Listings</h3>
40
+ <% @new_listings.each do |prop| %>
41
+ <%== render :partial => "caboose_rets/rets_mailer/property_row", :locals => { :p => prop, :url => @domain } %>
42
+ <% end %>
43
+ <div style="padding:10px 10px 40px 10px;text-align:center;">
44
+ <a style="display:inline-block;border-radius:5px;background:<%= @color %>;color:#fff;padding:5px 15px;line-height:30px;text-decoration:none;font-size:15px;" target="_blank" href="<%= @url %>">See More New Listings</a>
45
+ </div>
46
+ <% end %>
47
+
48
+ <% if @related_listings.count > 0 %>
49
+ <h3>Suggested Listings</h3>
50
+ <% @related_listings.each do |prop| %>
51
+ <%== render :partial => "caboose_rets/rets_mailer/property_row", :locals => { :p => prop, :url => @domain } %>
52
+ <% end %>
53
+ <div style="padding:10px 10px 40px 10px;text-align:center;">
54
+ <a style="display:inline-block;border-radius:5px;background:<%= @color %>;color:#fff;padding:5px 15px;line-height:30px;text-decoration:none;font-size:15px;" target="_blank" href="<%= @domain %>/saved-properties">See My Saved Listings</a>
55
+ </div>
56
+ <% end %>
57
+
58
+ </div>
59
+
60
+ <div style="text-align:center;margin-top:20px;padding:20px;background-color:<%= @color %>;">
61
+ <p style="margin:0;font-size:13px;">
62
+ <a style="color:#fff;text-decoration:none;font-size:13px;display:inline-block;" target="_blank" href="<%= @unsubscribe_url %>">
63
+ Unsubscribe
64
+ </a>
65
+ </p>
66
+ </div>
67
+
68
+ </td>
69
+ </tr>
70
+ </table>
71
+ </td>
72
+ </tr>
73
+ </table>
74
+ </body>
75
+ </html>
76
+
@@ -0,0 +1,69 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5
+ <title></title>
6
+ <style>
7
+ @media only screen and (max-width: 480px) {
8
+ #templateColumns{
9
+ width: 100% !important;
10
+ }
11
+ .templateColumnContainer{
12
+ display: block !important;
13
+ width: 100% !important;
14
+ padding: 10px !important;
15
+ }
16
+ }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <table border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
21
+ <tr>
22
+ <td align="center" valign="top">
23
+ <table border="0" cellpadding="20" cellspacing="0" width="800" id="emailContainer">
24
+ <tr>
25
+ <td align="center" valign="top">
26
+
27
+
28
+
29
+ <div style="text-align:center;margin-bottom:20px;padding:20px;background-color:<%= @color %>;">
30
+ <a style="display:inline-block;text-decoration:none;" href="<%= @url %>" target="_blank">
31
+ <img style="display:block;margin:0;" src="<%= @logo_url %>" alt="<%= @site.description %>" width="220" />
32
+ </a>
33
+ </div>
34
+
35
+ <div style="text-align:center;padding:20px;">
36
+ <h2>Price Changed</h2>
37
+
38
+ <% if @property %>
39
+ <%== render :partial => "caboose_rets/rets_mailer/property_row", :locals => { :p => @property, :url => @domain } %>
40
+ <div style="padding:0 10px;text-align:center;">
41
+ <p style="margin:0 0 6px 0;">
42
+ <strong>Old Price</strong>: <%= number_to_currency(@old_price, :precision => 0) %>
43
+ </p>
44
+ <p style="margin:0;">
45
+ <strong>New Price</strong>: <%= number_to_currency(@property.list_price, :precision => 0) %>
46
+ </p>
47
+ </div>
48
+ <% end %>
49
+
50
+
51
+ </div>
52
+
53
+ <div style="text-align:center;margin-top:20px;padding:20px;background-color:<%= @color %>;">
54
+ <p style="margin:0;font-size:13px;">
55
+ <a style="color:#fff;text-decoration:none;font-size:13px;display:inline-block;" target="_blank" href="<%= @unsubscribe_url %>">
56
+ Unsubscribe
57
+ </a>
58
+ </p>
59
+ </div>
60
+
61
+ </td>
62
+ </tr>
63
+ </table>
64
+ </td>
65
+ </tr>
66
+ </table>
67
+ </body>
68
+ </html>
69
+
@@ -0,0 +1,69 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5
+ <title></title>
6
+ <style>
7
+ @media only screen and (max-width: 480px) {
8
+ #templateColumns{
9
+ width: 100% !important;
10
+ }
11
+ .templateColumnContainer{
12
+ display: block !important;
13
+ width: 100% !important;
14
+ padding: 10px !important;
15
+ }
16
+ }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <table border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
21
+ <tr>
22
+ <td align="center" valign="top">
23
+ <table border="0" cellpadding="20" cellspacing="0" width="800" id="emailContainer">
24
+ <tr>
25
+ <td align="center" valign="top">
26
+
27
+
28
+
29
+ <div style="text-align:center;margin-bottom:20px;padding:20px;background-color:<%= @color %>;">
30
+ <a style="display:inline-block;text-decoration:none;" href="<%= @url %>" target="_blank">
31
+ <img style="display:block;margin:0;" src="<%= @logo_url %>" alt="<%= @site.description %>" width="220" />
32
+ </a>
33
+ </div>
34
+
35
+ <div style="text-align:center;padding:20px;">
36
+ <h2>Status Changed</h2>
37
+
38
+ <% if @property %>
39
+ <%== render :partial => "caboose_rets/rets_mailer/property_row", :locals => { :p => @property, :url => @domain } %>
40
+ <div style="padding:0 10px;text-align:center;">
41
+ <p style="margin:0 0 6px 0;">
42
+ <strong>Old Status</strong>: <%= @old_status %>
43
+ </p>
44
+ <p style="margin:0;">
45
+ <strong>New Status</strong>: <%= @property.status %>
46
+ </p>
47
+ </div>
48
+ <% end %>
49
+
50
+
51
+ </div>
52
+
53
+ <div style="text-align:center;margin-top:20px;padding:20px;background-color:<%= @color %>;">
54
+ <p style="margin:0;font-size:13px;">
55
+ <a style="color:#fff;text-decoration:none;font-size:13px;display:inline-block;" target="_blank" href="<%= @unsubscribe_url %>">
56
+ Unsubscribe
57
+ </a>
58
+ </p>
59
+ </div>
60
+
61
+ </td>
62
+ </tr>
63
+ </table>
64
+ </td>
65
+ </tr>
66
+ </table>
67
+ </body>
68
+ </html>
69
+
@@ -0,0 +1,20 @@
1
+ <div style="text-align:center;margin-bottom:20px;padding:20px;background-color:<%= @color %>;">
2
+ <a style="display:inline-block;text-decoration:none;" href="<%= @url %>" target="_blank">
3
+ <img style="display:block;margin:0;" src="<%= @logo_url %>" alt="<%= @site.description %>" width="220" />
4
+ </a>
5
+ </div>
6
+
7
+ <div style="text-align:center;padding:20px;">
8
+ <div style="max-width:500px;font-size:18px;margin:0 auto;line-height:26px;">
9
+ <h2>Welcome!</h2>
10
+ <p><%== @body %></p>
11
+ </div>
12
+ </div>
13
+
14
+ <div style="text-align:center;margin-top:20px;padding:20px;background-color:<%= @color %>;">
15
+ <p style="margin:0;font-size:13px;">
16
+ <a style="color:#fff;text-decoration:none;font-size:13px;display:inline-block;" target="_blank" href="<%= @unsubscribe_url %>">
17
+ Unsubscribe
18
+ </a>
19
+ </p>
20
+ </div>
@@ -4,7 +4,7 @@
4
4
 
5
5
  <div class="mb-holder">
6
6
  <p><div id='user_<%= @edituser.id %>_rets_agent_mls_id' ></div></p>
7
- <% if @edituser.date_created %><p>Date Registered: <%= @edituser.date_created.in_time_zone('Central Time (US & Canada)').strftime('%B %-d, %Y') %></p><% end %>
7
+ <% if @edituser.date_created %><p>Date Registered: <%= @edituser.date_created.in_time_zone('Central Time (US & Canada)').strftime('%B %-d, %Y, %l:%M%P') %></p><% end %>
8
8
  </div>
9
9
 
10
10
  <% if @saved.count > 0 %>
@@ -15,7 +15,30 @@
15
15
  <% next if prop.nil? || prop.status != 'Active' %>
16
16
  <li>
17
17
  <a href="/properties/<%= sp.mls_number %>/details" target="_blank">MLS #<%= sp.mls_number %> - <%= prop.full_address %></a>
18
- <% if !sp.date_saved.blank? %><span> - saved on <%= sp.date_saved.in_time_zone('Central Time (US & Canada)').strftime('%B %-d, %Y') %></span><% end %>
18
+ <% if !sp.date_saved.blank? %><span> - saved on <%= sp.date_saved.in_time_zone('Central Time (US & Canada)').strftime('%B %-d, %Y, %l:%M%P') %></span><% end %>
19
+ </li>
20
+ <% end %>
21
+ </ul>
22
+ <% end %>
23
+
24
+ <% if @emails.count > 0 %>
25
+ <h3 class="sp">Emails Sent</h3>
26
+ <ul class="saved">
27
+ <% @emails.each do |em| %>
28
+ <li>
29
+ <strong><%= em.kind %></strong>
30
+ <span>- sent on <%= em.date_sent.in_time_zone('Central Time (US & Canada)').strftime('%B %-d, %Y, %l:%M%P') %></span>
31
+ <% if !em.message.blank? %>
32
+ <p><%== em.message.gsub("\n","<br />") %></p>
33
+ <% elsif em.object_kind == 'Property' && em.object_id %>
34
+ <p><a href="/properties/<%= em.object_id %>/details" target="_blank">MLS # <%= em.object_id %></a>,
35
+ <% if em.kind.include?('Price') %>
36
+ Old Price: <%= number_to_currency(em.old_value, :precision => 0) %> -> New Price: <%= number_to_currency(em.new_value, :precision => 0) %>
37
+ <% else %>
38
+ Old Status: <%= em.old_value %> -> New Status: <%= em.new_value %>
39
+ <% end %>
40
+ </p>
41
+ <% end %>
19
42
  </li>
20
43
  <% end %>
21
44
  </ul>
@@ -29,12 +52,25 @@
29
52
  margin: 30px 0 10px 0;
30
53
  }
31
54
  ul.saved {
55
+ margin: 0;
32
56
  padding-left: 0;
33
57
  list-style-type: none;
34
58
  }
35
59
  ul.saved li {
36
60
  display: block;
37
- margin-bottom: 3px;
61
+ margin-bottom: 5px;
62
+ }
63
+ ul.saved li a {
64
+ text-decoration: none;
65
+ color: #3939a7;
66
+ }
67
+ ul.saved li a:hover {
68
+ color: #59a739;
69
+ }
70
+ ul.saved li p {
71
+ margin: 3px 0 0 0;
72
+ color: #3e3e3e;
73
+ font-size: 13px;
38
74
  }
39
75
  ul.saved li a {
40
76
  text-decoration: none;
@@ -1,3 +1,3 @@
1
1
  module CabooseRets
2
- VERSION = '0.1.180'
2
+ VERSION = '0.1.184'
3
3
  end
@@ -115,7 +115,7 @@ namespace :caboose_rets do
115
115
  params = {
116
116
  :search_type => 'OpenHouse',
117
117
  :class => 'OpenHouse',
118
- :query => "(OpenHouseKey=#{args.query})",
118
+ :query => "(ListingId=#{args.query})",
119
119
  :limit => 1,
120
120
  :timeout => -1
121
121
  }
@@ -242,6 +242,10 @@ namespace :caboose_rets do
242
242
  CabooseRets::RetsImporter.update_helper('Property', last_updated, false)
243
243
  end
244
244
 
245
+ task :send_daily_emails => :environment do
246
+ CabooseRets::Notification.delay(:queue => "rets").send_new_suggested_emails
247
+ end
248
+
245
249
  #desc "Delete old rets properties"
246
250
  #task :delete_old_properties => :environment do
247
251
  # CabooseRets::RetsImporter.delete_old_properties
@@ -271,8 +275,13 @@ namespace :caboose_rets do
271
275
 
272
276
  desc "update test"
273
277
  task :update_test => :environment do
274
- d = DateTime.now - 1.hours
275
- CabooseRets::RetsImporter.update_helper("Property", d, true)
278
+ d = DateTime.now - 6.hours
279
+ CabooseRets::RetsImporter.update_helper("OpenHouse", d, false)
280
+ end
281
+
282
+ desc "update rets"
283
+ task :updater => :environment do
284
+ CabooseRets::RetsImporter.delay(:queue => "rets").update_rets
276
285
  end
277
286
 
278
287
  desc "Updates all the listings from MLS"
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.1.180
4
+ version: 0.1.184
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Barry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-06 00:00:00.000000000 Z
11
+ date: 2021-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: caboose-cms
@@ -71,6 +71,7 @@ files:
71
71
  - app/models/caboose_rets/agent_meta.rb
72
72
  - app/models/caboose_rets/log.rb
73
73
  - app/models/caboose_rets/media.rb
74
+ - app/models/caboose_rets/notification.rb
74
75
  - app/models/caboose_rets/office.rb
75
76
  - app/models/caboose_rets/office_meta.rb
76
77
  - app/models/caboose_rets/open_house.rb
@@ -90,6 +91,8 @@ files:
90
91
  - app/views/caboose_rets/agents/contact.html.erb
91
92
  - app/views/caboose_rets/agents/details.html.erb
92
93
  - app/views/caboose_rets/agents/index.html.erb
94
+ - app/views/caboose_rets/agents/user_unsubscribe.html.erb
95
+ - app/views/caboose_rets/agents/user_unsubscribe_confirm.html.erb
93
96
  - app/views/caboose_rets/media/admin_property_media.html.erb
94
97
  - app/views/caboose_rets/offices/admin_edit.html.erb
95
98
  - app/views/caboose_rets/offices/admin_index.html.erb
@@ -105,7 +108,12 @@ files:
105
108
  - app/views/caboose_rets/properties/property_not_exists.html.erb
106
109
  - app/views/caboose_rets/properties/test_form.html.erb
107
110
  - app/views/caboose_rets/rets/admin_import_form.html.erb
111
+ - app/views/caboose_rets/rets_mailer/_property_row.html.erb
112
+ - app/views/caboose_rets/rets_mailer/daily_report.html.erb
108
113
  - app/views/caboose_rets/rets_mailer/new_user.html.erb
114
+ - app/views/caboose_rets/rets_mailer/property_price_change.html.erb
115
+ - app/views/caboose_rets/rets_mailer/property_status_change.html.erb
116
+ - app/views/caboose_rets/rets_mailer/user_welcome.html.erb
109
117
  - app/views/caboose_rets/saved_properties/index.html.erb
110
118
  - app/views/caboose_rets/saved_properties/rets_info.html.erb
111
119
  - app/views/caboose_rets/saved_properties/user_report.html.erb