sem4r 0.1.1
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/LICENSE +22 -0
- data/README.rdoc +74 -0
- data/Rakefile +126 -0
- data/VERSION.yml +5 -0
- data/bin/sem +34 -0
- data/config/sem4r.example.yml +51 -0
- data/examples_blog/2009-11-29-hello-world.rb +12 -0
- data/examples_blog/2009-12-12-create-campaign.rb +29 -0
- data/examples_blog/2010-02-06-constants-scope.rb +8 -0
- data/examples_blog/2010-02-07-ad-parameters.rb +47 -0
- data/examples_sem4r/01_get_account.rb +37 -0
- data/examples_sem4r/02_get_info.rb +32 -0
- data/examples_sem4r/03_list_ad.rb +47 -0
- data/examples_sem4r/04_list_keywords.rb +54 -0
- data/examples_sem4r/05_request_report.rb +51 -0
- data/examples_sem4r/05_request_report_2010.rb +40 -0
- data/examples_sem4r/06_create_campaigns.rb +86 -0
- data/examples_sem4r/07_create_campaigns_block.rb +99 -0
- data/examples_sem4r/07_create_campaigns_simple.rb +65 -0
- data/examples_sem4r/08_ad_params.rb +70 -0
- data/examples_sem4r/09_targeting_idea.rb +59 -0
- data/examples_sem4r/10_get_location.rb +28 -0
- data/examples_sem4r/11_submit_bulk_job.rb +82 -0
- data/examples_sem4r/12_list_bulk_job.rb +29 -0
- data/examples_sem4r/30_prune_empty_adgroup.rb +50 -0
- data/examples_sem4r/31_empty_accounts.rb +40 -0
- data/examples_sem4r/example_helper.rb +115 -0
- data/lib/sem4r/account.rb +349 -0
- data/lib/sem4r/ad_extension_override/ad_extension_override_service.rb +30 -0
- data/lib/sem4r/ad_group/ad_group.rb +340 -0
- data/lib/sem4r/ad_group/ad_group_bids.rb +170 -0
- data/lib/sem4r/ad_group/ad_group_service.rb +75 -0
- data/lib/sem4r/ad_group/mobile_ad_image.rb +33 -0
- data/lib/sem4r/ad_group_ad/ad_group_ad.rb +98 -0
- data/lib/sem4r/ad_group_ad/ad_group_ad_operations.rb +38 -0
- data/lib/sem4r/ad_group_ad/ad_group_ad_service.rb +56 -0
- data/lib/sem4r/ad_group_ad/ad_group_mobile_ad.rb +137 -0
- data/lib/sem4r/ad_group_ad/ad_group_text_ad.rb +98 -0
- data/lib/sem4r/ad_group_criterion/ad_group_criterion.rb +144 -0
- data/lib/sem4r/ad_group_criterion/ad_group_criterion_bids.rb +115 -0
- data/lib/sem4r/ad_group_criterion/ad_group_criterion_operations.rb +35 -0
- data/lib/sem4r/ad_group_criterion/ad_group_criterion_service.rb +58 -0
- data/lib/sem4r/ad_group_criterion/criterion.rb +67 -0
- data/lib/sem4r/ad_group_criterion/criterion_keyword.rb +80 -0
- data/lib/sem4r/ad_group_criterion/criterion_placement.rb +66 -0
- data/lib/sem4r/ad_param/ad_param.rb +96 -0
- data/lib/sem4r/ad_param/ad_param_operation.rb +34 -0
- data/lib/sem4r/ad_param/ad_param_service.rb +59 -0
- data/lib/sem4r/adwords.rb +242 -0
- data/lib/sem4r/api_counters.rb +7 -0
- data/lib/sem4r/base.rb +43 -0
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job.rb +108 -0
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_selector.rb +56 -0
- data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_service.rb +57 -0
- data/lib/sem4r/bulk_mutate_job/job_operations.rb +35 -0
- data/lib/sem4r/campaign/campaign.rb +193 -0
- data/lib/sem4r/campaign/campaign_service.rb +91 -0
- data/lib/sem4r/campaign_criterion/campaign_criterion_service.rb +30 -0
- data/lib/sem4r/campaign_target/campaign_target_service.rb +30 -0
- data/lib/sem4r/cli/cli_command.rb +101 -0
- data/lib/sem4r/cli/cli_commands.rb +60 -0
- data/lib/sem4r/cli/cli_common_args.rb +293 -0
- data/lib/sem4r/cli/cli_download_report.rb +81 -0
- data/lib/sem4r/cli/cli_helpers.rb +57 -0
- data/lib/sem4r/cli/cli_ideas.rb +100 -0
- data/lib/sem4r/cli/cli_list_ads.rb +74 -0
- data/lib/sem4r/cli/cli_list_keywords.rb +76 -0
- data/lib/sem4r/cli/cli_request_report.rb +104 -0
- data/lib/sem4r/cli/cli_sem.rb +64 -0
- data/lib/sem4r/common/operation.rb +78 -0
- data/lib/sem4r/credentials.rb +86 -0
- data/lib/sem4r/extensions.rb +62 -0
- data/lib/sem4r/geo_location/geo_location_service.rb +59 -0
- data/lib/sem4r/info/info_service.rb +115 -0
- data/lib/sem4r/report_definition/report_definition.rb +104 -0
- data/lib/sem4r/report_definition/report_definition_operation.rb +34 -0
- data/lib/sem4r/report_definition/report_definition_selector.rb +31 -0
- data/lib/sem4r/report_definition/report_definition_service.rb +55 -0
- data/lib/sem4r/sem4r_error.rb +28 -0
- data/lib/sem4r/services/service.rb +74 -0
- data/lib/sem4r/services/soap_call.rb +100 -0
- data/lib/sem4r/services/soap_connector.rb +284 -0
- data/lib/sem4r/services/soap_error.rb +38 -0
- data/lib/sem4r/services/soap_message_v13.rb +129 -0
- data/lib/sem4r/services/soap_message_v2009.rb +170 -0
- data/lib/sem4r/soap_attributes.rb +141 -0
- data/lib/sem4r/targeting_idea/targeting_idea.rb +158 -0
- data/lib/sem4r/targeting_idea/targeting_idea_selector.rb +200 -0
- data/lib/sem4r/targeting_idea/targeting_idea_service.rb +51 -0
- data/lib/sem4r/v13_account/account_service.rb +54 -0
- data/lib/sem4r/v13_account/billing_address.rb +67 -0
- data/lib/sem4r/v13_report/report.rb +185 -0
- data/lib/sem4r/v13_report/report_job.rb +51 -0
- data/lib/sem4r/v13_report/report_service.rb +89 -0
- data/lib/sem4r/v13_traffic_estimator/traffic_estimator_service.rb +30 -0
- data/lib/sem4r.rb +142 -0
- data/lib/sem4r_cli.rb +40 -0
- data/sem4r.gemspec +247 -0
- data/spec/aggregates_spec_helper.rb +59 -0
- data/spec/fixtures/sem4r.example.yml +26 -0
- data/spec/fixtures/services/ad_group/get-first-req.xml +28 -0
- data/spec/fixtures/services/ad_group/get-first-res.xml +91 -0
- data/spec/fixtures/services/ad_group/get-manual-cpm-bids-req.xml +106 -0
- data/spec/fixtures/services/ad_group/get-manual-cpm-bids-res.xml +75 -0
- data/spec/fixtures/services/ad_group/mutate_add-req.xml +52 -0
- data/spec/fixtures/services/ad_group/mutate_add-res.xml +33 -0
- data/spec/fixtures/services/ad_group_ad/get_mobile_ad-req.xml +28 -0
- data/spec/fixtures/services/ad_group_ad/get_mobile_ad-res.xml +194 -0
- data/spec/fixtures/services/ad_group_ad/get_text_ad-req.xml +29 -0
- data/spec/fixtures/services/ad_group_ad/get_text_ad-res.xml +55 -0
- data/spec/fixtures/services/ad_group_ad/mutate_add_mobile_ad-req.xml +50 -0
- data/spec/fixtures/services/ad_group_ad/mutate_add_mobile_ad-res.xml +42 -0
- data/spec/fixtures/services/ad_group_ad/mutate_add_text_ad-req.xml +37 -0
- data/spec/fixtures/services/ad_group_ad/mutate_add_text_ad-res.xml +32 -0
- data/spec/fixtures/services/ad_group_ad/mutate_add_two_criterions-req.xml +83 -0
- data/spec/fixtures/services/ad_group_ad/mutate_add_two_criterions-res.xml +95 -0
- data/spec/fixtures/services/ad_group_criterion/get-req.xml +28 -0
- data/spec/fixtures/services/ad_group_criterion/get-res.xml +242 -0
- data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_keyword-req.xml +48 -0
- data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_keyword-res.xml +47 -0
- data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_placement-req.xml +35 -0
- data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_placement-res.xml +32 -0
- data/spec/fixtures/services/ad_group_criterion/mutate_add_negative_keyword-req.xml +37 -0
- data/spec/fixtures/services/ad_group_criterion/mutate_add_negative_keyword-res.xml +51 -0
- data/spec/fixtures/services/ad_param/mutate_set-req.xml +43 -0
- data/spec/fixtures/services/ad_param/mutate_set-res.xml +29 -0
- data/spec/fixtures/services/bulk_mutate_job/get-req.xml +36 -0
- data/spec/fixtures/services/bulk_mutate_job/get-res.xml +54 -0
- data/spec/fixtures/services/bulk_mutate_job/mutate-req.xml +69 -0
- data/spec/fixtures/services/bulk_mutate_job/mutate-res.xml +48 -0
- data/spec/fixtures/services/campaign/get-req.xml +37 -0
- data/spec/fixtures/services/campaign/get-res.xml +1986 -0
- data/spec/fixtures/services/campaign/mutate_add-req.xml +37 -0
- data/spec/fixtures/services/campaign/mutate_add-res.xml +42 -0
- data/spec/fixtures/services/error.xml +28 -0
- data/spec/fixtures/services/info/get_unit_count-req.xml +30 -0
- data/spec/fixtures/services/info/get_unit_count-res.xml +29 -0
- data/spec/fixtures/services/report_definition/mutate_add-req.xml +24 -0
- data/spec/fixtures/services/report_definition/mutate_add-req_orig.xml +45 -0
- data/spec/fixtures/services/targeting_idea/get-req-all-options.xml +57 -0
- data/spec/fixtures/services/targeting_idea/get-req.xml +60 -0
- data/spec/fixtures/services/targeting_idea/get-res.xml +3601 -0
- data/spec/fixtures/services/v13_account/get_account_info-req.xml +23 -0
- data/spec/fixtures/services/v13_account/get_account_info-res.xml +54 -0
- data/spec/fixtures/services/v13_account/get_client_accounts-req.xml +22 -0
- data/spec/fixtures/services/v13_account/get_client_accounts-res.xml +37 -0
- data/spec/fixtures/services/v13_report/get_all_jobs-req.xml +21 -0
- data/spec/fixtures/services/v13_report/get_all_jobs-res.xml +109 -0
- data/spec/fixtures/services/v13_report/schedule_report_job-req.xml +56 -0
- data/spec/fixtures/services/v13_report/schedule_report_job-res.xml +24 -0
- data/spec/sem4r/account_spec.rb +86 -0
- data/spec/sem4r/ad_group/ad_group_bids_spec.rb +67 -0
- data/spec/sem4r/ad_group/ad_group_service_spec.rb +66 -0
- data/spec/sem4r/ad_group/ad_group_spec.rb +212 -0
- data/spec/sem4r/ad_group_ad/ad_group_ad_operation_spec.rb +88 -0
- data/spec/sem4r/ad_group_ad/ad_group_ad_service_spec.rb +55 -0
- data/spec/sem4r/ad_group_ad/ad_group_ad_spec.rb +173 -0
- data/spec/sem4r/ad_group_criterion/ad_group_criterion_bids_spec.rb +60 -0
- data/spec/sem4r/ad_group_criterion/ad_group_criterion_service_spec.rb +55 -0
- data/spec/sem4r/ad_group_criterion/ad_group_criterion_spec.rb +103 -0
- data/spec/sem4r/ad_group_criterion/criterion_spec.rb +85 -0
- data/spec/sem4r/ad_param/ad_param_service_spec.rb +55 -0
- data/spec/sem4r/ad_param/ad_param_spec.rb +59 -0
- data/spec/sem4r/adwords_spec.rb +110 -0
- data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_service_spec.rb +63 -0
- data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_spec.rb +69 -0
- data/spec/sem4r/bulk_mutate_job/job_operation_spec.rb +48 -0
- data/spec/sem4r/campaign/campaign_service_spec.rb +66 -0
- data/spec/sem4r/campaign/campaign_spec.rb +105 -0
- data/spec/sem4r/cli/cli_spec.rb +71 -0
- data/spec/sem4r/credentials_spec.rb +65 -0
- data/spec/sem4r/report_definition/report_definition_service_spec.rb +44 -0
- data/spec/sem4r/report_definition/report_definition_spec.rb +105 -0
- data/spec/sem4r/rexml_parsing_spec.rb +103 -0
- data/spec/sem4r/services/service_spec.rb +36 -0
- data/spec/sem4r/services/soap_call_spec.rb +115 -0
- data/spec/sem4r/services/soap_message_v13_spec.rb +54 -0
- data/spec/sem4r/soap_attributes_spec.rb +116 -0
- data/spec/sem4r/targeting_idea/targeting_idea_selector_spec.rb +120 -0
- data/spec/sem4r/targeting_idea/targeting_idea_service_spec.rb +44 -0
- data/spec/sem4r/targeting_idea/targeting_idea_spec.rb +53 -0
- data/spec/sem4r/v13_account/account_service_spec.rb +60 -0
- data/spec/sem4r/v13_report/report_service_spec.rb +104 -0
- data/spec/sem4r/v13_report/report_spec.rb +79 -0
- data/spec/sem4r_spec_helper.rb +353 -0
- data/spec/spec_helper.rb +12 -0
- metadata +375 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
# a copy of this software and associated documentation files (the
|
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
+
# the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be
|
|
13
|
+
# included in all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
#
|
|
23
|
+
# -------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
module Sem4r
|
|
26
|
+
|
|
27
|
+
class CliCommonArgs
|
|
28
|
+
|
|
29
|
+
def initialize
|
|
30
|
+
# defaults
|
|
31
|
+
@options = OpenStruct.new({
|
|
32
|
+
:verbose => true,
|
|
33
|
+
:force => false,
|
|
34
|
+
:default_logging => true,
|
|
35
|
+
# :dump_soap_to_file => true,
|
|
36
|
+
# :dump_soap_to_directory => true,
|
|
37
|
+
:profile => 'sandbox'
|
|
38
|
+
})
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def parse_and_run(argv)
|
|
42
|
+
parse(argv)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def parse(argv)
|
|
46
|
+
|
|
47
|
+
begin
|
|
48
|
+
rest = opt_parser(@options).parse(argv)
|
|
49
|
+
rescue OptionParser::AmbiguousOption => e
|
|
50
|
+
puts e.message
|
|
51
|
+
return false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if rest.length > 0
|
|
55
|
+
# cannot happen
|
|
56
|
+
puts "unknow options #{rest.join}"
|
|
57
|
+
return false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
if @options.list_profiles
|
|
61
|
+
puts "Profiles:"
|
|
62
|
+
profiles = Adwords.profiles( @options.config_name )
|
|
63
|
+
names = profiles.keys.map &:to_s
|
|
64
|
+
names.sort.each do |s|
|
|
65
|
+
puts " #{s} (#{profiles[s]['email']})"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
!@options.exit
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def opt_parser(options)
|
|
73
|
+
opt_parser = OptionParser.new
|
|
74
|
+
opt_parser.banner = "Sem is a simple command line interface using the sem4r library."
|
|
75
|
+
opt_parser.separator "It's alpha software, please don't use in production or use it at your risk!"
|
|
76
|
+
opt_parser.separator "Further information: http://www.sem4r.com"
|
|
77
|
+
opt_parser.separator ""
|
|
78
|
+
opt_parser.separator "Usage: sem [options] [COMMAND [command options]]"
|
|
79
|
+
|
|
80
|
+
opt_parser.separator ""
|
|
81
|
+
opt_parser.separator "execute COMMAND to an adwords account using google adwords api"
|
|
82
|
+
opt_parser.separator "To view help and options for a particular command, use 'sem COMMAND -h'"
|
|
83
|
+
|
|
84
|
+
#
|
|
85
|
+
# common options
|
|
86
|
+
#
|
|
87
|
+
opt_parser.separator ""
|
|
88
|
+
opt_parser.separator "common options: "
|
|
89
|
+
opt_parser.separator ""
|
|
90
|
+
|
|
91
|
+
opt_parser.on("-h", "--help", "show this message") do
|
|
92
|
+
puts opt_parser
|
|
93
|
+
options.exit = true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
opt_parser.on("--version", "show the sem4r version") do
|
|
97
|
+
puts "sem4r version #{Sem4r::version}"
|
|
98
|
+
options.exit = true
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
opt_parser.on("-v", "--[no-]verbose", "run verbosely") do |v|
|
|
102
|
+
options.verbose = v
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
opt_parser.on("-q", "--quiet", "quiet mode as --no-verbose") do |v|
|
|
106
|
+
options.verbose = false
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
opt_parser.on("-l", "--list-commands", "list commands") do
|
|
110
|
+
puts "SEM commands are:"
|
|
111
|
+
commands = CliCommand.commands.values.sort {|a,b| a.command <=> b.command }
|
|
112
|
+
commands.each do |cmd|
|
|
113
|
+
printf " %-20s %s\n", cmd.command, cmd.description
|
|
114
|
+
end
|
|
115
|
+
puts
|
|
116
|
+
puts "For help on a particular command, use 'sem COMMAND -h'"
|
|
117
|
+
options.exit = true
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
opt_parser.separator ""
|
|
121
|
+
opt_parser.separator "logging options: "
|
|
122
|
+
opt_parser.separator ""
|
|
123
|
+
|
|
124
|
+
opt_parser.on("--log FILE", "log sem4r messages to file") do |v|
|
|
125
|
+
options.logger = v
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
opt_parser.on("-f", "--dump-file FILE", "dump soap conversation to file") do |v|
|
|
129
|
+
options.dump_soap_to_file = v
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
str = "dump soap conversation to directory: each \n"
|
|
133
|
+
str << (" " * 37) + "request/response is in a single file"
|
|
134
|
+
opt_parser.on("-d", "--dump-dir DIRECTORY", str) do |v|
|
|
135
|
+
options.dump_soap_to_directory = v
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
opt_parser.separator ""
|
|
139
|
+
opt_parser.separator "profile and credentials options: "
|
|
140
|
+
opt_parser.separator " credentials options overwrite profile attributes"
|
|
141
|
+
opt_parser.separator ""
|
|
142
|
+
|
|
143
|
+
str = "file where profiles are defined\n"
|
|
144
|
+
str << (" " * 37) + "(default $HOME/.sem4r/sem4r.yaml)"
|
|
145
|
+
opt_parser.on("--config CONFIG", str) do |config|
|
|
146
|
+
options.config_name = config
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
opt_parser.on("--list-profiles", "list profiles") do
|
|
150
|
+
options.list_profiles = true
|
|
151
|
+
options.exit = true
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
opt_parser.on("-p", "--profile PROFILE", "select profile (default is sandbox)") do |profile|
|
|
155
|
+
options.profile = profile
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# email
|
|
159
|
+
opt_parser.on("--email EMAIL",
|
|
160
|
+
"email of adwords account") do |email|
|
|
161
|
+
options.email = email
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# password
|
|
165
|
+
opt_parser.on("--password PASSWORD",
|
|
166
|
+
"password of adwords account") do |password|
|
|
167
|
+
options.password = password
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# developer token
|
|
171
|
+
opt_parser.on("--token TOKEN",
|
|
172
|
+
"developer token to access adwords api") do |token|
|
|
173
|
+
options.developer_token = token
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# client account
|
|
177
|
+
opt_parser.on("-c", "--client EMAIL",
|
|
178
|
+
"email for client account") do |email|
|
|
179
|
+
options.client_email = email
|
|
180
|
+
end
|
|
181
|
+
opt_parser.separator ""
|
|
182
|
+
opt_parser
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def read_credentials_from_console
|
|
186
|
+
unless @options.environment
|
|
187
|
+
# The new and improved choose()...
|
|
188
|
+
say("\nThis is the new mode (default)...")
|
|
189
|
+
choose do |menu|
|
|
190
|
+
menu.prompt = "Please choose your favorite programming language? "
|
|
191
|
+
|
|
192
|
+
menu.choice :ruby do say("Good choice!") end
|
|
193
|
+
menu.choices(:python, :perl) do say("Not from around here, are you?") end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
@options.email ||= ask("Enter adwords email: ")
|
|
198
|
+
@options.password ||= ask("Enter adwords password: ") { |q| q.echo = "x" }
|
|
199
|
+
@options.developer_token ||= ask("Enter adwords developer_token: ")
|
|
200
|
+
|
|
201
|
+
config = {
|
|
202
|
+
:environment => @options.environment,
|
|
203
|
+
:email => @options.email,
|
|
204
|
+
:password => @options.password,
|
|
205
|
+
:developer_token => @options.developer_token
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
pp config
|
|
209
|
+
unless agree("credentials are correct?")
|
|
210
|
+
exit
|
|
211
|
+
end
|
|
212
|
+
config
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def account
|
|
216
|
+
#
|
|
217
|
+
# select profile
|
|
218
|
+
#
|
|
219
|
+
options = {}
|
|
220
|
+
if @options.config_name
|
|
221
|
+
options[:config_file] = @options.config_name
|
|
222
|
+
end
|
|
223
|
+
adwords = Adwords.new( @options.profile, options )
|
|
224
|
+
if @options.verbose
|
|
225
|
+
puts "using #{adwords.profile} profile"
|
|
226
|
+
puts "config file is #{adwords.config_file}"
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
#
|
|
230
|
+
# logging soap conversation
|
|
231
|
+
#
|
|
232
|
+
if @options.default_logging
|
|
233
|
+
configdir = File.join( ENV['HOME'], ".sem4r" )
|
|
234
|
+
unless File.exists?(configdir)
|
|
235
|
+
puts "Directory #{configdir} not exists"
|
|
236
|
+
FileUtils.mkdir(configdir)
|
|
237
|
+
end
|
|
238
|
+
dir = File.join(configdir, Time.new.strftime("%Y%m%d-soap-dump"))
|
|
239
|
+
@options.dump_soap_to_directory = dir
|
|
240
|
+
|
|
241
|
+
file = File.join(configdir, Time.new.strftime("%Y%m%d-sem4r-log"))
|
|
242
|
+
@options.logger = file
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
if @options.dump_soap_to_file
|
|
246
|
+
filename = @options.dump_soap_to_file
|
|
247
|
+
o = { :file => filename }
|
|
248
|
+
adwords.dump_soap_options( o )
|
|
249
|
+
end
|
|
250
|
+
if @options.dump_soap_to_directory
|
|
251
|
+
dir = @options.dump_soap_to_directory
|
|
252
|
+
o = { :directory => dir, :format => true }
|
|
253
|
+
adwords.dump_soap_options( o )
|
|
254
|
+
end
|
|
255
|
+
if @options.verbose and adwords.dump_soap?
|
|
256
|
+
puts "Logging soap conversation to '#{adwords.dump_soap_where}'"
|
|
257
|
+
end
|
|
258
|
+
if !adwords.dump_soap?
|
|
259
|
+
puts "it is highly recommended to activate the dump soap log"
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
#
|
|
263
|
+
# logging
|
|
264
|
+
#
|
|
265
|
+
# filename = File.join( tmp_dirname, File.basename(example_file).sub(/\.rb$/, ".log") )
|
|
266
|
+
if @options.logger
|
|
267
|
+
# filename = "sem.log"
|
|
268
|
+
adwords.logger = @options.logger
|
|
269
|
+
end
|
|
270
|
+
if adwords.logger
|
|
271
|
+
puts "Logger activated"
|
|
272
|
+
end
|
|
273
|
+
# adwords.logger = Logger.new(STDOUT)
|
|
274
|
+
|
|
275
|
+
#
|
|
276
|
+
# select account for command
|
|
277
|
+
#
|
|
278
|
+
if @options.client_email
|
|
279
|
+
account = adwords.account.client_accounts.find{ |c| c.client_email =~ /^#{@options.client_email}/ }
|
|
280
|
+
if account.nil?
|
|
281
|
+
puts "client account not found"
|
|
282
|
+
else
|
|
283
|
+
puts "using client #{account.client_email}"
|
|
284
|
+
end
|
|
285
|
+
else
|
|
286
|
+
account = adwords.account
|
|
287
|
+
end
|
|
288
|
+
return account
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
# a copy of this software and associated documentation files (the
|
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
+
# the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be
|
|
13
|
+
# included in all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
#
|
|
23
|
+
# -------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
module Sem4r
|
|
26
|
+
class CliDownloadReport < CliCommand
|
|
27
|
+
|
|
28
|
+
def self.command
|
|
29
|
+
"download"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.description
|
|
33
|
+
"download a generated report"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def initialize(common_args)
|
|
37
|
+
@common_args = common_args
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def command_opt_parser(options)
|
|
41
|
+
opt_parser = OptionParser.new
|
|
42
|
+
opt_parser.banner = "Usage #{self.class.command} [command_options ] <report_id>"
|
|
43
|
+
opt_parser.separator ""
|
|
44
|
+
opt_parser.separator "#{self.class.description}"
|
|
45
|
+
opt_parser.on("-h", "--help", "show this message") do
|
|
46
|
+
puts opt_parser
|
|
47
|
+
options.exit = true
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def parse_and_run(argv)
|
|
52
|
+
options = OpenStruct.new
|
|
53
|
+
rest = command_opt_parser(options).parse( argv )
|
|
54
|
+
if options.exit
|
|
55
|
+
return false
|
|
56
|
+
end
|
|
57
|
+
if rest.length != 1
|
|
58
|
+
puts "missing report id"
|
|
59
|
+
return false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
account = @common_args.account
|
|
63
|
+
report_id = rest[0].to_i
|
|
64
|
+
report = account.reports.find { |r| r.id == report_id }
|
|
65
|
+
if report.nil?
|
|
66
|
+
puts "report '#{report_id}' not found"
|
|
67
|
+
return false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
if report.status != 'Completed'
|
|
71
|
+
puts "cannot donwload report with status '#{report.status}'"
|
|
72
|
+
return false
|
|
73
|
+
end
|
|
74
|
+
report.download("test_report.xml")
|
|
75
|
+
|
|
76
|
+
account.adwords.p_counters
|
|
77
|
+
return true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Sem4r
|
|
2
|
+
# From http://gist.github.com/72234
|
|
3
|
+
#
|
|
4
|
+
# mysql-style output for an array of ActiveRecord objects
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# report(records) # displays report with all fields
|
|
8
|
+
# report(records, :field1, :field2, ...) # displays report with given fields
|
|
9
|
+
#
|
|
10
|
+
# Example:
|
|
11
|
+
# >> report(records, :id, :amount, :created_at)
|
|
12
|
+
# +------+-----------+--------------------------------+
|
|
13
|
+
# | id | amount | created_at |
|
|
14
|
+
# +------+-----------+--------------------------------+
|
|
15
|
+
# | 8301 | $12.40 | Sat Feb 28 09:20:47 -0800 2009 |
|
|
16
|
+
# | 6060 | $39.62 | Sun Feb 15 14:45:38 -0800 2009 |
|
|
17
|
+
# | 6061 | $167.52 | Sun Feb 15 14:45:38 -0800 2009 |
|
|
18
|
+
# | 6067 | $12.00 | Sun Feb 15 14:45:40 -0800 2009 |
|
|
19
|
+
# | 6059 | $1,000.00 | Sun Feb 15 14:45:38 -0800 2009 |
|
|
20
|
+
# +------+-----------+--------------------------------+
|
|
21
|
+
# 5 rows in set
|
|
22
|
+
#
|
|
23
|
+
def self.report(items, *fields)
|
|
24
|
+
# find max length for each field; start with the field names themselves
|
|
25
|
+
max_len = Hash[*fields.map {|f| [f, f.to_s.length]}.flatten]
|
|
26
|
+
|
|
27
|
+
items.each do |item|
|
|
28
|
+
fields.each do |field|
|
|
29
|
+
len = item.send(field).to_s.length
|
|
30
|
+
max_len[field] = len if len > max_len[field]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
border = '+-' + fields.map {|f| '-' * max_len[f] }.join('-+-') + '-+'
|
|
35
|
+
title_row = '| ' + fields.map {|f| sprintf("%-#{max_len[f]}s", f.to_s) }.join(' | ') + ' |'
|
|
36
|
+
|
|
37
|
+
puts border
|
|
38
|
+
puts title_row
|
|
39
|
+
puts border
|
|
40
|
+
|
|
41
|
+
items.each do |item|
|
|
42
|
+
row = '| ' + fields.map {|f| sprintf("%-#{max_len[f]}s", item.send(f)) }.join(' | ') + ' |'
|
|
43
|
+
puts row
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
puts border
|
|
47
|
+
puts "#{items.length} rows in set\n"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if __FILE__ == $0
|
|
52
|
+
require 'ostruct'
|
|
53
|
+
o = OpenStruct.new
|
|
54
|
+
o.a = 1
|
|
55
|
+
o.b = 2
|
|
56
|
+
Sem4r::report( [o], :a, :b )
|
|
57
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
# a copy of this software and associated documentation files (the
|
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
+
# the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be
|
|
13
|
+
# included in all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
#
|
|
23
|
+
# -------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
module Sem4r
|
|
26
|
+
|
|
27
|
+
class CliIdeas < CliCommand
|
|
28
|
+
|
|
29
|
+
def self.command
|
|
30
|
+
"ideas"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.description
|
|
34
|
+
"list ideas"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def initialize(common_args)
|
|
38
|
+
@common_args = common_args
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def opt_parser(options)
|
|
42
|
+
opt_parser = OptionParser.new
|
|
43
|
+
opt_parser.banner= "#{self.class.description}"
|
|
44
|
+
opt_parser.separator "Usage: sem [options] idea <keyword>"
|
|
45
|
+
|
|
46
|
+
opt_parser.separator ""
|
|
47
|
+
opt_parser.separator "common options: "
|
|
48
|
+
opt_parser.separator ""
|
|
49
|
+
opt_parser.on("-h", "--help", "show this message") do
|
|
50
|
+
puts opt_parser
|
|
51
|
+
options.exit = true
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def parse_and_run(argv)
|
|
56
|
+
options = OpenStruct.new
|
|
57
|
+
rest = opt_parser(options).parse( argv )
|
|
58
|
+
return false if options.exit
|
|
59
|
+
if rest.length < 1
|
|
60
|
+
puts "keyword missing see help"
|
|
61
|
+
return false
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
@keyword = rest[1]
|
|
65
|
+
account = @common_args.account
|
|
66
|
+
unless account
|
|
67
|
+
puts "select an account!"
|
|
68
|
+
else
|
|
69
|
+
_run account
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def _run(account)
|
|
74
|
+
ideas = account.targeting_idea do
|
|
75
|
+
idea_type "KEYWORD"
|
|
76
|
+
request_type "IDEAS"
|
|
77
|
+
|
|
78
|
+
related_to_keyword_search_parameter do
|
|
79
|
+
text @keyword
|
|
80
|
+
match_type 'EXACT'
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
items = []
|
|
85
|
+
ideas.each do |i|
|
|
86
|
+
i.each do |a|
|
|
87
|
+
next if a.class != TKeywordAttribute
|
|
88
|
+
o = OpenStruct.new
|
|
89
|
+
o.text = a.text
|
|
90
|
+
o.match = a.match_type
|
|
91
|
+
items << o
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
Sem4r::report(items, :text, :match)
|
|
96
|
+
account.adwords.p_counters
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
# a copy of this software and associated documentation files (the
|
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
+
# the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be
|
|
13
|
+
# included in all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
#
|
|
23
|
+
# -------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
module Sem4r
|
|
26
|
+
|
|
27
|
+
CliListAds = CliCommand.define_command("ads", "list ads") do |account|
|
|
28
|
+
puts "List AdGroup Advertising"
|
|
29
|
+
|
|
30
|
+
# if the accounts have client_accounts it is a master
|
|
31
|
+
client_accounts = account.client_accounts
|
|
32
|
+
if client_accounts.empty?
|
|
33
|
+
client_accounts = [account]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
items = []
|
|
37
|
+
need_newline = false
|
|
38
|
+
|
|
39
|
+
client_accounts.each do |client_account|
|
|
40
|
+
if need_newline
|
|
41
|
+
puts
|
|
42
|
+
need_newline = false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
#--
|
|
46
|
+
puts "examinate account '#{client_account.credentials.client_email}'"
|
|
47
|
+
client_account.campaigns.each do |campaign|
|
|
48
|
+
# puts "examinate campaign '#{campaign}'"
|
|
49
|
+
campaign.ad_groups.each do |ad_group|
|
|
50
|
+
# puts "examinate adgroup '#{ad_group}'"
|
|
51
|
+
ad_group.ads.each do |ad|
|
|
52
|
+
o = OpenStruct.new
|
|
53
|
+
o.client = client_account.credentials.client_email
|
|
54
|
+
o.campaign = campaign.name
|
|
55
|
+
o.ad_group = ad_group.name
|
|
56
|
+
o.url = ad.url
|
|
57
|
+
o.url = ad.display_url
|
|
58
|
+
items << o
|
|
59
|
+
end
|
|
60
|
+
print "."
|
|
61
|
+
need_newline = true
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
#--
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
if need_newline
|
|
68
|
+
puts
|
|
69
|
+
need_newline = false
|
|
70
|
+
end
|
|
71
|
+
report(items, :client, :campaign, :ad_group, :url, :display_url)
|
|
72
|
+
account.adwords.p_counters
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
# a copy of this software and associated documentation files (the
|
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
+
# the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be
|
|
13
|
+
# included in all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
#
|
|
23
|
+
# -------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
module Sem4r
|
|
26
|
+
|
|
27
|
+
CliListKeywords = CliCommand.define_command("keywords", "list keywords") do |account |
|
|
28
|
+
|
|
29
|
+
# if the account have client_accounts it is a master
|
|
30
|
+
client_accounts = account.client_accounts
|
|
31
|
+
if client_accounts.empty?
|
|
32
|
+
client_accounts = [account]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
items = []
|
|
36
|
+
need_newline = false
|
|
37
|
+
client_accounts.each do |client_account|
|
|
38
|
+
if need_newline
|
|
39
|
+
puts
|
|
40
|
+
need_newline = false
|
|
41
|
+
end
|
|
42
|
+
puts "examinate account '#{client_account.credentials.client_email}'"
|
|
43
|
+
client_account.campaigns.each do |campaign|
|
|
44
|
+
# puts "examinate campaign '#{campaign}'"
|
|
45
|
+
campaign.ad_groups.each do |ad_group|
|
|
46
|
+
ad_group.criterions.each do |criterion|
|
|
47
|
+
|
|
48
|
+
row = OpenStruct.new
|
|
49
|
+
row.client = client_account.credentials.client_email
|
|
50
|
+
row.campaign = campaign.name
|
|
51
|
+
row.ad_group = ad_group.name
|
|
52
|
+
|
|
53
|
+
row.type = criterion.type
|
|
54
|
+
case criterion.type
|
|
55
|
+
when Criterion::Keyword
|
|
56
|
+
row.text = criterion.text
|
|
57
|
+
row.match = criterion.match
|
|
58
|
+
when Criterion::Placement
|
|
59
|
+
row.text = criterion.url
|
|
60
|
+
end
|
|
61
|
+
items << row
|
|
62
|
+
print "."
|
|
63
|
+
need_newline = true
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
if need_newline
|
|
69
|
+
puts
|
|
70
|
+
need_newline = false
|
|
71
|
+
end
|
|
72
|
+
report(items, :client, :campaign, :ad_group, :type, :text, :match)
|
|
73
|
+
account.adwords.p_counters
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|