redis_analytics 0.7.1 → 1.0.0
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/views/layouts/redis_analytics/application.html.erb +3 -3
- data/coverage/index.html +423 -381
- data/lib/redis_analytics/configuration.rb +73 -73
- data/lib/redis_analytics/engine.rb +7 -0
- data/lib/redis_analytics/filter.rb +22 -22
- data/lib/redis_analytics/helpers.rb +58 -58
- data/lib/redis_analytics/metrics.rb +73 -73
- data/lib/redis_analytics/version.rb +3 -3
- data/lib/redis_analytics/visit.rb +96 -96
- data/test/dummy/config/environments/production.rb +1 -1
- data/test/dummy/config/routes.rb +1 -1
- data/test/dummy/log/development.log +1185 -0
- data/test/dummy/log/production.log +5172 -0
- data/test/dummy/public/assets/application-c900658d554ff9f314a2e1b97cd68997.js.gz +0 -0
- data/test/dummy/public/assets/manifest-567012ae8b6f03148f7121062750991f.json +1 -0
- data/test/dummy/public/assets/redis_analytics/redis_analytics-82322234eccfd3d7c55a2034ee41ab2a.js +10785 -0
- data/test/dummy/public/assets/redis_analytics/redis_analytics-82322234eccfd3d7c55a2034ee41ab2a.js.gz +0 -0
- data/test/dummy/public/assets/redis_analytics/redis_analytics-f13abe0904b7483ea8c8c08029e97be3.css +64 -0
- data/test/dummy/public/assets/redis_analytics/redis_analytics-f13abe0904b7483ea8c8c08029e97be3.css.gz +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/3b2603753f496fe7b73a04ce200ea6c0 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/4b838eac074ecce8b8ae19c261677ab6 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/63afa0e9c559fa85d5c63b6f37728141 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/6d0077b1948c42a8892f40120649d217 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/a317f594a79c3dd49a6f88b49aabc1b5 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/f069f087fa68223029e7fb8547e5bc3b +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/006eb7cc017cf18c3ea32cab97f0cc3c +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/192691b9e0bedebfde12633bb3629d0b +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/3b2603753f496fe7b73a04ce200ea6c0 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/4b838eac074ecce8b8ae19c261677ab6 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/5cf60638cb580f41112a5a83eb3b72ef +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/63afa0e9c559fa85d5c63b6f37728141 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/6c2f18f7338d56feecce32780357521c +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/6d0077b1948c42a8892f40120649d217 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/7751914469530dae0646d92467e9d72e +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/8d62150b658b3a8b6fed7e8951f52392 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/8ea331810c31cbe1ba34b91a02f769e7 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/953bc37b1ee0915247dba29773960202 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/9b54afa4ef026f4c288feeec49f2f526 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/a29d0dc527ff5ce1252ccf6370514620 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/a317f594a79c3dd49a6f88b49aabc1b5 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/b01583a39abd5ce0a3d2b2967c6f75bd +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/cfdf466647e83eb93e0d6630e4b51a25 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/d7491740e7757fe2ec57fd6d25461e5c +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/df16db3bfcc56fafc73b853b7999be1f +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/e7bd439b274a2155050b47e5a64ea72d +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/f069f087fa68223029e7fb8547e5bc3b +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/production/sprockets/f9a412f49281cc7e6512f0787f7ba05f +0 -0
- data/vendor/assets/javascripts/redis_analytics/{engine.js → redis_analytics.js} +6 -1
- data/vendor/assets/stylesheets/redis_analytics/{engine.css → redis_analytics.css} +3 -1
- metadata +43 -8
- data/app/views/redis_analytics/application/_footer.html.erb~ +0 -3
- data/app/views/redis_analytics/visits/_unique_visits_line.html.erb~ +0 -26
- data/app/views/redis_analytics/visits/index.html.erb~ +0 -71
- data/test/dummy/public/assets/manifest-5bcba723c313582b1d428fc3bf592b29.json +0 -1
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
module RedisAnalytics
|
|
2
|
+
module Configuration
|
|
3
|
+
# Redis connection instance
|
|
4
|
+
attr_accessor :redis_connection
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
# Redis namespace for keys
|
|
7
|
+
attr_writer :redis_namespace
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
# Name of the cookie which tracks first visitors
|
|
10
|
+
attr_writer :first_visit_cookie_name
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
# Name of the cookie which tracks current visits
|
|
13
|
+
attr_writer :current_visit_cookie_name
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
# Minutes the visit should timeout after (if no hit is received)
|
|
16
|
+
attr_writer :visit_timeout
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
# Endpoint for dashboard
|
|
19
|
+
attr_writer :dashboard_endpoint
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
# Endpoint for api
|
|
22
|
+
attr_writer :api_endpoint
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
attr_writer :path_filters
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
attr_writer :filters
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
# Path to the Geo IP Database file
|
|
29
|
+
attr_writer :geo_ip_data_path
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# Minutes the visit should timeout after (if no hit is received)
|
|
37
|
-
def visit_timeout
|
|
38
|
-
@visit_timeout ||= 30 # minutes
|
|
39
|
-
end
|
|
31
|
+
# Redis namespace for keys
|
|
32
|
+
def redis_namespace
|
|
33
|
+
@redis_namespace ||= 'ra'
|
|
34
|
+
end
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
# Minutes the visit should timeout after (if no hit is received)
|
|
37
|
+
def visit_timeout
|
|
38
|
+
@visit_timeout ||= 30 # minutes
|
|
39
|
+
end
|
|
45
40
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
# Name of the cookie which tracks first visitors (unknown visitors)
|
|
42
|
+
def first_visit_cookie_name
|
|
43
|
+
@first_visit_cookie_name ||= '_rucn'
|
|
44
|
+
end
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
# Name of the cookie which tracks visits
|
|
47
|
+
def current_visit_cookie_name
|
|
48
|
+
@current_visit_cookie_name ||= '_vcn'
|
|
49
|
+
end
|
|
54
50
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
def filters
|
|
52
|
+
@filters ||= []
|
|
53
|
+
end
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
def path_filters
|
|
56
|
+
@path_filters ||= []
|
|
57
|
+
end
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
def add_filter(&proc)
|
|
60
|
+
filters << RedisAnalytics::Filter.new(proc)
|
|
61
|
+
end
|
|
66
62
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
def add_path_filter(path)
|
|
64
|
+
path_filters << RedisAnalytics::PathFilter.new(path)
|
|
65
|
+
end
|
|
70
66
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
def geo_ip_data_path
|
|
68
|
+
@geo_ip_data_path = ::File.expand_path(::File.join(::File.dirname(__FILE__),'..','..')) + "/bin/GeoIP.dat"
|
|
69
|
+
end
|
|
74
70
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
def visitor_recency_slices
|
|
72
|
+
@visitor_recency_slices ||= [1, 7, 30]
|
|
73
|
+
end
|
|
78
74
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
def default_range
|
|
76
|
+
@default_range = :day
|
|
77
|
+
end
|
|
82
78
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
def redis_key_timestamps # [format, expire in seconds or nil]
|
|
80
|
+
['%Y', '%Y_%m', '%Y_%m_%d', '%Y_%m_%d_%H', '%Y_%m_%d_%H_%M']
|
|
81
|
+
end
|
|
86
82
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
83
|
+
def time_range_formats
|
|
84
|
+
[[:year, :month, "%b"], [:week, :day, "%a"], [:day, :hour, "%l%P"]]
|
|
85
|
+
end
|
|
90
86
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
def configure
|
|
88
|
+
yield self
|
|
89
|
+
end
|
|
94
90
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
def api_endpoint
|
|
92
|
+
@api_endpoint || dashboard_endpoint + '/api'
|
|
93
|
+
end
|
|
98
94
|
|
|
95
|
+
def dashboard_endpoint
|
|
96
|
+
@dashboard_endpoint || '/redis_analytics'
|
|
99
97
|
end
|
|
98
|
+
|
|
100
99
|
end
|
|
100
|
+
end
|
|
101
101
|
|
|
@@ -13,6 +13,13 @@ module RedisAnalytics
|
|
|
13
13
|
initializer "redis_analytics.view_helpers" do |app|
|
|
14
14
|
ActionController::Base.send :include, RedisAnalytics::Helpers
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
initializer "redis_analytics.precompile.hook", group: :all do |app|
|
|
18
|
+
app.config.assets.precompile += %w[
|
|
19
|
+
redis_analytics/redis_analytics.js
|
|
20
|
+
redis_analytics/redis_analytics.css
|
|
21
|
+
]
|
|
22
|
+
end
|
|
16
23
|
end
|
|
17
24
|
end
|
|
18
25
|
end
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
module RedisAnalytics
|
|
2
|
+
class Filter
|
|
3
|
+
attr_reader :filter_proc
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def matches?(request, response)
|
|
10
|
-
filter_proc.call(request, response)
|
|
11
|
-
end
|
|
5
|
+
def initialize(filter_proc)
|
|
6
|
+
@filter_proc = filter_proc
|
|
7
|
+
end
|
|
12
8
|
|
|
9
|
+
def matches?(request, response)
|
|
10
|
+
filter_proc.call(request, response)
|
|
13
11
|
end
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class PathFilter
|
|
16
|
+
attr_reader :filter_path
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
def initialize(filter_path)
|
|
19
|
+
@filter_path = filter_path
|
|
20
|
+
end
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
22
|
+
def matches?(request_path)
|
|
23
|
+
if filter_path.is_a?(String)
|
|
24
|
+
request_path == filter_path
|
|
25
|
+
elsif filter_path.is_a?(Regexp)
|
|
26
|
+
request_path =~ filter_path
|
|
28
27
|
end
|
|
29
28
|
end
|
|
30
|
-
|
|
31
29
|
end
|
|
32
30
|
|
|
31
|
+
end
|
|
32
|
+
|
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
module RedisAnalytics
|
|
2
|
+
module Helpers
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
FORMAT_SPECIFIER = [['%Y', 365], ['%m', 30], ['%d', 24], ['%H', 60], ['%M', 60]]
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
GRANULARITY = ['yearly', 'monthly', 'dayly', 'hourly', 'minutely']
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
end
|
|
8
|
+
private
|
|
9
|
+
def method_missing(meth, *args, &block)
|
|
10
|
+
if meth.to_s =~ /^(minute|hour|dai|day|month|year)ly_([a-z_0-9]+)$/
|
|
11
|
+
granularity = ($1 == 'dai' ? 'day' : $1) + 'ly'
|
|
12
|
+
metric_name = $2
|
|
13
|
+
data(granularity, metric_name, *args)
|
|
14
|
+
else
|
|
15
|
+
super
|
|
17
16
|
end
|
|
17
|
+
end
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
def metric_type(metric_name)
|
|
20
|
+
RedisAnalytics.redis_connection.hget("#{RedisAnalytics.redis_namespace}:#METRICS", metric_name)
|
|
21
|
+
end
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
def data(granularity, metric_name, from_date, options = {})
|
|
24
|
+
aggregate = options[:aggregate] || false
|
|
25
|
+
x = granularity[0..-3]
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
to_date = (options[:to_date] || Time.now).send("end_of_#{x}")
|
|
28
|
+
i = from_date.send("beginning_of_#{x}")
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
else
|
|
52
|
-
return time.zip(RedisAnalytics.redis_connection.mget(*union).map(&:to_i))
|
|
53
|
-
end
|
|
30
|
+
union = []
|
|
31
|
+
time = []
|
|
32
|
+
begin
|
|
33
|
+
slice_key = i.strftime(FORMAT_SPECIFIER[0..GRANULARITY.index(granularity)].map{|x| x[0]}.join('_'))
|
|
34
|
+
union << "#{RedisAnalytics.redis_namespace}:#{metric_name}:#{slice_key}"
|
|
35
|
+
time << slice_key.split('_')
|
|
36
|
+
i += 1.send(x)
|
|
37
|
+
end while i <= to_date
|
|
38
|
+
seq = get_next_seq
|
|
39
|
+
if metric_type(metric_name) == 'String'
|
|
40
|
+
if aggregate
|
|
41
|
+
union_key = "#{RedisAnalytics.redis_namespace}:#{seq}"
|
|
42
|
+
RedisAnalytics.redis_connection.zunionstore(union_key, union)
|
|
43
|
+
RedisAnalytics.redis_connection.expire(union_key, 100)
|
|
44
|
+
return Hash[RedisAnalytics.redis_connection.zrange(union_key, 0, -1, :with_scores => true)]
|
|
45
|
+
else
|
|
46
|
+
return time.zip(union.map{|x| Hash[RedisAnalytics.redis_connection.zrange(x, 0, -1, :with_scores => true)]})
|
|
47
|
+
end
|
|
48
|
+
elsif metric_type(metric_name) == 'Fixnum'
|
|
49
|
+
if aggregate
|
|
50
|
+
return RedisAnalytics.redis_connection.mget(*union).map(&:to_i).inject(:+)
|
|
54
51
|
else
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
return time.zip(RedisAnalytics.redis_connection.mget(*union).map(&:to_i))
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
if Metrics.public_instance_methods.any?{|m| m.to_s =~ /^#{metric_name}_ratio_per_(hit|visit)$/}
|
|
56
|
+
aggregate ? {} : time.zip([{}] * time.length)
|
|
57
|
+
elsif Metrics.public_instance_methods.any?{|m| m.to_s =~ /^#{metric_name}_count_per_(hit|visit)$/}
|
|
58
|
+
aggregate ? 0 : time.zip([0] * time.length)
|
|
59
|
+
else
|
|
60
|
+
aggregate ? 0 : time.zip([0] * time.length)
|
|
62
61
|
end
|
|
63
62
|
end
|
|
63
|
+
end
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
def get_next_seq
|
|
66
|
+
seq = RedisAnalytics.redis_connection.incr("#{RedisAnalytics.redis_namespace}:#SEQUENCER")
|
|
67
|
+
end
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
end
|
|
69
|
+
def time_range
|
|
70
|
+
(request.cookies["_rarng"] || RedisAnalytics.default_range).to_sym
|
|
72
71
|
end
|
|
73
72
|
end
|
|
73
|
+
end
|
|
74
74
|
|
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
module RedisAnalytics
|
|
2
|
+
module Metrics
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
attr_reader :visit_time_count_per_visit
|
|
5
|
+
attr_reader :visits_count_per_visit, :first_visits_count_per_visit, :repeat_visits_count_per_visit
|
|
6
|
+
attr_reader :unique_visits_ratio_per_visit
|
|
7
|
+
attr_reader :page_views_count_per_hit, :second_page_views_count_per_hit
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
# Developers can override or define new public methods here
|
|
10
|
+
# Methods should start with track and end with count or types
|
|
11
|
+
# Return types should be Fixnum or String resp.
|
|
12
|
+
# If you return nil or an error nothing will be tracked
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
def browser_ratio_per_visit
|
|
15
|
+
user_agent.name.to_s
|
|
16
|
+
end
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
def platform_ratio_per_visit
|
|
19
|
+
user_agent.platform.to_s
|
|
20
|
+
end
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
end
|
|
30
|
-
rescue Exception => e
|
|
31
|
-
warn "Unable to fetch country info #{e}"
|
|
22
|
+
def country_ratio_per_visit
|
|
23
|
+
if defined?(GeoIP)
|
|
24
|
+
begin
|
|
25
|
+
g = GeoIP.new(RedisAnalytics.geo_ip_data_path)
|
|
26
|
+
geo_country_code = g.country(@rack_request.ip).to_hash[:country_code2]
|
|
27
|
+
if geo_country_code and geo_country_code =~ /^[A-Z]{2}$/
|
|
28
|
+
return geo_country_code
|
|
32
29
|
end
|
|
30
|
+
rescue Exception => e
|
|
31
|
+
warn "Unable to fetch country info #{e}"
|
|
33
32
|
end
|
|
34
33
|
end
|
|
34
|
+
end
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
36
|
+
def recency_ratio_per_visit
|
|
37
|
+
# tracking for visitor recency
|
|
38
|
+
if @last_visit_time # from first_visit_cookie
|
|
39
|
+
days_since_last_visit = ((@t.to_i - @last_visit_time.to_i)/(24*3600)).round
|
|
40
|
+
if days_since_last_visit <= 1
|
|
41
|
+
return 'd'
|
|
42
|
+
elsif days_since_last_visit <= 7
|
|
43
|
+
return 'w'
|
|
44
|
+
elsif days_since_last_visit <= 30
|
|
45
|
+
return 'm'
|
|
46
|
+
else
|
|
47
|
+
return 'o'
|
|
49
48
|
end
|
|
50
49
|
end
|
|
50
|
+
end
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
def device_ratio_per_visit
|
|
53
|
+
return ((user_agent.mobile? or user_agent.tablet?) ? 'mobile' : 'desktop')
|
|
54
|
+
end
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
end
|
|
56
|
+
def referrer_ratio_per_visit
|
|
57
|
+
if @rack_request.referrer
|
|
58
|
+
['google', 'bing', 'yahoo', 'cleartrip', 'github'].each do |referrer|
|
|
59
|
+
# this will track x.google.mysite.com as google so its buggy, fix the regex
|
|
60
|
+
if m = @rack_request.referrer.match(/^(https?:\/\/)?([a-zA-Z0-9\.\-]+\.)?(#{referrer})\.([a-zA-Z\.]+)(:[0-9]+)?(\/.*)?$/)
|
|
61
|
+
"REFERRER => #{m.to_a[3]}"
|
|
62
|
+
referrer = m.to_a[3]
|
|
63
|
+
else
|
|
64
|
+
referrer = 'other'
|
|
66
65
|
end
|
|
67
|
-
else
|
|
68
|
-
referrer = 'organic'
|
|
69
66
|
end
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
# track the ratio of URL's visits
|
|
74
|
-
def url_ratio_per_hit
|
|
75
|
-
return @rack_request.path
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# track the landing pages ratio
|
|
79
|
-
def landing_page_ratio_per_hit
|
|
80
|
-
return @rack_request.path if @page_view_seq_no.to_i == 0
|
|
67
|
+
else
|
|
68
|
+
referrer = 'organic'
|
|
81
69
|
end
|
|
70
|
+
return referrer
|
|
71
|
+
end
|
|
82
72
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
73
|
+
# track the ratio of URL's visits
|
|
74
|
+
def url_ratio_per_hit
|
|
75
|
+
return @rack_request.path
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# track the landing pages ratio
|
|
79
|
+
def landing_page_ratio_per_hit
|
|
80
|
+
return @rack_request.path if @page_view_seq_no.to_i == 0
|
|
81
|
+
end
|
|
87
82
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
83
|
+
# track the landing pages ratio
|
|
84
|
+
def http_response_ratio_per_hit
|
|
85
|
+
return @rack_response.status.to_s
|
|
86
|
+
end
|
|
92
87
|
|
|
88
|
+
private
|
|
89
|
+
def user_agent
|
|
90
|
+
Browser.new(:ua => @rack_request.user_agent, :accept_language => 'en-us')
|
|
93
91
|
end
|
|
92
|
+
|
|
94
93
|
end
|
|
94
|
+
end
|
|
95
95
|
|