eclaircir 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +17 -0
  5. data/LICENSE +21 -0
  6. data/README.md +70 -0
  7. data/Rakefile +12 -0
  8. data/TODO.md +75 -0
  9. data/bin/console +10 -0
  10. data/bin/test.rb +17 -0
  11. data/eclaircir.gemspec +20 -0
  12. data/lib/eclaircir/api_models/attributes/nullable_date_time.rb +15 -0
  13. data/lib/eclaircir/api_models/attributes.rb +8 -0
  14. data/lib/eclaircir/api_models/base_model.rb +11 -0
  15. data/lib/eclaircir/api_models/concept.rb +12 -0
  16. data/lib/eclaircir/api_models/data.rb +17 -0
  17. data/lib/eclaircir/api_models/input.rb +22 -0
  18. data/lib/eclaircir/api_models/media.rb +9 -0
  19. data/lib/eclaircir/api_models/model.rb +29 -0
  20. data/lib/eclaircir/api_models/output.rb +14 -0
  21. data/lib/eclaircir/api_models/response.rb +16 -0
  22. data/lib/eclaircir/api_models/status.rb +46 -0
  23. data/lib/eclaircir/api_models.rb +16 -0
  24. data/lib/eclaircir/client/status_validator.rb +30 -0
  25. data/lib/eclaircir/client.rb +85 -0
  26. data/lib/eclaircir/configuration.rb +16 -0
  27. data/lib/eclaircir/constants.rb +5 -0
  28. data/lib/eclaircir/error.rb +27 -0
  29. data/lib/eclaircir/version.rb +5 -0
  30. data/lib/eclaircir.rb +32 -0
  31. data/spec/eclaircir/api_models/concept_spec.rb +32 -0
  32. data/spec/eclaircir/api_models/data_spec.rb +104 -0
  33. data/spec/eclaircir/api_models/input_spec.rb +70 -0
  34. data/spec/eclaircir/api_models/media_spec.rb +15 -0
  35. data/spec/eclaircir/api_models/model_spec.rb +109 -0
  36. data/spec/eclaircir/api_models/output_spec.rb +42 -0
  37. data/spec/eclaircir/api_models/response_spec.rb +63 -0
  38. data/spec/eclaircir/api_models/status_spec.rb +67 -0
  39. data/spec/eclaircir/client/status_validator_spec.rb +77 -0
  40. data/spec/eclaircir/client_spec.rb +123 -0
  41. data/spec/eclaircir/configuration_spec.rb +64 -0
  42. data/spec/eclaircir_spec.rb +55 -0
  43. data/spec/etc/fixtures/predict_outputs/invalid_request.json +7 -0
  44. data/spec/etc/fixtures/predict_outputs/success.json +58 -0
  45. data/spec/factories/concept.rb +8 -0
  46. data/spec/factories/data.rb +13 -0
  47. data/spec/factories/input.rb +7 -0
  48. data/spec/factories/media.rb +6 -0
  49. data/spec/factories/model.rb +9 -0
  50. data/spec/factories/response.rb +7 -0
  51. data/spec/factories/status.rb +7 -0
  52. data/spec/spec_helper.rb +53 -0
  53. metadata +122 -0
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eclaircir
4
+ class Error < StandardError
5
+ end
6
+
7
+ class APIError < Error
8
+ end
9
+
10
+ class InvalidAuthTokenError < APIError
11
+ end
12
+
13
+ class ApiKeyNotFoundError < APIError
14
+ end
15
+
16
+ class BadRequestFormatError < APIError
17
+ end
18
+
19
+ class InvalidRequestError < APIError
20
+ end
21
+
22
+ class DuplicateURLError < APIError
23
+ end
24
+
25
+ class ImageDecodingError < APIError
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eclaircir
4
+ VERSION = "0.0.3"
5
+ end
data/lib/eclaircir.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ require 'httparty'
6
+ require 'virtus'
7
+ require 'active_support/core_ext/hash/compact'
8
+
9
+ module Eclaircir
10
+ class << self
11
+ def configure
12
+ yield configuration
13
+ end
14
+
15
+ def configuration
16
+ @configuration ||= Configuration.new
17
+ end
18
+
19
+ def new_client
20
+ Client.new(configuration.api_key)
21
+ end
22
+ end
23
+ end
24
+
25
+ require_relative 'eclaircir/version'
26
+ require_relative 'eclaircir/constants'
27
+ require_relative 'eclaircir/error'
28
+ require_relative 'eclaircir/configuration'
29
+
30
+ require_relative 'eclaircir/api_models'
31
+
32
+ require_relative 'eclaircir/client'
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Eclaircir::Concept do
4
+ subject do
5
+ described_class.new(params)
6
+ end
7
+
8
+ let(:params) do
9
+ {
10
+ 'id' => 'ai_HLmqFqBf',
11
+ 'name' => 'train',
12
+ 'app_id' => 'blah',
13
+ 'value' => 0.9989112,
14
+ }
15
+ end
16
+
17
+ its(:id) { is_expected.to eq 'ai_HLmqFqBf' }
18
+ its(:name) { is_expected.to eq 'train' }
19
+ its(:app_id) { is_expected.to eq 'blah' }
20
+ its(:value) { is_expected.to eq 0.9989112 }
21
+
22
+ describe '#to_api_hash' do
23
+ it 'returns the right hash' do
24
+ expect(subject.to_api_hash).to eq({
25
+ id: 'ai_HLmqFqBf',
26
+ name: 'train',
27
+ app_id: 'blah',
28
+ value: 0.9989112,
29
+ })
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+
3
+ describe Eclaircir::Data do
4
+ subject do
5
+ described_class.new(params)
6
+ end
7
+
8
+ let(:params) do
9
+ {
10
+ 'concepts' => concepts,
11
+ 'image' => image,
12
+ }
13
+ end
14
+
15
+ let(:concepts) do
16
+ [
17
+ {
18
+ 'id' => 'ai_HLmqFqBf',
19
+ 'name' => 'train',
20
+ 'app_id' => nil,
21
+ 'value' => 0.9989112
22
+ },
23
+ {
24
+ 'id' => 'ai_fvlBqXZR',
25
+ 'name' => 'railway',
26
+ 'app_id' => nil,
27
+ 'value' => 0.9975532
28
+ }
29
+ ]
30
+ end
31
+
32
+ let(:image) do
33
+ {
34
+ 'url' => 'https://samples.clarifai.com/metro-north.jpg',
35
+ }
36
+ end
37
+
38
+ its(:image) { is_expected.to be_a Eclaircir::Media }
39
+ its(:concepts) { is_expected.to all(be_a(Eclaircir::Concept)) }
40
+
41
+ describe '#to_api_hash' do
42
+ it 'returns the right hash' do
43
+ expect(subject.to_api_hash).to eq({
44
+ concepts: [
45
+ {
46
+ id: 'ai_HLmqFqBf',
47
+ name: 'train',
48
+ app_id: nil,
49
+ value: 0.9989112
50
+ },
51
+ {
52
+ id: 'ai_fvlBqXZR',
53
+ name: 'railway',
54
+ app_id: nil,
55
+ value: 0.9975532
56
+ }
57
+ ],
58
+ image: {
59
+ url: 'https://samples.clarifai.com/metro-north.jpg'
60
+ }
61
+ })
62
+ end
63
+
64
+ context 'when the image is not provided' do
65
+ let(:image) do
66
+ nil
67
+ end
68
+
69
+ it 'returns the right hash' do
70
+ expect(subject.to_api_hash).to eq({
71
+ concepts: [
72
+ {
73
+ id: "ai_HLmqFqBf",
74
+ name: "train",
75
+ app_id: nil,
76
+ value: 0.9989112
77
+ },
78
+ {
79
+ id: "ai_fvlBqXZR",
80
+ name: "railway",
81
+ app_id: nil,
82
+ value: 0.9975532
83
+ }
84
+ ]
85
+ })
86
+ end
87
+ end
88
+
89
+ context 'when the concepts are not provided' do
90
+ let(:concepts) do
91
+ nil
92
+ end
93
+
94
+ it 'returns the right hash' do
95
+ expect(subject.to_api_hash).to eq({
96
+ concepts: [],
97
+ image: {
98
+ url: 'https://samples.clarifai.com/metro-north.jpg'
99
+ }
100
+ })
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Eclaircir::Input do
4
+ subject do
5
+ described_class.new(params)
6
+ end
7
+
8
+ let(:params) do
9
+ {
10
+ 'id' => 'ea68cac87c304b28a8046557062f34a0',
11
+ 'data' => {
12
+ 'image' => {
13
+ 'url' => "https://samples.clarifai.com/metro-north.jpg",
14
+ },
15
+ },
16
+ }
17
+ end
18
+
19
+ its(:id) { is_expected.to eq 'ea68cac87c304b28a8046557062f34a0' }
20
+ its(:data) { is_expected.to be_a Eclaircir::Data }
21
+
22
+ describe '#to_api_hash' do
23
+ it 'returns the right hash' do
24
+ expect(subject.to_api_hash).to eq({
25
+ id: 'ea68cac87c304b28a8046557062f34a0',
26
+ data: {
27
+ image: {
28
+ url: 'https://samples.clarifai.com/metro-north.jpg'
29
+ },
30
+ concepts: []
31
+ }
32
+ })
33
+ end
34
+ end
35
+
36
+ describe '.from_url' do
37
+ before do
38
+ allow(described_class)
39
+ .to receive(:new)
40
+ .with(data: fake_data)
41
+ .and_return(fake_image)
42
+
43
+ allow(Eclaircir::Data)
44
+ .to receive(:new)
45
+ .with(image: fake_media)
46
+ .and_return(fake_data)
47
+
48
+ allow(Eclaircir::Media)
49
+ .to receive(:new)
50
+ .with(url: 'http://example.com/lol.jpg')
51
+ .and_return(fake_media)
52
+ end
53
+
54
+ let(:fake_image) do
55
+ instance_double(described_class)
56
+ end
57
+
58
+ let(:fake_data) do
59
+ instance_double(Eclaircir::Data)
60
+ end
61
+
62
+ let(:fake_media) do
63
+ instance_double(Eclaircir::Media)
64
+ end
65
+
66
+ it 'builds and returns an image with the right url' do
67
+ expect(described_class.from_url('http://example.com/lol.jpg')).to be fake_image
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Eclaircir::Media do
4
+ subject do
5
+ described_class.new(params)
6
+ end
7
+
8
+ let(:params) do
9
+ {
10
+ 'url' => 'https://samples.clarifai.com/metro-north.jpg',
11
+ }
12
+ end
13
+
14
+ its(:url) { is_expected.to eq 'https://samples.clarifai.com/metro-north.jpg' }
15
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ describe Eclaircir::Model do
4
+ subject do
5
+ described_class.new(params)
6
+ end
7
+
8
+ let(:params) do
9
+ {
10
+ 'id' => 'aaa03c23b3724a16a56b629203edc62c',
11
+ 'name' => 'general-v1.3',
12
+ 'created_at' => '2016-03-09T17:11:39Z',
13
+ 'app_id' => 'blah',
14
+ 'output_info' => output_info,
15
+ 'model_version' => model_version
16
+ }
17
+ end
18
+
19
+ let(:output_info) do
20
+ {
21
+ 'message' => 'Show output_info with: GET /models/{model_id}/output_info',
22
+ 'type' => 'concept'
23
+ }
24
+ end
25
+
26
+ let(:model_version) do
27
+ {
28
+ 'id' => 'aa9ca48295b37401f8af92ad1af0d91d',
29
+ 'created_at' => '2016-07-13T01:19:12Z',
30
+ 'status' => {
31
+ 'code' => 21100,
32
+ 'description' => 'Model trained successfully',
33
+ }
34
+ }
35
+ end
36
+
37
+ its(:id) { is_expected.to eq 'aaa03c23b3724a16a56b629203edc62c' }
38
+ its(:name) { is_expected.to eq 'general-v1.3' }
39
+ its(:created_at) { is_expected.to eq DateTime.new(2016, 3, 9, 17, 11, 39) }
40
+ its(:app_id) { is_expected.to eq 'blah' }
41
+ its(:output_info) { is_expected.to eq output_info }
42
+ its(:model_version) { is_expected.to eq model_version }
43
+
44
+ describe '#predict_outputs' do
45
+ before do
46
+ allow(Eclaircir)
47
+ .to receive(:new_client)
48
+ .and_return(fake_client)
49
+ end
50
+
51
+ let(:fake_client) do
52
+ instance_double(Eclaircir::Client)
53
+ end
54
+
55
+ context 'when using an url' do
56
+ before do
57
+ allow(Eclaircir::Input)
58
+ .to receive(:from_url)
59
+ .with('http://example.com/lol.jpg')
60
+ .and_return(fake_input)
61
+ end
62
+
63
+ let(:fake_input) do
64
+ instance_double(Eclaircir::Input)
65
+ end
66
+
67
+ let(:predicted_outputs) do
68
+ instance_double(Eclaircir::Response)
69
+ end
70
+
71
+ it 'asks the client correctly' do
72
+ expect(fake_client)
73
+ .to receive(:predict_outputs)
74
+ .with(subject, fake_input)
75
+ .and_return(predicted_outputs)
76
+
77
+ subject.predict_outputs(url: 'http://example.com/lol.jpg')
78
+ end
79
+ end
80
+
81
+ context 'when using an input directly' do
82
+ let(:fake_input) do
83
+ instance_double(Eclaircir::Input)
84
+ end
85
+
86
+ let(:predicted_outputs) do
87
+ instance_double(Eclaircir::Response)
88
+ end
89
+
90
+ it 'asks the client correctly' do
91
+ expect(fake_client)
92
+ .to receive(:predict_outputs)
93
+ .with(subject, fake_input)
94
+ .and_return(predicted_outputs)
95
+
96
+ subject.predict_outputs(input: fake_input)
97
+ end
98
+ end
99
+
100
+ context 'when using no parameter' do
101
+ it 'raises an ArgumentError' do
102
+ expect do
103
+ subject.predict_outputs
104
+ end.to raise_error(ArgumentError,
105
+ /one of the following keyword arguments should be provided \[url, input\]/)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Eclaircir::Output do
4
+ subject do
5
+ described_class.new(params)
6
+ end
7
+
8
+ let(:params) do
9
+ {
10
+ 'id' => 'ea68cac87c304b28a8046557062f34a0',
11
+ 'status' => {
12
+ 'code' => 10000,
13
+ 'description' => 'Ok',
14
+ },
15
+ 'created_at' => '2016-11-22T16:50:25Z',
16
+ 'model' => {
17
+ 'name' => 'general-v1.3',
18
+ 'id' => 'aaa03c23b3724a16a56b629203edc62c',
19
+ 'created_at' => '2016-03-09T17:11:39Z',
20
+ 'app_id' => nil,
21
+ 'output_info' => {},
22
+ 'model_version' => {}
23
+ },
24
+ 'input' => {
25
+ 'id' => 'ea68cac87c304b28a8046557062f34a0',
26
+ 'data' => {
27
+ 'image' => {}
28
+ }
29
+ },
30
+ 'data' => {
31
+ 'concepts' => []
32
+ }
33
+ }
34
+ end
35
+
36
+ its(:id) { is_expected.to eq 'ea68cac87c304b28a8046557062f34a0' }
37
+ its(:status) { is_expected.to be_a Eclaircir::Status }
38
+ its(:created_at) { is_expected.to eq DateTime.new(2016, 11, 22, 16, 50, 25) }
39
+ its(:model) { is_expected.to be_a Eclaircir::Model }
40
+ its(:input) { is_expected.to be_a Eclaircir::Input }
41
+ its(:data) { is_expected.to be_a Eclaircir::Data }
42
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe Eclaircir::Response do
4
+ subject do
5
+ described_class.new(params)
6
+ end
7
+
8
+ let(:params) do
9
+ {
10
+ 'status' => {
11
+ 'code' => 10000,
12
+ 'description' => 'Ok',
13
+ },
14
+ 'outputs' => [
15
+ {
16
+ 'id' => 'ea68cac87c304b28a8046557062f34a0',
17
+ 'status' => {
18
+ 'code' => 10000,
19
+ 'description' => 'Ok',
20
+ },
21
+ 'created_at' => '2016-11-22T16:50:25Z',
22
+ 'model' => {
23
+ 'name' => 'general-v1.3',
24
+ 'id' => 'aaa03c23b3724a16a56b629203edc62c',
25
+ 'created_at' => '2016-03-09T17:11:39Z',
26
+ 'app_id' => nil,
27
+ 'output_info' => {},
28
+ 'model_version' => {}
29
+ },
30
+ 'input' => {
31
+ 'id' => 'ea68cac87c304b28a8046557062f34a0',
32
+ 'data' => {
33
+ 'image' => {}
34
+ }
35
+ },
36
+ 'data' => {
37
+ 'concepts' => []
38
+ }
39
+ }
40
+ ]
41
+ }
42
+ end
43
+
44
+ its(:status) { is_expected.to be_a Eclaircir::Status }
45
+ its(:outputs) { is_expected.to all(be_a(Eclaircir::Output)) }
46
+
47
+ describe '.parse' do
48
+ before do
49
+ allow(described_class)
50
+ .to receive(:new)
51
+ .with(params)
52
+ .and_return(fake_response)
53
+ end
54
+
55
+ let(:fake_response) do
56
+ instance_double(described_class)
57
+ end
58
+
59
+ it 'returns the built response' do
60
+ expect(described_class.parse(params)).to be fake_response
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Eclaircir::Status do
4
+ subject do
5
+ described_class.new(params)
6
+ end
7
+
8
+ let(:params) do
9
+ {
10
+ 'code' => code,
11
+ 'description' => 'Ok',
12
+ }
13
+ end
14
+
15
+ let(:code) do
16
+ 10_000
17
+ end
18
+
19
+ its(:code) { is_expected.to eq 10_000 }
20
+ its(:description) { is_expected.to eq 'Ok' }
21
+
22
+ describe '#success?' do
23
+ context 'when the code is Status::SUCCESS' do
24
+ its(:success?) { is_expected.to be true }
25
+ end
26
+
27
+ context 'when the code is not Status::SUCCESS' do
28
+ let(:code) do
29
+ 10_020
30
+ end
31
+
32
+ its(:success?) { is_expected.to be false }
33
+ end
34
+ end
35
+
36
+ describe '#validate!' do
37
+ context 'when the code is Status::SUCCESS' do
38
+ it 'returns true' do
39
+ expect(subject.validate!).to be true
40
+ end
41
+ end
42
+
43
+ context 'when the code has a specific error' do
44
+ let(:code) do
45
+ 30_300
46
+ end
47
+
48
+ it 'raises the specific error with the description' do
49
+ expect do
50
+ subject.validate!
51
+ end.to raise_error(Eclaircir::ImageDecodingError, /Ok/)
52
+ end
53
+ end
54
+
55
+ context 'when the code is not managed' do
56
+ let(:code) do
57
+ 99_999
58
+ end
59
+
60
+ it 'raises an APIError' do
61
+ expect do
62
+ subject.validate!
63
+ end.to raise_error(Eclaircir::APIError, /Ok/)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Eclaircir::Client::StatusValidator do
4
+ subject do
5
+ described_class.new(response)
6
+ end
7
+
8
+ let(:response) do
9
+ instance_double(Eclaircir::Response,
10
+ status: main_status,
11
+ outputs: [output])
12
+ end
13
+
14
+ let(:main_status) do
15
+ instance_double(Eclaircir::Status,
16
+ success?: main_status_success)
17
+ end
18
+
19
+ let(:main_status_success) do
20
+ true
21
+ end
22
+
23
+ let(:output) do
24
+ instance_double(Eclaircir::Output,
25
+ status: output_status)
26
+ end
27
+
28
+ let(:output_status) do
29
+ instance_double(Eclaircir::Status)
30
+ end
31
+
32
+ describe '#validate!' do
33
+ context 'when the main status is successful' do
34
+ it 'returns true' do
35
+ expect(subject.validate!).to be true
36
+ end
37
+ end
38
+
39
+ context 'when the main status is not successful' do
40
+ let(:main_status_success) do
41
+ false
42
+ end
43
+
44
+ context 'when an output status is failed' do
45
+ before do
46
+ allow(output_status)
47
+ .to receive(:validate!)
48
+ .and_raise(Eclaircir::APIError, 'output status error')
49
+ end
50
+
51
+ it 'raises the output status validation error' do
52
+ expect do
53
+ subject.validate!
54
+ end.to raise_error(Eclaircir::APIError, /output status error/)
55
+ end
56
+ end
57
+
58
+ context 'when all output status validate' do
59
+ before do
60
+ allow(output_status)
61
+ .to receive(:validate!)
62
+ .and_return(true)
63
+
64
+ allow(main_status)
65
+ .to receive(:validate!)
66
+ .and_raise(Eclaircir::APIError, 'main status error')
67
+ end
68
+
69
+ it 'raises the main status validation error' do
70
+ expect do
71
+ subject.validate!
72
+ end.to raise_error(Eclaircir::APIError, /main status error/)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end