lumidatum_client 0.1.4
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.
- checksums.yaml +7 -0
- data/bin/lumidatum_client +0 -0
- data/lib/lumidatum_client.rb +195 -0
- data/tests/test_client_unit_tests.rb +181 -0
- metadata +63 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 7f9eb2eee7f3432b6c5628a87aead931b889382d
|
|
4
|
+
data.tar.gz: 699fe1ea088c18ac0db067d3e27d9fdd2cbd48d9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ed2ffeb4a32e92a1498fb6caf1b670a0100a6a7b9c0dd5cff1dc770b75cc428441434468045086f56802abdc7087bca174a211fc9e9af47c4e05c00a8f90eb63
|
|
7
|
+
data.tar.gz: f9523e0ec6bbbf958dc009cb2555f4b900a7e86506456287a5029c45215e5347edce721d8bd37d2ca122b5063c95d6a763a20eb9042ac5a05ceca2e15208585b
|
|
File without changes
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
require "httpclient"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LumidatumClient
|
|
7
|
+
attr_accessor :authentication_token
|
|
8
|
+
attr_accessor :model_id
|
|
9
|
+
attr_accessor :host_address
|
|
10
|
+
attr_accessor :http_client
|
|
11
|
+
attr_accessor :file_handler
|
|
12
|
+
|
|
13
|
+
def initialize(authentication_token, model_id=nil, host_address="https://www.lumidatum.com", http_client=nil, file_handler=File)
|
|
14
|
+
if authentication_token == nil
|
|
15
|
+
raise ArgumentError, "authentication_token must not be nil"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@authentication_token = authentication_token
|
|
19
|
+
@model_id = model_id
|
|
20
|
+
@host_address = host_address.chomp("/")
|
|
21
|
+
|
|
22
|
+
if http_client == nil
|
|
23
|
+
@http_client = HTTPClient.new
|
|
24
|
+
else
|
|
25
|
+
@http_client = http_client;
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@file_handler = file_handler
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def getItemRecommendations(parameters, model_id: nil, deserialize_response: true)
|
|
33
|
+
model_id = _getModelIdOrError(model_id)
|
|
34
|
+
|
|
35
|
+
return api("POST", "api/predict/#{model_id}", nil, parameters, deserialize_response: deserialize_response)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def getUserRecommendations(parameters, model_id: nil, deserialize_response: true)
|
|
39
|
+
model_id = _getModelIdOrError(model_id)
|
|
40
|
+
|
|
41
|
+
return api("POST", "api/predict/#{model_id}", nil, parameters, deserialize_response: deserialize_response)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# Data upload
|
|
46
|
+
def sendUserData(data_string: nil, file_path: nil, model_id: nil)
|
|
47
|
+
model_id = _getModelIdOrError(model_id)
|
|
48
|
+
|
|
49
|
+
return sendData("users", data_string, file_path, model_id)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def sendItemData(data_string: nil, file_path: nil, model_id: nil)
|
|
53
|
+
model_id = _getModelIdOrError(model_id)
|
|
54
|
+
|
|
55
|
+
return sendData("items", data_string, file_path, model_id)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def sendTransactionData(data_string: nil, file_path: nil, model_id: nil)
|
|
59
|
+
model_id = _getModelIdOrError(model_id)
|
|
60
|
+
|
|
61
|
+
return sendData("transactions", data_string, file_path, model_id)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def sendData(data_type, data_string, file_path, model_id)
|
|
65
|
+
model_id = _getModelIdOrError(model_id)
|
|
66
|
+
|
|
67
|
+
url_endpoint = "api/data"
|
|
68
|
+
|
|
69
|
+
if data_string != nil
|
|
70
|
+
url_query_parameters = {:model_id => model_id, :data_type => data_type}
|
|
71
|
+
|
|
72
|
+
return api("POST", url_endpoint, url_query_parameters, parameters)
|
|
73
|
+
else
|
|
74
|
+
file_name = File.basename(file_path)
|
|
75
|
+
file_size = nil
|
|
76
|
+
presigned_response_object = getPresignedResponse(
|
|
77
|
+
nil,
|
|
78
|
+
model_id,
|
|
79
|
+
data_type: data_type,
|
|
80
|
+
file_name: file_name,
|
|
81
|
+
file_size: file_size
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
@file_handler.open(file_path) do |upload_file|
|
|
86
|
+
form_fields = presigned_response_object["fields"]
|
|
87
|
+
form_fields["file"] = upload_file
|
|
88
|
+
|
|
89
|
+
return @http_client.post(presigned_response_object["url"], form_fields)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# File download
|
|
94
|
+
def getLatestLTVReport(download_file_path, model_id: nil, zipped: true, stream_download: true)
|
|
95
|
+
model_id = _getModelIdOrError(model_id)
|
|
96
|
+
|
|
97
|
+
latest_key_name = getAvailableReports("LTV", model_id, zipped: zipped)
|
|
98
|
+
|
|
99
|
+
presigned_response_object = getPresignedResponse(latest_key_name, model_id, is_download: true)
|
|
100
|
+
|
|
101
|
+
report_response = @http_client.get(presigned_response_object["url"])
|
|
102
|
+
open(download_file_path, "wb") do |file|
|
|
103
|
+
file.write(report_response.body)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
return report_response
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def getLatestSegmentationReport(download_file_path, model_id: nil, zipped: true, stream_download: true)
|
|
110
|
+
model_id = _getModelIdOrError(model_id)
|
|
111
|
+
|
|
112
|
+
latest_key_name = getAvailableReports("SEG", model_id, zipped: zipped)
|
|
113
|
+
presigned_response_object = getPresignedResponse(latest_key_name, model_id, is_download: true)
|
|
114
|
+
|
|
115
|
+
report_response = @http_client.get(presigned_response_object["url"])
|
|
116
|
+
open(download_file_path, "wb") do |file|
|
|
117
|
+
file.write(report_response.body)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
return report_response
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def getAvailableReports(report_type, model_id, zipped: true, latest: true)
|
|
124
|
+
url_query_parameters = {:model_id => model_id, :report_type => report_type, :zipped => zipped, :latest => true}
|
|
125
|
+
list_reports_response = api("GET", "api/data", url_query_parameters, model_id, deserialize_response: false)
|
|
126
|
+
list_reports_response_object = JSON.parse(list_reports_response.body)
|
|
127
|
+
|
|
128
|
+
if list_reports_response.status != 200
|
|
129
|
+
raise IOError, "HTTP #{list_reports_response.status}: #{list_reports_response_object["error"]} "
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
if list_reports_response_object["latest_key_name"] != nil
|
|
133
|
+
|
|
134
|
+
return list_reports_response_object["latest_key_name"]
|
|
135
|
+
else
|
|
136
|
+
|
|
137
|
+
return list_reports_response_object["available_key_names"]
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def getPresignedResponse(key_name, model_id, data_type: nil, file_name: nil, file_size: nil, is_download: false)
|
|
142
|
+
parameters = {"model_id": model_id}
|
|
143
|
+
|
|
144
|
+
if is_download
|
|
145
|
+
parameters["key_name"] = key_name
|
|
146
|
+
parameters["is_download"] = true
|
|
147
|
+
else
|
|
148
|
+
parameters["data_type"] = data_type
|
|
149
|
+
parameters["file_name"] = file_name
|
|
150
|
+
parameters["file_size"] = file_size
|
|
151
|
+
parameters["presigned_url_http_method"] = "PUT"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
return api("POST", "api/data", nil, parameters)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def api(http_method, url_endpoint, url_query_parameters, parameters, deserialize_response: true)
|
|
159
|
+
formatted_url = "#{@host_address}/#{url_endpoint}"
|
|
160
|
+
if url_query_parameters
|
|
161
|
+
formatted_url = formatted_url + "?" + URI.encode_www_form(url_query_parameters)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
headers = {"authorization" => @authentication_token, "content-type" => "application/json"}
|
|
165
|
+
|
|
166
|
+
if http_method == "GET"
|
|
167
|
+
api_response = @http_client.get(formatted_url, header: headers)
|
|
168
|
+
elsif http_method == "POST"
|
|
169
|
+
if parameters.class != String
|
|
170
|
+
parameters_str = JSON.generate(parameters)
|
|
171
|
+
end
|
|
172
|
+
api_response = @http_client.post(formatted_url, header: headers, body: parameters_str)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
if deserialize_response
|
|
176
|
+
if api_response.status == 200 or api_response.status == 201
|
|
177
|
+
|
|
178
|
+
return JSON.parse(api_response.body)
|
|
179
|
+
end
|
|
180
|
+
else
|
|
181
|
+
|
|
182
|
+
return api_response
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _getModelIdOrError(model_id)
|
|
188
|
+
model_id = if model_id == nil then @model_id else model_id end
|
|
189
|
+
if model_id == nil
|
|
190
|
+
raise ArgumentError, "model_id must be set during client instantiation or provided during your instance method call."
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
return model_id
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "minitest/autorun"
|
|
3
|
+
|
|
4
|
+
require "webmock/test_unit"
|
|
5
|
+
|
|
6
|
+
require "./lib/lumidatum_client.rb"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ClientInit < Minitest::Test
|
|
10
|
+
def setup
|
|
11
|
+
@valid_api_token = "Token <API key>"
|
|
12
|
+
@valid_model_id = 123
|
|
13
|
+
@custom_host_address = "http://localhost:8000"
|
|
14
|
+
|
|
15
|
+
@default_host_address = "https://www.lumidatum.com"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# No specified host/default host
|
|
19
|
+
def test_client_init
|
|
20
|
+
test_client = LumidatumClient.new(@valid_api_token, @valid_model_id, @custom_host_address)
|
|
21
|
+
|
|
22
|
+
assert_instance_of(LumidatumClient, test_client)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_default_host
|
|
26
|
+
test_client = LumidatumClient.new(@valid_api_token, @valid_model_id)
|
|
27
|
+
|
|
28
|
+
# Default host should be https://www.lumidatum.com
|
|
29
|
+
assert_equal(@default_host_address, test_client.host_address)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_specifying_a_host
|
|
33
|
+
test_client = LumidatumClient.new(@valid_api_token, @valid_model_id, @custom_host_address)
|
|
34
|
+
|
|
35
|
+
assert_equal(@custom_host_address, test_client.host_address)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_nil_model_id_is_fine
|
|
39
|
+
test_client = LumidatumClient.new(@valid_api_token)
|
|
40
|
+
|
|
41
|
+
assert_nil(test_client.model_id)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_nil_api_token_error
|
|
45
|
+
assert_raises ArgumentError do
|
|
46
|
+
LumidatumClient.new(nil, @valid_model_id)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def createTestClient
|
|
53
|
+
|
|
54
|
+
return LumidatumClient.new("API Key", 123)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def createTestClientNoModelId
|
|
58
|
+
|
|
59
|
+
return LumidatumClient.new("API Key")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Personalization
|
|
64
|
+
def setup
|
|
65
|
+
@test_client = createTestClient
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_item_recs
|
|
69
|
+
test_recommendations = [[], [], []]
|
|
70
|
+
WebMock.stub_request(
|
|
71
|
+
:post,
|
|
72
|
+
"https://www.lumidatum.com/api/predict/123"
|
|
73
|
+
).to_return(
|
|
74
|
+
status: 200,
|
|
75
|
+
body: JSON.generate(test_recommendations)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
recommendations = @test_client.getItemRecommendations({})
|
|
79
|
+
|
|
80
|
+
assert_equal(test_recommendations, recommendations)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def setupValidUploadResponses
|
|
86
|
+
# Upload presign request
|
|
87
|
+
WebMock.stub_request(
|
|
88
|
+
:post,
|
|
89
|
+
"https://www.lumidatum.com/api/data"
|
|
90
|
+
).to_return(
|
|
91
|
+
status: 201,
|
|
92
|
+
body: JSON.generate({"url" => "http://test.upload.url", "fields" => {}})
|
|
93
|
+
)
|
|
94
|
+
# S3 upload response
|
|
95
|
+
WebMock.stub_request(
|
|
96
|
+
:post,
|
|
97
|
+
"http://test.upload.url"
|
|
98
|
+
).to_return(
|
|
99
|
+
status: 204
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class UploadDataFiles < Minitest::Test
|
|
105
|
+
def setup
|
|
106
|
+
@test_client = createTestClient
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_sending_file
|
|
110
|
+
setupValidUploadResponses
|
|
111
|
+
|
|
112
|
+
file_upload_response = @test_client.sendTransactionData(file_path: "tests/resources/test_data.csv")
|
|
113
|
+
|
|
114
|
+
assert_equal(204, file_upload_response.status)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_no_model_id_error
|
|
118
|
+
no_model_id_test_client = createTestClientNoModelId
|
|
119
|
+
|
|
120
|
+
assert_raises ArgumentError do
|
|
121
|
+
no_model_id_test_client.sendTransactionData(file_path: "tests/resources/test_data.csv")
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_no_model_id_in_client
|
|
126
|
+
setupValidUploadResponses
|
|
127
|
+
|
|
128
|
+
no_model_id_test_client = createTestClientNoModelId
|
|
129
|
+
|
|
130
|
+
no_model_id_test_client.sendTransactionData(file_path: "tests/resources/test_data.csv", model_id: 123)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class DownloadReports < Minitest::Test
|
|
135
|
+
def setup
|
|
136
|
+
@test_client = createTestClient
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_no_report_yet
|
|
140
|
+
# List response
|
|
141
|
+
WebMock.stub_request(
|
|
142
|
+
:get,
|
|
143
|
+
"https://www.lumidatum.com/api/data?latest=true&model_id=123&report_type=LTV&zipped=true&latest=true"
|
|
144
|
+
).to_return(
|
|
145
|
+
status: 404,
|
|
146
|
+
body: JSON.generate({"error" => "Requested item does not exist."})
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Raises error for 404 response on list call
|
|
150
|
+
assert_raises IOError do
|
|
151
|
+
file_download_response = @test_client.getLatestLTVReport("test_download_file.csv")
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def test_getting_report
|
|
156
|
+
# List response
|
|
157
|
+
WebMock.stub_request(
|
|
158
|
+
:get,
|
|
159
|
+
"https://www.lumidatum.com/api/data?latest=true&model_id=123&report_type=LTV&zipped=true&latest=true"
|
|
160
|
+
).to_return(
|
|
161
|
+
status: 200,
|
|
162
|
+
body: JSON.generate({"key_name" => "test_key_name"})
|
|
163
|
+
)
|
|
164
|
+
# Presign response
|
|
165
|
+
WebMock.stub_request(
|
|
166
|
+
:post, "https://www.lumidatum.com/api/data"
|
|
167
|
+
).to_return(
|
|
168
|
+
status: 200,
|
|
169
|
+
body: JSON.generate({"url" => "http://test.download.url"})
|
|
170
|
+
)
|
|
171
|
+
# S3 download response
|
|
172
|
+
WebMock.stub_request(:get, "http://test.download.url")
|
|
173
|
+
|
|
174
|
+
file_download_response = @test_client.getLatestLTVReport("test_download_file.csv")
|
|
175
|
+
|
|
176
|
+
assert_equal(200, file_download_response.status)
|
|
177
|
+
|
|
178
|
+
# Clean up
|
|
179
|
+
File.delete("test_download_file.csv")
|
|
180
|
+
end
|
|
181
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lumidatum_client
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.4
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mat Lee
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2017-03-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: httpclient
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.8'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.8'
|
|
27
|
+
description: ''
|
|
28
|
+
email:
|
|
29
|
+
- matt@lumidatum.com
|
|
30
|
+
executables:
|
|
31
|
+
- lumidatum_client
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- bin/lumidatum_client
|
|
36
|
+
- lib/lumidatum_client.rb
|
|
37
|
+
- tests/test_client_unit_tests.rb
|
|
38
|
+
homepage: https://www.lumidatum.com
|
|
39
|
+
licenses:
|
|
40
|
+
- MIT
|
|
41
|
+
metadata: {}
|
|
42
|
+
post_install_message:
|
|
43
|
+
rdoc_options: []
|
|
44
|
+
require_paths:
|
|
45
|
+
- lib
|
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
47
|
+
requirements:
|
|
48
|
+
- - ">="
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: '0'
|
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '0'
|
|
56
|
+
requirements: []
|
|
57
|
+
rubyforge_project:
|
|
58
|
+
rubygems_version: 2.6.10
|
|
59
|
+
signing_key:
|
|
60
|
+
specification_version: 4
|
|
61
|
+
summary: ''
|
|
62
|
+
test_files:
|
|
63
|
+
- tests/test_client_unit_tests.rb
|