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,150 @@
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 'ppc/api/qihu/sublink'
9
+ require 'ppc/api/qihu/bulk'
10
+ require 'ppc/api/qihu/rank'
11
+
12
+ module PPC
13
+ module API
14
+ class Qihu
15
+
16
+ extend ::PPC::API
17
+
18
+ def self.request_uri(param = {})
19
+ URI("https://api.e.360.cn/2.0/#{param[:service]}/#{param[:method]}")
20
+ end
21
+
22
+ def self.request_http_header(auth)
23
+ {
24
+ 'Content-Type' => 'application/x-www-form-urlencoded',
25
+ 'apiKey' => auth[:api_key],
26
+ 'accessToken' => auth[:token],
27
+ 'serveToken' => Time.now.to_i.to_s
28
+ }
29
+ end
30
+
31
+ def self.request_http_body(auth, params = {})
32
+ params["format"] = 'json'
33
+ params.map{|k,v| "#{k.to_s}=#{v.to_s}"}.join('&')
34
+ end
35
+
36
+ # def self.request( auth, service, method, params = {} )
37
+ # p 123
38
+ # url = "https://api.e.360.cn/2.0/#{service}/#{method}"
39
+ # # 日后考虑将httpparty用Net/http代替
40
+ # response = HTTParty.post(url,
41
+ # body: params,
42
+ # headers: {'apiKey' => auth[:api_key],
43
+ # 'accessToken' => auth[:token],
44
+ # 'serveToken' => Time.now.to_i.to_s }
45
+ # )
46
+ # p response
47
+ # response.parsed_response
48
+ # end
49
+
50
+ def self.process( response, key, failure_key = '', &func )
51
+ # special case solution
52
+ # 只有account getInfo 的key 为 '',因为其特殊逻辑,单独处理
53
+ result = { }
54
+ if response == []
55
+ result[:succ] = true
56
+ result[:failure] = nil
57
+ result[:result] = nil
58
+ result[:no_quota] = false
59
+ return result
60
+ end
61
+ if key == ''
62
+ if !response['failures'].nil?
63
+ result[:succ] = false
64
+ result[:result] = nil
65
+ result[:failure] = response['failures']
66
+ result[:no_quota] = is_no_quota(response['failures'], '90401')
67
+ return result
68
+ end
69
+ result[:succ] = true
70
+ result[:failure] = nil
71
+ result[:no_quota] = false
72
+ result[:result] = func[response]
73
+ return result
74
+ end
75
+ result[:result] = func[response[key]]
76
+ result[:failure] = response['failures']
77
+ result[:no_quota] = is_no_quota(response['failures'], '90401')
78
+ result[:succ] = response['failures'].nil? || response['failures'].size.zero?
79
+ #if response['failures'] != nil
80
+ # result[:succ] = false
81
+ # result[:failure] = response['failures']['item']
82
+ # result[:result] = nil
83
+ #else
84
+ # result[:succ] = true
85
+ # result[:result] = func[ key==''? response : response[ key ] ]
86
+ # result[:failure] = failure_key == ''? nil : response[ failure_key ]
87
+ #end
88
+ return result
89
+ end
90
+
91
+ def self.to_json_string( items )
92
+ '''
93
+ convert list of string/int to list of json string
94
+ '''
95
+ return '' if items == nil
96
+ items = [items] unless items.is_a? Array
97
+ items_str = []
98
+ items.each{ |x| items_str << x.to_s }
99
+ items_str.to_json
100
+ end
101
+
102
+ def self.to_id_list( ids_str )
103
+ return [] if ids_str == nil
104
+ ids_str = [ids_str] unless ids_str.is_a? Array
105
+ ids_i = []
106
+ ids_str.each{ |id| ids_i<<id.to_i }
107
+ ids_i
108
+ end
109
+
110
+ def self.make_type( params, map = @map)
111
+ '''
112
+ '''
113
+ params = [ params ] unless params.is_a? Array
114
+
115
+ types = []
116
+ params.each do |param|
117
+ type = {}
118
+
119
+ map.each do |key|
120
+ value = param[ key[0] ]
121
+ type[ key[1] ] = value if value != nil
122
+ end
123
+
124
+ types << type
125
+ end
126
+ return types
127
+ end
128
+
129
+ def self.reverse_type( types, map = @map )
130
+ '''
131
+ '''
132
+ types = [ types ] unless types.is_a? Array
133
+
134
+ params = []
135
+ types.each do |type|
136
+ param = {}
137
+
138
+ map.each do |key|
139
+ value = type[ key[1].to_s ]
140
+ param[ key[0] ] = value if value != nil
141
+ end
142
+
143
+ params << param
144
+ end
145
+ return params
146
+ end
147
+
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,86 @@
1
+ # -*- coding:utf-8 -*-
2
+ require 'json'
3
+ module PPC
4
+ module API
5
+ class Qihu
6
+ class Account < Qihu
7
+ Service = 'account'
8
+
9
+ @map = [
10
+ [ :id, :uid ],
11
+ [ :name, :userName ],
12
+ [ :email, :email],
13
+ [ :company, :companyName],
14
+ [ :industry1, :industry1],
15
+ [ :industry2, :industry2],
16
+ [ :balance, :balance],
17
+ [ :budget, :budget],
18
+ [ :resources, :resources],
19
+ [ :open_domains, :allowDomain]
20
+ ]
21
+
22
+ def self.info( auth )
23
+ response = request( auth, Service, 'getInfo' )
24
+ process( response, '' ){ |x| reverse_type( x )[0]}
25
+ end
26
+
27
+ def self.update( auth, params )
28
+ '''
29
+ 对奇虎两个update的在封装。如果所有操作成功,succ为true,否则为false
30
+ failure中以字符串方式返回失败的操作
31
+ '''
32
+ result = {}
33
+ result[:succ] = true
34
+ result[:failure] = []
35
+ result[:result] = []
36
+
37
+ if params[:budget] != nil
38
+ budget_result = update_budget( auth, params[:budget] )
39
+ result[:succ] = result[:succ] && budget_result[:succ]
40
+ result[:failure] << 'budget' unless budget_result[:succ]
41
+ end
42
+
43
+ if params[:exclude_ip] != nil
44
+ ip_result = update_exclude_ip( auth, params[:exclude_ip] )
45
+ result[:succ] = result[:succ] && ip_result[:succ]
46
+ result[:failure] << 'exclude_ip' unless budget_result[:succ]
47
+ end
48
+
49
+ return result
50
+ end
51
+
52
+ def self.get_all_object( auth, ids )
53
+ #文档上面写的输入类型是String?
54
+ body = { 'idList' => ids }
55
+ response = request( auth, Service, 'getAllObjects' )
56
+ process( response, 'account_getAllObjects_response' ){ |x| x }
57
+ end
58
+
59
+ def self.get_file_state( auth, id )
60
+ body = { 'fileId' => id }
61
+ response = request( auth, Service, 'getAllObjects' , body )
62
+ process( response, 'account_getFileState_response' ){ |x| x }
63
+ end
64
+
65
+ def self.get_exclude_ip( auth )
66
+ response = request( auth, Service, 'getExcludeIp' )
67
+ process( response, 'excludeIpList' ){ |x| x}
68
+ end
69
+
70
+ private
71
+ def self.update_budget( auth, budget )
72
+ response = request( auth, Service, 'updateBudget', { budget:budget })
73
+ process( response, 'affectedRecords' ){ | x | x.to_i==1 ? 'success' : 'failure' }
74
+ end
75
+
76
+ private
77
+ def self.update_exclude_ip( auth, ips )
78
+ ips = to_json_string( ips )
79
+ response = request( auth, Service, 'updateExcludeIp', { excludeIpList: ips } )
80
+ process( response, '' ){|x| x}
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,35 @@
1
+ # -*- coding:utf-8 -*-
2
+ require 'json'
3
+ module PPC
4
+ module API
5
+ class Qihu
6
+ class Bulk < Qihu
7
+ Service = 'account'
8
+
9
+ def self.get_all_object( auth, ids )
10
+ #文档上面写的输入类型是String?
11
+ body = nil
12
+ body = { 'idList' => to_json_string(ids) } if ids
13
+ response = request( auth, Service, 'getAllObjects', body )
14
+ process( response, 'fileId' ){ |x| x }
15
+ end
16
+
17
+ def self.get_file_state( auth, id )
18
+ body = { 'fileId' => id }
19
+ response = request( auth, Service, 'getFileState' , body )
20
+ process( response, '' ){ |x| x }
21
+ end
22
+
23
+ def self.download( auth, ids = nil)
24
+ result = get_all_object(auth, ids)
25
+ field_id = result[:result]
26
+ loop do
27
+ status = get_file_state(auth, field_id)
28
+ return status if status[:result]['isGenerated'] == 'success'
29
+ sleep 15
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,108 @@
1
+ # -*- coding:utf-8 -*-
2
+ module PPC
3
+ module API
4
+ class Qihu
5
+ class Creative< Qihu
6
+ Service = 'creative'
7
+
8
+ @map = [
9
+ [:id,:id] ,
10
+ [:group_id, :groupId],
11
+ [:title,:title],
12
+ [:description1, :description1],
13
+ [:description2, :description2],
14
+ [:pc_destination, :destinationUrl],
15
+ [:pc_display, :displayUrl],
16
+ [:mobile_destination, :mobileDestinationUrl],
17
+ [:mobile_display, :mobileDisplayUrl]
18
+ ]
19
+
20
+ @status_map = [
21
+ [:id,:id],
22
+ [:quality,:qualityScore],
23
+ [:status,:status]
24
+ ]
25
+
26
+ def self.get( auth, ids )
27
+ body = { 'idList' => to_json_string( ids ) }
28
+ response = request( auth, Service, 'getInfoByIdList', body )
29
+ process( response, 'creativeList'){ |x| reverse_type(x) }
30
+ end
31
+
32
+ def self.add( auth, creatives )
33
+ creative_types = make_type( creatives ).to_json
34
+ body = { 'creatives' => creative_types}
35
+ response = request( auth, Service, 'add', body )
36
+ process( response, 'creativeIdList'){ |x| to_id_hash_list(x) }
37
+ end
38
+
39
+ # helper function for self.add() method
40
+ private
41
+ def self.to_id_hash_list( str )
42
+ reuturn [] if str == nil
43
+ str = [str] unless str.is_a?Array
44
+ x= []
45
+ str.each{ |i| x << { id: i.to_i } }
46
+ return x
47
+ end
48
+
49
+ def self.update( auth, creatives )
50
+ creative_types = make_type( creatives ).to_json
51
+ body = { 'creatives' => creative_types}
52
+ response = request( auth, Service, 'update', body )
53
+ process( response, 'affectedRecords', 'failCreativeIds' ){ |x| x }
54
+ end
55
+
56
+ # 对update的再封装实现activate方法,未测试
57
+ def self.activate( auth, ids )
58
+ creatives = []
59
+ ids.each{ |id| creatives << { id: id, status:'enable'} }
60
+ update( auth, creatives )
61
+ end
62
+
63
+ def self.delete( auth, ids )
64
+ ids = to_json_string( ids )
65
+ body = { 'idList' => ids }
66
+ response = request( auth, Service, 'deleteByIdList', body )
67
+ process( response, 'affectedRecords' ){ |x|x }
68
+ end
69
+
70
+ def self.status( auth, ids )
71
+ body = { idList: to_json_string( ids ) }
72
+ response = request( auth, Service, 'getStatusByIdList', body )
73
+ process( response, 'creativeList' ){ |x| reverse_type( x, @status_map ) }
74
+ end
75
+
76
+ # quality 本质上和 status 在一个方法里面
77
+ def self.quality( auth, ids )
78
+ status( auth, ids)
79
+ end
80
+
81
+ def self.search_id_by_group_id( auth, id, status = nil)
82
+ # 处理条件
83
+ body = {}
84
+ body['status'] = status if status
85
+ body['groupId'] = id
86
+ response = request( auth, Service, 'getIdListByGroupId', body )
87
+ # 伪装成百度接口
88
+ process( response, 'creativeIdList' ){
89
+ |x|
90
+ [ { group_id:id, creative_ids:to_id_list(x) } ]
91
+ }
92
+ end
93
+
94
+ # combine two methods to provide another mether
95
+ def self.search_by_group_id( auth, id )
96
+ creative_ids = search_id_by_group_id( auth, id )
97
+ response = get( auth , creative_ids )
98
+ # 伪装成百度接口
99
+ if response[:succ]
100
+ response[:result] = [ { group_id:id, creatives:response[:result ] } ]
101
+ end
102
+ return response
103
+ end
104
+
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,96 @@
1
+ # -*- coding:utf-8 -*-
2
+ module PPC
3
+ module API
4
+ class Qihu
5
+ class Group < Qihu
6
+ Service = 'group'
7
+
8
+ @map = [
9
+ [:id, :id ],
10
+ [:plan_id, :campaignId],
11
+ [:status, :status ],
12
+ [:name, :name ],
13
+ [:price, :price ],
14
+ # negateive为json格式,make_type要定制
15
+ [:add_time, :addTime],
16
+ [:update_time, :updateTime]
17
+ ]
18
+
19
+ # 再次封装提供all和ids
20
+ def self.ids( auth )
21
+ '''
22
+ 接口与其他的相同,但是无论如何:succ均为true,不返回任何错误信息
23
+ 因为是重复调用了search的接口,速度非常的慢,* 慎用 *
24
+ '''
25
+ plan_ids = ::PPC::API::Qihu::Plan::ids( auth )[:result]
26
+ plan_group_ids = []
27
+
28
+ plan_ids.each do |plan_id|
29
+ plan_group_id = {}
30
+ plan_group_id[:plan_id] = plan_id
31
+ plan_group_id[:group_ids] = search_id_by_plan_id( auth, plan_id )[:result]
32
+ plan_group_ids << plan_group_id
33
+ end
34
+ return { succ: true, failure:nil, result: plan_group_ids }
35
+ end
36
+
37
+ def self.all( auth )
38
+ '''
39
+ unimplemented due to ineffciency
40
+ '''
41
+ # unimplement
42
+ end
43
+
44
+ # 奇虎组服务不提供批量add,delete和update方法
45
+ def self.add( auth, group )
46
+ response = request( auth, Service, 'add', make_type( group )[0] )
47
+ # 保证接口一致性
48
+ process( response, 'id' ){ |x| [ { id:x.to_i } ] }
49
+ end
50
+
51
+ def self.update( auth, group )
52
+ if group[:negative] || group[:exact_negative]
53
+ ng = {"exact" => group[:exact_negative], "phrase" => group[:negative]}.to_json
54
+ end
55
+ params = make_type(group)[0]
56
+ params.merge!({:negativeWords => ng}) if ng
57
+ response = request( auth, Service, 'update', params )
58
+ # 保证接口一致性
59
+ process( response, 'id' ){ |x|[ { id:x.to_i } ] }
60
+ end
61
+
62
+ def self.get( auth, ids )
63
+ ids = to_json_string( ids )
64
+ body = { 'idList' => ids }
65
+ response = request( auth, Service, 'getInfoByIdList', body )
66
+ process( response, 'groupList' ){ |x| reverse_type(x) }
67
+ end
68
+
69
+ def self.delete( auth, id )
70
+ response = request( auth, Service, 'deleteById', { id: id} )
71
+ process( response, 'affectedRecords' ){ |x| x == '1' }
72
+ end
73
+
74
+ def self.search_id_by_plan_id( auth, id )
75
+ response = request( auth, Service, 'getIdListByCampaignId', { 'campaignId' => id.to_s })
76
+ #为了保持接口一致性,这里也是伪装成了百度的接口
77
+ process( response, 'groupIdList' ){
78
+ |x|
79
+ [ { plan_id:id, group_ids: to_id_list(x) } ]
80
+ }
81
+ end
82
+
83
+ # combine searchIdbyId and get to provide another method
84
+ def self.search_by_plan_id( auth, id )
85
+ group_ids = search_id_by_plan_id( auth, id )[:result][0][:group_ids]
86
+ response = get( auth, group_ids )
87
+ if response[:succ]
88
+ response[:result] = [ { plan_id:id, groups:response[:result]}]
89
+ end
90
+ return response
91
+ end
92
+
93
+ end
94
+ end
95
+ end
96
+ end