predictionio 0.3.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.
- data/lib/predictionio.rb +1 -0
- data/lib/predictionio/async_request.rb +22 -0
- data/lib/predictionio/async_response.rb +30 -0
- data/lib/predictionio/client.rb +681 -0
- data/lib/predictionio/connection.rb +112 -0
- metadata +56 -0
data/lib/predictionio.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "predictionio/client"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module PredictionIO
|
2
|
+
# This class contains the URI path and query parameters that is consumed by PredictionIO::Connection for asynchronous HTTP requests.
|
3
|
+
class AsyncRequest
|
4
|
+
|
5
|
+
# The path portion of the request URI.
|
6
|
+
attr_reader :path
|
7
|
+
|
8
|
+
# Query parameters, or form data.
|
9
|
+
attr_reader :params
|
10
|
+
|
11
|
+
# Populates the package with request URI path, and optionally query parameters or form data.
|
12
|
+
def initialize(path, params = {})
|
13
|
+
@params = params
|
14
|
+
@path = path
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns an URI path with query parameters encoded for HTTP GET requests.
|
18
|
+
def qpath
|
19
|
+
"#{@path}?#{URI::encode_www_form(@params)}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module PredictionIO
|
4
|
+
# This class encapsulates an asynchronous response that will block the caller until the response is available.
|
5
|
+
class AsyncResponse
|
6
|
+
|
7
|
+
# The PredictionIO::AsyncRequest instance that created the current PredictionIO::AsyncResponse instance.
|
8
|
+
attr_reader :request
|
9
|
+
|
10
|
+
# Create the response by saving the request, and optionally the Net::HTTPResponse object.
|
11
|
+
def initialize(request, response = nil)
|
12
|
+
@request = request
|
13
|
+
@response = Queue.new
|
14
|
+
if response != nil then
|
15
|
+
set(response)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Save a Net::HTTPResponse instance to the current instance.
|
20
|
+
# This will unblock any caller that called #get.
|
21
|
+
def set(response)
|
22
|
+
@response.push(response)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get the Net::HTTPResponse instance. This will block if the response is not yet available.
|
26
|
+
def get
|
27
|
+
@response.pop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,681 @@
|
|
1
|
+
# Ruby SDK for convenient access of PredictionIO Output API.
|
2
|
+
#
|
3
|
+
# Author:: TappingStone (help@tappingstone.com)
|
4
|
+
# Copyright:: Copyright (c) 2013 TappingStone
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
|
7
|
+
require 'date'
|
8
|
+
require 'json'
|
9
|
+
require 'net/http'
|
10
|
+
require 'predictionio/async_request'
|
11
|
+
require 'predictionio/async_response'
|
12
|
+
require 'predictionio/connection'
|
13
|
+
|
14
|
+
# The PredictionIO module contains classes that provide convenient access of the PredictionIO output API over HTTP/HTTPS.
|
15
|
+
#
|
16
|
+
# To create an app and perform predictions, please download the PredictionIO suite from http://prediction.io.
|
17
|
+
#
|
18
|
+
# Most functionality is provided by the PredictionIO::Client class.
|
19
|
+
module PredictionIO
|
20
|
+
|
21
|
+
# This class contains methods that access PredictionIO via REST requests.
|
22
|
+
#
|
23
|
+
# Many REST request methods support optional arguments.
|
24
|
+
# They can be supplied to these methods as Hash'es.
|
25
|
+
# For a complete reference, please visit http://prediction.io.
|
26
|
+
#
|
27
|
+
# == High-performance Asynchronous Backend
|
28
|
+
#
|
29
|
+
# All REST request methods come in both synchronous and asynchronous flavors.
|
30
|
+
# Both flavors accept the same set of arguments.
|
31
|
+
# In addition, all synchronous request methods can instead accept a PredictionIO::AsyncResponse object generated from asynchronous request methods as its first argument.
|
32
|
+
# In this case, the method will block until a response is received from it.
|
33
|
+
#
|
34
|
+
# Any network reconnection and request retry is automatically handled in the background.
|
35
|
+
# Exceptions will be thrown after a request times out to avoid infinite blocking.
|
36
|
+
#
|
37
|
+
# == Special Handling of Some Optional Arguments
|
38
|
+
# Some optional arguments have additional special handling:
|
39
|
+
# - 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.
|
40
|
+
# - For all requests that accept "latlng" as input, they will also accept "latitude" and "longitude".
|
41
|
+
# When these are supplied, they will override any existing "latlng" value.
|
42
|
+
# - All time arguments (e.g. t, startT, endT, etc) can be supplied as either a Time or Float object.
|
43
|
+
# When supplied as a Float, the SDK will interpret it as a UNIX UTC timestamp in seconds.
|
44
|
+
# The SDK will automatically round to the nearest millisecond, e.g. 3.14159 => 3.142.
|
45
|
+
#
|
46
|
+
# == Installation
|
47
|
+
# Download the PredictionIO Ruby Gem from http://prediction.io
|
48
|
+
# gem install predictionio-0.1.0.gem
|
49
|
+
#
|
50
|
+
# == Synopsis
|
51
|
+
# The recommended usage of the SDK is to fire asynchronous requests as early as you can in your code,
|
52
|
+
# and check results later when you need them.
|
53
|
+
#
|
54
|
+
# === Instantiate PredictionIO Client
|
55
|
+
# # Include the PredictionIO SDK
|
56
|
+
# require "PredictionIO"
|
57
|
+
#
|
58
|
+
# client = PredictionIO::Client.new(<appkey>)
|
59
|
+
#
|
60
|
+
# === Import a User Record from Your App (with asynchronous/non-blocking requests)
|
61
|
+
#
|
62
|
+
# #
|
63
|
+
# # (your user registration logic)
|
64
|
+
# #
|
65
|
+
#
|
66
|
+
# uid = get_user_from_your_db()
|
67
|
+
#
|
68
|
+
# # PredictionIO call to create user
|
69
|
+
# response = client.acreate_user(uid)
|
70
|
+
#
|
71
|
+
# #
|
72
|
+
# # (other work to do for the rest of the page)
|
73
|
+
# #
|
74
|
+
#
|
75
|
+
# begin
|
76
|
+
# # PredictionIO call to retrieve results from an asynchronous response
|
77
|
+
# result = client.create_user(response)
|
78
|
+
# rescue UserNotCreatedError => e
|
79
|
+
# log_and_email_error(...)
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# === Import a User Action (View) form Your App (with synchronous/blocking requests)
|
83
|
+
# # PredictionIO call to record the view action
|
84
|
+
# begin
|
85
|
+
# result = client.user_view_item(4, 15)
|
86
|
+
# rescue U2IActionNotCreatedError => e
|
87
|
+
# ...
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# === Retrieving Top N Recommendations for a User
|
91
|
+
# # PredictionIO call to get recommendations
|
92
|
+
# response = client.aget_recommendation(4, 10)
|
93
|
+
#
|
94
|
+
# #
|
95
|
+
# # work you need to do for the page (rendering, db queries, etc)
|
96
|
+
# #
|
97
|
+
#
|
98
|
+
# begin
|
99
|
+
# result = client.get_recommendations(response)
|
100
|
+
# # display results, store results, or your other work...
|
101
|
+
# rescue RecommendationsNotFoundError => e
|
102
|
+
# # graceful error handling
|
103
|
+
# end
|
104
|
+
|
105
|
+
class Client
|
106
|
+
|
107
|
+
# Appkey can be changed on-the-fly after creation of the client.
|
108
|
+
attr_accessor :appkey
|
109
|
+
|
110
|
+
# API version can be changed on-the-fly after creation of the client.
|
111
|
+
attr_accessor :apiversion
|
112
|
+
|
113
|
+
# Only JSON is currently supported as API response format.
|
114
|
+
attr_accessor :apiformat
|
115
|
+
|
116
|
+
# Raised when a user is not created after a synchronous API call.
|
117
|
+
class UserNotCreatedError < StandardError; end
|
118
|
+
|
119
|
+
# Raised when a user is not found after a synchronous API call.
|
120
|
+
class UserNotFoundError < StandardError; end
|
121
|
+
|
122
|
+
# Raised when a user is not deleted after a synchronous API call.
|
123
|
+
class UserNotDeletedError < StandardError; end
|
124
|
+
|
125
|
+
# Raised when an item is not created after a synchronous API call.
|
126
|
+
class ItemNotCreatedError < StandardError; end
|
127
|
+
|
128
|
+
# Raised when an item is not found after a synchronous API call.
|
129
|
+
class ItemNotFoundError < StandardError; end
|
130
|
+
|
131
|
+
# Raised when an item is not deleted after a synchronous API call.
|
132
|
+
class ItemNotDeletedError < StandardError; end
|
133
|
+
|
134
|
+
# Raised when ItemRec results cannot be found for a user after a synchronous API call.
|
135
|
+
class ItemRecNotFoundError < StandardError; end
|
136
|
+
|
137
|
+
# Raised when an user-to-item action is not created after a synchronous API call.
|
138
|
+
class U2IActionNotCreatedError < StandardError; end
|
139
|
+
|
140
|
+
# Create a new PredictionIO client with default:
|
141
|
+
# - API entry point at http://localhost:8000
|
142
|
+
# - API return data format of json
|
143
|
+
# - 10 concurrent HTTP(S) connections
|
144
|
+
def initialize(appkey, threads = 10, apiurl = "http://localhost:8000", apiversion = "")
|
145
|
+
@appkey = appkey
|
146
|
+
@apiversion = apiversion
|
147
|
+
@apiformat = "json"
|
148
|
+
@http = PredictionIO::Connection.new(URI(apiurl), threads)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns the number of pending requests within the current client.
|
152
|
+
def pending_requests
|
153
|
+
@http.packages.size
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns PredictionIO's status in string.
|
157
|
+
def get_status
|
158
|
+
status = @http.aget(PredictionIO::AsyncRequest.new("/")).get()
|
159
|
+
begin
|
160
|
+
status.body
|
161
|
+
rescue Exception
|
162
|
+
status
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# :category: Asynchronous Methods
|
167
|
+
# Asynchronously request to create a user and return a PredictionIO::AsyncResponse object immediately.
|
168
|
+
#
|
169
|
+
# Corresponding REST API method: POST /users
|
170
|
+
#
|
171
|
+
# See also #create_user.
|
172
|
+
def acreate_user(uid, params = {})
|
173
|
+
rparams = params
|
174
|
+
rparams["appkey"] = @appkey
|
175
|
+
rparams["uid"] = uid
|
176
|
+
if params["latitude"] != nil && params["longitude"] != nil then
|
177
|
+
rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
|
178
|
+
end
|
179
|
+
|
180
|
+
@http.apost(PredictionIO::AsyncRequest.new(versioned_path("/users.#{@apiformat}"), rparams))
|
181
|
+
end
|
182
|
+
|
183
|
+
# :category: Synchronous Methods
|
184
|
+
# Synchronously request to create a user and block until a response is received.
|
185
|
+
#
|
186
|
+
# See also #acreate_user.
|
187
|
+
#
|
188
|
+
# call-seq:
|
189
|
+
# create_user(uid, params = {})
|
190
|
+
# create_user(async_response)
|
191
|
+
def create_user(*args)
|
192
|
+
uid_or_res = args[0]
|
193
|
+
if uid_or_res.is_a?(PredictionIO::AsyncResponse) then
|
194
|
+
uid = uid_or_res.request.params["uid"]
|
195
|
+
response = uid_or_res.get
|
196
|
+
else
|
197
|
+
uid = uid_or_res
|
198
|
+
response = acreate_user(*args).get
|
199
|
+
end
|
200
|
+
unless response.is_a?(Net::HTTPCreated) then
|
201
|
+
begin
|
202
|
+
msg = response.body
|
203
|
+
rescue Exception
|
204
|
+
raise UserNotCreatedError, response
|
205
|
+
end
|
206
|
+
raise UserNotCreatedError, msg
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# :category: Asynchronous Methods
|
211
|
+
# Asynchronously request to get a user and return a PredictionIO::AsyncResponse object immediately.
|
212
|
+
#
|
213
|
+
# Creation time of the user will be returned as a Time object.
|
214
|
+
#
|
215
|
+
# If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
|
216
|
+
#
|
217
|
+
# Corresponding REST API method: GET /users/:uid
|
218
|
+
#
|
219
|
+
# See also #get_user.
|
220
|
+
def aget_user(uid)
|
221
|
+
@http.aget(PredictionIO::AsyncRequest.new(versioned_path("/users/#{uid}.#{@apiformat}"),
|
222
|
+
"appkey" => @appkey,
|
223
|
+
"uid" => uid))
|
224
|
+
end
|
225
|
+
|
226
|
+
# :category: Synchronous Methods
|
227
|
+
# Synchronously request to get a user and block until a response is received.
|
228
|
+
#
|
229
|
+
# Creation time of the user will be returned as a Time object.
|
230
|
+
#
|
231
|
+
# If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
|
232
|
+
#
|
233
|
+
# See also #aget_user.
|
234
|
+
#
|
235
|
+
# call-seq:
|
236
|
+
# get_user(uid)
|
237
|
+
# get_user(async_response)
|
238
|
+
def get_user(uid_or_res)
|
239
|
+
if uid_or_res.is_a?(PredictionIO::AsyncResponse) then
|
240
|
+
response = uid_or_res.get
|
241
|
+
else
|
242
|
+
response = aget_user(uid_or_res).get
|
243
|
+
end
|
244
|
+
if response.is_a?(Net::HTTPOK) then
|
245
|
+
res = JSON.parse(response.body)
|
246
|
+
ct = Rational(res["ct"], 1000)
|
247
|
+
res["ct"] = Time.at(ct)
|
248
|
+
if res["latlng"] != nil then
|
249
|
+
latlng = res["latlng"]
|
250
|
+
res["latitude"] = latlng[0]
|
251
|
+
res["longitude"] = latlng[1]
|
252
|
+
end
|
253
|
+
res
|
254
|
+
else
|
255
|
+
begin
|
256
|
+
msg = response.body
|
257
|
+
rescue Exception
|
258
|
+
raise UserNotFoundError, response
|
259
|
+
end
|
260
|
+
raise UserNotFoundError, msg
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# :category: Asynchronous Methods
|
265
|
+
# Asynchronously request to delete a user and return a PredictionIO::AsyncResponse object immediately.
|
266
|
+
#
|
267
|
+
# Corresponding REST API method: DELETE /users/:uid
|
268
|
+
#
|
269
|
+
# See also #delete_user.
|
270
|
+
def adelete_user(uid)
|
271
|
+
@http.adelete(PredictionIO::AsyncRequest.new(versioned_path("/users/#{uid}.#{@apiformat}"),
|
272
|
+
"appkey" => @appkey,
|
273
|
+
"uid" => uid))
|
274
|
+
end
|
275
|
+
|
276
|
+
# :category: Synchronous Methods
|
277
|
+
# Synchronously request to delete a user and block until a response is received.
|
278
|
+
#
|
279
|
+
# See also #adelete_user.
|
280
|
+
#
|
281
|
+
# call-seq:
|
282
|
+
# delete_user(uid)
|
283
|
+
# delete_user(async_response)
|
284
|
+
def delete_user(uid_or_res)
|
285
|
+
if uid_or_res.is_a?(PredictionIO::AsyncResponse) then
|
286
|
+
response = uid_or_res.get
|
287
|
+
else
|
288
|
+
response = adelete_user(uid_or_res).get
|
289
|
+
end
|
290
|
+
unless response.is_a?(Net::HTTPOK) then
|
291
|
+
begin
|
292
|
+
msg = response.body
|
293
|
+
rescue Exception
|
294
|
+
raise UserNotDeletedError, response
|
295
|
+
end
|
296
|
+
raise msg
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# :category: Asynchronous Methods
|
301
|
+
# Asynchronously request to create an item and return a PredictionIO::AsyncResponse object immediately.
|
302
|
+
#
|
303
|
+
# Corresponding REST API method: POST /items
|
304
|
+
#
|
305
|
+
# See also #create_item.
|
306
|
+
def acreate_item(iid, itypes, params = {})
|
307
|
+
rparams = params
|
308
|
+
rparams["appkey"] = @appkey
|
309
|
+
rparams["iid"] = iid
|
310
|
+
begin
|
311
|
+
rparams["itypes"] = itypes.join(",")
|
312
|
+
rescue Exception
|
313
|
+
rparams["itypes"] = itypes
|
314
|
+
end
|
315
|
+
if params["latitude"] != nil && params["longitude"] != nil then
|
316
|
+
rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
|
317
|
+
end
|
318
|
+
if params["startT"] != nil then
|
319
|
+
rparams["startT"] = ((params["startT"].to_r) * 1000).round(0).to_s
|
320
|
+
end
|
321
|
+
if params["endT"] != nil then
|
322
|
+
rparams["endT"] = ((params["endT"].to_r) * 1000).round(0).to_s
|
323
|
+
end
|
324
|
+
|
325
|
+
@http.apost(PredictionIO::AsyncRequest.new(versioned_path("/items.#{@apiformat}"), rparams))
|
326
|
+
end
|
327
|
+
|
328
|
+
# :category: Synchronous Methods
|
329
|
+
# Synchronously request to create an item and block until a response is received.
|
330
|
+
#
|
331
|
+
# See #acreate_item for a description of other accepted arguments.
|
332
|
+
#
|
333
|
+
# call-seq:
|
334
|
+
# create_item(iid, itypes, params = {})
|
335
|
+
# create_item(async_response)
|
336
|
+
def create_item(*args)
|
337
|
+
iid_or_res = args[0]
|
338
|
+
if iid_or_res.is_a?(PredictionIO::AsyncResponse) then
|
339
|
+
response = iid_or_res.get
|
340
|
+
else
|
341
|
+
response = acreate_item(*args).get
|
342
|
+
end
|
343
|
+
unless response.is_a?(Net::HTTPCreated) then
|
344
|
+
begin
|
345
|
+
msg = response.body
|
346
|
+
rescue Exception
|
347
|
+
raise ItemNotCreatedError, response
|
348
|
+
end
|
349
|
+
raise ItemNotCreatedError, msg
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
# :category: Asynchronous Methods
|
354
|
+
# Asynchronously request to get an item and return a PredictionIO::AsyncResponse object immediately.
|
355
|
+
#
|
356
|
+
# Creation time of the user will be returned as a Time object.
|
357
|
+
#
|
358
|
+
# If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
|
359
|
+
#
|
360
|
+
# Corresponding REST API method: GET /items/:iid
|
361
|
+
#
|
362
|
+
# See also #get_item.
|
363
|
+
def aget_item(iid)
|
364
|
+
@http.aget(PredictionIO::AsyncRequest.new(versioned_path("/items/#{iid}.#{@apiformat}"),
|
365
|
+
"appkey" => @appkey,
|
366
|
+
"iid" => iid))
|
367
|
+
end
|
368
|
+
|
369
|
+
# :category: Synchronous Methods
|
370
|
+
# Synchronously request to get an item and block until a response is received.
|
371
|
+
#
|
372
|
+
# Creation time of the item will be returned as a Time object.
|
373
|
+
#
|
374
|
+
# If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
|
375
|
+
#
|
376
|
+
# See also #aget_item.
|
377
|
+
#
|
378
|
+
# call-seq:
|
379
|
+
# get_item(iid)
|
380
|
+
# get_item(async_response)
|
381
|
+
def get_item(iid_or_res)
|
382
|
+
if iid_or_res.is_a?(PredictionIO::AsyncResponse) then
|
383
|
+
response = iid_or_res.get
|
384
|
+
else
|
385
|
+
response = aget_item(iid_or_res).get
|
386
|
+
end
|
387
|
+
if response.is_a?(Net::HTTPOK) then
|
388
|
+
res = JSON.parse(response.body)
|
389
|
+
ct = Rational(res["ct"], 1000)
|
390
|
+
res["ct"] = Time.at(ct)
|
391
|
+
if res["latlng"] != nil then
|
392
|
+
latlng = res["latlng"]
|
393
|
+
res["latitude"] = latlng[0]
|
394
|
+
res["longitude"] = latlng[1]
|
395
|
+
end
|
396
|
+
if res["startT"] != nil then
|
397
|
+
startT = Rational(res["startT"], 1000)
|
398
|
+
res["startT"] = Time.at(startT)
|
399
|
+
end
|
400
|
+
if res["endT"] != nil then
|
401
|
+
endT = Rational(res["endT"], 1000)
|
402
|
+
res["endT"] = Time.at(endT)
|
403
|
+
end
|
404
|
+
res
|
405
|
+
else
|
406
|
+
begin
|
407
|
+
msg = response.body
|
408
|
+
rescue Exception
|
409
|
+
raise ItemNotFoundError, response
|
410
|
+
end
|
411
|
+
raise ItemNotFoundError, msg
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
# :category: Asynchronous Methods
|
416
|
+
# Asynchronously request to delete an item and return a PredictionIO::AsyncResponse object immediately.
|
417
|
+
#
|
418
|
+
# Corresponding REST API method: DELETE /items/:iid
|
419
|
+
#
|
420
|
+
# See also #delete_item.
|
421
|
+
def adelete_item(iid)
|
422
|
+
@http.adelete(PredictionIO::AsyncRequest.new(versioned_path("/items/#{iid}.#{@apiformat}"),
|
423
|
+
"appkey" => @appkey,
|
424
|
+
"iid" => iid))
|
425
|
+
end
|
426
|
+
|
427
|
+
# :category: Synchronous Methods
|
428
|
+
# Synchronously request to delete an item and block until a response is received.
|
429
|
+
#
|
430
|
+
# See also #adelete_item.
|
431
|
+
#
|
432
|
+
# call-seq:
|
433
|
+
# delete_item(iid)
|
434
|
+
# delete_item(async_response)
|
435
|
+
def delete_item(iid_or_res)
|
436
|
+
if iid_or_res.is_a?(PredictionIO::AsyncResponse) then
|
437
|
+
response = iid_or_res.get
|
438
|
+
else
|
439
|
+
response = adelete_item(iid_or_res).get
|
440
|
+
end
|
441
|
+
unless response.is_a?(Net::HTTPOK) then
|
442
|
+
begin
|
443
|
+
msg = response.body
|
444
|
+
rescue Exception
|
445
|
+
raise ItemNotDeletedError, response
|
446
|
+
end
|
447
|
+
raise ItemNotDeletedError, msg
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
# :category: Asynchronous Methods
|
452
|
+
# Asynchronously request to get the top n recommendations for a user from an ItemRec engine and return a PredictionIO::AsyncResponse object immediately.
|
453
|
+
#
|
454
|
+
# Corresponding REST API method: GET /engines/itemrec/:engine/topn
|
455
|
+
#
|
456
|
+
# See also #get_itemrec_top_n.
|
457
|
+
def aget_itemrec_top_n(engine, uid, n, params = {})
|
458
|
+
rparams = Hash.new
|
459
|
+
rparams["appkey"] = @appkey
|
460
|
+
rparams["uid"] = uid
|
461
|
+
rparams["n"] = n
|
462
|
+
if params["itypes"] != nil &&
|
463
|
+
params["itypes"].kind_of?(Array) &&
|
464
|
+
params["itypes"].length > 0 then
|
465
|
+
rparams["itypes"] = params["itypes"].join(",")
|
466
|
+
else
|
467
|
+
rparams["itypes"] = params["itypes"]
|
468
|
+
end
|
469
|
+
if params["latitude"] != nil && params["longitude"] != nil then
|
470
|
+
rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
|
471
|
+
end
|
472
|
+
if params["within"] != nil then
|
473
|
+
rparams["within"] = params["within"]
|
474
|
+
end
|
475
|
+
if params["unit"] != nil then
|
476
|
+
rparams["unit"] = params["unit"]
|
477
|
+
end
|
478
|
+
@http.aget(PredictionIO::AsyncRequest.new(versioned_path("/engines/itemrec/#{engine}/topn.#{@apiformat}"), rparams))
|
479
|
+
end
|
480
|
+
|
481
|
+
# :category: Synchronous Methods
|
482
|
+
# Synchronously request to get the top n recommendations for a user from an ItemRec engine and block until a response is received.
|
483
|
+
#
|
484
|
+
# See #aget_itemrec_top_n for a description of special argument handling.
|
485
|
+
#
|
486
|
+
# call-seq:
|
487
|
+
# get_recommendations(uid, n, params = {})
|
488
|
+
# get_recommendations(async_response)
|
489
|
+
def get_itemrec_top_n(*args)
|
490
|
+
uid_or_res = args[0]
|
491
|
+
if uid_or_res.is_a?(PredictionIO::AsyncResponse) then
|
492
|
+
response = uid_or_res.get
|
493
|
+
else
|
494
|
+
response = aget_itemrec_top_n(*args).get
|
495
|
+
end
|
496
|
+
if response.is_a?(Net::HTTPOK) then
|
497
|
+
res = JSON.parse(response.body)
|
498
|
+
res["iids"]
|
499
|
+
else
|
500
|
+
begin
|
501
|
+
msg = response.body
|
502
|
+
rescue Exception
|
503
|
+
raise ItemRecNotFoundError, response
|
504
|
+
end
|
505
|
+
raise ItemRecNotFoundError, msg
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
# :category: Asynchronous Methods
|
510
|
+
# Asynchronously request to record a user-rate-item action and return a PredictionIO::AsyncResponse object immediately.
|
511
|
+
#
|
512
|
+
# Corresponding REST API method: POST /actions/u2i/rate
|
513
|
+
#
|
514
|
+
# See also #user_rate_item.
|
515
|
+
def auser_rate_item(uid, iid, rate, params = {})
|
516
|
+
params["rate"] = rate
|
517
|
+
auser_action_item("rate", uid, iid, params)
|
518
|
+
end
|
519
|
+
|
520
|
+
# :category: Synchronous Methods
|
521
|
+
# Synchronously request to record a user-rate-item action and block until a response is received.
|
522
|
+
#
|
523
|
+
# See #auser_rate_item.
|
524
|
+
#
|
525
|
+
# call-seq:
|
526
|
+
# user_rate_item(uid, iid, rate, params = {})
|
527
|
+
# user_rate_item(async_response)
|
528
|
+
def user_rate_item(*args)
|
529
|
+
if !args[0].is_a?(PredictionIO::AsyncResponse) then
|
530
|
+
args.unshift("rate")
|
531
|
+
params = args[4]
|
532
|
+
if params == nil then
|
533
|
+
params = Hash.new
|
534
|
+
end
|
535
|
+
params["rate"] = args[3]
|
536
|
+
args[3] = params
|
537
|
+
end
|
538
|
+
user_action_item(*args)
|
539
|
+
end
|
540
|
+
|
541
|
+
# :category: Asynchronous Methods
|
542
|
+
# Asynchronously request to record a user-like-item action and return a PredictionIO::AsyncResponse object immediately.
|
543
|
+
#
|
544
|
+
# Corresponding REST API method: POST /actions/u2i/like
|
545
|
+
#
|
546
|
+
# See also #user_like_item.
|
547
|
+
def auser_like_item(uid, iid, params = {})
|
548
|
+
auser_action_item("like", uid, iid, params)
|
549
|
+
end
|
550
|
+
|
551
|
+
# :category: Synchronous Methods
|
552
|
+
# Synchronously request to record a user-like-item action and block until a response is received.
|
553
|
+
#
|
554
|
+
# See also #auser_like_item.
|
555
|
+
#
|
556
|
+
# call-seq:
|
557
|
+
# user_like_item(uid, iid, params = {})
|
558
|
+
# user_like_item(async_response)
|
559
|
+
def user_like_item(*args)
|
560
|
+
if !args[0].is_a?(PredictionIO::AsyncResponse) then
|
561
|
+
args.unshift("like")
|
562
|
+
end
|
563
|
+
user_action_item(*args)
|
564
|
+
end
|
565
|
+
|
566
|
+
# :category: Asynchronous Methods
|
567
|
+
# Asynchronously request to record a user-dislike-item action and return a PredictionIO::AsyncResponse object immediately.
|
568
|
+
#
|
569
|
+
# Corresponding REST API method: POST /actions/u2i/dislike
|
570
|
+
#
|
571
|
+
# See also #user_dislike_item.
|
572
|
+
def auser_dislike_item(uid, iid, params = {})
|
573
|
+
auser_action_item("dislike", uid, iid, params)
|
574
|
+
end
|
575
|
+
|
576
|
+
# :category: Synchronous Methods
|
577
|
+
# Synchronously request to record a user-dislike-item action and block until a response is received.
|
578
|
+
#
|
579
|
+
# See also #auser_dislike_item.
|
580
|
+
#
|
581
|
+
# call-seq:
|
582
|
+
# user_dislike_item(uid, iid, params = {})
|
583
|
+
# user_dislike_item(async_response)
|
584
|
+
def user_dislike_item(*args)
|
585
|
+
if !args[0].is_a?(PredictionIO::AsyncResponse) then
|
586
|
+
args.unshift("dislike")
|
587
|
+
end
|
588
|
+
user_action_item(*args)
|
589
|
+
end
|
590
|
+
|
591
|
+
# :category: Asynchronous Methods
|
592
|
+
# Asynchronously request to record a user-view-item action and return a PredictionIO::AsyncResponse object immediately.
|
593
|
+
#
|
594
|
+
# Corresponding REST API method: POST /actions/u2i/view
|
595
|
+
#
|
596
|
+
# See also #user_view_item.
|
597
|
+
def auser_view_item(uid, iid, params = {})
|
598
|
+
auser_action_item("view", uid, iid, params)
|
599
|
+
end
|
600
|
+
|
601
|
+
# :category: Synchronous Methods
|
602
|
+
# Synchronously request to record a user-view-item action and block until a response is received.
|
603
|
+
#
|
604
|
+
# See also #auser_view_item.
|
605
|
+
#
|
606
|
+
# call-seq:
|
607
|
+
# user_view_item(uid, iid, params = {})
|
608
|
+
# user_view_item(async_response)
|
609
|
+
def user_view_item(*args)
|
610
|
+
if !args[0].is_a?(PredictionIO::AsyncResponse) then
|
611
|
+
args.unshift("view")
|
612
|
+
end
|
613
|
+
user_action_item(*args)
|
614
|
+
end
|
615
|
+
|
616
|
+
# :category: Asynchronous Methods
|
617
|
+
# Asynchronously request to record a user-conversion-item action and return a PredictionIO::AsyncResponse object immediately.
|
618
|
+
#
|
619
|
+
# Corresponding REST API method: POST /actions/u2i/conversion
|
620
|
+
#
|
621
|
+
# See also #user_conversion_item.
|
622
|
+
def auser_conversion_item(uid, iid, params = {})
|
623
|
+
auser_action_item("conversion", uid, iid, params)
|
624
|
+
end
|
625
|
+
|
626
|
+
# :category: Synchronous Methods
|
627
|
+
# Synchronously request to record a user-conversion-item action and block until a response is received.
|
628
|
+
#
|
629
|
+
# See also #auser_conversion_item.
|
630
|
+
#
|
631
|
+
# call-seq:
|
632
|
+
# user_conversion_item(uid, iid, params = {})
|
633
|
+
# user_conversion_item(async_response)
|
634
|
+
def user_conversion_item(*args)
|
635
|
+
if !args[0].is_a?(PredictionIO::AsyncResponse) then
|
636
|
+
args.unshift("conversion")
|
637
|
+
end
|
638
|
+
user_action_item(*args)
|
639
|
+
end
|
640
|
+
|
641
|
+
# :nodoc: all
|
642
|
+
private
|
643
|
+
|
644
|
+
def versioned_path(path)
|
645
|
+
# disabled for now
|
646
|
+
# "/#{@apiversion}#{path}"
|
647
|
+
path
|
648
|
+
end
|
649
|
+
|
650
|
+
def auser_action_item(action, uid, iid, params = {})
|
651
|
+
rparams = params
|
652
|
+
rparams["appkey"] = @appkey
|
653
|
+
rparams["uid"] = uid
|
654
|
+
rparams["iid"] = iid
|
655
|
+
if params["t"] != nil then
|
656
|
+
rparams["t"] = ((params["t"].to_r) * 1000).round(0).to_s
|
657
|
+
end
|
658
|
+
if params["latitude"] != nil && params["longitude"] != nil then
|
659
|
+
rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
|
660
|
+
end
|
661
|
+
@http.apost(PredictionIO::AsyncRequest.new(versioned_path("/actions/u2i/#{action}.#{@apiformat}"), rparams))
|
662
|
+
end
|
663
|
+
|
664
|
+
def user_action_item(*args)
|
665
|
+
action_or_res = args[0]
|
666
|
+
if action_or_res.is_a?(PredictionIO::AsyncResponse) then
|
667
|
+
response = action_or_res.get
|
668
|
+
else
|
669
|
+
response = auser_action_item(*args).get
|
670
|
+
end
|
671
|
+
unless response.is_a?(Net::HTTPCreated) then
|
672
|
+
begin
|
673
|
+
msg = response.body
|
674
|
+
rescue Exception
|
675
|
+
raise U2IActionNotCreatedError, response
|
676
|
+
end
|
677
|
+
raise U2IActionNotCreatedError, msg
|
678
|
+
end
|
679
|
+
end
|
680
|
+
end
|
681
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module PredictionIO
|
2
|
+
|
3
|
+
# This class handles multithreading and asynchronous requests transparently for the REST client.
|
4
|
+
class Connection
|
5
|
+
|
6
|
+
# Number of pending asynchronous request and response packages.
|
7
|
+
attr_reader :packages
|
8
|
+
|
9
|
+
# Number of connections active
|
10
|
+
attr_reader :connections
|
11
|
+
|
12
|
+
# Timeout in seconds
|
13
|
+
attr_reader :timeout
|
14
|
+
|
15
|
+
# Spawns a number of threads with persistent HTTP connection to the specified URI.
|
16
|
+
# Sets a default timeout of 5 seconds.
|
17
|
+
def initialize(uri, threads = 1, timeout = 5)
|
18
|
+
@packages = Queue.new
|
19
|
+
@counter_lock = Mutex.new
|
20
|
+
@connections = 0
|
21
|
+
@timeout = timeout
|
22
|
+
threads.times do
|
23
|
+
Thread.new do
|
24
|
+
begin
|
25
|
+
Net::HTTP.start(uri.host, uri.port,
|
26
|
+
:use_ssl => uri.scheme == 'https') do |http|
|
27
|
+
@counter_lock.synchronize do
|
28
|
+
@connections += 1
|
29
|
+
end
|
30
|
+
catch(:exit) do
|
31
|
+
http.read_timeout = @timeout
|
32
|
+
loop do
|
33
|
+
package = @packages.pop
|
34
|
+
request = package[:request]
|
35
|
+
response = package[:response]
|
36
|
+
case package[:method]
|
37
|
+
when "get"
|
38
|
+
http_req = Net::HTTP::Get.new("#{uri.path}#{request.qpath}")
|
39
|
+
begin
|
40
|
+
response.set(http.request(http_req))
|
41
|
+
rescue Exception => details
|
42
|
+
response.set(details)
|
43
|
+
end
|
44
|
+
when "post"
|
45
|
+
http_req = Net::HTTP::Post.new("#{uri.path}#{request.path}")
|
46
|
+
http_req.set_form_data(request.params)
|
47
|
+
begin
|
48
|
+
response.set(http.request(http_req))
|
49
|
+
rescue Exception => details
|
50
|
+
response.set(details)
|
51
|
+
end
|
52
|
+
when "delete"
|
53
|
+
http_req = Net::HTTP::Delete.new("#{uri.path}#{request.qpath}")
|
54
|
+
begin
|
55
|
+
response.set(http.request(http_req))
|
56
|
+
rescue Exception => details
|
57
|
+
response.set(details)
|
58
|
+
end
|
59
|
+
when "exit"
|
60
|
+
@counter_lock.synchronize do
|
61
|
+
@connections -= 1
|
62
|
+
end
|
63
|
+
throw :exit
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
rescue Exception => detail
|
69
|
+
@counter_lock.synchronize do
|
70
|
+
if @connections == 0 then
|
71
|
+
# Use non-blocking pop to avoid dead-locking the current
|
72
|
+
# thread when there is no request, and give it a chance to re-connect.
|
73
|
+
begin
|
74
|
+
package = @packages.pop(true)
|
75
|
+
response = package[:response]
|
76
|
+
response.set(detail)
|
77
|
+
rescue Exception
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
sleep(1)
|
82
|
+
retry
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Create an asynchronous request and response package, put it in the pending queue, and return the response object.
|
89
|
+
def request(method, request)
|
90
|
+
response = AsyncResponse.new(request)
|
91
|
+
@packages.push(:method => method,
|
92
|
+
:request => request,
|
93
|
+
:response => response)
|
94
|
+
response
|
95
|
+
end
|
96
|
+
|
97
|
+
# Shortcut to create an asynchronous GET request with the response object returned.
|
98
|
+
def aget(areq)
|
99
|
+
request("get", areq)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Shortcut to create an asynchronous POST request with the response object returned.
|
103
|
+
def apost(areq)
|
104
|
+
request("post", areq)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Shortcut to create an asynchronous DELETE request with the response object returned.
|
108
|
+
def adelete(areq)
|
109
|
+
request("delete", areq)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: predictionio
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- The PredictionIO Team
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-25 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! 'PredictionIO is a prediction server for building smart applications.
|
15
|
+
This gem
|
16
|
+
|
17
|
+
provides convenient access of the PredictionIO API to Ruby programmers so that
|
18
|
+
|
19
|
+
they can focus on their application logic.
|
20
|
+
|
21
|
+
'
|
22
|
+
email: support@prediction.io
|
23
|
+
executables: []
|
24
|
+
extensions: []
|
25
|
+
extra_rdoc_files: []
|
26
|
+
files:
|
27
|
+
- lib/predictionio/client.rb
|
28
|
+
- lib/predictionio/async_request.rb
|
29
|
+
- lib/predictionio/connection.rb
|
30
|
+
- lib/predictionio/async_response.rb
|
31
|
+
- lib/predictionio.rb
|
32
|
+
homepage: http://prediction.io
|
33
|
+
licenses: []
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.9'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
requirements: []
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 1.8.23
|
53
|
+
signing_key:
|
54
|
+
specification_version: 3
|
55
|
+
summary: PredictionIO Ruby SDK
|
56
|
+
test_files: []
|