datarobot-ai_api 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/README.md +12 -0
- data/Rakefile +54 -0
- data/datarobot-ai_api.gemspec +19 -0
- data/lib/datarobot.rb +1 -0
- data/lib/datarobot/ai_api.rb +190 -0
- data/lib/datarobot/ai_api/ai.rb +191 -0
- data/lib/datarobot/ai_api/dataset.rb +99 -0
- data/lib/datarobot/ai_api/deployment.rb +20 -0
- data/lib/datarobot/ai_api/evaluation.rb +21 -0
- data/lib/datarobot/ai_api/learning_session.rb +112 -0
- data/lib/datarobot/ai_api/output.rb +58 -0
- data/lib/datarobot/ai_api/page.rb +50 -0
- data/lib/datarobot/ai_api/prediction.rb +20 -0
- data/lib/datarobot/ai_api/refreshable.rb +20 -0
- data/lib/datarobot/ai_api/task.rb +86 -0
- data/test/cassettes/Datarobot_AiApi/_ping/returns_unauthorized_when_not_authorized.yml +41 -0
- data/test/cassettes/Datarobot_AiApi/_ping/works_when_authorized.yml +58 -0
- data/test/cassettes/Datarobot_AiApi/_ping_me/returns_unauthorized_when_not_authorized.yml +41 -0
- data/test/cassettes/Datarobot_AiApi/_ping_me/works_when_authorized.yml +58 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/404s_if_there_is_no_matching_output.yml +190 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/_find/should_accept_a_block.yml +66 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/_next_page/can_go_to_the_next_page.yml +132 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/_next_page/returns_the_same_page_if_there_is_no_next_page.yml +123 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/_previous_page/can_go_to_the_previous_page.yml +262 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/_previous_page/returns_the_same_page_if_there_is_no_previous_page.yml +67 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/_refresh/can_refresh_to_a_new_object.yml +129 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/_refresh_/can_refresh_its_values.yml +129 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/can_add_datasets_to_AIs.yml +120 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/can_add_learning_sessions_to_AIs.yml +120 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/can_be_trained.yml +4554 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/can_create_AIs.yml +120 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/can_delete_AIs.yml +58 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/can_find_AIs.yml +63 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/can_get_AI_datasets.yml +131 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/can_get_AI_outputs.yml +124 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/can_list_AIs.yml +123 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_authorized/can_predict_things.yml +190 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_not_authorized/can_not_create_AIs.yml +43 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_not_authorized/can_not_find_AIs.yml +43 -0
- data/test/cassettes/Datarobot_AiApi_AI/when_not_authorized/can_not_list_AIs.yml +45 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/_find/should_accept_a_block.yml +64 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/_next_page/can_go_to_the_next_page.yml +126 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/_next_page/returns_the_same_page_if_there_is_no_next_page.yml +113 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/_previous_page/can_go_to_the_previous_page.yml +250 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/_previous_page/returns_the_same_page_if_there_is_no_previous_page.yml +64 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/_refresh/can_refresh_to_a_new_object.yml +125 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/_refresh_/can_refresh_its_values.yml +125 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/can_delete_datasets.yml +58 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/can_find_datasets.yml +61 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/can_import_a_dataset_from_a_file.yml +1077 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/can_import_a_dataset_from_a_url.yml +55 -0
- data/test/cassettes/Datarobot_AiApi_Dataset/when_authorized/can_list_datasets.yml +113 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/_find/should_accept_a_block.yml +67 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/_next_page/can_go_to_the_next_page.yml +67 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/_next_page/returns_the_same_page_if_there_is_no_next_page.yml +100 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/_previous_page/can_go_to_the_previous_page.yml +67 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/_previous_page/returns_the_same_page_if_there_is_no_previous_page.yml +100 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/_refresh/can_refresh_to_a_new_object.yml +131 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/_refresh_/can_refresh_its_values.yml +130 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/can_create_learning_sessions.yml +2815 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/can_delete_learning_sessions.yml +58 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/can_find_learning_sessions.yml +63 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/can_get_learning_session_deployment_data.yml +123 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/can_get_learning_session_evaluation_data.yml +67 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/can_get_learning_session_features.yml +127 -0
- data/test/cassettes/Datarobot_AiApi_LearningSession/when_authorized/can_list_learning_sessions.yml +100 -0
- data/test/cassettes/Datarobot_AiApi_Output/when_authorized/can_create_outputs.yml +60 -0
- data/test/cassettes/Datarobot_AiApi_Output/when_authorized/can_find_named_outputs.yml +123 -0
- data/test/cassettes/Datarobot_AiApi_Output/when_authorized/can_get_features_of_an_output.yml +184 -0
- data/test/cassettes/Datarobot_AiApi_Output/when_authorized/can_get_the_evaluation_of_an_output.yml +131 -0
- data/test/cassettes/Datarobot_AiApi_Output/when_authorized/can_list_outputs.yml +124 -0
- data/test/cassettes/Datarobot_AiApi_Task/when_authorized/_find/should_accept_a_block.yml +63 -0
- data/test/cassettes/Datarobot_AiApi_Task/when_authorized/_refresh/can_refresh_to_a_new_object.yml +124 -0
- data/test/cassettes/Datarobot_AiApi_Task/when_authorized/_refresh_/can_refresh_its_values.yml +125 -0
- data/test/cassettes/Datarobot_AiApi_Task/when_authorized/can_stop_a_task.yml +58 -0
- data/test/cassettes/Datarobot_AiApi_Task/when_authorized/can_wait_until_completed.yml +1138 -0
- data/test/cassettes/Datarobot_AiApi_Task/when_authorized/check_the_status_of_a_task.yml +64 -0
- data/test/data/delete-me.csv +8037 -0
- data/test/data/test.csv +8037 -0
- data/test/datarobot/ai_api/test_ai.rb +133 -0
- data/test/datarobot/ai_api/test_dataset.rb +64 -0
- data/test/datarobot/ai_api/test_learning_session.rb +77 -0
- data/test/datarobot/ai_api/test_output.rb +54 -0
- data/test/datarobot/ai_api/test_page.rb +50 -0
- data/test/datarobot/ai_api/test_prediction.rb +0 -0
- data/test/datarobot/ai_api/test_refreshable.rb +39 -0
- data/test/datarobot/ai_api/test_task.rb +53 -0
- data/test/datarobot/test_ai_api.rb +52 -0
- data/test/test_helper.rb +40 -0
- metadata +217 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7763e2dae82034b312b2daf53061aafa7a1db5f091964a118c5eca2ecc3f8b33
|
4
|
+
data.tar.gz: 1b0ad22bf75f5ef9742c5baedbbb9784ff56a3d533f7034f91b838e3d91c7b1c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 742b703100a6ca1d24fd5c37c7e39a1f8ee94cc23d756136b78910c66ec182105cf5f5921e4bb6d6d48557098d310d05aebce6871073b93ad3eec18ebdf74578
|
7
|
+
data.tar.gz: 2fafe6e650f301e35c5ccc3207ee09e86c117b822e36bce90ebd47cde56a7f05d66221f4a6d80a7f8f6ae72111bbb96fcdb4b1cdfe028f7c7098452122f8fa71
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rubygems/specification'
|
5
|
+
require 'rake'
|
6
|
+
|
7
|
+
def gemspec
|
8
|
+
@gemspec ||= begin
|
9
|
+
gem_name = "datarobot-ai_api"
|
10
|
+
file = File.expand_path("../#{gem_name}.gemspec", __FILE__)
|
11
|
+
eval(File.read(file), binding, file)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'rake/testtask'
|
16
|
+
Rake::TestTask.new(:test) do |test|
|
17
|
+
test.libs << 'test'
|
18
|
+
test.pattern = 'test/**/test_*.rb'
|
19
|
+
test.verbose = true
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
require 'rake/gempackagetask'
|
24
|
+
rescue LoadError
|
25
|
+
task(:gem) { $stderr.puts '`gem install rake` to package gems' }
|
26
|
+
else
|
27
|
+
Rake::GemPackageTask.new(gemspec) do |pkg|
|
28
|
+
pkg.gem_spec = gemspec
|
29
|
+
end
|
30
|
+
task :gem => :gemspec
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Validates the gemspec"
|
34
|
+
task :gemspec do
|
35
|
+
gemspec.validate
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Displays the current version"
|
39
|
+
task :version do
|
40
|
+
puts "Current version: #{gemspec.version}"
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Installs the gem locally"
|
44
|
+
task :install => :package do
|
45
|
+
sh "gem install pkg/#{gemspec.name}-#{gemspec.version}"
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Release the gem"
|
49
|
+
task :release => :package do
|
50
|
+
sh "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
|
51
|
+
end
|
52
|
+
|
53
|
+
task :package => :gemspec
|
54
|
+
task :default => :test
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'datarobot-ai_api'
|
3
|
+
s.version = '0.0.3'
|
4
|
+
s.date = '2019-04-16'
|
5
|
+
s.summary = "A Ruby wrapper for the DataRobot AI API"
|
6
|
+
s.description = "Provides an interface to the AI API"
|
7
|
+
s.authors = ["AI API team"]
|
8
|
+
s.email = 'ai-api-team@datarobot.com'
|
9
|
+
s.files = %w(.gitignore README.md Rakefile datarobot-ai_api.gemspec lib/datarobot.rb Gemfile) + Dir.glob("{lib,test}/**/*")
|
10
|
+
s.homepage =
|
11
|
+
'https://developers.datarobot.com/api'
|
12
|
+
s.license = 'Nonstandard'
|
13
|
+
s.add_development_dependency "minitest", "~> 5.8"
|
14
|
+
s.add_development_dependency "minitest-reporters", "~> 1.1"
|
15
|
+
s.add_development_dependency "minitest-vcr", "~> 1.4"
|
16
|
+
s.add_development_dependency "webmock", "~> 3.6"
|
17
|
+
s.add_development_dependency 'pry', '~> 0.12.2'
|
18
|
+
s.add_runtime_dependency 'multipart-post', '~> 2.1'
|
19
|
+
end
|
data/lib/datarobot.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'datarobot/ai_api'
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'json'
|
3
|
+
require 'cgi'
|
4
|
+
require "net/http/post/multipart"
|
5
|
+
|
6
|
+
module Datarobot
|
7
|
+
module AiApi
|
8
|
+
class UnauthorizedError < StandardError; end;
|
9
|
+
class NotFoundError < StandardError; end;
|
10
|
+
class ApiError < StandardError; end;
|
11
|
+
|
12
|
+
@@api_key = ''
|
13
|
+
@@base_url = 'https://developers.datarobot.com'
|
14
|
+
|
15
|
+
def self.api_key; @@api_key end
|
16
|
+
def self.api_key= v; @@api_key = v end
|
17
|
+
|
18
|
+
# Sets global API key
|
19
|
+
# @param [String] api_key The api key to use for authentication
|
20
|
+
# @return [Datarobot::AiApi]
|
21
|
+
def self.configure!(api_key: '', base_url: nil)
|
22
|
+
@@api_key = api_key
|
23
|
+
@@base_url = base_url if base_url
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
# Checks the `ping` endpoint. Useful for debugging
|
28
|
+
# @return [Hash]
|
29
|
+
def self.ping
|
30
|
+
request_endpoint('/aiapi/ping')
|
31
|
+
end
|
32
|
+
|
33
|
+
# Checks the `ping/me` endpoint. Useful for debugging authentication
|
34
|
+
# @return [Hash]
|
35
|
+
def self.ping_me
|
36
|
+
request_endpoint('/aiapi/ping/me')
|
37
|
+
end
|
38
|
+
|
39
|
+
# Adds default headers to a net http request
|
40
|
+
# @param [Net::HTTP::*] request The net/http request to add the headers to
|
41
|
+
# @param [Hash] additional_headers Will overwrite defaults
|
42
|
+
# @return [Net::Http::*] returns an aribitrary request type (GET/POST/etc.)
|
43
|
+
def self.add_headers(request, additional_headers={})
|
44
|
+
request["User-Agent"] = "datarobot-ai/ruby"
|
45
|
+
request["Content-Type"] = "application/json"
|
46
|
+
request["Authorization"] = "Bearer #{@@api_key}"
|
47
|
+
additional_headers.each do |k, v|
|
48
|
+
request[k] = v
|
49
|
+
end
|
50
|
+
request
|
51
|
+
end
|
52
|
+
|
53
|
+
# Make a request with https. This is a helper method to avoid having to
|
54
|
+
# write net/http boilerplate
|
55
|
+
# @param [String] uri The uri to make the request to
|
56
|
+
# @param [Hash] params Additoinal params to add to the request. Will
|
57
|
+
# overwrite params in the query string of the given uri
|
58
|
+
#
|
59
|
+
# @yield [uri, http] Yields the https request with the parsed uri and http object
|
60
|
+
def self.with_https(uri, params={})
|
61
|
+
uri = URI.parse(uri)
|
62
|
+
existing_params = CGI::parse(uri.query.to_s)
|
63
|
+
uri.query = URI.encode_www_form(existing_params.merge params)
|
64
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
|
65
|
+
yield uri, http
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Gets a bare uri. Useful for requests with `result` objects
|
70
|
+
# @param [String] bare_uri
|
71
|
+
# @param [Block] block passed to `handle_response`
|
72
|
+
def self.get(bare_uri, &block)
|
73
|
+
with_https(bare_uri) do |uri, http|
|
74
|
+
request = Net::HTTP::Get.new(uri)
|
75
|
+
request = add_headers(request)
|
76
|
+
|
77
|
+
response = http.request(request)
|
78
|
+
handle_response(response, &block)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Converts a file into an octet stream, and uploads it to given endpoint
|
83
|
+
# @param [String] endpoint The endpoint to upload the file to
|
84
|
+
# @param [String] file_path Path to local file for upload
|
85
|
+
# @return Delegates return values to `handle_response`
|
86
|
+
def self.upload_to_endpoint(endpoint, file_path, &block)
|
87
|
+
uri = "#{@@base_url}#{endpoint}"
|
88
|
+
with_https(uri) do |uri, http|
|
89
|
+
request = Net::HTTP::Post::Multipart.new uri.request_uri, "file" => UploadIO.new(file_path, "application/octet-stream")
|
90
|
+
request["User-Agent"] = "datarobot-ai/ruby"
|
91
|
+
request["Authorization"] = "Bearer #{@@api_key}"
|
92
|
+
response = http.request(request)
|
93
|
+
handle_response(response, &block)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Common error handling and response parsing
|
98
|
+
# @yield [data] Yields the parsed json response if there is one
|
99
|
+
# @return nil if body is empty
|
100
|
+
# @return [Hash] data Parsed json response if no block is provided
|
101
|
+
def self.handle_response(response, &block)
|
102
|
+
if response.code.to_i < 300
|
103
|
+
return nil if response.body.to_s.empty?
|
104
|
+
data = JSON.parse(response.body)
|
105
|
+
if block_given?
|
106
|
+
yield data
|
107
|
+
else
|
108
|
+
data
|
109
|
+
end
|
110
|
+
else
|
111
|
+
determine_error(response)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Makes a request to the given endpoint
|
116
|
+
# @param [String] endpoint The url path to the endpoint
|
117
|
+
# @param [String] method The HTTP method to make the request with
|
118
|
+
# @param [Hash] body JSON body. Only used in put and post requests
|
119
|
+
# @param [Hash] headers Additional headers to add to request
|
120
|
+
# @param [Hash] params Additoinal url parameters to add
|
121
|
+
# @return Delegates return values to `handle_response`
|
122
|
+
def self.request_endpoint(endpoint, method: 'get', body: {}, headers: {}, params: {}, &block)
|
123
|
+
uri_str = "#{@@base_url}#{endpoint}"
|
124
|
+
with_https(uri_str, params) do |uri, http|
|
125
|
+
case method.downcase
|
126
|
+
when 'put'
|
127
|
+
request = Net::HTTP::Put.new(uri.request_uri)
|
128
|
+
request.body = JSON.dump(body)
|
129
|
+
when 'delete'
|
130
|
+
request = Net::HTTP::Delete.new(uri.request_uri)
|
131
|
+
when 'post'
|
132
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
133
|
+
request.body = JSON.dump(body)
|
134
|
+
else
|
135
|
+
request = Net::HTTP::Get.new(uri)
|
136
|
+
end
|
137
|
+
request = add_headers(request, headers)
|
138
|
+
|
139
|
+
response = http.request(request)
|
140
|
+
handle_response(response, &block)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Uses request data to determin error classes
|
145
|
+
# Only raises values. Will never return
|
146
|
+
def self.determine_error(response_obj)
|
147
|
+
parsed = JSON.parse(response_obj.body)
|
148
|
+
msg = parsed["error"] || parsed["message"]
|
149
|
+
case response_obj.code
|
150
|
+
when "403"
|
151
|
+
raise Datarobot::AiApi::UnauthorizedError, msg
|
152
|
+
when "404"
|
153
|
+
raise Datarobot::AiApi::NotFoundError, msg
|
154
|
+
else
|
155
|
+
raise Datarobot::AiApi::ApiError, msg
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Given an arbitrary url, return the correct AiApi object
|
160
|
+
# @param [String] url
|
161
|
+
# @return [Datarobot::AiApi::*]
|
162
|
+
def self.determine_object(url)
|
163
|
+
case url
|
164
|
+
when /\/ais/
|
165
|
+
Datarobot::AiApi::AI
|
166
|
+
when /\/datasets/
|
167
|
+
Datarobot::AiApi::Dataset
|
168
|
+
when /\/learningSessions/
|
169
|
+
Datarobot::AiApi::LearningSession
|
170
|
+
when /\/outputs/
|
171
|
+
Datarobot::AiApi::Output
|
172
|
+
when /\/status/
|
173
|
+
Datarobot::AiApi::Task
|
174
|
+
when /\/deployment/
|
175
|
+
Datarobot::AiApi::Deployment
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
require 'datarobot/ai_api/page'
|
182
|
+
require 'datarobot/ai_api/refreshable'
|
183
|
+
require 'datarobot/ai_api/evaluation'
|
184
|
+
require 'datarobot/ai_api/deployment'
|
185
|
+
require 'datarobot/ai_api/ai'
|
186
|
+
require 'datarobot/ai_api/dataset'
|
187
|
+
require 'datarobot/ai_api/task'
|
188
|
+
require 'datarobot/ai_api/learning_session'
|
189
|
+
require 'datarobot/ai_api/output'
|
190
|
+
require 'datarobot/ai_api/prediction'
|
@@ -0,0 +1,191 @@
|
|
1
|
+
module Datarobot
|
2
|
+
module AiApi
|
3
|
+
|
4
|
+
# This is the primary model in the AI API. This is the thing that learns
|
5
|
+
# about the data that you give it
|
6
|
+
class AI
|
7
|
+
include Datarobot::AiApi::Refreshable
|
8
|
+
attr_reader :output_count, :dataset_count, :learning_session_count, :id
|
9
|
+
attr_accessor :name,
|
10
|
+
|
11
|
+
# Retrieves all AIs as a paginated resource.
|
12
|
+
#
|
13
|
+
# @param [Integer] limit Number of AIs per page
|
14
|
+
# @param [Integer] offset How many AIs to skip before this page
|
15
|
+
#
|
16
|
+
# @return [Datarobot::AiApi::Page(Datarobot::AiApi::AI)]
|
17
|
+
def self.all(limit: 50, offset: 0)
|
18
|
+
Datarobot::AiApi.request_endpoint('/aiapi/ais/', params: {limit: limit, offset: offset}) do |data|
|
19
|
+
Datarobot::AiApi::Page.new(self, data)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Retrieves an AI given an ID.
|
24
|
+
#
|
25
|
+
# @param [String] id The ID of the AI to retrieve
|
26
|
+
#
|
27
|
+
# @return [Datarobot::AiApi::AI]
|
28
|
+
def self.find(id, &block)
|
29
|
+
raise Datarobot::AiApi::NotFoundError, "Cannot find AI with id: nil" if id.nil?
|
30
|
+
Datarobot::AiApi.request_endpoint("/aiapi/ais/#{id}") do |data|
|
31
|
+
if block_given?
|
32
|
+
yield data
|
33
|
+
else
|
34
|
+
self.new(data)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Creates an AI
|
40
|
+
#
|
41
|
+
# @param [String] name The ID of the AI to retrieve
|
42
|
+
#
|
43
|
+
# @return [Datarobot::AiApi::AI]
|
44
|
+
def self.create(name: 'New AI')
|
45
|
+
Datarobot::AiApi.request_endpoint('/aiapi/ais/', method: 'post', body: { name: name }) do |data|
|
46
|
+
ai_data = Datarobot::AiApi.get(data["links"]["result"])
|
47
|
+
new(ai_data)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Deletes an AI. Returns `nil` if the action was successful. Will raise
|
52
|
+
# an error if the action was unsuccessful
|
53
|
+
#
|
54
|
+
# @param [String] id The ID of the AI to delete
|
55
|
+
#
|
56
|
+
# @return [nil]
|
57
|
+
def self.delete(id)
|
58
|
+
Datarobot::AiApi.request_endpoint("/aiapi/ais/#{id}", method: "delete")
|
59
|
+
end
|
60
|
+
|
61
|
+
# Given a parsed response body from the API, will create a new AI object
|
62
|
+
def initialize(options = {})
|
63
|
+
# Suppresses warnings about uninitialized variables
|
64
|
+
@id = nil
|
65
|
+
@name = nil
|
66
|
+
@dataset_count = nil
|
67
|
+
@output_count = nil
|
68
|
+
@learning_session_count = nil
|
69
|
+
|
70
|
+
set_from_options(options)
|
71
|
+
@outputs = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Takes a response body from the API. Will set all AI attributes from the
|
75
|
+
# response body
|
76
|
+
#
|
77
|
+
# @param [Hash] options A parsed response body
|
78
|
+
# @return [void]
|
79
|
+
def set_from_options(options = {})
|
80
|
+
# one-liner replacement for `stringify_keys`
|
81
|
+
options = options.collect{|k,v| [k.to_s, v]}.to_h
|
82
|
+
|
83
|
+
@output_count = options.dig("outputCount") || @output_count
|
84
|
+
@dataset_count = options.dig("datasetCount") || @dataset_count
|
85
|
+
@learning_session_count = options.dig("learningSessionCount") || @learning_session_count
|
86
|
+
@name = options.dig("name") || @name
|
87
|
+
@id = options.dig("id") || @id
|
88
|
+
end
|
89
|
+
|
90
|
+
# Lists all the outputs associated with the AI as a paginated resource
|
91
|
+
#
|
92
|
+
# @return [Datarobot::AiApi::Page(Datarobot::AiApi::Output)]
|
93
|
+
def outputs
|
94
|
+
Datarobot::AiApi.request_endpoint("/aiapi/ais/#{@id}/outputs") do |data|
|
95
|
+
data["aiId"] = @id
|
96
|
+
Datarobot::AiApi::Page.new(Datarobot::AiApi::Output, data)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Lists all the datasets associated with the AI as a paginated resource
|
101
|
+
#
|
102
|
+
# @return [Datarobot::AiApi::Page(Datarobot::AiApi::Dataset)]
|
103
|
+
def datasets
|
104
|
+
Datarobot::AiApi.request_endpoint("/aiapi/datasets?aiId=#{@id}") do |data|
|
105
|
+
data["aiId"] = @id
|
106
|
+
Datarobot::AiApi::Page.new(Datarobot::AiApi::Dataset, data)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Finds an output by name
|
111
|
+
#
|
112
|
+
# @param [String] name The name of the output associated with this AI
|
113
|
+
# that you want to find
|
114
|
+
# @return [Datarobot::AiApi::Output]
|
115
|
+
def find_output(name)
|
116
|
+
# TODO: Update this when AI API supports CGI escaped URIs
|
117
|
+
#
|
118
|
+
# This code was lifted from source of erb method:
|
119
|
+
# https://apidock.com/ruby/v2_6_3/ERB/Util/url_encode
|
120
|
+
#
|
121
|
+
# URI.escape is deprecated because it does not encode url control
|
122
|
+
# characters, thus making it a potential security issue and CGI.escape
|
123
|
+
# substitutes whitespace with a + which is correct, but unsupported by
|
124
|
+
# the AI API
|
125
|
+
encoded_name = name.gsub(/[^a-zA-Z0-9_\-.~]/) { |m|
|
126
|
+
sprintf("%%%02X", m.unpack1("C"))
|
127
|
+
}
|
128
|
+
|
129
|
+
Datarobot::AiApi.request_endpoint("/aiapi/ais/#{@id}/outputs/#{encoded_name}") do |data|
|
130
|
+
data["aiId"] = @id
|
131
|
+
Datarobot::AiApi::Output.new(data)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Adds a dataset to the AI. Raises an error on failure. Returns true on
|
136
|
+
# success
|
137
|
+
#
|
138
|
+
# @param [String] dataset_id The ID of the dataset to add
|
139
|
+
# @return [Bool] true
|
140
|
+
def add_dataset(dataset_id)
|
141
|
+
Datarobot::AiApi.request_endpoint("/aiapi/ais/#{@id}/datasets", method: 'post', body: { datasetId: dataset_id })
|
142
|
+
true
|
143
|
+
end
|
144
|
+
|
145
|
+
# Adds a learning session to the AI. Raises an error on failure. Returns
|
146
|
+
# true on success
|
147
|
+
#
|
148
|
+
# @param [String] session_id The ID of the learning session to add
|
149
|
+
# @return [Bool] true
|
150
|
+
def add_learning_session(session_id)
|
151
|
+
Datarobot::AiApi.request_endpoint("/aiapi/ais/#{@id}/learningSessions", method: 'post', body: { learningSessionId: session_id })
|
152
|
+
true
|
153
|
+
end
|
154
|
+
|
155
|
+
# Trains an AI on the given target with the data in the file at the given
|
156
|
+
# path. Returns true if successful. Raises an error otherwise
|
157
|
+
#
|
158
|
+
# @param [String] target The target to train the AI on
|
159
|
+
# @return [Bool] true
|
160
|
+
def train(target, file_path)
|
161
|
+
dataset = Datarobot::AiApi::Dataset.create_from_file(file_path)
|
162
|
+
add_dataset(dataset.id)
|
163
|
+
learning_session = Datarobot::AiApi::LearningSession.create(dataset_id: dataset.id, target: target)
|
164
|
+
add_learning_session(learning_session.id)
|
165
|
+
Datarobot::AiApi::Output.create(ai_id: @id, learning_session_id: learning_session.id, output_name: "#{@name} output")
|
166
|
+
true
|
167
|
+
end
|
168
|
+
|
169
|
+
# Predicts a target feature given a data hash
|
170
|
+
#
|
171
|
+
# @param target The target feature to predict
|
172
|
+
# @param data The remaining features used to predict target feature
|
173
|
+
def predict(target, data={})
|
174
|
+
output = outputs.find { |o| o.target == target }
|
175
|
+
|
176
|
+
raise NotFoundError, "No output with target #{target.inspect} found AI with ID #{@id.inspect}" if output.nil?
|
177
|
+
|
178
|
+
deployment_id = output.source["deploymentId"]
|
179
|
+
key = output.source["datarobot-key"]
|
180
|
+
Datarobot::AiApi.request_endpoint(
|
181
|
+
"/predApi/v1.0/deployments/#{deployment_id}/predictions/",
|
182
|
+
method: 'post',
|
183
|
+
body: [ data ],
|
184
|
+
headers: {"datarobot-key" => key}
|
185
|
+
) do |data|
|
186
|
+
Datarobot::AiApi::Page.new(Datarobot::AiApi::Prediction, data)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|