scaleapi 0.1.0
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 +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +573 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/scale.rb +94 -0
- data/lib/scale/api.rb +75 -0
- data/lib/scale/api/callback.rb +29 -0
- data/lib/scale/api/errors.rb +46 -0
- data/lib/scale/api/task_list.rb +55 -0
- data/lib/scale/api/tasks.rb +55 -0
- data/lib/scale/api/tasks/audio_transcription.rb +26 -0
- data/lib/scale/api/tasks/base_task.rb +71 -0
- data/lib/scale/api/tasks/categorization.rb +32 -0
- data/lib/scale/api/tasks/comparison.rb +27 -0
- data/lib/scale/api/tasks/datacollection.rb +26 -0
- data/lib/scale/api/tasks/image_recognition.rb +28 -0
- data/lib/scale/api/tasks/phone_call.rb +30 -0
- data/lib/scale/api/tasks/transcription.rb +27 -0
- data/scaleapi-ruby.gemspec +27 -0
- metadata +109 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "scaleapi/ruby"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/scale.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
class Scale
|
2
|
+
attr_accessor :api_key, :callback_auth_key, :default_request_params, :logging
|
3
|
+
|
4
|
+
def self.validate_api_key(api_key)
|
5
|
+
if api_key.length < 5 || !(api_key.start_with?('live') || api_key.start_with?('test'))
|
6
|
+
raise Api::APIKeyInvalid
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(api_key: nil, callback_auth_key: nil, default_request_params: {}, logging: false, callback_url: nil)
|
11
|
+
Scale.validate_api_key(api_key)
|
12
|
+
|
13
|
+
self.api_key = api_key
|
14
|
+
self.callback_auth_key = callback_auth_key
|
15
|
+
self.default_request_params = default_request_params.merge(callback_url: callback_url)
|
16
|
+
self.logging = logging
|
17
|
+
end
|
18
|
+
|
19
|
+
def live?
|
20
|
+
api_key.start_with?('live')
|
21
|
+
end
|
22
|
+
|
23
|
+
def test?
|
24
|
+
api_key.start_with?('test')
|
25
|
+
end
|
26
|
+
|
27
|
+
def client
|
28
|
+
@client ||= Api.new(api_key, callback_auth_key, default_request_params, logging)
|
29
|
+
end
|
30
|
+
|
31
|
+
def tasks
|
32
|
+
@tasks ||= Scale::Api::Tasks.new(client)
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_datacollection_task(args = {})
|
36
|
+
Api::Tasks::Datacollection.create(args.merge(client: client))
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_categorization_task(args = {})
|
40
|
+
Api::Tasks::Categorization.create(args.merge(client: client))
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_comparison_task(args = {})
|
44
|
+
Api::Tasks::Comparison.create(args.merge(client: client))
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_image_recognition_task(args = {})
|
48
|
+
Api::Tasks::ImageRecognition.create(args.merge(client: client))
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_phone_call_task(args = {})
|
52
|
+
Api::Tasks::PhoneCall.create(args.merge(client: client))
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_transcription_task(args = {})
|
56
|
+
Api::Tasks::Transcription.create(args.merge(client: client))
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_audio_transcription_task(args = {})
|
60
|
+
Api::Tasks::AudioTranscription.create(args.merge(client: client))
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_callback(params, callback_key: nil)
|
64
|
+
callback = Api::Callback.new(params, callback_key: callback_key, client: client)
|
65
|
+
|
66
|
+
if block_given?
|
67
|
+
yield callback
|
68
|
+
else
|
69
|
+
callback
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
alias_method :live, :live?
|
74
|
+
alias_method :test, :test?
|
75
|
+
alias_method :live_mode?, :live?
|
76
|
+
alias_method :test_mode?, :test?
|
77
|
+
alias_method :create_annotation_task, :create_image_recognition_task
|
78
|
+
alias_method :create_phonecall_task, :create_phone_call_task
|
79
|
+
alias_method :create_audiotranscription_task, :create_audio_transcription_task
|
80
|
+
end
|
81
|
+
|
82
|
+
require 'scale/api'
|
83
|
+
require 'scale/api/errors'
|
84
|
+
require 'scale/api/callback'
|
85
|
+
require 'scale/api/tasks'
|
86
|
+
require 'scale/api/tasks/audio_transcription'
|
87
|
+
require 'scale/api/tasks/base_task'
|
88
|
+
require 'scale/api/tasks/datacollection'
|
89
|
+
require 'scale/api/tasks/categorization'
|
90
|
+
require 'scale/api/tasks/comparison'
|
91
|
+
require 'scale/api/tasks/image_recognition'
|
92
|
+
require 'scale/api/tasks/phone_call'
|
93
|
+
require 'scale/api/tasks/transcription'
|
94
|
+
require 'scale/api/task_list'
|
data/lib/scale/api.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'faraday'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class Scale
|
6
|
+
class Api < Struct.new(:api_key, :callback_auth_key, :default_request_params, :logging)
|
7
|
+
SCALE_API_URL = 'https://api.scaleapi.com/v1/'
|
8
|
+
|
9
|
+
def connection
|
10
|
+
@connection ||= Faraday.new(:url => SCALE_API_URL) do |faraday|
|
11
|
+
faraday.request :basic_auth, self.api_key, ''
|
12
|
+
faraday.request :url_encoded # form-encode POST params
|
13
|
+
faraday.response :logger if logging # log requests to STDOUT
|
14
|
+
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def get(url, params = {})
|
19
|
+
response = connection.get do |req|
|
20
|
+
req.url "#{SCALE_API_URL}#{url}"
|
21
|
+
req.params.merge!(default_request_params.merge(params))
|
22
|
+
req.headers['X-API-Client'] = "Ruby"
|
23
|
+
req.headers["X-API-Client-Version"] = '0.1.0'
|
24
|
+
end
|
25
|
+
|
26
|
+
if response.status != 200
|
27
|
+
return handle_error(response)
|
28
|
+
end
|
29
|
+
|
30
|
+
response
|
31
|
+
rescue Faraday::Error::ConnectionFailed
|
32
|
+
raise Scale::Api::ConnectionError
|
33
|
+
end
|
34
|
+
|
35
|
+
def post(url, body = {})
|
36
|
+
body.delete(:callback_url) if body.keys.include?(:callback_url) && body[:callback_url].nil?
|
37
|
+
body = default_request_params.merge(body)
|
38
|
+
|
39
|
+
response = connection.post do |req|
|
40
|
+
req.url "#{SCALE_API_URL}#{url}"
|
41
|
+
req.headers['Content-Type'] = 'application/json'
|
42
|
+
req.body = body.to_json
|
43
|
+
req.headers['X-API-Client'] = "Ruby"
|
44
|
+
req.headers["X-API-Client-Version"] = '0.1.0'
|
45
|
+
end
|
46
|
+
|
47
|
+
if response.status != 200
|
48
|
+
return handle_error(response)
|
49
|
+
end
|
50
|
+
|
51
|
+
response
|
52
|
+
rescue Faraday::Error::ConnectionFailed
|
53
|
+
raise Scale::Api::ConnectionError
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_error(response)
|
57
|
+
error_body = JSON.parse(response.body)
|
58
|
+
if response.status == 404
|
59
|
+
raise Scale::Api::NotFound.new(error_body['error'], response.status)
|
60
|
+
elsif response.status == 429
|
61
|
+
raise Scale::Api::TooManyRequests.new(error_body['error'], response.status)
|
62
|
+
elsif response.status > 499
|
63
|
+
raise Scale::Api::InternalServerError.new(error_body['error'], response.status)
|
64
|
+
elsif response.status == 401
|
65
|
+
raise Scale::Api::Unauthorized.new(error_body['error'], response.status)
|
66
|
+
else
|
67
|
+
raise Scale::Api::BadRequest.new(error_body['error'], response.status)
|
68
|
+
end
|
69
|
+
rescue JSON::ParserError
|
70
|
+
raise Scale::Api::InternalServerError
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
require 'scale/api/errors'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Scale
|
2
|
+
class Api
|
3
|
+
class Callback
|
4
|
+
attr_reader :client, :response, :task, :task_id, :request_callback_key
|
5
|
+
|
6
|
+
def initialize(params, callback_key: nil, client: nil)
|
7
|
+
@client = client
|
8
|
+
@response = params[:response]
|
9
|
+
@request_callback_key = callback_key
|
10
|
+
|
11
|
+
if params['task']
|
12
|
+
@task_id = params['task']['id']
|
13
|
+
@task = Scale::Api::Tasks::BaseTask.from_hash(params['task'].merge('client' => client))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def verified?
|
18
|
+
Callback.valid_callback_auth_key?(request_callback_key, client.callback_auth_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.valid_callback_auth_key?(callback_key, request_callback_key)
|
22
|
+
!!(callback_key && request_callback_key && request_callback_key == callback_key)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'scale/api/tasks'
|
29
|
+
require 'scale/api/tasks/base_task'
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Scale
|
2
|
+
class Api
|
3
|
+
class Error < StandardError
|
4
|
+
attr_accessor :status_code
|
5
|
+
|
6
|
+
def initialize(message = 'Please double check the request was properly formed and try again', status_code = 400)
|
7
|
+
super(message)
|
8
|
+
self.status_code = status_code
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class BadRequest < Error
|
13
|
+
end
|
14
|
+
|
15
|
+
class TooManyRequests < Error
|
16
|
+
end
|
17
|
+
|
18
|
+
class NotFound < Error
|
19
|
+
end
|
20
|
+
|
21
|
+
class Unauthorized < Error
|
22
|
+
def initialize(message = "Please ensure that the api_key provided is correct. To find your API key, go to your Scale Dashboard", status_code = 401)
|
23
|
+
super(message, status_code)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class InternalServerError < Error
|
28
|
+
def initialize(message = "Scale's servers are currently experiencing issues. Please wait a moment and try again.", status_code = 500)
|
29
|
+
super(message, status_code)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class ConnectionError < Error
|
34
|
+
def initialize(message = "There's an issue connecting to Scale's servers. Please check you're connected to the internet, and try again.'", status_code = nil)
|
35
|
+
super(message, status_code)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class APIKeyInvalid < Error
|
40
|
+
def initialize(message = "Provided api_key is invalid. Please double check you passed it in correctly and try again. If you're having trouble finding it, check your Scale Dashboard", status_code = nil)
|
41
|
+
super(message, status_code)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'scale/api/tasks/base_task'
|
2
|
+
require 'scale/api/tasks/audio_transcription'
|
3
|
+
require 'scale/api/tasks/datacollection'
|
4
|
+
require 'scale/api/tasks/categorization'
|
5
|
+
require 'scale/api/tasks/comparison'
|
6
|
+
require 'scale/api/tasks/image_recognition'
|
7
|
+
require 'scale/api/tasks/phone_call'
|
8
|
+
require 'scale/api/tasks/transcription'
|
9
|
+
|
10
|
+
class Scale
|
11
|
+
class Api
|
12
|
+
class TaskList
|
13
|
+
include Enumerable
|
14
|
+
extend Forwardable
|
15
|
+
def_delegators :@docs, :each, :<<, :[], :[]=, :length, :count
|
16
|
+
attr_accessor :client, :docs, :limit, :offset, :has_more, :params
|
17
|
+
TASK_TYPES_TO_CLASSNAMES = {
|
18
|
+
'audiotranscription' => ::Scale::Api::Tasks::AudioTranscription,
|
19
|
+
'categorization' => ::Scale::Api::Tasks::Categorization,
|
20
|
+
'comparison' => ::Scale::Api::Tasks::Comparison,
|
21
|
+
'datacollection' => ::Scale::Api::Tasks::Datacollection,
|
22
|
+
'annotation' => ::Scale::Api::Tasks::ImageRecognition,
|
23
|
+
'phonecall' => ::Scale::Api::Tasks::PhoneCall,
|
24
|
+
'transcription' => ::Scale::Api::Tasks::Transcription
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
def initialize(client: nil, docs: [], limit: 99, offset: 0, has_more: false, params: {})
|
28
|
+
self.client = client
|
29
|
+
self.docs = docs.map do |doc|
|
30
|
+
::Scale::Api::Tasks::BaseTask.from_hash(doc.merge('client' => client))
|
31
|
+
end
|
32
|
+
|
33
|
+
self.limit = limit
|
34
|
+
self.offset = offset
|
35
|
+
self.has_more = has_more
|
36
|
+
self.params = params # Used to get next page
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_more?
|
40
|
+
!!has_more
|
41
|
+
end
|
42
|
+
|
43
|
+
def page
|
44
|
+
(offset + (limit * 1)) / limit
|
45
|
+
end
|
46
|
+
|
47
|
+
def next_page
|
48
|
+
next_page_params = params.dup
|
49
|
+
params[:offset] = params[:limit] + params[:offset]
|
50
|
+
Scale::Api::Tasks.new(client).list(params)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class Scale
|
2
|
+
class Api
|
3
|
+
class Tasks < Struct.new(:client)
|
4
|
+
def list(start_time: nil, end_time: nil, limit: 99, offset: 0, type: nil, status: nil)
|
5
|
+
params = {
|
6
|
+
start_time: start_time ? start_time.iso8601 : nil,
|
7
|
+
end_time: end_time ? end_time.iso8601 : nil,
|
8
|
+
limit: limit,
|
9
|
+
offset: offset,
|
10
|
+
status: status,
|
11
|
+
type: type
|
12
|
+
}
|
13
|
+
|
14
|
+
response = client.get('tasks', params)
|
15
|
+
body = JSON.parse(response.body)
|
16
|
+
|
17
|
+
TaskList.new({
|
18
|
+
client: client,
|
19
|
+
docs: body['docs'],
|
20
|
+
limit: body['limit'],
|
21
|
+
offset: body['offset'],
|
22
|
+
has_more: body['has_more'],
|
23
|
+
params: params
|
24
|
+
})
|
25
|
+
end
|
26
|
+
|
27
|
+
def find(task_id)
|
28
|
+
response = client.get("task/#{task_id}")
|
29
|
+
BaseTask.from_hash(JSON.parse(response.body).merge('client' => client))
|
30
|
+
end
|
31
|
+
|
32
|
+
def cancel(task_id)
|
33
|
+
response = client.post("task/#{task_id}/cancel")
|
34
|
+
BaseTask.from_hash(JSON.parse(response.body).merge('client' => client))
|
35
|
+
end
|
36
|
+
|
37
|
+
def create(args = {})
|
38
|
+
raise ArgumentError.new('Task type is required') if (args[:type].nil? && args['type'].nil?)
|
39
|
+
klass = ::Scale::Api::TaskList::TASK_TYPES_TO_CLASSNAMES[(args[:type] || args['type']).to_s]
|
40
|
+
|
41
|
+
unless klass
|
42
|
+
raise ArgumentError.new('Unsupported task type. Supported task types: ' + ::Scale::Api::TaskList::TASK_TYPES_TO_CLASSNAMES.keys.join(','))
|
43
|
+
end
|
44
|
+
|
45
|
+
args.delete(:type)
|
46
|
+
klass.create(args.merge(client: client))
|
47
|
+
end
|
48
|
+
|
49
|
+
alias_method :all, :list
|
50
|
+
alias_method :where, :list
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
require 'scale/api/task_list'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'scale/api/tasks/base_task'
|
3
|
+
|
4
|
+
class Scale
|
5
|
+
class Api
|
6
|
+
class Tasks
|
7
|
+
class AudioTranscription < BaseTask
|
8
|
+
CREATE_PATH = 'task/audiotranscription'.freeze
|
9
|
+
|
10
|
+
def self.create(callback_url: nil, instruction: nil, attachment_type: 'audio', attachment: nil, verbatim: false, urgency: 'day', metadata: {}, client: nil)
|
11
|
+
response = client.post(CREATE_PATH, {
|
12
|
+
callback_url: callback_url,
|
13
|
+
instruction: instruction,
|
14
|
+
attachment_type: attachment_type,
|
15
|
+
attachment: attachment,
|
16
|
+
verbatim: verbatim,
|
17
|
+
urgency: urgency,
|
18
|
+
metadata: metadata
|
19
|
+
})
|
20
|
+
|
21
|
+
AudioTranscription.new(JSON.parse(response.body))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
class Scale
|
4
|
+
class Api
|
5
|
+
class Tasks
|
6
|
+
class BaseTask
|
7
|
+
attr_accessor :client
|
8
|
+
ATTRIBUTES = %w(task_id type instruction params urgency response callback_url created_at status completed_at callback_succeeded_at metadata).freeze
|
9
|
+
ATTRIBUTES.each { |attr| attr_reader attr }
|
10
|
+
|
11
|
+
alias_method :id, :task_id
|
12
|
+
|
13
|
+
def self.from_hash(hash)
|
14
|
+
klass = ::Scale::Api::TaskList::TASK_TYPES_TO_CLASSNAMES[(hash[:type] || hash['type']).to_s] || self
|
15
|
+
klass.new(hash)
|
16
|
+
end
|
17
|
+
|
18
|
+
def cancel!
|
19
|
+
Tasks.new(client).cancel(id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(json = {})
|
23
|
+
|
24
|
+
ATTRIBUTES.each do |attr|
|
25
|
+
instance_variable_set "@#{attr}", json[attr]
|
26
|
+
end
|
27
|
+
|
28
|
+
@client = (json[:client] || json['client'])
|
29
|
+
|
30
|
+
tweak_attributes
|
31
|
+
end
|
32
|
+
|
33
|
+
def day?
|
34
|
+
urgency == 'day'
|
35
|
+
end
|
36
|
+
|
37
|
+
def week?
|
38
|
+
urgency == 'week'
|
39
|
+
end
|
40
|
+
|
41
|
+
def immediate?
|
42
|
+
urgency == 'immediate'
|
43
|
+
end
|
44
|
+
|
45
|
+
def pending?
|
46
|
+
status == 'pending'
|
47
|
+
end
|
48
|
+
|
49
|
+
def completed?
|
50
|
+
status == 'completed'
|
51
|
+
end
|
52
|
+
|
53
|
+
def canceled?
|
54
|
+
status == 'canceled'
|
55
|
+
end
|
56
|
+
|
57
|
+
def callback_succeeded?
|
58
|
+
!!callback_succeeded_at
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def tweak_attributes
|
64
|
+
@created_at = Time.parse(created_at) rescue nil
|
65
|
+
@completed_at = Time.parse(completed_at) rescue nil
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|