mitamirri 0.13.8
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.
- data/.document +5 -0
- data/.gitignore +26 -0
- data/README.rdoc +117 -0
- data/Rakefile +26 -0
- data/VERSION +1 -0
- data/app/controllers/application_controller.rb +10 -0
- data/app/controllers/trackable_actions_controller.rb +10 -0
- data/app/controllers/trackable_sessions_controller.rb +100 -0
- data/app/helpers/application_helper.rb +2 -0
- data/app/models/trackable_action.rb +193 -0
- data/app/models/trackable_location.rb +19 -0
- data/app/models/trackable_session.rb +352 -0
- data/app/models/trackable_stat.rb +64 -0
- data/app/views/layouts/application.html.erb +20 -0
- data/app/views/trackable_sessions/_keywords_graph.html.erb +24 -0
- data/app/views/trackable_sessions/_print_buttons.html.erb +3 -0
- data/app/views/trackable_sessions/_sessions.html.erb +26 -0
- data/app/views/trackable_sessions/_top_referrers.html.erb +24 -0
- data/app/views/trackable_sessions/_traffic_summary_all.html.erb +64 -0
- data/app/views/trackable_sessions/_traffic_summary_by_kind.html.erb +48 -0
- data/app/views/trackable_sessions/_visits_graph.html.erb +25 -0
- data/app/views/trackable_sessions/content.html.erb +119 -0
- data/app/views/trackable_sessions/explorer.html.erb +67 -0
- data/app/views/trackable_sessions/export.xls.erb +5 -0
- data/app/views/trackable_sessions/index.html.erb +47 -0
- data/app/views/trackable_sessions/intersite.html.erb +97 -0
- data/app/views/trackable_sessions/leads.html.erb +98 -0
- data/app/views/trackable_sessions/show.html.erb +113 -0
- data/app/views/trackable_sessions/visitor_profile.html.erb +114 -0
- data/config/boot.rb +110 -0
- data/config/database.yml +0 -0
- data/config/environment.rb +24 -0
- data/config/environments/development.rb +17 -0
- data/config/environments/production.rb +28 -0
- data/config/environments/test.rb +31 -0
- data/config/initializers/backtrace_silencers.rb +7 -0
- data/config/initializers/database.rb +1 -0
- data/config/initializers/inflections.rb +10 -0
- data/config/initializers/mime_types.rb +5 -0
- data/config/initializers/new_rails_defaults.rb +21 -0
- data/config/initializers/session_store.rb +15 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +11 -0
- data/db/development.sqlite3 +0 -0
- data/db/migrate/20100810173533_create_trackable_sessions.rb +23 -0
- data/db/migrate/20100810173605_create_trackable_actions.rb +20 -0
- data/db/test.sqlite3 +1 -0
- data/doc/README_FOR_APP +2 -0
- data/init.rb +8 -0
- data/lib/mitamirri/content_report.rb +75 -0
- data/lib/mitamirri/helper.rb +106 -0
- data/lib/mitamirri/intersite_traffic_report.rb +122 -0
- data/lib/mitamirri/leads_report.rb +90 -0
- data/lib/mitamirri/session_report.rb +219 -0
- data/lib/mitamirri/stat_report.rb +216 -0
- data/lib/mitamirri/tasks.rb +27 -0
- data/lib/mitamirri/visitor_profile_report.rb +127 -0
- data/lib/mitamirri.rb +15 -0
- data/log/development.log +0 -0
- data/log/production.log +0 -0
- data/log/server.log +0 -0
- data/log/test.log +0 -0
- data/mitamirri.gemspec +125 -0
- data/public/stylesheets/mitamirri.css +736 -0
- data/script/about +4 -0
- data/script/console +3 -0
- data/script/dbconsole +3 -0
- data/script/destroy +3 -0
- data/script/generate +3 -0
- data/script/performance/benchmarker +3 -0
- data/script/performance/profiler +3 -0
- data/script/plugin +3 -0
- data/script/runner +3 -0
- data/script/server +3 -0
- data/spec/controllers/trackable_actions_controller_spec.rb +5 -0
- data/spec/controllers/trackable_sessions_controller_spec.rb +5 -0
- data/spec/custom_matchers.rb +23 -0
- data/spec/helpers/application_helper_spec.rb +57 -0
- data/spec/lib/content_report_spec.rb +37 -0
- data/spec/lib/intersite_traffic_report_spec.rb +51 -0
- data/spec/lib/leads_report_spec.rb +46 -0
- data/spec/lib/session_report_spec.rb +107 -0
- data/spec/lib/stat_report_spec.rb +106 -0
- data/spec/models/trackable_action_spec.rb +42 -0
- data/spec/models/trackable_session_spec.rb +154 -0
- data/spec/rcov.opts +4 -0
- data/spec/schema.rb +34 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +32 -0
- metadata +155 -0
@@ -0,0 +1,352 @@
|
|
1
|
+
class TrackableSession
|
2
|
+
|
3
|
+
include MongoMapper::Document
|
4
|
+
|
5
|
+
# MongoMapper Setup ==============================================================================
|
6
|
+
|
7
|
+
belongs_to :trackable_stat
|
8
|
+
many :trackable_actions, :order => :created_at
|
9
|
+
|
10
|
+
key :bounce, Boolean, :index => true
|
11
|
+
key :clickthrough_destination, String
|
12
|
+
key :duration, Integer
|
13
|
+
key :entrance_page, String
|
14
|
+
key :exit_page, String
|
15
|
+
key :has_clicks, Boolean, :index => true
|
16
|
+
key :has_clickthroughs, Boolean, :index => true
|
17
|
+
key :has_conversions, Boolean
|
18
|
+
key :has_mouseovers, Boolean, :index => true
|
19
|
+
key :has_scrolls, Boolean, :index => true
|
20
|
+
key :initial_request_url, String
|
21
|
+
key :ip_address, String, :index => true
|
22
|
+
key :kind, String, :index => true
|
23
|
+
key :location, String
|
24
|
+
key :new_visit, Boolean, :index => true
|
25
|
+
key :referrer, String, :index => true
|
26
|
+
key :referring_keywords, String
|
27
|
+
key :session_id, String, :index => true
|
28
|
+
key :site, String, :index => true
|
29
|
+
key :spider, Boolean, :index => true
|
30
|
+
key :trackable_stat_id, ObjectId
|
31
|
+
key :user_agent, String
|
32
|
+
key :views_count, Integer
|
33
|
+
timestamps!
|
34
|
+
ensure_index :created_at
|
35
|
+
|
36
|
+
# Callbacks ======================================================================================
|
37
|
+
|
38
|
+
before_create :init
|
39
|
+
|
40
|
+
# Constants ======================================================================================
|
41
|
+
|
42
|
+
KINDS = ['direct', 'natural', 'paid', 'search']
|
43
|
+
HUMAN_USER_AGENTS = ['Chrome', 'Firefox', 'Gecko', 'Mozilla', 'MSIE', 'Opera','Safari']
|
44
|
+
|
45
|
+
# Scopes =========================================================================================
|
46
|
+
|
47
|
+
scope :by_ip_address, lambda { |ip| { :conditions => { :ip_address => ip } } }
|
48
|
+
scope :of_kind, lambda { |k| { :conditions => { :kind => k } } }
|
49
|
+
scope :for_site, lambda { |s| { :conditions => { :site => s } } }
|
50
|
+
scope :for_month, lambda { |d| { :conditions => { :created_at => { '$gte' => d.beginning_of_month, '$lte' => d.end_of_month } } } }
|
51
|
+
scope :for_week, lambda { |d| { :conditions => { :created_at => { '$gte' => d.beginning_of_week, '$lte' => d.end_of_week } } } }
|
52
|
+
scope :for_date_range, lambda { |start_date,end_date| { :conditions => { :created_at => { '$gte' => start_date.beginning_of_month, '$lte' => end_date.end_of_month } } } }
|
53
|
+
scope :bounces, :conditions => {:bounce => true}
|
54
|
+
scope :direct_visits, :conditions => { :kind => 'direct' }, :order => 'created_at'
|
55
|
+
scope :new_visits, :conditions => { :new_visit => true }, :order => 'created_at'
|
56
|
+
scope :organic_visits, :conditions => { :kind => 'natural' }, :order => 'created_at'
|
57
|
+
scope :ppc_visits, :conditions => { :kind => 'paid' }, :order => 'created_at'
|
58
|
+
scope :spider_visits, :conditions => { :spider => true }, :order => 'created_at'
|
59
|
+
scope :recent_visits, :conditions => { :created_at => { '$gte' => Time.zone.now.beginning_of_month }}, :order => 'created_at DESC', :limit => 25
|
60
|
+
scope :return_visits, :conditions => { :new_visit => false }, :order => 'created_at'
|
61
|
+
scope :search_visits, :conditions => { :kind => 'search' }, :order => 'created_at'
|
62
|
+
scope :with_any_actions
|
63
|
+
scope :with_clicks, :conditions => { :has_clicks => true }, :order => 'created_at'
|
64
|
+
scope :with_clickthroughs, :conditions => { :has_clickthroughs => true }, :order => 'created_at'
|
65
|
+
scope :with_conversions, :conditions => { :has_conversions => true }, :order => 'created_at'
|
66
|
+
scope :with_leads, :conditions => { :has_conversions => true }, :order => 'created_at'
|
67
|
+
scope :with_mouseovers, :conditions => { :has_mouseovers => true }, :order => 'created_at'
|
68
|
+
scope :with_scrolls, :conditions => { :has_scrolls => true }, :order => 'created_at'
|
69
|
+
scope :with_views, :conditions => { :views_count => { '$gte' => 1 } }, :order => 'created_at'
|
70
|
+
|
71
|
+
# Class Methods ==================================================================================
|
72
|
+
|
73
|
+
def self.conversion_rate(args)
|
74
|
+
(TrackableSession.search(args).with_conversions.count.to_f / TrackableSession.search(args).count) * 100
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.clickthroughs_histogram(args)
|
78
|
+
conditions = TrackableSession.search(args).to_hash
|
79
|
+
TrackableSession.collection.group("function(x) { return { destination: x.clickthrough_destination }; }", conditions, { :count => 0}, "function(x,y){y.count++}", true).inject({}){|h,k| h[k['destination']] = k['count'] unless k['destination'].blank?; h}
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.entrance_pages_histogram(args)
|
83
|
+
conditions = TrackableSession.search(args).to_hash
|
84
|
+
TrackableSession.collection.group("function(x) { return { entrance_page: x.entrance_page }; }", conditions, { :count => 0}, "function(x,y){y.count++}", true).inject({}){|h,k| h[k['entrance_page']] = k['count']; h}.sort{|a,b| a[1] <=> b[1]}.reverse
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.exit_pages_histogram(args)
|
88
|
+
conditions = TrackableSession.search(args).to_hash
|
89
|
+
TrackableSession.collection.group("function(x) { return { exit_page: x.exit_page }; }", conditions, { :count => 0}, "function(x,y){y.count++}", true).inject({}){|h,k| h[k['exit_page']] = k['count']; h}.sort{|a,b| a[1] <=> b[1]}.reverse
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.keywords_histogram(args)
|
93
|
+
conditions = TrackableSession.search(args).to_hash
|
94
|
+
TrackableSession.collection.group("function(x) { return { keyword: x.referring_keywords }; }", conditions, { :count => 0}, "function(x,y){y.count++}", true).inject({}){|h,k| h[k['keyword']] = k['count'] unless k['keyword'].blank?; h}
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.kinds_for_select
|
98
|
+
[['All', 'all']] | TrackableSession::KINDS.sort.map{|k| [k.titleize, k]}
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.locations_histogram(args)
|
102
|
+
conditions = TrackableSession.search(args).to_hash
|
103
|
+
TrackableSession.collection.group("function(x) { return { location: x.location }; }", conditions, { :count => 0}, "function(x,y){y.count++}", true).inject({}){|h,k| h[k['location']] = k['count'].to_i unless k['location'].blank? || k['location'] == 'Unknown'; h}
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.parsed_user_agent(user_agent)
|
107
|
+
begin
|
108
|
+
_ua = Agent.new(user_agent)
|
109
|
+
_user_agent = "#{_ua.name} #{_ua.version} (#{_ua.os})"
|
110
|
+
rescue
|
111
|
+
_user_agent = "Unknown"
|
112
|
+
end
|
113
|
+
_user_agent
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.referrers_histogram(args)
|
117
|
+
conditions = TrackableSession.search(args).to_hash
|
118
|
+
TrackableSession.collection.group("function(x) { return { referrer: x.referrer }; }", conditions, { :count => 0}, "function(x,y){y.count++}", true).inject({}){|h,k| h[k['referrer']] = k['count'] unless k['referrer'].blank?; h}
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.search(args = {})
|
122
|
+
args[:site] ||= 'all sites'
|
123
|
+
args[:visit_kind] ||= 'all'
|
124
|
+
args[:action_kind] ||= 'any_action'
|
125
|
+
args[:action_kind] = args[:action_kind].pluralize
|
126
|
+
[:site, :action_kind, :visit_kind, :time_period].each{|a| args[a] = args[a].downcase if args[a]}
|
127
|
+
case args[:time_period]
|
128
|
+
when 'past 3 months'
|
129
|
+
args[:start_date] = (Time.zone.now - 2.months).beginning_of_month
|
130
|
+
args[:end_date] = Time.zone.now
|
131
|
+
when 'past 6 months'
|
132
|
+
args[:start_date] = (Time.zone.now - 5.months).beginning_of_month
|
133
|
+
args[:end_date] = Time.zone.now
|
134
|
+
when 'past 12 months'
|
135
|
+
args[:start_date] = (Time.zone.now - 11.months).beginning_of_month
|
136
|
+
args[:end_date] = Time.zone.now
|
137
|
+
else
|
138
|
+
args[:start_date] = TrackableSession.first.created_at
|
139
|
+
args[:end_date] = Time.zone.now
|
140
|
+
end
|
141
|
+
if args[:time_period]
|
142
|
+
if args[:visit_kind] == 'all'
|
143
|
+
if args[:site] == 'all sites'
|
144
|
+
TrackableSession.send("with_#{args[:action_kind]}").for_date_range(args[:start_date], args[:end_date])
|
145
|
+
else
|
146
|
+
TrackableSession.for_site(args[:site]).send("with_#{args[:action_kind]}").for_date_range(args[:start_date], args[:end_date])
|
147
|
+
end
|
148
|
+
else
|
149
|
+
if args[:site] == 'all sites'
|
150
|
+
TrackableSession.of_kind(args[:visit_kind]).send("with_#{args[:action_kind]}").for_date_range(args[:start_date], args[:end_date])
|
151
|
+
else
|
152
|
+
TrackableSession.for_site(args[:site]).of_kind(args[:visit_kind]).send("with_#{args[:action_kind]}").for_date_range(args[:start_date], args[:end_date])
|
153
|
+
end
|
154
|
+
end
|
155
|
+
else
|
156
|
+
if args[:visit_kind] == 'all'
|
157
|
+
if args[:site] == 'all sites'
|
158
|
+
TrackableSession.send("with_#{args[:action_kind]}")
|
159
|
+
else
|
160
|
+
TrackableSession.for_site(args[:site]).send("with_#{args[:action_kind]}")
|
161
|
+
end
|
162
|
+
else
|
163
|
+
if args[:site] == 'all sites'
|
164
|
+
TrackableSession.of_kind(args[:visit_kind]).send("with_#{args[:action_kind]}")
|
165
|
+
else
|
166
|
+
TrackableSession.for_site(args[:site]).of_kind(args[:visit_kind]).send("with_#{args[:action_kind]}")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Can't combine date conditions, so this is needed for SessionReport
|
173
|
+
def self.search_without_date(args = {})
|
174
|
+
args[:site] ||= 'all sites'
|
175
|
+
args[:visit_kind] ||= 'all'
|
176
|
+
args[:action_kind] ||= 'any_action'
|
177
|
+
args[:action_kind] = args[:action_kind].pluralize
|
178
|
+
args[:time_period] ||= 'past 3 months'
|
179
|
+
[:site, :action_kind, :visit_kind].each{|a| args[a] = args[a].downcase}
|
180
|
+
if args[:visit_kind] == 'all'
|
181
|
+
if args[:site] == 'all sites'
|
182
|
+
TrackableSession.send("with_#{args[:action_kind]}")
|
183
|
+
else
|
184
|
+
TrackableSession.for_site(args[:site]).send("with_#{args[:action_kind]}")
|
185
|
+
end
|
186
|
+
else
|
187
|
+
if args[:site] == 'all sites'
|
188
|
+
TrackableSession.of_kind(args[:visit_kind]).send("with_#{args[:action_kind]}")
|
189
|
+
else
|
190
|
+
TrackableSession.for_site(args[:site]).of_kind(args[:visit_kind]).send("with_#{args[:action_kind]}")
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.user_agents_histogram(args)
|
196
|
+
conditions = TrackableSession.search(args).to_hash
|
197
|
+
_histogram = TrackableSession.collection.group("function(x) { return { user_agent: x.user_agent }; }", conditions, { :count => 0}, "function(x,y){y.count++}", true)
|
198
|
+
_final = {}
|
199
|
+
_histogram.each do |ua|
|
200
|
+
_ua = parsed_user_agent(ua['user_agent'])
|
201
|
+
_final[_ua] ||= 0
|
202
|
+
_final[_ua] += ua['count'].to_i unless ua['user_agent'].blank? || ua['user_agent'] == 'Unknown'
|
203
|
+
end
|
204
|
+
_final
|
205
|
+
end
|
206
|
+
|
207
|
+
# Instance Methods ===============================================================================
|
208
|
+
|
209
|
+
def actions
|
210
|
+
self.trackable_actions.count - self.trackable_actions.views.count
|
211
|
+
end
|
212
|
+
|
213
|
+
def detect_new_visit
|
214
|
+
TrackableSession.find_by_ip_address(self.ip_address) ? false : true
|
215
|
+
end
|
216
|
+
|
217
|
+
def hr_duration
|
218
|
+
return 0 unless self.duration
|
219
|
+
self.duration > 60 ? sprintf("%.1f minutes", self.duration / 60.0) : sprintf("%.1f seconds", self.duration)
|
220
|
+
end
|
221
|
+
|
222
|
+
def init
|
223
|
+
self.bounce = true
|
224
|
+
self.kind ||= self.kind_by_referrer
|
225
|
+
self.new_visit ||= self.detect_new_visit
|
226
|
+
self.referring_keywords = self.sanitize_referring_keywords
|
227
|
+
self.spider ||= spider_visit?
|
228
|
+
begin
|
229
|
+
_location = Geokit::Geocoders::MultiGeocoder.geocode(self.ip_address)
|
230
|
+
self.location ||= _location.success? ? "#{_location.city}, #{_location.state}" : "Unknown"
|
231
|
+
rescue
|
232
|
+
self.location = "Unknown"
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def kind_by_referrer
|
237
|
+
return "direct" unless self.referrer
|
238
|
+
unless self.referrer.blank?
|
239
|
+
return 'search' if ['google','bing','yahoo'].include?(self.referrer.split('.')[-2])
|
240
|
+
return 'paid' if ppc_visit?(self.initial_request_url)
|
241
|
+
end
|
242
|
+
"natural"
|
243
|
+
end
|
244
|
+
|
245
|
+
def pageviews
|
246
|
+
self.trackable_actions.views.count
|
247
|
+
end
|
248
|
+
|
249
|
+
def parsed_user_agent
|
250
|
+
TrackableSession.parsed_user_agent(self.user_agent)
|
251
|
+
end
|
252
|
+
|
253
|
+
def last_visit_date
|
254
|
+
return nil if previous_visits_count.zero?
|
255
|
+
TrackableSession.by_ip_address(self.ip_address).fields(:created_at).map{|s| s.created_at.to_s(:concise)}.sort[-2]
|
256
|
+
end
|
257
|
+
|
258
|
+
def previous_visits_count
|
259
|
+
TrackableSession.by_ip_address(self.ip_address).count - 1
|
260
|
+
end
|
261
|
+
|
262
|
+
def sanitize_referring_keywords
|
263
|
+
self.referring_keywords.to_s.gsub('+',' ')
|
264
|
+
end
|
265
|
+
|
266
|
+
# Update the record field based on continued session activity
|
267
|
+
def touch(action_kind = nil, destination = nil, last_url = nil)
|
268
|
+
if action_kind && action_kind == 'scroll'
|
269
|
+
self.update_attributes(
|
270
|
+
:has_scrolls => true,
|
271
|
+
:bounce => self.trackable_actions.count <= 1,
|
272
|
+
:updated_at => Time.zone.now,
|
273
|
+
:duration => Time.zone.now - self.created_at
|
274
|
+
)
|
275
|
+
elsif action_kind && action_kind == 'click'
|
276
|
+
self.update_attributes(
|
277
|
+
:has_clicks => true,
|
278
|
+
:bounce => self.trackable_actions.count <= 1,
|
279
|
+
:updated_at => Time.zone.now,
|
280
|
+
:duration => Time.zone.now - self.created_at
|
281
|
+
)
|
282
|
+
elsif action_kind && action_kind == 'clickthrough'
|
283
|
+
self.update_attributes(
|
284
|
+
:has_clickthroughs => true,
|
285
|
+
:clickthrough_destination => destination,
|
286
|
+
:bounce => false,
|
287
|
+
:updated_at => Time.zone.now,
|
288
|
+
:duration => Time.zone.now - self.created_at
|
289
|
+
)
|
290
|
+
elsif action_kind && action_kind == 'conversion'
|
291
|
+
self.update_attributes(
|
292
|
+
:has_conversions => true,
|
293
|
+
:bounce => self.trackable_actions.count <= 1,
|
294
|
+
:updated_at => Time.zone.now,
|
295
|
+
:duration => Time.zone.now - self.created_at
|
296
|
+
)
|
297
|
+
elsif action_kind && action_kind == 'mouseover'
|
298
|
+
self.update_attributes(
|
299
|
+
:has_mouseovers => true,
|
300
|
+
:bounce => self.trackable_actions.count <= 1,
|
301
|
+
:updated_at => Time.zone.now,
|
302
|
+
:duration => Time.zone.now - self.created_at
|
303
|
+
)
|
304
|
+
else
|
305
|
+
# View
|
306
|
+
self.update_attributes(:updated_at => Time.zone.now, :duration => Time.zone.now - self.created_at, :views_count => self.views_count.to_i + 1, :exit_page => last_url, :bounce => self.trackable_actions.count > 1 ? false : true)
|
307
|
+
end
|
308
|
+
touch_stat
|
309
|
+
end
|
310
|
+
|
311
|
+
def visit_type
|
312
|
+
self.new_visit? ? "New" : "Returning"
|
313
|
+
end
|
314
|
+
|
315
|
+
def touch_stat
|
316
|
+
# Upsert associated stat
|
317
|
+
if trackable_stat = TrackableStat.by_site(self.site).by_date(self.created_at).first
|
318
|
+
trackable_stat.touch(self, self.trackable_actions[-1], self.trackable_actions[-2])
|
319
|
+
else
|
320
|
+
trackable_stat = TrackableStat.create(:site => self.site, :created_at => self.created_at)
|
321
|
+
trackable_stat.touch(self, self.trackable_actions.last)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
private
|
326
|
+
|
327
|
+
def ppc_visit?(url)
|
328
|
+
begin
|
329
|
+
_params = URI.parse(url).query.to_s.downcase
|
330
|
+
rescue
|
331
|
+
false
|
332
|
+
end
|
333
|
+
return false if _params.blank?
|
334
|
+
return true if _params.include?('gclid') # Google AdWords
|
335
|
+
return true if _params.include?('keyword') # AdCenter
|
336
|
+
return true if _params.include?('adid') # AdCenter?
|
337
|
+
return true if _params.include?('matchtype') # Generic
|
338
|
+
return true if _params.include?('ppc') # Generic
|
339
|
+
return true if _params.include?('cpc') # Generic
|
340
|
+
return false
|
341
|
+
end
|
342
|
+
|
343
|
+
def spider_visit?
|
344
|
+
_spider = true
|
345
|
+
return false if self.user_agent == "Unknown" || self.user_agent.nil?
|
346
|
+
unless self.user_agent.to_s.include?('bot')
|
347
|
+
HUMAN_USER_AGENTS.each{ |ua| _spider = false if self.user_agent.to_s.include?(ua) }
|
348
|
+
end
|
349
|
+
_spider
|
350
|
+
end
|
351
|
+
|
352
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class TrackableStat
|
2
|
+
|
3
|
+
include MongoMapper::Document
|
4
|
+
|
5
|
+
# MongoMapper Setup ==============================================================================
|
6
|
+
|
7
|
+
many :trackable_sessions, :order => :created_at
|
8
|
+
|
9
|
+
key :site, String, :index => true
|
10
|
+
key :bounces, :default => 0
|
11
|
+
key :clickthroughs, :default => 0
|
12
|
+
key :conversions, :default => 0
|
13
|
+
key :duration, :default => 0
|
14
|
+
key :new_visits, :default => 0
|
15
|
+
key :organic_visits, :default => 0
|
16
|
+
key :pageviews, :default => 0
|
17
|
+
key :ppc_visits, :default => 0
|
18
|
+
key :direct_visits, :default => 0
|
19
|
+
key :return_visits, :default => 0
|
20
|
+
key :search_visits, :default => 0
|
21
|
+
key :visits, :default => 0
|
22
|
+
timestamps!
|
23
|
+
|
24
|
+
# Scopes =========================================================================================
|
25
|
+
|
26
|
+
scope :by_site, lambda { |site| { :conditions => { :site => site } } }
|
27
|
+
scope :by_date, lambda { |date| { :conditions => { :created_at => { '$gte' => date.beginning_of_month, '$lte' => date.end_of_month } } } }
|
28
|
+
scope :for_date_range, lambda { |start_date,end_date| { :conditions => { :created_at => { '$gte' => start_date.beginning_of_month, '$lte' => end_date.end_of_month } } } }
|
29
|
+
|
30
|
+
# Class Methods ==================================================================================
|
31
|
+
|
32
|
+
def self.sites
|
33
|
+
TrackableStat.all(:fields => :site).map{|s| s.site.downcase}.uniq
|
34
|
+
end
|
35
|
+
|
36
|
+
# Instance Methods ===============================================================================
|
37
|
+
|
38
|
+
def touch(session, last_action, previous_action = nil)
|
39
|
+
if previous_action.nil?
|
40
|
+
# New session
|
41
|
+
self.trackable_sessions << session
|
42
|
+
self.clickthroughs += 1 if last_action.clickthrough?
|
43
|
+
self.conversions += 1 if last_action.conversion?
|
44
|
+
self.direct_visits += 1 if session.kind == 'direct'
|
45
|
+
self.new_visits += 1 if session.new_visit
|
46
|
+
self.organic_visits += 1 if session.kind == 'natural'
|
47
|
+
self.pageviews += 1
|
48
|
+
self.ppc_visits += 1 if session.kind == 'paid'
|
49
|
+
self.return_visits += 1 if ! session.new_visit
|
50
|
+
self.search_visits += 1 if session.kind == 'search'
|
51
|
+
self.visits += 1
|
52
|
+
self.bounces += 1 # Assume that it's a bounce until a second view is recorded
|
53
|
+
else
|
54
|
+
# Existing session
|
55
|
+
self.clickthroughs += 1 if last_action.clickthrough?
|
56
|
+
self.conversions += 1 if last_action.conversion?
|
57
|
+
self.duration += last_action.created_at - previous_action.created_at
|
58
|
+
self.pageviews += 1 if last_action.view?
|
59
|
+
self.bounces -= 1 if session.trackable_actions.count == 2
|
60
|
+
end
|
61
|
+
self.save
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
|
+
<head>
|
6
|
+
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
|
7
|
+
<title>Mitamirri: <%= yield :title -%></title>
|
8
|
+
<%= stylesheet_link_tag 'mitamirri' %>
|
9
|
+
<%= Seer::init_visualization -%>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
|
13
|
+
<p style="color: green"><%= flash[:notice] %></p>
|
14
|
+
|
15
|
+
<%= yield %>
|
16
|
+
|
17
|
+
<p class="footer">Generated by <a href="http://github.com/Bantik/mitamirri/">Mitamirri</a> by <a href="http://www.idolhands.com/">Corey Ehmke</a> / <a href="http://www.seologic.com/">SEOLogic</a>.</p>
|
18
|
+
|
19
|
+
</body>
|
20
|
+
</html>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<fieldset class="form_container">
|
2
|
+
<%= legend_tag "Top Referring Keywords", :help => 'This graph lists the actual search terms that drove traffic to the selected site(s). Click on each bar for a count of visits using a particular keyword phrase.' -%>
|
3
|
+
<br />
|
4
|
+
<div id="keyword_chart" class="chart" style="width: 100%; border: 1px solid #999999; background-color: #ffffff; text-align: center;"></div>
|
5
|
+
|
6
|
+
<%= Seer::visualize(
|
7
|
+
@keywords_series,
|
8
|
+
:as => :bar_chart,
|
9
|
+
:in_element => 'keyword_chart',
|
10
|
+
:series => {:series_label => 'keyword', :data_method => 'count'},
|
11
|
+
:chart_options => {
|
12
|
+
:height => @keywords_series.size * 20,
|
13
|
+
:width => 900,
|
14
|
+
:is_3_d => false,
|
15
|
+
:legend => 'none',
|
16
|
+
:colors => ["#7e7587"],
|
17
|
+
:title => "Visit Count by Keyword",
|
18
|
+
:title_font_size => 9,
|
19
|
+
:title_x => '',
|
20
|
+
:axis_font_size => 10
|
21
|
+
}
|
22
|
+
)
|
23
|
+
-%>
|
24
|
+
</fieldset>
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<%= link_to 'Print', "javascript:print();", :class => 'button' -%>
|
2
|
+
<%= link_to('Download PDF', "#{request.path}.pdf?#{request.query_string}", :class => 'button', :popup => true) if Mitamirri::pdf_output_enabled? -%>
|
3
|
+
<%= link_to "Export to Excel", traffic_export_url(params), :class => 'button' if @sessions %>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<%- if @sessions.blank? -%>
|
2
|
+
<p>No visits matched your criteria.</p>
|
3
|
+
<%- else -%>
|
4
|
+
<table class="standard">
|
5
|
+
<tr>
|
6
|
+
<th>Date</th>
|
7
|
+
<%- if @sites.size > 2 -%><th>Site</th><%- end -%>
|
8
|
+
<th>Referral Type</th>
|
9
|
+
<th>Visit Type</th>
|
10
|
+
<th>Visit Length</th>
|
11
|
+
<th>Pageviews</th>
|
12
|
+
<th></th>
|
13
|
+
</tr>
|
14
|
+
<%- @sessions.each do |session| -%>
|
15
|
+
<tr class="<%= cycle('odd', 'even') -%>">
|
16
|
+
<td><%= session.created_at.to_s(:short_date_time) %></td>
|
17
|
+
<%- if @sites.size > 2 -%><td><%= session.site %></td><%- end -%>
|
18
|
+
<td><%= session.kind.titleize %></td>
|
19
|
+
<td><%= session.visit_type %></td>
|
20
|
+
<td><%= session.hr_duration %></td>
|
21
|
+
<td><%= session.views_count %></td>
|
22
|
+
<td><%= link_to 'Details', session %></td>
|
23
|
+
</tr>
|
24
|
+
<%- end -%>
|
25
|
+
</table>
|
26
|
+
<%- end -%>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<fieldset class="form_container">
|
2
|
+
<%= legend_tag "Top Referring Sites", :help => 'Users visited the selected site(s) via links from sites in the list below. Click on each bar for a count of visits from the corresponding site.' -%>
|
3
|
+
<br />
|
4
|
+
<div id="referrer_chart" class="chart" style="width: 100%; border: 1px solid #999999; background-color: #ffffff; text-align: center;"></div>
|
5
|
+
|
6
|
+
<%= Seer::visualize(
|
7
|
+
@referrers_series,
|
8
|
+
:as => :bar_chart,
|
9
|
+
:in_element => 'referrer_chart',
|
10
|
+
:series => {:series_label => 'referrer', :data_method => 'count'},
|
11
|
+
:chart_options => {
|
12
|
+
:height => @referrers_series.size * 20,
|
13
|
+
:width => 900,
|
14
|
+
:is_3_d => false,
|
15
|
+
:legend => 'none',
|
16
|
+
:colors => ["#7e7587"],
|
17
|
+
:title => "Visit Count by Referrer",
|
18
|
+
:title_font_size => 9,
|
19
|
+
:title_x => '',
|
20
|
+
:axis_font_size => 10
|
21
|
+
}
|
22
|
+
)
|
23
|
+
-%>
|
24
|
+
</fieldset>
|
@@ -0,0 +1,64 @@
|
|
1
|
+
<fieldset class="form_container">
|
2
|
+
<%= legend_tag "Traffic Summary" -%>
|
3
|
+
|
4
|
+
<div class="three_column">
|
5
|
+
<fieldset>
|
6
|
+
<label>Total Visits:</label><br />
|
7
|
+
<div class="faux_field"><%= @report.total_visits -%></div>
|
8
|
+
</fieldset>
|
9
|
+
<fieldset>
|
10
|
+
<label>New Visits:</label><br />
|
11
|
+
<div class="faux_field"><%= @report.total_new_visits -%> (<%= @report.total_new_visits_percentage -%>)</div>
|
12
|
+
</fieldset>
|
13
|
+
<fieldset>
|
14
|
+
<label>Return Visits:</label><br />
|
15
|
+
<div class="faux_field"><%= @report.total_return_visits -%> (<%= @report.total_return_visits_percentage -%>)</div>
|
16
|
+
</fieldset>
|
17
|
+
<fieldset>
|
18
|
+
<%= tag_for_label_with_inline_help "Bounce Rate:", 'bounce_rate', "Percentage of visitors who left the site after viewing only one page." -%>
|
19
|
+
<div class="faux_field"><%= @report.bounce_rate -%></div>
|
20
|
+
</fieldset>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div class="three_column">
|
24
|
+
<fieldset>
|
25
|
+
<label>Visits from Referring Sites:</label><br />
|
26
|
+
<div class="faux_field"><%= @report.total_organic_visits -%> (<%= @report.total_organic_visits_percentage -%>)</div>
|
27
|
+
</fieldset>
|
28
|
+
<fieldset>
|
29
|
+
<label>Visits from Paid Search:</label><br />
|
30
|
+
<div class="faux_field"><%= @report.total_ppc_visits -%> (<%= @report.total_ppc_visits_percentage -%>)</div>
|
31
|
+
</fieldset>
|
32
|
+
<fieldset>
|
33
|
+
<label>Visits from Organic Search:</label><br />
|
34
|
+
<div class="faux_field"><%= @report.total_search_visits -%> (<%= @report.total_search_visits_percentage -%>)</div>
|
35
|
+
</fieldset>
|
36
|
+
<fieldset>
|
37
|
+
<label>Other Visits:</label><br />
|
38
|
+
<div class="faux_field"><%= @report.total_direct_visits -%> (<%= @report.total_direct_visits_percentage -%>)</div>
|
39
|
+
</fieldset>
|
40
|
+
</div>
|
41
|
+
|
42
|
+
<div class="three_column">
|
43
|
+
<fieldset>
|
44
|
+
<label>Total Pageviews:</label><br />
|
45
|
+
<div class="faux_field"><%= @report.total_pageviews -%></div>
|
46
|
+
</fieldset>
|
47
|
+
<fieldset>
|
48
|
+
<%= tag_for_label_with_inline_help "Total Leads / Lead Conversion Rate:", 'conversion', "Number and percentage of visitors who submitted a contact form." -%>
|
49
|
+
<div class="faux_field"><%= @report.total_conversions -%> (<%= @report.conversion_rate -%>)</div>
|
50
|
+
</fieldset>
|
51
|
+
<fieldset>
|
52
|
+
<%= tag_for_label_with_inline_help "Total Intersite Clickthroughs:", 'clickthroughs', "Number and percentage of visitors who clicked through from a local site to another site." -%>
|
53
|
+
<div class="faux_field"><%= @report.total_clickthroughs -%> (<%= @report.total_clickthroughs_percentage -%>) </div>
|
54
|
+
</fieldset>
|
55
|
+
<fieldset>
|
56
|
+
<label>Average Pages/Visit:</label><br />
|
57
|
+
<div class="faux_field"><%= @report.average_pages_per_visit -%></div>
|
58
|
+
</fieldset>
|
59
|
+
<fieldset>
|
60
|
+
<label>Average Visit Length:</label><br />
|
61
|
+
<div class="faux_field"><%= @report.average_time_on_site -%></div>
|
62
|
+
</fieldset>
|
63
|
+
</div>
|
64
|
+
</fieldset>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<fieldset class="form_container">
|
2
|
+
<%= legend_tag "Traffic Summary" -%>
|
3
|
+
|
4
|
+
<div class="three_column">
|
5
|
+
<fieldset>
|
6
|
+
<label>Total Visits:</label><br />
|
7
|
+
<div class="faux_field"><%= @report.total_visits -%></div>
|
8
|
+
</fieldset>
|
9
|
+
<fieldset>
|
10
|
+
<label>New Visits:</label><br />
|
11
|
+
<div class="faux_field"><%= @report.total_new_visits -%> (<%= @report.total_new_visits_percentage -%>)</div>
|
12
|
+
</fieldset>
|
13
|
+
<fieldset>
|
14
|
+
<label>Return Visits:</label><br />
|
15
|
+
<div class="faux_field"><%= @report.total_return_visits -%> (<%= @report.total_return_visits_percentage -%>)</div>
|
16
|
+
</fieldset>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div class="three_column">
|
20
|
+
<fieldset>
|
21
|
+
<label>Total Pageviews:</label><br />
|
22
|
+
<div class="faux_field"><%= @report.total_pageviews -%></div>
|
23
|
+
</fieldset>
|
24
|
+
<fieldset>
|
25
|
+
<%= tag_for_label_with_inline_help "Total Leads / Lead Conversion Rate:", 'conversion', "Number and percentage of visitors who submitted a contact form." -%>
|
26
|
+
<div class="faux_field"><%= @report.total_conversions -%> (<%= @report.conversion_rate -%>)</div>
|
27
|
+
</fieldset>
|
28
|
+
<fieldset>
|
29
|
+
<%= tag_for_label_with_inline_help "Total Intersite Clickthroughs:", 'clickthroughs', "Number and percentage of visitors who clicked through from a local site to another site." -%>
|
30
|
+
<div class="faux_field"><%= @report.total_clickthroughs -%> (<%= @report.total_clickthroughs_percentage -%>) </div>
|
31
|
+
</fieldset>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<div class="three_column">
|
35
|
+
<fieldset>
|
36
|
+
<label>Average Pages/Visit:</label><br />
|
37
|
+
<div class="faux_field"><%= @report.average_pages_per_visit -%></div>
|
38
|
+
</fieldset>
|
39
|
+
<fieldset>
|
40
|
+
<label>Average Visit Length:</label><br />
|
41
|
+
<div class="faux_field"><%= @report.average_time_on_site -%></div>
|
42
|
+
</fieldset>
|
43
|
+
<fieldset>
|
44
|
+
<%= tag_for_label_with_inline_help "Bounce Rate:", 'bounce_rate', "Percentage of visitors who left the site after viewing only one page." -%>
|
45
|
+
<div class="faux_field"><%= @report.bounce_rate -%></div>
|
46
|
+
</fieldset>
|
47
|
+
</div>
|
48
|
+
</fieldset>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<fieldset class="form_container">
|
2
|
+
<%= legend_tag "Visits by Kind (#{params[:time_period].titleize})", :help => 'This graph shows a breakdown of visits based on the visit source (direct, referring site, organic search, or paid search). Click on a data point in the graph for more information.' -%>
|
3
|
+
<br />
|
4
|
+
<div id="visits_chart" class="chart" style="width: 100%; border: 1px solid #999999; background-color: #ffffff; text-align: center;"></div>
|
5
|
+
<%= Seer::visualize(
|
6
|
+
@visits,
|
7
|
+
:as => :line_chart,
|
8
|
+
:in_element => 'visits_chart',
|
9
|
+
:series => {
|
10
|
+
:series_label => 'kind',
|
11
|
+
:data_label => 'short_date',
|
12
|
+
:data_method => 'visits',
|
13
|
+
:data_series => @visits_series
|
14
|
+
},
|
15
|
+
:chart_options => {
|
16
|
+
:height => 300,
|
17
|
+
:width => 900,
|
18
|
+
:axis_font_size => 11,
|
19
|
+
:colors => ['#7e7587','#7e7e00','#007e7e','#7e0000', '#007e00'],
|
20
|
+
:title => "",
|
21
|
+
:point_size => 5
|
22
|
+
}
|
23
|
+
)
|
24
|
+
-%>
|
25
|
+
</fieldset>
|