eclaircir 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.
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