gricer 0.0.1

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 (88) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +84 -0
  3. data/Rakefile +49 -0
  4. data/app/assets/images/gricer/fluid/breadcrumb.png +0 -0
  5. data/app/assets/javascripts/gricer.js.coffee +85 -0
  6. data/app/assets/javascripts/gricer_backend_jquery.js.coffee +352 -0
  7. data/app/assets/javascripts/jquery.flot.js +2599 -0
  8. data/app/assets/javascripts/jquery.flot.pie.js +750 -0
  9. data/app/assets/javascripts/jquery.flot.resize.js +60 -0
  10. data/app/assets/javascripts/jquery.flot.symbol.js +70 -0
  11. data/app/assets/javascripts/worldmap.js +146 -0
  12. data/app/assets/stylesheets/gricer/fluid-jquery-ui.css.scss +1298 -0
  13. data/app/assets/stylesheets/gricer/fluid.css.scss +240 -0
  14. data/app/assets/stylesheets/gricer/helpers/css3.css.scss +21 -0
  15. data/app/controllers/gricer/base_controller.rb +141 -0
  16. data/app/controllers/gricer/capture_controller.rb +42 -0
  17. data/app/controllers/gricer/dashboard_controller.rb +18 -0
  18. data/app/controllers/gricer/requests_controller.rb +42 -0
  19. data/app/controllers/gricer/sessions_controller.rb +24 -0
  20. data/app/helpers/gricer/base_helper.rb +22 -0
  21. data/app/models/gricer/agent.rb +789 -0
  22. data/app/models/gricer/request.rb +239 -0
  23. data/app/models/gricer/session.rb +433 -0
  24. data/app/views/gricer/capture/index.html.erb +1 -0
  25. data/app/views/gricer/dashboard/_menu.html.erb +10 -0
  26. data/app/views/gricer/dashboard/_overview.html.erb +33 -0
  27. data/app/views/gricer/dashboard/index.html.erb +19 -0
  28. data/config/routes.rb +51 -0
  29. data/lib/gricer.rb +36 -0
  30. data/lib/gricer/action_controller/base.rb +28 -0
  31. data/lib/gricer/action_controller/track.rb +132 -0
  32. data/lib/gricer/active_model/statistics.rb +167 -0
  33. data/lib/gricer/config.rb +125 -0
  34. data/lib/gricer/engine.rb +9 -0
  35. data/lib/gricer/localization.rb +3 -0
  36. data/lib/tasks/gricer_tasks.rake +92 -0
  37. data/spec/controllers/gricer/base_controller_spec.rb +207 -0
  38. data/spec/controllers/gricer/capture_controller_spec.rb +44 -0
  39. data/spec/controllers/gricer/dashboard_controller_spec.rb +44 -0
  40. data/spec/controllers/gricer/requests_controller_spec.rb +36 -0
  41. data/spec/controllers/gricer/sessions_controller_spec.rb +37 -0
  42. data/spec/dummy/Rakefile +7 -0
  43. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  44. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  45. data/spec/dummy/app/assets/stylesheets/dashboard.css +4 -0
  46. data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
  47. data/spec/dummy/app/assets/stylesheets/sessions.css +4 -0
  48. data/spec/dummy/app/controllers/application_controller.rb +23 -0
  49. data/spec/dummy/app/controllers/dashboard_controller.rb +19 -0
  50. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  51. data/spec/dummy/app/helpers/dashboard_helper.rb +2 -0
  52. data/spec/dummy/app/views/dashboard/index.html.erb +236 -0
  53. data/spec/dummy/app/views/layouts/application.html.erb +16 -0
  54. data/spec/dummy/config.ru +4 -0
  55. data/spec/dummy/config/application.rb +48 -0
  56. data/spec/dummy/config/boot.rb +10 -0
  57. data/spec/dummy/config/cucumber.yml +9 -0
  58. data/spec/dummy/config/database.yml +25 -0
  59. data/spec/dummy/config/environment.rb +5 -0
  60. data/spec/dummy/config/environments/development.rb +27 -0
  61. data/spec/dummy/config/environments/production.rb +51 -0
  62. data/spec/dummy/config/environments/test.rb +39 -0
  63. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  64. data/spec/dummy/config/initializers/inflections.rb +10 -0
  65. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  66. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  67. data/spec/dummy/config/initializers/session_store.rb +8 -0
  68. data/spec/dummy/config/initializers/wrap_parameters.rb +12 -0
  69. data/spec/dummy/config/locales/en.yml +5 -0
  70. data/spec/dummy/config/routes.rb +11 -0
  71. data/spec/dummy/db/schema.rb +241 -0
  72. data/spec/dummy/log/development.log +0 -0
  73. data/spec/dummy/public/404.html +26 -0
  74. data/spec/dummy/public/422.html +26 -0
  75. data/spec/dummy/public/500.html +26 -0
  76. data/spec/dummy/public/favicon.ico +0 -0
  77. data/spec/dummy/script/rails +6 -0
  78. data/spec/helpers/gricer/base_helper_spec.rb +28 -0
  79. data/spec/lib/gricer/action_controller/track_spec.rb +63 -0
  80. data/spec/models/gricer/agent_spec.rb +829 -0
  81. data/spec/models/gricer/request_spec.rb +145 -0
  82. data/spec/models/gricer/session_spec.rb +209 -0
  83. data/spec/routing/capture_routes_spec.rb +6 -0
  84. data/spec/routing/dashboard_routes_spec.rb +9 -0
  85. data/spec/routing/requests_routes_spec.rb +90 -0
  86. data/spec/routing/sessions_routes_spec.rb +115 -0
  87. data/spec/spec_helper.rb +23 -0
  88. metadata +185 -0
@@ -0,0 +1,239 @@
1
+ module Gricer
2
+ # ActiveRecord Model for Request Statistics
3
+ # @attr [Gricer::Session] session
4
+ # The current value of the associated session.
5
+ #
6
+ # @attr [Gricer::Agent] agent
7
+ # The current value of the associated agent.
8
+ #
9
+ # @attr [String] host
10
+ # The current value of the host requested.
11
+ #
12
+ # @attr [String] path
13
+ # The current value of the path requested.
14
+ #
15
+ # @attr [String] method
16
+ # The current value of the method requested.
17
+ #
18
+ # @attr [String] protocol
19
+ # The current value of the protocol requested.
20
+ #
21
+ # @attr [String] controller
22
+ # The current value of the controller requested.
23
+ #
24
+ # @attr [String] action
25
+ # The current value of the action requested.
26
+ #
27
+ # @attr [String] format
28
+ # The current value of the format requested.
29
+ #
30
+ # @attr [String] param_id
31
+ # The current value of the http GET/POST id parameter requested.
32
+ #
33
+ # @attr [Integer] user_id
34
+ # The current id of the user logged in.
35
+ #
36
+ # @see Gricer::ActionController::Base#gricer_user_id
37
+ #
38
+ # @attr [Integer] status_code
39
+ # The current value of the HTTP status returned.
40
+ #
41
+ # @attr [String] content_type
42
+ # The current value of the content type returned.
43
+ #
44
+ # @attr [String] body_size
45
+ # The current size of the body returned.
46
+ #
47
+ # @attr [String] system_time
48
+ # The current value of the system time elapsed processing this request.
49
+ #
50
+ # @attr [String] user_time
51
+ # The current value of the user time elapsed processing this request.
52
+ #
53
+ # @attr [String] total_time
54
+ # The current value of the total time elapsed processing this request.
55
+ #
56
+ # @attr [String] real_time
57
+ # The current value of the real time elapsed processing this request.
58
+ #
59
+ # @attr [Boolean] javascript
60
+ # The current value of the javascript capability of the requesting agent.
61
+ #
62
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
63
+ #
64
+ # @attr [Integer] window_width
65
+ # The current value of the width in pixels of the requesting agent's window.
66
+ #
67
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
68
+ #
69
+ # @attr [Integer] window_height
70
+ # The current value of the height in pixels of the requesting agent's window.
71
+ #
72
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
73
+ #
74
+ # @attr [String] referer_protocol
75
+ # The current value of the protocol of the referering page.
76
+ #
77
+ # @attr [String] referer_host
78
+ # The current value of the host of the referering page.
79
+ #
80
+ # @attr [String] referer_path
81
+ # The current value of the path of the referering page.
82
+ #
83
+ # @attr [String] referer_params
84
+ # The current value of the params of the referering page.
85
+ #
86
+ # @attr [String] search_engine
87
+ # The current value of the search engine name refering to get to this request.
88
+ #
89
+ # @attr [String] search_query
90
+ # The current value of the search query refering to get to this request.
91
+ #
92
+ # @attr [Boolean] is_first_in_session
93
+ # The current value of the first in session flag.
94
+ # This is true if it is the first request within a Gricer::Session.
95
+ #
96
+ # @attr [String] locale_major
97
+ # The current value of the locale responded (major locale only)
98
+ #
99
+ # @attr [String] locale_minor
100
+ # The current value of the locale responded (major locale only)
101
+ #
102
+ # @attr [String] locale
103
+ # The current value of the locale responded
104
+ #
105
+ class Request < ::ActiveRecord::Base
106
+ set_table_name "#{::Gricer::config.table_name_prefix}requests"
107
+ include ActiveModel::Statistics
108
+
109
+ belongs_to :session, class_name: 'Gricer::Session', counter_cache: true
110
+ belongs_to :agent, class_name: 'Gricer::Agent', counter_cache: true
111
+
112
+ before_create :init_session
113
+
114
+ # Filter out anything that is not a Browser or MobileBrowser
115
+ # @return [ActiveRecord::Relation]
116
+ def self.browsers
117
+ includes("agent")
118
+ .where("\"#{Agent.table_name}\".\"agent_class_id\" IN (?)", [0x1000, 0x2000])
119
+ end
120
+
121
+ # Filter out anything that has not the first_in_session flag
122
+ # @return [ActiveRecord::Relation]
123
+ def self.only_first_in_session
124
+ where('is_first_in_session = ?', true)
125
+ end
126
+
127
+ # Store the ip address from which the request was sent for init_session
128
+ # @see #init_session
129
+ def ip_address=(ip)
130
+ @ip_address = ip
131
+ end
132
+
133
+ # Find or Create Gricer::Agent corrosponding to the given user agent string as given in the HTTP header
134
+ #
135
+ # @param agent_header [String] A user agent string as in a HTTP header
136
+ # @return [Gricer::Agent]
137
+ def agent_header=(agent_header)
138
+ self.agent = Agent.find_or_create_by_request_header agent_header
139
+ end
140
+
141
+ # Parse the Ruby on Rails request to fill attributes
142
+ def request=(request)
143
+ self.ip_address = request.remote_ip
144
+ self.agent_header = request.headers['HTTP_USER_AGENT']
145
+ self.referer = request.headers['HTTP_X_FORWARDED_REFERER'] || request.headers['HTTP_REFERER']
146
+
147
+ self.host = request.host
148
+ self.path = request.path
149
+ self.method = request.request_method
150
+ self.protocol = request.protocol.sub(/[:\/]*$/, '').upcase
151
+ self.locale = I18n.locale
152
+
153
+ @request_locale = request.headers['HTTP_ACCEPT_LANGUAGE'].try(:split, ',').try(:first)
154
+
155
+ logger.debug I18n.locale
156
+ end
157
+
158
+ def locale=(locale)
159
+ self.locale_major, self.locale_minor = locale.try(:to_s).try(:downcase).try(:split, '-')
160
+ end
161
+
162
+ def locale
163
+ if locale_minor
164
+ "#{locale_major}-#{locale_minor}"
165
+ else
166
+ locale_major
167
+ end
168
+ end
169
+
170
+ # Parse the referer to fill referer and search engine attributes
171
+ #
172
+ # @param referer [String] The Referer as given in HTTP request
173
+ def referer=(referer)
174
+ if referer
175
+ void, self.referer_protocol, self.referer_host, self.referer_path, self.referer_params = referer.match(/([A-Za-z0-9]*):\/\/([^\/]*)([^\?]*)[\?]?(.*)/).to_a
176
+
177
+ # Sanatize/Normalize referer values
178
+ self.referer_protocol = referer_protocol.try(:upcase)
179
+ self.referer_host = referer_host.try(:downcase)
180
+ self.referer_path = '/' if referer_path.blank?
181
+ self.referer_params = nil if referer_params.blank?
182
+ params = CGI::parse(referer_params) unless referer_params.blank?
183
+
184
+ # Detect Search Engines
185
+ if referer_host =~ /^www\.google(?:\.com?)?(?:\.[a-z]{2})?$/ and ['/search', '/url'].include? referer_path
186
+ self.search_engine = 'Google'
187
+ self.search_query = params['q'].try(:first)
188
+ elsif referer_host == 'www.bing.com' and referer_path == '/search'
189
+ self.search_engine = 'Bing'
190
+ self.search_query = params['q'].try(:first)
191
+ elsif referer_host =~ /search\.yahoo\.com$/ and referer_path =~ /\/search;/
192
+ self.search_engine = 'Yahoo'
193
+ self.search_query = params['p'].try(:first)
194
+ elsif referer_host == 'www.baidu.com' and referer_path == '/s'
195
+ self.search_engine = 'Baidu'
196
+ self.search_query = params['wd'].try(:first)
197
+ elsif referer_host =~ /ask\.com$/ and referer_path =~ /^\/web/
198
+ self.search_engine = 'Ask'
199
+ self.search_query = params['q'].try(:first)
200
+ elsif referer_host == 'search.aol.com' and referer_path == '/aol/search'
201
+ self.search_engine = 'AOL'
202
+ self.search_query = params['q'].try(:first)
203
+ elsif referer_host =~ /metacrawler\.com$/ and referer_path =~ /search\/web/
204
+ self.search_engine = 'MetaCrawler'
205
+ self.search_query = params['q'].try(:first)
206
+ elsif referer_host =~ /dogpile\.com/ and referer_path =~ /dogpile\/ws\/results\/Web\//
207
+ self.search_engine = 'Dogpile'
208
+ void, self.search_query = referer_path.match(/ws\/results\/Web\/([^\/]*)\//).to_a
209
+ self.search_query = CGI::unescape(self.search_query).gsub('!FE', '.')
210
+ end
211
+ end
212
+ end
213
+
214
+ # Parse the params to fill param_id and format attributes
215
+ def params=(params)
216
+ self.param_id = params[:id]
217
+ self.format = params[:format]
218
+ end
219
+
220
+ # Init the corrosponding Gricer::Session (called before create)
221
+ #
222
+ # @return [Gricer::Session]
223
+ def init_session
224
+ if session
225
+ if session.updated_at < Time.now - ::Gricer.config.max_session_duration
226
+ self.session = Session.create previous_session: session, ip_address: @ip_address, agent: agent, requested_locale: @request_locale
227
+ else
228
+ self.session.touch
229
+ end
230
+ else
231
+ self.is_first_in_session = true
232
+ self.session = Session.create ip_address: @ip_address, agent: agent, requested_locale: @request_locale
233
+ self.session.touch
234
+ end
235
+
236
+ session
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,433 @@
1
+ module Gricer
2
+ # ActiveRecord Model for Session Statistics
3
+ # @attr [Gricer::Session] previous_session
4
+ # The current value of the associated previous session.
5
+ #
6
+ # @attr [Gricer::Agent] agent
7
+ # The current value of the associated agent.
8
+ #
9
+ # @attr [String] ip_address_hash
10
+ # The current value of the ip address in an anonyminized hash
11
+ #
12
+ # @attr [String] domain
13
+ # The current value of the domain (only major info) from which the session was requested.
14
+ #
15
+ # @attr [String] country
16
+ # The current value of the country from which the session was requested.
17
+ #
18
+ # You need to configure a GeoIP in the {Gricer::Config} instance.
19
+ #
20
+ # @attr [String] region
21
+ # The current value of the region from which the session was requested.
22
+ #
23
+ # You need to configure a GeoIP in the {Gricer::Config} instance.
24
+ #
25
+ # @attr [String] city
26
+ # The current value of the city from which the session was requested.
27
+ #
28
+ # You need to configure a GeoIP in the {Gricer::Config} instance.
29
+ #
30
+ # @attr [String] postal_code
31
+ # The current value of the region from which the session was requested.
32
+ #
33
+ # You need to configure a GeoIP in the {Gricer::Config} instance.
34
+ #
35
+ # @attr [Float] longitude
36
+ # The current value of the longitude of the city/region from which the session was requested.
37
+ #
38
+ # You need to configure a GeoIP in the {Gricer::Config} instance.
39
+ #
40
+ # @attr [Float] latitude
41
+ # The current value of the latitude of the city/region from which the session was requested.
42
+ #
43
+ # You need to configure a GeoIP in the {Gricer::Config} instance.
44
+ #
45
+ # @attr [Boolean] javascript
46
+ # The current value of the javascript capability of the requesting agent.
47
+ #
48
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
49
+ #
50
+ # @attr [Boolean] java
51
+ # The current value of the java capability of the requesting agent.
52
+ #
53
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
54
+ #
55
+ # @attr [String] flash_version
56
+ # The current value of the version of flash installed on the requesting agent.
57
+ #
58
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
59
+ #
60
+ # @attr [String] flash_major_version
61
+ # The current value of the major version of flash installed on the requesting agent.
62
+ #
63
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
64
+ #
65
+ # @attr [String] silverlight_version
66
+ # The current value of the version of silverlight installed on the requesting agent.
67
+ #
68
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
69
+ #
70
+ # @attr [String] silverlight_major_version
71
+ # The current value of the major version of silverlight installed on the requesting agent.
72
+ #
73
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
74
+ #
75
+ # @attr [Integer] screen_width
76
+ # The current value of the width in pixels of the screen the requesting agent's window is on.
77
+ #
78
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
79
+ #
80
+ # @attr [Integer] screen_height
81
+ # The current value of the height in pixels of the screen the requesting agent's window is on.
82
+ #
83
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
84
+ #
85
+ # @attr [Integer] screen_depth
86
+ # The current value of the depth in bits of the screen the requesting agent's window is on.
87
+ #
88
+ # This feature needs the usage of {Gricer::TrackHelper#gricer_track_tag}.
89
+ #
90
+ # @attr [String] requested_locale_major
91
+ # The current value of the locale requested (major locale only)
92
+ #
93
+ # @attr [String] requested_locale_minor
94
+ # The current value of the locale requested (major locale only)
95
+ #
96
+ # @attr [String] requested_locale
97
+ # The current value of the locale requested
98
+ #
99
+ class Session < ::ActiveRecord::Base
100
+ set_table_name "#{::Gricer::config.table_name_prefix}sessions"
101
+ include ActiveModel::Statistics
102
+
103
+ has_many :requests, class_name: 'Gricer::Request', foreign_key: :session_id, order: 'created_at ASC'
104
+ belongs_to :agent, class_name: 'Gricer::Agent', foreign_key: :agent_id, counter_cache: true
105
+ belongs_to :previous_session, class_name: 'Gricer::Session', foreign_key: :previous_session_id
106
+
107
+ # Filter out anything that is not a Browser or MobileBrowser
108
+ # @return [ActiveRecord::Relation]
109
+ def self.browsers
110
+ self.includes("agent")
111
+ .where("\"#{Agent.table_name}\".\"agent_class_id\" IN (?)", [0x1000, 0x2000])
112
+ end
113
+
114
+ # Filter out only bounce sessions (sessions with just one request)
115
+ # @return [ActiveRecord::Relation]
116
+ def self.bounce_sessions
117
+ self.where("\"#{self.table_name}\".\"requests_count\" = ?", 1)
118
+ end
119
+
120
+ # Filter out only new visits (which does not have a previous_session)
121
+ # @return [ActiveRecord::Relation]
122
+ def self.new_visits
123
+ where("\"#{self.table_name}\".\"previous_session_id\" IS NULL")
124
+ end
125
+
126
+ # This is just a list of two-part domain endings.
127
+ # It is used to get the domain from the request.
128
+ # return [Array]
129
+ def two_part_domain_endings
130
+ [
131
+ 'com.ac','edu.ac','gov.ac','net.ac','mil.ac',
132
+ 'org.ac','com.ae','net.ae','org.ae','gov.ae','ac.ae',
133
+ 'co.ae','sch.ae','pro.ae','com.ai','org.ai','edu.ai',
134
+ 'gov.ai','com.ar','net.ar','org.ar','gov.ar','mil.ar',
135
+ 'edu.ar','int.ar','co.at','ac.at','or.at','gv.at','priv.at',
136
+ 'com.au','gov.au','org.au','edu.au','id.au','oz.au',
137
+ 'info.au','net.au','asn.au','csiro.au','telememo.au',
138
+ 'conf.au','otc.au','com.az','net.az','org.az','com.bb',
139
+ 'net.bb','org.bb','ac.be','belgie.be','dns.be','fgov.be',
140
+ 'com.bh','gov.bh','net.bh','edu.bh','org.bh','com.bm',
141
+ 'edu.bm','gov.bm','org.bm','net.bm','adm.br','adv.br',
142
+ 'agr.br','am.br','arq.br','art.br','ato.br','bio.br',
143
+ 'bmd.br','cim.br','cng.br','cnt.br','com.br','coop.br',
144
+ 'ecn.br','edu.br','eng.br','esp.br','etc.br','eti.br',
145
+ 'far.br','fm.br','fnd.br','fot.br','fst.br','g12.br',
146
+ 'ggf.br','gov.br','imb.br','ind.br','inf.br','jor.br',
147
+ 'lel.br','mat.br','med.br','mil.br','mus.br','net.br',
148
+ 'nom.br','not.br','ntr.br','odo.br','org.br','ppg.br',
149
+ 'pro.br','psc.br','psi.br','qsl.br','rec.br','slg.br',
150
+ 'srv.br','tmp.br','trd.br','tur.br','tv.br','vet.br',
151
+ 'zlg.br','com.bs','net.bs','org.bs','ab.ca','bc.ca',
152
+ 'mb.ca','nb.ca','nf.ca','nl.ca','ns.ca','nt.ca','nu.ca',
153
+ 'on.ca','pe.ca','qc.ca','sk.ca','yk.ca','gc.ca','co.ck',
154
+ 'net.ck','org.ck','edu.ck','gov.ck','com.cn','edu.cn',
155
+ 'gov.cn','net.cn','org.cn','ac.cn','ah.cn','bj.cn','cq.cn',
156
+ 'gd.cn','gs.cn','gx.cn','gz.cn','hb.cn','he.cn','hi.cn',
157
+ 'hk.cn','hl.cn','hn.cn','jl.cn','js.cn','ln.cn','mo.cn',
158
+ 'nm.cn','nx.cn','qh.cn','sc.cn','sn.cn','sh.cn','sx.cn',
159
+ 'tj.cn','tw.cn','xj.cn','xz.cn','yn.cn','zj.cn','arts.co',
160
+ 'com.co','edu.co','firm.co','gov.co','info.co','int.co',
161
+ 'nom.co','mil.co','org.co','rec.co','store.co','web.co',
162
+ 'ac.cr','co.cr','ed.cr','fi.cr','go.cr','or.cr','sa.cr',
163
+ 'com.cu','net.cu','org.cu','ac.cy','com.cy','gov.cy',
164
+ 'net.cy','org.cy','co.dk','art.do','com.do','edu.do',
165
+ 'gov.do','gob.do','org.do','mil.do','net.do','sld.do',
166
+ 'web.do','com.dz','org.dz','net.dz','gov.dz','edu.dz',
167
+ 'ass.dz','pol.dz','art.dz','com.ec','k12.ec','edu.ec',
168
+ 'fin.ec','med.ec','gov.ec','mil.ec','org.ec','net.ec',
169
+ 'com.ee','pri.ee','fie.ee','org.ee','med.ee','com.eg',
170
+ 'edu.eg','eun.eg','gov.eg','net.eg','org.eg','sci.eg',
171
+ 'com.er','net.er','org.er','edu.er','mil.er','gov.er',
172
+ 'ind.er','com.es','org.es','gob.es','edu.es','nom.es',
173
+ 'com.et','gov.et','org.et','edu.et','net.et','biz.et',
174
+ 'name.et','info.et','ac.fj','com.fj','gov.fj','id.fj',
175
+ 'org.fj','school.fj','com.fk','ac.fk','gov.fk','net.fk',
176
+ 'nom.fk','org.fk','asso.fr','nom.fr','barreau.fr',
177
+ 'com.fr','prd.fr','presse.fr','tm.fr','aeroport.fr',
178
+ 'assedic.fr','avocat.fr','avoues.fr','cci.fr',
179
+ 'chambagri.fr','chirurgiens-dentistes.fr',
180
+ 'experts-comptables.fr','geometre-expert.fr',
181
+ 'gouv.fr','greta.fr','huissier-justice.fr','medecin.fr',
182
+ 'notaires.fr','pharmacien.fr','port.fr','veterinaire.fr',
183
+ 'com.ge','edu.ge','gov.ge','mil.ge','net.ge','org.ge',
184
+ 'pvt.ge','co.gg','org.gg','sch.gg','ac.gg','gov.gg',
185
+ 'ltd.gg','ind.gg','net.gg','alderney.gg','guernsey.gg',
186
+ 'sark.gg','com.gt','edu.gt','net.gt','gob.gt','org.gt',
187
+ 'mil.gt','ind.gt','com.gu','edu.gu','net.gu','org.gu',
188
+ 'gov.gu','mil.gu','com.hk','net.hk','org.hk','idv.hk',
189
+ 'gov.hk','edu.hk','co.hu','2000.hu','erotika.hu',
190
+ 'jogasz.hu','sex.hu','video.hu','info.hu','agrar.hu',
191
+ 'film.hu','konyvelo.hu','shop.hu','org.hu','bolt.hu',
192
+ 'forum.hu','lakas.hu','suli.hu','priv.hu','casino.hu',
193
+ 'games.hu','media.hu','szex.hu','sport.hu','city.hu',
194
+ 'hotel.hu','news.hu','tozsde.hu','tm.hu','erotica.hu',
195
+ 'ingatlan.hu','reklam.hu','utazas.hu','ac.id','co.id',
196
+ 'go.id','mil.id','net.id','or.id','co.il','net.il',
197
+ 'org.il','ac.il','gov.il','k12.il','muni.il','idf.il',
198
+ 'co.im','net.im','org.im','ac.im','lkd.co.im','gov.im',
199
+ 'nic.im','plc.co.im','co.in','net.in','ac.in','ernet.in',
200
+ 'gov.in','nic.in','res.in','gen.in','firm.in','mil.in',
201
+ 'org.in','ind.in','ac.je','co.je','net.je','org.je',
202
+ 'gov.je','ind.je','jersey.je','ltd.je','sch.je','com.jo',
203
+ 'org.jo','net.jo','gov.jo','edu.jo','mil.jo','ad.jp',
204
+ 'ac.jp','co.jp','go.jp','or.jp','ne.jp','gr.jp','ed.jp',
205
+ 'lg.jp','net.jp','org.jp','gov.jp','hokkaido.jp',
206
+ 'aomori.jp','iwate.jp','miyagi.jp','akita.jp',
207
+ 'yamagata.jp','fukushima.jp','ibaraki.jp','tochigi.jp',
208
+ 'gunma.jp','saitama.jp','chiba.jp','tokyo.jp',
209
+ 'kanagawa.jp','niigata.jp','toyama.jp','ishikawa.jp',
210
+ 'fukui.jp','yamanashi.jp','nagano.jp','gifu.jp',
211
+ 'shizuoka.jp','aichi.jp','mie.jp','shiga.jp','kyoto.jp',
212
+ 'osaka.jp','hyogo.jp','nara.jp','wakayama.jp','tottori.jp',
213
+ 'shimane.jp','okayama.jp','hiroshima.jp','yamaguchi.jp',
214
+ 'tokushima.jp','kagawa.jp','ehime.jp','kochi.jp',
215
+ 'fukuoka.jp','saga.jp','nagasaki.jp','kumamoto.jp',
216
+ 'oita.jp','miyazaki.jp','kagoshima.jp','okinawa.jp',
217
+ 'sapporo.jp','sendai.jp','yokohama.jp','kawasaki.jp',
218
+ 'nagoya.jp','kobe.jp','kitakyushu.jp','utsunomiya.jp',
219
+ 'kanazawa.jp','takamatsu.jp','matsuyama.jp','com.kh',
220
+ 'net.kh','org.kh','per.kh','edu.kh','gov.kh','mil.kh',
221
+ 'ac.kr','co.kr','go.kr','ne.kr','or.kr','pe.kr','re.kr',
222
+ 'seoul.kr','kyonggi.kr','com.kw','net.kw','org.kw',
223
+ 'edu.kw','gov.kw','com.la','net.la','org.la','com.lb',
224
+ 'org.lb','net.lb','edu.lb','gov.lb','mil.lb','com.lc',
225
+ 'edu.lc','gov.lc','net.lc','org.lc','com.lv','net.lv',
226
+ 'org.lv','edu.lv','gov.lv','mil.lv','id.lv','asn.lv',
227
+ 'conf.lv','com.ly','net.ly','org.ly','co.ma','net.ma',
228
+ 'org.ma','press.ma','ac.ma','com.mk','com.mm','net.mm',
229
+ 'org.mm','edu.mm','gov.mm','com.mo','net.mo','org.mo',
230
+ 'edu.mo','gov.mo','com.mt','net.mt','org.mt','edu.mt',
231
+ 'tm.mt','uu.mt','com.mx','net.mx','org.mx','com.my',
232
+ 'org.my','gov.my','edu.my','net.my','com.na','org.na',
233
+ 'net.na','alt.na','edu.na','cul.na','unam.na','telecom.na',
234
+ 'com.nc','net.nc','org.nc','ac.ng','edu.ng','sch.ng',
235
+ 'com.ng','gov.ng','org.ng','net.ng','gob.ni','com.ni',
236
+ 'net.ni','edu.ni','nom.ni','org.ni','com.np','net.np',
237
+ 'org.np','gov.np','edu.np','ac.nz','co.nz','cri.nz',
238
+ 'gen.nz','geek.nz','govt.nz','iwi.nz','maori.nz','mil.nz',
239
+ 'net.nz','org.nz','school.nz','com.om','co.om','edu.om',
240
+ 'ac.om','gov.om','net.om','org.om','mod.om','museum.om',
241
+ 'biz.om','pro.om','med.om','com.pa','net.pa','org.pa',
242
+ 'edu.pa','ac.pa','gob.pa','sld.pa','edu.pe','gob.pe',
243
+ 'nom.pe','mil.pe','org.pe','com.pe','net.pe','com.pg',
244
+ 'net.pg','ac.pg','com.ph','net.ph','org.ph','mil.ph',
245
+ 'ngo.ph','aid.pl','agro.pl','atm.pl','auto.pl','biz.pl',
246
+ 'com.pl','edu.pl','gmina.pl','gsm.pl','info.pl','mail.pl',
247
+ 'miasta.pl','media.pl','mil.pl','net.pl',
248
+ 'nieruchomosci.pl','nom.pl','org.pl','pc.pl','powiat.pl',
249
+ 'priv.pl','realestate.pl','rel.pl','sex.pl','shop.pl',
250
+ 'sklep.pl','sos.pl','szkola.pl','targi.pl','tm.pl',
251
+ 'tourism.pl','travel.pl','turystyka.pl','com.pk','net.pk',
252
+ 'edu.pk','org.pk','fam.pk','biz.pk','web.pk','gov.pk',
253
+ 'gob.pk','gok.pk','gon.pk','gop.pk','gos.pk','edu.ps',
254
+ 'gov.ps','plo.ps','sec.ps','com.py','net.py','org.py',
255
+ 'edu.py','com.qa','net.qa','org.qa','edu.qa','gov.qa',
256
+ 'asso.re','com.re','nom.re','com.ro','org.ro','tm.ro',
257
+ 'nt.ro','nom.ro','info.ro','rec.ro','arts.ro','firm.ro',
258
+ 'store.ro','www.ro','com.ru','net.ru','org.ru','gov.ru',
259
+ 'pp.ru','com.sa','edu.sa','sch.sa','med.sa','gov.sa',
260
+ 'net.sa','org.sa','pub.sa','com.sb','net.sb','org.sb',
261
+ 'edu.sb','gov.sb','com.sd','net.sd','org.sd','edu.sd',
262
+ 'sch.sd','med.sd','gov.sd','tm.se','press.se','parti.se',
263
+ 'brand.se','fh.se','fhsk.se','fhv.se','komforb.se',
264
+ 'kommunalforbund.se','komvux.se','lanarb.se','lanbib.se',
265
+ 'naturbruksgymn.se','sshn.se','org.se','pp.se','com.sg',
266
+ 'net.sg','org.sg','edu.sg','gov.sg','per.sg','com.sh',
267
+ 'net.sh','org.sh','edu.sh','gov.sh','mil.sh','gov.st',
268
+ 'saotome.st','principe.st','consulado.st','embaixada.st',
269
+ 'org.st','edu.st','net.st','com.st','store.st','mil.st',
270
+ 'co.st','com.sv','org.sv','edu.sv','gob.sv','red.sv',
271
+ 'com.sy','net.sy','org.sy','gov.sy','ac.th','co.th',
272
+ 'go.th','net.th','or.th','com.tn','net.tn','org.tn',
273
+ 'edunet.tn','gov.tn','ens.tn','fin.tn','nat.tn','ind.tn',
274
+ 'info.tn','intl.tn','rnrt.tn','rnu.tn','rns.tn',
275
+ 'tourism.tn','com.tr','net.tr','org.tr','edu.tr','gov.tr',
276
+ 'mil.tr','bbs.tr','k12.tr','gen.tr','co.tt','com.tt',
277
+ 'org.tt','net.tt','biz.tt','info.tt','pro.tt','name.tt',
278
+ 'gov.tt','edu.tt','nic.tt','us.tt','uk.tt','ca.tt','eu.tt',
279
+ 'es.tt','fr.tt','it.tt','se.tt','dk.tt','be.tt','de.tt',
280
+ 'at.tt','au.tt','co.tv','com.tw','net.tw','org.tw',
281
+ 'edu.tw','idv.tw','gove.tw','com.ua','net.ua','org.ua',
282
+ 'edu.ua','gov.ua','ac.ug','co.ug','or.ug','go.ug','co.uk',
283
+ 'me.uk','org.uk','edu.uk','ltd.uk','plc.uk','net.uk',
284
+ 'sch.uk','nic.uk','ac.uk','gov.uk','nhs.uk','police.uk',
285
+ 'mod.uk','dni.us','fed.us','com.uy','edu.uy','net.uy',
286
+ 'org.uy','gub.uy','mil.uy','com.ve','net.ve','org.ve',
287
+ 'co.ve','edu.ve','gov.ve','mil.ve','arts.ve','bib.ve',
288
+ 'firm.ve','info.ve','int.ve','nom.ve','rec.ve','store.ve',
289
+ 'tec.ve','web.ve','co.vi','net.vi','org.vi','com.vn',
290
+ 'biz.vn','edu.vn','gov.vn','net.vn','org.vn','int.vn',
291
+ 'ac.vn','pro.vn','info.vn','health.vn','name.vn','com.vu',
292
+ 'edu.vu','net.vu','org.vu','de.vu','ch.vu','fr.vu',
293
+ 'com.ws','net.ws','org.ws','gov.ws','edu.ws','ac.yu',
294
+ 'co.yu','edu.yu','org.yu','com.ye','net.ye','org.ye',
295
+ 'gov.ye','edu.ye','mil.ye','ac.za','alt.za','bourse.za',
296
+ 'city.za','co.za','edu.za','gov.za','law.za','mil.za',
297
+ 'net.za','ngo.za','nom.za','org.za','school.za','tm.za',
298
+ 'web.za','co.zw','ac.zw','org.zw','gov.zw','eu.org',
299
+ 'au.com','br.com','cn.com','de.com','de.net','eu.com',
300
+ 'gb.com','gb.net','hu.com','no.com','qc.com','ru.com',
301
+ 'sa.com','se.com','uk.com','uk.net','us.com','uy.com',
302
+ 'za.com','dk.org','tel.no','fax.nr','mob.nr','mobil.nr',
303
+ 'mobile.nr','tel.nr','tlf.nr','e164.arpa'
304
+ ]
305
+ end
306
+
307
+ # Parse the requesting IP address and fill corrosponding attributes
308
+ # @param ip [String] An IP address (IPv4 only at the moment)
309
+ def ip_address=(ip)
310
+ require 'digest/sha1'
311
+ require 'resolv'
312
+
313
+ logger.debug("IP: #{ip}")
314
+
315
+ unless ip.blank?
316
+ self.ip_address_hash = Digest::SHA1.hexdigest(ip)
317
+
318
+ # IP
319
+ begin
320
+ domain_parts = Resolv.getname(ip).split('.')
321
+
322
+ self.domain = "#{domain_parts[-1]}"
323
+
324
+ self.domain = "#{domain_parts[-2]}.#{domain}" if domain_parts[-2]
325
+
326
+ if two_part_domain_endings.include? self.domain
327
+ self.domain = "#{domain_parts[-3]}.#{self.domain}"
328
+ end
329
+ rescue
330
+ ip_parts = ip.split('.')
331
+ self.domain = "#{ip_parts[0]}.#{ip_parts[1]}.x.x"
332
+ end
333
+
334
+ # GeoIP
335
+ if ::Gricer.config.geoip_db
336
+ if Rails.env.development?
337
+ geoip = {:country_code=>"DE", :country_code3=>"DEU", :country_name=>"Germany", :latitude=>51.0, :longitude=>9.0}
338
+ else
339
+ geoip = ::Gricer.config.geoip_db.look_up(ip)
340
+ end
341
+
342
+
343
+ if geoip
344
+ self.country = geoip[:country_code].try(:downcase)
345
+ self.region = geoip[:region].try(:downcase)
346
+ self.city = geoip[:city]
347
+ self.postal_code = geoip[:postal_code]
348
+ self.longitude = geoip[:longitude]
349
+ self.latitude = geoip[:latitude]
350
+ end
351
+ end
352
+ end
353
+ end
354
+
355
+ def requested_locale=(locale)
356
+ self.requested_locale_major, self.requested_locale_minor = locale.try(:downcase).try(:split, '-')
357
+ end
358
+
359
+ def requested_locale
360
+ if requested_locale_minor
361
+ "#{requested_locale_major}-#{requested_locale_minor}"
362
+ else
363
+ requested_locale_major
364
+ end
365
+ end
366
+
367
+ def silverlight_version=(version)
368
+ self[:silverlight_version] = version
369
+ self.silverlight_major_version = silverlight_version.match(/^([0-9]*\.[0-9]*)/).to_a.last if silverlight_version
370
+ end
371
+
372
+ def flash_version=(version)
373
+ self[:flash_version] = version
374
+ self.flash_major_version = flash_version.match(/^([0-9]*\.[0-9]*)/).to_a.last if flash_version
375
+ end
376
+
377
+ # Get the bounce rate
378
+ #
379
+ # This is the rate of sessions with one request to sessions with multiple requests.
380
+ # @return [Float]
381
+ def self.bounce_rate
382
+ if (c = self.count) > 0
383
+ self.bounce_sessions.count / c.to_f
384
+ else
385
+ 0
386
+ end
387
+ end
388
+
389
+ # Get the average count of requests per session.
390
+ #
391
+ # @return [Float]
392
+ def self.requests_per_session
393
+ if (c = self.count) > 0
394
+ self.sum(:requests_count) / c.to_f
395
+ else
396
+ 0
397
+ end
398
+ end
399
+
400
+ # Get the average duration of sessions in seconds.
401
+ #
402
+ # @return [Float]
403
+ def self.avg_duration
404
+ if (c = self.count) > 0
405
+ logger.debug ActiveRecord::Base.connection.class
406
+
407
+ if ActiveRecord::Base.connection.class.to_s == 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter'
408
+ self.sum("date_part('epoch', \"#{self.table_name}\".\"updated_at\") - date_part('epoch', \"#{self.table_name}\".\"created_at\")").to_f / c.to_f
409
+ else
410
+ self.sum("strftime('%s', \"#{self.table_name}\".\"updated_at\") - strftime('%s', \"#{self.table_name}\".\"created_at\")") / c.to_f
411
+ end
412
+ else
413
+ 0
414
+ end
415
+ end
416
+
417
+ # Get the duration of the current session instance
418
+ def duration
419
+ updated_at - created_at
420
+ end
421
+
422
+ # Get the new visitor rate
423
+ #
424
+ # This is the rate of new sessions to all sessions
425
+ def self.new_visitors
426
+ if (c = self.count) > 0
427
+ self.new_visits.count / c.to_f
428
+ else
429
+ 0
430
+ end
431
+ end
432
+ end
433
+ end