Merge branch 'release-0.5.0'
diff --git a/lib/predictionio/client.rb b/lib/predictionio/client.rb
index ac83e76..ba3a2c8 100644
--- a/lib/predictionio/client.rb
+++ b/lib/predictionio/client.rb
@@ -37,15 +37,15 @@
# == Special Handling of Some Optional Arguments
# Some optional arguments have additional special handling:
# - For all requests that accept "itypes" as input, the value can be supplied as either an Array of String's, or a comma-delimited String.
- # - For all requests that accept "latlng" as input, they will also accept "latitude" and "longitude".
- # When these are supplied, they will override any existing "latlng" value.
- # - All time arguments (e.g. t, startT, endT, etc) can be supplied as either a Time or Float object.
+ # - For all requests that accept "pio_latlng" as input, they will also accept "pio_latitude" and "pio_longitude".
+ # When these are supplied, they will override any existing "pio_latlng" value.
+ # - All time arguments (e.g. t, pio_startT, pio_endT, etc) can be supplied as either a Time or Float object.
# When supplied as a Float, the SDK will interpret it as a UNIX UTC timestamp in seconds.
# The SDK will automatically round to the nearest millisecond, e.g. 3.14159 => 3.142.
#
# == Installation
- # Download the PredictionIO Ruby Gem from http://prediction.io
- # gem install predictionio-0.1.0.gem
+ # The easiest way is to use RubyGems:
+ # gem install predictionio-0.5.0.gem
#
# == Synopsis
# The recommended usage of the SDK is to fire asynchronous requests as early as you can in your code,
@@ -79,26 +79,28 @@
# log_and_email_error(...)
# end
#
- # === Import a User Action (View) form Your App (with synchronous/blocking requests)
+ # === Import a User Action (Rate) from Your App (with synchronous/blocking requests)
# # PredictionIO call to record the view action
# begin
- # result = client.user_view_item(4, 15)
+ # client.identify("foouser")
+ # result = client.record_action_on_item("rate", "baritem", "pio_rate" => 4)
# rescue U2IActionNotCreatedError => e
# ...
# end
#
# === Retrieving Top N Recommendations for a User
# # PredictionIO call to get recommendations
- # response = client.aget_recommendation(4, 10)
+ # client.identify("foouser")
+ # response = client.aget_itemrec_top_n("barengine", 10)
#
# #
# # work you need to do for the page (rendering, db queries, etc)
# #
#
# begin
- # result = client.get_recommendations(response)
+ # result = client.get_itemrec_top_n(response)
# # display results, store results, or your other work...
- # rescue RecommendationsNotFoundError => e
+ # rescue ItemRecNotFoundError => e
# # graceful error handling
# end
@@ -113,6 +115,9 @@
# Only JSON is currently supported as API response format.
attr_accessor :apiformat
+ # The UID used for recording user-to-item actions and retrieving recommendations.
+ attr_accessor :apiuid
+
# Raised when a user is not created after a synchronous API call.
class UserNotCreatedError < StandardError; end
@@ -171,10 +176,10 @@
# See also #create_user.
def acreate_user(uid, params = {})
rparams = params
- rparams["appkey"] = @appkey
- rparams["uid"] = uid
- if params["latitude"] != nil && params["longitude"] != nil then
- rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
+ rparams["pio_appkey"] = @appkey
+ rparams["pio_uid"] = uid
+ if params["pio_latitude"] != nil && params["pio_longitude"] != nil then
+ rparams["pio_latlng"] = "#{params["pio_latitude"]},#{params["pio_longitude"]}"
end
@http.apost(PredictionIO::AsyncRequest.new(versioned_path("/users.#{@apiformat}"), rparams))
@@ -191,7 +196,6 @@
def create_user(*args)
uid_or_res = args[0]
if uid_or_res.is_a?(PredictionIO::AsyncResponse) then
- uid = uid_or_res.request.params["uid"]
response = uid_or_res.get
else
uid = uid_or_res
@@ -219,8 +223,8 @@
# See also #get_user.
def aget_user(uid)
@http.aget(PredictionIO::AsyncRequest.new(versioned_path("/users/#{uid}.#{@apiformat}"),
- "appkey" => @appkey,
- "uid" => uid))
+ "pio_appkey" => @appkey,
+ "pio_uid" => uid))
end
# :category: Synchronous Methods
@@ -243,12 +247,10 @@
end
if response.is_a?(Net::HTTPOK) then
res = JSON.parse(response.body)
- ct = Rational(res["ct"], 1000)
- res["ct"] = Time.at(ct)
- if res["latlng"] != nil then
- latlng = res["latlng"]
- res["latitude"] = latlng[0]
- res["longitude"] = latlng[1]
+ if res["pio_latlng"] != nil then
+ latlng = res["pio_latlng"]
+ res["pio_latitude"] = latlng[0]
+ res["pio_longitude"] = latlng[1]
end
res
else
@@ -269,8 +271,8 @@
# See also #delete_user.
def adelete_user(uid)
@http.adelete(PredictionIO::AsyncRequest.new(versioned_path("/users/#{uid}.#{@apiformat}"),
- "appkey" => @appkey,
- "uid" => uid))
+ "pio_appkey" => @appkey,
+ "pio_uid" => uid))
end
# :category: Synchronous Methods
@@ -305,21 +307,21 @@
# See also #create_item.
def acreate_item(iid, itypes, params = {})
rparams = params
- rparams["appkey"] = @appkey
- rparams["iid"] = iid
+ rparams["pio_appkey"] = @appkey
+ rparams["pio_iid"] = iid
begin
- rparams["itypes"] = itypes.join(",")
+ rparams["pio_itypes"] = itypes.join(",")
rescue Exception
- rparams["itypes"] = itypes
+ rparams["pio_itypes"] = itypes
end
- if params["latitude"] != nil && params["longitude"] != nil then
- rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
+ if params["pio_latitude"] != nil && params["pio_longitude"] != nil then
+ rparams["pio_latlng"] = "#{params["pio_latitude"]},#{params["pio_longitude"]}"
end
- if params["startT"] != nil then
- rparams["startT"] = ((params["startT"].to_r) * 1000).round(0).to_s
+ if params["pio_startT"] != nil then
+ rparams["pio_startT"] = ((params["pio_startT"].to_r) * 1000).round(0).to_s
end
- if params["endT"] != nil then
- rparams["endT"] = ((params["endT"].to_r) * 1000).round(0).to_s
+ if params["pio_endT"] != nil then
+ rparams["pio_endT"] = ((params["pio_endT"].to_r) * 1000).round(0).to_s
end
@http.apost(PredictionIO::AsyncRequest.new(versioned_path("/items.#{@apiformat}"), rparams))
@@ -362,8 +364,8 @@
# See also #get_item.
def aget_item(iid)
@http.aget(PredictionIO::AsyncRequest.new(versioned_path("/items/#{iid}.#{@apiformat}"),
- "appkey" => @appkey,
- "iid" => iid))
+ "pio_appkey" => @appkey,
+ "pio_iid" => iid))
end
# :category: Synchronous Methods
@@ -386,20 +388,18 @@
end
if response.is_a?(Net::HTTPOK) then
res = JSON.parse(response.body)
- ct = Rational(res["ct"], 1000)
- res["ct"] = Time.at(ct)
- if res["latlng"] != nil then
- latlng = res["latlng"]
- res["latitude"] = latlng[0]
- res["longitude"] = latlng[1]
+ if res["pio_latlng"] != nil then
+ latlng = res["pio_latlng"]
+ res["pio_latitude"] = latlng[0]
+ res["pio_longitude"] = latlng[1]
end
- if res["startT"] != nil then
- startT = Rational(res["startT"], 1000)
- res["startT"] = Time.at(startT)
+ if res["pio_startT"] != nil then
+ startT = Rational(res["pio_startT"], 1000)
+ res["pio_startT"] = Time.at(startT)
end
- if res["endT"] != nil then
- endT = Rational(res["endT"], 1000)
- res["endT"] = Time.at(endT)
+ if res["pio_endT"] != nil then
+ endT = Rational(res["pio_endT"], 1000)
+ res["pio_endT"] = Time.at(endT)
end
res
else
@@ -420,8 +420,8 @@
# See also #delete_item.
def adelete_item(iid)
@http.adelete(PredictionIO::AsyncRequest.new(versioned_path("/items/#{iid}.#{@apiformat}"),
- "appkey" => @appkey,
- "iid" => iid))
+ "pio_appkey" => @appkey,
+ "pio_iid" => iid))
end
# :category: Synchronous Methods
@@ -448,32 +448,37 @@
end
end
+ # Set the user ID for use in all subsequent user-to-item action recording and user recommendation retrieval.
+ def identify(uid)
+ @apiuid = uid
+ end
+
# :category: Asynchronous Methods
# Asynchronously request to get the top n recommendations for a user from an ItemRec engine and return a PredictionIO::AsyncResponse object immediately.
#
# Corresponding REST API method: GET /engines/itemrec/:engine/topn
#
# See also #get_itemrec_top_n.
- def aget_itemrec_top_n(engine, uid, n, params = {})
+ def aget_itemrec_top_n(engine, n, params = {})
rparams = Hash.new
- rparams["appkey"] = @appkey
- rparams["uid"] = uid
- rparams["n"] = n
- if params["itypes"] != nil &&
- params["itypes"].kind_of?(Array) &&
- params["itypes"].length > 0 then
- rparams["itypes"] = params["itypes"].join(",")
+ rparams["pio_appkey"] = @appkey
+ rparams["pio_uid"] = @apiuid
+ rparams["pio_n"] = n
+ if params["pio_itypes"] != nil &&
+ params["pio_itypes"].kind_of?(Array) &&
+ params["pio_itypes"].length > 0 then
+ rparams["pio_itypes"] = params["pio_itypes"].join(",")
else
- rparams["itypes"] = params["itypes"]
+ rparams["pio_itypes"] = params["pio_itypes"]
end
- if params["latitude"] != nil && params["longitude"] != nil then
- rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
+ if params["pio_latitude"] != nil && params["pio_longitude"] != nil then
+ rparams["pio_latlng"] = "#{params["pio_latitude"]},#{params["pio_longitude"]}"
end
- if params["within"] != nil then
- rparams["within"] = params["within"]
+ if params["pio_within"] != nil then
+ rparams["pio_within"] = params["pio_within"]
end
- if params["unit"] != nil then
- rparams["unit"] = params["unit"]
+ if params["pio_unit"] != nil then
+ rparams["pio_unit"] = params["pio_unit"]
end
@http.aget(PredictionIO::AsyncRequest.new(versioned_path("/engines/itemrec/#{engine}/topn.#{@apiformat}"), rparams))
end
@@ -484,8 +489,8 @@
# See #aget_itemrec_top_n for a description of special argument handling.
#
# call-seq:
- # get_recommendations(uid, n, params = {})
- # get_recommendations(async_response)
+ # aget_itemrec_top_n(engine, n, params = {})
+ # aget_itemrec_top_n(async_response)
def get_itemrec_top_n(*args)
uid_or_res = args[0]
if uid_or_res.is_a?(PredictionIO::AsyncResponse) then
@@ -495,7 +500,7 @@
end
if response.is_a?(Net::HTTPOK) then
res = JSON.parse(response.body)
- res["iids"]
+ res["pio_iids"]
else
begin
msg = response.body
@@ -507,166 +512,40 @@
end
# :category: Asynchronous Methods
- # Asynchronously request to record a user-rate-item action and return a PredictionIO::AsyncResponse object immediately.
+ # Asynchronously request to record an action on an item and return a PredictionIO::AsyncResponse object immediately.
#
- # Corresponding REST API method: POST /actions/u2i/rate
+ # Corresponding REST API method: POST /actions/u2i
#
- # See also #user_rate_item.
- def auser_rate_item(uid, iid, rate, params = {})
- params["rate"] = rate
- auser_action_item("rate", uid, iid, params)
- end
-
- # :category: Synchronous Methods
- # Synchronously request to record a user-rate-item action and block until a response is received.
- #
- # See #auser_rate_item.
- #
- # call-seq:
- # user_rate_item(uid, iid, rate, params = {})
- # user_rate_item(async_response)
- def user_rate_item(*args)
- if !args[0].is_a?(PredictionIO::AsyncResponse) then
- args.unshift("rate")
- params = args[4]
- if params == nil then
- params = Hash.new
- end
- params["rate"] = args[3]
- args[3] = params
- end
- user_action_item(*args)
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to record a user-like-item action and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: POST /actions/u2i/like
- #
- # See also #user_like_item.
- def auser_like_item(uid, iid, params = {})
- auser_action_item("like", uid, iid, params)
- end
-
- # :category: Synchronous Methods
- # Synchronously request to record a user-like-item action and block until a response is received.
- #
- # See also #auser_like_item.
- #
- # call-seq:
- # user_like_item(uid, iid, params = {})
- # user_like_item(async_response)
- def user_like_item(*args)
- if !args[0].is_a?(PredictionIO::AsyncResponse) then
- args.unshift("like")
- end
- user_action_item(*args)
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to record a user-dislike-item action and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: POST /actions/u2i/dislike
- #
- # See also #user_dislike_item.
- def auser_dislike_item(uid, iid, params = {})
- auser_action_item("dislike", uid, iid, params)
- end
-
- # :category: Synchronous Methods
- # Synchronously request to record a user-dislike-item action and block until a response is received.
- #
- # See also #auser_dislike_item.
- #
- # call-seq:
- # user_dislike_item(uid, iid, params = {})
- # user_dislike_item(async_response)
- def user_dislike_item(*args)
- if !args[0].is_a?(PredictionIO::AsyncResponse) then
- args.unshift("dislike")
- end
- user_action_item(*args)
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to record a user-view-item action and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: POST /actions/u2i/view
- #
- # See also #user_view_item.
- def auser_view_item(uid, iid, params = {})
- auser_action_item("view", uid, iid, params)
- end
-
- # :category: Synchronous Methods
- # Synchronously request to record a user-view-item action and block until a response is received.
- #
- # See also #auser_view_item.
- #
- # call-seq:
- # user_view_item(uid, iid, params = {})
- # user_view_item(async_response)
- def user_view_item(*args)
- if !args[0].is_a?(PredictionIO::AsyncResponse) then
- args.unshift("view")
- end
- user_action_item(*args)
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to record a user-conversion-item action and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: POST /actions/u2i/conversion
- #
- # See also #user_conversion_item.
- def auser_conversion_item(uid, iid, params = {})
- auser_action_item("conversion", uid, iid, params)
- end
-
- # :category: Synchronous Methods
- # Synchronously request to record a user-conversion-item action and block until a response is received.
- #
- # See also #auser_conversion_item.
- #
- # call-seq:
- # user_conversion_item(uid, iid, params = {})
- # user_conversion_item(async_response)
- def user_conversion_item(*args)
- if !args[0].is_a?(PredictionIO::AsyncResponse) then
- args.unshift("conversion")
- end
- user_action_item(*args)
- end
-
- # :nodoc: all
- private
-
- def versioned_path(path)
- # disabled for now
- # "/#{@apiversion}#{path}"
- path
- end
-
- def auser_action_item(action, uid, iid, params = {})
+ # See also #record_action_on_item.
+ def arecord_action_on_item(action, iid, params = {})
rparams = params
- rparams["appkey"] = @appkey
- rparams["uid"] = uid
- rparams["iid"] = iid
- if params["t"] != nil then
- rparams["t"] = ((params["t"].to_r) * 1000).round(0).to_s
+ rparams["pio_appkey"] = @appkey
+ rparams["pio_action"] = action
+ rparams["pio_uid"] = @apiuid
+ rparams["pio_iid"] = iid
+ if params["pio_t"] != nil then
+ rparams["pio_t"] = ((params["pio_t"].to_r) * 1000).round(0).to_s
end
- if params["latitude"] != nil && params["longitude"] != nil then
- rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
+ if params["pio_latitude"] != nil && params["pio_longitude"] != nil then
+ rparams["pio_latlng"] = "#{params["pio_latitude"]},#{params["pio_longitude"]}"
end
- @http.apost(PredictionIO::AsyncRequest.new(versioned_path("/actions/u2i/#{action}.#{@apiformat}"), rparams))
+ @http.apost(PredictionIO::AsyncRequest.new(versioned_path("/actions/u2i.#{@apiformat}"), rparams))
end
- def user_action_item(*args)
+ # :category: Synchronous Methods
+ # Synchronously request to record an action on an item and block until a response is received.
+ #
+ # See also #arecord_action_on_item.
+ #
+ # call-seq:
+ # record_action_on_item(action, uid, iid, params = {})
+ # record_action_on_item(async_response)
+ def record_action_on_item(*args)
action_or_res = args[0]
if action_or_res.is_a?(PredictionIO::AsyncResponse) then
response = action_or_res.get
else
- response = auser_action_item(*args).get
+ response = arecord_action_on_item(*args).get
end
unless response.is_a?(Net::HTTPCreated) then
begin
@@ -677,5 +556,14 @@
raise U2IActionNotCreatedError, msg
end
end
+
+ # :nodoc: all
+ private
+
+ def versioned_path(path)
+ # disabled for now
+ # "/#{@apiversion}#{path}"
+ path
+ end
end
end
diff --git a/predictionio.gemspec b/predictionio.gemspec
index 30a1634..46472f0 100644
--- a/predictionio.gemspec
+++ b/predictionio.gemspec
@@ -6,7 +6,7 @@
provides convenient access of the PredictionIO API to Ruby programmers so that
they can focus on their application logic.
EOF
- s.version = "0.3.1"
+ s.version = "0.5.0"
s.author = "The PredictionIO Team"
s.email = "support@prediction.io"
s.homepage = "http://prediction.io"
diff --git a/test/test.rb b/test/test.rb
index d10a683..a2dbd43 100644
--- a/test/test.rb
+++ b/test/test.rb
@@ -6,7 +6,7 @@
if ENV["APPKEY"] then
APPKEY = ENV["APPKEY"]
else
- APPKEY = "6MBUgJBRQdjPBtYIIqNSt3uIK6XaH3Wv0iFQvSzaImeIQHJEOUXtkj88EO39LtPn"
+ APPKEY = "k4f6rCV8YTM5x0PbRdIG4yrWiKLhOv16V0Q8COE2AcvnYmSlxbAcXR5pucI5HO21"
end
if ENV["APIURL"] then
APIURL = ENV["APIURL"]
@@ -28,21 +28,21 @@
def test_user
client = PredictionIO::Client.new(APPKEY, APITHREADS, APIURL)
client.create_user("ruby_foobar")
- assert_equal("ruby_foobar", client.get_user("ruby_foobar")["uid"], "uid: ruby_foobar")
+ assert_equal("ruby_foobar", client.get_user("ruby_foobar")["pio_uid"], "uid: ruby_foobar")
client.delete_user("ruby_foobar")
client.create_user("ruby_barbaz",
"gender" => "F",
"bday" => "1985-05-05",
- "latitude" => 21.109,
- "longitude" => -48.7479,
- "inactive" => true)
+ "pio_latitude" => 21.109,
+ "pio_longitude" => -48.7479,
+ "pio_inactive" => true)
ruby_barbaz = client.get_user("ruby_barbaz")
- assert_equal("ruby_barbaz", ruby_barbaz["uid"], "uid: ruby_barbaz")
+ assert_equal("ruby_barbaz", ruby_barbaz["pio_uid"], "uid: ruby_barbaz")
#assert_equal("F", ruby_barbaz["gender"], "gender: F")
- assert_equal(21.109, ruby_barbaz["latitude"], "lat: 21.109")
- assert_equal(-48.7479, ruby_barbaz["longitude"], "lng: -48.7479")
+ assert_equal(21.109, ruby_barbaz["pio_latitude"], "lat: 21.109")
+ assert_equal(-48.7479, ruby_barbaz["pio_longitude"], "lng: -48.7479")
#assert_equal("1985-05-05", ruby_barbaz["bday"], "bday: 1985-05-05")
- assert(ruby_barbaz["inactive"], "inactive: true")
+ assert(ruby_barbaz["pio_inactive"], "inactive: true")
client.delete_user("ruby_barbaz")
end
@@ -50,32 +50,38 @@
client = PredictionIO::Client.new(APPKEY, APITHREADS, APIURL)
client.create_item("ruby_barbaz",
["218", "55"],
- "latitude" => -58.24089,
- "longitude" => 48.17890,
- "startT" => Time.at(478308922000))
+ "pio_latitude" => -58.24089,
+ "pio_longitude" => 48.17890,
+ "pio_startT" => Time.at(478308922000))
assert_raises(PredictionIO::Client::ItemNotFoundError) { client.get_item("randomstuff") }
ruby_barbaz = client.get_item("ruby_barbaz")
- assert_equal("ruby_barbaz", ruby_barbaz["iid"], "iid: ruby_barbaz")
- assert(ruby_barbaz["itypes"].include?("218"), "itypes: 218")
- assert(ruby_barbaz["itypes"].include?("55"), "itypes: 55")
- assert_equal(-58.24089, ruby_barbaz["latitude"], "lat: -58.24089")
- assert_equal(48.1789, ruby_barbaz["longitude"], "lng: 48.1789")
- assert_equal(Time.at(478308922000), ruby_barbaz["startT"], "startT: 478308922000")
+ assert_equal("ruby_barbaz", ruby_barbaz["pio_iid"], "iid: ruby_barbaz")
+ assert(ruby_barbaz["pio_itypes"].include?("218"), "itypes: 218")
+ assert(ruby_barbaz["pio_itypes"].include?("55"), "itypes: 55")
+ assert_equal(-58.24089, ruby_barbaz["pio_latitude"], "lat: -58.24089")
+ assert_equal(48.1789, ruby_barbaz["pio_longitude"], "lng: 48.1789")
+ assert_equal(Time.at(478308922000), ruby_barbaz["pio_startT"], "startT: 478308922000")
client.delete_item("ruby_barbaz")
end
def test_u2i
client = PredictionIO::Client.new(APPKEY, APITHREADS, APIURL)
- client.user_rate_item("foo1", "bar2", 4)
- client.user_like_item("foo2", "bar4")
- client.user_dislike_item("foo4", "bar8")
- client.user_view_item("foo8", "bar16")
- client.user_conversion_item("foo16", "bar32")
+ client.identify("foo1")
+ client.record_action_on_item("rate", "bar2", "pio_rate" => 4)
+ client.identify("foo2")
+ client.record_action_on_item("like", "bar4")
+ client.identify("foo4")
+ client.record_action_on_item("dislike", "bar8")
+ client.identify("foo8")
+ client.record_action_on_item("view", "bar16")
+ client.identify("foo16")
+ client.record_action_on_item("conversion", "bar32")
end
def test_itemrec
client = PredictionIO::Client.new(APPKEY, APITHREADS, APIURL)
- iids = client.get_itemrec_top_n("test", "218", 5)
+ client.identify("218")
+ iids = client.get_itemrec_top_n("test", 5)
assert(iids.include?("itemrec"))
assert(iids.include?("218"))
assert(iids.include?("1"))