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.
Files changed (187) hide show
  1. data/LICENSE +22 -0
  2. data/README.rdoc +74 -0
  3. data/Rakefile +126 -0
  4. data/VERSION.yml +5 -0
  5. data/bin/sem +34 -0
  6. data/config/sem4r.example.yml +51 -0
  7. data/examples_blog/2009-11-29-hello-world.rb +12 -0
  8. data/examples_blog/2009-12-12-create-campaign.rb +29 -0
  9. data/examples_blog/2010-02-06-constants-scope.rb +8 -0
  10. data/examples_blog/2010-02-07-ad-parameters.rb +47 -0
  11. data/examples_sem4r/01_get_account.rb +37 -0
  12. data/examples_sem4r/02_get_info.rb +32 -0
  13. data/examples_sem4r/03_list_ad.rb +47 -0
  14. data/examples_sem4r/04_list_keywords.rb +54 -0
  15. data/examples_sem4r/05_request_report.rb +51 -0
  16. data/examples_sem4r/05_request_report_2010.rb +40 -0
  17. data/examples_sem4r/06_create_campaigns.rb +86 -0
  18. data/examples_sem4r/07_create_campaigns_block.rb +99 -0
  19. data/examples_sem4r/07_create_campaigns_simple.rb +65 -0
  20. data/examples_sem4r/08_ad_params.rb +70 -0
  21. data/examples_sem4r/09_targeting_idea.rb +59 -0
  22. data/examples_sem4r/10_get_location.rb +28 -0
  23. data/examples_sem4r/11_submit_bulk_job.rb +82 -0
  24. data/examples_sem4r/12_list_bulk_job.rb +29 -0
  25. data/examples_sem4r/30_prune_empty_adgroup.rb +50 -0
  26. data/examples_sem4r/31_empty_accounts.rb +40 -0
  27. data/examples_sem4r/example_helper.rb +115 -0
  28. data/lib/sem4r/account.rb +349 -0
  29. data/lib/sem4r/ad_extension_override/ad_extension_override_service.rb +30 -0
  30. data/lib/sem4r/ad_group/ad_group.rb +340 -0
  31. data/lib/sem4r/ad_group/ad_group_bids.rb +170 -0
  32. data/lib/sem4r/ad_group/ad_group_service.rb +75 -0
  33. data/lib/sem4r/ad_group/mobile_ad_image.rb +33 -0
  34. data/lib/sem4r/ad_group_ad/ad_group_ad.rb +98 -0
  35. data/lib/sem4r/ad_group_ad/ad_group_ad_operations.rb +38 -0
  36. data/lib/sem4r/ad_group_ad/ad_group_ad_service.rb +56 -0
  37. data/lib/sem4r/ad_group_ad/ad_group_mobile_ad.rb +137 -0
  38. data/lib/sem4r/ad_group_ad/ad_group_text_ad.rb +98 -0
  39. data/lib/sem4r/ad_group_criterion/ad_group_criterion.rb +144 -0
  40. data/lib/sem4r/ad_group_criterion/ad_group_criterion_bids.rb +115 -0
  41. data/lib/sem4r/ad_group_criterion/ad_group_criterion_operations.rb +35 -0
  42. data/lib/sem4r/ad_group_criterion/ad_group_criterion_service.rb +58 -0
  43. data/lib/sem4r/ad_group_criterion/criterion.rb +67 -0
  44. data/lib/sem4r/ad_group_criterion/criterion_keyword.rb +80 -0
  45. data/lib/sem4r/ad_group_criterion/criterion_placement.rb +66 -0
  46. data/lib/sem4r/ad_param/ad_param.rb +96 -0
  47. data/lib/sem4r/ad_param/ad_param_operation.rb +34 -0
  48. data/lib/sem4r/ad_param/ad_param_service.rb +59 -0
  49. data/lib/sem4r/adwords.rb +242 -0
  50. data/lib/sem4r/api_counters.rb +7 -0
  51. data/lib/sem4r/base.rb +43 -0
  52. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job.rb +108 -0
  53. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_selector.rb +56 -0
  54. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_service.rb +57 -0
  55. data/lib/sem4r/bulk_mutate_job/job_operations.rb +35 -0
  56. data/lib/sem4r/campaign/campaign.rb +193 -0
  57. data/lib/sem4r/campaign/campaign_service.rb +91 -0
  58. data/lib/sem4r/campaign_criterion/campaign_criterion_service.rb +30 -0
  59. data/lib/sem4r/campaign_target/campaign_target_service.rb +30 -0
  60. data/lib/sem4r/cli/cli_command.rb +101 -0
  61. data/lib/sem4r/cli/cli_commands.rb +60 -0
  62. data/lib/sem4r/cli/cli_common_args.rb +293 -0
  63. data/lib/sem4r/cli/cli_download_report.rb +81 -0
  64. data/lib/sem4r/cli/cli_helpers.rb +57 -0
  65. data/lib/sem4r/cli/cli_ideas.rb +100 -0
  66. data/lib/sem4r/cli/cli_list_ads.rb +74 -0
  67. data/lib/sem4r/cli/cli_list_keywords.rb +76 -0
  68. data/lib/sem4r/cli/cli_request_report.rb +104 -0
  69. data/lib/sem4r/cli/cli_sem.rb +64 -0
  70. data/lib/sem4r/common/operation.rb +78 -0
  71. data/lib/sem4r/credentials.rb +86 -0
  72. data/lib/sem4r/extensions.rb +62 -0
  73. data/lib/sem4r/geo_location/geo_location_service.rb +59 -0
  74. data/lib/sem4r/info/info_service.rb +115 -0
  75. data/lib/sem4r/report_definition/report_definition.rb +104 -0
  76. data/lib/sem4r/report_definition/report_definition_operation.rb +34 -0
  77. data/lib/sem4r/report_definition/report_definition_selector.rb +31 -0
  78. data/lib/sem4r/report_definition/report_definition_service.rb +55 -0
  79. data/lib/sem4r/sem4r_error.rb +28 -0
  80. data/lib/sem4r/services/service.rb +74 -0
  81. data/lib/sem4r/services/soap_call.rb +100 -0
  82. data/lib/sem4r/services/soap_connector.rb +284 -0
  83. data/lib/sem4r/services/soap_error.rb +38 -0
  84. data/lib/sem4r/services/soap_message_v13.rb +129 -0
  85. data/lib/sem4r/services/soap_message_v2009.rb +170 -0
  86. data/lib/sem4r/soap_attributes.rb +141 -0
  87. data/lib/sem4r/targeting_idea/targeting_idea.rb +158 -0
  88. data/lib/sem4r/targeting_idea/targeting_idea_selector.rb +200 -0
  89. data/lib/sem4r/targeting_idea/targeting_idea_service.rb +51 -0
  90. data/lib/sem4r/v13_account/account_service.rb +54 -0
  91. data/lib/sem4r/v13_account/billing_address.rb +67 -0
  92. data/lib/sem4r/v13_report/report.rb +185 -0
  93. data/lib/sem4r/v13_report/report_job.rb +51 -0
  94. data/lib/sem4r/v13_report/report_service.rb +89 -0
  95. data/lib/sem4r/v13_traffic_estimator/traffic_estimator_service.rb +30 -0
  96. data/lib/sem4r.rb +142 -0
  97. data/lib/sem4r_cli.rb +40 -0
  98. data/sem4r.gemspec +247 -0
  99. data/spec/aggregates_spec_helper.rb +59 -0
  100. data/spec/fixtures/sem4r.example.yml +26 -0
  101. data/spec/fixtures/services/ad_group/get-first-req.xml +28 -0
  102. data/spec/fixtures/services/ad_group/get-first-res.xml +91 -0
  103. data/spec/fixtures/services/ad_group/get-manual-cpm-bids-req.xml +106 -0
  104. data/spec/fixtures/services/ad_group/get-manual-cpm-bids-res.xml +75 -0
  105. data/spec/fixtures/services/ad_group/mutate_add-req.xml +52 -0
  106. data/spec/fixtures/services/ad_group/mutate_add-res.xml +33 -0
  107. data/spec/fixtures/services/ad_group_ad/get_mobile_ad-req.xml +28 -0
  108. data/spec/fixtures/services/ad_group_ad/get_mobile_ad-res.xml +194 -0
  109. data/spec/fixtures/services/ad_group_ad/get_text_ad-req.xml +29 -0
  110. data/spec/fixtures/services/ad_group_ad/get_text_ad-res.xml +55 -0
  111. data/spec/fixtures/services/ad_group_ad/mutate_add_mobile_ad-req.xml +50 -0
  112. data/spec/fixtures/services/ad_group_ad/mutate_add_mobile_ad-res.xml +42 -0
  113. data/spec/fixtures/services/ad_group_ad/mutate_add_text_ad-req.xml +37 -0
  114. data/spec/fixtures/services/ad_group_ad/mutate_add_text_ad-res.xml +32 -0
  115. data/spec/fixtures/services/ad_group_ad/mutate_add_two_criterions-req.xml +83 -0
  116. data/spec/fixtures/services/ad_group_ad/mutate_add_two_criterions-res.xml +95 -0
  117. data/spec/fixtures/services/ad_group_criterion/get-req.xml +28 -0
  118. data/spec/fixtures/services/ad_group_criterion/get-res.xml +242 -0
  119. data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_keyword-req.xml +48 -0
  120. data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_keyword-res.xml +47 -0
  121. data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_placement-req.xml +35 -0
  122. data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_placement-res.xml +32 -0
  123. data/spec/fixtures/services/ad_group_criterion/mutate_add_negative_keyword-req.xml +37 -0
  124. data/spec/fixtures/services/ad_group_criterion/mutate_add_negative_keyword-res.xml +51 -0
  125. data/spec/fixtures/services/ad_param/mutate_set-req.xml +43 -0
  126. data/spec/fixtures/services/ad_param/mutate_set-res.xml +29 -0
  127. data/spec/fixtures/services/bulk_mutate_job/get-req.xml +36 -0
  128. data/spec/fixtures/services/bulk_mutate_job/get-res.xml +54 -0
  129. data/spec/fixtures/services/bulk_mutate_job/mutate-req.xml +69 -0
  130. data/spec/fixtures/services/bulk_mutate_job/mutate-res.xml +48 -0
  131. data/spec/fixtures/services/campaign/get-req.xml +37 -0
  132. data/spec/fixtures/services/campaign/get-res.xml +1986 -0
  133. data/spec/fixtures/services/campaign/mutate_add-req.xml +37 -0
  134. data/spec/fixtures/services/campaign/mutate_add-res.xml +42 -0
  135. data/spec/fixtures/services/error.xml +28 -0
  136. data/spec/fixtures/services/info/get_unit_count-req.xml +30 -0
  137. data/spec/fixtures/services/info/get_unit_count-res.xml +29 -0
  138. data/spec/fixtures/services/report_definition/mutate_add-req.xml +24 -0
  139. data/spec/fixtures/services/report_definition/mutate_add-req_orig.xml +45 -0
  140. data/spec/fixtures/services/targeting_idea/get-req-all-options.xml +57 -0
  141. data/spec/fixtures/services/targeting_idea/get-req.xml +60 -0
  142. data/spec/fixtures/services/targeting_idea/get-res.xml +3601 -0
  143. data/spec/fixtures/services/v13_account/get_account_info-req.xml +23 -0
  144. data/spec/fixtures/services/v13_account/get_account_info-res.xml +54 -0
  145. data/spec/fixtures/services/v13_account/get_client_accounts-req.xml +22 -0
  146. data/spec/fixtures/services/v13_account/get_client_accounts-res.xml +37 -0
  147. data/spec/fixtures/services/v13_report/get_all_jobs-req.xml +21 -0
  148. data/spec/fixtures/services/v13_report/get_all_jobs-res.xml +109 -0
  149. data/spec/fixtures/services/v13_report/schedule_report_job-req.xml +56 -0
  150. data/spec/fixtures/services/v13_report/schedule_report_job-res.xml +24 -0
  151. data/spec/sem4r/account_spec.rb +86 -0
  152. data/spec/sem4r/ad_group/ad_group_bids_spec.rb +67 -0
  153. data/spec/sem4r/ad_group/ad_group_service_spec.rb +66 -0
  154. data/spec/sem4r/ad_group/ad_group_spec.rb +212 -0
  155. data/spec/sem4r/ad_group_ad/ad_group_ad_operation_spec.rb +88 -0
  156. data/spec/sem4r/ad_group_ad/ad_group_ad_service_spec.rb +55 -0
  157. data/spec/sem4r/ad_group_ad/ad_group_ad_spec.rb +173 -0
  158. data/spec/sem4r/ad_group_criterion/ad_group_criterion_bids_spec.rb +60 -0
  159. data/spec/sem4r/ad_group_criterion/ad_group_criterion_service_spec.rb +55 -0
  160. data/spec/sem4r/ad_group_criterion/ad_group_criterion_spec.rb +103 -0
  161. data/spec/sem4r/ad_group_criterion/criterion_spec.rb +85 -0
  162. data/spec/sem4r/ad_param/ad_param_service_spec.rb +55 -0
  163. data/spec/sem4r/ad_param/ad_param_spec.rb +59 -0
  164. data/spec/sem4r/adwords_spec.rb +110 -0
  165. data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_service_spec.rb +63 -0
  166. data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_spec.rb +69 -0
  167. data/spec/sem4r/bulk_mutate_job/job_operation_spec.rb +48 -0
  168. data/spec/sem4r/campaign/campaign_service_spec.rb +66 -0
  169. data/spec/sem4r/campaign/campaign_spec.rb +105 -0
  170. data/spec/sem4r/cli/cli_spec.rb +71 -0
  171. data/spec/sem4r/credentials_spec.rb +65 -0
  172. data/spec/sem4r/report_definition/report_definition_service_spec.rb +44 -0
  173. data/spec/sem4r/report_definition/report_definition_spec.rb +105 -0
  174. data/spec/sem4r/rexml_parsing_spec.rb +103 -0
  175. data/spec/sem4r/services/service_spec.rb +36 -0
  176. data/spec/sem4r/services/soap_call_spec.rb +115 -0
  177. data/spec/sem4r/services/soap_message_v13_spec.rb +54 -0
  178. data/spec/sem4r/soap_attributes_spec.rb +116 -0
  179. data/spec/sem4r/targeting_idea/targeting_idea_selector_spec.rb +120 -0
  180. data/spec/sem4r/targeting_idea/targeting_idea_service_spec.rb +44 -0
  181. data/spec/sem4r/targeting_idea/targeting_idea_spec.rb +53 -0
  182. data/spec/sem4r/v13_account/account_service_spec.rb +60 -0
  183. data/spec/sem4r/v13_report/report_service_spec.rb +104 -0
  184. data/spec/sem4r/v13_report/report_spec.rb +79 -0
  185. data/spec/sem4r_spec_helper.rb +353 -0
  186. data/spec/spec_helper.rb +12 -0
  187. metadata +375 -0
@@ -0,0 +1,340 @@
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
+ module Sem4r
25
+ class AdGroup < Base
26
+
27
+ #
28
+ # enum AdGroup.Status
29
+ #
30
+ enum :Statuses, [
31
+ :ENABLED,
32
+ :PAUSED,
33
+ :DELETED
34
+ ]
35
+
36
+ attr_reader :campaign
37
+
38
+ g_accessor :name
39
+ g_accessor :status
40
+
41
+ def initialize(campaign, name = nil, &block)
42
+ super( campaign.adwords, campaign.credentials )
43
+ @campaign = campaign
44
+ @id = nil
45
+ @criterions = nil
46
+ self.name = name unless name.nil?
47
+ if block_given?
48
+ @inside_initialize = true
49
+ block.arity < 1 ? instance_eval(&block) : block.call(self)
50
+ save unless campaign.inside_initialize?
51
+ end
52
+ @inside_initialize = false
53
+ end
54
+
55
+ def inside_initialize?
56
+ @inside_initialize
57
+ end
58
+
59
+ def id
60
+ _save unless @id
61
+ @id
62
+ end
63
+
64
+ def to_s
65
+ "#{@id ? @id : 'unsaved'} '#{@name}' (#{@status}) - #{@bid}"
66
+ end
67
+
68
+ def xml(t)
69
+ t.campaignId campaign.id
70
+ t.name name
71
+ t.status "ENABLED"
72
+ @bids.to_xml(t) if @bids
73
+ end
74
+
75
+ def to_xml(tag)
76
+ builder = Builder::XmlMarkup.new
77
+ builder.tag!(tag) { |t|
78
+ xml(t)
79
+ # xml.campaignId campaign.id
80
+ # xml.name name
81
+ # xml.status "ENABLED"
82
+ # @bids.to_xml(xml) if @bids
83
+ }
84
+ end
85
+
86
+ def empty?
87
+ criterions.empty?
88
+ end
89
+
90
+ ###########################################################################
91
+ # management
92
+
93
+ def self.create(campaign, &block)
94
+ new(campaign, &block).save
95
+ end
96
+
97
+ def self.from_element(campaign, el)
98
+ new(campaign) do
99
+ @id = el.elements["id"].text.strip.to_i
100
+ name el.elements["name"].text.strip
101
+ status el.elements["status"].text.strip
102
+ bids el.elements["bids"]
103
+ end
104
+ end
105
+
106
+ def save
107
+ _save
108
+ _save_criterions
109
+ _save_ads
110
+ _save_ad_params
111
+ self
112
+ end
113
+
114
+ private
115
+
116
+ def _save
117
+ unless @id
118
+ soap_message = service.ad_group.create(credentials, to_xml("operand"))
119
+ add_counters( soap_message.counters )
120
+ rval = REXML::XPath.first( soap_message.response, "//mutateResponse/rval")
121
+ id = REXML::XPath.match( rval, "value/id" ).first
122
+ @id = id.text.strip.to_i
123
+ end
124
+ end
125
+
126
+ public
127
+
128
+ def delete
129
+ soap_message = service.ad_group.delete(credentials, @id)
130
+ add_counters( soap_message.counters )
131
+ @id = -1 # logical delete
132
+ end
133
+
134
+ ###########################################################################
135
+ # bids management
136
+
137
+ def bids(el = nil)
138
+ @bids = AdGroupBids.from_element(el) if el
139
+ @bids
140
+ end
141
+
142
+ def manual_cpc_bids(&block)
143
+ @bids = ManualCPCAdGroupBids.new(&block)
144
+ end
145
+
146
+ def manual_cpm_bids(&block)
147
+ @bids = ManualCPMAdGroupBids.new(&block)
148
+ end
149
+
150
+ ###########################################################################
151
+ # ad management
152
+
153
+ def text_ad(&block)
154
+ ad = AdGroupTextAd.new(self, &block)
155
+ @ads ||= []
156
+ @ads.push(ad)
157
+ ad
158
+ end
159
+
160
+ def mobile_ad(&block)
161
+ ad = AdGroupMobileAd.new(self, &block)
162
+ @ads ||= []
163
+ @ads.push(ad)
164
+ ad
165
+ end
166
+
167
+ def ads(refresh = false)
168
+ _ads unless @ads and !refresh
169
+ @ads
170
+ end
171
+
172
+ def p_ads(refresh = false)
173
+ ads(refresh).each do |ad|
174
+ puts ad.to_s
175
+ end
176
+ self
177
+ end
178
+
179
+ private
180
+
181
+ def _ads
182
+ soap_message = service.ad_group_ad.all(credentials, id)
183
+ add_counters( soap_message.counters )
184
+ rval = REXML::XPath.first( soap_message.response, "//getResponse/rval")
185
+ els = REXML::XPath.match( rval, "entries/ad")
186
+ @ads = els.map do |el|
187
+ AdGroupAd.from_element( self, el )
188
+ end
189
+ end
190
+
191
+ def _save_ads
192
+ return unless @ads
193
+
194
+ unsaved_ads = @ads.select { |a| !a.saved? }
195
+ return if unsaved_ads.empty?
196
+
197
+ xml = unsaved_ads.inject('') do |xml,ad|
198
+ o = AdGroupAdOperation.new.add(ad)
199
+ xml + o.to_xml("operations")
200
+ end
201
+ soap_message = service.ad_group_ad.mutate(credentials, xml)
202
+ add_counters( soap_message.counters )
203
+ els = REXML::XPath.match( soap_message.response, "//mutateResponse/rval/value/ad/id")
204
+ els.each_with_index do |e,index|
205
+ id = e.text.strip.to_i
206
+ unsaved_ads[index].instance_eval{ @id = id }
207
+ end
208
+ end
209
+
210
+ public
211
+
212
+ ###########################################################################
213
+ # criterion management
214
+
215
+ def negative_keyword(text = nil, match = CriterionKeyword::BROAD, &block)
216
+ negative_criterion = NegativeAdGroupCriterion.new(self)
217
+ criterion = CriterionKeyword.new(self, text, match, &block)
218
+ negative_criterion.criterion = criterion
219
+ @criterions ||= []
220
+ @criterions.push( negative_criterion )
221
+ negative_criterion
222
+ end
223
+
224
+ #
225
+ # instanziate an BiddableAdGroupCriterion but it is called 'keyword' for convenience
226
+ #
227
+ # http://code.google.com/apis/adwords/v2009/docs/reference/AdGroupCriterionService.BiddableAdGroupCriterion.html
228
+ #
229
+ def keyword(text = nil, match = nil, &block)
230
+ biddable_criterion = BiddableAdGroupCriterion.new(self)
231
+ criterion = CriterionKeyword.new(self, text, match, &block)
232
+ biddable_criterion.criterion = criterion
233
+ @criterions ||= []
234
+ @criterions.push( biddable_criterion )
235
+ biddable_criterion
236
+ end
237
+
238
+ def placement(url = nil, &block)
239
+ biddable_criterion = BiddableAdGroupCriterion.new(self)
240
+ criterion = CriterionPlacement.new(self, url, &block)
241
+ biddable_criterion.criterion = criterion
242
+ @criterions ||= []
243
+ @criterions.push( biddable_criterion )
244
+ biddable_criterion
245
+ end
246
+
247
+ def criterions(refresh = false)
248
+ _criterions unless @criterions and !refresh
249
+ @criterions
250
+ end
251
+
252
+ def p_criterions(refresh = false)
253
+ criterions(refresh).each do |criterion|
254
+ puts criterion.to_s
255
+ end
256
+ self
257
+ end
258
+
259
+ def find_criterion(criterion_id, refresh = false)
260
+ _criterions unless @criterions and !refresh
261
+ @criterions.find {|c| c.id == criterion_id}
262
+ end
263
+
264
+ private
265
+
266
+ def _criterions
267
+ soap_message = service.ad_group_criterion.all(credentials, id)
268
+ add_counters( soap_message.counters )
269
+ rval = REXML::XPath.first( soap_message.response, "//getResponse/rval")
270
+ els = REXML::XPath.match( rval, "entries/criterion")
271
+ @criterions = els.map do |el|
272
+ Criterion.from_element( self, el )
273
+ end
274
+ end
275
+
276
+ def _save_criterions
277
+ # @criterions.each { |criterion| criterion.save } if @criterions
278
+
279
+ return unless @criterions
280
+
281
+ unsaved_criterions = @criterions.select { |a| !a.saved? }
282
+ return if unsaved_criterions.empty?
283
+
284
+ xml = unsaved_criterions.inject('') do |xml,ad|
285
+ o = AdGroupCriterionOperation.new.add(ad)
286
+ xml + o.to_xml("operations")
287
+ end
288
+ soap_message = service.ad_group_criterion.mutate(credentials, xml)
289
+ add_counters( soap_message.counters )
290
+ els = REXML::XPath.match( soap_message.response, "//mutateResponse/rval/value/criterion/id")
291
+ els.each_with_index do |e,index|
292
+ id = e.text.strip.to_i
293
+ unsaved_criterions[index].criterion.instance_eval{ @id = id }
294
+ end
295
+ end
296
+
297
+ public
298
+
299
+ ###########################################################################
300
+ # ad param management
301
+
302
+ def ad_param(criterion, index = nil, text = nil, &block)
303
+ ad_param = AdParam.new(self, criterion, index, text, &block)
304
+ ad_param.save unless inside_initialize? or criterion.inside_initialize?
305
+ @ad_params ||= []
306
+ @ad_params.push( ad_param )
307
+ ad_param
308
+ end
309
+
310
+ def ad_params(refresh = false)
311
+ _ad_params unless @ad_params and !refresh
312
+ @ad_params
313
+ end
314
+
315
+ def p_ad_params(refresh = false)
316
+ ad_params(refresh).each do |ad_param|
317
+ puts ad_param.to_s
318
+ end
319
+ self
320
+ end
321
+
322
+ private
323
+
324
+ def _ad_params
325
+ soap_message = service.ad_param.all(credentials, id)
326
+ add_counters( soap_message.counters )
327
+ rval = REXML::XPath.first( soap_message.response, "//getResponse/rval")
328
+ els = REXML::XPath.match( rval, "entries")
329
+ @ad_params = els.map do |el|
330
+ AdParam.from_element( self, el )
331
+ end
332
+ end
333
+
334
+ def _save_ad_params
335
+ return unless @ad_params
336
+ @ad_params.each { |p| p.save }
337
+ end
338
+
339
+ end
340
+ end
@@ -0,0 +1,170 @@
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
+ module Sem4r
25
+ class AdGroupBids
26
+ include SoapAttributes
27
+
28
+ enum :Types, [
29
+ :BudgetOptimizerAdGroupBids,
30
+ :ConversionOptimizerAdGroupBids,
31
+ :ManualCPCAdGroupBids,
32
+ :ManualCPMAdGroupBids]
33
+
34
+ # g_accessor :type
35
+
36
+ def initialize(&block)
37
+ if block_given?
38
+ block.arity < 1 ? instance_eval(&block) : block.call(self)
39
+ end
40
+ end
41
+
42
+ def self.from_element(el)
43
+ type = el.elements["AdGroupBids.Type"].text.strip
44
+ klass = Module::const_get(type)
45
+ klass.from_element(el)
46
+ end
47
+
48
+ def to_s
49
+ "#{@maxcpc / 10000} cents"
50
+ end
51
+ end
52
+
53
+ class BudgetOptimizerAdGroupBids < AdGroupBids
54
+ end
55
+
56
+ class ConversionOptimizerAdGroupBids < AdGroupBids
57
+ end
58
+
59
+ class ManualCPCAdGroupBids < AdGroupBids
60
+
61
+ g_accessor :keyword_max_cpc
62
+ g_accessor :site_max_cpc
63
+
64
+ def xml(t)
65
+ return "" unless @keyword_max_cpc or @site_max_cpc
66
+ t.tag!('bids', 'xsi:type' => 'ManualCPCAdGroupBids') { |xml|
67
+ xml.tag!('AdGroupBids.Type') { xml.text! 'ManualCPCAdGroupBids' }
68
+
69
+ if @keyword_max_cpc
70
+ xml.keywordMaxCpc {
71
+ xml.amount {
72
+ xml.tag!('ComparableValue.Type') { xml.text! 'Money' }
73
+ xml.microAmount keyword_max_cpc
74
+ }
75
+ }
76
+ end
77
+
78
+ if @site_max_cpc
79
+ xml.siteMaxCpc {
80
+ xml.amount {
81
+ xml.tag!('ComparableValue.Type') { xml.text! 'Money' }
82
+ xml.microAmount site_max_cpc
83
+ }
84
+ }
85
+ end
86
+ }
87
+ end
88
+
89
+ def to_xml(builder = nil)
90
+ builder = Builder::XmlMarkup.new unless builder
91
+ xml(builder)
92
+ # xml = builder.tag!('bids', 'xsi:type' => 'ManualCPCAdGroupBids') { |xml|
93
+ # xml.tag!('AdGroupBids.Type') { xml.text! 'ManualCPCAdGroupBids' }
94
+ #
95
+ # if @keyword_max_cpc
96
+ # xml.keywordMaxCpc {
97
+ # xml.amount {
98
+ # xml.tag!('ComparableValue.Type') { xml.text! 'Money' }
99
+ # xml.microAmount keyword_max_cpc
100
+ # }
101
+ # }
102
+ # end
103
+ #
104
+ # if @site_max_cpc
105
+ # xml.siteMaxCpc {
106
+ # xml.amount {
107
+ # xml.tag!('ComparableValue.Type') { xml.text! 'Money' }
108
+ # xml.microAmount site_max_cpc
109
+ # }
110
+ # }
111
+ # end
112
+ # }
113
+ end
114
+
115
+ def self.from_element(el)
116
+ new do
117
+ kel = el.elements["keywordMaxCpc"]
118
+ if kel
119
+ el_amount = kel.elements["amount"]
120
+ keyword_max_cpc el_amount.elements["microAmount"].text.strip.to_i
121
+ end
122
+ sel = el.elements["siteMaxCpc"]
123
+ if sel
124
+ el_amount = sel.elements["amount"]
125
+ site_max_cpc el_amount.elements["microAmount"].text.strip.to_i
126
+ end
127
+ # TODO: it is possible something like:
128
+ # el.elements["maxCpc"] do |el|
129
+ # el.elements["amount"] do el
130
+ # max_cpc el["microAmount"]
131
+ # end
132
+ # end
133
+ end
134
+ end
135
+ end
136
+
137
+ class ManualCPMAdGroupBids < AdGroupBids
138
+ g_accessor :max_cpm
139
+
140
+ def xml(t)
141
+ return "" unless @max_cpm
142
+ t.tag!('bids', 'xsi:type' => 'ManualCPMAdGroupBids') { |xml|
143
+ xml.tag!('AdGroupBids.Type') { xml.text! 'ManualCPMAdGroupBids' }
144
+ xml.maxCpm {
145
+ xml.amount {
146
+ xml.tag!('ComparableValue.Type') { xml.text! 'Money' }
147
+ xml.microAmount max_cpm
148
+ }
149
+ }
150
+ }
151
+ end
152
+
153
+ def to_xml(builder = nil)
154
+ builder = Builder::XmlMarkup.new unless builder
155
+ xml(builder)
156
+ end
157
+
158
+ def self.from_element(el)
159
+ new do
160
+ kel = el.elements["maxCpm"]
161
+ if kel
162
+ el_amount = kel.elements["amount"]
163
+ max_cpm el_amount.elements["microAmount"].text.strip.to_i
164
+ end
165
+ end
166
+ end
167
+
168
+ end
169
+
170
+ end
@@ -0,0 +1,75 @@
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
+ module Sem4r
25
+ class AdGroupService
26
+ include SoapCall
27
+
28
+ def initialize(connector)
29
+ @connector = connector
30
+ @service_namespace = "https://adwords.google.com/api/adwords/cm/v201003"
31
+ @header_namespace = @service_namespace
32
+ @sandbox_service_url = "https://adwords-sandbox.google.com/api/adwords/cm/v201003/AdGroupService"
33
+ end
34
+
35
+ soap_call_v2009 :all, :mutate => false
36
+ soap_call_v2009 :create, :mutate => true
37
+ soap_call_v2009 :delete, :mutate => true
38
+
39
+ def _all(campaign_id)
40
+ <<-EOFS
41
+ <get xmlns="#{@service_namespace}">
42
+ <selector>
43
+ <campaignIds>#{campaign_id}</campaignIds>
44
+ </selector>
45
+ </get>
46
+ EOFS
47
+ end
48
+
49
+ def _create( xml )
50
+ <<-EOFS
51
+ <mutate xmlns="#{@service_namespace}">
52
+ <operations xsi:type="AdGroupOperation">
53
+ <operator>ADD</operator>
54
+ #{xml}
55
+ </operations>
56
+ </mutate>
57
+ EOFS
58
+ end
59
+
60
+ def _delete( id )
61
+ <<-EOFS
62
+ <mutate xmlns="#{@service_namespace}">
63
+ <operations xsi:type="AdGroupOperation">
64
+ <operator>SET</operator>
65
+ <operand>
66
+ <id>#{id}</id>
67
+ <status>DELETED</status>
68
+ </operand>
69
+ </operations>
70
+ </mutate>
71
+ EOFS
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,33 @@
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 MobileAdImage
28
+ def initialize(mobile_ad, &block)
29
+ @mobile_ad = mobile_ad
30
+ end
31
+ end
32
+
33
+ end