sklik-api 0.0.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.
@@ -0,0 +1,99 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class SklikApi
3
+ class Adtext
4
+
5
+ NAME = "ad"
6
+
7
+ include Object
8
+ =begin
9
+ Example of input hash
10
+ {
11
+ :headline => "Super headline",
12
+ :description1 => "Trying to do ",
13
+ :description2 => "best description ever",
14
+ :display_url => "my_test_url.cz",
15
+ :url => "http://my_test_url.cz"
16
+ }
17
+
18
+
19
+ =end
20
+
21
+ def initialize adgroup, args
22
+ @adtext_data = nil
23
+ #set adtext owner adgroup
24
+ @adgroup = adgroup
25
+
26
+ super args
27
+ end
28
+
29
+
30
+ def create_args
31
+ raise ArgumentError, "Adtexts need's to know adgroup_id" unless @adgroup.args[:adgroup_id]
32
+ out = []
33
+ #add campaign id to know where to create adgroup
34
+ out << @adgroup.args[:adgroup_id]
35
+
36
+ #add adtext struct
37
+ args = {}
38
+ args[:creative1] = @args[:headline]
39
+ args[:creative2] = @args[:description1]
40
+ args[:creative3] = @args[:description2]
41
+ args[:clickthruText] = @args[:display_url]
42
+ args[:clickthruUrl] = @args[:url]
43
+ out << args
44
+
45
+ #return output
46
+ out
47
+ end
48
+
49
+ def self.find adgroup, args = {}
50
+ out = []
51
+ super(NAME, adgroup.args[:adgroup_id]).each do |adtext|
52
+ if args[:adtext_id].nil? || (args[:adtext_id] && args[:adtext_id].to_i == adtext[:id].to_i)
53
+ out << Adtext.new(
54
+ adgroup,
55
+ :adtext_id => adtext[:id],
56
+ :headline => adtext[:creative1],
57
+ :description1 => adtext[:creative2],
58
+ :description2 => adtext[:creative3],
59
+ :display_url =>adtext[:clickthruText],
60
+ :url => adtext[:clickthruUrl],
61
+ :name => adtext[:name],
62
+ :status => fix_status(adtext)
63
+ )
64
+ end
65
+ end
66
+ out
67
+ end
68
+
69
+ def self.fix_status adtext
70
+ if adtext[:removed] == true
71
+ return :stopped
72
+ elsif adtext[:status] == "active"
73
+ return :running
74
+ elsif adtext[:status] == "suspend"
75
+ return :paused
76
+ else
77
+ return :unknown
78
+ end
79
+ end
80
+
81
+ def to_hash
82
+ if @adtext_data
83
+ @adtext_data
84
+ else
85
+ @adtext_data = @args
86
+ end
87
+ end
88
+
89
+ def save
90
+ if @args[:adtext_id] #do update
91
+
92
+ else #do save
93
+ #create adtext
94
+ create
95
+ end
96
+ end
97
+ end
98
+ end
99
+
@@ -0,0 +1,109 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class SklikApi
3
+ class Keyword
4
+
5
+ NAME = "keyword"
6
+
7
+ include Object
8
+ =begin
9
+ Example of input hash
10
+ {
11
+ :keyword => "\"some funny keyword\""
12
+ }
13
+ =end
14
+
15
+ def initialize adgroup, args
16
+ @keyword_data = nil
17
+ #set keyword owner adgroup
18
+ @adgroup = adgroup
19
+
20
+ super args
21
+ end
22
+
23
+ def create_args
24
+ raise ArgumentError, "Keyword need's to know adgroup_id" unless @adgroup.args[:adgroup_id]
25
+ out = []
26
+ #add campaign id to know where to create adgroup
27
+ out << @adgroup.args[:adgroup_id]
28
+
29
+ #add adtext struct
30
+ args = {}
31
+ args[:name] = strip_match_type @args[:keyword]
32
+ args[:matchType] = get_math_type @args[:keyword]
33
+ out << args
34
+
35
+ #return output
36
+ out
37
+ end
38
+
39
+ def strip_match_type keyword
40
+ keyword.gsub(/(\[|\]|\")/, "")
41
+ end
42
+
43
+ def get_math_type keyword
44
+ if /^\[.*\]$/ =~ keyword
45
+ return "exact"
46
+ elsif /^\".*\"$/ =~ keyword
47
+ return "phrase"
48
+ else
49
+ return "broad"
50
+ end
51
+ end
52
+
53
+ def self.apply_math_type keyword, match_type
54
+ return case match_type
55
+ when "broad" then keyword
56
+ when "phrase" then "\"#{keyword}\""
57
+ when "exact" then "[#{keyword}]"
58
+ else keyword
59
+ end
60
+ end
61
+
62
+ def self.find adgroup, args = {}
63
+ out = []
64
+ super(NAME, adgroup.args[:adgroup_id]).each do |keyword|
65
+ if args[:keyword_id].nil? || (args[:keyword_id] && args[:keyword_id].to_i == keyword[:id].to_i)
66
+ out << Keyword.new(
67
+ adgroup,
68
+ :keyword_id => keyword[:id],
69
+ :keyword => apply_math_type(keyword[:name], keyword[:matchType] ),
70
+ :status => fix_status(keyword)
71
+ )
72
+ end
73
+ end
74
+ out
75
+ end
76
+
77
+ def self.fix_status keyword
78
+ if keyword[:removed] == true
79
+ return :stopped
80
+ elsif keyword[:status] == "active"
81
+ return :running
82
+ elsif keyword[:status] == "suspend"
83
+ return :paused
84
+ elsif keyword[:status] == "nonactive"
85
+ return :paused_by_low_cpc
86
+ else
87
+ return :unknown
88
+ end
89
+ end
90
+
91
+ def to_hash
92
+ if @keyword_data
93
+ @keyword_data
94
+ else
95
+ @keyword_data = @args[:keyword]
96
+ end
97
+ end
98
+
99
+ def save
100
+ if @args[:keyword_id] #do update
101
+
102
+ else #do save
103
+ #create adtext
104
+ create
105
+ end
106
+ end
107
+ end
108
+ end
109
+
@@ -0,0 +1,82 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class SklikApi
3
+ class Connection
4
+
5
+ MAX_RETRIES = 3
6
+ DEFAULTS = {
7
+ :debug => false,
8
+ :timeout => 100
9
+ }
10
+
11
+ def initialize args = {}
12
+ @args = DEFAULTS.merge(args)
13
+ end
14
+
15
+ def self.connection
16
+ @connection ||= SklikApi::Connection.new(:debug => false)
17
+ end
18
+
19
+ #prepare connection to sklik
20
+ def connection
21
+ server = XMLRPC::Client.new3(:host => "api.sklik.cz", :path => "/RPC2", :port => 443, :use_ssl => true, :timeout => @args[:timeout])
22
+ server.instance_variable_get(:@http).instance_variable_set(:@verify_mode, OpenSSL::SSL::VERIFY_NONE)
23
+ #fix of UTF-8 encoding
24
+ server.extend(XMLRPCWorkAround)
25
+ #debug mode to see what XMLRPC is doing
26
+ server.set_debug(File.open("log/xmlrpc-#{Time.now.strftime("%Y_%m_%d-%H_%M_%S")}.log","a:UTF-8")) if @args[:debug]
27
+
28
+ server
29
+ end
30
+
31
+ #Get session is method for login into sklik
32
+ #save session for other requests until it expires
33
+ #every taxonomy has its own session!
34
+ def get_session force = false
35
+ @session ||= {}
36
+ if @session.has_key?(SklikApi::Access.uniq_identifier) && !force
37
+ @session[SklikApi::Access.uniq_identifier]
38
+ else
39
+ begin
40
+ param = connection.call("client.login", SklikApi::Access.email, SklikApi::Access.password).symbolize_keys
41
+ if param[:status] == 401
42
+ raise ArgumentError, "Invalid login for: #{SklikApi::Access.email}"
43
+ elsif param[:status] == 200
44
+ return @session[SklikApi::Access.uniq_identifier] = param[:session]
45
+ else
46
+ raise ArgumentError, param[:statusMessage]
47
+ end
48
+ rescue XMLRPC::FaultException => e
49
+ raise ArgumentError, "#{e.faultString}, #{e.faultCode}"
50
+ rescue Exception => e
51
+ raise e
52
+ end
53
+ end
54
+ end
55
+
56
+ # method to wrap method call to sklik -> allow retry and problem with session expiration
57
+ def call method, *args
58
+ retry_count = MAX_RETRIES
59
+ begin
60
+ #get response from sklik
61
+ param = connection.call( method, get_session, *args ).symbolize_keys
62
+ if param[:status] == 200
63
+ return yield(param)
64
+ elsif param[:status] == 406
65
+ raise ArgumentError, "Sklik returned: #{param[:statusMessage]} \n #{args.inspect}"
66
+ elsif param[:statusMessage] == "Session has expired or is malformed."
67
+ raise ArgumentError, "session has expired"
68
+ else
69
+ raise ArgumentError, "There is error from sklik #{method}: #{param[:statusMessage]}"
70
+ end
71
+ rescue Exception => e
72
+ pp "Rescuing from request by: #{e.class} - #{e.message}"
73
+ #if session expired then get new one! and retry
74
+ get_session(true) if e.message == "session has expired"
75
+
76
+ retry_count -= 1
77
+ retry if retry_count > 0
78
+ raise e
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,60 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class SklikApi
3
+ module Object
4
+ def self.included(base)
5
+ base.send :extend, ClassMethods
6
+ base.send :include, InstanceMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def connection
12
+ SklikApi::Connection.connection
13
+ end
14
+
15
+ def find name, id = nil
16
+ if id
17
+ args = ["list#{name.pluralize.camelize}", id]
18
+ else
19
+ args = ["list#{name.pluralize.camelize}"]
20
+ end
21
+ return connection.call(*args) { |param|
22
+ #return list of all objects
23
+ param[name.pluralize.to_sym].collect{|c| c.symbolize_keys }
24
+ }
25
+ end
26
+ end
27
+
28
+ module InstanceMethods
29
+ #get connection for request
30
+ def connection
31
+ SklikApi::Connection.connection
32
+ end
33
+
34
+ def initialize args
35
+ @args = args
36
+ return self
37
+ end
38
+
39
+ def args
40
+ @args
41
+ end
42
+
43
+ def create
44
+ out = connection.call("#{self.class::NAME}.create", *create_args ) { |param|
45
+ param["#{self.class::NAME}Id".to_sym]
46
+ }
47
+ @args["#{self.class.to_s.downcase.split(":").last}_id".to_sym] = out
48
+ @args
49
+ end
50
+
51
+ def create_args
52
+ raise(NoMethodError, "Please implement 'create_args' method in class: #{self.class} - should return array which will be placed into create method")
53
+ end
54
+
55
+ def to_hash
56
+ raise(NoMethodError, "Please implement 'to_hash' method in class: #{self.class} - should return hash which contains all data")
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ #Settings for sklik & eTarget
4
+ XMLRPC::Config.const_set(:ENABLE_NIL_PARSER, true)
5
+ XMLRPC::Config.const_set(:ENABLE_NIL_CREATE, true)
6
+
7
+ #Hack for enabling debug mode!
8
+ class XMLRPC::Client
9
+ def set_debug out = $stderr
10
+ @http.set_debug_output(out);
11
+ end
12
+ end
13
+
14
+ #Hack for force encoding to UTF-8
15
+ module XMLRPCWorkAround
16
+ def do_rpc(request, async=false)
17
+ data = super
18
+ data.force_encoding("UTF-8")
19
+ data
20
+ end
21
+ end
@@ -0,0 +1,101 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "sklik-api"
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ondrej Bartas"]
12
+ s.date = "2012-03-25"
13
+ s.description = "Sklik advertising PPC api for creating campaigns and updating them when they runs"
14
+ s.email = "ondrej@bartas.cz"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.markdown"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "LICENSE.txt",
23
+ "README.markdown",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "config/access.rb.example",
27
+ "lib/sklik-api.rb",
28
+ "lib/sklik-api/access.rb",
29
+ "lib/sklik-api/campaign.rb",
30
+ "lib/sklik-api/campaign_parts/adgroup.rb",
31
+ "lib/sklik-api/campaign_parts/adtext.rb",
32
+ "lib/sklik-api/campaign_parts/keyword.rb",
33
+ "lib/sklik-api/connection.rb",
34
+ "lib/sklik-api/sklik_object.rb",
35
+ "lib/sklik-api/xmlrpc_setup.rb",
36
+ "sklik-api.gemspec",
37
+ "test/fake_web.rb",
38
+ "test/helper.rb",
39
+ "test/unit/campaign.rb"
40
+ ]
41
+ s.homepage = "http://github.com/ondrejbartas/sklik-api"
42
+ s.licenses = ["MIT"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = "1.8.10"
45
+ s.summary = "Sklik advertising PPC api for creating campaigns"
46
+
47
+ if s.respond_to? :specification_version then
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_runtime_dependency(%q<json>, [">= 0"])
52
+ s.add_runtime_dependency(%q<unicode>, ["~> 0.4.0"])
53
+ s.add_runtime_dependency(%q<text>, ["~> 0.2.0"])
54
+ s.add_runtime_dependency(%q<i18n>, ["~> 0.6.0"])
55
+ s.add_runtime_dependency(%q<activesupport>, ["~> 3.1.0"])
56
+ s.add_development_dependency(%q<rack-test>, [">= 0"])
57
+ s.add_development_dependency(%q<shoulda-context>, [">= 0"])
58
+ s.add_development_dependency(%q<turn>, ["~> 0.8.2"])
59
+ s.add_development_dependency(%q<minitest>, [">= 0"])
60
+ s.add_development_dependency(%q<ansi>, ["~> 1.2.5"])
61
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
62
+ s.add_development_dependency(%q<fakeweb>, [">= 0"])
63
+ s.add_development_dependency(%q<thin>, [">= 0"])
64
+ s.add_development_dependency(%q<shotgun>, [">= 0"])
65
+ s.add_development_dependency(%q<rcov>, ["= 0.9.10"])
66
+ else
67
+ s.add_dependency(%q<json>, [">= 0"])
68
+ s.add_dependency(%q<unicode>, ["~> 0.4.0"])
69
+ s.add_dependency(%q<text>, ["~> 0.2.0"])
70
+ s.add_dependency(%q<i18n>, ["~> 0.6.0"])
71
+ s.add_dependency(%q<activesupport>, ["~> 3.1.0"])
72
+ s.add_dependency(%q<rack-test>, [">= 0"])
73
+ s.add_dependency(%q<shoulda-context>, [">= 0"])
74
+ s.add_dependency(%q<turn>, ["~> 0.8.2"])
75
+ s.add_dependency(%q<minitest>, [">= 0"])
76
+ s.add_dependency(%q<ansi>, ["~> 1.2.5"])
77
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
78
+ s.add_dependency(%q<fakeweb>, [">= 0"])
79
+ s.add_dependency(%q<thin>, [">= 0"])
80
+ s.add_dependency(%q<shotgun>, [">= 0"])
81
+ s.add_dependency(%q<rcov>, ["= 0.9.10"])
82
+ end
83
+ else
84
+ s.add_dependency(%q<json>, [">= 0"])
85
+ s.add_dependency(%q<unicode>, ["~> 0.4.0"])
86
+ s.add_dependency(%q<text>, ["~> 0.2.0"])
87
+ s.add_dependency(%q<i18n>, ["~> 0.6.0"])
88
+ s.add_dependency(%q<activesupport>, ["~> 3.1.0"])
89
+ s.add_dependency(%q<rack-test>, [">= 0"])
90
+ s.add_dependency(%q<shoulda-context>, [">= 0"])
91
+ s.add_dependency(%q<turn>, ["~> 0.8.2"])
92
+ s.add_dependency(%q<minitest>, [">= 0"])
93
+ s.add_dependency(%q<ansi>, ["~> 1.2.5"])
94
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
95
+ s.add_dependency(%q<fakeweb>, [">= 0"])
96
+ s.add_dependency(%q<thin>, [">= 0"])
97
+ s.add_dependency(%q<shotgun>, [">= 0"])
98
+ s.add_dependency(%q<rcov>, ["= 0.9.10"])
99
+ end
100
+ end
101
+