Class: Visit
Overview
== Schema Information
Table name: visits
Database name: primary
id :bigint not null, primary key
browser :string
city :string
country :string
device_type :string
dnt :integer
gbraid :string
gclid :string
ip :string(39)
landing_page :text
latitude :decimal(10, 6)
locale :string(5)
longitude :decimal(10, 6)
os :string
postal_code :string
processed :boolean
referral_code :string
referrer :text
referring_domain :string
region :string
region_code :string(10)
screen_height :integer
screen_width :integer
search_keyword :string
started_at :datetime
user_agent :text
utm_campaign :string
utm_content :string
utm_medium :string
utm_source :string
utm_term :string
visitor_token :uuid
wbraid :string
session_id :string
source_id :integer
user_id :integer
utm_id :string
Indexes
index_visits_on_referral_code (referral_code)
index_visits_on_referring_domain (referring_domain) USING gin
index_visits_on_source_id (source_id)
index_visits_on_started_at (started_at)
index_visits_on_user_id (user_id)
Foreign Keys
fk_rails_... (source_id => sources.id)
fk_rails_... (user_id => parties.id) ON DELETE => nullify
Constant Summary
collapse
- SQL_CONVERSIONS =
%w[orders quotes opportunities room_configurations].map { |t| "exists(select 1 from #{t} where #{t}.visit_id = visits.id)" }.join(' or ').freeze
Class Method Summary
collapse
Instance Method Summary
collapse
ransackable_associations, ransackable_attributes, ransortable_attributes, #to_relation
#publish_event
Class Method Details
.by_source_id_with_descendants ⇒ ActiveRecord::Relation<Visit>
A relation of Visits that are by source id with descendants. Active Record Scope
74
|
# File 'app/models/visit.rb', line 74
scope :by_source_id_with_descendants, ->(source_id) { where(source_id: Source.self_and_descendants_ids(source_id)) }
|
.populate_empty_sources(visits_query = nil) ⇒ Object
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
# File 'app/models/visit.rb', line 178
def self.populate_empty_sources(visits_query = nil)
counter = 0
visits_query ||= Visit.all
visits_query = visits_query.where(source_id: nil)
max_counter = visits_query.size
visits_query.find_in_batches do |visits|
Visit.transaction do
visits.each do |v|
counter += 1
new_source = v.find_source
puts "[#{counter} / #{max_counter}] visit id #{v.id} -> #{new_source&.full_name || 'No Match'} [#{new_source&.id || '?'}]"
v.update_column(:source_id, new_source.id) if new_source
end
end
end
end
|
.populate_utm_id ⇒ Object
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
# File 'app/models/visit.rb', line 90
def self.populate_utm_id
visits = Visit.where(utm_id: nil).where("landing_page LIKE '%utm_id%'")
total = visits.count
puts "Populating utm_id for #{total} visits"
visits.select(:id, :landing_page).find_in_batches.with_index do |group, batch|
visit_ids_per_utm_id = {}
offset = batch * 1000
position = offset
group.each_with_index do |v, _index|
position += 1
progress = ((position.to_f / total.to_f) * 100).round(2) if total > 0
puts "[#{progress} % - #{position} / #{total}] #{v.id} -> #{v.landing_page}"
utm_id = v.landing_page_params[:utm_id]
if utm_id.present?
visit_ids_per_utm_id[utm_id] ||= []
visit_ids_per_utm_id[utm_id] << v.id
end
end
visit_ids_per_utm_id.each do |utm_id, ids|
puts "#{utm_id} : #{ids.inspect}"
Visit.where(id: ids).update_all(utm_id: utm_id)
end
end
end
|
.ransackable_scopes(_auth_object = nil) ⇒ Object
86
87
88
|
# File 'app/models/visit.rb', line 86
def self.ransackable_scopes(_auth_object = nil)
[:by_source_id_with_descendants]
end
|
.reconcile_with_parties(start_time:, end_time: nil) ⇒ Object
This method is under development
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
|
# File 'app/models/visit.rb', line 220
def self.reconcile_with_parties(start_time:, end_time: nil)
visits = Visit.where(user_id: nil).where(Visit[:started_at].gteq(start_time))
visits = visits.where(Visit[:started_at].lteq(end_time)) if end_time
visits.find_each do |visit|
customer = Customer.find_by(visit_id: visit.id)
if customer.nil?
puts 'Customer not found by visit id, attempting time range and ip lookup'
time_range = (visit.started_at - 5.seconds)..(visit.started_at + 5.seconds)
Customer.where(creation_method: 'web', created_at: time_range).order(Customer[:created_at].desc).find_each do |lookup_customer|
if lookup_customer.versions.where(event: 'create').where(ip: visit.ip).exists?
customer = lookup_customer
puts "Customer #{customer.id} found based on ip creation event"
break
end
end
else
puts "Found customer #{customer.id} via Visit id"
end
if customer
visit.update_column(:user_id, customer.id)
customer.update_column(:source_id, visit.source_id) if (customer.source.nil? || customer.source.unknown?) && visit.source_id
end
end
end
|
.with_conversions ⇒ ActiveRecord::Relation<Visit>
A relation of Visits that are with conversions. Active Record Scope
71
|
# File 'app/models/visit.rb', line 71
scope :with_conversions, -> { select("*,(#{SQL_CONVERSIONS}) as has_conversions") }
|
.with_events_count ⇒ ActiveRecord::Relation<Visit>
A relation of Visits that are with events count. Active Record Scope
72
|
# File 'app/models/visit.rb', line 72
scope :with_events_count, -> { select('visits.*, (SELECT COUNT(*) FROM visit_events WHERE visit_events.visit_id = visits.id) as events_count') }
|
.with_order_conversions ⇒ ActiveRecord::Relation<Visit>
A relation of Visits that are with order conversions. Active Record Scope
73
|
# File 'app/models/visit.rb', line 73
scope :with_order_conversions, -> { joins(:orders).where("orders.state = 'invoiced'") }
|
Instance Method Details
#country_iso3 ⇒ Object
132
133
134
|
# File 'app/models/visit.rb', line 132
def country_iso3
country_iso3166&.alpha3
end
|
#country_iso3166 ⇒ Object
126
127
128
129
130
|
# File 'app/models/visit.rb', line 126
def country_iso3166
return unless country.present?
ISO3166::Country.find_country_by_any_name(country)
end
|
#find_source ⇒ Object
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
# File 'app/models/visit.rb', line 163
def find_source
params = landing_page_params.dup
params[:referral_code] ||= referral_code if respond_to?(:referral_code)
params[:utm_id] ||= utm_id if respond_to?(:utm_id)
params[:utm_campaign] ||= utm_campaign if respond_to?(:utm_campaign)
params[:utm_source] ||= utm_source if respond_to?(:utm_source)
params[:utm_medium] ||= utm_medium if respond_to?(:utm_medium)
params[:gclid] ||= gclid if respond_to?(:gclid)
params[:gbraid] ||= gbraid if respond_to?(:gbraid)
params[:wbraid] ||= wbraid if respond_to?(:wbraid)
Source.find_from_params(params, referrer: referrer)
end
|
#geocode ⇒ Object
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
|
# File 'app/models/visit.rb', line 247
def geocode
return if latitude && longitude
location =
begin
Geocoder.search(ip).first
rescue StandardError
Rails.logger.error "Geocode error: #{e.class.name}: #{e.message}"
nil
end
return unless location && location.country.present?
data = {
country: location.country,
region: location.try(:state).presence,
city: location.try(:city).presence,
postal_code: location.try(:postal_code).presence,
latitude: location.try(:latitude).presence,
longitude: location.try(:longitude).presence
}
update(data)
end
|
#keywords ⇒ Object
116
117
118
|
# File 'app/models/visit.rb', line 116
def keywords
utm_term || search_keyword
end
|
#landing_page_addressable ⇒ Object
140
141
142
143
144
145
146
|
# File 'app/models/visit.rb', line 140
def landing_page_addressable
@landing_page_parsed ||= begin
Addressable::URI.parse(landing_page)
rescue StandardError
nil
end
end
|
#landing_page_params ⇒ Object
152
153
154
155
156
157
158
159
160
|
# File 'app/models/visit.rb', line 152
def landing_page_params
params = {}
return params unless query = landing_page_addressable&.query
params = Rack::Utils.parse_nested_query query
params.with_indifferent_access
rescue StandardError
{}
end
|
#landing_page_path ⇒ Object
148
149
150
|
# File 'app/models/visit.rb', line 148
def landing_page_path
landing_page_addressable&.path || landing_page
end
|
#location_summary ⇒ Object
136
137
138
|
# File 'app/models/visit.rb', line 136
def location_summary
[city, region, postal_code, country].compact.join(' ')
end
|
#opportunities ⇒ ActiveRecord::Relation<Opportunity>
68
|
# File 'app/models/visit.rb', line 68
has_many :opportunities
|
#orders ⇒ ActiveRecord::Relation<Order>
66
|
# File 'app/models/visit.rb', line 66
has_many :orders
|
63
|
# File 'app/models/visit.rb', line 63
has_one :party, inverse_of: :visit
|
#quotes ⇒ ActiveRecord::Relation<Quote>
67
|
# File 'app/models/visit.rb', line 67
has_many :quotes
|
#remove_self_referrer ⇒ Object
195
196
197
198
199
200
|
# File 'app/models/visit.rb', line 195
def remove_self_referrer
return unless /\A(www\.)?warmlyyours\.com/.match?(referring_domain)
self.referrer = nil
self.referring_domain = nil
end
|
#room_configurations ⇒ ActiveRecord::Relation<RoomConfiguration>
69
|
# File 'app/models/visit.rb', line 69
has_many :room_configurations
|
#set_source ⇒ Object
202
203
204
|
# File 'app/models/visit.rb', line 202
def set_source
self.source = find_source
end
|
61
|
# File 'app/models/visit.rb', line 61
belongs_to :source, inverse_of: :visits, optional: true
|
#state_code ⇒ Object
120
121
122
123
124
|
# File 'app/models/visit.rb', line 120
def state_code
return unless (c = country_iso3166) && region.present?
c.find_subdivision_by_name(region)&.code
end
|
#update_associated_customer_source ⇒ Object
206
207
208
209
210
211
212
213
214
215
216
217
|
# File 'app/models/visit.rb', line 206
def update_associated_customer_source
return unless source && user
current = user.source
return unless current.nil? ||
current.unknown_source? ||
source.descendant_of?(current)
user.update_attribute(:source_id, source.id)
end
|
60
|
# File 'app/models/visit.rb', line 60
belongs_to :user, class_name: 'Party', inverse_of: :visits, optional: true
|
#visit_events ⇒ ActiveRecord::Relation<VisitEvent>
65
|
# File 'app/models/visit.rb', line 65
has_many :visit_events, inverse_of: :visit
|