adapi 0.0.2 → 0.0.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.
@@ -13,7 +13,7 @@ single method call: Adapi::Campaign.create(campaign_data). Various convenience
13
13
  methods are added to the models, for example: campaign.pause! (pauses
14
14
  campaign). Adapi enables multiple AdWords accounts to be used at the same time.
15
15
 
16
- Adapi is STILL IN DEVELOPMENT, not nearly done yet! Version 0.1.0 should have
16
+ Adapi is STILL IN DEVELOPMENT and not nearly done yet! Version 1.0.0 should have
17
17
  all planned functionality.
18
18
 
19
19
  == Installation
@@ -25,12 +25,50 @@ all planned functionality.
25
25
  bundle install
26
26
  rake install
27
27
 
28
- == Configuration of AdWords Account(s)
29
-
30
- This section explains how to connect to specific account and client.
31
-
32
- In general, you can set many AdWords accounts to connect to (similarly as you
33
- can set up several databases in Rails, for example).
28
+ == Configuration
29
+
30
+ This section explains how to connect to specific AdWords account and client.
31
+
32
+ You can set many AdWords accounts to connect to and switch between while running
33
+ the application. You can even update single values of the settings on-the-fly.
34
+
35
+ # load the settings
36
+ Adapi::Config.load_settings(:in_hash => {
37
+ :coca_cola => {
38
+ :authentication => {
39
+ :method => 'ClientLogin',
40
+ :email => 'coca_cola_email@gmail.com',
41
+ :password => 'coca_cola_password',
42
+ :developer_token => 'coca_cola_developer_token',
43
+ :user_agent => 'Coca-Cola Adwords API Test'
44
+ },
45
+ :service => {
46
+ :environment => 'SANDBOX'
47
+ }
48
+ },
49
+ :pepsi => {
50
+ :authentication => {
51
+ :method => 'ClientLogin',
52
+ :email => 'pepsi_email@gmail.com',
53
+ :password => 'pepsi_password',
54
+ :developer_token => 'pepsi_developer_token',
55
+ :user_agent => 'Pepsi Adwords API Test'
56
+ },
57
+ :service => {
58
+ :environment => 'SANDBOX'
59
+ }
60
+ }
61
+ })
62
+
63
+ # set to pepsi and specific client
64
+ Adapi::Config.set(:pepsi, :client_customer_id => '555-666-7777')
65
+
66
+ # do some stuff here...
67
+
68
+ # set to coca-cola and another client
69
+ Adapi::Config.set(:coca_cola, :client_customer_id => '777-666-5555')
70
+
71
+ # do some stuff here...
34
72
 
35
73
  === Authentication workflow
36
74
 
@@ -83,38 +121,35 @@ the same configuration will also work for adapi: ~/adwords_api.yml
83
121
  Before logging into the Adwords API, you can set global settings through
84
122
  Adapi::Config:
85
123
 
86
- Adapi::Config.set({
87
- :authentication:
88
- :method: ClientLogin
89
- :email: sandbox_email@gmail.com
90
- :password: sandbox_password
91
- :developer_token: sandbox_token
92
- :client_email: sandbox_client_email@gmail.com
93
- :user_agent: Adwords API Test
94
- :service:
95
- :environment: SANDBOX
96
- })
97
-
98
- This will override any previous configuration and set the account as :default.
99
- You can specify more accounts this way, with aliases, in the same way as in
100
- ~/adapi.yml.
101
-
102
- == API
103
-
104
- There are two ways of working with API. Both examples show adding an ad to
105
- specific campaign's ad group:
106
-
107
- === Model-centric
108
-
109
- Adapi::Campaign.create(campaign_data)
110
-
111
- === TODO Collection-centric
112
-
113
- account = Adapi::Account.new(:account_alias)
114
-
115
- campaign = account.campaigns.create(campaign_data)
116
-
117
- account.campaigns[ campaign[:id] ].ad_groups << ad_group_data
124
+ # load the settings
125
+ Adapi::Config.load_settings(:in_hash => {
126
+ :sandbox => {
127
+ :authentication => {
128
+ :method => "ClientLogin"
129
+ :email => "sandbox_email@gmail.com",
130
+ :password => "sandbox_password",
131
+ :developer_token => "sandbox_token",
132
+ :client_email => "sandbox_client_email@gmail.com",
133
+ :user_agent => "Adwords API Test"
134
+ },
135
+ :service => {
136
+ :environment => "SANDBOX"
137
+ }
138
+ }
139
+ })
140
+
141
+ Adapi::Config.set(:sandbox)
142
+
143
+ == TODO API
144
+
145
+ AdWords Services are implemented as models, similar to ActiveRecord models of
146
+ database tables. They are built on ActiveModel.
147
+
148
+ == API Version Support
149
+
150
+ Adapi supports only the latest version of Google AdWords API: v201101. Older
151
+ versions will not be supported. v201101 and newer versions will still be
152
+ supported when new versions are released.
118
153
 
119
154
  == Examples
120
155
 
@@ -20,6 +20,10 @@ Gem::Specification.new do |s|
20
20
 
21
21
  s.add_dependency "google-ads-common", "~> 0.5.0"
22
22
  s.add_dependency "google-adwords-api", "~> 0.4.0"
23
+ s.add_dependency "activemodel", "~> 3.1.0" # , :require => "active_model"
24
+ s.add_dependency "activesupport", "~> 3.1.0" #, :require => "active_support"
25
+ s.add_dependency "rake", "~> 0.9.2"
26
+ s.add_dependency "curb", "~> 0.7.15"
23
27
 
24
28
  s.add_development_dependency "turn"
25
29
  s.add_development_dependency "shoulda"
@@ -2,7 +2,7 @@
2
2
  require 'adapi'
3
3
 
4
4
  # create campaign
5
- require 'add_bare_campaign'
5
+ require File.join(File.dirname(__FILE__), 'add_bare_campaign')
6
6
 
7
7
  # create ad group
8
8
 
@@ -10,29 +10,11 @@ ad_group_data = {
10
10
  :name => "AdGroup #%d" % (Time.new.to_f * 1000).to_i,
11
11
  :status => 'ENABLED',
12
12
  :campaign_id => $campaign[:id],
13
- :bids => {
14
- :xsi_type => 'ManualCPCAdGroupBids',
15
- :keyword_max_cpc => {
16
- :amount => {
17
- :micro_amount => 10000000
18
- }
19
- }
20
- },
21
13
 
22
- :criteria => [
23
- { # keyword_criterion
24
- :xsi_type => 'BiddableAdGroupCriterion',
25
- :criterion => { :xsi_type => 'Keyword', :text => 'codez', :match_type => 'BROAD' }
26
- },
27
- { # placement_criterion
28
- :xsi_type => 'BiddableAdGroupCriterion',
29
- :criterion => { :xsi_type => 'Placement', :url => 'http://www.blogger.com' }
30
- }
31
- ],
14
+ :keywords => [ 'dem codez', '"top coder"', '[-code]' ],
32
15
 
33
16
  :ads => [
34
17
  {
35
- :xsi_type => 'TextAd',
36
18
  :headline => "Code like Neo",
37
19
  :description1 => 'Need mad coding skills?',
38
20
  :description2 => 'Check out my new blog!',
@@ -40,7 +22,9 @@ ad_group_data = {
40
22
  :display_url => 'http://www.demcodez.com'
41
23
  }
42
24
  ]
43
-
44
25
  }
45
26
 
46
- p Adapi::AdGroup.create(:data => ad_group_data)
27
+ $ad_group = Adapi::AdGroup.create(ad_group_data)
28
+
29
+ p "Created ad_group ID #{$ad_group.id} for campaign ID #{$ad_group.campaign_id}"
30
+ p $ad_group.attributes
@@ -2,18 +2,21 @@
2
2
  require 'adapi'
3
3
 
4
4
  # create campaign
5
- require 'add_bare_campaign'
5
+ require File.join(File.dirname(__FILE__), 'add_bare_campaign')
6
6
 
7
7
  # create ad group with basic data only
8
8
  # this script is used as an include in other scripts
9
9
 
10
10
  $ad_group_data = {
11
+ :campaign_id => $campaign[:id],
11
12
  :name => "AdGroup #%d" % (Time.new.to_f * 1000).to_i,
12
13
  :status => 'ENABLED',
13
- :campaign_id => $campaign[:id],
14
+ # TODO refactor AdGroup.bids DSL
14
15
  :bids => {
15
- :xsi_type => 'ManualCPCAdGroupBids',
16
- :keyword_max_cpc => {
16
+ # this should be set automatically, it's dependent on Campaign.bids
17
+ :xsi_type => 'BudgetOptimizerAdGroupBids',
18
+ # :keyword_max_cpc => 10
19
+ :proxy_keyword_max_cpc => {
17
20
  :amount => {
18
21
  :micro_amount => 10000000
19
22
  }
@@ -21,6 +24,8 @@ $ad_group_data = {
21
24
  }
22
25
  }
23
26
 
24
- $ad_group = Adapi::AdGroup.create(:data => $ad_group_data)
27
+ $ad_group = Adapi::AdGroup.create($ad_group_data)
28
+
25
29
 
26
- p $ad_group
30
+ p "Created ad_group ID #{$ad_group.id} for campaign ID #{$ad_group.campaign_id}"
31
+ p $ad_group.attributes
@@ -7,12 +7,9 @@ require 'adapi'
7
7
  $campaign_data = {
8
8
  :name => "Campaign #%d" % (Time.new.to_f * 1000).to_i,
9
9
  :status => 'PAUSED',
10
- :bidding_strategy => { :xsi_type => 'ManualCPC' },
11
- :budget => {
12
- :period => 'DAILY',
13
- :amount => { :micro_amount => 50000000 },
14
- :delivery_method => 'STANDARD'
15
- },
10
+ # :bidding_strategy => 'ManualCPC',
11
+ :bidding_strategy => { :xsi_type => 'BudgetOptimizer', :bid_ceiling => 55 },
12
+ :budget => 50,
16
13
 
17
14
  :network_setting => {
18
15
  :target_google_search => true,
@@ -24,4 +21,5 @@ $campaign_data = {
24
21
 
25
22
  $campaign = Adapi::Campaign.create($campaign_data)
26
23
 
27
- p $campaign
24
+ p "Created campaign ID #{$campaign.id}"
25
+ p $campaign.attributes
@@ -7,12 +7,9 @@ require 'adapi'
7
7
  campaign_data = {
8
8
  :name => "Campaign #%d" % (Time.new.to_f * 1000).to_i,
9
9
  :status => 'PAUSED',
10
- :bidding_strategy => { :xsi_type => 'ManualCPC' },
11
- :budget => {
12
- :period => 'DAILY',
13
- :amount => { :micro_amount => 50000000 },
14
- :delivery_method => 'STANDARD'
15
- },
10
+ # Automatic CPC: BudgetOptimizer or ManualCPC
11
+ :bidding_strategy => { :xsi_type => 'BudgetOptimizer', :bid_ceiling => 100 },
12
+ :budget => { :amount => 50, :delivery_method => 'STANDARD' },
16
13
 
17
14
  :network_setting => {
18
15
  :target_google_search => true,
@@ -23,36 +20,19 @@ campaign_data = {
23
20
 
24
21
  :targets => {
25
22
  :language => [ 'en', 'cs' ],
26
- :geo => { :country => 'CZ' }
23
+ # TODO test together with city target
24
+ :geo => { :proximity => { :geo_point => '38.89859,-77.035971', :radius => '10 km' } }
27
25
  },
28
26
 
29
27
  :ad_groups => [
30
28
  {
31
29
  :name => "AdGroup #%d" % (Time.new.to_f * 1000).to_i,
32
30
  :status => 'ENABLED',
33
- :bids => {
34
- :xsi_type => 'ManualCPCAdGroupBids',
35
- :keyword_max_cpc => {
36
- :amount => {
37
- :micro_amount => 10000000
38
- }
39
- }
40
- },
41
-
42
- :criteria => [
43
- { # keyword_criterion
44
- :xsi_type => 'BiddableAdGroupCriterion',
45
- :criterion => { :xsi_type => 'Keyword', :text => 'codez', :match_type => 'BROAD' }
46
- },
47
- { # placement_criterion
48
- :xsi_type => 'BiddableAdGroupCriterion',
49
- :criterion => { :xsi_type => 'Placement', :url => 'http://www.blogger.com' }
50
- }
51
- ],
52
-
31
+
32
+ :keywords => [ 'dem codez', '"top coder"', "[-code]" ],
33
+
53
34
  :ads => [
54
35
  {
55
- :xsi_type => 'TextAd',
56
36
  :headline => "Code like Neo",
57
37
  :description1 => 'Need mad coding skills?',
58
38
  :description2 => 'Check out my new blog!',
@@ -65,4 +45,7 @@ campaign_data = {
65
45
 
66
46
  }
67
47
 
68
- p Adapi::Campaign.create(campaign_data)
48
+ $campaign = Adapi::Campaign.create(campaign_data)
49
+
50
+ p "Created campaign ID #{$campaign.id}"
51
+ p $campaign.attributes
@@ -2,15 +2,24 @@
2
2
  require 'adapi'
3
3
 
4
4
  # create campaign
5
- require 'add_bare_campaign'
5
+ require File.join(File.dirname(__FILE__), 'add_bare_campaign')
6
6
 
7
- # create campaign targets
8
- campaign_target_data = {
7
+ # TODO we should be able call it from campaign instance, for example:
8
+ # $campaign.set_targets(:language => [ 'en' ], ...)
9
+
10
+ $campaign_target = Adapi::CampaignTarget.new(
9
11
  :campaign_id => $campaign[:id],
10
12
  :targets => {
11
- :language => [ 'en', 'cs' ],
12
- :geo => { :country => 'CZ' }
13
+ :language => [ 'en' ],
14
+ :geo => {
15
+ # :country => 'CZ'
16
+ # :province => 'CZ-PR'
17
+ # :city => { :city_name => 'Prague', :province_code => 'CZ-PR', :country_code => 'CZ' }
18
+ :proximity => { :geo_point => '50.083333,14.366667', :radius => '50 km' }
19
+ }
13
20
  }
14
- }
21
+ )
22
+
23
+ $campaign_target.create
15
24
 
16
- p Adapi::CampaignTarget.create(campaign_target_data)
25
+ p $campaign_target.attributes
@@ -0,0 +1,35 @@
1
+
2
+ require 'adapi'
3
+
4
+ # create campaign
5
+ require File.join(File.dirname(__FILE__), 'add_bare_campaign')
6
+
7
+ # create ad group
8
+
9
+ ad_group_data = {
10
+ :name => "AdGroup #%d" % (Time.new.to_f * 1000).to_i,
11
+ :status => 'ENABLED',
12
+ :campaign_id => $campaign[:id],
13
+
14
+ :keywords => [ 'dem codez', '"top coder"', '[-code]' ],
15
+
16
+ :ads => [
17
+ {
18
+ :headline => "Code like Neo",
19
+ :description1 => 'Need mad coding skills?',
20
+ :description2 => 'Check out my new blog!',
21
+ # this should throw an error
22
+ :url => 'http://www.demcodez.com THIS IS INVALID',
23
+ :display_url => 'http://www.demcodez.com'
24
+ }
25
+ ]
26
+ }
27
+
28
+ $ad_group = Adapi::AdGroup.create(ad_group_data)
29
+
30
+ if $ad_group.errors.empty?
31
+ p $ad_group.data
32
+ else
33
+ p "ERRORS:"
34
+ p $ad_group.errors.to_a
35
+ end
@@ -0,0 +1,30 @@
1
+
2
+ require 'adapi'
3
+
4
+ # PolicyViolations will appread only in production, not in sandbox
5
+ #
6
+ #Adapi::Config.load_settings
7
+ #Adapi::Config.set(:production_settings)
8
+ #
9
+ #pp "Running in #{Adapi::Config.read[:service][:environment]}"
10
+
11
+ # create ad group
12
+ require File.join(File.dirname(__FILE__), 'add_bare_ad_group')
13
+
14
+ $ad = Adapi::Ad::TextAd.create(
15
+ :ad_group_id => $ad_group[:id],
16
+ :headline => "Code a Blog",
17
+ :description1 => 'Need mad coding skill?',
18
+ :description2 => 'Check out my new blog!!!', # !!! - this is invalid
19
+ :url => 'http://www.demcodez.com',
20
+ :display_url => 'http://www.demcodez.com',
21
+ :status => 'PAUSED'
22
+ )
23
+
24
+ if $ad.errors.empty?
25
+ p "OK"
26
+ p $ad.data
27
+ else
28
+ p "ERROR"
29
+ p $ad.errors.messages
30
+ end
@@ -0,0 +1,14 @@
1
+
2
+ require 'adapi'
3
+
4
+ # create ad group
5
+ require File.join(File.dirname(__FILE__), 'add_bare_ad_group')
6
+
7
+ $keywords = Adapi::Keyword.new(
8
+ :ad_group_id => $ad_group[:id],
9
+ :keywords => [ 'dem codez', '"top coder"', '[-code]' ]
10
+ )
11
+
12
+ $r = $keywords.create
13
+
14
+ p $keywords
@@ -0,0 +1,23 @@
1
+
2
+ require 'adapi'
3
+
4
+ # create ad group
5
+ require File.join(File.dirname(__FILE__), 'add_bare_ad_group')
6
+
7
+ ad = Adapi::Ad::TextAd.create(
8
+ :ad_group_id => $ad_group[:id],
9
+ :headline => "Code like Neo",
10
+ :description1 => 'Need mad coding skills?',
11
+ :description2 => 'Check out my new blog!',
12
+ :url => 'http://www.demcodez.com',
13
+ :display_url => 'http://www.demcodez.com',
14
+ :status => 'PAUSED'
15
+ )
16
+
17
+ if ad
18
+ p "OK"
19
+ p ad.data
20
+ else
21
+ p "ERROR"
22
+ p ad.errors.messages
23
+ end