chrisle-gattica 0.6.3
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/Gemfile +6 -0
- data/Gemfile.lock +21 -0
- data/LICENSE +22 -0
- data/README.md +506 -0
- data/Rakefile +24 -0
- data/VERSION.yml +5 -0
- data/chrisle-gattica.gemspec +68 -0
- data/lib/gattica.rb +35 -0
- data/lib/gattica/account.rb +54 -0
- data/lib/gattica/auth.rb +47 -0
- data/lib/gattica/convertible.rb +39 -0
- data/lib/gattica/data_point.rb +73 -0
- data/lib/gattica/data_set.rb +57 -0
- data/lib/gattica/engine.rb +295 -0
- data/lib/gattica/exceptions.rb +21 -0
- data/lib/gattica/goals.rb +33 -0
- data/lib/gattica/hash_extensions.rb +20 -0
- data/lib/gattica/profiles.rb +33 -0
- data/lib/gattica/segment.rb +17 -0
- data/lib/gattica/settings.rb +34 -0
- data/lib/gattica/user.rb +31 -0
- data/test/helper.rb +14 -0
- data/test/settings.rb +28 -0
- data/test/suite.rb +6 -0
- data/test/test_engine.rb +48 -0
- data/test/test_results.rb +23 -0
- data/test/test_user.rb +24 -0
- metadata +109 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module GatticaError
|
2
|
+
# user errors
|
3
|
+
class InvalidEmail < StandardError; end;
|
4
|
+
class InvalidPassword < StandardError; end;
|
5
|
+
# authentication errors
|
6
|
+
class CouldNotAuthenticate < StandardError; end;
|
7
|
+
class NoLoginOrToken < StandardError; end;
|
8
|
+
class InvalidToken < StandardError; end;
|
9
|
+
# profile errors
|
10
|
+
class InvalidProfileId < StandardError; end;
|
11
|
+
# search errors
|
12
|
+
class TooManyDimensions < StandardError; end;
|
13
|
+
class TooManyMetrics < StandardError; end;
|
14
|
+
class InvalidSort < StandardError; end;
|
15
|
+
class InvalidFilter < StandardError; end;
|
16
|
+
class MissingStartDate < StandardError; end;
|
17
|
+
class MissingEndDate < StandardError; end;
|
18
|
+
# errors from Analytics
|
19
|
+
class AnalyticsError < StandardError; end;
|
20
|
+
class UnknownAnalyticsError < StandardError; end;
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
module Gattica
|
5
|
+
class Goals
|
6
|
+
include Convertible
|
7
|
+
|
8
|
+
attr_reader :id, :updated, :title, :table_id, :account_id, :account_name,
|
9
|
+
:profile_id, :web_property_id, :goals
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(xml)
|
13
|
+
@id = xml.at(:id).inner_html
|
14
|
+
@updated = DateTime.parse(xml.at(:updated).inner_html)
|
15
|
+
@account_id = xml.at("dxp:property[@name='ga:accountId']").attributes['value'].to_i
|
16
|
+
@account_name = xml.at("dxp:property[@name='ga:accountName']").attributes['value']
|
17
|
+
|
18
|
+
@title = xml.at("dxp:property[@name='ga:profileName']").attributes['value']
|
19
|
+
@table_id = xml.at("dxp:property[@name='dxp:tableId']").attributes['value']
|
20
|
+
@profile_id = xml.at("dxp:property[@name='ga:profileId']").attributes['value'].to_i
|
21
|
+
@web_property_id = xml.at("dxp:property[@name='ga:webPropertyId']").attributes['value']
|
22
|
+
|
23
|
+
# @goals = xml.search('ga:goal').collect do |goal| {
|
24
|
+
# :active => goal.attributes['active'],
|
25
|
+
# :name => goal.attributes['name'],
|
26
|
+
# :number => goal.attributes['number'].to_i,
|
27
|
+
# :value => goal.attributes['value'].to_f,
|
28
|
+
# }
|
29
|
+
# end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Gattica
|
2
|
+
module HashExtensions
|
3
|
+
|
4
|
+
def to_query
|
5
|
+
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
6
|
+
self.collect do |key, value|
|
7
|
+
"#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
|
8
|
+
end.sort * '&'
|
9
|
+
end
|
10
|
+
|
11
|
+
def key
|
12
|
+
self.keys.first if self.length == 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def value
|
16
|
+
self.values.first if self.length == 1
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
module Gattica
|
5
|
+
class Profiles
|
6
|
+
include Convertible
|
7
|
+
|
8
|
+
attr_reader :id, :updated, :title, :table_id, :account_id, :account_name,
|
9
|
+
:profile_id, :web_property_id, :goals
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(xml)
|
13
|
+
@id = xml.at(:id).inner_html
|
14
|
+
@updated = DateTime.parse(xml.at(:updated).inner_html)
|
15
|
+
@account_id = xml.at("dxp:property[@name='ga:accountId']").attributes['value'].to_i
|
16
|
+
@account_name = xml.at("dxp:property[@name='ga:accountName']").attributes['value']
|
17
|
+
|
18
|
+
@title = xml.at("dxp:property[@name='ga:profileName']").attributes['value']
|
19
|
+
@table_id = xml.at("dxp:property[@name='dxp:tableId']").attributes['value']
|
20
|
+
@profile_id = xml.at("dxp:property[@name='ga:profileId']").attributes['value'].to_i
|
21
|
+
@web_property_id = xml.at("dxp:property[@name='ga:webPropertyId']").attributes['value']
|
22
|
+
|
23
|
+
# @goals = xml.search('ga:goal').collect do |goal| {
|
24
|
+
# :active => goal.attributes['active'],
|
25
|
+
# :name => goal.attributes['name'],
|
26
|
+
# :number => goal.attributes['number'].to_i,
|
27
|
+
# :value => goal.attributes['value'].to_f,
|
28
|
+
# }
|
29
|
+
# end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
module Gattica
|
5
|
+
class Segment
|
6
|
+
include Convertible
|
7
|
+
|
8
|
+
attr_reader :id, :name, :definition
|
9
|
+
|
10
|
+
def initialize(xml)
|
11
|
+
@id = xml.attributes['id']
|
12
|
+
@name = xml.attributes['name']
|
13
|
+
@definition = xml.at("dxp:definition").inner_html
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Gattica
|
2
|
+
module Settings
|
3
|
+
|
4
|
+
USE_SSL = true
|
5
|
+
SSL_PORT = 443
|
6
|
+
NON_SSL_PORT = 80
|
7
|
+
|
8
|
+
TIMEOUT = 100
|
9
|
+
|
10
|
+
DEFAULT_ARGS = {
|
11
|
+
:start_date => nil,
|
12
|
+
:end_date => nil,
|
13
|
+
:dimensions => [],
|
14
|
+
:metrics => [],
|
15
|
+
:filters => [],
|
16
|
+
:sort => []
|
17
|
+
}
|
18
|
+
|
19
|
+
DEFAULT_OPTIONS = {
|
20
|
+
:email => nil, # eg: 'email@gmail.com'
|
21
|
+
:password => nil, # eg: '$up3r_$ekret'
|
22
|
+
:token => nil,
|
23
|
+
:profile_id => nil,
|
24
|
+
:debug => false,
|
25
|
+
:headers => {},
|
26
|
+
:logger => Logger.new(STDOUT)
|
27
|
+
}
|
28
|
+
|
29
|
+
FILTER_METRIC_OPERATORS = %w{ == != > < >= <= }
|
30
|
+
FILTER_DIMENSION_OPERATORS = %w{ == != =~ !~ =@ ~@ }
|
31
|
+
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/lib/gattica/user.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Gattica
|
2
|
+
|
3
|
+
# Represents a user to be authenticated by GA
|
4
|
+
|
5
|
+
class User
|
6
|
+
|
7
|
+
include Convertible
|
8
|
+
|
9
|
+
attr_accessor :email, :password
|
10
|
+
|
11
|
+
def initialize(email,password)
|
12
|
+
@email = email
|
13
|
+
@password = password
|
14
|
+
validate
|
15
|
+
end
|
16
|
+
|
17
|
+
# User gets a special +to_h+ because Google expects +Email+ and +Passwd+ instead of our nicer internal names
|
18
|
+
def to_h
|
19
|
+
{ :Email => @email,
|
20
|
+
:Passwd => @password }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
# Determine whether or not this is a valid user
|
25
|
+
def validate
|
26
|
+
raise GatticaError::InvalidEmail, "The email address '#{@email}' is not valid" if not @email.match(/^(?:[_a-z0-9-]+)(\.[_a-z0-9-]+)*@([a-z0-9-]+)(\.[a-zA-Z0-9\-\.]+)*(\.[a-z]{2,4})$/i)
|
27
|
+
raise GatticaError::InvalidPassword, "The password cannot be blank" if @password.empty? || @password.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. lib gattica])
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
# include Gattica
|
7
|
+
|
8
|
+
def fixture(name)
|
9
|
+
File.read(File.join(File.dirname(__FILE__), 'fixtures', name))
|
10
|
+
end
|
11
|
+
|
12
|
+
def absolute_project_path
|
13
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
14
|
+
end
|
data/test/settings.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module GatticaTest
|
2
|
+
|
3
|
+
DEFAULT_AUTH = {
|
4
|
+
:email => 'name@email.com',
|
5
|
+
:password => 'password',
|
6
|
+
:debug => true
|
7
|
+
}
|
8
|
+
DEFAULT_QUERY = {
|
9
|
+
:start_date => '2010-01-01',
|
10
|
+
:end_date => '2011-01-01',
|
11
|
+
:dimensions => ['date'],
|
12
|
+
:metrics => ['visits']
|
13
|
+
}
|
14
|
+
PROFILE_ID = 23987717
|
15
|
+
|
16
|
+
def self.ga(options={}, profile_id=PROFILE_ID)
|
17
|
+
unless defined? @ga
|
18
|
+
@ga = Gattica.new(DEFAULT_AUTH)
|
19
|
+
@ga.profile_id = profile_id
|
20
|
+
end
|
21
|
+
@ga
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.get(options={}, profile_id=PROFILE_ID)
|
25
|
+
ga.get(DEFAULT_QUERY.merge(options))
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/test/suite.rb
ADDED
data/test/test_engine.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
require File.expand_path('../settings', __FILE__)
|
3
|
+
|
4
|
+
class TestEngine < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_login_with_good_password
|
7
|
+
assert Gattica.new(GatticaTest::DEFAULT_AUTH),
|
8
|
+
"should have been able to login"
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_login_with_bad_user_password
|
12
|
+
assert_raise GatticaError::CouldNotAuthenticate do
|
13
|
+
Gattica.new({ :email => 'bad-email@gmail.com',
|
14
|
+
:password => 'bad-password' })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_accounts
|
19
|
+
ga = Gattica.new(GatticaTest::DEFAULT_AUTH)
|
20
|
+
accounts = ga.accounts
|
21
|
+
assert accounts.count > 900, "should have gotten at least 900 accounts"
|
22
|
+
end
|
23
|
+
|
24
|
+
# def test_timeout_too_short
|
25
|
+
# ga = Gattica.new(GatticaTest::DEFAULT_AUTH.merge!(:timeout => 0))
|
26
|
+
# assert_raise Timeout::Error do
|
27
|
+
# ga.accounts
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
|
31
|
+
def test_setting_timeout
|
32
|
+
ga = Gattica.new((GatticaTest::DEFAULT_AUTH).merge!(:timeout => 300))
|
33
|
+
http = ga.instance_variable_get('@http')
|
34
|
+
assert http.read_timeout == 300, "http timeout should be 300"
|
35
|
+
end
|
36
|
+
|
37
|
+
# def test_raise_error_when_no_user_pass_or_token_specified
|
38
|
+
# assert_raise GatticaError::NoLoginOrToken do
|
39
|
+
# Gattica.new
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# def test_use_an_existing_token
|
44
|
+
# token = Gattica.new(@auth).token
|
45
|
+
# assert Gattica.new({ :token => token })
|
46
|
+
# end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
require File.expand_path('../settings', __FILE__)
|
3
|
+
|
4
|
+
class TestResults < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@response = GatticaTest::get({ :start_index => 5, :max_results => 5 })
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_max_results
|
11
|
+
assert @response.points.count == 5, "should only return 5 results"
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_start_index
|
15
|
+
assert @response.points.first.title == "ga:date=20100105", "should start on the 5th"
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_conversions
|
19
|
+
assert @response.class.inspect == 'Gattica::DataSet', "should be a Gattica:DataSet"
|
20
|
+
assert @response.to_h.class.inspect == 'Hash', "Should be a hash"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/test/test_user.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
class TestUser < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_can_create_user
|
9
|
+
assert Gattica::User.new('anonymous@anon.com','none')
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_invalid_email
|
13
|
+
assert_raise GatticaError::InvalidEmail do Gattica::User.new('','') end
|
14
|
+
assert_raise ArgumentError do Gattica::User.new('') end
|
15
|
+
assert_raise GatticaError::InvalidEmail do Gattica::User.new('anonymous','none') end
|
16
|
+
assert_raise GatticaError::InvalidEmail do Gattica::User.new('anonymous@asdfcom','none') end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_invalid_password
|
20
|
+
assert_raise GatticaError::InvalidPassword do Gattica::User.new('anonymous@anon.com','') end
|
21
|
+
assert_raise ArgumentError do Gattica::User.new('anonymous@anon.com') end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chrisle-gattica
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Christopher Le, et all
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: test-unit
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: hpricot
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Gattica is a easy to use Ruby Gem for getting data from the Google Analytics
|
47
|
+
API. It supports metrics, dimensions, sort, filters, goals, and segments. It can
|
48
|
+
handle accounts with 1000+ profiles, and can return data in CSV, Hash, or JSON
|
49
|
+
email: chrisl@seerinteractive.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files:
|
53
|
+
- LICENSE
|
54
|
+
- README.md
|
55
|
+
files:
|
56
|
+
- Gemfile
|
57
|
+
- Gemfile.lock
|
58
|
+
- LICENSE
|
59
|
+
- README.md
|
60
|
+
- Rakefile
|
61
|
+
- VERSION.yml
|
62
|
+
- chrisle-gattica.gemspec
|
63
|
+
- lib/gattica.rb
|
64
|
+
- lib/gattica/account.rb
|
65
|
+
- lib/gattica/auth.rb
|
66
|
+
- lib/gattica/convertible.rb
|
67
|
+
- lib/gattica/data_point.rb
|
68
|
+
- lib/gattica/data_set.rb
|
69
|
+
- lib/gattica/engine.rb
|
70
|
+
- lib/gattica/exceptions.rb
|
71
|
+
- lib/gattica/goals.rb
|
72
|
+
- lib/gattica/hash_extensions.rb
|
73
|
+
- lib/gattica/profiles.rb
|
74
|
+
- lib/gattica/segment.rb
|
75
|
+
- lib/gattica/settings.rb
|
76
|
+
- lib/gattica/user.rb
|
77
|
+
- test/helper.rb
|
78
|
+
- test/settings.rb
|
79
|
+
- test/suite.rb
|
80
|
+
- test/test_engine.rb
|
81
|
+
- test/test_results.rb
|
82
|
+
- test/test_user.rb
|
83
|
+
homepage: http://github.com/chrisle/gattica
|
84
|
+
licenses: []
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 1.8.24
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: Gattica is a easy to use Ruby Gem for getting data from the Google Analytics
|
107
|
+
API.
|
108
|
+
test_files: []
|
109
|
+
has_rdoc:
|