datarobot-ai_api 0.0.3
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/.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
|