ppc 1.3.0 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/LICENSE +334 -17
  4. data/README.md +75 -3
  5. data/lib/ppc.rb +3 -1
  6. data/lib/ppc/api.rb +137 -2
  7. data/lib/ppc/api/baidu.rb +20 -99
  8. data/lib/ppc/api/baidu/account.rb +21 -21
  9. data/lib/ppc/api/baidu/bulk.rb +39 -3
  10. data/lib/ppc/api/baidu/creative.rb +33 -33
  11. data/lib/ppc/api/baidu/group.rb +14 -14
  12. data/lib/ppc/api/baidu/keyword.rb +57 -46
  13. data/lib/ppc/api/baidu/phone_new_creative.rb +51 -0
  14. data/lib/ppc/api/baidu/plan.rb +34 -31
  15. data/lib/ppc/api/baidu/rank.rb +23 -0
  16. data/lib/ppc/api/baidu/report.rb +100 -81
  17. data/lib/ppc/api/qihu.rb +150 -0
  18. data/lib/ppc/api/qihu/account.rb +86 -0
  19. data/lib/ppc/api/qihu/bulk.rb +35 -0
  20. data/lib/ppc/api/qihu/creative.rb +108 -0
  21. data/lib/ppc/api/qihu/group.rb +96 -0
  22. data/lib/ppc/api/qihu/keyword.rb +113 -0
  23. data/lib/ppc/api/qihu/plan.rb +64 -0
  24. data/lib/ppc/api/qihu/rank.rb +25 -0
  25. data/lib/ppc/api/qihu/report.rb +159 -0
  26. data/lib/ppc/api/qihu/sublink.rb +71 -0
  27. data/lib/ppc/api/shenma.rb +64 -0
  28. data/lib/ppc/api/shenma/report.rb +135 -0
  29. data/lib/ppc/api/sm.rb +50 -0
  30. data/lib/ppc/api/sm/account.rb +44 -0
  31. data/lib/ppc/api/sm/bulk.rb +69 -0
  32. data/lib/ppc/api/sm/creative.rb +86 -0
  33. data/lib/ppc/api/sm/group.rb +94 -0
  34. data/lib/ppc/api/sm/keyword.rb +132 -0
  35. data/lib/ppc/api/sm/phone_new_creative.rb +51 -0
  36. data/lib/ppc/api/sm/plan.rb +63 -0
  37. data/lib/ppc/api/sm/report.rb +136 -0
  38. data/lib/ppc/api/sogou.rb +118 -0
  39. data/lib/ppc/api/sogou/account.rb +42 -0
  40. data/lib/ppc/api/sogou/bulk.rb +69 -0
  41. data/lib/ppc/api/sogou/creative.rb +117 -0
  42. data/lib/ppc/api/sogou/group.rb +123 -0
  43. data/lib/ppc/api/sogou/keyword.rb +188 -0
  44. data/lib/ppc/api/sogou/plan.rb +66 -0
  45. data/lib/ppc/api/sogou/report.rb +129 -0
  46. data/lib/ppc/ext.rb +17 -0
  47. data/lib/ppc/operation.rb +50 -36
  48. data/lib/ppc/operation/account.rb +19 -7
  49. data/lib/ppc/operation/creative.rb +7 -7
  50. data/lib/ppc/operation/group.rb +2 -2
  51. data/lib/ppc/operation/keyword.rb +11 -8
  52. data/lib/ppc/operation/report.rb +23 -0
  53. data/ppc.gemspec +14 -4
  54. data/spec/baidu/api_baidu_account_spec.rb +16 -0
  55. data/spec/baidu/api_baidu_creative_spec.rb +73 -0
  56. data/spec/{api_baidu_group_spec.rb → baidu/api_baidu_group_spec.rb} +12 -17
  57. data/spec/baidu/api_baidu_keyword_spec.rb +61 -0
  58. data/spec/baidu/api_baidu_phone_spec.rb +22 -0
  59. data/spec/{api_baidu_plan_spec.rb → baidu/api_baidu_plan_spec.rb} +11 -10
  60. data/spec/baidu/api_baidu_report_spec.rb +44 -0
  61. data/spec/{api_baidu_spec.rb → baidu/api_baidu_spec.rb} +10 -15
  62. data/spec/operation/operation_baidu_report_spec.rb +17 -0
  63. data/spec/operation/operation_baidu_spec.rb +126 -0
  64. data/spec/operation/operation_qihu_report_spec.rb +18 -0
  65. data/spec/operation/operation_qihu_spec.rb +94 -0
  66. data/spec/operation/operation_sm_report_spec.rb +21 -0
  67. data/spec/operation/operation_sogou_report_spec.rb +17 -0
  68. data/spec/operation/operation_sogou_spec.rb +88 -0
  69. data/spec/{operation_spec_helper.rb → operation/operation_spec_helper.rb} +3 -5
  70. data/spec/qihu/api_qihu_account_spec.rb +29 -0
  71. data/spec/qihu/api_qihu_creative_spec.rb +48 -0
  72. data/spec/qihu/api_qihu_group_spec.rb +40 -0
  73. data/spec/qihu/api_qihu_keyword_spec.rb +50 -0
  74. data/spec/qihu/api_qihu_plan_spec.rb +39 -0
  75. data/spec/qihu/api_qihu_report_spec.rb +54 -0
  76. data/spec/qihu/api_qihu_sublink_spec.rb +36 -0
  77. data/spec/sm/api_sm_account_spec.rb +8 -0
  78. data/spec/sm/api_sm_creative_spec.rb +52 -0
  79. data/spec/sm/api_sm_group_spec.rb +75 -0
  80. data/spec/sm/api_sm_keyword_spec.rb +59 -0
  81. data/spec/sm/api_sm_plan_spec.rb +39 -0
  82. data/spec/sm/api_sm_report_spec.rb +30 -0
  83. data/spec/sogou/api_sogou_account_spec.rb +17 -0
  84. data/spec/sogou/api_sogou_creative_spec.rb +51 -0
  85. data/spec/sogou/api_sogou_group_spec.rb +50 -0
  86. data/spec/sogou/api_sogou_keyword_spec.rb +54 -0
  87. data/spec/sogou/api_sogou_plan_spec.rb +43 -0
  88. data/spec/sogou/api_sogou_report_spec.rb +51 -0
  89. data/spec/spec_helper.rb +49 -10
  90. metadata +140 -46
  91. data/lib/ppc/baidu.rb +0 -152
  92. data/lib/ppc/baidu/account.rb +0 -88
  93. data/lib/ppc/baidu/bulk.rb +0 -52
  94. data/lib/ppc/baidu/group.rb +0 -99
  95. data/lib/ppc/baidu/key.rb +0 -38
  96. data/lib/ppc/baidu/plan.rb +0 -85
  97. data/lib/ppc/baidu/report.rb +0 -82
  98. data/spec/api_baidu_account_spec.rb +0 -18
  99. data/spec/api_baidu_creative_spec.rb +0 -71
  100. data/spec/api_baidu_keyword_spec.rb +0 -64
  101. data/spec/api_baidu_report_spec.rb +0 -32
  102. data/spec/baidu_account_spec.rb +0 -32
  103. data/spec/baidu_bulk_spec.rb +0 -21
  104. data/spec/baidu_group_spec.rb +0 -56
  105. data/spec/baidu_plan_spec.rb +0 -129
  106. data/spec/baidu_report_spec.rb +0 -24
  107. data/spec/operation_spec.rb +0 -87
@@ -0,0 +1,135 @@
1
+ # -*- coding:utf-8 -*-
2
+ module PPC
3
+ module API
4
+ class Shenma
5
+ class Report< Shenma
6
+ Service = 'Report'
7
+
8
+ # 需要用到的映射集合
9
+ Type_map = { 'account' =>2, 'plan'=> 10, 'group'=> 11,
10
+ 'keyword'=> 14, 'creative'=> 12, 'region'=> 3,
11
+ 'query'=> 6, 'phone' => 22, 'app' => 23 }
12
+
13
+ Level_map = { 'account' => 2, 'plan' => 3, 'group' => 5,
14
+ 'creative' => 7, 'keyword' => 11, 'phone' => 22,
15
+ 'app' => 23, 'pair' => 12 }
16
+
17
+ Unit_map = { 'day' => 5, 'month' => 3, 'default' => 8 }
18
+
19
+ def self.get_id( auth, params, debug = false )
20
+ request = make_reportrequest( params )
21
+ body = { ReportRequestType: request }
22
+ response = request( auth, '/report/getReport' ,body)
23
+ process( response, 'reportId', debug ){ |x| x }
24
+ end
25
+
26
+ def self.get_state( auth, id, debug = false)
27
+ '''
28
+ input id should be string
29
+ '''
30
+ status = {1=>'Waiting' ,2=>'Opearting' ,3=>'Finished'}
31
+ body = { taskId: id }
32
+ response = request( auth, '/task/getTaskState' ,body)
33
+ process( response, 'isGenerated', debug ){ |x| status[x] }
34
+ end
35
+
36
+ # todo : rewrite method
37
+ def self.download( auth, id, debug = false )
38
+ body = { fileId: id }
39
+ response = request( auth, '/file/download' ,body)
40
+ process( response, 'reportPath', debug ){ |x| x }
41
+ end
42
+
43
+ private
44
+ def self.make_reportrequest( param )
45
+ '''
46
+ make RepoerRequestType
47
+ ======================
48
+ For more docs please have a look at
49
+ ::PPC::API::Baidu::Report:make_reportrequest()
50
+ '''
51
+ requesttype = {}
52
+ requesttype[:performanceData] = param[:fields]
53
+ requesttype[:reportType] = Type_map[ param[:type] ] if param[:type]
54
+ requesttype[:levelOfDetails] = Level_map[ param[:level] ] if param[:level]
55
+ requesttype[:statRange] = Level_map[ param[:range] ] if param[:range]
56
+ requesttype[:unitOfTime] = Unit_map[ param[:unit] ] if param[:unit]
57
+ requesttype[:idOnly] = param[:id_only] || false
58
+ requesttype[:statIds] = param[:ids] if param[:ids] != nil
59
+ requesttype[:startDate] = parse_date( param[:startDate] )
60
+ requesttype[:endDate] = parse_date( param[:endDate] )
61
+ return requesttype
62
+ end
63
+
64
+ private
65
+ def self.parse_date( date )
66
+ """
67
+ Cast string to time:
68
+ 'YYYYMMDD' => Time
69
+ """
70
+ if date
71
+ y = date[0..3]
72
+ m = date[4..5]
73
+ d = date[6..7]
74
+ date = Time.new( y, m, d )
75
+ else
76
+ date = (Time.now - 24*3600)
77
+ end
78
+ date.to_s[0,10]
79
+ end
80
+
81
+ def download_report( auth, param, debug = false )
82
+ response = call('report').get_id( auth, param )
83
+ if response[:succ]
84
+ id = response[:result]
85
+ p "Got report id:" + id.to_s if debug
86
+ loop do
87
+ sleep 2
88
+ break if call('report').get_state( auth, id )[:result] == 'Finished'
89
+ p "Report is not generated, waiting..." if debug
90
+ end
91
+ # need to rewrite the download method here.
92
+ url = call('report').get_url( auth, id )[:result]
93
+ return open(url).read.force_encoding('gb18030').encode('utf-8')
94
+ else
95
+ raise response[:failure][0]["message"]
96
+ end
97
+ end
98
+
99
+ ###########################
100
+ # intreface for Operation #
101
+ ###########################
102
+ def query_report( auth, param = nil, debug = false )
103
+ param = {} if not param
104
+ param[:type] ||= 'query'
105
+ param[:fields] ||= %w(click)
106
+ param[:level] ||= 'pair'
107
+ param[:range] ||= 'account'
108
+ param[:unit] ||= 'day'
109
+ download_report( param, debug )
110
+ end
111
+
112
+ def creative_report( auth, param = nil, debug = false )
113
+ param = {} if not param
114
+ param[:type] ||= 'creative'
115
+ param[:fields] ||= %w( cost cpc click impression ctr )
116
+ param[:level] ||= 'creative'
117
+ param[:range] ||= 'creative'
118
+ param[:unit] ||= 'day'
119
+ download_report( param, debug )
120
+ end
121
+
122
+ def keyword_report( auth, param = nil, debug = false )
123
+ param = {} if not param
124
+ param[:type] ||= 'keyword'
125
+ param[:fields] ||= %w( cost cpc click impression ctr )
126
+ param[:level] ||= 'keywordid'
127
+ param[:range] ||= 'keywordid'
128
+ param[:unit] ||= 'day'
129
+ download_report( param, debug )
130
+ end
131
+
132
+ end # Repost
133
+ end # Baidu
134
+ end # API
135
+ end # PPC
data/lib/ppc/api/sm.rb ADDED
@@ -0,0 +1,50 @@
1
+ # -*- coding:utf-8 -*-
2
+ require 'ppc/api/sm/account'
3
+ require 'ppc/api/sm/plan'
4
+ require 'ppc/api/sm/bulk'
5
+ require 'ppc/api/sm/group'
6
+ require 'ppc/api/sm/keyword'
7
+ require 'ppc/api/sm/report'
8
+ require 'ppc/api/sm/creative'
9
+ module PPC
10
+ module API
11
+ class Sm
12
+
13
+ extend ::PPC::API
14
+
15
+ def self.request_uri(param = {})
16
+ URI("https://e.sm.cn/api/#{param[:service]}/#{param[:method]}")
17
+ end
18
+
19
+ def self.process( response, key, &func)
20
+ '''
21
+ Process Http response. If operation successes, return value of given keys.
22
+ You can process the result using function &func, or do nothing by passing
23
+ block {|x|x}
24
+ ===========================
25
+ @Output: resultType{ desc: boolean, failure: Array, result: Array }
26
+
27
+ failure is the failures part of response\'s header
28
+ result is the processed response body.
29
+ '''
30
+ result = {}
31
+ result[:succ] = response['header']['desc'] =='success'
32
+ result[:failure] = response['header']['failures']
33
+ if !response['body'].nil? && response['body'][key]
34
+ result[:result] = func[ response['body'][key] ]
35
+ end
36
+ result[:no_quota] = is_no_quota(response['header']['failures'], '8501')
37
+ result
38
+ end # process
39
+
40
+ def self.reverse_type( types, maps = @map )
41
+ types = [ types ] unless types.is_a? Array
42
+ types.map do |item|
43
+ maps.each{|m| item.filter_and_replace_key(m[0],m[1].to_s)}
44
+ item
45
+ end
46
+ end
47
+
48
+ end # Sm
49
+ end # API
50
+ end # PPC
@@ -0,0 +1,44 @@
1
+ module PPC
2
+ module API
3
+ class Sm
4
+ class Account < Sm
5
+ Service = 'account'
6
+
7
+ @map = [
8
+ [:id,:userId],
9
+ [:balance,:balance],
10
+ [:cost,:cost],
11
+ [:payment,:payment],
12
+ [:budget_type,:budgetType],
13
+ [:budget,:budget],
14
+ [:region,:regionTarget],
15
+ [:exclude_ip,:excludeIp],
16
+ [:open_domains,:openDomains],
17
+ [:reg_domain,:regDomain],
18
+ [:offline_time,:budgetOfflineTime],
19
+ [:weekly_budget,:weeklyBudget]
20
+ ]
21
+
22
+ def self.info( auth )
23
+ response = request(auth, Service, 'getAccount', {requestData: ["account_all"]})
24
+ p response
25
+ return process( response, 'accountInfoType' ){ |x|reverse_type(x)[0] }
26
+ end
27
+
28
+ def self.update(auth, param = {} )
29
+ """
30
+ update account info
31
+ @ params : account_info_type
32
+ @return : account info_type
33
+ """
34
+ # for account service, there is not bulk operation
35
+ infoType = make_type( param )[0]
36
+ body = { accountInfoType: infoType }
37
+ response = request(auth, Service, 'updateAccount', body)
38
+ return process( response, 'accountInfoType' ){ |x|reverse_type(x)[0] }
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,69 @@
1
+ module PPC
2
+ module API
3
+ class Sm
4
+ class Bulk < Sm
5
+ Service = 'bulkJob'
6
+
7
+ def self.get_all_object( auth,params = {})
8
+
9
+ plan_ids = params[:plan_ids]
10
+
11
+ unless plan_ids.nil?
12
+ plan_ids = plan_ids.class == Array ? plan_ids : [plan_ids]
13
+ end
14
+
15
+ options = {
16
+ bulkJobRequestType: {
17
+ campaignIds: plan_ids || []
18
+ }
19
+ }
20
+ response = request( auth, Service, 'getAllObjects',options )
21
+ process( response, 'taskId'){ |x| x }
22
+ end
23
+
24
+ def self.get_file_id( auth, id)
25
+ raise "empty id" if id.nil?
26
+ response = request(auth, 'task', 'getTaskState',{taskId: id})
27
+ process( response, 'fileId'){ |x| x }
28
+ end
29
+
30
+ def self.do_download(auth, id)
31
+ request(auth, 'file', 'download',{fileId: id})
32
+ end
33
+
34
+ ###########################
35
+ # interface for operation #
36
+ ###########################
37
+ def self.download( auth, params = {} )
38
+ """
39
+ """
40
+ begin
41
+ result = get_all_object( auth, params )
42
+ if result[:succ]
43
+ task_id = result[:result]
44
+ else
45
+ raise "获取task id 失败"
46
+ end
47
+
48
+ puts "task_id: #{task_id}" if @debug
49
+
50
+ loop do
51
+ file_id = get_file_id( auth, task_id )[:result]
52
+ if file_id.nil?
53
+ sleep 15
54
+ next
55
+ end
56
+ File.open("sm_#{file_id}.zip", "w") do |f|
57
+ f.puts do_download(auth, file_id)
58
+ end
59
+ return
60
+ end
61
+ rescue => e
62
+ p "Error encounter:#{e.to_s}"
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,86 @@
1
+ # -*- coding:utf-8 -*-
2
+ module PPC
3
+ module API
4
+ class Sm
5
+ class Creative< Sm
6
+ Service = 'creative'
7
+
8
+ @map =[
9
+ [:id, :creativeId],
10
+ [:group_id, :adgroupId],
11
+ [:title, :title],
12
+ [:description1, :description1],
13
+ [:pause, :pause],
14
+ [:mobile_destination, :destinationUrl],
15
+ [:mobile_display, :displayUrl],
16
+ [:status, :status]
17
+ ]
18
+
19
+ def self.add(auth, creatives)
20
+ body = {creativeTypes: make_type(creatives)}
21
+ response = request(auth, Service, 'addCreative', body)
22
+ process(response, 'creativeTypes'){|x| reverse_type(x)}
23
+ end
24
+
25
+ def self.get(auth, ids)
26
+ ids = [ids] unless ids.is_a? Array
27
+ body = {creativeIds: ids}
28
+ response = request(auth, Service, 'getCreativeByCreativeId', body)
29
+ process(response, 'creativeTypes'){|x| reverse_type(x)}
30
+ end
31
+
32
+ def self.update(auth, creatives)
33
+ body = {creativeTypes: make_type(creatives)}
34
+ response = request(auth, Service, 'updateCreative', body)
35
+ process(response, 'creativeTypes'){|x| reverse_type(x)}
36
+ end
37
+
38
+ def self.delete(auth, ids)
39
+ ids = [ids] unless ids.is_a? Array
40
+ body = {creativeIds: ids}
41
+ response = request(auth, Service, 'deleteCreative', body, 'delete')
42
+ process(response, 'result'){|x| x}
43
+ end
44
+
45
+ def self.search_id_by_group_id(auth, ids)
46
+ ids = [ids] unless ids.is_a? Array
47
+ body = {adgroupIds: ids}
48
+ response = request(auth, Service, 'getCreativeIdByAdgroupId', body)
49
+ process(response, 'groupCreativeIds'){|x| make_groupCreativeIds(x)}
50
+ end
51
+
52
+ def self.search_by_group_id(auth, ids)
53
+ ids = [ids] unless ids.is_a? Array
54
+ body = {adgroupIds: ids}
55
+ response = request(auth, Service, 'getCreativeByAdgroupId', body)
56
+ process(response, 'groupCreatives'){|x| make_groupCreatives(x)}
57
+ end
58
+
59
+ private
60
+ def self.make_groupCreativeIds( groupCreativeIds )
61
+ group_creative_ids = []
62
+ groupCreativeIds.each do |groupCreativeId|
63
+ group_creative_id = { }
64
+ group_creative_id[:group_id] = groupCreativeId['adgroupId']
65
+ group_creative_id[:creative_ids] = groupCreativeId['creativeIds']
66
+ group_creative_ids << group_creative_id
67
+ end
68
+ return group_creative_ids
69
+ end
70
+
71
+ private
72
+ def self.make_groupCreatives( groupCreatives )
73
+ group_creatives = []
74
+ groupCreatives.each do |groupCreative |
75
+ group_creative = {}
76
+ group_creative[:group_id] = groupCreative['adgroupId']
77
+ group_creative[:creatives] = reverse_type( groupCreative['creativeTypes'] )
78
+ group_creatives << group_creative
79
+ end
80
+ return group_creatives
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,94 @@
1
+ # -*- coding:utf-8 -*-
2
+ module PPC
3
+ module API
4
+ class Sm
5
+ class Group < Sm
6
+ Service = 'adgroup'
7
+
8
+ @map =[
9
+ [:plan_id, :campaignId],
10
+ [:id, :adgroupId],
11
+ [:name, :adgroupName],
12
+ [:price, :maxPrice],
13
+ [:negative, :negativeWords],
14
+ [:exact_negative, :exactNegativeWords],
15
+ [:pause, :pause],
16
+ [:status, :status],
17
+ [:os, :adPlatformOS]
18
+ ]
19
+
20
+ def self.ids( auth )
21
+ response = request(auth, Service, "getAllAdgroupId")
22
+ process(response, 'campaignAdgroupIds'){|x| make_planGroupIds(x)}
23
+ end
24
+
25
+ def self.get(auth, ids)
26
+ ids = [ ids ] unless ids.is_a? Array
27
+ body = {adgroupIds: ids}
28
+ response = request(auth, Service, "getAdgroupByAdgroupId", body)
29
+ process(response, 'adgroupTypes'){|x| reverse_type(x)}
30
+ end
31
+
32
+ def self.add(auth, groups)
33
+ adgroup_types = make_type(groups)
34
+ body = {adgroupTypes: adgroup_types}
35
+ response = request(auth, Service, "addAdgroup", body)
36
+ process(response, 'adgroupTypes'){|x| reverse_type(x)}
37
+ end
38
+
39
+ def self.update(auth, groups)
40
+ adgroup_types = make_type(groups)
41
+ body = {adgroupTypes: adgroup_types}
42
+ response = request(auth, Service, "updateAdgroup", body)
43
+ process(response, 'adgroupTypes'){|x| reverse_type(x)}
44
+ end
45
+
46
+ def self.delete(auth, ids)
47
+ ids = [ids] unless ids.is_a? Array
48
+ body = {adgroupIds: ids}
49
+ response = request(auth, Service, "deleteAdgroup", body, "delete")
50
+ process(response, 'result'){ |x| x }
51
+ end
52
+
53
+ def self.search_by_plan_id(auth, ids)
54
+ ids = [ ids ] unless ids.class == Array
55
+ body = { campaignIds: ids }
56
+ response = request(auth, Service, "getAdgroupByCampaignId", body)
57
+ process(response, 'campaignAdgroups' ){ |x| make_planGroups( x ) }
58
+ end
59
+
60
+ def self.search_id_by_plan_id( auth, ids )
61
+ ids = [ ids ] unless ids.class == Array
62
+ body = { campaignIds: ids }
63
+ response = request(auth, Service, "getAdgroupIdByCampaignId", body)
64
+ process(response, 'campaignAdgroupIds'){ |x| make_planGroupIds( x ) }
65
+ end
66
+
67
+ private
68
+ def self.make_planGroupIds( campaignAdgroupIds )
69
+ planGroupIds = []
70
+ campaignAdgroupIds.each do |campaignAdgroupId|
71
+ planGroupId = { }
72
+ planGroupId[:plan_id] = campaignAdgroupId['campaignId']
73
+ planGroupId[:group_ids] = campaignAdgroupId['adgroupIds']
74
+ planGroupIds << planGroupId
75
+ end
76
+ return planGroupIds
77
+ end
78
+
79
+ private
80
+ def self.make_planGroups( campaignAdgroups )
81
+ planGroups = []
82
+ campaignAdgroups.each do |campaignAdgroup|
83
+ planGroup = {}
84
+ planGroup[:plan_id] = campaignAdgroup['campaignId']
85
+ planGroup[:groups] = reverse_type( campaignAdgroup['adgroupTypes'] )
86
+ planGroups << planGroup
87
+ end
88
+ return planGroups
89
+ end
90
+
91
+ end # class group
92
+ end # class baidu
93
+ end # API
94
+ end # module