sem4r 0.1.2 → 0.1.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 +13 -2
- data/Gemfile.lock +13 -7
- data/README.rdoc +44 -7
- data/Rakefile +2 -122
- data/VERSION.yml +1 -1
- data/bin/sem +1 -0
- data/examples_sem4r/01_get_account.rb +1 -0
- data/examples_sem4r/02_get_info.rb +1 -0
- data/examples_sem4r/03_list_ad.rb +1 -0
- data/examples_sem4r/04_list_keywords.rb +1 -0
- data/examples_sem4r/05_request_report.rb +25 -14
- data/examples_sem4r/{05_request_report_2010.rb → 06_request_report_definition.rb} +2 -4
- data/examples_sem4r/{06_create_campaigns.rb → 07_create_campaigns.rb} +1 -0
- data/examples_sem4r/{07_create_campaigns_block.rb → 08_create_campaigns_block.rb} +1 -0
- data/examples_sem4r/{07_create_campaigns_simple.rb → 09_create_campaigns_simple.rb} +1 -0
- data/examples_sem4r/{08_ad_params.rb → 10_ad_params.rb} +1 -0
- data/examples_sem4r/{09_targeting_idea.rb → 11_targeting_idea.rb} +1 -0
- data/examples_sem4r/{10_get_location.rb → 12_get_location.rb} +1 -0
- data/examples_sem4r/{11_submit_bulk_job.rb → 13_submit_bulk_job.rb} +1 -1
- data/examples_sem4r/{12_list_bulk_job.rb → 14_list_bulk_job.rb} +3 -0
- data/examples_sem4r/30_prune_empty_adgroup.rb +1 -0
- data/examples_sem4r/31_empty_accounts.rb +1 -0
- data/examples_sem4r/example_helper.rb +2 -1
- data/lib/sem4r/ad_group/ad_group.rb +22 -7
- data/lib/sem4r/ad_group/ad_group_bids.rb +1 -1
- data/lib/sem4r/ad_group/ad_group_service.rb +12 -5
- data/lib/sem4r/ad_group_ad/ad_group_ad.rb +1 -0
- data/lib/sem4r/ad_group_ad/ad_group_ad_operations.rb +1 -0
- data/lib/sem4r/ad_group_ad/ad_group_ad_service.rb +14 -9
- data/lib/sem4r/ad_group_ad/ad_group_mobile_ad.rb +16 -25
- data/lib/sem4r/ad_group_ad/ad_group_text_ad.rb +18 -14
- data/lib/sem4r/ad_group_criterion/ad_group_criterion.rb +27 -19
- data/lib/sem4r/ad_group_criterion/ad_group_criterion_bids.rb +2 -1
- data/lib/sem4r/ad_group_criterion/ad_group_criterion_operations.rb +1 -0
- data/lib/sem4r/ad_group_criterion/ad_group_criterion_service.rb +9 -4
- data/lib/sem4r/ad_group_criterion/criterion.rb +1 -0
- data/lib/sem4r/ad_group_criterion/criterion_keyword.rb +1 -0
- data/lib/sem4r/ad_group_criterion/criterion_placement.rb +1 -0
- data/lib/sem4r/ad_param/ad_param.rb +24 -8
- data/lib/sem4r/ad_param/ad_param_service.rb +8 -5
- data/lib/sem4r/adwords.rb +232 -76
- data/lib/sem4r/base.rb +1 -1
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job.rb +41 -35
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_account_extension.rb +49 -15
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_selector.rb +29 -20
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_service.rb +13 -14
- data/lib/sem4r/bulk_mutate_job/job_operations.rb +1 -1
- data/lib/sem4r/campaign/campaign.rb +1 -0
- data/lib/sem4r/campaign/campaign_account_extension.rb +2 -1
- data/lib/sem4r/campaign/campaign_service.rb +9 -5
- data/lib/sem4r/credentials.rb +11 -5
- data/lib/sem4r/geo_location/address.rb +56 -0
- data/lib/sem4r/geo_location/geo_location_account_extension.rb +27 -6
- data/lib/sem4r/geo_location/geo_location_selector.rb +57 -0
- data/lib/sem4r/geo_location/geo_location_service.rb +9 -16
- data/lib/sem4r/info/info_account_extension.rb +3 -2
- data/lib/sem4r/info/info_selector.rb +2 -1
- data/lib/sem4r/info/info_service.rb +10 -3
- data/lib/sem4r/operation.rb +39 -20
- data/lib/sem4r/report_definition/report_definition.rb +37 -19
- data/lib/sem4r/report_definition/report_definition_account_extension.rb +66 -16
- data/lib/sem4r/report_definition/report_definition_operation.rb +2 -2
- data/lib/sem4r/report_definition/report_definition_selector.rb +1 -1
- data/lib/sem4r/report_definition/report_definition_service.rb +11 -7
- data/lib/sem4r/report_definition/report_field.rb +3 -2
- data/lib/sem4r/sem4r_templates.rb +77 -0
- data/lib/sem4r/{services/service.rb → service.rb} +23 -23
- data/lib/sem4r/targeting_idea/targeting_idea.rb +4 -4
- data/lib/sem4r/targeting_idea/targeting_idea_account_extension.rb +1 -1
- data/lib/sem4r/targeting_idea/targeting_idea_selector.rb +6 -6
- data/lib/sem4r/targeting_idea/targeting_idea_service.rb +7 -5
- data/lib/sem4r/v13_account/account_account_extension.rb +7 -10
- data/lib/sem4r/v13_account/account_service.rb +10 -4
- data/lib/sem4r/v13_account/billing_address.rb +2 -2
- data/lib/sem4r/v13_report/report_service.rb +11 -9
- data/lib/sem4r.rb +46 -31
- data/lib/{sem4r/cli → sem4r_cli}/cli_helpers.rb +3 -2
- data/lib/{sem4r/cli/cli_command.rb → sem4r_cli/cli_mini_framework.rb} +77 -32
- data/lib/{sem4r/cli/cli_common_args.rb → sem4r_cli/cli_sem.rb} +177 -157
- data/lib/{sem4r/cli/commands/cli_list_ads.rb → sem4r_cli/commands/cli_ads.rb} +7 -9
- data/lib/sem4r_cli/commands/cli_campaign.rb +69 -0
- data/lib/{sem4r/cli/commands/cli_list_client.rb → sem4r_cli/commands/cli_clients.rb} +2 -2
- data/lib/{sem4r/cli → sem4r_cli}/commands/cli_ideas.rb +35 -27
- data/lib/{sem4r/cli → sem4r_cli}/commands/cli_info.rb +3 -4
- data/lib/{sem4r/cli/commands/cli_repdef.rb → sem4r_cli/commands/cli_job.rb} +57 -55
- data/lib/{sem4r/cli/commands/cli_list_keywords.rb → sem4r_cli/commands/cli_keywords.rb} +14 -13
- data/lib/sem4r_cli/commands/cli_profile.rb +138 -0
- data/lib/{sem4r/cli/commands/cli_report.rb → sem4r_cli/commands/cli_repdef.rb} +67 -20
- data/lib/{sem4r/cli/commands/cli_request_report.rb → sem4r_cli/commands/cli_report.rb} +82 -19
- data/lib/sem4r_cli.rb +5 -7
- data/lib/sem4r_soap/http_connector.rb +206 -0
- data/lib/{soap_helpers → sem4r_soap}/soap_attributes.rb +8 -3
- data/lib/{sem4r/services → sem4r_soap}/soap_dumper.rb +36 -20
- data/lib/{sem4r/services → sem4r_soap}/soap_error.rb +5 -2
- data/lib/sem4r_soap/soap_response.rb +75 -0
- data/lib/sem4r_soap/soap_service.rb +137 -0
- data/lib/{sem4r/cli/cli_sem.rb → sem4r_soap/soap_service_v13.rb} +27 -28
- data/lib/sem4r_soap/soap_service_v2010.rb +80 -0
- data/lib/sem4r_soap.rb +17 -0
- data/sem4r.gemspec +93 -58
- data/spec/build_fixtures.rb +49 -42
- data/spec/fixtures/password.example.yml +3 -0
- data/spec/fixtures/sem4r.example.yml +6 -0
- data/spec/fixtures/{services/error.xml → soap_error.xml} +0 -0
- data/spec/fixtures/soap_error2.xml +29 -0
- data/spec/helpers/dump_interceptor.rb +90 -0
- data/spec/helpers/fixtures_bulk_mutate_job.rb +48 -0
- data/spec/helpers/fixtures_geo_location.rb +45 -0
- data/{lib/sem4r/campaign_criterion/campaign_criterion_service.rb → spec/helpers/fixtures_info.rb} +10 -6
- data/spec/helpers/fixtures_report_definition.rb +65 -0
- data/spec/{rspec_hash.rb → helpers/rspec_hash.rb} +1 -0
- data/spec/{rspec_matchers.rb → helpers/rspec_matchers.rb} +46 -19
- data/spec/{rspec_sem4r_helper.rb → helpers/rspec_sem4r_helper.rb} +19 -16
- data/spec/{sem4r_stubs.rb → helpers/sem4r_stubs.rb} +5 -4
- data/spec/rspec_helper.rb +4 -4
- data/spec/sem4r/account_spec.rb +2 -3
- data/spec/sem4r/ad_group/ad_group_service_spec.rb +1 -1
- data/spec/sem4r/ad_group/ad_group_spec.rb +1 -1
- data/spec/sem4r/ad_group_ad/ad_group_ad_operation_spec.rb +3 -3
- data/spec/sem4r/adwords_spec.rb +62 -39
- data/{lib/sem4r/cli/commands/cli_list_campaign.rb → spec/sem4r/bulk_mutate_job/bulk_mutate_job_selector_spec.rb} +14 -11
- data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_service_spec.rb +2 -3
- data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_spec.rb +23 -17
- data/spec/sem4r/bulk_mutate_job/fixtures/get-list_job-req.xml +35 -0
- data/spec/sem4r/bulk_mutate_job/fixtures/get-list_job-res.xml +417 -0
- data/spec/sem4r/bulk_mutate_job/fixtures/mutate-add_job-req.xml +106 -0
- data/spec/sem4r/bulk_mutate_job/fixtures/mutate-add_job-res.xml +48 -0
- data/spec/sem4r/bulk_mutate_job/job_operation_spec.rb +5 -7
- data/spec/sem4r/campaign/campaign_service_spec.rb +2 -2
- data/spec/sem4r/credentials_spec.rb +10 -8
- data/spec/sem4r/geo_location/address_spec.rb +62 -0
- data/spec/sem4r/geo_location/fixtures/get-req.xml +42 -0
- data/spec/sem4r/geo_location/fixtures/get-res.xml +60 -0
- data/spec/sem4r/info/fixtures/get-req.xml +34 -0
- data/spec/sem4r/info/fixtures/get-res.xml +27 -0
- data/spec/sem4r/nokogiri_parsing_spec.rb +1 -0
- data/spec/sem4r/operation_spec.rb +72 -0
- data/spec/sem4r/report_definition/fixtures/get-list-repdef-req.xml +22 -0
- data/spec/sem4r/report_definition/fixtures/get-list-repdef-res.xml +73 -0
- data/spec/sem4r/report_definition/fixtures/getReportFields-req.xml +24 -0
- data/spec/sem4r/report_definition/fixtures/getReportFields-res.xml +1199 -0
- data/spec/sem4r/report_definition/fixtures/mutate-add-report-req.xml +63 -0
- data/spec/sem4r/report_definition/fixtures/mutate-add-report-res.xml +68 -0
- data/spec/sem4r/report_definition/report_definition_service_spec.rb +5 -5
- data/spec/sem4r/report_definition/report_definition_spec.rb +28 -43
- data/{lib/sem4r/cli/commands/cli_list_report.rb → spec/sem4r/report_definition/report_field_spec.rb} +12 -10
- data/spec/sem4r/rexml_parsing_spec.rb +1 -0
- data/spec/sem4r/{services/service_spec.rb → service_spec.rb} +1 -1
- data/spec/sem4r/targeting_idea/targeting_idea_selector_spec.rb +1 -1
- data/spec/sem4r/targeting_idea/targeting_idea_service_spec.rb +1 -1
- data/spec/sem4r/targeting_idea/targeting_idea_spec.rb +1 -1
- data/spec/sem4r/v13_account/account_service_spec.rb +2 -2
- data/spec/sem4r/v13_report/report_service_spec.rb +2 -2
- data/spec/{sem4r/cli → sem4r_cli}/cli_spec.rb +22 -27
- data/spec/{soap_helpers → sem4r_soap}/soap_attributes_spec.rb +3 -3
- data/spec/{sem4r/services/soap_message_v13_spec.rb → sem4r_soap/soap_response_spec.rb} +10 -15
- data/spec/{sem4r/services/soap_call_spec.rb → sem4r_soap/soap_service_spec.rb} +35 -54
- data/tasks/jeweler.rake +66 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/sem4r.rake +25 -0
- data/tasks/yard.rake +31 -0
- metadata +173 -76
- data/lib/sem4r/ad_extension_override/ad_extension_override_service.rb +0 -30
- data/lib/sem4r/api_counters.rb +0 -8
- data/lib/sem4r/campaign_target/campaign_target_service.rb +0 -30
- data/lib/sem4r/cli/commands/cli_download_report.rb +0 -82
- data/lib/sem4r/services/http_connector.rb +0 -93
- data/lib/sem4r/services/soap_call.rb +0 -122
- data/lib/sem4r/services/soap_connector.rb +0 -139
- data/lib/sem4r/services/soap_message_v13.rb +0 -135
- data/lib/sem4r/services/soap_message_v2010.rb +0 -184
- data/lib/sem4r/v13_traffic_estimator/traffic_estimator_service.rb +0 -30
- data/spec/aggregates_rspec_helper.rb +0 -59
- data/spec/sem4r/report_definition/fixtures/mutate_add-req.xml +0 -24
- data/spec/sem4r/report_definition/fixtures/mutate_add-req_orig.xml +0 -45
data/lib/sem4r/adwords.rb
CHANGED
|
@@ -27,80 +27,149 @@ module Sem4r
|
|
|
27
27
|
class Adwords
|
|
28
28
|
|
|
29
29
|
attr_reader :profile
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
class << self
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
|
|
33
|
+
#
|
|
34
|
+
# Initialize Adwords with sandbox profile
|
|
35
|
+
# @see Adwords#initialize
|
|
36
|
+
#
|
|
37
|
+
def sandbox(options = nil)
|
|
38
|
+
new("sandbox", options)
|
|
34
39
|
end
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
#
|
|
42
|
+
# Initialize Adwords with production profile
|
|
43
|
+
# @see Adwords#initialize
|
|
44
|
+
#
|
|
45
|
+
def production(options = nil)
|
|
46
|
+
new("production", options)
|
|
38
47
|
end
|
|
39
48
|
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
#
|
|
50
|
+
# @private
|
|
51
|
+
# Search configuration file in standard locations
|
|
52
|
+
# @return [OpenStruct] with
|
|
53
|
+
# :config_dir
|
|
54
|
+
# :config_file
|
|
55
|
+
# :password_file
|
|
56
|
+
#
|
|
57
|
+
def search_config
|
|
58
|
+
config_filename = "sem4r.yml"
|
|
59
|
+
password_filename = "sem4r_passwords.yml"
|
|
42
60
|
|
|
43
61
|
#
|
|
44
62
|
# try current directory
|
|
45
63
|
#
|
|
46
|
-
return File.expand_path(config_filename) if File.exists?( config_filename)
|
|
64
|
+
# return File.expand_path(config_filename) if File.exists?( config_filename)
|
|
47
65
|
|
|
48
66
|
#
|
|
49
67
|
# try ~/.sem4r/sem4r.yml
|
|
50
68
|
#
|
|
51
69
|
# require 'etc'
|
|
52
70
|
# homedir = Etc.getpwuid.dir
|
|
53
|
-
homedir
|
|
54
|
-
config_filepath
|
|
55
|
-
|
|
71
|
+
homedir = ENV['HOME']
|
|
72
|
+
config_filepath = File.join(homedir, ".sem4r", config_filename)
|
|
73
|
+
if File.exists?(config_filepath)
|
|
74
|
+
return OpenStruct.new(
|
|
75
|
+
:config_dir => File.join(homedir, ".sem4r"),
|
|
76
|
+
:config_file => config_filepath,
|
|
77
|
+
:password_file => File.join(homedir, ".sem4r", password_filename))
|
|
78
|
+
end
|
|
56
79
|
|
|
57
80
|
#
|
|
58
81
|
# try <home_sem4r>/config/sem4r
|
|
59
82
|
#
|
|
60
|
-
config_filepath =
|
|
61
|
-
|
|
83
|
+
config_filepath = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "config", config_filename))
|
|
84
|
+
if File.exists?(config_filepath)
|
|
85
|
+
return OpenStruct.new(
|
|
86
|
+
:config_dir => nil,
|
|
87
|
+
:config_file => config_filepath,
|
|
88
|
+
:password_file => nil)
|
|
89
|
+
end
|
|
62
90
|
|
|
63
|
-
config_filepath =
|
|
64
|
-
|
|
91
|
+
config_filepath = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "config", "sem4r.example.yml"))
|
|
92
|
+
if File.exists?(config_filepath)
|
|
93
|
+
return OpenStruct.new(
|
|
94
|
+
:config_dir => nil,
|
|
95
|
+
:config_file => config_filepath,
|
|
96
|
+
:password_file => nil)
|
|
97
|
+
end
|
|
65
98
|
|
|
66
|
-
|
|
99
|
+
OpenStruct.new(
|
|
100
|
+
:config_dir => nil,
|
|
101
|
+
:config_file => nil,
|
|
102
|
+
:password_file => nil)
|
|
67
103
|
end
|
|
68
104
|
|
|
69
|
-
|
|
70
|
-
|
|
105
|
+
#
|
|
106
|
+
# Return list of profile contained into config file
|
|
107
|
+
#
|
|
108
|
+
def profiles(profile_file = nil)
|
|
71
109
|
unless profile_file
|
|
72
|
-
|
|
110
|
+
config = search_config
|
|
111
|
+
unless config
|
|
112
|
+
raise Sem4rError, "config file 'sem4r' not found"
|
|
113
|
+
end
|
|
114
|
+
profile_file = config.config_file
|
|
73
115
|
end
|
|
74
116
|
# puts "Loaded profiles from #{profile_file}"
|
|
75
|
-
yaml
|
|
117
|
+
yaml = YAML::load(File.open(profile_file))
|
|
76
118
|
profiles = yaml['google_adwords']
|
|
77
119
|
profiles
|
|
78
120
|
end
|
|
79
121
|
end
|
|
80
122
|
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
|
|
86
|
-
|
|
123
|
+
#
|
|
124
|
+
# initialize Adwords lib
|
|
125
|
+
# profiles "sandbox" and "production" have blocked environment
|
|
126
|
+
#
|
|
127
|
+
# @param [String] profile name
|
|
128
|
+
# @param [Hash] options the options for profile.
|
|
129
|
+
#
|
|
130
|
+
# @option opts [String] :config_file default to "~/.sem4r/sem4r.yml"
|
|
131
|
+
# @option opts [String] :password_file default to "~/.sem4r/sem4r_password.yml"
|
|
132
|
+
#
|
|
133
|
+
# @option opts [String] :mutable default to true,
|
|
134
|
+
# if true doesn't call mutable api call
|
|
135
|
+
#
|
|
136
|
+
# @option opts [String] :environment "sandbox", "production"
|
|
137
|
+
# @option opts [String] :email
|
|
138
|
+
# @option opts [String] :password
|
|
139
|
+
# @option opts [String] :developer_token
|
|
140
|
+
#
|
|
141
|
+
# @example
|
|
142
|
+
# Adwords.new( "sandbox", {:email=>"..."} )
|
|
143
|
+
# Adwords.new( {:environment=>"...", email => "..." } )
|
|
144
|
+
# Adwords.new( "sandbox" )
|
|
145
|
+
# Adwords.new() # default to sandbox
|
|
146
|
+
#
|
|
147
|
+
def initialize(profile = "sandbox", options = nil)
|
|
148
|
+
all_keys = %w{environment email password developer_token mutable config_file password_file}
|
|
149
|
+
@logger = nil
|
|
87
150
|
if not options.nil?
|
|
88
|
-
# new( "
|
|
89
|
-
|
|
90
|
-
options
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
151
|
+
# new( "profile", {:email=>"..."} )
|
|
152
|
+
options = options.stringify_keys
|
|
153
|
+
options.assert_valid_keys(*all_keys)
|
|
154
|
+
|
|
155
|
+
config_file = options.delete("config_file")
|
|
156
|
+
password_file = options.delete("password_file")
|
|
157
|
+
configure(config_file, password_file)
|
|
158
|
+
|
|
159
|
+
@profile = profile.to_s
|
|
94
160
|
@options = load_config(@profile)
|
|
95
161
|
@options = @options.merge(options)
|
|
96
162
|
elsif profile.respond_to?(:keys)
|
|
97
163
|
# new( {:environment=>"...", email => "..." } )
|
|
98
|
-
|
|
99
|
-
|
|
164
|
+
options = profile.stringify_keys
|
|
165
|
+
options.assert_valid_keys( *(all_keys-%w{config_file password_file}) )
|
|
166
|
+
configure
|
|
167
|
+
@options = options
|
|
100
168
|
@profile = "anonymous_" + @options["environment"]
|
|
101
169
|
else
|
|
102
170
|
# new( "sandbox" )
|
|
103
171
|
@profile = profile.to_s
|
|
172
|
+
configure
|
|
104
173
|
@options = load_config(@profile)
|
|
105
174
|
end
|
|
106
175
|
if ["sandbox", "production"].include?(profile)
|
|
@@ -108,48 +177,124 @@ module Sem4r
|
|
|
108
177
|
@options["environment"] = profile
|
|
109
178
|
end
|
|
110
179
|
if @options["environment"] != profile
|
|
111
|
-
raise "you cannot use profile '#{profile}' with environment #{@options["environment"]}"
|
|
180
|
+
raise "you cannot use profile '#{profile}' with environment '#{@options["environment"]}'"
|
|
112
181
|
end
|
|
113
182
|
end
|
|
183
|
+
|
|
184
|
+
try_to_find_password_in_password_file unless @options["password"]
|
|
114
185
|
end
|
|
115
186
|
|
|
116
187
|
def to_s
|
|
117
188
|
"adwords profile: '#{profile}' config file: '#{config_file}'"
|
|
118
189
|
end
|
|
119
190
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
puts "#{config_filepath} not exists"
|
|
126
|
-
end
|
|
127
|
-
@config_filepath
|
|
191
|
+
#
|
|
192
|
+
# returns profiles contained into current config file
|
|
193
|
+
#
|
|
194
|
+
def profiles
|
|
195
|
+
self.class.profiles @config.config_file
|
|
128
196
|
end
|
|
129
197
|
|
|
130
|
-
#
|
|
198
|
+
#
|
|
199
|
+
# @return [String] the config file name
|
|
200
|
+
#
|
|
131
201
|
def config_file
|
|
132
|
-
return @
|
|
133
|
-
@
|
|
134
|
-
@
|
|
202
|
+
return @config.config_file if @config
|
|
203
|
+
@config = Adwords.search_config
|
|
204
|
+
unless @config
|
|
205
|
+
puts "cannot find configuration files"
|
|
206
|
+
end
|
|
207
|
+
@config.config_file
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
######################################################################################################
|
|
211
|
+
# Password
|
|
212
|
+
|
|
213
|
+
#
|
|
214
|
+
# password is defined?
|
|
215
|
+
#
|
|
216
|
+
def has_password?
|
|
217
|
+
!@options["password"].nil?
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
#
|
|
221
|
+
# set password
|
|
222
|
+
#
|
|
223
|
+
def password=(pwd)
|
|
224
|
+
if @account
|
|
225
|
+
raise "already connected cannot change password"
|
|
226
|
+
end
|
|
227
|
+
@options["password"]=pwd
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def try_to_find_password_in_password_file
|
|
231
|
+
return unless @config.password_file
|
|
232
|
+
return unless File.exists?(@config.password_file)
|
|
233
|
+
passwords = YAML.load(File.open(@config.password_file))
|
|
234
|
+
pass = passwords[@options["email"]]
|
|
235
|
+
@options["password"] = pass if pass
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def save_passwords
|
|
239
|
+
if @config.password_file.nil?
|
|
240
|
+
raise "cannot save password"
|
|
241
|
+
end
|
|
242
|
+
puts "save password in #{@config.password_file} (security warning!)"
|
|
243
|
+
passwords = {}
|
|
244
|
+
if File.exists?(@config.password_file)
|
|
245
|
+
passwords = YAML.load(File.open(@config.password_file))
|
|
246
|
+
end
|
|
247
|
+
passwords[@options["email"]] = @options["password"]
|
|
248
|
+
File.open(@config.password_file, "w") do |f|
|
|
249
|
+
f.write(passwords.to_yaml)
|
|
250
|
+
end
|
|
135
251
|
end
|
|
136
252
|
|
|
253
|
+
private
|
|
254
|
+
|
|
255
|
+
#
|
|
256
|
+
# @private
|
|
257
|
+
#
|
|
258
|
+
# force config paths
|
|
259
|
+
# and the config file
|
|
260
|
+
#
|
|
261
|
+
def configure(config_file = nil, password_file = nil)
|
|
262
|
+
@config = Adwords.search_config
|
|
263
|
+
unless config_file.nil?
|
|
264
|
+
config_file = File.expand_path(config_file)
|
|
265
|
+
unless File.exists?(config_file)
|
|
266
|
+
raise "#{config_file} not exists"
|
|
267
|
+
end
|
|
268
|
+
@config.config_file = config_file
|
|
269
|
+
end
|
|
270
|
+
unless password_file.nil?
|
|
271
|
+
password_file = File.expand_path(password_file)
|
|
272
|
+
@config.password_file = password_file
|
|
273
|
+
end
|
|
274
|
+
@config
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# @private
|
|
279
|
+
# load the configuration
|
|
137
280
|
def load_config(profile)
|
|
138
|
-
unless config_file
|
|
281
|
+
unless @config.config_file
|
|
139
282
|
raise Sem4rError, "config file 'sem4r' not found"
|
|
140
283
|
end
|
|
141
284
|
# puts "Loaded profiles from #{config_file}"
|
|
142
|
-
yaml
|
|
143
|
-
config
|
|
285
|
+
yaml = YAML::load(File.open(@config.config_file))
|
|
286
|
+
config = yaml['google_adwords'][profile]
|
|
144
287
|
config || {}
|
|
145
288
|
end
|
|
146
289
|
|
|
290
|
+
public
|
|
291
|
+
|
|
147
292
|
##########################################################################
|
|
148
293
|
# logging
|
|
149
294
|
|
|
150
|
-
def dump_soap_options(
|
|
295
|
+
def dump_soap_options(dump_options)
|
|
151
296
|
@dump_soap_options = dump_options
|
|
152
|
-
@connector.dump_soap_options(
|
|
297
|
+
@connector.dump_soap_options(dump_options) if @connector
|
|
153
298
|
end
|
|
154
299
|
|
|
155
300
|
def dump_soap?
|
|
@@ -166,13 +311,13 @@ module Sem4r
|
|
|
166
311
|
|
|
167
312
|
def logger= logger
|
|
168
313
|
unless logger.instance_of?(Logger)
|
|
169
|
-
file
|
|
170
|
-
file.sync
|
|
171
|
-
logger
|
|
314
|
+
file = File.open(logger, "a")
|
|
315
|
+
file.sync = true
|
|
316
|
+
logger = Logger.new(file)
|
|
172
317
|
logger.formatter = proc { |severity, datetime, progname, msg|
|
|
173
318
|
"#{datetime.strftime("%H:%M:%S")}: #{msg}\n"
|
|
174
319
|
}
|
|
175
|
-
end
|
|
320
|
+
end
|
|
176
321
|
@logger= logger
|
|
177
322
|
|
|
178
323
|
@connector.logger= logger if @connector
|
|
@@ -182,11 +327,20 @@ module Sem4r
|
|
|
182
327
|
@logger
|
|
183
328
|
end
|
|
184
329
|
|
|
330
|
+
##########################################################################
|
|
331
|
+
# others
|
|
332
|
+
|
|
333
|
+
#
|
|
334
|
+
# @return [Account]
|
|
335
|
+
#
|
|
185
336
|
def account
|
|
186
337
|
deferred_initialize unless @initialized
|
|
187
338
|
@account
|
|
188
339
|
end
|
|
189
340
|
|
|
341
|
+
#
|
|
342
|
+
# print api counters on stdout
|
|
343
|
+
#
|
|
190
344
|
def p_counters
|
|
191
345
|
operations = @counters[:operations]
|
|
192
346
|
units = @counters[:units]
|
|
@@ -199,12 +353,12 @@ module Sem4r
|
|
|
199
353
|
|
|
200
354
|
attr_reader :service
|
|
201
355
|
|
|
202
|
-
#
|
|
356
|
+
# @private
|
|
203
357
|
# TODO: credentials are necessary because you might use more then account/credentials
|
|
204
358
|
# at the same time
|
|
205
359
|
#
|
|
206
|
-
def add_counters(
|
|
207
|
-
counters.each_pair { |k,v|
|
|
360
|
+
def add_counters(credentials, counters)
|
|
361
|
+
counters.each_pair { |k, v|
|
|
208
362
|
@counters[k] ||= 0
|
|
209
363
|
@counters[k] += v
|
|
210
364
|
}
|
|
@@ -212,32 +366,34 @@ module Sem4r
|
|
|
212
366
|
|
|
213
367
|
private
|
|
214
368
|
|
|
369
|
+
# @private
|
|
370
|
+
# really initialize connections to the google server
|
|
371
|
+
#
|
|
215
372
|
def deferred_initialize
|
|
216
373
|
@initialized = true
|
|
217
374
|
|
|
218
|
-
@connector
|
|
375
|
+
@connector = Sem4rSoap::HttpConnector.get(@logger, :http_client)
|
|
219
376
|
@connector.dump_soap_options(@dump_soap_options) if @dump_soap_options
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
:mutable => @options["mutable"]
|
|
377
|
+
|
|
378
|
+
@credentials = Credentials.new(
|
|
379
|
+
:environment => @options["environment"],
|
|
380
|
+
:email => @options["email"],
|
|
381
|
+
:password => @options["password"],
|
|
382
|
+
:useragent => "Sem4r Adwords Ruby Client Library (http://github.com/sem4r/sem4r)",
|
|
383
|
+
:developer_token => @options["developer_token"],
|
|
384
|
+
:mutable => @options["mutable"]
|
|
229
385
|
)
|
|
230
386
|
@credentials.connector = @connector
|
|
231
387
|
|
|
232
|
-
@service
|
|
233
|
-
@account
|
|
234
|
-
@counters
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
388
|
+
@service = Service.new(@connector)
|
|
389
|
+
@account = Account.new(self, @credentials)
|
|
390
|
+
@counters = {
|
|
391
|
+
:operations => 0,
|
|
392
|
+
:response_time => 0,
|
|
393
|
+
:units => 0
|
|
238
394
|
}
|
|
239
395
|
end
|
|
240
396
|
|
|
241
397
|
end
|
|
242
398
|
|
|
243
|
-
end
|
|
399
|
+
end # module Sem4r
|
data/lib/sem4r/base.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
1
2
|
# -------------------------------------------------------------------------
|
|
2
3
|
# Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
|
|
3
4
|
#
|
|
@@ -19,21 +20,22 @@
|
|
|
19
20
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
21
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
22
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
-
#
|
|
23
23
|
# -------------------------------------------------------------------------
|
|
24
24
|
|
|
25
25
|
module Sem4r
|
|
26
26
|
class BulkMutateJob
|
|
27
|
-
include SoapAttributes
|
|
27
|
+
include Sem4rSoap::SoapAttributes
|
|
28
28
|
|
|
29
29
|
attr_reader :id
|
|
30
30
|
attr_accessor :campaign_id
|
|
31
31
|
attr_reader :operations
|
|
32
|
+
attr_accessor :num_parts
|
|
32
33
|
|
|
33
34
|
g_accessor :status
|
|
34
35
|
|
|
35
36
|
def initialize(&block)
|
|
36
37
|
@operations = []
|
|
38
|
+
@num_parts = 1
|
|
37
39
|
if block_given?
|
|
38
40
|
block.arity < 1 ? instance_eval(&block) : block.call(self)
|
|
39
41
|
end
|
|
@@ -48,54 +50,58 @@ module Sem4r
|
|
|
48
50
|
@operations.empty?
|
|
49
51
|
end
|
|
50
52
|
|
|
51
|
-
def add(
|
|
53
|
+
def add(something)
|
|
52
54
|
case something
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
raise "how you suppose I must do when incounter a #{something.class}?"
|
|
55
|
+
when AdGroupTextAd
|
|
56
|
+
ad_operation = AdGroupAdOperation.add something
|
|
57
|
+
else
|
|
58
|
+
raise "how you suppose I must do when incounter a #{something.class}?"
|
|
58
59
|
end
|
|
59
60
|
self
|
|
60
61
|
end
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<operationStreams>
|
|
72
|
-
<scopingEntityId>
|
|
73
|
-
<type>CAMPAIGN_ID</type>
|
|
74
|
-
<value>#{campaign_id}</value>
|
|
75
|
-
</scopingEntityId>
|
|
76
|
-
EOS
|
|
63
|
+
#
|
|
64
|
+
# @private
|
|
65
|
+
#
|
|
66
|
+
def _xml(t)
|
|
67
|
+
if @id
|
|
68
|
+
t.id id
|
|
69
|
+
else
|
|
70
|
+
t.request do |t|
|
|
71
|
+
t.partIndex 0
|
|
77
72
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
t.operationStreams do |t|
|
|
74
|
+
t.scopingEntityId do |t|
|
|
75
|
+
t.type "CAMPAIGN_ID"
|
|
76
|
+
t.value campaign_id
|
|
77
|
+
end
|
|
78
|
+
@operations.each { |operation| operation.xml(t, "operations") } if @operations
|
|
79
|
+
end
|
|
81
80
|
end
|
|
81
|
+
t.numRequestParts num_parts
|
|
82
82
|
end
|
|
83
|
-
|
|
84
|
-
xml +=<<-EOS
|
|
85
|
-
</operationStreams>
|
|
86
|
-
</request>
|
|
87
|
-
<numRequestParts>1</numRequestParts>
|
|
88
|
-
EOS
|
|
83
|
+
end
|
|
89
84
|
|
|
85
|
+
#
|
|
86
|
+
# Marshal to xml using Builder
|
|
87
|
+
# @parm [Builder::XmlBuilder]
|
|
88
|
+
#
|
|
89
|
+
def xml(t, tag = nil)
|
|
90
90
|
if tag
|
|
91
|
-
|
|
91
|
+
t.__send__(tag, {"xsi:type"=>'BulkMutateJob'}) { |t| _xml(t) }
|
|
92
|
+
else
|
|
93
|
+
_xml(t)
|
|
92
94
|
end
|
|
93
95
|
end
|
|
94
96
|
|
|
97
|
+
def to_xml(tag = "operand")
|
|
98
|
+
xml(Builder::XmlMarkup.new, tag)
|
|
99
|
+
end
|
|
100
|
+
|
|
95
101
|
def self.from_element(el)
|
|
96
102
|
new do
|
|
97
|
-
@id
|
|
98
|
-
status
|
|
103
|
+
@id = el.at_xpath("id").text.strip.to_i
|
|
104
|
+
status el.at_xpath("status").text
|
|
99
105
|
end
|
|
100
106
|
end
|
|
101
107
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
1
2
|
# -------------------------------------------------------------------------
|
|
2
3
|
# Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
|
|
3
4
|
#
|
|
@@ -19,24 +20,41 @@
|
|
|
19
20
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
21
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
22
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
-
#
|
|
23
23
|
# -------------------------------------------------------------------------
|
|
24
24
|
|
|
25
25
|
module Sem4r
|
|
26
26
|
|
|
27
27
|
module BulkMutateJobAccountExtension
|
|
28
|
-
############################################################################
|
|
29
|
-
# Bulk Jobs
|
|
30
28
|
|
|
31
|
-
def
|
|
32
|
-
selector = BulkMutateJobSelector.new
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
jobs = els.map do |el|
|
|
37
|
-
BulkMutateJob.from_element(el)
|
|
29
|
+
def job_result(job_id)
|
|
30
|
+
selector = BulkMutateJobSelector.new do
|
|
31
|
+
jobId job_id
|
|
32
|
+
stats true
|
|
33
|
+
history true
|
|
38
34
|
end
|
|
35
|
+
soap_message = service.bulk_mutate_job.get(credentials, selector.to_xml)
|
|
36
|
+
add_counters(soap_message.counters)
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def job_delete(job_id)
|
|
41
|
+
job = BulkMutateJob.new
|
|
42
|
+
job.instance_eval { @id = job_id }
|
|
43
|
+
operation = JobOperation.remove(job)
|
|
44
|
+
|
|
45
|
+
soap_message = service.bulk_mutate_job.mutate(credentials, operation.to_xml("operation"))
|
|
46
|
+
add_counters(soap_message.counters)
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def job_mutate(bulk_mutate_job)
|
|
51
|
+
soap_message = service.bulk_mutate_job.mutate(credentials, bulk_mutate_job.to_xml("operation"))
|
|
52
|
+
add_counters(soap_message.counters)
|
|
53
|
+
el = soap_message.response.at_xpath("//rval")
|
|
54
|
+
BulkMutateJob.from_element(el)
|
|
55
|
+
end
|
|
39
56
|
|
|
57
|
+
def p_jobs
|
|
40
58
|
puts "#{jobs.length} bulk mutate jobs"
|
|
41
59
|
jobs.each do |job|
|
|
42
60
|
puts job.to_s
|
|
@@ -44,11 +62,27 @@ module Sem4r
|
|
|
44
62
|
self
|
|
45
63
|
end
|
|
46
64
|
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
def jobs(refresh = false)
|
|
66
|
+
_jobs unless @jobs and !refresh
|
|
67
|
+
@jobs
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def _jobs
|
|
73
|
+
selector = BulkMutateJobSelector.new do
|
|
74
|
+
status BulkMutateJobSelector::COMPLETED
|
|
75
|
+
status BulkMutateJobSelector::PROCESSING
|
|
76
|
+
status BulkMutateJobSelector::FAILED
|
|
77
|
+
status BulkMutateJobSelector::PENDING
|
|
78
|
+
end
|
|
79
|
+
soap_message = service.bulk_mutate_job.get(credentials, selector.to_xml)
|
|
80
|
+
add_counters(soap_message.counters)
|
|
81
|
+
els = soap_message.response.xpath("//getResponse/rval")
|
|
82
|
+
@jobs = els.map do |el|
|
|
83
|
+
BulkMutateJob.from_element(el)
|
|
84
|
+
end
|
|
85
|
+
@jobs
|
|
52
86
|
end
|
|
53
87
|
end
|
|
54
88
|
|