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.
Files changed (49) hide show
  1. data/.gitignore +3 -0
  2. data/GUIDELINES.markdown +61 -0
  3. data/README.markdown +79 -45
  4. data/Rakefile +31 -3
  5. data/adapi.gemspec +10 -3
  6. data/examples/add_ad_group.rb +1 -1
  7. data/examples/add_bare_ad_group.rb +1 -4
  8. data/examples/add_campaign.rb +1 -0
  9. data/examples/add_campaign_criteria.rb +27 -0
  10. data/examples/add_invalid_ad_group.rb +3 -1
  11. data/examples/add_invalid_text_ad.rb +1 -1
  12. data/examples/add_keywords.rb +1 -1
  13. data/examples/add_negative_campaign_criteria.rb +23 -0
  14. data/examples/add_text_ad.rb +1 -1
  15. data/examples/customize_configuration.rb +1 -2
  16. data/examples/delete_keyword.rb +1 -1
  17. data/examples/find_campaign.rb +5 -5
  18. data/examples/find_campaign_ad_groups.rb +1 -1
  19. data/examples/find_campaign_criteria.rb +103 -0
  20. data/examples/find_locations.rb +21 -0
  21. data/examples/rollback_campaign.rb +7 -6
  22. data/examples/update_campaign.rb +1 -1
  23. data/examples/update_campaign_status.rb +1 -1
  24. data/lib/adapi.rb +11 -9
  25. data/lib/adapi/ad/text_ad.rb +2 -1
  26. data/lib/adapi/ad_group.rb +13 -9
  27. data/lib/adapi/ad_param.rb +89 -0
  28. data/lib/adapi/api.rb +8 -0
  29. data/lib/adapi/campaign.rb +27 -18
  30. data/lib/adapi/campaign_criterion.rb +278 -0
  31. data/lib/adapi/campaign_target.rb +5 -123
  32. data/lib/adapi/config.rb +36 -31
  33. data/lib/adapi/constant_data.rb +13 -0
  34. data/lib/adapi/constant_data/language.rb +45 -0
  35. data/lib/adapi/keyword.rb +15 -5
  36. data/lib/adapi/location.rb +91 -0
  37. data/lib/adapi/version.rb +8 -1
  38. data/lib/httpi_request_monkeypatch.rb +4 -0
  39. data/test/config/adapi.yml.template +21 -0
  40. data/test/config/adwords_api.yml.template +10 -0
  41. data/test/integration/create_campaign_test.rb +54 -0
  42. data/test/test_helper.rb +2 -3
  43. data/test/unit/ad_group_test.rb +3 -4
  44. data/test/unit/ad_test.rb +1 -1
  45. data/test/unit/campaign_criterion_test.rb +23 -0
  46. data/test/unit/config_test.rb +52 -0
  47. metadata +48 -35
  48. data/examples/add_campaign_targets.rb +0 -26
  49. data/test/unit/campaign_target_test.rb +0 -51
@@ -2,7 +2,7 @@
2
2
  require 'adapi'
3
3
 
4
4
  # create ad group
5
- require File.join(File.dirname(__FILE__), 'add_bare_ad_group')
5
+ require_relative 'add_bare_ad_group'
6
6
 
7
7
  ad = Adapi::Ad::TextAd.create(
8
8
  :ad_group_id => $ad_group[:id],
@@ -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
- :path => File.expand_path(File.dirname(__FILE__)),
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
-
@@ -3,7 +3,7 @@
3
3
  require 'adapi'
4
4
 
5
5
  # create ad group
6
- require File.join(File.dirname(__FILE__), 'add_bare_ad_group')
6
+ require_relative 'add_bare_ad_group'
7
7
 
8
8
  $keywords = Adapi::Keyword.new(
9
9
  :ad_group_id => $ad_group[:id],
@@ -3,7 +3,7 @@
3
3
  require 'adapi'
4
4
 
5
5
  # create campaign
6
- require File.join(File.dirname(__FILE__), 'add_campaign')
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 "\nTargets:"
22
- $campaign[:targets].each do |target|
23
- p target[:xsi_type]
24
- p target
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):"
@@ -3,7 +3,7 @@
3
3
  require 'adapi'
4
4
 
5
5
  # create campaign
6
- require File.join(File.dirname(__FILE__), 'add_bare_ad_group')
6
+ require_relative 'add_bare_ad_group'
7
7
 
8
8
  # find all ad_groups for campaign
9
9
 
@@ -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 campaign by single command, with campaing targets, with ad_groups
6
- # including keywords and ads
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
- :targets => {
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 FAILS
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
-
@@ -1,7 +1,7 @@
1
1
  require 'adapi'
2
2
 
3
3
  # create campaign
4
- require File.join(File.dirname(__FILE__), 'add_bare_campaign')
4
+ require_relative 'add_bare_campaign'
5
5
 
6
6
  p "ORIGINAL CAMPAIGN:"
7
7
  pp $campaign.attributes
@@ -2,7 +2,7 @@
2
2
  require 'adapi'
3
3
 
4
4
  # create campaign
5
- require File.join(File.dirname(__FILE__), 'add_bare_campaign')
5
+ require_relative 'add_bare_campaign'
6
6
 
7
7
  p "ORIGINAL STATUS: %s" % $campaign.status
8
8
 
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 = :v201101
36
+ API_VERSION = :v201109
32
37
  end
33
38
 
34
- # check ruby version, should be 1.9
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
@@ -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
- {:field => param_name.to_s.camelcase, :operator => 'EQUALS', :values => params[param_name] }
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
 
@@ -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
- # althogh I'm not sure if just string makes sense in this case
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
- if @bids[:keyword_max_cpc] and not @bids[:keyword_max_cpc].is_a?(Hash)
35
- @bids[:keyword_max_cpc] = {
36
- :amount => {
37
- :micro_amount => Api.to_micro_units(@bids[:keyword_max_cpc])
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
- {:field => param_name.to_s.camelcase, :operator => 'EQUALS', :values => params[param_name] }
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