rugalytics 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.
- data/CHANGELOG +3 -0
- data/LICENSE +20 -0
- data/Manifest +24 -0
- data/README +128 -0
- data/README.rdoc +128 -0
- data/lib/rugalytics/account.rb +42 -0
- data/lib/rugalytics/analytics.rb +0 -0
- data/lib/rugalytics/connection.rb +39 -0
- data/lib/rugalytics/graph.rb +31 -0
- data/lib/rugalytics/item.rb +19 -0
- data/lib/rugalytics/profile.rb +116 -0
- data/lib/rugalytics/report.rb +97 -0
- data/lib/rugalytics.rb +50 -0
- data/rugalytics.gemspec +57 -0
- data/spec/fixtures/analytics_account_find_all.html +296 -0
- data/spec/fixtures/analytics_profile_find_all.html +296 -0
- data/spec/fixtures/dashboard_report_webgroup.xml +2705 -0
- data/spec/lib/rugalytics/account_spec.rb +86 -0
- data/spec/lib/rugalytics/graph_spec.rb +43 -0
- data/spec/lib/rugalytics/item_spec.rb +23 -0
- data/spec/lib/rugalytics/profile_spec.rb +104 -0
- data/spec/lib/rugalytics/report_spec.rb +153 -0
- data/spec/lib/rugalytics_spec.rb +34 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +9 -0
- metadata +121 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Rob McKinnon
|
2
|
+
Copyright (c) 2007 John Nunemaker
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
6
|
+
in the Software without restriction, including without limitation the rights
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
9
|
+
furnished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be included in
|
12
|
+
all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
THE SOFTWARE.
|
data/Manifest
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
lib/rugalytics/account.rb
|
3
|
+
lib/rugalytics/analytics.rb
|
4
|
+
lib/rugalytics/connection.rb
|
5
|
+
lib/rugalytics/graph.rb
|
6
|
+
lib/rugalytics/item.rb
|
7
|
+
lib/rugalytics/profile.rb
|
8
|
+
lib/rugalytics/report.rb
|
9
|
+
lib/rugalytics.rb
|
10
|
+
LICENSE
|
11
|
+
Manifest
|
12
|
+
README
|
13
|
+
README.rdoc
|
14
|
+
spec/fixtures/analytics_account_find_all.html
|
15
|
+
spec/fixtures/analytics_profile_find_all.html
|
16
|
+
spec/fixtures/dashboard_report_webgroup.xml
|
17
|
+
spec/lib/rugalytics/account_spec.rb
|
18
|
+
spec/lib/rugalytics/graph_spec.rb
|
19
|
+
spec/lib/rugalytics/item_spec.rb
|
20
|
+
spec/lib/rugalytics/profile_spec.rb
|
21
|
+
spec/lib/rugalytics/report_spec.rb
|
22
|
+
spec/lib/rugalytics_spec.rb
|
23
|
+
spec/spec.opts
|
24
|
+
spec/spec_helper.rb
|
data/README
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
Rugalytics is a Ruby API for Google Analytics.
|
2
|
+
|
3
|
+
= Warning - API under development
|
4
|
+
|
5
|
+
The Rugalytics API is in early development so it may change slightly over time.
|
6
|
+
It should be in working order, so please give it a test spin!
|
7
|
+
|
8
|
+
The source code is hosted at github. Feel free to fork the code if you have
|
9
|
+
something to contribute:
|
10
|
+
|
11
|
+
http://github.com/robmckinnon/rugalytics
|
12
|
+
|
13
|
+
|
14
|
+
== Install as a Gem
|
15
|
+
|
16
|
+
Should be up at rubyforge, so to install:
|
17
|
+
|
18
|
+
sudo gem install rugalytics
|
19
|
+
|
20
|
+
|
21
|
+
== Authenticate
|
22
|
+
|
23
|
+
Login with your Google Analytics user name and password:
|
24
|
+
|
25
|
+
require 'rugalytics'
|
26
|
+
|
27
|
+
Rugalytics.login 'username', 'password'
|
28
|
+
|
29
|
+
|
30
|
+
== Obtain Profile
|
31
|
+
|
32
|
+
Get profile using account name and profile name:
|
33
|
+
|
34
|
+
profile = Rugalytics.find_profile('your_site.com', 'blog.your_site.com')
|
35
|
+
|
36
|
+
|
37
|
+
If account name and profile name are the same:
|
38
|
+
|
39
|
+
profile = Rugalytics.find_profile('your_site.com')
|
40
|
+
|
41
|
+
|
42
|
+
== Get Profile Summary Statistics
|
43
|
+
|
44
|
+
Obtaining page views:
|
45
|
+
|
46
|
+
profile.pageviews # default period is one month ending today
|
47
|
+
=> 160600
|
48
|
+
|
49
|
+
profile.pageviews :from=>'2007-01-01'
|
50
|
+
=> 2267550
|
51
|
+
|
52
|
+
profile.pageviews :from=>'2007-01-01', :to=>'2007-01-02'
|
53
|
+
=> 24980
|
54
|
+
|
55
|
+
The +pageviews+ method is doing this under the hood:
|
56
|
+
|
57
|
+
report = profile.load_report('Pageviews', :from=>'2007-01-01', :to=>'2007-01-02')
|
58
|
+
|
59
|
+
report.page_views_total
|
60
|
+
=> 24980
|
61
|
+
|
62
|
+
And under the hood, the +page_views_total+ method is actually calling:
|
63
|
+
|
64
|
+
report.page_views_graph.sum_of_points
|
65
|
+
=> 24980
|
66
|
+
|
67
|
+
== Load a Report
|
68
|
+
|
69
|
+
The report name, e.g. 'Pageviews' or 'TrafficSources', is the rpt parameter from
|
70
|
+
the Google Analytics URL for a CSV report export, e.g.:
|
71
|
+
|
72
|
+
https://www.google.com/analytics/reporting/export?fmt=2&...&&rpt=TrafficSourcesReport&...
|
73
|
+
|
74
|
+
From a page on the Analytics website, you can find the CSV URL by clicking on
|
75
|
+
the Export tab, and then mousing over the CSV option.
|
76
|
+
|
77
|
+
Let's load the TrafficSources report:
|
78
|
+
|
79
|
+
report = profile.load_report 'TrafficSources'
|
80
|
+
|
81
|
+
report.report_name
|
82
|
+
=> "Traffic Sources Overview"
|
83
|
+
|
84
|
+
report.start_date
|
85
|
+
=> "28 May 2008"
|
86
|
+
|
87
|
+
report.end_date
|
88
|
+
=> "4 June 2008"
|
89
|
+
|
90
|
+
report.source_items.collect{|s| "#{s.sources}: #{s.visits}"}.first
|
91
|
+
=> "google (organic): 15210"
|
92
|
+
|
93
|
+
report.keyword_items.collect{|k| "#{k.keywords}: #{k.visits}"}[1]
|
94
|
+
=> "oecd nz report summary 2007: 14"
|
95
|
+
|
96
|
+
Let's try another report, VisitorsOverview:
|
97
|
+
|
98
|
+
report = profile.load_report 'VisitorsOverview'
|
99
|
+
|
100
|
+
report.browser_items[1]
|
101
|
+
=> #<Rugalytics::Item @percentage_visits="0.18", @visits="3140", @browser="Firefox">
|
102
|
+
|
103
|
+
report.connection_speed_items[3]
|
104
|
+
=> #<Rugalytics::Item @connection_speed="Dialup", @percentage_visits="0.06340057402849197", @visits="1100">
|
105
|
+
|
106
|
+
Let's now grab 100 lines of the Networks report:
|
107
|
+
|
108
|
+
report = profile.load_report 'Networks', :rows=>100
|
109
|
+
|
110
|
+
report.items.size
|
111
|
+
=> 100
|
112
|
+
|
113
|
+
report.items.first.network_location
|
114
|
+
=> "telecom xtra"
|
115
|
+
|
116
|
+
|
117
|
+
==Acknowledgements
|
118
|
+
|
119
|
+
Rugalytics started life as a fork of jnunemaker's Statwhore. As the code and
|
120
|
+
project scope began to diverge significantly from Statwhore, a new project was
|
121
|
+
initiated.
|
122
|
+
|
123
|
+
Rugalytics makes use of the Morph gem to emerge Ruby class definitions at
|
124
|
+
runtime based on the contents of the CSV reports from Google Analytics.
|
125
|
+
|
126
|
+
==License
|
127
|
+
|
128
|
+
See LICENSE for the terms of this software.
|
data/README.rdoc
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
Rugalytics is a Ruby API for Google Analytics.
|
2
|
+
|
3
|
+
= Warning - API under development
|
4
|
+
|
5
|
+
The Rugalytics API is in early development so it may change slightly over time.
|
6
|
+
It should be in working order, so please give it a test spin!
|
7
|
+
|
8
|
+
The source code is hosted at github. Feel free to fork the code if you have
|
9
|
+
something to contribute:
|
10
|
+
|
11
|
+
http://github.com/robmckinnon/rugalytics
|
12
|
+
|
13
|
+
|
14
|
+
== Install as a Gem
|
15
|
+
|
16
|
+
Should be up at rubyforge, so to install:
|
17
|
+
|
18
|
+
sudo gem install rugalytics
|
19
|
+
|
20
|
+
|
21
|
+
== Authenticate
|
22
|
+
|
23
|
+
Login with your Google Analytics user name and password:
|
24
|
+
|
25
|
+
require 'rugalytics'
|
26
|
+
|
27
|
+
Rugalytics.login 'username', 'password'
|
28
|
+
|
29
|
+
|
30
|
+
== Obtain Profile
|
31
|
+
|
32
|
+
Get profile using account name and profile name:
|
33
|
+
|
34
|
+
profile = Rugalytics.find_profile('your_site.com', 'blog.your_site.com')
|
35
|
+
|
36
|
+
|
37
|
+
If account name and profile name are the same:
|
38
|
+
|
39
|
+
profile = Rugalytics.find_profile('your_site.com')
|
40
|
+
|
41
|
+
|
42
|
+
== Get Profile Summary Statistics
|
43
|
+
|
44
|
+
Obtaining page views:
|
45
|
+
|
46
|
+
profile.pageviews # default period is one month ending today
|
47
|
+
=> 160600
|
48
|
+
|
49
|
+
profile.pageviews :from=>'2007-01-01'
|
50
|
+
=> 2267550
|
51
|
+
|
52
|
+
profile.pageviews :from=>'2007-01-01', :to=>'2007-01-02'
|
53
|
+
=> 24980
|
54
|
+
|
55
|
+
The +pageviews+ method is doing this under the hood:
|
56
|
+
|
57
|
+
report = profile.load_report('Pageviews', :from=>'2007-01-01', :to=>'2007-01-02')
|
58
|
+
|
59
|
+
report.page_views_total
|
60
|
+
=> 24980
|
61
|
+
|
62
|
+
And under the hood, the +page_views_total+ method is actually calling:
|
63
|
+
|
64
|
+
report.page_views_graph.sum_of_points
|
65
|
+
=> 24980
|
66
|
+
|
67
|
+
== Load a Report
|
68
|
+
|
69
|
+
The report name, e.g. 'Pageviews' or 'TrafficSources', is the rpt parameter from
|
70
|
+
the Google Analytics URL for a CSV report export, e.g.:
|
71
|
+
|
72
|
+
https://www.google.com/analytics/reporting/export?fmt=2&...&&rpt=TrafficSourcesReport&...
|
73
|
+
|
74
|
+
From a page on the Analytics website, you can find the CSV URL by clicking on
|
75
|
+
the Export tab, and then mousing over the CSV option.
|
76
|
+
|
77
|
+
Let's load the TrafficSources report:
|
78
|
+
|
79
|
+
report = profile.load_report 'TrafficSources'
|
80
|
+
|
81
|
+
report.report_name
|
82
|
+
=> "Traffic Sources Overview"
|
83
|
+
|
84
|
+
report.start_date
|
85
|
+
=> "28 May 2008"
|
86
|
+
|
87
|
+
report.end_date
|
88
|
+
=> "4 June 2008"
|
89
|
+
|
90
|
+
report.source_items.collect{|s| "#{s.sources}: #{s.visits}"}.first
|
91
|
+
=> "google (organic): 15210"
|
92
|
+
|
93
|
+
report.keyword_items.collect{|k| "#{k.keywords}: #{k.visits}"}[1]
|
94
|
+
=> "oecd nz report summary 2007: 14"
|
95
|
+
|
96
|
+
Let's try another report, VisitorsOverview:
|
97
|
+
|
98
|
+
report = profile.load_report 'VisitorsOverview'
|
99
|
+
|
100
|
+
report.browser_items[1]
|
101
|
+
=> #<Rugalytics::Item @percentage_visits="0.18", @visits="3140", @browser="Firefox">
|
102
|
+
|
103
|
+
report.connection_speed_items[3]
|
104
|
+
=> #<Rugalytics::Item @connection_speed="Dialup", @percentage_visits="0.06340057402849197", @visits="1100">
|
105
|
+
|
106
|
+
Let's now grab 100 lines of the Networks report:
|
107
|
+
|
108
|
+
report = profile.load_report 'Networks', :rows=>100
|
109
|
+
|
110
|
+
report.items.size
|
111
|
+
=> 100
|
112
|
+
|
113
|
+
report.items.first.network_location
|
114
|
+
=> "telecom xtra"
|
115
|
+
|
116
|
+
|
117
|
+
==Acknowledgements
|
118
|
+
|
119
|
+
Rugalytics started life as a fork of jnunemaker's Statwhore. As the code and
|
120
|
+
project scope began to diverge significantly from Statwhore, a new project was
|
121
|
+
initiated.
|
122
|
+
|
123
|
+
Rugalytics makes use of the Morph gem to emerge Ruby class definitions at
|
124
|
+
runtime based on the contents of the CSV reports from Google Analytics.
|
125
|
+
|
126
|
+
==License
|
127
|
+
|
128
|
+
See LICENSE for the terms of this software.
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Rugalytics
|
2
|
+
class Account < ::Google::Base
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def find_all
|
6
|
+
doc = Hpricot::XML get('https://www.google.com:443/analytics/home/')
|
7
|
+
(doc/'select[@name=account_list] option').inject([]) do |accounts, option|
|
8
|
+
account_id = option['value'].to_i
|
9
|
+
accounts << new(:account_id => account_id, :name => option.inner_html) if account_id > 0
|
10
|
+
accounts
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def find account_id_or_name
|
15
|
+
matchs = find_all.select{|a| a.name == account_id_or_name || a.account_id.to_s == account_id_or_name.to_s }
|
16
|
+
matchs.empty? ? nil : matchs.first
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_accessor :name, :account_id
|
21
|
+
|
22
|
+
def initialize(attrs)
|
23
|
+
@name = attrs[:name]
|
24
|
+
@account_id = attrs[:account_id]
|
25
|
+
end
|
26
|
+
|
27
|
+
def profiles(force=false)
|
28
|
+
if force || @profiles.nil?
|
29
|
+
@profiles = Profile.find_all(account_id)
|
30
|
+
end
|
31
|
+
@profiles
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_profile profile_id_or_name
|
35
|
+
profiles.detect { |p| p.profile_id.to_s == profile_id_or_name.to_s || p.name == profile_id_or_name }
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
"#{name} (#{account_id})"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
File without changes
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
|
3
|
+
module Rugalytics
|
4
|
+
class Connection
|
5
|
+
def initialize(base_url, args = {})
|
6
|
+
@base_url = base_url
|
7
|
+
@username = args[:username]
|
8
|
+
@password = args[:password]
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(resource, args = nil)
|
12
|
+
request(resource, "get", args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def post(resource, args = nil)
|
16
|
+
request(resource, "post", args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def request(resource, method = "get", args = nil)
|
20
|
+
url = URI.join(@base_url, resource)
|
21
|
+
url.query = args.map { |k,v| "%s=%s" % [URI.encode(k.to_s), URI.encode(v.to_s)] }.join("&") if args
|
22
|
+
|
23
|
+
case method
|
24
|
+
when "get"
|
25
|
+
req = Net::HTTP::Get.new(url.request_uri)
|
26
|
+
when "post"
|
27
|
+
req = Net::HTTP::Post.new(url.request_uri)
|
28
|
+
end
|
29
|
+
|
30
|
+
req.basic_auth(@username, @password) if @username && @password
|
31
|
+
|
32
|
+
http = Net::HTTP.new(url.host, url.port)
|
33
|
+
http.use_ssl = (url.port == 443)
|
34
|
+
|
35
|
+
res = http.start() { |conn| conn.request(req) }
|
36
|
+
res.body
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Rugalytics
|
2
|
+
class Graph
|
3
|
+
|
4
|
+
attr_reader :name, :points, :from, :to
|
5
|
+
|
6
|
+
def initialize name, period, points
|
7
|
+
@name = name
|
8
|
+
@period = period
|
9
|
+
dates = @period.split('-')
|
10
|
+
unless dates.empty?
|
11
|
+
@from = Date.parse(dates[0].strip)
|
12
|
+
@to = Date.parse(dates[1].strip)
|
13
|
+
end
|
14
|
+
@points = points
|
15
|
+
end
|
16
|
+
|
17
|
+
def sum_of_points
|
18
|
+
points.sum
|
19
|
+
end
|
20
|
+
|
21
|
+
def points_by_day
|
22
|
+
by_day = []
|
23
|
+
index = 0
|
24
|
+
from.upto(to) do |date|
|
25
|
+
by_day << [date, points[index] ]
|
26
|
+
index = index.next
|
27
|
+
end
|
28
|
+
by_day
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rugalytics
|
2
|
+
|
3
|
+
class Item
|
4
|
+
include Morph
|
5
|
+
|
6
|
+
def initialize attributes, values, base_url
|
7
|
+
attributes.each_with_index do |attribute, index|
|
8
|
+
attribute.sub!('$','dollar')
|
9
|
+
attribute.sub!('/',' per ')
|
10
|
+
attribute.sub!('.',' ')
|
11
|
+
value = values[index]
|
12
|
+
morph(attribute, value)
|
13
|
+
end
|
14
|
+
|
15
|
+
self.url = "http://#{base_url}#{url}" if respond_to?(:url)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Rugalytics
|
2
|
+
class Profile < ::Google::Base
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def find_all(account_id)
|
6
|
+
doc = Hpricot::XML get("https://www.google.com:443/analytics/home/admin?scid=#{account_id}")
|
7
|
+
(doc/'select[@name=profile_list] option').inject([]) do |profiles, option|
|
8
|
+
profile_id = option['value'].to_i
|
9
|
+
profiles << Profile.new(:account_id => account_id, :profile_id => profile_id, :name => option.inner_html) if profile_id > 0
|
10
|
+
profiles
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(account_id_or_name, profile_id_or_name=nil)
|
15
|
+
profile_id_or_name = account_id_or_name unless profile_id_or_name
|
16
|
+
account = Account.find(account_id_or_name)
|
17
|
+
account ? account.find_profile(profile_id_or_name) : nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :account_id, :name, :profile_id
|
22
|
+
|
23
|
+
def initialize(attrs)
|
24
|
+
raise ArgumentError, ":profile_id is required" unless attrs.has_key?(:profile_id)
|
25
|
+
@account_id = attrs[:account_id] if attrs.has_key?(:account_id)
|
26
|
+
@name = attrs[:name] if attrs.has_key?(:name)
|
27
|
+
@profile_id = attrs[:profile_id] if attrs.has_key?(:profile_id)
|
28
|
+
end
|
29
|
+
|
30
|
+
def load_report(name, options={})
|
31
|
+
report = Rugalytics::Report.new report(options.merge({:report=>name}))
|
32
|
+
puts report.attribute_names
|
33
|
+
report
|
34
|
+
end
|
35
|
+
|
36
|
+
def report(options={})
|
37
|
+
options.reverse_merge!({
|
38
|
+
:report => 'Dashboard',
|
39
|
+
:from => Time.now.utc - 7.days,
|
40
|
+
:to => Time.now.utc,
|
41
|
+
:tab => 0,
|
42
|
+
:format => FORMAT_CSV,
|
43
|
+
:rows => 50,
|
44
|
+
:compute => 'average',
|
45
|
+
:gdfmt => 'nth_day',
|
46
|
+
:view => 0
|
47
|
+
})
|
48
|
+
options[:from] = ensure_datetime_in_google_format(options[:from])
|
49
|
+
options[:to] = ensure_datetime_in_google_format(options[:to])
|
50
|
+
|
51
|
+
params = {
|
52
|
+
:pdr => "#{options[:from]}-#{options[:to]}",
|
53
|
+
:rpt => "#{options[:report]}Report",
|
54
|
+
:cmp => options[:compute],
|
55
|
+
:fmt => options[:format],
|
56
|
+
:view => options[:view],
|
57
|
+
:tab => options[:tab],
|
58
|
+
:trows=> options[:rows],
|
59
|
+
:gdfmt=> options[:gdfmt],
|
60
|
+
:id => profile_id,
|
61
|
+
}
|
62
|
+
puts params.inspect
|
63
|
+
# https://www.google.com/analytics/reporting/export?fmt=2&id=1712313&pdr=20080504-20080603&cmp=average&&rpt=PageviewsReport
|
64
|
+
self.class.get("https://google.com/analytics/reporting/export", :query_hash => params)
|
65
|
+
end
|
66
|
+
|
67
|
+
def pageviews(options={})
|
68
|
+
load_report('Pageviews',options).page_views_total
|
69
|
+
end
|
70
|
+
|
71
|
+
def pageviews_by_day(options={})
|
72
|
+
load_report('Pageviews',options).page_views_by_day
|
73
|
+
end
|
74
|
+
|
75
|
+
def visits(options={})
|
76
|
+
load_report('Visits',options).visits_total
|
77
|
+
end
|
78
|
+
|
79
|
+
def visits_by_day(options={})
|
80
|
+
load_report('Visits',options).visits_by_day
|
81
|
+
end
|
82
|
+
|
83
|
+
# takes a Date, Time or String
|
84
|
+
def ensure_datetime_in_google_format(time)
|
85
|
+
time = Date.parse(time) if time.is_a?(String)
|
86
|
+
time.is_a?(Time) || time.is_a?(Date) ? time.strftime('%Y%m%d') : time
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_s
|
90
|
+
"#{name} (#{profile_id})"
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def get_item_summary_by_message(options={})
|
95
|
+
raise ArgumentError unless options.has_key?(:message)
|
96
|
+
message = options.delete(:message).to_s.capitalize
|
97
|
+
response = report(options.merge({:report => 'Dashboard'}))
|
98
|
+
doc = Hpricot::XML(response)
|
99
|
+
pageviews = (doc/:ItemSummary).detect { |summary| summary.at('Message').inner_html == message }
|
100
|
+
pageviews && pageviews.at('SummaryValue') ? pageviews.at('SummaryValue').inner_html.gsub(/\D/, '').to_i : 0
|
101
|
+
end
|
102
|
+
|
103
|
+
def get_serie_by_label(options={})
|
104
|
+
raise ArgumentError unless options.has_key?(:label)
|
105
|
+
label = options.delete(:label).to_s.capitalize
|
106
|
+
response = report(options.merge({:report => 'Dashboard'}))
|
107
|
+
doc = Hpricot::XML(response)
|
108
|
+
serie = (doc/:Serie).detect { |serie| serie.at('Label').inner_html == label }
|
109
|
+
if serie
|
110
|
+
(serie/:Point).inject([]) { |collection, point| collection << [Date.parse(point.at('Label').inner_html), point.at('Value').inner_html.to_i] }
|
111
|
+
else
|
112
|
+
[]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Rugalytics
|
2
|
+
|
3
|
+
class Report
|
4
|
+
|
5
|
+
include MorphLessMethodMissing
|
6
|
+
|
7
|
+
attr_reader :base_url, :report_name, :start_date, :end_date
|
8
|
+
|
9
|
+
def initialize csv=''
|
10
|
+
return if csv.empty?
|
11
|
+
lines = csv.split("\n")
|
12
|
+
set_attributes lines
|
13
|
+
handle_graphs lines
|
14
|
+
handle_tables lines
|
15
|
+
end
|
16
|
+
|
17
|
+
def attribute_names
|
18
|
+
Report.morph_methods.select {|m| m[/[a-z]$/]}.select {|m| send(m.to_sym)}
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing symbol, *args
|
22
|
+
|
23
|
+
if is_writer = symbol.to_s[/=$/]
|
24
|
+
morph_method_missing(symbol, *args)
|
25
|
+
|
26
|
+
elsif symbol.to_s.match /(.*)_(total|by_day)/
|
27
|
+
graph = "#{$1}_graph".to_sym
|
28
|
+
|
29
|
+
if respond_to?(graph)
|
30
|
+
$2 == 'total' ? send(graph).sum_of_points : send(graph).points_by_day
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def set_attributes lines
|
43
|
+
@base_url = lines[1]
|
44
|
+
@report_name = lines[2].chomp(',')
|
45
|
+
dates = lines[3].split(',')
|
46
|
+
@start_date = dates[0]
|
47
|
+
@end_date = dates[1]
|
48
|
+
end
|
49
|
+
|
50
|
+
def handle_graphs lines
|
51
|
+
index = 5
|
52
|
+
while index < lines.size
|
53
|
+
while (lines[index][/^# Graph/].nil? || lines[index].strip.size == 0)
|
54
|
+
index = index.next
|
55
|
+
return if index == lines.size
|
56
|
+
end
|
57
|
+
index = index + 2
|
58
|
+
period = lines[index]
|
59
|
+
index = index.next
|
60
|
+
name = lines[index]
|
61
|
+
index = index.next
|
62
|
+
|
63
|
+
points = []
|
64
|
+
while (point = lines[index]) && point.strip.size > 0
|
65
|
+
points << point.tr('",','').to_i
|
66
|
+
index = index.next
|
67
|
+
end
|
68
|
+
|
69
|
+
graph = Graph.new name, period, points
|
70
|
+
morph("#{name} graph", graph)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def handle_tables lines
|
75
|
+
index = 5
|
76
|
+
while index < lines.size
|
77
|
+
while (lines[index][/^# .*Table/].nil? || lines[index].strip.size == 0)
|
78
|
+
index = index.next
|
79
|
+
return if index == lines.size
|
80
|
+
end
|
81
|
+
type = lines[index][/^# (.*)MiniTable/,1]
|
82
|
+
index = index + 2
|
83
|
+
attributes = lines[index].split(',')
|
84
|
+
index = index.next
|
85
|
+
|
86
|
+
items = []
|
87
|
+
items_attribute = (type && type.size > 0) ? "#{type.gsub(/([a-z])([A-Z])/, '\1_\2').downcase}_items" : 'items'
|
88
|
+
morph(items_attribute, items)
|
89
|
+
|
90
|
+
while (values = lines[index]) && values[/^# -/].nil? && values.strip.size > 0
|
91
|
+
items << Item.new(attributes, values.split(','), base_url)
|
92
|
+
index = index.next
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|