mitamirri 0.13.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/.document +5 -0
  2. data/.gitignore +26 -0
  3. data/README.rdoc +117 -0
  4. data/Rakefile +26 -0
  5. data/VERSION +1 -0
  6. data/app/controllers/application_controller.rb +10 -0
  7. data/app/controllers/trackable_actions_controller.rb +10 -0
  8. data/app/controllers/trackable_sessions_controller.rb +100 -0
  9. data/app/helpers/application_helper.rb +2 -0
  10. data/app/models/trackable_action.rb +193 -0
  11. data/app/models/trackable_location.rb +19 -0
  12. data/app/models/trackable_session.rb +352 -0
  13. data/app/models/trackable_stat.rb +64 -0
  14. data/app/views/layouts/application.html.erb +20 -0
  15. data/app/views/trackable_sessions/_keywords_graph.html.erb +24 -0
  16. data/app/views/trackable_sessions/_print_buttons.html.erb +3 -0
  17. data/app/views/trackable_sessions/_sessions.html.erb +26 -0
  18. data/app/views/trackable_sessions/_top_referrers.html.erb +24 -0
  19. data/app/views/trackable_sessions/_traffic_summary_all.html.erb +64 -0
  20. data/app/views/trackable_sessions/_traffic_summary_by_kind.html.erb +48 -0
  21. data/app/views/trackable_sessions/_visits_graph.html.erb +25 -0
  22. data/app/views/trackable_sessions/content.html.erb +119 -0
  23. data/app/views/trackable_sessions/explorer.html.erb +67 -0
  24. data/app/views/trackable_sessions/export.xls.erb +5 -0
  25. data/app/views/trackable_sessions/index.html.erb +47 -0
  26. data/app/views/trackable_sessions/intersite.html.erb +97 -0
  27. data/app/views/trackable_sessions/leads.html.erb +98 -0
  28. data/app/views/trackable_sessions/show.html.erb +113 -0
  29. data/app/views/trackable_sessions/visitor_profile.html.erb +114 -0
  30. data/config/boot.rb +110 -0
  31. data/config/database.yml +0 -0
  32. data/config/environment.rb +24 -0
  33. data/config/environments/development.rb +17 -0
  34. data/config/environments/production.rb +28 -0
  35. data/config/environments/test.rb +31 -0
  36. data/config/initializers/backtrace_silencers.rb +7 -0
  37. data/config/initializers/database.rb +1 -0
  38. data/config/initializers/inflections.rb +10 -0
  39. data/config/initializers/mime_types.rb +5 -0
  40. data/config/initializers/new_rails_defaults.rb +21 -0
  41. data/config/initializers/session_store.rb +15 -0
  42. data/config/locales/en.yml +5 -0
  43. data/config/routes.rb +11 -0
  44. data/db/development.sqlite3 +0 -0
  45. data/db/migrate/20100810173533_create_trackable_sessions.rb +23 -0
  46. data/db/migrate/20100810173605_create_trackable_actions.rb +20 -0
  47. data/db/test.sqlite3 +1 -0
  48. data/doc/README_FOR_APP +2 -0
  49. data/init.rb +8 -0
  50. data/lib/mitamirri/content_report.rb +75 -0
  51. data/lib/mitamirri/helper.rb +106 -0
  52. data/lib/mitamirri/intersite_traffic_report.rb +122 -0
  53. data/lib/mitamirri/leads_report.rb +90 -0
  54. data/lib/mitamirri/session_report.rb +219 -0
  55. data/lib/mitamirri/stat_report.rb +216 -0
  56. data/lib/mitamirri/tasks.rb +27 -0
  57. data/lib/mitamirri/visitor_profile_report.rb +127 -0
  58. data/lib/mitamirri.rb +15 -0
  59. data/log/development.log +0 -0
  60. data/log/production.log +0 -0
  61. data/log/server.log +0 -0
  62. data/log/test.log +0 -0
  63. data/mitamirri.gemspec +125 -0
  64. data/public/stylesheets/mitamirri.css +736 -0
  65. data/script/about +4 -0
  66. data/script/console +3 -0
  67. data/script/dbconsole +3 -0
  68. data/script/destroy +3 -0
  69. data/script/generate +3 -0
  70. data/script/performance/benchmarker +3 -0
  71. data/script/performance/profiler +3 -0
  72. data/script/plugin +3 -0
  73. data/script/runner +3 -0
  74. data/script/server +3 -0
  75. data/spec/controllers/trackable_actions_controller_spec.rb +5 -0
  76. data/spec/controllers/trackable_sessions_controller_spec.rb +5 -0
  77. data/spec/custom_matchers.rb +23 -0
  78. data/spec/helpers/application_helper_spec.rb +57 -0
  79. data/spec/lib/content_report_spec.rb +37 -0
  80. data/spec/lib/intersite_traffic_report_spec.rb +51 -0
  81. data/spec/lib/leads_report_spec.rb +46 -0
  82. data/spec/lib/session_report_spec.rb +107 -0
  83. data/spec/lib/stat_report_spec.rb +106 -0
  84. data/spec/models/trackable_action_spec.rb +42 -0
  85. data/spec/models/trackable_session_spec.rb +154 -0
  86. data/spec/rcov.opts +4 -0
  87. data/spec/schema.rb +34 -0
  88. data/spec/spec.opts +4 -0
  89. data/spec/spec_helper.rb +32 -0
  90. 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>