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.
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>