mindee 3.6.0 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +37 -3
- data/docs/international_id_v2.md +31 -0
- data/docs/invoices_v4.md +20 -2
- data/lib/mindee/client.rb +4 -4
- data/lib/mindee/http/endpoint.rb +24 -17
- data/lib/mindee/http/error.rb +24 -5
- data/lib/mindee/http/response_validation.rb +61 -0
- data/lib/mindee/parsing/common/api_response.rb +6 -1
- data/lib/mindee/product/invoice/invoice_v4_document.rb +10 -0
- data/lib/mindee/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b22c708fcdd44e0e5104e5b5e0f9cf669b7f41e46f1c627af252b86ed5b45d7
|
4
|
+
data.tar.gz: 5695f02623e644bbce0d26a3cec7096370d103b01914a97402887a87eba8cc2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b1e14cdac5f7e0a54d5326a2a9a54387568bddc7060e505468ee3f56896cba9a6d1da341d9a5918f695e99ef7aa79d95d065b43a692f8f036892fd0c4fae070
|
7
|
+
data.tar.gz: 92a95c7c1802fc6099a5ce13a869add7d7865ace5331c8d5a449ca4ed83d4e5e6573a193ed151e75f7b4ec8c2da41f30bd23cc2c71e1889d83ddf4012978123d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Mindee Ruby API Library Changelog
|
2
2
|
|
3
|
+
## v3.6.2 - 2024-03-28
|
4
|
+
### Changes
|
5
|
+
* :sparkles: update Invoice to v4.5
|
6
|
+
### Fixes
|
7
|
+
* :bug: fix invalid error codes
|
8
|
+
|
9
|
+
|
10
|
+
## v3.6.1 - 2024-03-07
|
11
|
+
### Changes
|
12
|
+
* :recycle: update error handling to account for future evolutions
|
13
|
+
* :memo: update miscellaneous product documentations
|
14
|
+
* :memo: add used environment variables to readme
|
15
|
+
|
16
|
+
|
3
17
|
## v3.6.0 - 2024-02-21
|
4
18
|
### Changes
|
5
19
|
* :sparkles: add support for resume V1
|
data/README.md
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
[![License: MIT](https://img.shields.io/github/license/mindee/mindee-api-ruby)](https://opensource.org/licenses/MIT) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/mindee/mindee-api-ruby/test.yml)](https://github.com/mindee/mindee-api-ruby) [![Gem Version](https://img.shields.io/gem/v/mindee)](https://rubygems.org/gems/mindee) [![Downloads](https://img.shields.io/gem/dt/mindee.svg)](https://rubygems.org/gems/mindee)
|
2
2
|
|
3
3
|
# Mindee API Helper Library for Ruby
|
4
|
+
|
4
5
|
Quickly and easily connect to Mindee's API services using Ruby.
|
5
6
|
|
6
7
|
## Requirements
|
8
|
+
|
7
9
|
The following Ruby versions are tested and supported: 2.6, 2.7, 3.0, 3.1, 3.2
|
8
10
|
|
9
11
|
## Quick Start
|
12
|
+
|
10
13
|
Here's the TL;DR of getting started.
|
11
14
|
|
12
15
|
First, get an [API Key](https://developers.mindee.com/docs/create-api-key)
|
@@ -18,15 +21,38 @@ gem 'mindee'
|
|
18
21
|
```
|
19
22
|
|
20
23
|
And then execute:
|
24
|
+
|
21
25
|
```sh
|
22
26
|
bundle install
|
23
27
|
```
|
24
28
|
|
25
29
|
Finally, Ruby away!
|
26
30
|
|
31
|
+
### Environment Variables
|
32
|
+
|
33
|
+
This library offers customizable features through environment variables. While there may be instances where you need to
|
34
|
+
rely on them, it's crucial to exercise caution when modifying them to avoid unintended consequences.
|
35
|
+
|
36
|
+
If you're unsure whether you need to adjust these variables, it's advisable to refrain from doing so unless you have a
|
37
|
+
specific reason. Accidentally overwriting them can lead to unexpected behavior.
|
38
|
+
|
39
|
+
Before making any changes, we recommend reviewing the following information to understand the purpose and potential
|
40
|
+
impact of each environment variable:
|
41
|
+
|
42
|
+
* `MINDEE_API_KEY`:
|
43
|
+
* **Description**: Your personal Mindee API Key as shown on the platform. Be careful not to show this publicly!
|
44
|
+
* **Default Value**: `nil`
|
45
|
+
* `MINDEE_BASE_URL`:
|
46
|
+
* **Description**: The default base URL of the API endpoint. Use this variable to specify the root URL for API requests. Modify as needed for proxy configurations or changes in API endpoint location.
|
47
|
+
* **Default Value**: `https://api.mindee.net/v1`
|
48
|
+
* `MINDEE_REQUEST_TIMEOUT`:
|
49
|
+
* **Description**: The default timeout for HTTP requests (in seconds).
|
50
|
+
* **Default Value**: `120`
|
51
|
+
|
27
52
|
### Loading a File and Parsing It
|
28
53
|
|
29
54
|
#### Global Documents
|
55
|
+
|
30
56
|
```ruby
|
31
57
|
require 'mindee'
|
32
58
|
|
@@ -47,26 +73,31 @@ puts result.document
|
|
47
73
|
**Note:** Files can also be loaded from:
|
48
74
|
|
49
75
|
A URL (`https`):
|
76
|
+
|
50
77
|
```rb
|
51
78
|
input_source = mindee_client.source_from_url("https://my-url")
|
52
79
|
```
|
53
80
|
|
54
81
|
A bytes input stream:
|
82
|
+
|
55
83
|
```rb
|
56
84
|
input_source = mindee_client.source_from_bytes('/path/to/the/file.ext', "name-of-my-file.ext")
|
57
85
|
```
|
58
86
|
|
59
87
|
A base64 encoded string:
|
88
|
+
|
60
89
|
```rb
|
61
90
|
input_source = mindee_client.source_from_b64string('/path/to/the/file.ext', "name-of-my-file.ext")
|
62
91
|
```
|
63
92
|
|
64
93
|
A ruby `file` object:
|
94
|
+
|
65
95
|
```rb
|
66
96
|
input_source = mindee_client.source_from_file(input_file, "name-of-my-file.ext")
|
67
97
|
```
|
68
98
|
|
69
99
|
#### Region-Specific Documents
|
100
|
+
|
70
101
|
```ruby
|
71
102
|
require 'mindee'
|
72
103
|
|
@@ -86,6 +117,7 @@ puts result.document
|
|
86
117
|
```
|
87
118
|
|
88
119
|
### Custom Document (API Builder)
|
120
|
+
|
89
121
|
```ruby
|
90
122
|
require 'mindee'
|
91
123
|
|
@@ -119,17 +151,19 @@ end
|
|
119
151
|
## CLI Tool
|
120
152
|
|
121
153
|
A command-line interface tool is available to quickly test documents:
|
154
|
+
|
122
155
|
```sh
|
123
156
|
ruby ./bin/mindee.rb invoice path/to/your/file.ext
|
124
157
|
```
|
125
158
|
|
126
|
-
|
127
159
|
Using the ruby bundler:
|
160
|
+
|
128
161
|
```sh
|
129
162
|
bundle exec ruby ./bin/mindee.rb invoice path/to/your/file.ext
|
130
163
|
```
|
131
164
|
|
132
165
|
## Further Reading
|
166
|
+
|
133
167
|
There's more to it than that for those that need more features, or want to
|
134
168
|
customize the experience.
|
135
169
|
|
@@ -157,15 +191,15 @@ customize the experience.
|
|
157
191
|
* [Invoice Splitter API Ruby](https://developers.mindee.com/docs/invoice-splitter-api-ruby)
|
158
192
|
* [Multi Receipts Detector API Ruby](https://developers.mindee.com/docs/multi-receipts-detector-api-ruby)
|
159
193
|
|
160
|
-
|
161
194
|
You can also take a look at the
|
162
195
|
[Reference Documentation](https://mindee.github.io/mindee-api-ruby/).
|
163
196
|
|
164
|
-
|
165
197
|
## License
|
198
|
+
|
166
199
|
Copyright © Mindee, SA
|
167
200
|
|
168
201
|
Available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
169
202
|
|
170
203
|
## Questions?
|
204
|
+
|
171
205
|
[Join our Slack](https://join.slack.com/t/mindee-community/shared_invite/zt-2d0ds7dtz-DPAF81ZqTy20chsYpQBW5g)
|
data/docs/international_id_v2.md
CHANGED
@@ -31,6 +31,37 @@ puts result.document
|
|
31
31
|
|
32
32
|
**Output (RST):**
|
33
33
|
```rst
|
34
|
+
########
|
35
|
+
Document
|
36
|
+
########
|
37
|
+
:Mindee ID: cfa20a58-20cf-43b6-8cec-9505fa69d1c2
|
38
|
+
:Filename: default_sample.jpg
|
39
|
+
|
40
|
+
Inference
|
41
|
+
#########
|
42
|
+
:Product: mindee/international_id v2.0
|
43
|
+
:Rotation applied: No
|
44
|
+
|
45
|
+
Prediction
|
46
|
+
==========
|
47
|
+
:Document Type: IDENTIFICATION_CARD
|
48
|
+
:Document Number: 12345678A
|
49
|
+
:Surnames: MUESTRA
|
50
|
+
MUESTRA
|
51
|
+
:Given Names: CARMEN
|
52
|
+
:Sex: F
|
53
|
+
:Birth Date: 1980-01-01
|
54
|
+
:Birth Place: CAMPO DE CRIPTANA CIUDAD REAL ESPANA
|
55
|
+
:Nationality: ESP
|
56
|
+
:Personal Number: BAB1834284<44282767Q0
|
57
|
+
:Country of Issue: ESP
|
58
|
+
:State of Issue: MADRID
|
59
|
+
:Issue Date:
|
60
|
+
:Expiration Date: 2030-01-01
|
61
|
+
:Address: C/REAL N13, 1 DCHA COLLADO VILLALBA MADRID MADRID MADRID
|
62
|
+
:MRZ Line 1: IDESPBAB1834284<44282767Q0<<<<
|
63
|
+
:MRZ Line 2: 8001010F1301017ESP<<<<<<<<<<<3
|
64
|
+
:MRZ Line 3: MUESTRA<MUESTRA<<CARMEN<<<<<<<
|
34
65
|
```
|
35
66
|
|
36
67
|
# Field Types
|
data/docs/invoices_v4.md
CHANGED
@@ -34,12 +34,12 @@ puts result.document
|
|
34
34
|
########
|
35
35
|
Document
|
36
36
|
########
|
37
|
-
:Mindee ID:
|
37
|
+
:Mindee ID: a9062dd1-0d34-42ab-9505-0481a8545106
|
38
38
|
:Filename: default_sample.jpg
|
39
39
|
|
40
40
|
Inference
|
41
41
|
#########
|
42
|
-
:Product: mindee/invoices v4.
|
42
|
+
:Product: mindee/invoices v4.5
|
43
43
|
:Rotation applied: Yes
|
44
44
|
|
45
45
|
Prediction
|
@@ -65,6 +65,8 @@ Prediction
|
|
65
65
|
:Customer Name: JIRO DOI
|
66
66
|
:Customer Company Registrations:
|
67
67
|
:Customer Address: 1954 Bloor Street West Toronto, ON, M6P 3K9 Canada
|
68
|
+
:Shipping Address:
|
69
|
+
:Billing Address: 1954 Bloor Street West Toronto, ON, M6P 3K9 Canada
|
68
70
|
:Document Type: INVOICE
|
69
71
|
:Line Items:
|
70
72
|
+--------------------------------------+--------------+----------+------------+--------------+--------------+------------+
|
@@ -103,6 +105,8 @@ Page 0
|
|
103
105
|
:Customer Name: JIRO DOI
|
104
106
|
:Customer Company Registrations:
|
105
107
|
:Customer Address: 1954 Bloor Street West Toronto, ON, M6P 3K9 Canada
|
108
|
+
:Shipping Address:
|
109
|
+
:Billing Address: 1954 Bloor Street West Toronto, ON, M6P 3K9 Canada
|
106
110
|
:Document Type: INVOICE
|
107
111
|
:Line Items:
|
108
112
|
+--------------------------------------+--------------+----------+------------+--------------+--------------+------------+
|
@@ -205,6 +209,13 @@ A `InvoiceV4LineItem` implements the following attributes:
|
|
205
209
|
# Attributes
|
206
210
|
The following fields are extracted for Invoice V4:
|
207
211
|
|
212
|
+
## Billing Address
|
213
|
+
**billing_address** ([StringField](#string-field)): The customer's address used for billing.
|
214
|
+
|
215
|
+
```rb
|
216
|
+
puts result.document.inference.prediction.billing_address.value
|
217
|
+
```
|
218
|
+
|
208
219
|
## Customer Address
|
209
220
|
**customer_address** ([StringField](#string-field)): The address of the customer.
|
210
221
|
|
@@ -281,6 +292,13 @@ for reference_numbers_elem in result.document.inference.prediction.reference_num
|
|
281
292
|
end
|
282
293
|
```
|
283
294
|
|
295
|
+
## Shipping Address
|
296
|
+
**shipping_address** ([StringField](#string-field)): Customer's delivery address.
|
297
|
+
|
298
|
+
```rb
|
299
|
+
puts result.document.inference.prediction.shipping_address.value
|
300
|
+
```
|
301
|
+
|
284
302
|
## Supplier Address
|
285
303
|
**supplier_address** ([StringField](#string-field)): The address of the supplier or merchant.
|
286
304
|
|
data/lib/mindee/client.rb
CHANGED
@@ -138,9 +138,9 @@ module Mindee
|
|
138
138
|
# * `:on_min_pages` Apply the operation only if document has at least this many pages.
|
139
139
|
# @param cropper [Boolean, nil] Whether to include cropper results for each page.
|
140
140
|
# This performs a cropping operation on the server and will increase response time.
|
141
|
-
# @param initial_delay_sec [Integer, Float
|
142
|
-
# @param delay_sec [Integer, Float
|
143
|
-
# @param max_retries [Integer
|
141
|
+
# @param initial_delay_sec [Integer, Float] initial delay before polling. Defaults to 4.
|
142
|
+
# @param delay_sec [Integer, Float] delay between polling attempts. Defaults to 2.
|
143
|
+
# @param max_retries [Integer] maximum amount of retries. Defaults to 60.
|
144
144
|
# @return [Mindee::Parsing::Common::ApiResponse]
|
145
145
|
def enqueue_and_parse(
|
146
146
|
input_source,
|
@@ -167,7 +167,7 @@ module Mindee
|
|
167
167
|
polling_attempts = 1
|
168
168
|
job_id = enqueue_res.job.id
|
169
169
|
queue_res = parse_queued(job_id, product_class, endpoint: endpoint)
|
170
|
-
while
|
170
|
+
while queue_res.job.status != Mindee::Parsing::Common::JobStatus::COMPLETED && polling_attempts < max_retries
|
171
171
|
sleep(delay_sec)
|
172
172
|
queue_res = parse_queued(job_id, product_class, endpoint: endpoint)
|
173
173
|
polling_attempts += 1
|
data/lib/mindee/http/endpoint.rb
CHANGED
@@ -4,6 +4,7 @@ require 'json'
|
|
4
4
|
require 'net/http'
|
5
5
|
require_relative 'error'
|
6
6
|
require_relative '../version'
|
7
|
+
require_relative 'response_validation'
|
7
8
|
|
8
9
|
module Mindee
|
9
10
|
module HTTP
|
@@ -49,14 +50,15 @@ module Mindee
|
|
49
50
|
# @param all_words [Boolean] Whether the full word extraction needs to be performed
|
50
51
|
# @param close_file [Boolean] Whether the file will be closed after reading
|
51
52
|
# @param cropper [Boolean] Whether a cropping operation will be applied
|
52
|
-
# @return [
|
53
|
+
# @return [Array]
|
53
54
|
def predict(input_source, all_words, close_file, cropper)
|
54
55
|
check_api_key
|
55
56
|
response = predict_req_post(input_source, all_words: all_words, close_file: close_file, cropper: cropper)
|
56
57
|
hashed_response = JSON.parse(response.body, object_class: Hash)
|
57
|
-
return [hashed_response, response.body] if
|
58
|
+
return [hashed_response, response.body] if ResponseValidation.valid_sync_response?(response)
|
58
59
|
|
59
|
-
|
60
|
+
ResponseValidation.clean_request!(response)
|
61
|
+
error = Error.handle_error(@url_name, response)
|
60
62
|
raise error
|
61
63
|
end
|
62
64
|
|
@@ -65,27 +67,29 @@ module Mindee
|
|
65
67
|
# @param all_words [Boolean] Whether the full word extraction needs to be performed
|
66
68
|
# @param close_file [Boolean] Whether the file will be closed after reading
|
67
69
|
# @param cropper [Boolean] Whether a cropping operation will be applied
|
68
|
-
# @return [
|
70
|
+
# @return [Array]
|
69
71
|
def predict_async(input_source, all_words, close_file, cropper)
|
70
72
|
check_api_key
|
71
73
|
response = document_queue_req_get(input_source, all_words, close_file, cropper)
|
72
74
|
hashed_response = JSON.parse(response.body, object_class: Hash)
|
73
|
-
return [hashed_response, response.body] if
|
75
|
+
return [hashed_response, response.body] if ResponseValidation.valid_async_response?(response)
|
74
76
|
|
75
|
-
|
77
|
+
ResponseValidation.clean_request!(response)
|
78
|
+
error = Error.handle_error(@url_name, response)
|
76
79
|
raise error
|
77
80
|
end
|
78
81
|
|
79
82
|
# Calls the parsed async doc.
|
80
83
|
# @param job_id [String]
|
81
|
-
# @return [
|
84
|
+
# @return [Array]
|
82
85
|
def parse_async(job_id)
|
83
86
|
check_api_key
|
84
87
|
response = document_queue_req(job_id)
|
85
88
|
hashed_response = JSON.parse(response.body, object_class: Hash)
|
86
|
-
return [hashed_response, response.body] if
|
89
|
+
return [hashed_response, response.body] if ResponseValidation.valid_async_response?(response)
|
87
90
|
|
88
|
-
|
91
|
+
ResponseValidation.clean_request!(response)
|
92
|
+
error = Error.handle_error(@url_name, response)
|
89
93
|
raise error
|
90
94
|
end
|
91
95
|
|
@@ -95,7 +99,7 @@ module Mindee
|
|
95
99
|
# @param all_words [Boolean] Whether the full word extraction needs to be performed
|
96
100
|
# @param close_file [Boolean] Whether the file will be closed after reading
|
97
101
|
# @param cropper [Boolean] Whether a cropping operation will be applied
|
98
|
-
# @return [Net::
|
102
|
+
# @return [Net::HTTPResponse, nil]
|
99
103
|
def predict_req_post(input_source, all_words: false, close_file: true, cropper: false)
|
100
104
|
uri = URI("#{@url_root}/predict")
|
101
105
|
|
@@ -116,17 +120,18 @@ module Mindee
|
|
116
120
|
form_data.push ['include_mvision', 'true'] if all_words
|
117
121
|
|
118
122
|
req.set_form(form_data, 'multipart/form-data')
|
119
|
-
|
123
|
+
response = nil
|
120
124
|
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, read_timeout: @request_timeout) do |http|
|
121
|
-
http.request(req)
|
125
|
+
response = http.request(req)
|
122
126
|
end
|
127
|
+
response
|
123
128
|
end
|
124
129
|
|
125
130
|
# @param input_source [Mindee::Input::Source::LocalInputSource, Mindee::Input::Source::UrlInputSource]
|
126
131
|
# @param all_words [Boolean] Whether the full word extraction needs to be performed
|
127
132
|
# @param close_file [Boolean] Whether the file will be closed after reading
|
128
133
|
# @param cropper [Boolean] Whether a cropping operation will be applied
|
129
|
-
# @return [Net::HTTPResponse]
|
134
|
+
# @return [Net::HTTPResponse, nil]
|
130
135
|
def document_queue_req_get(input_source, all_words, close_file, cropper)
|
131
136
|
uri = URI("#{@url_root}/predict_async")
|
132
137
|
|
@@ -148,13 +153,15 @@ module Mindee
|
|
148
153
|
|
149
154
|
req.set_form(form_data, 'multipart/form-data')
|
150
155
|
|
156
|
+
response = nil
|
151
157
|
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, read_timeout: @request_timeout) do |http|
|
152
|
-
http.request(req)
|
158
|
+
response = http.request(req)
|
153
159
|
end
|
160
|
+
response
|
154
161
|
end
|
155
162
|
|
156
163
|
# @param job_id [String]
|
157
|
-
# @return [Net::HTTPResponse]
|
164
|
+
# @return [Net::HTTPResponse, nil]
|
158
165
|
def document_queue_req(job_id)
|
159
166
|
uri = URI("#{@url_root}/documents/queue/#{job_id}")
|
160
167
|
|
@@ -171,8 +178,8 @@ module Mindee
|
|
171
178
|
|
172
179
|
if response.code.to_i > 299 && response.code.to_i < 400
|
173
180
|
req = Net::HTTP::Get.new(response['location'], headers)
|
174
|
-
|
175
|
-
http.request(req)
|
181
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, read_timeout: @request_timeout) do |http|
|
182
|
+
response = http.request(req)
|
176
183
|
end
|
177
184
|
end
|
178
185
|
response
|
data/lib/mindee/http/error.rb
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
module Mindee
|
4
6
|
module HTTP
|
5
7
|
# Mindee HTTP error module.
|
6
8
|
module Error
|
7
9
|
module_function
|
8
10
|
|
11
|
+
# Extracts the HTTP error from the response hash, or the job error if there is one.
|
12
|
+
# @param response [Hash] dictionary response retrieved by the server
|
13
|
+
def extract_error(response)
|
14
|
+
return unless response.respond_to?(:each_pair)
|
15
|
+
|
16
|
+
if !response.dig('api_request', 'error').empty?
|
17
|
+
response.dig('api_request', 'error')
|
18
|
+
elsif !response.dig('job', 'error').empty?
|
19
|
+
response.dig('job', 'error')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
9
23
|
# Creates an error object based on what's retrieved from a request.
|
10
24
|
# @param response [Hash] dictionary response retrieved by the server
|
11
25
|
def create_error_obj(response)
|
12
|
-
error_obj =
|
26
|
+
error_obj = extract_error(response)
|
13
27
|
if error_obj.nil?
|
14
28
|
error_obj = if response.include?('Maximum pdf pages')
|
15
29
|
{
|
@@ -50,15 +64,20 @@ module Mindee
|
|
50
64
|
end
|
51
65
|
|
52
66
|
end
|
53
|
-
error_obj
|
67
|
+
error_obj.nil? ? {} : error_obj
|
54
68
|
end
|
55
69
|
|
56
70
|
# Creates an appropriate HTTP error exception, based on retrieved http error code
|
57
71
|
# @param url [String] the url of the product
|
58
72
|
# @param response [Hash] dictionary response retrieved by the server
|
59
|
-
|
60
|
-
|
61
|
-
|
73
|
+
def handle_error(url, response)
|
74
|
+
code = response.code.to_i
|
75
|
+
begin
|
76
|
+
parsed_hash = JSON.parse(response.body, object_class: Hash)
|
77
|
+
rescue JSON::ParserError
|
78
|
+
parsed_hash = response.body.to_s
|
79
|
+
end
|
80
|
+
error_obj = create_error_obj(parsed_hash)
|
62
81
|
case code
|
63
82
|
when 400..499
|
64
83
|
MindeeHttpClientError.new(error_obj, url, code)
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module Mindee
|
7
|
+
module HTTP
|
8
|
+
# Module dedicated to the validation & sanitizing of HTTP responses.
|
9
|
+
module ResponseValidation
|
10
|
+
# Checks if the synchronous response is valid. Returns True if the response is valid.
|
11
|
+
# @param [Net::HTTPResponse] response
|
12
|
+
# @return [Boolean]
|
13
|
+
def self.valid_sync_response?(response)
|
14
|
+
return false unless (200..399).cover?(response.code.to_i)
|
15
|
+
|
16
|
+
begin
|
17
|
+
JSON.parse(response.body, object_class: Hash)
|
18
|
+
rescue StandardError
|
19
|
+
return false
|
20
|
+
end
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
# Checks if the asynchronous response is valid. Also checks if it is a valid synchronous response.
|
25
|
+
# Returns true if the response is valid.
|
26
|
+
# @param [Net::HTTPResponse] response
|
27
|
+
# @return [Boolean]
|
28
|
+
def self.valid_async_response?(response)
|
29
|
+
return false unless valid_sync_response?(response)
|
30
|
+
|
31
|
+
return false unless (200..302).cover?(response.code.to_i)
|
32
|
+
|
33
|
+
hashed_response = JSON.parse(response.body, object_class: Hash)
|
34
|
+
|
35
|
+
return false if hashed_response.dig('job', 'status') == Mindee::Parsing::Common::JobStatus::FAILURE
|
36
|
+
|
37
|
+
return false if hashed_response.dig('job', 'error') && !hashed_response.dig('job', 'error').empty?
|
38
|
+
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
# Checks and correct the response object depending on the possible kinds of returns.
|
43
|
+
# @param response [Net::HTTPResponse]
|
44
|
+
def self.clean_request!(response)
|
45
|
+
return response if (response.code.to_i < 200) || (response.code.to_i > 302)
|
46
|
+
|
47
|
+
return response if response.body.empty?
|
48
|
+
|
49
|
+
hashed_response = JSON.parse(response.body, object_class: Hash)
|
50
|
+
if hashed_response.dig('api_request', 'status_code').to_i > 399
|
51
|
+
response.instance_variable_set(:@code, hashed_response['api_request']['status_code'].to_s)
|
52
|
+
end
|
53
|
+
|
54
|
+
return if !hashed_response.dig('job', 'error').empty? &&
|
55
|
+
(hashed_response.dig('job', 'status') != Mindee::Parsing::Common::JobStatus::FAILURE.to_s)
|
56
|
+
|
57
|
+
response.instance_variable_set(:@code, '500')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -14,6 +14,8 @@ module Mindee
|
|
14
14
|
PROCESSING = :processing
|
15
15
|
# Document parsing is complete.
|
16
16
|
COMPLETED = :completed
|
17
|
+
# Job failed
|
18
|
+
FAILURE = :failed
|
17
19
|
end
|
18
20
|
|
19
21
|
# Potential values for requests.
|
@@ -36,10 +38,13 @@ module Mindee
|
|
36
38
|
attr_reader :status
|
37
39
|
# @return [Integer, nil]
|
38
40
|
attr_reader :millisecs_taken
|
41
|
+
# @return [Hash, nil]
|
42
|
+
attr_reader :error
|
39
43
|
|
40
44
|
# @param http_response [Hash]
|
41
45
|
def initialize(http_response)
|
42
46
|
@id = http_response['id']
|
47
|
+
@error = http_response['error']
|
43
48
|
@issued_at = Time.iso8601(http_response['issued_at'])
|
44
49
|
if http_response.key?('available_at') && !http_response['available_at'].nil?
|
45
50
|
@available_at = Time.iso8601(http_response['available_at'])
|
@@ -108,7 +113,7 @@ module Mindee
|
|
108
113
|
end
|
109
114
|
if http_response.key?('document') &&
|
110
115
|
(!http_response.key?('job') ||
|
111
|
-
|
116
|
+
http_response['job']['status'] == 'completed') &&
|
112
117
|
@api_request.status == RequestStatus::SUCCESS
|
113
118
|
@document = Mindee::Parsing::Common::Document.new(product_class, http_response['document'])
|
114
119
|
end
|
@@ -9,6 +9,9 @@ module Mindee
|
|
9
9
|
# Invoice V4 document prediction.
|
10
10
|
class InvoiceV4Document < Mindee::Parsing::Common::Prediction
|
11
11
|
include Mindee::Parsing::Standard
|
12
|
+
# The customer's address used for billing.
|
13
|
+
# @return [Mindee::Parsing::Standard::StringField]
|
14
|
+
attr_reader :billing_address
|
12
15
|
# The address of the customer.
|
13
16
|
# @return [Mindee::Parsing::Standard::StringField]
|
14
17
|
attr_reader :customer_address
|
@@ -39,6 +42,9 @@ module Mindee
|
|
39
42
|
# List of Reference numbers, including PO number.
|
40
43
|
# @return [Array<Mindee::Parsing::Standard::StringField>]
|
41
44
|
attr_reader :reference_numbers
|
45
|
+
# Customer's delivery address.
|
46
|
+
# @return [Mindee::Parsing::Standard::StringField]
|
47
|
+
attr_reader :shipping_address
|
42
48
|
# The address of the supplier or merchant.
|
43
49
|
# @return [Mindee::Parsing::Standard::StringField]
|
44
50
|
attr_reader :supplier_address
|
@@ -68,6 +74,7 @@ module Mindee
|
|
68
74
|
# @param page_id [Integer, nil]
|
69
75
|
def initialize(prediction, page_id)
|
70
76
|
super()
|
77
|
+
@billing_address = StringField.new(prediction['billing_address'], page_id)
|
71
78
|
@customer_address = StringField.new(prediction['customer_address'], page_id)
|
72
79
|
@customer_company_registrations = []
|
73
80
|
prediction['customer_company_registrations'].each do |item|
|
@@ -87,6 +94,7 @@ module Mindee
|
|
87
94
|
prediction['reference_numbers'].each do |item|
|
88
95
|
@reference_numbers.push(StringField.new(item, page_id))
|
89
96
|
end
|
97
|
+
@shipping_address = StringField.new(prediction['shipping_address'], page_id)
|
90
98
|
@supplier_address = StringField.new(prediction['supplier_address'], page_id)
|
91
99
|
@supplier_company_registrations = []
|
92
100
|
prediction['supplier_company_registrations'].each do |item|
|
@@ -127,6 +135,8 @@ module Mindee
|
|
127
135
|
out_str << "\n:Customer Name: #{@customer_name}".rstrip
|
128
136
|
out_str << "\n:Customer Company Registrations: #{customer_company_registrations}".rstrip
|
129
137
|
out_str << "\n:Customer Address: #{@customer_address}".rstrip
|
138
|
+
out_str << "\n:Shipping Address: #{@shipping_address}".rstrip
|
139
|
+
out_str << "\n:Billing Address: #{@billing_address}".rstrip
|
130
140
|
out_str << "\n:Document Type: #{@document_type}".rstrip
|
131
141
|
out_str << "\n:Line Items:"
|
132
142
|
out_str << line_items
|
data/lib/mindee/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mindee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mindee, SA
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: marcel
|
@@ -181,6 +181,7 @@ files:
|
|
181
181
|
- lib/mindee/http/.rubocop.yml
|
182
182
|
- lib/mindee/http/endpoint.rb
|
183
183
|
- lib/mindee/http/error.rb
|
184
|
+
- lib/mindee/http/response_validation.rb
|
184
185
|
- lib/mindee/input.rb
|
185
186
|
- lib/mindee/input/sources.rb
|
186
187
|
- lib/mindee/parsing.rb
|