mitamirri 0.13.8 → 2.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (236) hide show
  1. data/Capfile +4 -0
  2. data/Gemfile +29 -0
  3. data/README.rdoc +6 -55
  4. data/Rakefile +34 -21
  5. data/VERSION +1 -1
  6. data/app/controllers/admin/tracking/reports_controller.rb +78 -0
  7. data/app/controllers/admin/tracking/trackable_sessions_controller.rb +7 -0
  8. data/app/controllers/application_controller.rb +1 -8
  9. data/app/controllers/trackable_actions_controller.rb +0 -2
  10. data/app/helpers/application_helper.rb +21 -0
  11. data/app/helpers/layout_helper.rb +18 -0
  12. data/app/models/content_report.rb +35 -0
  13. data/app/models/content_stat.rb +4 -0
  14. data/app/models/destination.rb +4 -0
  15. data/app/models/intersite_traffic_report.rb +47 -0
  16. data/app/models/keyword_stat.rb +4 -0
  17. data/app/models/lead.rb +3 -0
  18. data/app/models/lead_stat.rb +5 -0
  19. data/app/models/leads_report.rb +32 -0
  20. data/app/models/location_stat.rb +37 -0
  21. data/app/models/referrer_stat.rb +4 -0
  22. data/app/models/report.rb +83 -0
  23. data/app/models/stat_base.rb +11 -0
  24. data/app/models/trackable_action.rb +114 -91
  25. data/app/models/trackable_base.rb +12 -0
  26. data/app/models/trackable_location.rb +7 -13
  27. data/app/models/trackable_session.rb +255 -171
  28. data/app/models/trackable_stat.rb +45 -27
  29. data/app/models/traffic_explorer_report.rb +92 -0
  30. data/app/models/traffic_summary_report.rb +142 -0
  31. data/app/models/user_agent_stat.rb +4 -0
  32. data/app/models/visit.rb +6 -0
  33. data/app/models/visit_stat.rb +5 -0
  34. data/app/models/visitor.rb +4 -0
  35. data/app/models/visitor_profile_report.rb +23 -0
  36. data/app/views/admin/tracking/reports/_content.html.erb +28 -0
  37. data/app/views/admin/tracking/reports/_conversion_rate.html.erb +30 -0
  38. data/app/views/admin/tracking/reports/_criteria.html.erb +33 -0
  39. data/app/views/admin/tracking/reports/_entrance_pages.html.erb +29 -0
  40. data/app/views/admin/tracking/reports/_exit_pages.html.erb +28 -0
  41. data/app/views/admin/tracking/reports/_explorer_criteria.html.erb +22 -0
  42. data/app/views/admin/tracking/reports/_explorer_summary.html.erb +22 -0
  43. data/app/views/admin/tracking/reports/_intersite_destinations.html.erb +28 -0
  44. data/app/views/admin/tracking/reports/_intersite_referrals.html.erb +30 -0
  45. data/app/views/admin/tracking/reports/_leads.html.erb +30 -0
  46. data/app/views/admin/tracking/reports/_locations.html.erb +47 -0
  47. data/app/views/admin/tracking/reports/_recent_visits.html.erb +33 -0
  48. data/app/views/admin/tracking/reports/_referring_keywords.html.erb +28 -0
  49. data/app/views/admin/tracking/reports/_referring_sites.html.erb +28 -0
  50. data/app/views/admin/tracking/reports/_traffic_summary.html.erb +26 -0
  51. data/app/views/admin/tracking/reports/_user_agents.html.erb +28 -0
  52. data/app/views/{trackable_sessions/_visits_graph.html.erb → admin/tracking/reports/_visits_by_kind.html.erb} +3 -3
  53. data/app/views/admin/tracking/reports/content.html.erb +12 -0
  54. data/app/views/admin/tracking/reports/explorer.html.erb +13 -0
  55. data/app/views/admin/tracking/reports/index.html.erb +16 -0
  56. data/app/views/admin/tracking/reports/intersite.html.erb +11 -0
  57. data/app/views/admin/tracking/reports/leads.html.erb +11 -0
  58. data/app/views/admin/tracking/reports/summary.html.erb +16 -0
  59. data/app/views/admin/tracking/reports/visitor_profile.html.erb +11 -0
  60. data/app/views/{trackable_sessions → admin/tracking/trackable_sessions}/show.html.erb +5 -4
  61. data/app/views/layouts/application.html.erb +51 -18
  62. data/config.ru +4 -0
  63. data/config/application.rb +13 -0
  64. data/config/boot.rb +13 -110
  65. data/config/cucumber.yml +10 -0
  66. data/config/deploy.rb +40 -0
  67. data/config/environment.rb +4 -23
  68. data/config/environments/development.rb +24 -13
  69. data/config/environments/production.rb +41 -20
  70. data/config/environments/test.rb +28 -24
  71. data/config/initializers/formats.rb +19 -0
  72. data/config/initializers/metric_fu.rb +9 -0
  73. data/config/initializers/secret_token.rb +7 -0
  74. data/config/initializers/session_store.rb +3 -10
  75. data/config/locales/en.yml +1 -1
  76. data/config/mongoid.yml +19 -0
  77. data/config/routes.rb +16 -10
  78. data/features/access_reports.feature +26 -0
  79. data/features/step_definitions/reports_steps.rb +9 -0
  80. data/features/step_definitions/web_steps.rb +219 -0
  81. data/features/support/env.rb +32 -0
  82. data/features/support/hooks.rb +1 -0
  83. data/features/support/paths.rb +33 -0
  84. data/features/traffic_summary_report.feature +43 -0
  85. data/init.rb +0 -7
  86. data/lib/mitamirri.rb +9 -12
  87. data/lib/mitamirri/engine.rb +7 -0
  88. data/lib/mitamirri/helper.rb +13 -29
  89. data/lib/mitamirri/railtie.rb +10 -0
  90. data/lib/tasks/cucumber.rake +60 -0
  91. data/lib/tasks/rcov.rake +44 -0
  92. data/mitamirri.gemspec +228 -100
  93. data/public/404.html +26 -0
  94. data/public/422.html +26 -0
  95. data/public/500.html +26 -0
  96. data/{config/database.yml → public/favicon.ico} +0 -0
  97. data/public/images/icons/add.png +0 -0
  98. data/public/images/icons/collapsed.gif +0 -0
  99. data/public/images/icons/delete.png +0 -0
  100. data/public/images/icons/drag.png +0 -0
  101. data/public/images/icons/edit.png +0 -0
  102. data/public/images/icons/expanded.gif +0 -0
  103. data/public/images/icons/help_icon.png +0 -0
  104. data/public/images/icons/link_icon.png +0 -0
  105. data/public/images/icons/move.png +0 -0
  106. data/public/images/icons/move_white.png +0 -0
  107. data/public/images/icons/note.png +0 -0
  108. data/public/images/icons/note_white.png +0 -0
  109. data/public/images/icons/notification_icon_sprite.png +0 -0
  110. data/public/images/icons/spinner.gif +0 -0
  111. data/public/images/icons/view.png +0 -0
  112. data/public/images/icons/warning.png +0 -0
  113. data/public/images/icons/warning_2.png +0 -0
  114. data/public/images/icons/warning_box.png +0 -0
  115. data/public/images/icons/warning_icon.png +0 -0
  116. data/public/images/icons/warning_white.png +0 -0
  117. data/public/images/layout/arrow_asc.png +0 -0
  118. data/public/images/layout/arrow_desc.png +0 -0
  119. data/public/images/layout/back.png +0 -0
  120. data/public/images/layout/black_bar.png +0 -0
  121. data/public/images/layout/branding.png +0 -0
  122. data/public/images/layout/breadcrumb_bg.png +0 -0
  123. data/public/images/layout/button_bg.png +0 -0
  124. data/public/images/layout/content_left_bg.png +0 -0
  125. data/public/images/layout/content_right_bg.png +0 -0
  126. data/public/images/layout/footer_bg.png +0 -0
  127. data/public/images/layout/h1_bg.png +0 -0
  128. data/public/images/layout/h2_bg.png +0 -0
  129. data/public/images/layout/h2_bg_for_table.png +0 -0
  130. data/public/images/layout/header_bg_grey.png +0 -0
  131. data/public/images/layout/header_bg_purple.png +0 -0
  132. data/public/images/layout/legend_bg.png +0 -0
  133. data/public/images/layout/menu_box_bg.png +0 -0
  134. data/public/images/layout/shadow_border.png +0 -0
  135. data/public/images/layout/shadow_border_2.png +0 -0
  136. data/public/images/layout/shadow_border_3.png +0 -0
  137. data/public/images/layout/shadow_border_4.png +0 -0
  138. data/public/images/layout/tab.png +0 -0
  139. data/public/images/layout/tab_active.png +0 -0
  140. data/public/images/layout/table_header.png +0 -0
  141. data/public/images/layout/text_field_bg.jpg +0 -0
  142. data/public/images/layout/text_field_error_bg.png +0 -0
  143. data/public/images/layout/th_bg.png +0 -0
  144. data/public/images/layout/th_bg_selected.png +0 -0
  145. data/public/images/menu_icons/content_report.png +0 -0
  146. data/public/images/menu_icons/content_report_on.png +0 -0
  147. data/public/images/menu_icons/funnel.png +0 -0
  148. data/public/images/menu_icons/funnel_on.png +0 -0
  149. data/public/images/menu_icons/intersite_traffic.png +0 -0
  150. data/public/images/menu_icons/intersite_traffic_on.png +0 -0
  151. data/public/images/menu_icons/leads.png +0 -0
  152. data/public/images/menu_icons/leads_on.png +0 -0
  153. data/public/images/menu_icons/traffic_explorer.png +0 -0
  154. data/public/images/menu_icons/traffic_explorer_on.png +0 -0
  155. data/public/images/menu_icons/traffic_summary.png +0 -0
  156. data/public/images/menu_icons/traffic_summary_on.png +0 -0
  157. data/public/images/menu_icons/visitor_profile.png +0 -0
  158. data/public/images/menu_icons/visitor_profile_on.png +0 -0
  159. data/public/index.html +8 -0
  160. data/public/javascripts/application.js +2 -0
  161. data/public/javascripts/controls.js +965 -0
  162. data/public/javascripts/dragdrop.js +974 -0
  163. data/public/javascripts/effects.js +1123 -0
  164. data/public/javascripts/prototype.js +6001 -0
  165. data/public/javascripts/rails.js +175 -0
  166. data/public/robots.txt +5 -0
  167. data/public/stylesheets/.gitkeep +0 -0
  168. data/public/stylesheets/application.css +876 -0
  169. data/public/stylesheets/core.css +1146 -0
  170. data/public/stylesheets/core_ie.css +52 -0
  171. data/public/stylesheets/csshover3.htc +14 -0
  172. data/public/stylesheets/mitamirri.css +37 -724
  173. data/public/stylesheets/mitamirri_print.css +3 -0
  174. data/script/cucumber +10 -0
  175. data/script/rails +6 -0
  176. data/spec/blueprints.rb +13 -0
  177. data/spec/helpers/application_helper_spec.rb +23 -35
  178. data/spec/{lib → models}/content_report_spec.rb +12 -8
  179. data/spec/{lib → models}/intersite_traffic_report_spec.rb +6 -9
  180. data/spec/{lib → models}/leads_report_spec.rb +11 -12
  181. data/spec/models/location_stat_spec.rb +18 -0
  182. data/spec/models/trackable_action_spec.rb +46 -14
  183. data/spec/models/trackable_base_spec.rb +27 -0
  184. data/spec/models/trackable_session_spec.rb +181 -57
  185. data/spec/{lib/session_report_spec.rb → models/traffic_explorer_report_spec.rb} +8 -25
  186. data/spec/spec_helper.rb +8 -28
  187. data/{lib/mitamirri/tasks.rb → tasks/mitamirri.rake} +6 -7
  188. data/vendor/plugins/.gitkeep +0 -0
  189. metadata +316 -77
  190. data/.gitignore +0 -26
  191. data/app/controllers/trackable_sessions_controller.rb +0 -100
  192. data/app/views/trackable_sessions/_keywords_graph.html.erb +0 -24
  193. data/app/views/trackable_sessions/_print_buttons.html.erb +0 -3
  194. data/app/views/trackable_sessions/_sessions.html.erb +0 -26
  195. data/app/views/trackable_sessions/_top_referrers.html.erb +0 -24
  196. data/app/views/trackable_sessions/_traffic_summary_all.html.erb +0 -64
  197. data/app/views/trackable_sessions/_traffic_summary_by_kind.html.erb +0 -48
  198. data/app/views/trackable_sessions/content.html.erb +0 -119
  199. data/app/views/trackable_sessions/explorer.html.erb +0 -67
  200. data/app/views/trackable_sessions/export.xls.erb +0 -5
  201. data/app/views/trackable_sessions/index.html.erb +0 -47
  202. data/app/views/trackable_sessions/intersite.html.erb +0 -97
  203. data/app/views/trackable_sessions/leads.html.erb +0 -98
  204. data/app/views/trackable_sessions/visitor_profile.html.erb +0 -114
  205. data/config/initializers/backtrace_silencers.rb +0 -7
  206. data/config/initializers/database.rb +0 -1
  207. data/config/initializers/inflections.rb +0 -10
  208. data/config/initializers/mime_types.rb +0 -5
  209. data/config/initializers/new_rails_defaults.rb +0 -21
  210. data/db/development.sqlite3 +0 -0
  211. data/db/migrate/20100810173533_create_trackable_sessions.rb +0 -23
  212. data/db/migrate/20100810173605_create_trackable_actions.rb +0 -20
  213. data/db/test.sqlite3 +0 -1
  214. data/lib/mitamirri/content_report.rb +0 -75
  215. data/lib/mitamirri/intersite_traffic_report.rb +0 -122
  216. data/lib/mitamirri/leads_report.rb +0 -90
  217. data/lib/mitamirri/session_report.rb +0 -219
  218. data/lib/mitamirri/stat_report.rb +0 -216
  219. data/lib/mitamirri/visitor_profile_report.rb +0 -127
  220. data/script/about +0 -4
  221. data/script/console +0 -3
  222. data/script/dbconsole +0 -3
  223. data/script/destroy +0 -3
  224. data/script/generate +0 -3
  225. data/script/performance/benchmarker +0 -3
  226. data/script/performance/profiler +0 -3
  227. data/script/plugin +0 -3
  228. data/script/runner +0 -3
  229. data/script/server +0 -3
  230. data/spec/controllers/trackable_actions_controller_spec.rb +0 -5
  231. data/spec/controllers/trackable_sessions_controller_spec.rb +0 -5
  232. data/spec/custom_matchers.rb +0 -23
  233. data/spec/lib/stat_report_spec.rb +0 -106
  234. data/spec/rcov.opts +0 -4
  235. data/spec/schema.rb +0 -34
  236. data/spec/spec.opts +0 -4
@@ -0,0 +1,12 @@
1
+ class TrackableBase
2
+
3
+ def self.dates_from_time_period(time_period="past 3 months")
4
+ start_date = TrackableAction.first.try(:created_at) || Time.zone.now
5
+ start_date = (Time.zone.now - 2.months).beginning_of_month if time_period == 'past 3 months'
6
+ start_date = (Time.zone.now - 5.months).beginning_of_month if time_period == 'past 6 months'
7
+ start_date = (Time.zone.now - 11.months).beginning_of_month if time_period == 'past 12 months'
8
+ end_date = Time.zone.now.end_of_month
9
+ [start_date, end_date]
10
+ end
11
+
12
+ end
@@ -1,19 +1,13 @@
1
- class TrackableLocation
1
+ class TrackableLocation < TrackableBase
2
2
 
3
- include MongoMapper::Document
3
+ include Mongoid::Document
4
4
 
5
- # MongoMapper Setup ==============================================================================
5
+ # Mongoid Setup ==============================================================================
6
6
 
7
- key :location, String, :index => true
8
- key :latitude, String
9
- key :longitude, String
10
-
11
- # Scopes =========================================================================================
12
-
13
- # Constants ======================================================================================
14
-
15
- # Class Methods ==================================================================================
7
+ field :location
8
+ field :latitude
9
+ field :longitude
16
10
 
17
- # Instance Methods ===============================================================================
11
+ index :location, :unique => false
18
12
 
19
13
  end
@@ -1,42 +1,57 @@
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
-
1
+ # Tracks user activity to a given site.
2
+ class TrackableSession < TrackableBase
3
+
4
+ # Mongoid Config =================================================================================
5
+
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+
9
+ referenced_in :trackable_stat
10
+ references_many :trackable_actions
11
+
12
+ field :bounce, :type => Boolean
13
+ field :clickthrough_destination
14
+ field :duration, :type => Integer
15
+ field :entrance_page
16
+ field :exit_page
17
+ field :has_clicks, :type => Boolean
18
+ field :has_clickthroughs, :type => Boolean
19
+ field :has_conversions, :type => Boolean
20
+ field :has_mouseovers, :type => Boolean
21
+ field :has_scrolls, :type => Boolean
22
+ field :initial_request_url
23
+ field :ip_address
24
+ field :kind
25
+ field :location
26
+ field :new_visit, :type => Boolean
27
+ field :referrer
28
+ field :referring_keywords
29
+ field :session_id
30
+ field :site
31
+ field :spider, :type => Boolean
32
+ field :user_agent
33
+ field :views_count, :type => Integer
34
+ field :visit_kind
35
+
36
+ index :bounce, :unique => false
37
+ index :created_at, :unique => false
38
+ index :has_clicks, :unique => false
39
+ index :has_clickthroughs, :unique => false
40
+ index :has_mouseovers, :unique => false
41
+ index :has_scrolls, :unique => false
42
+ index :ip_address, :unique => false
43
+ index :kind, :unique => false
44
+ index :new_visit, :unique => false
45
+ index :referrer, :unique => false
46
+ index :session_id, :unique => false
47
+ index :site, :unique => false
48
+ index :spider, :unique => false
49
+ index :visit_kind, :unique => false
50
+
36
51
  # Callbacks ======================================================================================
37
52
 
38
53
  before_create :init
39
-
54
+
40
55
  # Constants ======================================================================================
41
56
 
42
57
  KINDS = ['direct', 'natural', 'paid', 'search']
@@ -44,65 +59,132 @@ class TrackableSession
44
59
 
45
60
  # Scopes =========================================================================================
46
61
 
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 :by_ip_address, lambda { |ip| { :where => { :ip_address => ip } } }
63
+ scope :of_kind, lambda { |k| { :where => { :kind => k } } }
64
+ scope :for_site, lambda { |s| { :where => { :site => s } } }
65
+ scope :for_month, lambda { |d| { :where => { :created_at => { '$gte' => d.beginning_of_month, '$lte' => d.end_of_month } } } }
66
+ scope :for_week, lambda { |d| { :where => { :created_at => { '$gte' => d.beginning_of_week, '$lte' => d.end_of_week } } } }
67
+ scope :for_date_range, lambda { |start_date,end_date| { :where => { :created_at => { '$gte' => start_date.beginning_of_month, '$lte' => end_date.end_of_month } } } }
68
+ scope :bounces, :where => {:bounce => true}
69
+ scope :direct_visits, :where => { :kind => 'direct' }, :order => 'created_at'
70
+ scope :new_visits, :where => { :new_visit => true }, :order => 'created_at'
71
+ scope :organic_visits, :where => { :kind => 'natural' }, :order => 'created_at'
72
+ scope :ppc_visits, :where => { :kind => 'paid' }, :order => 'created_at'
73
+ scope :spider_visits, :where => { :spider => true }, :order => 'created_at'
74
+ scope :recent_visits, :where => { :created_at => { '$gte' => Time.zone.now.beginning_of_month }}, :order => 'created_at DESC', :limit => 25
75
+ scope :return_visits, :where => { :new_visit => false }, :order => 'created_at'
76
+ scope :search_visits, :where => { :kind => 'search' }, :order => 'created_at'
62
77
  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'
78
+ scope :with_clicks, :where => { :has_clicks => true }, :order => 'created_at'
79
+ scope :with_clickthroughs, :where => { :has_clickthroughs => true }
80
+ scope :with_conversions, :where => { :has_conversions => true }, :order => 'created_at'
81
+ scope :with_leads, :where => { :has_conversions => true }, :order => 'created_at'
82
+ scope :with_mouseovers, :where => { :has_mouseovers => true }, :order => 'created_at'
83
+ scope :with_scrolls, :where => { :has_scrolls => true }, :order => 'created_at'
84
+ scope :with_views, :where => { :views_count => { '$gte' => 1 } }, :order => 'created_at'
70
85
 
71
86
  # Class Methods ==================================================================================
72
87
 
88
+ # Returns the conversion rate for sessions matching the specified arguments, ie: percentage of the total number of visits that were conversions.
89
+ #
90
+ # ==== Attributes
91
+ #
92
+ # * +:time_period+ - date range as a string: 'past 3 months', 'past 6 months', 'past 12 months'
93
+ # * +:site+ - name of a site
94
+ # * +:visit_kind+ - kind of visit as a string: 'direct', 'natural', 'paid', 'search'
95
+ #
96
+ # ==== Examples
97
+ #
98
+ # TrackableSession.conversion_rate( :site => 'foo.com' )
73
99
  def self.conversion_rate(args)
74
100
  (TrackableSession.search(args).with_conversions.count.to_f / TrackableSession.search(args).count) * 100
75
101
  end
76
-
102
+
103
+ # Returns a histogram for the number of clickthroughs to each referred site(s).
104
+ #
105
+ # ==== Attributes
106
+ #
107
+ # * +:time_period+ - date range as a string: 'past 3 months', 'past 6 months', 'past 12 months'
108
+ # * +:site+ - name of a site
109
+ # * +:visit_kind+ - kind of visit as a string: 'direct', 'natural', 'paid', 'search'
110
+ #
111
+ # ==== Examples
112
+ #
113
+ # TrackableSession.clickthroughs_histogram( :site => 'foo.com' )
77
114
  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}
115
+ conditions = TrackableSession.search(args).selector
116
+ TrackableSession.collection.group(:keyf => "function(x) { return { destination: x.clickthrough_destination }; }", :cond => conditions, :initial => { :count => 0}, :reduce => "function(x,y){y.count++}").inject({}){|h,k| h[k['destination']] = k['count'] unless k['destination'].blank?; h}
80
117
  end
81
118
 
119
+ # Returns a histogram for the first page users visited for the specified site(s).
120
+ #
121
+ # ==== Attributes
122
+ #
123
+ # * +:time_period+ - date range as a string: 'past 3 months', 'past 6 months', 'past 12 months'
124
+ # * +:site+ - name of a site
125
+ # * +:visit_kind+ - kind of visit as a string: 'direct', 'natural', 'paid', 'search'
126
+ #
127
+ # ==== Examples
128
+ #
129
+ # TrackableSession.entrance_pages_histogram( :site => 'foo.com' )
82
130
  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
131
+ conditions = TrackableSession.search(args).selector
132
+ TrackableSession.collection.group(:keyf => "function(x) { return { entrance_page: x.entrance_page }; }", :cond => conditions, :initial => { :count => 0}, :reduce => "function(x,y){y.count++}").inject({}){|h,k| h[k['entrance_page']] = k['count']; h}.sort{|a,b| a[1] <=> b[1]}.reverse
85
133
  end
86
134
 
135
+ # Returns a histogram for the last page users visited for the specified site(s).
136
+ #
137
+ # ==== Attributes
138
+ #
139
+ # * +:time_period+ - date range as a string: 'past 3 months', 'past 6 months', 'past 12 months'
140
+ # * +:site+ - name of a site
141
+ # * +:visit_kind+ - kind of visit as a string: 'direct', 'natural', 'paid', 'search'
142
+ #
143
+ # ==== Examples
144
+ #
145
+ # TrackableSession.exit_pages_histogram( :site => 'foo.com' )
87
146
  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
147
+ conditions = TrackableSession.search(args).selector
148
+ TrackableSession.collection.group(:keyf => "function(x) { return { exit_page: x.exit_page }; }", :cond => conditions, :initial => { :count => 0}, :reduce => "function(x,y){y.count++}").inject({}){|h,k| h[k['exit_page']] = k['count']; h}.sort{|a,b| a[1] <=> b[1]}.reverse
90
149
  end
91
150
 
151
+ # Returns a histogram for the search terms users used to find the specified site(s).
152
+ #
153
+ # ==== Attributes
154
+ #
155
+ # * +:time_period+ - date range as a string: 'past 3 months', 'past 6 months', 'past 12 months'
156
+ # * +:site+ - name of a site
157
+ # * +:visit_kind+ - kind of visit as a string: 'direct', 'natural', 'paid', 'search'
158
+ #
159
+ # ==== Examples
160
+ #
161
+ # TrackableSession.keywords_histogram( :site => 'foo.com' )
92
162
  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}
163
+ conditions = TrackableSession.search(args).selector
164
+ TrackableSession.collection.group(:keyf => "function(x) { return { keyword: x.referring_keywords }; }", :cond => conditions, :initial => { :count => 0}, :reduce => "function(x,y){y.count++}").inject({}){|h,k| h[k['keyword']] = k['count'] unless k['keyword'].blank?; h}
95
165
  end
96
-
166
+
97
167
  def self.kinds_for_select
98
168
  [['All', 'all']] | TrackableSession::KINDS.sort.map{|k| [k.titleize, k]}
99
169
  end
100
-
170
+
171
+ # Returns a histogram for the geocoded locations of visitors to the specified site(s).
172
+ #
173
+ # ==== Attributes
174
+ #
175
+ # * +:time_period+ - date range as a string: 'past 3 months', 'past 6 months', 'past 12 months'
176
+ # * +:site+ - name of a site
177
+ # * +:visit_kind+ - kind of visit as a string: 'direct', 'natural', 'paid', 'search'
178
+ #
179
+ # ==== Examples
180
+ #
181
+ # TrackableSession.locations_histogram( :site => 'foo.com' )
101
182
  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}
183
+ conditions = TrackableSession.search(args).selector
184
+ TrackableSession.collection.group(:keyf => "function(x) { return { location: x.location }; }", :cond => conditions, :initial => { :count => 0}, :reduce => "function(x,y){y.count++}").inject({}){|h,k| h[k['location']] = k['count'].to_i unless k['location'].blank? || k['location'] == 'Unknown'; h}
104
185
  end
105
-
186
+
187
+ # Returns a human-readable user agent for the browser.
106
188
  def self.parsed_user_agent(user_agent)
107
189
  begin
108
190
  _ua = Agent.new(user_agent)
@@ -112,89 +194,73 @@ class TrackableSession
112
194
  end
113
195
  _user_agent
114
196
  end
115
-
197
+
198
+ # Returns a histogram for the referrers for site(s) matching the specified arguments.
199
+ #
200
+ # ==== Attributes
201
+ #
202
+ # * +:time_period+ - date range as a string: 'past 3 months', 'past 6 months', 'past 12 months'
203
+ # * +:site+ - name of a site
204
+ # * +:visit_kind+ - kind of visit as a string: 'direct', 'natural', 'paid', 'search'
205
+ #
206
+ # ==== Examples
207
+ #
208
+ # TrackableSession.referrers_histogram( :site => 'foo.com' )
116
209
  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}
210
+ conditions = TrackableSession.search(args).selector
211
+ TrackableSession.collection.group(:keyf => "function(x) { return { referrer: x.referrer }; }", :cond => conditions, :initial => { :count => 0}, :reduce => "function(x,y){y.count++}").inject({}){|h,k| h[k['referrer']] = k['count'] unless k['referrer'].blank?; h}
119
212
  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
213
+
214
+ # Returns a scope based on time period, site, and visit kind. Null arguments returns all.
215
+ #
216
+ # ==== Attributes
217
+ #
218
+ # * +:time_period+ - date range as a string: 'past 3 months', 'past 6 months', 'past 12 months'
219
+ # * +:site+ - name of a site
220
+ # * +:visit_kind+ - kind of visit as a string: 'direct', 'natural', 'paid', 'search'
221
+ #
222
+ # ==== Examples
223
+ #
224
+ # TrackableSession.search( :site => 'foo.com' )
225
+ def self.search(args)
226
+ (args[:start_date], args[:end_date]) = dates_from_time_period(args[:time_period])
227
+ sessions = args[:time_period] ? TrackableSession.for_date_range(args[:start_date], args[:end_date]) : TrackableSession.all
228
+ sessions = sessions.send("with_#{args[:action_kind].pluralize.downcase}") if args[:action_kind]
229
+ sessions = sessions.for_site(args[:site].downcase) unless args[:site].downcase == 'all sites'
230
+ sessions = sessions.of_kind(args[:visit_kind].downcase) if args[:visit_kind] && args[:visit_kind].downcase != 'all'
231
+ sessions.desc(:created_at)
170
232
  end
171
-
172
- # Can't combine date conditions, so this is needed for SessionReport
233
+
234
+ # Returns a scope based on site and visit kind only.
235
+ #
236
+ # ==== Attributes
237
+ #
238
+ # * +:site+ - name of a site
239
+ # * +:visit_kind+ - kind of visit as a string: 'direct', 'natural', 'paid', 'search'
240
+ #
241
+ # ==== Examples
242
+ #
243
+ # TrackableSession.search_without_date( :site => 'foo.com' )
173
244
  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
245
+ args = args.clone
246
+ args.delete(:time_period)
247
+ self.search(args)
193
248
  end
194
249
 
250
+ # Returns a histogram for the user agents for site(s) matching the specified arguments.
251
+ #
252
+ # ==== Attributes
253
+ #
254
+ # * +:time_period+ - date range as a string: 'past 3 months', 'past 6 months', 'past 12 months'
255
+ # * +:site+ - name of a site
256
+ # * +:visit_kind+ - kind of visit as a string: 'direct', 'natural', 'paid', 'search'
257
+ #
258
+ # ==== Examples
259
+ #
260
+ # TrackableSession.user_agents_histogram( :site => 'foo.com' )
195
261
  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)
262
+ conditions = TrackableSession.search(args).selector
263
+ _histogram = TrackableSession.collection.group(:keyf => "function(x) { return { user_agent: x.user_agent }; }", :cond => conditions, :initial => { :count => 0}, :reduce => "function(x,y){y.count++}")
198
264
  _final = {}
199
265
  _histogram.each do |ua|
200
266
  _ua = parsed_user_agent(ua['user_agent'])
@@ -203,22 +269,26 @@ class TrackableSession
203
269
  end
204
270
  _final
205
271
  end
206
-
272
+
207
273
  # Instance Methods ===============================================================================
208
274
 
275
+ # Returns the number of non-view actions for this session.
209
276
  def actions
210
277
  self.trackable_actions.count - self.trackable_actions.views.count
211
278
  end
212
-
279
+
280
+ # Returns true if this is a new visit.
213
281
  def detect_new_visit
214
- TrackableSession.find_by_ip_address(self.ip_address) ? false : true
282
+ TrackableSession.where(:ip_address => self.ip_address) ? false : true
215
283
  end
216
-
284
+
285
+ # Returns this session's duration in X minutes or Y seconds.
217
286
  def hr_duration
218
287
  return 0 unless self.duration
219
288
  self.duration > 60 ? sprintf("%.1f minutes", self.duration / 60.0) : sprintf("%.1f seconds", self.duration)
220
289
  end
221
-
290
+
291
+ # Initializes a new session.
222
292
  def init
223
293
  self.bounce = true
224
294
  self.kind ||= self.kind_by_referrer
@@ -226,13 +296,15 @@ class TrackableSession
226
296
  self.referring_keywords = self.sanitize_referring_keywords
227
297
  self.spider ||= spider_visit?
228
298
  begin
299
+ raise ArgumentError, "No IP address available" unless self.ip_address
229
300
  _location = Geokit::Geocoders::MultiGeocoder.geocode(self.ip_address)
230
301
  self.location ||= _location.success? ? "#{_location.city}, #{_location.state}" : "Unknown"
231
302
  rescue
232
303
  self.location = "Unknown"
233
304
  end
234
305
  end
235
-
306
+
307
+ # Returns whether this session was initiated by a direct (bookmark or typed URL), search, paid (landing page), or natural (non-search link) referrer.
236
308
  def kind_by_referrer
237
309
  return "direct" unless self.referrer
238
310
  unless self.referrer.blank?
@@ -241,42 +313,55 @@ class TrackableSession
241
313
  end
242
314
  "natural"
243
315
  end
244
-
316
+
317
+ # Returns the number of page views associated with this session.
245
318
  def pageviews
246
319
  self.trackable_actions.views.count
247
320
  end
248
321
 
322
+ # Returns this session's human-readable user agent.
249
323
  def parsed_user_agent
250
324
  TrackableSession.parsed_user_agent(self.user_agent)
251
325
  end
252
-
326
+
327
+ # Returns the date of the last visit if this user visited before.
253
328
  def last_visit_date
254
329
  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]
330
+ TrackableSession.by_ip_address(self.ip_address).asc(:created_at).only(:created_at).map{|s| s.created_at}.sort[-2]
256
331
  end
257
-
332
+
333
+ # Returns the number of times this user has been here before.
258
334
  def previous_visits_count
259
335
  TrackableSession.by_ip_address(self.ip_address).count - 1
260
336
  end
261
-
337
+
338
+ # Unescapes referring keywords for display.
262
339
  def sanitize_referring_keywords
263
340
  self.referring_keywords.to_s.gsub('+',' ')
264
341
  end
265
-
266
- # Update the record field based on continued session activity
342
+
343
+ # Updates this session with whatever the user just did.
344
+ #
345
+ # ==== Attributes
346
+ #
347
+ # * +:action_kind+ - action as a string: 'click', 'clickthrough', 'conversion', 'mouseover', 'scroll', 'view'
348
+ # * +:destination+ - clickthrough destination
349
+ # * +:last_url+ - the referring URL
350
+ #
351
+ # ==== Examples
352
+ #
353
+ # trackable_session.touch( 'click', 'cnn.com', 'foo.com/cnn' )
267
354
  def touch(action_kind = nil, destination = nil, last_url = nil)
268
355
  if action_kind && action_kind == 'scroll'
269
356
  self.update_attributes(
270
357
  :has_scrolls => true,
271
358
  :bounce => self.trackable_actions.count <= 1,
272
- :updated_at => Time.zone.now,
273
359
  :duration => Time.zone.now - self.created_at
274
360
  )
275
361
  elsif action_kind && action_kind == 'click'
276
362
  self.update_attributes(
277
363
  :has_clicks => true,
278
364
  :bounce => self.trackable_actions.count <= 1,
279
- :updated_at => Time.zone.now,
280
365
  :duration => Time.zone.now - self.created_at
281
366
  )
282
367
  elsif action_kind && action_kind == 'clickthrough'
@@ -284,21 +369,18 @@ class TrackableSession
284
369
  :has_clickthroughs => true,
285
370
  :clickthrough_destination => destination,
286
371
  :bounce => false,
287
- :updated_at => Time.zone.now,
288
372
  :duration => Time.zone.now - self.created_at
289
373
  )
290
374
  elsif action_kind && action_kind == 'conversion'
291
375
  self.update_attributes(
292
376
  :has_conversions => true,
293
377
  :bounce => self.trackable_actions.count <= 1,
294
- :updated_at => Time.zone.now,
295
378
  :duration => Time.zone.now - self.created_at
296
379
  )
297
380
  elsif action_kind && action_kind == 'mouseover'
298
381
  self.update_attributes(
299
382
  :has_mouseovers => true,
300
383
  :bounce => self.trackable_actions.count <= 1,
301
- :updated_at => Time.zone.now,
302
384
  :duration => Time.zone.now - self.created_at
303
385
  )
304
386
  else
@@ -307,11 +389,13 @@ class TrackableSession
307
389
  end
308
390
  touch_stat
309
391
  end
310
-
392
+
393
+ # Returns whether this session is new or existing.
311
394
  def visit_type
312
395
  self.new_visit? ? "New" : "Returning"
313
396
  end
314
397
 
398
+ # Upserts stats associated with this session. Called every time this session is touched.
315
399
  def touch_stat
316
400
  # Upsert associated stat
317
401
  if trackable_stat = TrackableStat.by_site(self.site).by_date(self.created_at).first
@@ -321,9 +405,9 @@ class TrackableSession
321
405
  trackable_stat.touch(self, self.trackable_actions.last)
322
406
  end
323
407
  end
324
-
408
+
325
409
  private
326
-
410
+
327
411
  def ppc_visit?(url)
328
412
  begin
329
413
  _params = URI.parse(url).query.to_s.downcase
@@ -339,7 +423,7 @@ class TrackableSession
339
423
  return true if _params.include?('cpc') # Generic
340
424
  return false
341
425
  end
342
-
426
+
343
427
  def spider_visit?
344
428
  _spider = true
345
429
  return false if self.user_agent == "Unknown" || self.user_agent.nil?
@@ -348,5 +432,5 @@ class TrackableSession
348
432
  end
349
433
  _spider
350
434
  end
351
-
435
+
352
436
  end