mitamirri 0.13.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +26 -0
- data/README.rdoc +117 -0
- data/Rakefile +26 -0
- data/VERSION +1 -0
- data/app/controllers/application_controller.rb +10 -0
- data/app/controllers/trackable_actions_controller.rb +10 -0
- data/app/controllers/trackable_sessions_controller.rb +100 -0
- data/app/helpers/application_helper.rb +2 -0
- data/app/models/trackable_action.rb +193 -0
- data/app/models/trackable_location.rb +19 -0
- data/app/models/trackable_session.rb +352 -0
- data/app/models/trackable_stat.rb +64 -0
- data/app/views/layouts/application.html.erb +20 -0
- data/app/views/trackable_sessions/_keywords_graph.html.erb +24 -0
- data/app/views/trackable_sessions/_print_buttons.html.erb +3 -0
- data/app/views/trackable_sessions/_sessions.html.erb +26 -0
- data/app/views/trackable_sessions/_top_referrers.html.erb +24 -0
- data/app/views/trackable_sessions/_traffic_summary_all.html.erb +64 -0
- data/app/views/trackable_sessions/_traffic_summary_by_kind.html.erb +48 -0
- data/app/views/trackable_sessions/_visits_graph.html.erb +25 -0
- data/app/views/trackable_sessions/content.html.erb +119 -0
- data/app/views/trackable_sessions/explorer.html.erb +67 -0
- data/app/views/trackable_sessions/export.xls.erb +5 -0
- data/app/views/trackable_sessions/index.html.erb +47 -0
- data/app/views/trackable_sessions/intersite.html.erb +97 -0
- data/app/views/trackable_sessions/leads.html.erb +98 -0
- data/app/views/trackable_sessions/show.html.erb +113 -0
- data/app/views/trackable_sessions/visitor_profile.html.erb +114 -0
- data/config/boot.rb +110 -0
- data/config/database.yml +0 -0
- data/config/environment.rb +24 -0
- data/config/environments/development.rb +17 -0
- data/config/environments/production.rb +28 -0
- data/config/environments/test.rb +31 -0
- data/config/initializers/backtrace_silencers.rb +7 -0
- data/config/initializers/database.rb +1 -0
- data/config/initializers/inflections.rb +10 -0
- data/config/initializers/mime_types.rb +5 -0
- data/config/initializers/new_rails_defaults.rb +21 -0
- data/config/initializers/session_store.rb +15 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +11 -0
- data/db/development.sqlite3 +0 -0
- data/db/migrate/20100810173533_create_trackable_sessions.rb +23 -0
- data/db/migrate/20100810173605_create_trackable_actions.rb +20 -0
- data/db/test.sqlite3 +1 -0
- data/doc/README_FOR_APP +2 -0
- data/init.rb +8 -0
- data/lib/mitamirri/content_report.rb +75 -0
- data/lib/mitamirri/helper.rb +106 -0
- data/lib/mitamirri/intersite_traffic_report.rb +122 -0
- data/lib/mitamirri/leads_report.rb +90 -0
- data/lib/mitamirri/session_report.rb +219 -0
- data/lib/mitamirri/stat_report.rb +216 -0
- data/lib/mitamirri/tasks.rb +27 -0
- data/lib/mitamirri/visitor_profile_report.rb +127 -0
- data/lib/mitamirri.rb +15 -0
- data/log/development.log +0 -0
- data/log/production.log +0 -0
- data/log/server.log +0 -0
- data/log/test.log +0 -0
- data/mitamirri.gemspec +125 -0
- data/public/stylesheets/mitamirri.css +736 -0
- data/script/about +4 -0
- data/script/console +3 -0
- data/script/dbconsole +3 -0
- data/script/destroy +3 -0
- data/script/generate +3 -0
- data/script/performance/benchmarker +3 -0
- data/script/performance/profiler +3 -0
- data/script/plugin +3 -0
- data/script/runner +3 -0
- data/script/server +3 -0
- data/spec/controllers/trackable_actions_controller_spec.rb +5 -0
- data/spec/controllers/trackable_sessions_controller_spec.rb +5 -0
- data/spec/custom_matchers.rb +23 -0
- data/spec/helpers/application_helper_spec.rb +57 -0
- data/spec/lib/content_report_spec.rb +37 -0
- data/spec/lib/intersite_traffic_report_spec.rb +51 -0
- data/spec/lib/leads_report_spec.rb +46 -0
- data/spec/lib/session_report_spec.rb +107 -0
- data/spec/lib/stat_report_spec.rb +106 -0
- data/spec/models/trackable_action_spec.rb +42 -0
- data/spec/models/trackable_session_spec.rb +154 -0
- data/spec/rcov.opts +4 -0
- data/spec/schema.rb +34 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +32 -0
- metadata +155 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
module Mitamirri
|
2
|
+
|
3
|
+
class LeadsReport
|
4
|
+
|
5
|
+
# Search params
|
6
|
+
attr_accessor :site
|
7
|
+
attr_accessor :time_period
|
8
|
+
attr_accessor :start_date
|
9
|
+
attr_accessor :end_date
|
10
|
+
|
11
|
+
# Stat attributes
|
12
|
+
|
13
|
+
# Initialization
|
14
|
+
|
15
|
+
def initialize(args = {})
|
16
|
+
args.each{|k,v| self.send("#{k}=",v) if self.respond_to?(k)}
|
17
|
+
self.time_period = args[:time_period] || 'past 6 months'
|
18
|
+
self.start_date = self.dates.first
|
19
|
+
self.end_date = self.dates.last
|
20
|
+
end
|
21
|
+
|
22
|
+
# Generate an array of dates spanning the appropriate time period.
|
23
|
+
def dates
|
24
|
+
return @dates if @dates
|
25
|
+
@dates = []
|
26
|
+
case self.time_period.downcase
|
27
|
+
when 'past month'
|
28
|
+
_increment = 4
|
29
|
+
when 'past 3 months'
|
30
|
+
_increment = 12
|
31
|
+
when 'past 6 months'
|
32
|
+
_increment = 24
|
33
|
+
when 'past 12 months'
|
34
|
+
_increment = 52
|
35
|
+
else 'all time'
|
36
|
+
_increment = 104
|
37
|
+
end
|
38
|
+
|
39
|
+
(1.._increment).each{ |i| @dates << (Time.zone.now - i.weeks).beginning_of_week }
|
40
|
+
@dates.reverse!
|
41
|
+
@dates << Time.zone.now
|
42
|
+
@dates
|
43
|
+
end
|
44
|
+
|
45
|
+
# Graphing objects
|
46
|
+
|
47
|
+
def leads(args)
|
48
|
+
_leads = []
|
49
|
+
_leads << Lead.new(:stats => lead_stats(args))
|
50
|
+
_leads
|
51
|
+
end
|
52
|
+
|
53
|
+
def lead_stats(args)
|
54
|
+
_lead_stats = []
|
55
|
+
dates.each do |date|
|
56
|
+
_total_sessions = TrackableSession.search_without_date(args).for_week(date).count
|
57
|
+
_total_conversions = TrackableSession.search_without_date(args).for_week(date).with_conversions.count
|
58
|
+
_conversion_rate = (_total_conversions.to_f / _total_sessions) * 100
|
59
|
+
_lead_stats << LeadStat.new(:date => date, :leads => _total_conversions, :conversion_rate => sprintf("%0.1f",_conversion_rate).to_f)
|
60
|
+
end
|
61
|
+
_lead_stats
|
62
|
+
end
|
63
|
+
|
64
|
+
# Inner stat classes
|
65
|
+
|
66
|
+
class Lead
|
67
|
+
attr_accessor :stats
|
68
|
+
def initialize(args)
|
69
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class LeadStat
|
74
|
+
attr_accessor :date
|
75
|
+
attr_accessor :leads
|
76
|
+
attr_accessor :conversion_rate
|
77
|
+
|
78
|
+
def initialize(args)
|
79
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def short_date
|
83
|
+
self.date.to_s(:concise)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
module Mitamirri
|
2
|
+
|
3
|
+
class SessionReport
|
4
|
+
|
5
|
+
# Search params
|
6
|
+
attr_accessor :action_kind
|
7
|
+
attr_accessor :site
|
8
|
+
attr_accessor :time_period
|
9
|
+
attr_accessor :visit_kind
|
10
|
+
|
11
|
+
# Initialization
|
12
|
+
|
13
|
+
def initialize(args = {})
|
14
|
+
args.each{|k,v| self.send("#{k}=",v) if self.respond_to?(k)}
|
15
|
+
self.time_period = args[:time_period] || 'past 6 months'
|
16
|
+
self.sessions
|
17
|
+
end
|
18
|
+
|
19
|
+
def criteria
|
20
|
+
_criteria = self.sessions.to_hash
|
21
|
+
_criteria.delete(:sort)
|
22
|
+
_criteria
|
23
|
+
end
|
24
|
+
|
25
|
+
def sessions
|
26
|
+
@sessions ||= TrackableSession.search(
|
27
|
+
:action_kind => self.action_kind,
|
28
|
+
:site => self.site,
|
29
|
+
:time_period => self.time_period,
|
30
|
+
:visit_kind => self.visit_kind
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Generate an array of dates spanning the appropriate time period.
|
35
|
+
def dates
|
36
|
+
return @dates if @dates
|
37
|
+
@dates = []
|
38
|
+
case self.time_period
|
39
|
+
when 'past month'
|
40
|
+
_increment = 1
|
41
|
+
when 'past 3 months'
|
42
|
+
_increment = 2
|
43
|
+
when 'past 6 months'
|
44
|
+
_increment = 5
|
45
|
+
when 'past 12 months'
|
46
|
+
_increment = 11
|
47
|
+
else 'all time'
|
48
|
+
_increment = 24
|
49
|
+
end
|
50
|
+
|
51
|
+
(1.._increment).each{ |i| @dates << (Time.zone.now - i.months).beginning_of_month }
|
52
|
+
@dates.reverse!
|
53
|
+
@dates << Time.zone.now.end_of_month
|
54
|
+
@dates
|
55
|
+
end
|
56
|
+
|
57
|
+
# Graphing data
|
58
|
+
def self.keywords_by_frequency(args)
|
59
|
+
TrackableSession.keywords_histogram(args).inject([]){|a,v| a << KeywordStat.new(:keyword => v[0], :count => v[1]); a}.sort{|a,b| a.count <=> b.count}.reverse[0..24]
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.top_referrers(args)
|
63
|
+
TrackableSession.referrers_histogram(args).inject([]){|a,v| a << ReferrerStat.new(:referrer => v[0], :count => v[1]); a}.sort{|a,b| a.count <=> b.count}.reverse[0..24]
|
64
|
+
end
|
65
|
+
|
66
|
+
def visits(args)
|
67
|
+
_visits = []
|
68
|
+
_visits << Visit.new(:kind => "#{args[:visit_kind].titleize} Visits", :stats => visit_stats(args))
|
69
|
+
_visits
|
70
|
+
end
|
71
|
+
|
72
|
+
def visit_stats(args)
|
73
|
+
_visit_stats = []
|
74
|
+
dates.each do |date|
|
75
|
+
_visit_stats << VisitStat.new(:date => date, :visits => TrackableSession.search_without_date(args).for_month(date).count)
|
76
|
+
end
|
77
|
+
_visit_stats
|
78
|
+
end
|
79
|
+
|
80
|
+
# Statistics
|
81
|
+
|
82
|
+
def average_pages_per_visit
|
83
|
+
sprintf("%.1f", total_pageviews / total_visits.to_f)
|
84
|
+
end
|
85
|
+
|
86
|
+
def average_time_on_site
|
87
|
+
average = total_duration / total_visits.to_f
|
88
|
+
average > 60 ? sprintf("%.1f minutes", average / 60.0) : sprintf("%.1f seconds", average)
|
89
|
+
end
|
90
|
+
|
91
|
+
def bounce_rate
|
92
|
+
bounce_rate = total_bounces / total_visits.to_f
|
93
|
+
sprintf("%.1f%", bounce_rate * 100)
|
94
|
+
end
|
95
|
+
|
96
|
+
def conversion_rate
|
97
|
+
conversion_rate = total_conversions / total_visits.to_f
|
98
|
+
sprintf("%.1f%", conversion_rate * 100)
|
99
|
+
end
|
100
|
+
|
101
|
+
def total_bounces
|
102
|
+
@total_bounces ||= self.sessions.bounces.count
|
103
|
+
end
|
104
|
+
|
105
|
+
def total_clickthroughs
|
106
|
+
@total_clickthroughs ||= self.sessions.with_clickthroughs.count
|
107
|
+
end
|
108
|
+
|
109
|
+
def total_clickthroughs_percentage
|
110
|
+
sprintf("%.1f%", (total_clickthroughs / total_visits.to_f) * 100)
|
111
|
+
end
|
112
|
+
|
113
|
+
def total_conversions
|
114
|
+
@total_conversions ||= self.sessions.with_conversions.count
|
115
|
+
end
|
116
|
+
|
117
|
+
def total_direct_visits
|
118
|
+
@total_direct_visits ||= self.sessions.direct_visits.count
|
119
|
+
end
|
120
|
+
|
121
|
+
def total_duration
|
122
|
+
@total_duration ||= TrackableSession.collection.group(["site"], self.criteria, {:duration => 0}, "function(doc,prev) { prev.duration += doc.duration}", true).map{|x| x['duration']}.sum.to_i
|
123
|
+
end
|
124
|
+
|
125
|
+
def total_new_visits
|
126
|
+
@total_new_visits ||= self.sessions.new_visits.count
|
127
|
+
end
|
128
|
+
|
129
|
+
def total_return_visits
|
130
|
+
@total_return_visits ||= total_visits - total_new_visits
|
131
|
+
end
|
132
|
+
|
133
|
+
def total_organic_visits
|
134
|
+
@total_organic_visits ||= self.sessions.organic_visits.count
|
135
|
+
end
|
136
|
+
|
137
|
+
def total_pageviews
|
138
|
+
@total_pageviews ||= TrackableSession.collection.group(["site"], self.criteria, {:views_count => 0}, "function(doc,prev) { prev.views_count += doc.views_count}", true).map{|x| x['views_count']}.sum.to_i
|
139
|
+
end
|
140
|
+
|
141
|
+
def total_ppc_visits
|
142
|
+
@total_ppc_visits ||= self.sessions.ppc_visits.count
|
143
|
+
end
|
144
|
+
|
145
|
+
def total_search_visits
|
146
|
+
@total_search_visits ||= self.sessions.search_visits.count
|
147
|
+
end
|
148
|
+
|
149
|
+
def total_direct_visits_percentage
|
150
|
+
sprintf("%.1f%", (total_direct_visits / total_visits.to_f) * 100)
|
151
|
+
end
|
152
|
+
|
153
|
+
def total_organic_visits_percentage
|
154
|
+
sprintf("%.1f%", (total_organic_visits / total_visits.to_f) * 100)
|
155
|
+
end
|
156
|
+
|
157
|
+
def total_new_visits_percentage
|
158
|
+
sprintf("%.1f%", (total_new_visits / total_visits.to_f) * 100)
|
159
|
+
end
|
160
|
+
|
161
|
+
def total_ppc_visits_percentage
|
162
|
+
sprintf("%.1f%", (total_ppc_visits / total_visits.to_f) * 100)
|
163
|
+
end
|
164
|
+
|
165
|
+
def total_return_visits_percentage
|
166
|
+
sprintf("%.1f%", (total_return_visits / total_visits.to_f) * 100)
|
167
|
+
end
|
168
|
+
|
169
|
+
def total_search_visits_percentage
|
170
|
+
sprintf("%.1f%", (total_search_visits / total_visits.to_f) * 100)
|
171
|
+
end
|
172
|
+
|
173
|
+
def total_visits
|
174
|
+
@total_visits ||= self.sessions.count
|
175
|
+
end
|
176
|
+
|
177
|
+
# Inner stat classes
|
178
|
+
class KeywordStat
|
179
|
+
attr_accessor :keyword
|
180
|
+
attr_accessor :count
|
181
|
+
def initialize(args)
|
182
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class ReferrerStat
|
187
|
+
attr_accessor :referrer
|
188
|
+
attr_accessor :count
|
189
|
+
def initialize(args)
|
190
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
class Visit
|
195
|
+
attr_accessor :kind
|
196
|
+
attr_accessor :date
|
197
|
+
attr_accessor :stats
|
198
|
+
def initialize(args)
|
199
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
class VisitStat
|
204
|
+
attr_accessor :date
|
205
|
+
attr_accessor :visits
|
206
|
+
|
207
|
+
def initialize(args)
|
208
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
209
|
+
end
|
210
|
+
|
211
|
+
def short_date
|
212
|
+
self.date.to_s(:month_year)
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
module Mitamirri
|
2
|
+
|
3
|
+
class StatReport
|
4
|
+
|
5
|
+
# Search params
|
6
|
+
attr_accessor :action_kind
|
7
|
+
attr_accessor :site
|
8
|
+
attr_accessor :time_period
|
9
|
+
attr_accessor :visit_kind
|
10
|
+
|
11
|
+
# Initialization
|
12
|
+
|
13
|
+
def initialize(args = {})
|
14
|
+
args.each{|k,v| self.send("#{k}=",v) if self.respond_to?(k)}
|
15
|
+
self.time_period = args[:time_period] || 'past 6 months'
|
16
|
+
self.stats
|
17
|
+
end
|
18
|
+
|
19
|
+
def stats
|
20
|
+
if self.site && self.site.downcase != 'all sites'
|
21
|
+
@stats ||= TrackableStat.by_site(self.site).for_date_range(dates.first, dates.last.end_of_month)
|
22
|
+
else
|
23
|
+
@stats ||= TrackableStat.for_date_range(dates.first, dates.last.end_of_month)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Generate an array of dates spanning the appropriate time period.
|
28
|
+
def dates
|
29
|
+
return @dates if @dates
|
30
|
+
@dates = []
|
31
|
+
case self.time_period
|
32
|
+
when 'past month'
|
33
|
+
_increment = 1
|
34
|
+
when 'past 3 months'
|
35
|
+
_increment = 2
|
36
|
+
when 'past 6 months'
|
37
|
+
_increment = 5
|
38
|
+
when 'past 12 months'
|
39
|
+
_increment = 11
|
40
|
+
else 'all time'
|
41
|
+
_increment = 24
|
42
|
+
end
|
43
|
+
|
44
|
+
(1.._increment).each{ |i| @dates << (Time.zone.now - i.months).beginning_of_month }
|
45
|
+
@dates.reverse!
|
46
|
+
@dates << Time.zone.now.end_of_month
|
47
|
+
@dates
|
48
|
+
end
|
49
|
+
|
50
|
+
# Graphing data
|
51
|
+
def self.keywords_by_frequency(args)
|
52
|
+
TrackableSession.keywords_histogram(args).inject([]){|a,v| a << KeywordStat.new(:keyword => v[0], :count => v[1]); a}.sort{|a,b| a.count <=> b.count}.reverse[0..24]
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.top_referrers(args)
|
56
|
+
TrackableSession.referrers_histogram(args).inject([]){|a,v| a << ReferrerStat.new(:referrer => v[0], :count => v[1]); a}.sort{|a,b| a.count <=> b.count}.reverse[0..24]
|
57
|
+
end
|
58
|
+
|
59
|
+
def visits(args)
|
60
|
+
_visits = []
|
61
|
+
_visits << Visit.new(:kind => 'All', :stats => visit_stats(args))
|
62
|
+
_visits << Visit.new(:kind => 'Direct Traffic', :stats => visit_stats(args.merge(:visit_kind => 'direct')))
|
63
|
+
_visits << Visit.new(:kind => 'Referring Sites', :stats => visit_stats(args.merge(:visit_kind => 'natural')))
|
64
|
+
_visits << Visit.new(:kind => 'Organic Search', :stats => visit_stats(args.merge(:visit_kind => 'search')))
|
65
|
+
_visits << Visit.new(:kind => 'Paid Search', :stats => visit_stats(args.merge(:visit_kind => 'paid')))
|
66
|
+
_visits
|
67
|
+
end
|
68
|
+
|
69
|
+
def visit_stats(args)
|
70
|
+
_visit_stats = []
|
71
|
+
dates.each do |date|
|
72
|
+
_visit_stats << VisitStat.new(:date => date, :visits => TrackableSession.search_without_date(args).for_month(date).count)
|
73
|
+
end
|
74
|
+
_visit_stats
|
75
|
+
end
|
76
|
+
|
77
|
+
# Statistics
|
78
|
+
|
79
|
+
def average_pages_per_visit
|
80
|
+
sprintf("%.1f", total_pageviews / total_visits.to_f)
|
81
|
+
end
|
82
|
+
|
83
|
+
def average_time_on_site
|
84
|
+
average = total_duration / total_visits.to_f
|
85
|
+
average > 60 ? sprintf("%.1f minutes", average / 60.0) : sprintf("%.1f seconds", average)
|
86
|
+
end
|
87
|
+
|
88
|
+
def bounce_rate
|
89
|
+
bounce_rate = total_bounces / total_visits.to_f
|
90
|
+
sprintf("%.1f%", bounce_rate * 100)
|
91
|
+
end
|
92
|
+
|
93
|
+
def conversion_rate
|
94
|
+
conversion_rate = total_conversions / total_visits.to_f
|
95
|
+
sprintf("%.1f%", conversion_rate * 100)
|
96
|
+
end
|
97
|
+
|
98
|
+
def total_bounces
|
99
|
+
@total_bounces ||= stats.fields(:bounces).map{|s| s.bounces}.sum
|
100
|
+
end
|
101
|
+
|
102
|
+
def total_clickthroughs
|
103
|
+
@total_clickthroughs ||= stats.fields(:clickthroughs).map{|s| s.clickthroughs}.sum
|
104
|
+
end
|
105
|
+
|
106
|
+
def total_clickthroughs_percentage
|
107
|
+
sprintf("%.1f%", (total_clickthroughs / total_visits.to_f) * 100)
|
108
|
+
end
|
109
|
+
|
110
|
+
def total_conversions
|
111
|
+
@total_conversions ||= stats.fields(:conversions).map{|s| s.conversions}.sum
|
112
|
+
end
|
113
|
+
|
114
|
+
def total_direct_visits
|
115
|
+
@total_direct_visits ||= stats.fields(:direct_visits).map{|s| s.direct_visits}.sum
|
116
|
+
end
|
117
|
+
|
118
|
+
def total_direct_visits_percentage
|
119
|
+
sprintf("%.1f%", (total_direct_visits / total_visits.to_f) * 100)
|
120
|
+
end
|
121
|
+
|
122
|
+
def total_duration
|
123
|
+
@total_duration ||= stats.fields(:duration).map{|s| s.duration}.sum
|
124
|
+
end
|
125
|
+
|
126
|
+
def total_new_visits
|
127
|
+
@total_new_visits ||= stats.fields(:new_visits).map{|s| s.new_visits}.sum
|
128
|
+
end
|
129
|
+
|
130
|
+
def total_new_visits_percentage
|
131
|
+
sprintf("%.1f%", (total_new_visits / total_visits.to_f) * 100)
|
132
|
+
end
|
133
|
+
|
134
|
+
def total_organic_visits
|
135
|
+
@total_organic_visits ||= stats.fields(:organic_visits).map{|s| s.organic_visits}.sum
|
136
|
+
end
|
137
|
+
|
138
|
+
def total_organic_visits_percentage
|
139
|
+
sprintf("%.1f%", (total_organic_visits / total_visits.to_f) * 100)
|
140
|
+
end
|
141
|
+
|
142
|
+
def total_pageviews
|
143
|
+
@total_pageviews ||= stats.fields(:pageviews).map{|s| s.pageviews}.sum
|
144
|
+
end
|
145
|
+
|
146
|
+
def total_ppc_visits
|
147
|
+
@total_ppc_visits ||= stats.fields(:ppc_visits).map{|s| s.ppc_visits}.sum
|
148
|
+
end
|
149
|
+
|
150
|
+
def total_ppc_visits_percentage
|
151
|
+
sprintf("%.1f%", (total_ppc_visits / total_visits.to_f) * 100)
|
152
|
+
end
|
153
|
+
|
154
|
+
def total_return_visits
|
155
|
+
@total_return_visits ||= stats.fields(:return_visits).map{|s| s.return_visits}.sum
|
156
|
+
end
|
157
|
+
|
158
|
+
def total_return_visits_percentage
|
159
|
+
sprintf("%.1f%", (total_return_visits / total_visits.to_f) * 100)
|
160
|
+
end
|
161
|
+
|
162
|
+
def total_search_visits
|
163
|
+
@total_search_visits ||= stats.fields(:search_visits).map{|s| s.search_visits}.sum
|
164
|
+
end
|
165
|
+
|
166
|
+
def total_search_visits_percentage
|
167
|
+
sprintf("%.1f%", (total_search_visits / total_visits.to_f) * 100)
|
168
|
+
end
|
169
|
+
|
170
|
+
def total_visits
|
171
|
+
@total_visits ||= stats.fields(:visits).map{|s| s.visits}.sum
|
172
|
+
end
|
173
|
+
|
174
|
+
# Inner stat classes
|
175
|
+
class KeywordStat
|
176
|
+
attr_accessor :keyword
|
177
|
+
attr_accessor :count
|
178
|
+
def initialize(args)
|
179
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
class ReferrerStat
|
184
|
+
attr_accessor :referrer
|
185
|
+
attr_accessor :count
|
186
|
+
def initialize(args)
|
187
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
class Visit
|
192
|
+
attr_accessor :kind
|
193
|
+
attr_accessor :date
|
194
|
+
attr_accessor :stats
|
195
|
+
def initialize(args)
|
196
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class VisitStat
|
201
|
+
attr_accessor :date
|
202
|
+
attr_accessor :visits
|
203
|
+
|
204
|
+
def initialize(args)
|
205
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
206
|
+
end
|
207
|
+
|
208
|
+
def short_date
|
209
|
+
self.date.to_s(:month_year)
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# require 'mitamirri/tasks' to add tasks to the host application.
|
2
|
+
|
3
|
+
namespace :mitamirri do
|
4
|
+
|
5
|
+
desc "Sync migrations and other files from Mitamirri."
|
6
|
+
task :install do
|
7
|
+
dir = Gem.searcher.find('mitamirri').full_gem_path
|
8
|
+
system "rsync -ruv #{dir}/public/stylesheets public/"
|
9
|
+
system "rsync -ruv #{dir}/config/initializers/database.rb config/initializers/"
|
10
|
+
puts
|
11
|
+
puts "Mitamarri file sync complete."
|
12
|
+
puts "Run rake mitamirri:override to install local views and controllers to override default behaviour."
|
13
|
+
puts
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Copy default view and controller files from Mitamirri to the host application."
|
17
|
+
task :override do
|
18
|
+
dir = Gem.searcher.find('mitamirri').full_gem_path
|
19
|
+
system "rsync -ruv #{dir}/app/controllers/trackable_actions_controller.rb app/controllers/"
|
20
|
+
system "rsync -ruv #{dir}/app/controllers/trackable_sessions_controller.rb app/controllers/"
|
21
|
+
system "rsync -ruv #{dir}/app/views/trackable_sessions app/views/"
|
22
|
+
puts
|
23
|
+
puts "Mitamarri views and controllers copied and ready to override."
|
24
|
+
puts
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Mitamirri
|
2
|
+
|
3
|
+
class VisitorProfileReport
|
4
|
+
|
5
|
+
# Search params
|
6
|
+
attr_accessor :action_kind
|
7
|
+
attr_accessor :site
|
8
|
+
attr_accessor :time_period
|
9
|
+
attr_accessor :visit_kind
|
10
|
+
|
11
|
+
# Initialization
|
12
|
+
|
13
|
+
def initialize(args = {})
|
14
|
+
args.each{|k,v| self.send("#{k}=",v) if self.respond_to?(k)}
|
15
|
+
self.time_period = args[:time_period] || 'past 6 months'
|
16
|
+
self.sessions
|
17
|
+
end
|
18
|
+
|
19
|
+
def criteria
|
20
|
+
_criteria = self.sessions.to_hash
|
21
|
+
_criteria.delete(:sort)
|
22
|
+
_criteria
|
23
|
+
end
|
24
|
+
|
25
|
+
def sessions
|
26
|
+
@sessions ||= TrackableSession.search(
|
27
|
+
:action_kind => self.action_kind,
|
28
|
+
:site => self.site,
|
29
|
+
:time_period => self.time_period,
|
30
|
+
:visit_kind => self.visit_kind
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Generate an array of dates spanning the appropriate time period.
|
35
|
+
def dates
|
36
|
+
return @dates if @dates
|
37
|
+
@dates = []
|
38
|
+
case self.time_period
|
39
|
+
when 'past month'
|
40
|
+
_increment = 1
|
41
|
+
when 'past 3 months'
|
42
|
+
_increment = 2
|
43
|
+
when 'past 6 months'
|
44
|
+
_increment = 5
|
45
|
+
when 'past 12 months'
|
46
|
+
_increment = 11
|
47
|
+
else 'all time'
|
48
|
+
_increment = 24
|
49
|
+
end
|
50
|
+
|
51
|
+
(1.._increment).each{ |i| @dates << (Time.zone.now - i.months).beginning_of_month }
|
52
|
+
@dates.reverse!
|
53
|
+
@dates << Time.zone.now.end_of_month
|
54
|
+
@dates
|
55
|
+
end
|
56
|
+
|
57
|
+
# Graphing data
|
58
|
+
|
59
|
+
def locations_by_frequency(args)
|
60
|
+
TrackableSession.locations_histogram(args).inject([]){|a,v| a << LocationStat.new(:name => v[0], :count => v[1]); a}.sort{|a,b| a.count <=> b.count}.reverse[0..24]
|
61
|
+
end
|
62
|
+
|
63
|
+
def user_agents_by_frequency(args)
|
64
|
+
TrackableSession.user_agents_histogram(args).inject([]){|a,v| a << UserAgentStat.new(:user_agent => v[0], :count => v[1]); a}.sort{|a,b| a.count <=> b.count}.reverse[0..24]
|
65
|
+
end
|
66
|
+
|
67
|
+
# Inner stat classes
|
68
|
+
|
69
|
+
class Visitor
|
70
|
+
attr_accessor :user_agent_stats
|
71
|
+
attr_accessor :location_stats
|
72
|
+
def initialize(args)
|
73
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class UserAgentStat
|
78
|
+
attr_accessor :user_agent
|
79
|
+
attr_accessor :count
|
80
|
+
def initialize(args)
|
81
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class LocationStat
|
86
|
+
|
87
|
+
attr_accessor :name
|
88
|
+
attr_accessor :latitude
|
89
|
+
attr_accessor :longitude
|
90
|
+
attr_accessor :count
|
91
|
+
|
92
|
+
def initialize(args)
|
93
|
+
args.each{ |k,v| self.send("#{k}=", v) }
|
94
|
+
geocode
|
95
|
+
end
|
96
|
+
|
97
|
+
def geocode
|
98
|
+
if _location = TrackableLocation.where(:location => self.name).first
|
99
|
+
self.latitude = _location.latitude
|
100
|
+
self.longitude = _location.longitude
|
101
|
+
else
|
102
|
+
begin
|
103
|
+
_coords = Geokit::Geocoders::MultiGeocoder.geocode(self.name)
|
104
|
+
rescue
|
105
|
+
end
|
106
|
+
if _coords && _coords.success
|
107
|
+
self.latitude = _coords.lat
|
108
|
+
self.longitude = _coords.lng
|
109
|
+
TrackableLocation.create(:location => self.name, :latitude => self.latitude, :longitude => self.longitude)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def geocoded?
|
115
|
+
self.latitude && self.longitude
|
116
|
+
end
|
117
|
+
|
118
|
+
def sanitized_name
|
119
|
+
self.name.gsub(/'/,"\\'")
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
data/lib/mitamirri.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Mitamirri
|
2
|
+
require 'mitamirri'
|
3
|
+
require 'mitamirri/session_report'
|
4
|
+
|
5
|
+
# Detects the ability of the host application to output reports to PDF format.
|
6
|
+
def self.pdf_output_enabled?
|
7
|
+
@pdf_output_enabled ||= false
|
8
|
+
begin
|
9
|
+
@pdf_output_enabled = Rails.configuration.middleware.include?('PDFKit::Middleware')
|
10
|
+
rescue
|
11
|
+
end
|
12
|
+
@pdf_output_enabled
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/log/development.log
ADDED
File without changes
|
data/log/production.log
ADDED
File without changes
|
data/log/server.log
ADDED
File without changes
|
data/log/test.log
ADDED
File without changes
|