mls 1.5.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ class Flyer < MLS::Model
2
+
3
+ belongs_to :account
4
+ belongs_to :listing
5
+ belongs_to :document
6
+
7
+ def fields
8
+ read_attribute(:fields).with_indifferent_access
9
+ end
10
+
11
+ end
@@ -1,6 +1,6 @@
1
1
  class ImageOrdering < MLS::Model
2
2
 
3
- belongs_to :subject, :counter_cache => :photos_count, :polymorphic => true
3
+ belongs_to :subject, :polymorphic => true
4
4
  belongs_to :image
5
5
 
6
6
  end
@@ -0,0 +1,21 @@
1
+ class ImpressionCount < MLS::Model
2
+ belongs_to :subject, polymorphic: true
3
+
4
+ def self.by_day(**body)
5
+ req = Net::HTTP::Get.new("/impression_counts/by_day")
6
+ req.body = body.to_json
7
+ connection.instance_variable_get(:@connection).send_request(req).body
8
+ end
9
+
10
+ def self.by_week(**body)
11
+ req = Net::HTTP::Get.new("/impression_counts/by_week")
12
+ req.body = body.to_json
13
+ connection.instance_variable_get(:@connection).send_request(req).body
14
+ end
15
+
16
+ def self.by_month(**body)
17
+ req = Net::HTTP::Get.new("/impression_counts/by_month")
18
+ req.body = body.to_json
19
+ connection.instance_variable_get(:@connection).send_request(req).body
20
+ end
21
+ end
@@ -1,11 +1,59 @@
1
1
  class Inquiry < MLS::Model
2
2
 
3
3
  has_many :emails
4
- belongs_to :lead
4
+ has_many :leads
5
5
  belongs_to :subject, polymorphic: true
6
+ belongs_to :account
7
+
8
+ accepts_nested_attributes_for :account
6
9
 
7
10
  def property
8
11
  subject.is_a? MLS::Model::Listing ? subject.property : subject
9
12
  end
13
+
14
+ def account_attributes=(account_attrs)
15
+ account_attrs = account_attrs&.with_indifferent_access
16
+ self.account = if account_attrs.nil?
17
+ nil
18
+ elsif account_attrs["id"]
19
+ accnt = Account.find(account_attrs.delete("id"))
20
+ accnt.assign_attributes(account_attrs)
21
+ accnt
22
+ else
23
+ if account_attrs["email_addresses_attributes"]
24
+ email_address = EmailAddress.filter(address: account_attrs["email_addresses_attributes"].map{|ea| ea["address"].downcase}, account_id: true).first
25
+ accnt = email_address.account
26
+ accnt.assign_attributes(account_attrs)
27
+ end
28
+
29
+ if !accnt && account_attrs["phones_attributes"]
30
+ phone = Phone.filter(number: account_attrs["phones_attributes"].map{|p| PhoneValidator.normalize(p["number"])}, account_id: true).first
31
+ accnt = phone.account
32
+ accnt.assign_attributes(account_attrs)
33
+ end
34
+
35
+ if !accnt
36
+ accnt = Account.new(account_attrs)
37
+ end
38
+
39
+ accnt
40
+ end
41
+ end
42
+
43
+ def self.by_day(filter)
44
+ req = Net::HTTP::Get.new("/inquiries/by_day")
45
+ req.body = {
46
+ where: filter
47
+ }.to_json
48
+ connection.instance_variable_get(:@connection).send_request(req).body
49
+ end
50
+
51
+ def self.by_week(filter)
52
+ req = Net::HTTP::Get.new("/inquiries/by_week")
53
+ req.body = {
54
+ where: filter
55
+ }.to_json
56
+ connection.instance_variable_get(:@connection).send_request(req).body
57
+ end
10
58
 
11
59
  end
@@ -0,0 +1,16 @@
1
+ class Invoice < MLS::Model
2
+
3
+ belongs_to :credit_card
4
+ belongs_to :membership
5
+
6
+ def amount
7
+ read_attribute(:amount) / 100.0 if read_attribute(:amount)
8
+ end
9
+
10
+ def status
11
+ return "refunded" if refunded_at
12
+ return "paid" if cleared_at
13
+ return "pending"
14
+ end
15
+
16
+ end
@@ -1,10 +1,6 @@
1
1
  class Lead < MLS::Model
2
2
 
3
3
  belongs_to :account
4
- belongs_to :agent, class_name: "Account"
5
-
6
- has_many :recommendations
7
- # Removing lead_listings after recommendations release
8
- has_many :lead_listings
4
+ belongs_to :inquiry
9
5
 
10
6
  end
@@ -2,8 +2,10 @@ class Listing < MLS::Model
2
2
  self.inheritance_column = nil
3
3
 
4
4
  include MLS::Slugger
5
+ include MLS::Avatar
5
6
 
6
- SPACE_TYPES = %w(unit floor building)
7
+ UNIT_TYPES = %w(unit floor building)
8
+ FLOORS = ["Basement", "Mezzanine", "Penthouse", "Concourse", "Lower Level"] + (1..150).to_a
7
9
  TYPES = %w(Sale Lease Sublease)
8
10
  TERMS = ['Full Service', 'Net Lease', 'NN', 'NNN', 'Absolute NNN', 'Gross Lease', 'Modified Gross', 'Industrial Gross', 'Absolute Gross', 'Ground Lease', 'Other']
9
11
  SALE_TERMS = ['Cash to Seller', 'Purchase Money Mtg.', 'Owner Financing', 'Build-to-Suit', 'Sale/Leaseback', 'Other']
@@ -14,34 +16,45 @@ class Listing < MLS::Model
14
16
  '/yr' => 'rate_per_year',
15
17
  }
16
18
  TERM_UNITS = ['years', 'months']
17
- AMENITIES = %W(kitchen showers outdoor_space reception turnkey build_to_suit furniture
18
- natural_light high_ceilings plug_and_play additional_storage storefront)
19
+ AMENITIES = %W(kitchen showers outdoor_space reception turnkey build_to_suit
20
+ furniture natural_light high_ceilings plug_and_play additional_storage
21
+ storefront offices conference_rooms bathrooms)
19
22
 
20
23
  belongs_to :flyer, :class_name => 'Document'
21
- belongs_to :unit
24
+ belongs_to :floorplan, :class_name => 'Document'
22
25
  belongs_to :property
23
26
 
24
- has_many :photos, -> { order(:order => :asc) }, :as => :subject, :inverse_of => :subject
25
-
26
- has_many :agencies, -> { order(:order) }
27
- has_many :agents, -> { order('agencies.order') }, :through => :agencies, :source => :agent
28
-
27
+ has_many :ownerships, as: :asset
28
+ has_many :accounts, through: :ownerships, source: :account, inverse_of: :listings
29
+ has_many :image_orderings, as: :subject
30
+ has_many :photos, through: :image_orderings, source: :image
31
+
32
+ has_and_belongs_to_many :uses
33
+
29
34
  has_one :address
30
35
  has_many :addresses
36
+ has_many :references, as: :subject
31
37
 
32
- # has_many :comments
33
- # has_many :regions
34
- # has_many :agents
38
+ accepts_nested_attributes_for :uses, :ownerships, :image_orderings
39
+
40
+ def premium_property?
41
+ Subscription.filter(started_at: true, ends_at: false, type: "premium", subject_type: "Property", subject_id: self.property_id).count > 0
42
+ end
35
43
 
36
- # has_many :favoritizations, :foreign_key => :favorite_id
37
- # has_many :accounts, :through => :favoritizations
38
- # has_many :inquiries, :as => :subject, :inverse_of => :subject
39
- # has_many :agencies, -> { order('"order"') }, :dependent => :destroy, :inverse_of => :subject, :as => :subject
40
- # has_many :agents, -> { order('agencies.order') }, :through => :agencies, :inverse_of => :listings, :source => :agent
41
- # has_many :lead_listings, :dependent => :delete_all
44
+ def contacts
45
+ if ownerships.loaded?
46
+ @contacts ||= ownerships.select{|o| o.receives_inquiries }.map(&:account)
47
+ else
48
+ @contacts ||= ownerships.eager_load(:account).filter(:receives_inquiries => true).map(&:account)
49
+ end
50
+ end
42
51
 
43
- def contact
44
- @contact ||= agents.first
52
+ def lead_contact
53
+ if ownerships.loaded?
54
+ @lead_contact ||= ownerships.select{|o| o.lead}.first.try(:account)
55
+ else
56
+ @lead_contact ||= ownerships.eager_load(:account).filter(:lead => true).first.try(:account)
57
+ end
45
58
  end
46
59
 
47
60
  def rate(units=nil)
@@ -106,6 +119,18 @@ class Listing < MLS::Model
106
119
 
107
120
  price.round(2)
108
121
  end
122
+
123
+ def regions
124
+ Region.where(:id => self.region_ids)
125
+ end
126
+
127
+ def longitude
128
+ location.x
129
+ end
130
+
131
+ def latitude
132
+ location.y
133
+ end
109
134
 
110
135
  def lease? # TODO: test me
111
136
  type == 'Lease'
@@ -120,7 +145,17 @@ class Listing < MLS::Model
120
145
  end
121
146
 
122
147
  def name
123
- return read_attribute(:name) if read_attribute(:name)
124
- unit.name
148
+ return "New Listing" if !self.id
149
+ name = ""
150
+ if self.type == "building"
151
+ name += "Entire Building"
152
+ else
153
+ name = "Unit #{self.unit}" if self.unit.present?
154
+ name += " (" if self.unit.present? && self.floor.present?
155
+ name += "Floor #{self.floor}" if self.floor.present?
156
+ name += ")" if self.unit.present? && self.floor.present?
157
+ end
158
+ name = "Space" if name.blank?
159
+ name
125
160
  end
126
161
  end
@@ -0,0 +1,27 @@
1
+ class Membership < MLS::Model
2
+ self.inheritance_column = nil
3
+
4
+ has_many :accounts
5
+ has_many :invoices
6
+ has_many :subscriptions
7
+ belongs_to :organization
8
+ belongs_to :billing_contact, class_name: "Account"
9
+ belongs_to :credit_card
10
+ belongs_to :sourced_by, class_name: "Account"
11
+ has_and_belongs_to_many :invoice_recipients, class_name: 'EmailAddress'
12
+
13
+ accepts_nested_attributes_for :subscriptions
14
+
15
+ def rate
16
+ subscriptions.select{|x| !x.ends_at}.map(&:cost).compact.sum
17
+ end
18
+
19
+ def costs
20
+ (read_attribute(:costs) || {}).with_indifferent_access
21
+ end
22
+
23
+ def value
24
+ read_attribute(:value) / 100.0 if read_attribute(:value)
25
+ end
26
+
27
+ end
@@ -0,0 +1,3 @@
1
+ class Metadatum < MLS::Model
2
+ has_many :actions, foreign_key: :event_id, primary_key: :event_id
3
+ end
@@ -4,7 +4,7 @@ class Mistake < MLS::Model
4
4
  TYPES = %w(missing spelling incorrect other)
5
5
 
6
6
  belongs_to :task
7
- belongs_to :change
7
+ belongs_to :action
8
8
  belongs_to :account
9
9
 
10
10
  end
@@ -2,7 +2,9 @@ class Organization < MLS::Model
2
2
  include MLS::Avatar
3
3
  include MLS::Slugger
4
4
 
5
- has_many :agents, :class_name => 'Account'
5
+ has_many :accounts, :class_name => 'Account'
6
+ has_many :listings, -> { distinct }, through: :accounts
7
+ has_many :references, as: :subject
6
8
  has_and_belongs_to_many :regions
7
9
 
8
10
  def name
@@ -0,0 +1,8 @@
1
+ class Ownership < MLS::Model
2
+
3
+ belongs_to :account
4
+ belongs_to :asset, polymorphic: true
5
+
6
+ accepts_nested_attributes_for :account
7
+
8
+ end
@@ -4,5 +4,11 @@ class Phone < MLS::Model
4
4
  TYPES = ['mobile', 'work', 'home', 'main', 'home fax' 'work fax', 'other fax', 'pager', 'other', 'office']
5
5
 
6
6
  belongs_to :account
7
-
7
+
8
+ def number=(value)
9
+ write_attribute(:number, PhoneValidator.normalize(value))
10
+ write_attribute(:carrier_name, nil)
11
+ write_attribute(:carrier_type, nil)
12
+ end
13
+
8
14
  end
@@ -3,12 +3,26 @@ class Property < MLS::Model
3
3
  include MLS::Slugger
4
4
  include MLS::Avatar
5
5
 
6
- has_many :units
6
+ LEED_CERTIFICATIONS = %w(None Certified Silver Gold Platinum)
7
+ AMENITIES = %W(parking_garage lobby_attendant gym common_kitchen
8
+ common_bike_storage onsite_parking key_card_access freight_elevator
9
+ ada_accessible on_site_security
10
+ elevators close_highway close_public_transit close_points_of_interest
11
+ parking_ratio number_of_buildings)
12
+ CONSTRUCTION_TYPES = %w(brick steel concrete masonry tiltwall wood glass)
13
+
7
14
  has_many :references, as: :subject
8
15
  has_many :listings
9
16
  has_many :localities
17
+ has_many :coworking_spaces
10
18
  has_many :regions, :through => :localities
11
19
  has_many :image_orderings, as: :subject
20
+ has_many :data, as: :subject
21
+ has_many :photos, through: :image_orderings, source: :image
22
+ has_many :subscriptions, as: :subject
23
+
24
+ has_many :uses
25
+ # has_and_belongs_to_many :uses
12
26
 
13
27
  has_many :addresses do
14
28
  def primary
@@ -16,119 +30,129 @@ class Property < MLS::Model
16
30
  end
17
31
  end
18
32
 
19
- def photos
20
- image_orderings.sort_by(&:order).map(&:image)
21
- end
33
+ accepts_nested_attributes_for :image_orderings, :addresses
22
34
 
23
- def contact
24
- @contact ||= listings.where(leased_at: nil, authorized: true, type: ['Lease', 'Sublease'])
25
- .order(size: :desc).first.try(:contact)
35
+ def contacts
36
+ @contact ||= listings.eager_load(:accounts => [:email_addresses, :phones, :organization]).filter(leased_at: nil, authorized: true, type: ['Lease', 'Sublease'], :touched_at => {:gte => 90.days.ago})
37
+ .order(size: :desc).first.try(:contacts)
26
38
  end
27
39
 
28
40
  def address
29
41
  addresses.find(&:primary)
30
42
  end
31
-
43
+
32
44
  def longitude
33
45
  location.x
34
46
  end
35
-
47
+
36
48
  def latitude
37
49
  location.y
38
50
  end
39
-
40
- def automated_description
41
- external_data['narrativescience.com']
51
+
52
+ def display_description
53
+ return description if description.present?
54
+ return unless description_data_entry
55
+ # If has bullets
56
+ if description_data_entry && description_data_entry.split("\n").all? { |x| ['-','*', '•'].include?(x.strip[0]) }
57
+ <<~MD
58
+ ##Features
59
+ #{description_data_entry}
60
+ MD
61
+ else description_data_entry
62
+ show_amenities = amenities.select{ |k,v| v }
63
+ <<~MD
64
+ #{description_data_entry}
65
+ #{"This building's amenities include " + show_amenities.map {|key, v| key.to_s.humanize.downcase }.to_sentence + "."}
66
+ MD
67
+ end
42
68
  end
43
-
69
+
44
70
  def internet_providers
45
- data = []
71
+ idata = []
46
72
 
47
- if external_data['broadbandmap.gov']
48
- if external_data['broadbandmap.gov']['wirelineServices']
49
- external_data['broadbandmap.gov']['wirelineServices'].sort_by{|p| p['technologies'].sort_by{|t| t['maximumAdvertisedDownloadSpeed']}.reverse.first['maximumAdvertisedDownloadSpeed']}.reverse.each do |provider|
50
- tech = provider['technologies'].sort_by{|t| t['maximumAdvertisedDownloadSpeed']}.reverse.first
51
- speedcase = -> (speedCode) {
52
- case speedCode
53
- when 1 then '200 kbps'
54
- when 2 then '768 kbps'
55
- when 3 then '1.5 Mb/s'
56
- when 4 then '3 Mb/s'
57
- when 5 then '6 Mb/s'
58
- when 6 then '10 Mb/s'
59
- when 7 then '25 Mb/s'
60
- when 8 then '50 Mb/s'
61
- when 9 then '100 Mb/s'
62
- when 10 then '1 Gb/s'
63
- when 11 then '1 Gb/s+'
64
- else 'Unknown'
65
- end
66
- }
67
-
68
- data << {
69
- provider_name: provider['doingBusinessAs'] || provider['providerName'],
70
- provider_url: provider['providerURL'],
71
- technology: case tech['technologyCode']
72
- when 10 then 'DSL'
73
- when 20 then 'DSL'
74
- when 30 then 'Copper Wireline'
75
- when 40 then 'Cable'
76
- when 41 then 'Cable'
77
- when 50 then 'Fiber'
78
- when 90 then 'Power Line'
79
- else 'Other'
80
- end,
81
- bandwidth: {
82
- up: speedcase.call(tech['maximumAdvertisedUploadSpeed']),
83
- down: speedcase.call(tech['maximumAdvertisedDownloadSpeed'])
84
- }
85
- }
86
- end
87
- end
73
+ datum = data.select{|d| d.source == "broadbandmap.gov"}.first.try(:datum) if data.loaded?
74
+ datum ||= data.where(source: 'broadbandmap.gov').first.try(:datum)
75
+ return idata unless datum && datum['wirelineServices']
76
+
77
+ datum['wirelineServices'].sort_by{|p| p['technologies'].sort_by{|t| t['maximumAdvertisedDownloadSpeed']}.reverse.first['maximumAdvertisedDownloadSpeed']}.reverse.each do |provider|
78
+ tech = provider['technologies'].sort_by{|t| t['maximumAdvertisedDownloadSpeed']}.reverse.first
79
+ speedcase = -> (speedCode) {
80
+ case speedCode
81
+ when 1 then '200 kbps'
82
+ when 2 then '768 kbps'
83
+ when 3 then '1.5 Mb/s'
84
+ when 4 then '3 Mb/s'
85
+ when 5 then '6 Mb/s'
86
+ when 6 then '10 Mb/s'
87
+ when 7 then '25 Mb/s'
88
+ when 8 then '50 Mb/s'
89
+ when 9 then '100 Mb/s'
90
+ when 10 then '1 Gb/s'
91
+ when 11 then '10 Gb/s'
92
+ when 12 then '100 Gb/s'
93
+ when 13 then '100 Gb/s+'
94
+ else 'Unknown'
95
+ end
96
+ }
97
+
98
+ idata << {
99
+ provider_name: provider['doingBusinessAs'] || provider['providerName'],
100
+ provider_url: provider['providerURL'],
101
+ technology: case tech['technologyCode']
102
+ when 10 then 'DSL'
103
+ when 20 then 'DSL'
104
+ when 30 then 'Copper Wireline'
105
+ when 40 then 'Cable'
106
+ when 41 then 'Cable'
107
+ when 50 then 'Fiber'
108
+ when 90 then 'Power Line'
109
+ else 'Other'
110
+ end,
111
+ bandwidth: {
112
+ up: speedcase.call(tech['maximumAdvertisedUploadSpeed']),
113
+ down: speedcase.call(tech['maximumAdvertisedDownloadSpeed'])
114
+ }
115
+ }
88
116
  end
89
117
 
90
- data
118
+ idata
91
119
  end
92
-
120
+
93
121
  def closest_region
94
122
  region = neighborhood_region
95
123
  region ||= city_region
96
124
  region ||= market
97
125
  region
98
126
  end
99
-
127
+
100
128
  def neighborhood_region
101
129
  return @neighborhood_region if defined? @neighborhood_region
102
130
  params = {:query => neighborhood} if neighborhood
103
- params = {:type => "Neighborhood"}
131
+ params ||= {:type => "Neighborhood"}
104
132
  @neighborhood_region = fetch_region(params)
105
133
  end
106
-
134
+
107
135
  def city_region
108
136
  return @city_region if defined? @city_region
109
137
  @city_region = fetch_region(:type => "City")
110
138
  end
111
-
139
+
112
140
  def market
113
141
  return @market if defined? @market
114
142
  @market = fetch_region(:is_market => true)
115
143
  end
116
-
144
+
117
145
  def flagship
118
146
  return @flagship if defined? @flagship
119
147
  @flagship = fetch_region(:is_flagship => true)
120
148
  end
121
-
149
+
122
150
  def fetch_region(params)
123
- if regions.loaded?
124
- params = params.map{|k,v| [k, v]}
125
- if params[0][0] == :query
126
- regions.to_a.find{|r| r.name == params[0][1]}
127
- else
128
- regions.to_a.find{|r| r[params[0][0]] == params[0][1]}
129
- end
151
+ params = params.map{|k,v| [k, v]}
152
+ if params[0][0] == :query
153
+ regions.to_a.find{|r| r.name == params[0][1]}
130
154
  else
131
- regions.where(params).first
155
+ regions.to_a.find{|r| r[params[0][0]] == params[0][1]}
132
156
  end
133
157
  end
134
158
  end