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,159 @@
1
+ # -*- coding:utf-8 -*-
2
+ require 'json'
3
+
4
+ module PPC
5
+ module API
6
+ class Qihu
7
+ class Report< Qihu
8
+ Service = 'report'
9
+
10
+ @map =[
11
+ [:queryword,:queryword],
12
+ [:plan_id,:campaignId],
13
+ [:creative_id,:creativeId],
14
+ [:keyword,:keyword],
15
+ [:views,:views],
16
+ [:clicks,:clicks],
17
+ [:startDate,:startDate],
18
+ [:endDate,:endDate],
19
+ [:date,:date],
20
+ [:keyword_id,:keywordId],
21
+ [:group_id,:groupId],
22
+ [:cost,:totalCost],
23
+ [:position,:avgPosition],
24
+ [:total_num,:totalNumber],
25
+ [:total_page,:totalPage]
26
+ ]
27
+
28
+ ###################
29
+ # API abstraction #
30
+ ###################
31
+ def self.abstract( auth, type_name, method_name, key, param = nil, &func )
32
+ body = make_type( param )
33
+ response = request( auth, Service, method_name, body )
34
+ process( response, key ){ |x| func[ x ] }
35
+ end
36
+
37
+ type_list = ['keyword', 'query', 'creative', 'sublink']
38
+ type_list.each do |type|
39
+ # type
40
+ define_singleton_method type.to_sym do |auth, param|
41
+ abstract( auth, type, type, type+'List', param ){ |x| x['item']}
42
+ end
43
+ # typeCount
44
+ define_singleton_method (type+'_count').to_sym do |auth, param|
45
+ response = abstract( auth, type, type+'Count', '', param ){ |x| get_item(x) }
46
+ response[:result] = response[:result][0]
47
+ return response
48
+ end
49
+ # typeNow
50
+ define_singleton_method (type+'_now').to_sym do |auth, param|
51
+ abstract( auth, type, type+'Now', type+'List', param ){ |x| x['item']}
52
+ end
53
+ # typeNowCount
54
+ define_singleton_method (type+'_now_count').to_sym do |auth, param|
55
+ response = abstract( auth, type, type+'NowCount', '', param ){ |x| get_item(x) }
56
+ response[:result] = response[:result][0]
57
+ return response
58
+ end
59
+ end
60
+
61
+ ############################
62
+ # Interfaces for operation #
63
+ ############################
64
+ def self.keyword_report( auth, param, debug = false )
65
+ download_report(auth, 'keyword', param, debug )
66
+ end
67
+
68
+ def self.creative_report( auth, param, debug = false )
69
+ download_report(auth, 'creative', param, debug)
70
+ end
71
+
72
+ def self.download_report(auth, type, param, debug = false)
73
+ # deal_with time
74
+ now = Time.now.to_s[0...10]
75
+ is_now = now==parse_date(param[:startDate])
76
+
77
+ # get page num
78
+ if is_now
79
+ method = (type+'_now_count').to_sym
80
+ count = send(method, auth, param)[:result]
81
+ method = (type+'_now').to_sym
82
+ else
83
+ method = (type+'_count').to_sym
84
+ count = send(method, auth, param)[:result]
85
+ method = type.to_sym
86
+ end
87
+
88
+ report = []
89
+ count[:total_page].to_i.times do | page_i|
90
+ p "Start downloading #{page_i+1}th page, totally #{count[:total_page]} pages"
91
+ param[:page] = page_i +1
92
+ report_i = send(method, auth, param)[:result]
93
+ report += report_i
94
+ end
95
+
96
+ return report
97
+ end
98
+
99
+ ###################
100
+ # Helper Function #
101
+ ###################
102
+ # incase idlist == nil
103
+ private
104
+ def self.get_item( params )
105
+ return nil if params == nil
106
+ return reverse_type( params )
107
+ end
108
+
109
+ private
110
+ def self.make_type( param )
111
+ type = {}
112
+ # add option
113
+ type[:level] = param[:level] || 'account'
114
+ type[:page] = param[:page] || 1
115
+ # add ids
116
+ if param[:ids] != nil
117
+ ids = param[:ids]
118
+ ids = [ ids ] unless ids.is_a? Array
119
+ type[:IdList] = ids.to_json
120
+ end
121
+ # add date
122
+ if param[:startDate]==nil || param[:endDate]==nil
123
+ type[:startDate], type[:endDate] = get_date()
124
+ else
125
+ type[:startDate] = parse_date( param[:startDate] )
126
+ type[:endDate] = parse_date( param[:endDate] )
127
+ end
128
+
129
+ return type
130
+ end
131
+
132
+ private
133
+ def self.get_date()
134
+ endDate = Time.now.to_s[0,10]
135
+ startDate = (Time.now - 24*3600).to_s[0,10]
136
+ return startDate,endDate
137
+ end
138
+
139
+ private
140
+ def self.parse_date( date )
141
+ """
142
+ Cast string to time:
143
+ 'YYYYMMDD' => Time
144
+ """
145
+ if date
146
+ y = date[0..3]
147
+ m = date[4..5]
148
+ d = date[6..7]
149
+ date = Time.new( y, m, d )
150
+ else
151
+ date = (Time.now - 24*3600)
152
+ end
153
+ date.to_s[0,10]
154
+ end
155
+
156
+ end # Report
157
+ end # Qihu
158
+ end # API
159
+ end # PPC
@@ -0,0 +1,64 @@
1
+ module PPC
2
+ module API
3
+ class Baidu
4
+ @map = nil
5
+ @@debug = false
6
+
7
+ def self.debug_on
8
+ @@debug = true
9
+ end
10
+
11
+ def self.debug_off
12
+ @@debug = false
13
+ end
14
+
15
+ def self.request( auth, path, params = {} )
16
+ '''
17
+ request should return whole http response including header
18
+ '''
19
+ uri = URI("https://e.sm.cn#{path}")
20
+ http_body = {
21
+ header: {
22
+ username: auth[:username],
23
+ password: auth[:password],
24
+ token: auth[:token],
25
+ target: auth[:tarvget]
26
+ },
27
+ body: params
28
+ }.to_json
29
+
30
+ http_header = {
31
+ 'Content-Type' => 'application/json; charset=UTF-8'
32
+ }
33
+
34
+ http = Net::HTTP.new(uri.host, 443)
35
+ # 是否显示http通信输出
36
+ http.set_debug_output( $stdout ) if @@debug
37
+ http.use_ssl = true
38
+
39
+ response = http.post(uri.path, http_body, http_header)
40
+ response = JSON.parse( response.body )
41
+ end
42
+
43
+ def self.process( response, key, &func)
44
+ '''
45
+ Process Http response. If operation successes, return value of given keys.
46
+ You can process the result using function &func, or do nothing by passing
47
+ block {|x|x}
48
+ ===========================
49
+ @Output: resultType{ desc: boolean, failure: Array, result: Array }
50
+
51
+ failure is the failures part of response\'s header
52
+ result is the processed response body.
53
+ '''
54
+ p response
55
+ result = {}
56
+ result[:succ] = response['header']['desc']=='success'? true : false
57
+ result[:failure] = response['header']['failures']
58
+ result[:result] = func[ response['body'][key] ]
59
+ return result
60
+ end # process
61
+
62
+ end
63
+ end
64
+ end
@@ -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
@@ -0,0 +1,122 @@
1
+ require 'savon'
2
+ require 'active_support'
3
+ require 'ppc/api/sogou/account'
4
+ require 'ppc/api/sogou/plan'
5
+ require 'ppc/api/sogou/group'
6
+ require 'ppc/api/sogou/keyword'
7
+ require 'ppc/api/sogou/report'
8
+ require 'ppc/api/sogou/creative'
9
+
10
+ module PPC
11
+ module API
12
+ class Sogou
13
+
14
+ @map = nil
15
+ @@debug = false
16
+
17
+ def self.debug_on
18
+ @@debug = true
19
+ end
20
+
21
+ def self.debug_off
22
+ @@debug = false
23
+ end
24
+
25
+ def self.request( auth, service, method, params = {})
26
+ '''
27
+ ps. in savon3, .hash method will turn CamelXML to snake hash
28
+ preprocess response, extract
29
+ '''
30
+
31
+ service = service + "Service"
32
+ client = Savon.new( "http://api.agent.sogou.com:8080/sem/sms/v1/#{service}?wsdl" )
33
+ operation = client.operation( service, service, method )
34
+
35
+ operation.header = {
36
+ AuthHeader:{
37
+ username: auth[:username],
38
+ password: auth[:password],
39
+ token: auth[:token]
40
+ }
41
+ }
42
+ operation.body = { (method+'Request').to_sym => params }
43
+ # debug print
44
+ debug_print( operation ) if @@debug
45
+ result = operation.call.hash[:envelope]
46
+ #extract header and body
47
+ response = { }
48
+ response[:header] = result[:header][:res_header]
49
+ response[:body] = result[:body][ (method + "Response").snake_case.to_sym ]
50
+ # debug print
51
+ p response if @@debug
52
+ return response
53
+ end
54
+
55
+ def self.debug_print( operation )
56
+ p '{:header=>' + operation.header.to_s + ', :body=>' + operation.body.to_s + '}'
57
+ end
58
+
59
+ def self.process( response, key, debug = false, &func )
60
+ '''
61
+ @input
62
+ : type key : string
63
+ : param key : type name, we will transfer camel string
64
+ into snake_case symbol inside the method
65
+ '''
66
+ return response if debug
67
+
68
+ result = {}
69
+ result[:succ] = response[:header][:desc]=='success'? true : false
70
+ result[:failure] = response[:header][:failures]
71
+ result[:result] = func[ response[:body][ key.snake_case.to_sym ] ]
72
+ return result
73
+ end
74
+
75
+ def self.make_type( params, map = @map)
76
+ '''
77
+ For more info visit ::PPC::API::sogou:make_type
78
+ '''
79
+ params = [ params ] unless params.is_a? Array
80
+
81
+ types = []
82
+ params.each do |param|
83
+ type = {}
84
+
85
+ map.each do |key|
86
+ value = param[ key[0] ]
87
+ type[ key[1] ] = value if value != nil
88
+ end
89
+
90
+ types << type
91
+ end
92
+ return types
93
+ end
94
+
95
+ def self.reverse_type( types, map = @map )
96
+ '''
97
+ For more info visit ::PPC::API::sogou:reverse_type
98
+ Here, the camel key will be turn into snake key
99
+ '''
100
+ types = [ types ] unless types.is_a? Array
101
+
102
+ params = []
103
+ types.each do |type|
104
+ param = {}
105
+
106
+ map.each do |key|
107
+ # 这里做两次转换并不是十分高效的方法,考虑maintain两张map
108
+ value = type[ key[1].to_s.snake_case.to_sym ]
109
+ param[ key[0] ] = value if value != nil
110
+ end
111
+
112
+ params << param
113
+ end
114
+ return params
115
+ end
116
+
117
+
118
+
119
+ end
120
+ end
121
+ end
122
+