adapi 0.0.4 → 0.0.5
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/.gitignore +3 -0
- data/GUIDELINES.markdown +61 -0
- data/README.markdown +79 -45
- data/Rakefile +31 -3
- data/adapi.gemspec +10 -3
- data/examples/add_ad_group.rb +1 -1
- data/examples/add_bare_ad_group.rb +1 -4
- data/examples/add_campaign.rb +1 -0
- data/examples/add_campaign_criteria.rb +27 -0
- data/examples/add_invalid_ad_group.rb +3 -1
- data/examples/add_invalid_text_ad.rb +1 -1
- data/examples/add_keywords.rb +1 -1
- data/examples/add_negative_campaign_criteria.rb +23 -0
- data/examples/add_text_ad.rb +1 -1
- data/examples/customize_configuration.rb +1 -2
- data/examples/delete_keyword.rb +1 -1
- data/examples/find_campaign.rb +5 -5
- data/examples/find_campaign_ad_groups.rb +1 -1
- data/examples/find_campaign_criteria.rb +103 -0
- data/examples/find_locations.rb +21 -0
- data/examples/rollback_campaign.rb +7 -6
- data/examples/update_campaign.rb +1 -1
- data/examples/update_campaign_status.rb +1 -1
- data/lib/adapi.rb +11 -9
- data/lib/adapi/ad/text_ad.rb +2 -1
- data/lib/adapi/ad_group.rb +13 -9
- data/lib/adapi/ad_param.rb +89 -0
- data/lib/adapi/api.rb +8 -0
- data/lib/adapi/campaign.rb +27 -18
- data/lib/adapi/campaign_criterion.rb +278 -0
- data/lib/adapi/campaign_target.rb +5 -123
- data/lib/adapi/config.rb +36 -31
- data/lib/adapi/constant_data.rb +13 -0
- data/lib/adapi/constant_data/language.rb +45 -0
- data/lib/adapi/keyword.rb +15 -5
- data/lib/adapi/location.rb +91 -0
- data/lib/adapi/version.rb +8 -1
- data/lib/httpi_request_monkeypatch.rb +4 -0
- data/test/config/adapi.yml.template +21 -0
- data/test/config/adwords_api.yml.template +10 -0
- data/test/integration/create_campaign_test.rb +54 -0
- data/test/test_helper.rb +2 -3
- data/test/unit/ad_group_test.rb +3 -4
- data/test/unit/ad_test.rb +1 -1
- data/test/unit/campaign_criterion_test.rb +23 -0
- data/test/unit/config_test.rb +52 -0
- metadata +48 -35
- data/examples/add_campaign_targets.rb +0 -26
- data/test/unit/campaign_target_test.rb +0 -51
data/examples/add_text_ad.rb
CHANGED
@@ -5,7 +5,7 @@ require 'adapi'
|
|
5
5
|
# this example shows how to load custom settings
|
6
6
|
|
7
7
|
Adapi::Config.load_settings(
|
8
|
-
:
|
8
|
+
:dir => File.expand_path(File.dirname(__FILE__)),
|
9
9
|
:filename => 'custom_settings.yml'
|
10
10
|
)
|
11
11
|
|
@@ -16,4 +16,3 @@ p Adapi::Config.read[:authentication][:email]
|
|
16
16
|
Adapi::Config.set(:sandbox)
|
17
17
|
p "Set :sandbox account:"
|
18
18
|
p Adapi::Config.read[:authentication][:email]
|
19
|
-
|
data/examples/delete_keyword.rb
CHANGED
data/examples/find_campaign.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'adapi'
|
4
4
|
|
5
5
|
# create campaign
|
6
|
-
|
6
|
+
require_relative 'add_campaign'
|
7
7
|
|
8
8
|
$campaign = Adapi::Campaign.find_complete($campaign.id).to_hash
|
9
9
|
|
@@ -18,10 +18,10 @@ puts "Budget period: %s" % $campaign[:budget][:period]
|
|
18
18
|
puts "\nBidding strategy type: %s" % $campaign[:bidding_strategy][:xsi_type]
|
19
19
|
# TODO bidding_strategy.bid_ceiling
|
20
20
|
|
21
|
-
puts "\
|
22
|
-
$campaign[:
|
23
|
-
p
|
24
|
-
p
|
21
|
+
puts "\nCriteria:"
|
22
|
+
$campaign[:criteria].each do |criterion|
|
23
|
+
p criterion[:xsi_type]
|
24
|
+
p criterion
|
25
25
|
end
|
26
26
|
|
27
27
|
puts "\nAd groups (#{$campaign[:ad_groups].size} in total):"
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Encoding: utf-8
|
3
|
+
#
|
4
|
+
# Author:: api.dklimkin@gmail.com (Danial Klimkin)
|
5
|
+
#
|
6
|
+
# Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
|
7
|
+
#
|
8
|
+
# License:: Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
17
|
+
# implied.
|
18
|
+
# See the License for the specific language governing permissions and
|
19
|
+
# limitations under the License.
|
20
|
+
#
|
21
|
+
# This example illustrates how to retrieve all the campaign targets. To set
|
22
|
+
# campaign targets, run add_campaign_targeting_criteria.rb.
|
23
|
+
#
|
24
|
+
# Tags: CampaignCriterionService.get
|
25
|
+
|
26
|
+
require 'adwords_api'
|
27
|
+
|
28
|
+
API_VERSION = :v201109
|
29
|
+
PAGE_SIZE = 500
|
30
|
+
|
31
|
+
def get_campaign_targeting_criteria()
|
32
|
+
# AdwordsApi::Api will read a config file from ENV['HOME']/adwords_api.yml
|
33
|
+
# when called without parameters.
|
34
|
+
adwords = AdwordsApi::Api.new
|
35
|
+
|
36
|
+
# To enable logging of SOAP requests, set the log_level value to 'DEBUG' in
|
37
|
+
# the configuration file or provide your own logger:
|
38
|
+
# adwords.logger = Logger.new('adwords_xml.log')
|
39
|
+
|
40
|
+
campaign_criterion_srv =
|
41
|
+
adwords.service(:CampaignCriterionService, API_VERSION)
|
42
|
+
|
43
|
+
# Specify campaign ID to get targeting for.
|
44
|
+
campaign_id = 'INSERT_CAMPAIGN_ID_HERE'.to_i
|
45
|
+
|
46
|
+
# Selector to get all the targeting for this campaign.
|
47
|
+
selector = {
|
48
|
+
:fields => ['Id', 'CriteriaType', 'KeywordText'],
|
49
|
+
:predicates => [
|
50
|
+
{:field => 'CampaignId', :operator => 'EQUALS', :values => [campaign_id]}
|
51
|
+
],
|
52
|
+
:paging => {
|
53
|
+
:start_index => 0,
|
54
|
+
:number_results => PAGE_SIZE
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
# Set initial values.
|
59
|
+
offset, page = 0, {}
|
60
|
+
|
61
|
+
begin
|
62
|
+
page = campaign_criterion_srv.get(selector)
|
63
|
+
if page[:entries]
|
64
|
+
page[:entries].each do |typed_criterion|
|
65
|
+
negative = typed_criterion[:xsi_type] == 'NegativeCampaignCriterion' ?
|
66
|
+
' (negative)' : ''
|
67
|
+
criterion = typed_criterion[:criterion]
|
68
|
+
puts ("Campaign criterion%s with ID %d, type '%s' and text '%s'" +
|
69
|
+
" was found.") %
|
70
|
+
[negative, criterion[:id], criterion[:type], criterion[:text]]
|
71
|
+
end
|
72
|
+
# Increment values to request the next page.
|
73
|
+
offset += PAGE_SIZE
|
74
|
+
selector[:paging][:start_index] = offset
|
75
|
+
end
|
76
|
+
end while page[:total_num_entries] > offset
|
77
|
+
|
78
|
+
if page.include?(:total_num_entries)
|
79
|
+
puts "\tCampaign ID %d has %d criteria." %
|
80
|
+
[campaign_id, page[:total_num_entries]]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
if __FILE__ == $0
|
85
|
+
begin
|
86
|
+
get_campaign_targeting_criteria()
|
87
|
+
|
88
|
+
# HTTP errors.
|
89
|
+
rescue AdsCommon::Errors::HttpError => e
|
90
|
+
puts "HTTP Error: %s" % e
|
91
|
+
|
92
|
+
# API errors.
|
93
|
+
rescue AdwordsApi::Errors::ApiException => e
|
94
|
+
puts "Message: %s" % e.message
|
95
|
+
puts 'Errors:'
|
96
|
+
e.errors.each_with_index do |error, index|
|
97
|
+
puts "\tError [%d]:" % (index + 1)
|
98
|
+
error.each do |field, value|
|
99
|
+
puts "\t\t%s: %s" % [field, value]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'adapi'
|
4
|
+
|
5
|
+
# find location by LocationService
|
6
|
+
|
7
|
+
$search_params = [
|
8
|
+
{ :country => 'CZ' },
|
9
|
+
{ :country => 'CZ', :province => 'Prague' },
|
10
|
+
{ :country => 'CZ', :province => 'Prague', :city => 'Prague' },
|
11
|
+
{ :province => 'Prague' },
|
12
|
+
{ :city => 'Prague' }
|
13
|
+
]
|
14
|
+
|
15
|
+
$search_params.each do |params|
|
16
|
+
$location = Adapi::Location.find(params)
|
17
|
+
|
18
|
+
puts "\nSearched for: " + params.inspect
|
19
|
+
# puts "Found Location ID: #{Adapi::Location.location_tree($location)}"
|
20
|
+
puts "Found Location ID: #{$location[:id]}"
|
21
|
+
end
|
@@ -2,8 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'adapi'
|
4
4
|
|
5
|
-
# create
|
6
|
-
#
|
5
|
+
# This test tries to create a complete campaign and fails because ad is
|
6
|
+
# intentionally left without url. The point is to test how create_campaign
|
7
|
+
# fails: campaign status should be set to DELETED and name changed (so the
|
8
|
+
# name isn't blocked and another campaign can be created with the same name
|
9
|
+
# eventually)
|
7
10
|
|
8
11
|
campaign_data = {
|
9
12
|
:name => "Campaign #%d" % (Time.new.to_f * 1000).to_i,
|
@@ -19,9 +22,8 @@ campaign_data = {
|
|
19
22
|
:target_content_contextual => false
|
20
23
|
},
|
21
24
|
|
22
|
-
:
|
25
|
+
:criteria => {
|
23
26
|
:language => [ 'en', 'cs' ],
|
24
|
-
# TODO test together with city target
|
25
27
|
:geo => { :proximity => { :geo_point => '38.89859,-77.035971', :radius => '10 km' } }
|
26
28
|
},
|
27
29
|
|
@@ -37,7 +39,7 @@ campaign_data = {
|
|
37
39
|
:headline => "Code like Neo",
|
38
40
|
:description1 => 'Need mad coding skills?',
|
39
41
|
:description2 => 'Check out my new blog!',
|
40
|
-
:url => '', # THIS
|
42
|
+
:url => '', # THIS SHOULD FAIL
|
41
43
|
:display_url => 'http://www.demcodez.com'
|
42
44
|
}
|
43
45
|
]
|
@@ -54,4 +56,3 @@ pp $campaign.attributes
|
|
54
56
|
|
55
57
|
p "with errors:"
|
56
58
|
pp $campaign.errors.to_a
|
57
|
-
|
data/examples/update_campaign.rb
CHANGED
data/lib/adapi.rb
CHANGED
@@ -2,38 +2,40 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'adwords_api'
|
5
|
+
# require 'logger'
|
5
6
|
require 'yaml'
|
6
7
|
require 'pp'
|
7
8
|
|
8
9
|
require 'active_model'
|
9
|
-
# require only ActiveSupport parts that we actually use
|
10
|
+
# TODO require only ActiveSupport parts that we actually use
|
10
11
|
require 'active_support/all'
|
11
12
|
|
12
13
|
require 'adapi/version'
|
13
14
|
require 'adapi/config'
|
14
15
|
require 'adapi/api'
|
15
16
|
require 'adapi/campaign'
|
17
|
+
require 'adapi/campaign_criterion'
|
16
18
|
require 'adapi/campaign_target'
|
17
19
|
require 'adapi/ad_group'
|
18
20
|
require 'adapi/ad_group_criterion'
|
19
21
|
require 'adapi/keyword'
|
20
22
|
require 'adapi/ad'
|
21
23
|
require 'adapi/ad/text_ad'
|
24
|
+
require 'adapi/ad_param'
|
25
|
+
require 'adapi/constant_data'
|
26
|
+
require 'adapi/constant_data/language'
|
27
|
+
require 'adapi/location'
|
22
28
|
|
23
|
-
# monkeypatch HTTPI
|
29
|
+
# monkeypatch HTTPI - important, check the file!
|
24
30
|
require 'httpi_request_monkeypatch'
|
25
31
|
|
26
32
|
HTTPI.adapter = :curb
|
27
|
-
# supress HTTPI output
|
28
|
-
# HTTPI.log = false
|
33
|
+
HTTPI.log = false # supress HTTPI output
|
29
34
|
|
30
35
|
module Adapi
|
31
|
-
API_VERSION = :
|
36
|
+
API_VERSION = :v201109
|
32
37
|
end
|
33
38
|
|
34
|
-
|
35
|
-
# FIXME there's gotta be more elegant way to do this
|
36
|
-
`ruby -v`.to_s =~ /^ruby (\d\.\d)\./
|
37
|
-
if $1.to_f < 1.9
|
39
|
+
if RUBY_VERSION < '1.9'
|
38
40
|
puts "WARNING: please use ruby 1.9, adapi gem won't work properly in 1.8 and earlier versions"
|
39
41
|
end
|
data/lib/adapi/ad/text_ad.rb
CHANGED
@@ -101,7 +101,8 @@ module Adapi
|
|
101
101
|
# supported condition parameters: ad_group_id and id
|
102
102
|
predicates = [ :ad_group_id, :id ].map do |param_name|
|
103
103
|
if params[param_name]
|
104
|
-
|
104
|
+
value = Array.try_convert(params[param_name]) ? params_param_name : [params[param_name]]
|
105
|
+
{:field => param_name.to_s.camelcase, :operator => 'IN', :values => value }
|
105
106
|
end
|
106
107
|
end.compact
|
107
108
|
|
data/lib/adapi/ad_group.rb
CHANGED
@@ -24,19 +24,22 @@ module Adapi
|
|
24
24
|
# convert bids to GoogleApi format
|
25
25
|
#
|
26
26
|
# can be either string (just xsi_type) or hash (xsi_type with params)
|
27
|
-
#
|
27
|
+
# although I'm not sure if just string makes sense in this case
|
28
28
|
#
|
29
29
|
if @bids
|
30
30
|
unless @bids.is_a?(Hash)
|
31
31
|
@bids = { :xsi_type => @bids }
|
32
32
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
|
34
|
+
# convert bid amounts to micro_amounts
|
35
|
+
[ :proxy_keyword_max_cpc, :proxy_site_max_cpc ].each do |k|
|
36
|
+
if @bids[k] and not @bids[k].is_a?(Hash)
|
37
|
+
@bids[k] = {
|
38
|
+
:amount => {
|
39
|
+
:micro_amount => Api.to_micro_units(@bids[k])
|
40
|
+
}
|
38
41
|
}
|
39
|
-
|
42
|
+
end
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
@@ -93,10 +96,11 @@ module Adapi
|
|
93
96
|
first_only = (amount.to_sym == :first)
|
94
97
|
|
95
98
|
raise "Campaign ID is required" unless params[:campaign_id]
|
96
|
-
|
99
|
+
|
97
100
|
predicates = [ :campaign_id, :id ].map do |param_name|
|
98
101
|
if params[param_name]
|
99
|
-
|
102
|
+
value = Array.try_convert(params[param_name]) ? params_param_name : [params[param_name]]
|
103
|
+
{:field => param_name.to_s.camelcase, :operator => 'IN', :values => value }
|
100
104
|
end
|
101
105
|
end.compact
|
102
106
|
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Adapi
|
4
|
+
class AdParam < Api
|
5
|
+
attr_accessor :ad_group_id, :criterion_id, :insertion_text, :param_index
|
6
|
+
|
7
|
+
validates_presence_of :ad_group_id, :criterion_id
|
8
|
+
|
9
|
+
def attributes
|
10
|
+
super.merge(
|
11
|
+
'ad_group_id' => ad_group_id, 'criterion_id' => criterion_id,
|
12
|
+
'insertion_text' => insertion_text, 'param_index' => param_index
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(params = {})
|
17
|
+
params[:service_name] = :AdParamService
|
18
|
+
|
19
|
+
%w{ ad_group_id criterion_id insertion_text param_index }.each do |param_name|
|
20
|
+
self.send "#{param_name}=", params[param_name.to_sym]
|
21
|
+
end
|
22
|
+
|
23
|
+
super(params)
|
24
|
+
end
|
25
|
+
|
26
|
+
def create
|
27
|
+
operation = {
|
28
|
+
:operator => 'SET',
|
29
|
+
:operand => serializable_hash
|
30
|
+
}
|
31
|
+
|
32
|
+
begin
|
33
|
+
response = @service.mutate([operation])
|
34
|
+
|
35
|
+
#rescue AdsCommon::Errors::HttpError => e
|
36
|
+
#self.errors.add(:base, e.message)
|
37
|
+
|
38
|
+
## traps any exceptions raised by AdWords API
|
39
|
+
#rescue AdwordsApi::Errors::ApiException => e
|
40
|
+
# # return PolicyViolations so they can be sent again
|
41
|
+
# e.errors.each do |error|
|
42
|
+
# if (error[:api_error_type] == 'PolicyViolationError') && error[:is_exemptable]
|
43
|
+
# self.errors.add(error[:api_error_type], error[:key])
|
44
|
+
# else
|
45
|
+
# # otherwise, just report the errors
|
46
|
+
# self.errors.add( "[#{self.xsi_type.underscore}]", "#{error[:error_string]} @ #{error[:field_path]}")
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
end
|
50
|
+
|
51
|
+
response
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.find(params = {})
|
55
|
+
params.symbolize_keys!
|
56
|
+
|
57
|
+
predicates = [ :ad_group_id, :criterion_id ].map do |param_name|
|
58
|
+
if params[param_name]
|
59
|
+
value = Array.try_convert(params[param_name]) ? params_param_name : [params[param_name]]
|
60
|
+
{:field => param_name.to_s.camelcase, :operator => 'IN', :values => value }
|
61
|
+
end
|
62
|
+
end.compact
|
63
|
+
|
64
|
+
selector = {
|
65
|
+
:fields => ['AdGroupId', 'CriterionId', 'InsertionText', 'ParamIndex'],
|
66
|
+
:predicates => predicates
|
67
|
+
}
|
68
|
+
|
69
|
+
response = AdParam.new.service.get(selector)
|
70
|
+
|
71
|
+
response = (response and response[:entries]) ? response[:entries] : []
|
72
|
+
|
73
|
+
response.map! do |ad_params_data|
|
74
|
+
AdParam.new(ad_params_data)
|
75
|
+
end
|
76
|
+
|
77
|
+
response
|
78
|
+
end
|
79
|
+
|
80
|
+
def serializable_hash
|
81
|
+
{
|
82
|
+
:ad_group_id => ad_group_id,
|
83
|
+
:criterion_id => criterion_id,
|
84
|
+
:param_index => param_index,
|
85
|
+
:insertion_text => insertion_text
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|