ppc 0.3.0

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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +37 -0
  3. data/.rspec +2 -0
  4. data/LICENSE +339 -0
  5. data/README.md +78 -0
  6. data/lib/ppc.rb +17 -0
  7. data/lib/ppc/api.rb +10 -0
  8. data/lib/ppc/api/baidu.rb +138 -0
  9. data/lib/ppc/api/baidu/account.rb +47 -0
  10. data/lib/ppc/api/baidu/bulk.rb +41 -0
  11. data/lib/ppc/api/baidu/creative.rb +125 -0
  12. data/lib/ppc/api/baidu/group.rb +111 -0
  13. data/lib/ppc/api/baidu/keyword.rb +204 -0
  14. data/lib/ppc/api/baidu/plan.rb +68 -0
  15. data/lib/ppc/api/baidu/report.rb +143 -0
  16. data/lib/ppc/api/qihu.rb +107 -0
  17. data/lib/ppc/api/qihu/account.rb +86 -0
  18. data/lib/ppc/api/qihu/creative.rb +106 -0
  19. data/lib/ppc/api/qihu/group.rb +113 -0
  20. data/lib/ppc/api/qihu/keyword.rb +111 -0
  21. data/lib/ppc/api/qihu/plan.rb +64 -0
  22. data/lib/ppc/api/qihu/report.rb +159 -0
  23. data/lib/ppc/api/shenma.rb +64 -0
  24. data/lib/ppc/api/shenma/report.rb +135 -0
  25. data/lib/ppc/api/sogou.rb +122 -0
  26. data/lib/ppc/api/sogou/account.rb +42 -0
  27. data/lib/ppc/api/sogou/creative.rb +117 -0
  28. data/lib/ppc/api/sogou/group.rb +116 -0
  29. data/lib/ppc/api/sogou/keyword.rb +182 -0
  30. data/lib/ppc/api/sogou/plan.rb +66 -0
  31. data/lib/ppc/api/sogou/report.rb +129 -0
  32. data/lib/ppc/ext.rb +9 -0
  33. data/lib/ppc/operation.rb +196 -0
  34. data/lib/ppc/operation/account.rb +53 -0
  35. data/lib/ppc/operation/creative.rb +28 -0
  36. data/lib/ppc/operation/group.rb +59 -0
  37. data/lib/ppc/operation/keyword.rb +32 -0
  38. data/lib/ppc/operation/plan.rb +47 -0
  39. data/lib/ppc/operation/report.rb +19 -0
  40. data/ppc.gemspec +26 -0
  41. data/spec/baidu/api_baidu_account_spec.rb +15 -0
  42. data/spec/baidu/api_baidu_creative_spec.rb +67 -0
  43. data/spec/baidu/api_baidu_group_spec.rb +45 -0
  44. data/spec/baidu/api_baidu_keyword_spec.rb +61 -0
  45. data/spec/baidu/api_baidu_plan_spec.rb +43 -0
  46. data/spec/baidu/api_baidu_report_spec.rb +44 -0
  47. data/spec/baidu/api_baidu_spec.rb +55 -0
  48. data/spec/operation/operation_baidu_report_spec.rb +17 -0
  49. data/spec/operation/operation_baidu_spec.rb +78 -0
  50. data/spec/operation/operation_qihu_report_spec.rb +18 -0
  51. data/spec/operation/operation_qihu_spec.rb +51 -0
  52. data/spec/operation/operation_sogou_report_spec.rb +17 -0
  53. data/spec/operation/operation_sogou_spec.rb +51 -0
  54. data/spec/operation/operation_spec_helper.rb +51 -0
  55. data/spec/qihu/api_qihu_account_spec.rb +25 -0
  56. data/spec/qihu/api_qihu_creative_spec.rb +48 -0
  57. data/spec/qihu/api_qihu_group_spec.rb +40 -0
  58. data/spec/qihu/api_qihu_keyword_spec.rb +50 -0
  59. data/spec/qihu/api_qihu_plan_spec.rb +39 -0
  60. data/spec/qihu/api_qihu_report_spec.rb +54 -0
  61. data/spec/sogou/api_sogou_account_spec.rb +15 -0
  62. data/spec/sogou/api_sogou_creative_spec.rb +48 -0
  63. data/spec/sogou/api_sogou_group_spec.rb +45 -0
  64. data/spec/sogou/api_sogou_keyword_spec.rb +50 -0
  65. data/spec/sogou/api_sogou_plan_spec.rb +39 -0
  66. data/spec/sogou/api_sogou_report_spec.rb +51 -0
  67. data/spec/spec_helper.rb +134 -0
  68. metadata +177 -0
@@ -0,0 +1,204 @@
1
+ # -*- coding:utf-8 -*-
2
+ module PPC
3
+ module API
4
+ class Baidu
5
+ class Keyword< Baidu
6
+ Service = 'Keyword'
7
+
8
+ Match_type = { 'exact' => 1, 'pharse' => 2, 'wide' => 3,1 => 'exact', 2=> 'pharse' , 3 => 'wide' }
9
+ Device = { 'pc' => 0, 'mobile' => 1, 'all' => 2 }
10
+ Type = { 'plan' => 3, 'group' => 5, 'keyword' => 11 }
11
+
12
+ @map = [
13
+ [:id,:keywordId],
14
+ [:group_id,:adgroupId],
15
+ [:keyword,:keyword],
16
+ [:price,:price],
17
+ [:pc_destination,:pcDestinationUrl],
18
+ [:mobile_destination,:mobileDestinationUrl],
19
+ [:match_type,:matchType],
20
+ [:phrase_type,:phraseType],
21
+ [:status,:status],
22
+ [:pause,:pause]
23
+ ]
24
+
25
+ @quality10_map = [
26
+ [ :id, :id ],
27
+ [ :group_id, :adgroupId ],
28
+ [ :plan_id, :Campaigned ],
29
+ [ :pc_quality, :pcQuality ],
30
+ [ :pc_reliable, :pcReliable ],
31
+ [ :pc_reason, :pcReason ],
32
+ [ :pc_scale, :pcScale ],
33
+ [ :mobile_quality, :mobileQuality ],
34
+ [ :mobile_reliable, :mobileReliable ],
35
+ [ :mobile_reason, :mobileReason ],
36
+ [ :mobile_scale, :mobileScale ]
37
+ ]
38
+
39
+ # 后面改成info方法
40
+ def self.get( auth, ids )
41
+ '''
42
+ getKeywordByKeywordId
43
+ '''
44
+ ids = [ ids ] unless ids.is_a? Array
45
+ body = { keywordIds: ids}
46
+ response = request( auth, Service, 'getKeywordByKeywordId', body )
47
+ return process(response, 'keywordTypes' ){|x| reverse_type( x ) }
48
+ end
49
+
50
+ def self.add( auth, keywords )
51
+ '''
52
+ '''
53
+ keywordtypes = make_type( keywords )
54
+ body = { keywordTypes: keywordtypes }
55
+ response = request( auth, Service, "addKeyword", body )
56
+ return process(response, 'keywordTypes' ){|x| reverse_type(x) }
57
+ end
58
+
59
+ def self.update( auth, keywords )
60
+ '''
61
+ '''
62
+ keywordtypes = make_type( keywords )
63
+ body = { keywordTypes: keywordtypes }
64
+ response = request( auth, Service, "updateKeyword", body )
65
+ return process(response, 'keywordTypes' ){|x| reverse_type(x) }
66
+ end
67
+
68
+ def self.delete( auth, ids )
69
+ """
70
+ """
71
+ ids = [ ids ] unless ids.is_a? Array
72
+ body = { keywordIds: ids}
73
+ response = request( auth, Service, 'deleteKeyword', body )
74
+ return process(response, 'result' ){|x| x }
75
+ end
76
+
77
+ def self.activate( auth, ids )
78
+ """
79
+ """
80
+ ids = [ ids ] unless ids.is_a? Array
81
+ body = { keywordIds: ids }
82
+ response = request( auth, Service, 'activateKeyword', body)
83
+ return process(response, 'keywordTypes' ){|x| reverse_type(x) }
84
+ end
85
+
86
+ def self.search_by_group_id( auth, group_ids )
87
+ """
88
+ getKeywordByGroupIds
89
+ @input: list of group id
90
+ @output: list of groupKeyword
91
+ """
92
+ group_ids = [ group_ids ] unless group_ids.is_a? Array
93
+ body = { adgroupIds: group_ids }
94
+ response = request( auth, Service, "getKeywordByAdgroupId", body )
95
+ return process(response, 'groupKeywords' ){|x| make_groupKeywords( x ) }
96
+ end
97
+
98
+ def self.search_id_by_group_id( auth, group_ids )
99
+ group_ids = [ group_ids ] unless group_ids.is_a? Array
100
+ body = { adgroupIds: group_ids }
101
+ response = request( auth, Service, "getKeywordIdByAdgroupId", body )
102
+ return process(response, 'groupKeywordIds' ){|x| make_groupKeywordIds( x ) }
103
+ end
104
+
105
+ # 下面三个操作操作对象包括计划,组和关键字
106
+ # 不知道放在这里合不合适
107
+ def self.status( auth, ids, type )
108
+ '''
109
+ Return [{ id: id, status: status } ... ]
110
+ '''
111
+ ids = [ ids ] unless ids.is_a? Array
112
+ body = { ids: ids, type: Type[type]}
113
+ response = request( auth, Service, 'getKeywordStatus', body )
114
+ return process(response, 'keywordStatus' ){ |statusTypes|
115
+ statusTypes = [statusTypes] unless statusTypes.is_a? Array
116
+ status =[]
117
+
118
+ statusTypes.each do |statusType|
119
+ status << { id: statusType['id'], status: statusType['status'] }
120
+ end
121
+ return status
122
+ }
123
+ end
124
+
125
+ def self.quality( auth ,ids, type, device )
126
+ '''
127
+ Return 10Quanlity *Not the old Quality* of given ketword id
128
+ '''
129
+ ids = [ ids ] unless ids.is_a? Array
130
+ body = { ids: ids, type: Type[type], device:Device[device] }
131
+ response = request( auth, Service, 'getKeyword10Quality', body )
132
+ return process(response, 'keyword10Quality' ){|x| reverse_type( x, @quality10_map ) }
133
+ end
134
+
135
+ private
136
+ def self.make_groupKeywordIds( groupKeywordIds )
137
+ group_keyword_ids = []
138
+ groupKeywordIds.each do |groupKeywordId|
139
+ group_keyword_id = { }
140
+ group_keyword_id[:group_id] = groupKeywordId['adgroupIds']
141
+ group_keyword_id[:keyword_ids] = groupKeywordId['keywordIds']
142
+ group_keyword_ids << group_keyword_id
143
+ end
144
+ return group_keyword_ids
145
+ end
146
+
147
+ private
148
+ def self.make_groupKeywords( groupKeywords )
149
+ group_keywords = []
150
+ groupKeywords.each do |groupKeyword|
151
+ group_keyword = {}
152
+ group_keyword[:group_id] = groupKeyword['adgroupId']
153
+ group_keyword[:keywords] = reverse_type( groupKeyword['keywordTypes'] )
154
+ group_keywords << group_keyword
155
+ end
156
+ return group_keywords
157
+ end
158
+
159
+ # Override
160
+ def self.make_type( params, map = @map)
161
+ params = [ params ] unless params.is_a? Array
162
+ types = []
163
+ params.each do |param|
164
+ type = {}
165
+ map.each do |key|
166
+ # 增加对matchtype的自动转换
167
+ if key[0] == :match_type
168
+ value = param[ key[0] ]
169
+ type[ key[1] ] = Match_type[ value ] if value
170
+ else
171
+ value = param[ key[0] ]
172
+ type[ key[1] ] = value if value
173
+ end
174
+ end
175
+ types << type
176
+ end
177
+ return types
178
+ end
179
+
180
+ # Overwrite
181
+ def self.reverse_type( types, map = @map )
182
+ types = [ types ] unless types.is_a? Array
183
+ params = []
184
+ types.each do |type|
185
+ param = {}
186
+ # 增加对matchtype的自动转换
187
+ map.each do |key|
188
+ if key[0] == :match_type
189
+ value = type[ key[1].to_s ]
190
+ param[ key[0] ] = Match_type[ value ] if value
191
+ else
192
+ value = type[ key[1].to_s ]
193
+ param[ key[0] ] = value if value
194
+ end
195
+ end # map.each
196
+ params << param
197
+ end # types.each
198
+ return params
199
+ end
200
+
201
+ end # keyword
202
+ end # Baidu
203
+ end # API
204
+ end # PPC
@@ -0,0 +1,68 @@
1
+ module PPC
2
+ module API
3
+ class Baidu
4
+ class Plan< Baidu
5
+ Service = 'Campaign'
6
+
7
+ @map = [
8
+ [:id,:campaignId],
9
+ [:name,:campaignName],
10
+ [:budget,:budget],
11
+ [:region,:regionTarget],
12
+ [:ip,:excludeIp] ,
13
+ [:negative,:negativeWords],
14
+ [:exact_negative,:exactNegativeWords],
15
+ [:schedule,:schedule],
16
+ [:budget_offline_time,:budgetOfflineTime],
17
+ [:show_prob,:showProb],
18
+ [:device,:device],
19
+ [:price_ratio,:priceRatio],
20
+ [:is_dynamic,:isDynamicCreative],
21
+ [:pause,:pause],
22
+ [:status,:status]
23
+ ]
24
+
25
+ def self.all( auth )
26
+ response = request( auth, Service, 'getAllCampaign' )
27
+ process( response, 'campaignTypes' ){ |x| reverse_type(x) }
28
+ end
29
+
30
+ def self.ids( auth )
31
+ response = request( auth, Service, 'getAllCampaignId' )
32
+ process( response, 'campaignIds' ){ |x| x }
33
+ end
34
+
35
+ def self.get( auth, ids )
36
+ ids = [ ids ] unless ids.is_a? Array
37
+ body = { campaignIds: ids }
38
+ response = request( auth, Service, 'getCampaignByCampaignId', body)
39
+ process( response, 'campaignTypes' ){ |x| reverse_type(x) }
40
+ end
41
+
42
+ def self.add( auth, plans )
43
+ campaigntypes = make_type( plans )
44
+ # set extended = 1 to allow change of isDynamicCreative
45
+ body = { campaignTypes: campaigntypes, extended:1 }
46
+ response = request( auth, Service, 'addCampaign', body)
47
+ process( response, 'campaignTypes' ){ |x| reverse_type(x) }
48
+ end
49
+
50
+ def self.update(auth,plans )
51
+ campaigntypes = make_type( plans )
52
+ # set extended = 1 to allow change of isDynamicCreative
53
+ body = { campaignTypes: campaigntypes, extended:1 }
54
+ response = request( auth, Service, 'updateCampaign', body)
55
+ process( response, 'campaignTypes' ){ |x| reverse_type(x) }
56
+ end
57
+
58
+ def self.delete(auth, ids )
59
+ ids = [ ids ] unless ids.class == Array
60
+ body = { campaignIds: ids }
61
+ response = request( auth, Service, 'deleteCampaign', body)
62
+ process( response, 'result' ){ |x| x }
63
+ end
64
+
65
+ end # Service
66
+ end # baidu
67
+ end # API
68
+ end # PPC
@@ -0,0 +1,143 @@
1
+ # -*- coding:utf-8 -*-
2
+ module PPC
3
+ module API
4
+ class Baidu
5
+ class Report< Baidu
6
+ Service = 'Report'
7
+
8
+ # 需要用到的映射集合
9
+ Type_map = { 'account' => 2, 'plan'=> 10, 'group'=> 11,
10
+ 'keyword'=> 14, 'creative'=> 12, 'pair'=> 15,
11
+ 'region'=> 3, 'wordid'=> 9 , 'query'=>6 }
12
+
13
+ Level_map = { 'account' => 2, 'plan' => 3, 'group' => 5,
14
+ 'creative'=> 7, 'keywordid' => 11, 'pair' => 12,
15
+ 'wordid' => 6 }
16
+
17
+ Device_map = { 'all' => 0, 'pc' => 1, 'mobile' => 2 }
18
+
19
+ Unit_map = { 'day' => 5, 'week' => 4, 'month' => 3, 'year' => 1, 'hour' => 7 }
20
+
21
+ ########################
22
+ # API wraping function #
23
+ ########################
24
+ def self.get_id( auth, params )
25
+ request = make_reportrequest( params )
26
+ body = { reportRequestType: request }
27
+ response = request( auth, Service, 'getProfessionalReportId' ,body)
28
+ process( response, 'reportId' ){ |x| x }
29
+ end
30
+
31
+ def self.get_state( auth, id )
32
+ '''
33
+ input id should be string
34
+ '''
35
+ status = {1=>'Waiting' ,2=>'Opearting' ,3=>'Finished'}
36
+ body = { reportId: id }
37
+ response = request( auth, Service, 'getReportState' ,body)
38
+ process( response, 'isGenerated' ){ |x| status[x] }
39
+ end
40
+
41
+ def self.get_url( auth, id )
42
+ body = { reportId: id }
43
+ response = request( auth, Service, 'getReportFileUrl' ,body)
44
+ process( response, 'reportFilePath' ){ |x| x }
45
+ end
46
+
47
+ private
48
+ def self.make_reportrequest( param )
49
+ """
50
+ make RepoerRequestType
51
+ ==============
52
+ @Input : :fields,:type,:level,:range,:unit,:device,:id_only,:startDate:endDate
53
+ ==============
54
+ Note:
55
+ We cast [ type, level, range, unit,device ] from int to string.
56
+ For more information please see those map at the begining of the file
57
+ """
58
+ requesttype = {}
59
+ requesttype[:performanceData] = param[:fields] || %w(click impression)
60
+ requesttype[:reportType] = Type_map[ param[:type] ] if param[:type]
61
+ requesttype[:levelOfDetails] = Level_map[ param[:level] ] if param[:level]
62
+ requesttype[:statRange] = Level_map[ param[:range] ] if param[:range]
63
+ requesttype[:unitOfTime] = Unit_map[ param[:unit] ] if param[:unit]
64
+ requesttype[:device] = Device_map[ param[:device] ] if param[:device]
65
+ requesttype[:idOnly] = param[:id_only] || false
66
+ requesttype[:startDate] = parse_date( param[:startDate] )
67
+ requesttype[:endDate] = parse_date( param[:endDate] )
68
+ return requesttype
69
+ end
70
+
71
+ private
72
+ def self.parse_date( date )
73
+ """
74
+ Cast string to time:
75
+ 'YYYYMMDD' => Time
76
+ """
77
+ if date
78
+ y = date[0..3]
79
+ m = date[4..5]
80
+ d = date[6..7]
81
+ date = Time.new( y, m, d )
82
+ else
83
+ date = (Time.now - 24*3600)
84
+ end
85
+ date
86
+ end
87
+
88
+ #################################
89
+ # useful function for operation #
90
+ #################################
91
+ def self.query_report( auth, param = nil, debug = false )
92
+ param = {} if not param
93
+ param[:type] ||= 'query'
94
+ param[:fields] ||= %w(click impression)
95
+ param[:level] ||= 'pair'
96
+ param[:range] ||= 'account'
97
+ param[:unit] ||= 'day'
98
+ download_report( auth, param, debug )
99
+ end
100
+
101
+ def self.creative_report( auth, param = nil, debug = false )
102
+ param = {} if not param
103
+ param[:type] ||= 'creative'
104
+ param[:fields] ||= %w(impression click cpc cost ctr cpm position conversion)
105
+ param[:level] ||= 'creative'
106
+ param[:range] ||= 'creative'
107
+ param[:unit] ||= 'day'
108
+ download_report( auth, param, debug )
109
+ end
110
+
111
+ def self.keyword_report( auth, param = nil, debug = false )
112
+ param = {} if not param
113
+ param[:type] ||= 'keyword'
114
+ param[:fields] ||= %w(impression click cpc cost ctr cpm position conversion)
115
+ param[:level] ||= 'keywordid'
116
+ param[:range] ||= 'keywordid'
117
+ param[:unit] ||= 'day'
118
+ download_report( auth, param, debug )
119
+ end
120
+
121
+ def self.download_report( auth, param, debug = false )
122
+ p param
123
+ response = get_id( auth, param )
124
+ if response[:succ]
125
+ id = response[:result]
126
+ p "Got report id:" + id.to_s if debug
127
+ loop do
128
+ sleep 2
129
+ break if get_state( auth, id )[:result] == 'Finished'
130
+ p "Report is not generated, waiting..." if debug
131
+ end
132
+
133
+ url = get_url( auth, id )[:result]
134
+ return open(url).read.force_encoding('gb18030').encode('utf-8')
135
+ else
136
+ raise response[:failure][0]["message"]
137
+ end
138
+ end
139
+
140
+ end # Repost
141
+ end # Baidu
142
+ end # API
143
+ end # PPC
@@ -0,0 +1,107 @@
1
+ # -*- coding:utf-8 -*-
2
+ require 'ppc/api/qihu/account'
3
+ require 'ppc/api/qihu/plan'
4
+ require 'ppc/api/qihu/group'
5
+ require 'ppc/api/qihu/keyword'
6
+ require 'ppc/api/qihu/creative'
7
+ require 'ppc/api/qihu/report'
8
+ require 'httparty'
9
+ require 'json'
10
+
11
+ module PPC
12
+ module API
13
+ class Qihu
14
+
15
+ def self.request( auth, service, method, params = {} )
16
+ url = "https://api.e.360.cn/2.0/#{service}/#{method}"
17
+ # 日后考虑将httpparty用Net/http代替
18
+ response = HTTParty.post(url,
19
+ body: params,
20
+ headers: {'apiKey' => auth[:token],
21
+ 'accessToken' => auth[:accessToken],
22
+ 'serveToken' => Time.now.to_i.to_s }
23
+ )
24
+ response.parsed_response
25
+ end
26
+
27
+ def self.process( response, key, failure_key = '', &func )
28
+ response_key = response.keys[0]
29
+ content = response[ response_key ]
30
+ # special case solution
31
+ if content == nil
32
+ return{ succ:true, failure:nil, result:nil }
33
+ end
34
+
35
+ result = { }
36
+ if content['failures'] != nil
37
+ result[:succ] = false
38
+ result[:failure] = content['failures']['item']
39
+ result[:result] = nil
40
+ else
41
+ result[:succ] = true
42
+ result[:result] = func[ key==''? content : content[ key ] ]
43
+ result[:failure] = failure_key == ''? nil : content[ failure_key ]
44
+ end
45
+ return result
46
+ end
47
+
48
+ def self.to_json_string( items )
49
+ '''
50
+ convert list of string/int to list of json string
51
+ '''
52
+ return '' if items == nil
53
+ items = [items] unless items.is_a? Array
54
+ items_str = []
55
+ items.each{ |x| items_str << x.to_s }
56
+ items_str.to_json
57
+ end
58
+
59
+ def self.to_id_list( ids_str )
60
+ return [] if ids_str == nil
61
+ ids_str = [ids_str] unless ids_str.is_a? Array
62
+ ids_i = []
63
+ ids_str.each{ |id| ids_i<<id.to_i }
64
+ ids_i
65
+ end
66
+
67
+ def self.make_type( params, map = @map)
68
+ '''
69
+ '''
70
+ params = [ params ] unless params.is_a? Array
71
+
72
+ types = []
73
+ params.each do |param|
74
+ type = {}
75
+
76
+ map.each do |key|
77
+ value = param[ key[0] ]
78
+ type[ key[1] ] = value if value != nil
79
+ end
80
+
81
+ types << type
82
+ end
83
+ return types
84
+ end
85
+
86
+ def self.reverse_type( types, map = @map )
87
+ '''
88
+ '''
89
+ types = [ types ] unless types.is_a? Array
90
+
91
+ params = []
92
+ types.each do |type|
93
+ param = {}
94
+
95
+ map.each do |key|
96
+ value = type[ key[1].to_s ]
97
+ param[ key[0] ] = value if value != nil
98
+ end
99
+
100
+ params << param
101
+ end
102
+ return params
103
+ end
104
+
105
+ end
106
+ end
107
+ end